1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-20 07:23:51 +00:00

Load interaction profile suggestions from xml instead of hardcoding them.

This commit is contained in:
Mads Buvik Sandvei 2020-12-02 21:46:03 +01:00
parent c3418a45ae
commit e42dada208
11 changed files with 470 additions and 313 deletions

View file

@ -258,6 +258,9 @@ if(BUILD_OPENMW_VR)
${GAME} ${GAME_HEADER} ${GAME} ${GAME_HEADER}
${APPLE_BUNDLE_RESOURCES} ${APPLE_BUNDLE_RESOURCES}
) )
configure_resource_file(${OpenMW_SOURCE_DIR}/files/xrcontrollersuggestions.xml
"${OpenMW_BINARY_DIR}" "xrcontrollersuggestions.xml")
########### Import the OpenXR SDK ########### Import the OpenXR SDK
# Force the openxr-sdk to use its bundled jsoncpp to avoid problems from system jsoncpp if present # Force the openxr-sdk to use its bundled jsoncpp to avoid problems from system jsoncpp if present

View file

@ -718,10 +718,29 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string()); Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
mEnvironment.setWindowManager (window); mEnvironment.setWindowManager (window);
MWInput::InputManager* input =
#ifdef USE_OPENXR #ifdef USE_OPENXR
new MWVR::VRInputManager(mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab); const std::string xrinputuserdefault = mCfgMgr.getUserConfigPath().string() + "/xrcontrollersuggestions.xml";
const std::string xrinputlocaldefault = mCfgMgr.getLocalPath().string() + "/xrcontrollersuggestions.xml";
const std::string xrinputglobaldefault = mCfgMgr.getGlobalPath().string() + "/xrcontrollersuggestions.xml";
std::string xrControllerSuggestions;
if (boost::filesystem::exists(xrinputuserdefault))
xrControllerSuggestions = xrinputuserdefault;
else if (boost::filesystem::exists(xrinputlocaldefault))
xrControllerSuggestions = xrinputlocaldefault;
else if (boost::filesystem::exists(xrinputglobaldefault))
xrControllerSuggestions = xrinputglobaldefault;
else
xrControllerSuggestions = ""; //if it doesn't exist, pass in an empty string
Log(Debug::Verbose) << "xrinputuserdefault: " << xrinputuserdefault;
Log(Debug::Verbose) << "xrinputlocaldefault: " << xrinputlocaldefault;
Log(Debug::Verbose) << "xrinputglobaldefault: " << xrinputglobaldefault;
MWInput::InputManager* input =
new MWVR::VRInputManager(mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab, xrControllerSuggestions);
#else #else
MWInput::InputManager* input =
new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab); new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
#endif #endif
mEnvironment.setInputManager (input); mEnvironment.setInputManager (input);

View file

@ -120,7 +120,8 @@ namespace MWVR
const std::string& actionName, const std::string& actionName,
const std::string& localName) const std::string& localName)
{ {
mActionMap.emplace(openMWAction, new A(openMWAction, std::move(createXRAction(AT, mInternalName + "_" + actionName, mLocalizedName + " " + localName)))); auto xrAction = createXRAction(AT, mInternalName + "_" + actionName, mLocalizedName + " " + localName);
mActionMap.emplace(actionName, new A(openMWAction, std::move(xrAction)));
} }
XrActionSet XrActionSet

View file

@ -43,7 +43,7 @@ namespace MWVR
XrActionSet mActionSet{ nullptr }; XrActionSet mActionSet{ nullptr };
std::string mLocalizedName{}; std::string mLocalizedName{};
std::string mInternalName{}; std::string mInternalName{};
std::map<int, std::unique_ptr<Action>> mActionMap; std::map<std::string, std::unique_ptr<Action>> mActionMap;
std::map<TrackedLimb, std::unique_ptr<PoseAction>> mTrackerMap; std::map<TrackedLimb, std::unique_ptr<PoseAction>> mTrackerMap;
std::map<TrackedLimb, std::unique_ptr<HapticsAction>> mHapticsMap; std::map<TrackedLimb, std::unique_ptr<HapticsAction>> mHapticsMap;
std::deque<const Action*> mActionQueue{}; std::deque<const Action*> mActionQueue{};

View file

@ -28,9 +28,9 @@ namespace MWVR
return it->second; return it->second;
} }
void OpenXRInput::suggestBindings(ActionSet actionSet, std::string profile, const SuggestedBindings& mwSuggestedBindings) void OpenXRInput::suggestBindings(ActionSet actionSet, std::string profilePath, const SuggestedBindings& mwSuggestedBindings)
{ {
getActionSet(actionSet).suggestBindings(mSuggestedBindings[profile], mwSuggestedBindings); getActionSet(actionSet).suggestBindings(mSuggestedBindings[profilePath], mwSuggestedBindings);
} }
void OpenXRInput::attachActionSets() void OpenXRInput::attachActionSets()
@ -48,6 +48,8 @@ namespace MWVR
xrProfileSuggestedBindings.suggestedBindings = profile.second.data(); xrProfileSuggestedBindings.suggestedBindings = profile.second.data();
xrProfileSuggestedBindings.countSuggestedBindings = (uint32_t)profile.second.size(); xrProfileSuggestedBindings.countSuggestedBindings = (uint32_t)profile.second.size();
CHECK_XRCMD(xrSuggestInteractionProfileBindings(xr->impl().xrInstance(), &xrProfileSuggestedBindings)); CHECK_XRCMD(xrSuggestInteractionProfileBindings(xr->impl().xrInstance(), &xrProfileSuggestedBindings));
mInteractionProfileNames[profilePath] = profile.first;
mInteractionProfilePaths[profile.first] = profilePath;
} }
// OpenXR requires that xrAttachSessionActionSets be called at most once per session. // OpenXR requires that xrAttachSessionActionSets be called at most once per session.
@ -62,4 +64,49 @@ namespace MWVR
attachInfo.actionSets = actionSets.data(); attachInfo.actionSets = actionSets.data();
CHECK_XRCMD(xrAttachSessionActionSets(xr->impl().xrSession(), &attachInfo)); CHECK_XRCMD(xrAttachSessionActionSets(xr->impl().xrSession(), &attachInfo));
} }
void OpenXRInput::notifyInteractionProfileChanged()
{
auto xr = MWVR::Environment::get().getManager();
xr->impl().xrSession();
// Unfortunately, openxr does not tell us WHICH profile has changed.
std::array<std::string, 5> topLevelUserPaths =
{
"/user/hand/left",
"/user/hand/right",
"/user/head",
"/user/gamepad",
"/user/treadmill"
};
for (auto& userPath : topLevelUserPaths)
{
auto pathIt = mInteractionProfilePaths.find(userPath);
if (pathIt == mInteractionProfilePaths.end())
{
XrPath xrUserPath = XR_NULL_PATH;
CHECK_XRCMD(
xrStringToPath(xr->impl().xrInstance(), userPath.c_str(), &xrUserPath));
mInteractionProfilePaths[userPath] = xrUserPath;
pathIt = mInteractionProfilePaths.find(userPath);
}
XrInteractionProfileState interactionProfileState{
XR_TYPE_INTERACTION_PROFILE_STATE
};
xrGetCurrentInteractionProfile(xr->impl().xrSession(), pathIt->second, &interactionProfileState);
if (interactionProfileState.interactionProfile)
{
auto activeProfileIt = mActiveInteractionProfiles.find(pathIt->second);
if (activeProfileIt == mActiveInteractionProfiles.end() || interactionProfileState.interactionProfile != activeProfileIt->second)
{
auto activeProfileNameIt = mInteractionProfileNames.find(interactionProfileState.interactionProfile);
Log(Debug::Verbose) << userPath << ": Interaction profile changed to '" << activeProfileNameIt->second << "'";
mActiveInteractionProfiles[pathIt->second] = interactionProfileState.interactionProfile;
}
}
}
}
} }

