Load interaction profile suggestions from xml instead of hardcoding them.

pull/615/head
Mads Buvik Sandvei 4 years ago
parent c3418a45ae
commit e42dada208

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

@ -718,10 +718,29 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
mEnvironment.setWindowManager (window);
MWInput::InputManager* input =
#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
MWInput::InputManager* input =
new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
#endif
mEnvironment.setInputManager (input);

@ -120,7 +120,8 @@ namespace MWVR
const std::string& actionName,
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

@ -43,7 +43,7 @@ namespace MWVR
XrActionSet mActionSet{ nullptr };
std::string mLocalizedName{};
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<HapticsAction>> mHapticsMap;
std::deque<const Action*> mActionQueue{};

@ -28,9 +28,9 @@ namespace MWVR
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()
@ -48,6 +48,8 @@ namespace MWVR
xrProfileSuggestedBindings.suggestedBindings = profile.second.data();
xrProfileSuggestedBindings.countSuggestedBindings = (uint32_t)profile.second.size();
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.
@ -62,4 +64,49 @@ namespace MWVR
attachInfo.actionSets = actionSets.data();
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;
}
}
}
}
}

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

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

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

@ -35,6 +35,7 @@
#include "../mwrender/camera.hpp"
#include <extern/oics/ICSInputControlSystem.h>
#include <extern/oics/tinyxml.h>
#include <iostream>
@ -57,6 +58,11 @@ namespace MWVR
return mXRInput->getActionSet(ActionSet::Gameplay);
}
void VRInputManager::notifyInteractionProfileChanged()
{
mXRInput->notifyInteractionProfileChanged();
}
void VRInputManager::updateActivationIndication(void)
{
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.
*/
@ -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";
// Set up default bindings for the khronos simple controller.
// Note: The simple controller is the equivalent to a universal "default".
// It has highly reduced functionality. Only poses and two click actions
// 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);
std::stringstream ss;
ss << mXrControllerSuggestionsFile << "." << element->Row() << "." << element->Value();
ss << ": " << error;
throw std::runtime_error(ss.str());
}
void VRInputManager::suggestBindingsOculusTouch()
std::string VRInputManager::requireAttribute(TiXmlElement* element, std::string attribute)
{
std::string controllerProfilePath = "/interaction_profiles/oculus/touch_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_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);
const char* value = element->Attribute(attribute.c_str());
if (!value)
throwDocumentError(element, std::string() + "Missing attribute '" + attribute + "'");
return value;
}
void VRInputManager::suggestBindingsHpMixedReality()
void VRInputManager::readInteractionProfile(TiXmlElement* element)
{
std::string controllerProfilePath = "/interaction_profiles/hp/mixed_reality_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/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
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/value"},
};
mXRInput->suggestBindings(ActionSet::Gameplay, controllerProfilePath, gameplayBindings);
mXRInput->suggestBindings(ActionSet::GUI, controllerProfilePath, GUIBindings);
}
std::string interactionProfilePath = requireAttribute(element, "Path");
mInteractionProfileLocalNames[interactionProfilePath] = requireAttribute(element, "LocalName");
void VRInputManager::suggestBindingsHuaweiController()
{
std::string controllerProfilePath = "/interaction_profiles/huawei/controller";
// In-game character controls
SuggestedBindings gameplayBindings{
{A_Recenter, "/user/hand/left/input/home/click"},
{A_VrMetaMenu, "/user/hand/left/input/home/click"},
{MWInput::A_Jump, "/user/hand/left/input/trigger/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_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 GUIBindings{
{A_MenuBack, "/user/hand/left/input/trackpad/click"},
{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);
mXRInput->suggestBindings(ActionSet::GUI, controllerProfilePath, GUIBindings);
}
Log(Debug::Verbose) << "Configuring interaction profile '" << interactionProfilePath << "' (" << mInteractionProfileLocalNames[interactionProfilePath] << ")";
void VRInputManager::suggestBindingsMicrosoftMixedReality()
{
std::string controllerProfilePath = "/interaction_profiles/microsoft/motion_controller";
// In-game character controls
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);
}
// Check extension if present
TiXmlElement* extensionElement = element->FirstChildElement("Extension");
if (extensionElement)
{
std::string extension = requireAttribute(extensionElement, "Name");
auto xr = MWVR::Environment::get().getManager();
if (!xr->xrExtensionIsEnabled(XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME))
{
Log(Debug::Verbose) << " Required extension '" << extension << "' not supported. Skipping interaction profile.";
return;
}
}
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);
}
TiXmlElement* actionSetGameplay = nullptr;
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;
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);
}
child = child->NextSiblingElement("ActionSet");
}
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);
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::suggestBindingsXboxController()
void VRInputManager::readInteractionProfileActionSet(TiXmlElement* element, ActionSet actionSet, std::string interactionProfilePath)
{
//TODO
SuggestedBindings suggestedBindings;
TiXmlElement* child = element->FirstChildElement("Binding");
while (child)
{
std::string action = requireAttribute(child, "ActionName");
std::string path = requireAttribute(child, "Path");
suggestedBindings.push_back(
SuggestedBinding{
path, action
});
Log(Debug::Debug) << " " << action << ": " << path;
child = child->NextSiblingElement("Binding");
}
mXRInput->suggestBindings(actionSet, interactionProfilePath, suggestedBindings);
}
void VRInputManager::requestRecenter()
@ -497,7 +286,8 @@ namespace MWVR
bool userFileExists,
const std::string& userControllerBindingsFile,
const std::string& controllerBindingsFile,
bool grab)
bool grab,
const std::string& xrControllerSuggestionsFile)
: MWInput::InputManager(
window,
viewer,
@ -509,23 +299,46 @@ namespace MWVR
controllerBindingsFile,
grab)
, mXRInput(new OpenXRInput)
, mXrControllerSuggestionsFile(xrControllerSuggestionsFile)
, mHapticsEnabled{ Settings::Manager::getBool("haptics enabled", "VR") }
{
auto xr = MWVR::Environment::get().getManager();
suggestBindingsSimple();
suggestBindingsOculusTouch();
suggestBindingsMicrosoftMixedReality();
suggestBindingsIndex();
suggestBindingsVive();
suggestBindingsXboxController();
if (xr->xrExtensionIsEnabled(XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME))
suggestBindingsHpMixedReality();
if (xr->xrExtensionIsEnabled(XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME))
suggestBindingsHuaweiController();
if (xr->xrExtensionIsEnabled(XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME))
suggestBindingsViveCosmos();
if (xrControllerSuggestionsFile.empty())
throw std::runtime_error("No interaction profiles available (xrcontrollersuggestions.xml not found)");
Log(Debug::Verbose) << "Reading Input Profile Path suggestions from " << xrControllerSuggestionsFile;
TiXmlDocument* xmlDoc = nullptr;
TiXmlElement* xmlRoot = nullptr;
xmlDoc = new TiXmlDocument(xrControllerSuggestionsFile.c_str());
xmlDoc->LoadFile();
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();
}

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

@ -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>
Loading…
Cancel
Save