View file

@ -28,8 +28,14 @@ namespace MWVR
//! Set bindings and attach actionSets to the session. //! Set bindings and attach actionSets to the session.
void attachActionSets(); void attachActionSets();
//! Notify that active interaction profile has changed
void notifyInteractionProfileChanged();
protected: protected:
std::map<ActionSet, OpenXRActionSet> mActionSets{}; std::map<ActionSet, OpenXRActionSet> mActionSets{};
std::map<XrPath, std::string> mInteractionProfileNames{};
std::map<std::string, XrPath> mInteractionProfilePaths{};
std::map<XrPath, XrPath> mActiveInteractionProfiles;
XrProfileSuggestedBindings mSuggestedBindings{}; XrProfileSuggestedBindings mSuggestedBindings{};
bool mAttached = false; bool mAttached = false;
}; };

View file

@ -2,6 +2,8 @@
#include "openxrdebug.hpp" #include "openxrdebug.hpp"
#include "openxrswapchain.hpp" #include "openxrswapchain.hpp"
#include "openxrswapchainimpl.hpp" #include "openxrswapchainimpl.hpp"
#include "vrenvironment.hpp"
#include "vrinputmanager.hpp"
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp> #include <components/sdlutil/sdlgraphicswindow.hpp>
@ -685,8 +687,10 @@ namespace MWVR
return handleSessionStateChanged(*stateChangeEvent); return handleSessionStateChanged(*stateChangeEvent);
break; break;
} }
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
MWVR::Environment::get().getInputManager()->notifyInteractionProfileChanged();
break;
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING:
default: default:
{ {

View file

@ -29,8 +29,8 @@ namespace MWVR
/// \brief Suggest a binding by binding an action to a path on a given hand (left or right). /// \brief Suggest a binding by binding an action to a path on a given hand (left or right).
struct SuggestedBinding struct SuggestedBinding
{ {
int action;
std::string path; std::string path;
std::string action;
}; };
using SuggestedBindings = std::vector<SuggestedBinding>; using SuggestedBindings = std::vector<SuggestedBinding>;
@ -124,7 +124,6 @@ namespace MWVR
void updateAndQueue(std::deque<const Action*>& queue); void updateAndQueue(std::deque<const Action*>& queue);
protected: protected:
std::unique_ptr<OpenXRAction> mXRAction; std::unique_ptr<OpenXRAction> mXRAction;
int mOpenMWAction; int mOpenMWAction;
float mValue{ 0.f }; float mValue{ 0.f };

View file

@ -35,6 +35,7 @@
#include "../mwrender/camera.hpp" #include "../mwrender/camera.hpp"
#include <extern/oics/ICSInputControlSystem.h> #include <extern/oics/ICSInputControlSystem.h>
#include <extern/oics/tinyxml.h>
#include <iostream> #include <iostream>
@ -57,6 +58,11 @@ namespace MWVR
return mXRInput->getActionSet(ActionSet::Gameplay); return mXRInput->getActionSet(ActionSet::Gameplay);
} }
void VRInputManager::notifyInteractionProfileChanged()
{
mXRInput->notifyInteractionProfileChanged();
}
void VRInputManager::updateActivationIndication(void) void VRInputManager::updateActivationIndication(void)
{ {
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
@ -68,7 +74,6 @@ namespace MWVR
} }
} }
/** /**
* Makes it possible to use ItemModel::moveItem to move an item from an inventory to the world. * Makes it possible to use ItemModel::moveItem to move an item from an inventory to the world.
*/ */
@ -184,302 +189,86 @@ namespace MWVR
} }
} }
void VRInputManager::suggestBindingsSimple() void VRInputManager::throwDocumentError(TiXmlElement* element, std::string error)
{ {
std::string simpleProfilePath = "/interaction_profiles/khr/simple_controller"; std::stringstream ss;
// Set up default bindings for the khronos simple controller. ss << mXrControllerSuggestionsFile << "." << element->Row() << "." << element->Value();
// Note: The simple controller is the equivalent to a universal "default". ss << ": " << error;
// It has highly reduced functionality. Only poses and two click actions throw std::runtime_error(ss.str());
// are available for each hand, reducing the possible functionality of the profile
// to that of a wonky preview.
// The available click actions are 'select' and 'menu', and i cannot control what
// real buttons this is mapped to. On the Oculus Touch they are X, Y, A, and B.
// In-game character controls
SuggestedBindings simpleGameplayBindings{
{MWInput::A_Use, "/user/hand/left/input/select/click"}, // Touch: X
{A_VrMetaMenu, "/user/hand/left/input/menu/click"}, // Touch: Y
{A_Recenter, "/user/hand/left/input/menu/click"}, // Touch: Y
{A_ActivateTouch, "/user/hand/right/input/select/click"}, // Touch: A
{MWInput::A_AutoMove, "/user/hand/right/input/menu/click"}, // Touch: B
};
// GUI controls
SuggestedBindings simpleGUIBindings{
{MWInput::A_Use, "/user/hand/left/input/select/click"}, // Touch: X
{MWInput::A_GameMenu, "/user/hand/left/input/menu/click"}, // Touch: Y
{A_Recenter, "/user/hand/left/input/menu/click"}, // Touch: Y
{A_MenuSelect, "/user/hand/right/input/select/click"}, // Touch: A
{A_MenuBack, "/user/hand/right/input/menu/click"}, // Touch: B
};
mXRInput->suggestBindings(ActionSet::Gameplay, simpleProfilePath, simpleGameplayBindings);
mXRInput->suggestBindings(ActionSet::GUI, simpleProfilePath, simpleGUIBindings);
} }
void VRInputManager::suggestBindingsOculusTouch() std::string VRInputManager::requireAttribute(TiXmlElement* element, std::string attribute)
{ {
std::string controllerProfilePath = "/interaction_profiles/oculus/touch_controller"; const char* value = element->Attribute(attribute.c_str());
if (!value)
// In-game character controls throwDocumentError(element, std::string() + "Missing attribute '" + attribute + "'");
SuggestedBindings gameplayBindings{ return value;
{A_Recenter, "/user/hand/left/input/menu/click"},
{A_VrMetaMenu, "/user/hand/left/input/menu/click"},
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/value"},
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
{MWInput::A_ToggleSpell, "/user/hand/left/input/x/click"},
{MWInput::A_Rest, "/user/hand/left/input/y/click"},
{MWInput::A_ToggleWeapon, "/user/hand/right/input/a/click"},
{MWInput::A_Inventory, "/user/hand/right/input/b/click"},
{A_ActivateTouch, "/user/hand/right/input/squeeze/value"},
{MWInput::A_Activate, "/user/hand/right/input/squeeze/value"},
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
};
// GUI controls
SuggestedBindings GUIBindings{
{A_MenuUpDown, "/user/hand/right/input/thumbstick/y"},
{A_MenuLeftRight, "/user/hand/right/input/thumbstick/x"},
{A_MenuSelect, "/user/hand/right/input/a/click"},
{A_MenuBack, "/user/hand/right/input/b/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
{MWInput::A_GameMenu, "/user/hand/left/input/menu/click"},
{A_Recenter, "/user/hand/left/input/menu/click"},
};
mXRInput->suggestBindings(ActionSet::Gameplay, controllerProfilePath, gameplayBindings);
mXRInput->suggestBindings(ActionSet::GUI, controllerProfilePath, GUIBindings);
} }
void VRInputManager::suggestBindingsHpMixedReality() void VRInputManager::readInteractionProfile(TiXmlElement* element)
{ {
std::string controllerProfilePath = "/interaction_profiles/hp/mixed_reality_controller"; std::string interactionProfilePath = requireAttribute(element, "Path");
mInteractionProfileLocalNames[interactionProfilePath] = requireAttribute(element, "LocalName");
// In-game character controls Log(Debug::Verbose) << "Configuring interaction profile '" << interactionProfilePath << "' (" << mInteractionProfileLocalNames[interactionProfilePath] << ")";
SuggestedBindings gameplayBindings{
{A_Recenter, "/user/hand/left/input/menu/click"},
{A_VrMetaMenu, "/user/hand/left/input/menu/click"},
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/value"},
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
{MWInput::A_ToggleSpell, "/user/hand/left/input/x/click"},
{MWInput::A_Rest, "/user/hand/left/input/y/click"},
{MWInput::A_ToggleWeapon, "/user/hand/right/input/a/click"},
{MWInput::A_Inventory, "/user/hand/right/input/b/click"},
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
{A_ActivateTouch, "/user/hand/right/input/squeeze/value"},
{MWInput::A_Activate, "/user/hand/right/input/squeeze/value"},
};
// GUI controls // Check extension if present
SuggestedBindings GUIBindings{ TiXmlElement* extensionElement = element->FirstChildElement("Extension");
{A_Recenter, "/user/hand/left/input/menu/click"}, if (extensionElement)
{MWInput::A_GameMenu, "/user/hand/left/input/menu/click"}, {
{A_MenuUpDown, "/user/hand/right/input/thumbstick/y"}, std::string extension = requireAttribute(extensionElement, "Name");
{A_MenuLeftRight, "/user/hand/right/input/thumbstick/x"}, auto xr = MWVR::Environment::get().getManager();
{A_MenuSelect, "/user/hand/right/input/a/click"}, if (!xr->xrExtensionIsEnabled(XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME))
{A_MenuBack, "/user/hand/right/input/b/click"}, {
{MWInput::A_Use, "/user/hand/right/input/trigger/value"}, Log(Debug::Verbose) << " Required extension '" << extension << "' not supported. Skipping interaction profile.";
}; return;
}
}
mXRInput->suggestBindings(ActionSet::Gameplay, controllerProfilePath, gameplayBindings); TiXmlElement* actionSetGameplay = nullptr;
mXRInput->suggestBindings(ActionSet::GUI, controllerProfilePath, GUIBindings); TiXmlElement* actionSetGUI = nullptr;
TiXmlElement* child = element->FirstChildElement("ActionSet");
while (child)
{
std::string name = requireAttribute(child, "Name");
if (name == "Gameplay")
actionSetGameplay = child;
else if (name == "GUI")
actionSetGUI = child;
child = child->NextSiblingElement("ActionSet");
}
if (!actionSetGameplay)
throwDocumentError(element, "Gameplay action set missing");
if (!actionSetGUI)
throwDocumentError(element, "GUI action set missing");
readInteractionProfileActionSet(actionSetGameplay, ActionSet::Gameplay, interactionProfilePath);
readInteractionProfileActionSet(actionSetGUI, ActionSet::GUI, interactionProfilePath);
} }
void VRInputManager::suggestBindingsHuaweiController() void VRInputManager::readInteractionProfileActionSet(TiXmlElement* element, ActionSet actionSet, std::string interactionProfilePath)
{ {
std::string controllerProfilePath = "/interaction_profiles/huawei/controller"; SuggestedBindings suggestedBindings;
// In-game character controls TiXmlElement* child = element->FirstChildElement("Binding");
SuggestedBindings gameplayBindings{ while (child)
{A_Recenter, "/user/hand/left/input/home/click"}, {
{A_VrMetaMenu, "/user/hand/left/input/home/click"}, std::string action = requireAttribute(child, "ActionName");
{MWInput::A_Jump, "/user/hand/left/input/trigger/click"}, std::string path = requireAttribute(child, "Path");
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/trackpad/y"},
{MWInput::A_MoveLeftRight, "/user/hand/left/input/trackpad/x"},
{MWInput::A_ToggleSpell, "/user/hand/left/input/trackpad/click"},
{MWInput::A_Sneak, "/user/hand/left/input/back/click"},
{MWInput::A_LookLeftRight, "/user/hand/right/input/trackpad/x"},
{MWInput::A_ToggleWeapon, "/user/hand/right/input/trackpad/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/click"},
{A_ActivateTouch, "/user/hand/right/input/squeeze/click"},
{MWInput::A_Activate, "/user/hand/right/input/squeeze/click"},
};
// GUI controls suggestedBindings.push_back(
SuggestedBindings GUIBindings{ SuggestedBinding{
{A_MenuBack, "/user/hand/left/input/trackpad/click"}, path, action
{MWInput::A_GameMenu, "/user/hand/left/input/home/click"}, });
{A_Recenter, "/user/hand/left/input/home/click"},
{A_MenuUpDown, "/user/hand/right/input/thumbstick/y"},
{A_MenuLeftRight, "/user/hand/right/input/thumbstick/x"},
{A_MenuSelect, "/user/hand/right/input/trackpad/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/click"},
};
mXRInput->suggestBindings(ActionSet::Gameplay, controllerProfilePath, gameplayBindings); Log(Debug::Debug) << " " << action << ": " << path;
mXRInput->suggestBindings(ActionSet::GUI, controllerProfilePath, GUIBindings);
}
void VRInputManager::suggestBindingsMicrosoftMixedReality() child = child->NextSiblingElement("Binding");
{ }
std::string controllerProfilePath = "/interaction_profiles/microsoft/motion_controller";
// In-game character controls mXRInput->suggestBindings(actionSet, interactionProfilePath, suggestedBindings);
SuggestedBindings gameplayBindings{
{A_Recenter, "/user/hand/left/input/menu/click"},
{A_VrMetaMenu, "/user/hand/right/input/squeeze/click"},
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/trackpad/y"},
{MWInput::A_MoveLeftRight, "/user/hand/left/input/trackpad/x"},
{MWInput::A_Rest, "/user/hand/left/input/thumbstick/click"},
{MWInput::A_ToggleSpell, "/user/hand/left/input/trackpad/click"},
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/click"},
{MWInput::A_Inventory, "/user/hand/right/input/thumbstick/click"},
{MWInput::A_LookLeftRight, "/user/hand/right/input/trackpad/x"},
{MWInput::A_ToggleWeapon, "/user/hand/right/input/trackpad/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
{A_ActivateTouch, "/user/hand/right/input/squeeze/click"},
{MWInput::A_Activate, "/user/hand/right/input/squeeze/click"},
};
// GUI controls
SuggestedBindings GUIBindings{
{A_MenuBack, "/user/hand/left/input/trackpad/click"},
{MWInput::A_GameMenu, "/user/hand/left/input/menu/click"},
{A_Recenter, "/user/hand/left/input/menu/click"},
{A_MenuUpDown, "/user/hand/right/input/trackpad/y"},
{A_MenuLeftRight, "/user/hand/right/input/trackpad/x"},
{A_MenuSelect, "/user/hand/right/input/trackpad/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
};
mXRInput->suggestBindings(ActionSet::Gameplay, controllerProfilePath, gameplayBindings);
mXRInput->suggestBindings(ActionSet::GUI, controllerProfilePath, GUIBindings);
}
void VRInputManager::suggestBindingsIndex()
{
std::string controllerProfilePath = "/interaction_profiles/valve/index_controller";
// In-game character controls
SuggestedBindings gameplayBindings{
{MWInput::A_ToggleSpell, "/user/hand/left/input/a/click"},
{MWInput::A_Rest, "/user/hand/left/input/b/click"},
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
{A_Recenter, "/user/hand/left/input/trackpad/force"},
{A_VrMetaMenu, "/user/hand/left/input/trackpad/force"},
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/force"},
{MWInput::A_ToggleWeapon, "/user/hand/right/input/a/click"},
{MWInput::A_Inventory, "/user/hand/right/input/b/click"},
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
{A_ActivateTouch, "/user/hand/right/input/squeeze/force"},
{MWInput::A_Activate, "/user/hand/right/input/squeeze/force"},
};
// GUI controls
SuggestedBindings GUIBindings{
{A_Recenter, "/user/hand/left/input/thumbstick/click"},
{MWInput::A_GameMenu, "/user/hand/left/input/trackpad/force"},
{A_MenuSelect, "/user/hand/right/input/a/click"},
{A_MenuBack, "/user/hand/right/input/b/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
{A_MenuUpDown, "/user/hand/right/input/thumbstick/y"},
{A_MenuLeftRight, "/user/hand/right/input/thumbstick/x"},
};
mXRInput->suggestBindings(ActionSet::Gameplay, controllerProfilePath, gameplayBindings);
mXRInput->suggestBindings(ActionSet::GUI, controllerProfilePath, GUIBindings);
}
void VRInputManager::suggestBindingsVive()
{
std::string controllerProfilePath = "/interaction_profiles/htc/vive_controller";
// In-game character controls
SuggestedBindings gameplayBindings{
{A_Recenter, "/user/hand/left/input/menu/click"},
{A_VrMetaMenu, "/user/hand/left/input/menu/click"},
{A_VrMetaMenu, "/user/hand/right/input/squeeze/click"},
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/trackpad/y"},
{MWInput::A_MoveLeftRight, "/user/hand/left/input/trackpad/x"},
{MWInput::A_ToggleSpell, "/user/hand/left/input/trackpad/click"},
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/click"},
{MWInput::A_LookLeftRight, "/user/hand/right/input/trackpad/x"},
{MWInput::A_ToggleWeapon, "/user/hand/right/input/trackpad/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
{A_ActivateTouch, "/user/hand/right/input/squeeze/click"},
{MWInput::A_Activate, "/user/hand/right/input/squeeze/click"},
};
// GUI controls
SuggestedBindings GUIBindings{
{A_MenuUpDown, "/user/hand/right/input/trackpad/y"},
{A_MenuLeftRight, "/user/hand/right/input/trackpad/x"},
{A_MenuSelect, "/user/hand/right/input/trackpad/click"},
{A_MenuBack, "/user/hand/left/input/trackpad/click"},
{MWInput::A_GameMenu, "/user/hand/left/input/menu/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
{A_Recenter, "/user/hand/left/input/menu/click"},
};
mXRInput->suggestBindings(ActionSet::Gameplay, controllerProfilePath, gameplayBindings);
mXRInput->suggestBindings(ActionSet::GUI, controllerProfilePath, GUIBindings);
}
void VRInputManager::suggestBindingsViveCosmos()
{
std::string controllerProfilePath = "/interaction_profiles/htc/vive_cosmos_controller";
// In-game character controls
SuggestedBindings gameplayBindings{
{A_Recenter, "/user/hand/left/input/menu/click"},
{A_VrMetaMenu, "/user/hand/left/input/menu/click"},
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/value"},
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
{MWInput::A_Jump, "/user/hand/left/input/trigger/click"},
{MWInput::A_ToggleSpell, "/user/hand/left/input/x/click"},
{MWInput::A_Rest, "/user/hand/left/input/y/click"},
{MWInput::A_ToggleWeapon, "/user/hand/right/input/a/click"},
{MWInput::A_Inventory, "/user/hand/right/input/b/click"},
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/click"},
{A_ActivateTouch, "/user/hand/right/input/squeeze/value"},
{MWInput::A_Activate, "/user/hand/right/input/squeeze/value"},
};
// GUI controls
SuggestedBindings GUIBindings{
{A_Recenter, "/user/hand/left/input/menu/click"},
{MWInput::A_GameMenu, "/user/hand/left/input/menu/click"},
{A_MenuUpDown, "/user/hand/right/input/thumbstick/y"},
{A_MenuLeftRight, "/user/hand/right/input/thumbstick/x"},
{A_MenuSelect, "/user/hand/right/input/a/click"},
{A_MenuBack, "/user/hand/right/input/b/click"},
{MWInput::A_Use, "/user/hand/right/input/trigger/click"},
};
mXRInput->suggestBindings(ActionSet::Gameplay, controllerProfilePath, gameplayBindings);
mXRInput->suggestBindings(ActionSet::GUI, controllerProfilePath, GUIBindings);
}
void VRInputManager::suggestBindingsXboxController()
{
//TODO
} }
void VRInputManager::requestRecenter() void VRInputManager::requestRecenter()
@ -497,7 +286,8 @@ namespace MWVR
bool userFileExists, bool userFileExists,
const std::string& userControllerBindingsFile, const std::string& userControllerBindingsFile,
const std::string& controllerBindingsFile, const std::string& controllerBindingsFile,
bool grab) bool grab,
const std::string& xrControllerSuggestionsFile)
: MWInput::InputManager( : MWInput::InputManager(
window, window,
viewer, viewer,
@ -509,23 +299,46 @@ namespace MWVR
controllerBindingsFile, controllerBindingsFile,
grab) grab)
, mXRInput(new OpenXRInput) , mXRInput(new OpenXRInput)
, mXrControllerSuggestionsFile(xrControllerSuggestionsFile)
, mHapticsEnabled{ Settings::Manager::getBool("haptics enabled", "VR") } , mHapticsEnabled{ Settings::Manager::getBool("haptics enabled", "VR") }
{ {
auto xr = MWVR::Environment::get().getManager(); if (xrControllerSuggestionsFile.empty())
throw std::runtime_error("No interaction profiles available (xrcontrollersuggestions.xml not found)");
suggestBindingsSimple(); Log(Debug::Verbose) << "Reading Input Profile Path suggestions from " << xrControllerSuggestionsFile;
suggestBindingsOculusTouch();
suggestBindingsMicrosoftMixedReality();
suggestBindingsIndex();
suggestBindingsVive();
suggestBindingsXboxController();
if (xr->xrExtensionIsEnabled(XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME)) TiXmlDocument* xmlDoc = nullptr;
suggestBindingsHpMixedReality(); TiXmlElement* xmlRoot = nullptr;
if (xr->xrExtensionIsEnabled(XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME))
suggestBindingsHuaweiController(); xmlDoc = new TiXmlDocument(xrControllerSuggestionsFile.c_str());
if (xr->xrExtensionIsEnabled(XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME)) xmlDoc->LoadFile();
suggestBindingsViveCosmos();
if (xmlDoc->Error())
{
std::ostringstream message;
message << "TinyXml reported an error reading \"" + xrControllerSuggestionsFile + "\". Row " <<
(int)xmlDoc->ErrorRow() << ", Col " << (int)xmlDoc->ErrorCol() << ": " <<
xmlDoc->ErrorDesc();
Log(Debug::Error) << message.str();
throw std::runtime_error(message.str());
delete xmlDoc;
return;
}
xmlRoot = xmlDoc->RootElement();
if (std::string(xmlRoot->Value()) != "Root") {
Log(Debug::Verbose) << "Error: Invalid xr controllers file. Missing <Root> element.";
delete xmlDoc;
return;
}
TiXmlElement* profile = xmlRoot->FirstChildElement("Profile");
while (profile)
{
readInteractionProfile(profile);
profile = profile->NextSiblingElement("Profile");
}
mXRInput->attachActionSets(); mXRInput->attachActionSets();
} }

View file

@ -2,6 +2,7 @@
#define VR_INPUT_MANAGER_HPP #define VR_INPUT_MANAGER_HPP
#include "vrtypes.hpp" #include "vrtypes.hpp"
#include "vrinput.hpp"
#include "../mwinput/inputmanagerimp.hpp" #include "../mwinput/inputmanagerimp.hpp"
@ -11,6 +12,8 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
class TiXmlElement;
namespace MWVR namespace MWVR
{ {
struct OpenXRInput; struct OpenXRInput;
@ -31,7 +34,8 @@ namespace MWVR
osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation, osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
const std::string& userFile, bool userFileExists, const std::string& userFile, bool userFileExists,
const std::string& userControllerBindingsFile, const std::string& userControllerBindingsFile,
const std::string& controllerBindingsFile, bool grab); const std::string& controllerBindingsFile, bool grab,
const std::string& xrControllerSuggestionsFile);
virtual ~VRInputManager(); virtual ~VRInputManager();
@ -50,6 +54,9 @@ namespace MWVR
/// Currently active action set /// Currently active action set
OpenXRActionSet& activeActionSet(); OpenXRActionSet& activeActionSet();
/// Notify input manager that the active interaction profile has changed
void notifyInteractionProfileChanged();
protected: protected:
void processAction(const class Action* action, float dt, bool disableControls); void processAction(const class Action* action, float dt, bool disableControls);
@ -63,21 +70,19 @@ namespace MWVR
void applyHapticsRightHand(float intensity) override; void applyHapticsRightHand(float intensity) override;
void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed) override; void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed) override;
private: void throwDocumentError(TiXmlElement* element, std::string error);
void suggestBindingsSimple(); std::string requireAttribute(TiXmlElement* element, std::string attribute);
void suggestBindingsOculusTouch(); void readInteractionProfile(TiXmlElement* element);
void suggestBindingsHpMixedReality(); void readInteractionProfileActionSet(TiXmlElement* element, ActionSet actionSet, std::string profilePath);
void suggestBindingsHuaweiController();
void suggestBindingsIndex();
void suggestBindingsMicrosoftMixedReality();
void suggestBindingsVive();
void suggestBindingsViveCosmos();
void suggestBindingsXboxController();
private:
std::unique_ptr<OpenXRInput> mXRInput; std::unique_ptr<OpenXRInput> mXRInput;
std::unique_ptr<RealisticCombat::StateMachine> mRealisticCombat; std::unique_ptr<RealisticCombat::StateMachine> mRealisticCombat;
std::string mXrControllerSuggestionsFile;
bool mActivationIndication{ false }; bool mActivationIndication{ false };
bool mHapticsEnabled{ true }; bool mHapticsEnabled{ true };
std::map<std::string, std::string> mInteractionProfileLocalNames;
}; };
} }

View file

@ -0,0 +1,260 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2020 Mads Buvik Sandvei <madssandvei@protonmail.com>
Instructions:
To edit this file. Copy it to my games/openmw/ (or equivalent for your platform)
Consult the openxr specification for details of what Paths are available for your controllers:
https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-interaction-profiles
Note that some controllers require OpenXR extensions. In which case consult the relevant extension section
of the openxr spec instead of the linked section.
You will not make an interaction profile work by removing the extension requirement form this file.
-->
<Root>
<Actions>
<!-- The <Actions> section exists for reference only and is not processed by OpenMW VR-->
<!-- Note that some actions, such as menu_*, only take affect in GUI mode and vice versa -->
<Action Name="game_menu" LocalName="Gameplay Game Menu"/>
<Action Name="meta_menu" LocalName="Gameplay Meta Menu"/>
<Action Name="reposition_menu" LocalName="Gameplay Reposition Menu"/>
<Action Name="inventory" LocalName="Gameplay Inventory"/>
<Action Name="activate" LocalName="Gameplay Activate"/>
<Action Name="use" LocalName="Gameplay Use"/>
<Action Name="jump" LocalName="Gameplay Jump"/>
<Action Name="weapon" LocalName="Gameplay Weapon"/>
<Action Name="spell" LocalName="Gameplay Spell"/>
<Action Name="cycle_spell_left" LocalName="Gameplay Cycle Spell Left"/>
<Action Name="cycle_spell_right" LocalName="Gameplay Cycle Spell Right"/>
<Action Name="cycle_weapon_left" LocalName="Gameplay Cycle Weapon Left"/>
<Action Name="cycle_weapon_right" LocalName="Gameplay Cycle Weapon Right"/>
<Action Name="sneak" LocalName="Gameplay Sneak"/>
<Action Name="quick_menu" LocalName="Gameplay Quick Menu"/>
<Action Name="look_left_right" LocalName="Gameplay Look Left Right"/>
<Action Name="move_forward_backward" LocalName="Gameplay Move Forward Backward"/>
<Action Name="move_left_right" LocalName="Gameplay Move Left Right"/>
<Action Name="journal_book" LocalName="Gameplay Journal Book"/>
<Action Name="quick_save" LocalName="Gameplay Quick Save"/>
<Action Name="rest" LocalName="Gameplay Rest"/>
<Action Name="activate_touched" LocalName="Gameplay Activate Touch"/>
<Action Name="always_run" LocalName="Gameplay Always Run"/>
<Action Name="auto_move" LocalName="Gameplay Auto Move"/>
<Action Name="toggle_hud" LocalName="Gameplay Toggle HUD"/>
<Action Name="toggle_debug" LocalName="Gameplay Toggle the debug hud"/>
<Action Name="menu_up_down" LocalName="Gameplay Menu Up Down"/>
<Action Name="menu_left_right" LocalName="Gameplay Menu Left Right"/>
<Action Name="menu_select" LocalName="Gameplay Menu Select"/>
<Action Name="menu_back" LocalName="Gameplay Menu Back"/>
</Actions>
<Profile LocalName="Generic" Path="/interaction_profiles/khr/simple_controller">
<ActionSet Name="Gameplay">
<Binding ActionName="use" Path="/user/hand/left/input/select/click"/>
<Binding ActionName="meta_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="activate_touched" Path="/user/hand/right/input/select/click"/>
<Binding ActionName="auto_move" Path="/user/hand/right/input/menu/click"/>
</ActionSet>
<ActionSet Name="GUI">
<Binding ActionName="use" Path="/user/hand/left/input/select/click"/>
<Binding ActionName="game_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="menu_select" Path="/user/hand/right/input/select/click"/>
<Binding ActionName="menu_back" Path="/user/hand/right/input/menu/click"/>
</ActionSet>
</Profile>
<Profile LocalName="Oculus Touch Controllers" Path="/interaction_profiles/oculus/touch_controller">
<ActionSet Name="Gameplay">
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="meta_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="sneak" Path="/user/hand/left/input/squeeze/value"/>
<Binding ActionName="move_left_right" Path="/user/hand/left/input/thumbstick/x"/>
<Binding ActionName="move_forward_backward" Path="/user/hand/left/input/thumbstick/y"/>
<Binding ActionName="always_run" Path="/user/hand/left/input/thumbstick/click"/>
<Binding ActionName="jump" Path="/user/hand/left/input/trigger/value"/>
<Binding ActionName="spell" Path="/user/hand/left/input/x/click"/>
<Binding ActionName="rest" Path="/user/hand/left/input/y/click"/>
<Binding ActionName="weapon" Path="/user/hand/right/input/a/click"/>
<Binding ActionName="inventory" Path="/user/hand/right/input/b/click"/>
<Binding ActionName="activate_touched" Path="/user/hand/right/input/squeeze/value"/>
<Binding ActionName="activate" Path="/user/hand/right/input/squeeze/value"/>
<Binding ActionName="look_left_right" Path="/user/hand/right/input/thumbstick/x"/>
<Binding ActionName="auto_move" Path="/user/hand/right/input/thumbstick/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/value"/>
</ActionSet>
<ActionSet Name="GUI">
<Binding ActionName="menu_up_down" Path="/user/hand/right/input/thumbstick/y"/>
<Binding ActionName="menu_left_right" Path="/user/hand/right/input/thumbstick/x"/>
<Binding ActionName="menu_select" Path="/user/hand/right/input/a/click"/>
<Binding ActionName="menu_back" Path="/user/hand/right/input/b/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/value"/>
<Binding ActionName="game_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
</ActionSet>
</Profile>
<Profile LocalName="Microsoft Motion Controllers" Path="/interaction_profiles/microsoft/motion_controller">
<ActionSet Name="Gameplay">
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="meta_menu" Path="/user/hand/right/input/squeeze/click"/>
<Binding ActionName="jump" Path="/user/hand/left/input/trigger/value"/>
<Binding ActionName="move_forward_backward" Path="/user/hand/left/input/trackpad/y"/>
<Binding ActionName="move_left_right" Path="/user/hand/left/input/trackpad/x"/>
<Binding ActionName="rest" Path="/user/hand/left/input/thumbstick/click"/>
<Binding ActionName="spell" Path="/user/hand/left/input/trackpad/click"/>
<Binding ActionName="sneak" Path="/user/hand/left/input/squeeze/click"/>
<Binding ActionName="inventory" Path="/user/hand/right/input/thumbstick/click"/>
<Binding ActionName="look_left_right" Path="/user/hand/right/input/trackpad/x"/>
<Binding ActionName="weapon" Path="/user/hand/right/input/trackpad/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/value"/>
<Binding ActionName="activate_touched" Path="/user/hand/right/input/squeeze/click"/>
<Binding ActionName="activate" Path="/user/hand/right/input/squeeze/click"/>
</ActionSet>
<ActionSet Name="GUI">
<Binding ActionName="menu_back" Path="/user/hand/left/input/trackpad/click"/>
<Binding ActionName="game_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="menu_up_down" Path="/user/hand/right/input/trackpad/y"/>
<Binding ActionName="menu_left_right" Path="/user/hand/right/input/trackpad/x"/>
<Binding ActionName="menu_select" Path="/user/hand/right/input/trackpad/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/value"/>
</ActionSet>
</Profile>
<Profile LocalName="Index Knuckles" Path="/interaction_profiles/valve/index_controller">
<ActionSet Name="Gameplay">
<Binding ActionName="spell" Path="/user/hand/left/input/a/click"/>
<Binding ActionName="rest" Path="/user/hand/left/input/b/click"/>
<Binding ActionName="move_forward_backward" Path="/user/hand/left/input/thumbstick/y"/>
<Binding ActionName="move_left_right" Path="/user/hand/left/input/thumbstick/x"/>
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/trackpad/force"/>
<Binding ActionName="meta_menu" Path="/user/hand/left/input/trackpad/force"/>
<Binding ActionName="jump" Path="/user/hand/left/input/trigger/value"/>
<Binding ActionName="sneak" Path="/user/hand/left/input/squeeze/force"/>
<Binding ActionName="weapon" Path="/user/hand/right/input/a/click"/>
<Binding ActionName="inventory" Path="/user/hand/right/input/b/click"/>
<Binding ActionName="look_left_right" Path="/user/hand/right/input/thumbstick/x"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/value"/>
<Binding ActionName="activate_touched" Path="/user/hand/right/input/squeeze/force"/>
<Binding ActionName="activate" Path="/user/hand/right/input/squeeze/force"/>
</ActionSet>
<ActionSet Name="GUI">
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/thumbstick/click"/>
<Binding ActionName="game_menu" Path="/user/hand/left/input/trackpad/force"/>
<Binding ActionName="menu_select" Path="/user/hand/right/input/a/click"/>
<Binding ActionName="menu_back" Path="/user/hand/right/input/b/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/value"/>
<Binding ActionName="menu_up_down" Path="/user/hand/right/input/thumbstick/y"/>
<Binding ActionName="menu_left_right" Path="/user/hand/right/input/thumbstick/x"/>
</ActionSet>
</Profile>
<Profile LocalName="Vive Wands" Path="/interaction_profiles/htc/vive_controller">
<ActionSet Name="Gameplay">
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="meta_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="meta_menu" Path="/user/hand/right/input/squeeze/click"/>
<Binding ActionName="move_forward_backward" Path="/user/hand/left/input/trackpad/y"/>
<Binding ActionName="move_left_right" Path="/user/hand/left/input/trackpad/x"/>
<Binding ActionName="spell" Path="/user/hand/left/input/trackpad/click"/>
<Binding ActionName="jump" Path="/user/hand/left/input/trigger/value"/>
<Binding ActionName="sneak" Path="/user/hand/left/input/squeeze/click"/>
<Binding ActionName="look_left_right" Path="/user/hand/right/input/trackpad/x"/>
<Binding ActionName="weapon" Path="/user/hand/right/input/trackpad/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/value"/>
<Binding ActionName="activate_touched" Path="/user/hand/right/input/squeeze/click"/>
<Binding ActionName="activate" Path="/user/hand/right/input/squeeze/click"/>
</ActionSet>
<ActionSet Name="GUI">
<Binding ActionName="menu_up_down" Path="/user/hand/right/input/trackpad/y"/>
<Binding ActionName="menu_left_right" Path="/user/hand/right/input/trackpad/x"/>
<Binding ActionName="menu_select" Path="/user/hand/right/input/trackpad/click"/>
<Binding ActionName="menu_back" Path="/user/hand/left/input/trackpad/click"/>
<Binding ActionName="game_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/value"/>
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
</ActionSet>
</Profile>
<Profile LocalName="Hp Mixed Reality Controllers" Path="/interaction_profiles/hp/mixed_reality_controller">
<Extension Name="XR_EXT_hp_mixed_reality_controller"/>
<ActionSet Name="Gameplay">
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="meta_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="sneak" Path="/user/hand/left/input/squeeze/value"/>
<Binding ActionName="move_forward_backward" Path="/user/hand/left/input/thumbstick/y"/>
<Binding ActionName="move_left_right" Path="/user/hand/left/input/thumbstick/x"/>
<Binding ActionName="always_run" Path="/user/hand/left/input/thumbstick/click"/>
<Binding ActionName="jump" Path="/user/hand/left/input/trigger/value"/>
<Binding ActionName="spell" Path="/user/hand/left/input/x/click"/>
<Binding ActionName="rest" Path="/user/hand/left/input/y/click"/>
<Binding ActionName="weapon" Path="/user/hand/right/input/a/click"/>
<Binding ActionName="inventory" Path="/user/hand/right/input/b/click"/>
<Binding ActionName="look_left_right" Path="/user/hand/right/input/thumbstick/x"/>
<Binding ActionName="auto_move" Path="/user/hand/right/input/thumbstick/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/value"/>
<Binding ActionName="activate_touched" Path="/user/hand/right/input/squeeze/value"/>
<Binding ActionName="activate" Path="/user/hand/right/input/squeeze/value"/>
</ActionSet>
<ActionSet Name="GUI">
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="game_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="menu_up_down" Path="/user/hand/right/input/thumbstick/y"/>
<Binding ActionName="menu_left_right" Path="/user/hand/right/input/thumbstick/x"/>
<Binding ActionName="menu_select" Path="/user/hand/right/input/a/click"/>
<Binding ActionName="menu_back" Path="/user/hand/right/input/b/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/value"/>
</ActionSet>
</Profile>
<Profile LocalName="Huawei Controllers" Path="/interaction_profiles/huawei/controller">
<Extension Name="XR_HUAWEI_controller_interaction"/>
<ActionSet Name="Gameplay">
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/home/click"/>
<Binding ActionName="meta_menu" Path="/user/hand/left/input/home/click"/>
<Binding ActionName="jump" Path="/user/hand/left/input/trigger/click"/>
<Binding ActionName="move_forward_backward" Path="/user/hand/left/input/trackpad/y"/>
<Binding ActionName="move_left_right" Path="/user/hand/left/input/trackpad/x"/>
<Binding ActionName="spell" Path="/user/hand/left/input/trackpad/click"/>
<Binding ActionName="sneak" Path="/user/hand/left/input/back/click"/>
<Binding ActionName="look_left_right" Path="/user/hand/right/input/trackpad/x"/>
<Binding ActionName="weapon" Path="/user/hand/right/input/trackpad/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/click"/>
<Binding ActionName="activate_touched" Path="/user/hand/right/input/squeeze/click"/>
<Binding ActionName="activate" Path="/user/hand/right/input/squeeze/click"/>
</ActionSet>
<ActionSet Name="GUI">
<Binding ActionName="menu_back" Path="/user/hand/left/input/trackpad/click"/>
<Binding ActionName="game_menu" Path="/user/hand/left/input/home/click"/>
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/home/click"/>
<Binding ActionName="menu_up_down" Path="/user/hand/right/input/thumbstick/y"/>
<Binding ActionName="menu_left_right" Path="/user/hand/right/input/thumbstick/x"/>
<Binding ActionName="menu_select" Path="/user/hand/right/input/trackpad/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/click"/>
</ActionSet>
</Profile>
<Profile LocalName="Vive Cosmos Controllers" Path="/interaction_profiles/htc/vive_cosmos_controller">
<Extension Name="XR_HTC_vive_cosmos_controller_interaction"/>
<ActionSet Name="Gameplay">
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="meta_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="sneak" Path="/user/hand/left/input/squeeze/value"/>
<Binding ActionName="move_forward_backward" Path="/user/hand/left/input/thumbstick/y"/>
<Binding ActionName="move_left_right" Path="/user/hand/left/input/thumbstick/x"/>
<Binding ActionName="always_run" Path="/user/hand/left/input/thumbstick/click"/>
<Binding ActionName="jump" Path="/user/hand/left/input/trigger/click"/>
<Binding ActionName="spell" Path="/user/hand/left/input/x/click"/>
<Binding ActionName="rest" Path="/user/hand/left/input/y/click"/>
<Binding ActionName="weapon" Path="/user/hand/right/input/a/click"/>
<Binding ActionName="inventory" Path="/user/hand/right/input/b/click"/>
<Binding ActionName="look_left_right" Path="/user/hand/right/input/thumbstick/x"/>
<Binding ActionName="auto_move" Path="/user/hand/right/input/thumbstick/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/click"/>
<Binding ActionName="activate_touched" Path="/user/hand/right/input/squeeze/value"/>
<Binding ActionName="activate" Path="/user/hand/right/input/squeeze/value"/>
</ActionSet>
<ActionSet Name="GUI">
<Binding ActionName="reposition_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="game_menu" Path="/user/hand/left/input/menu/click"/>
<Binding ActionName="menu_up_down" Path="/user/hand/right/input/thumbstick/y"/>
<Binding ActionName="menu_left_right" Path="/user/hand/right/input/thumbstick/x"/>
<Binding ActionName="menu_select" Path="/user/hand/right/input/a/click"/>
<Binding ActionName="menu_back" Path="/user/hand/right/input/b/click"/>
<Binding ActionName="use" Path="/user/hand/right/input/trigger/click"/>
</ActionSet>
</Profile>
</Root>