mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-28 13:06:43 +00:00
commit d8564b8e501c98fa2e3cde582b8d06d7c78ba6ce Author: Mads Buvik Sandvei <madssandvei@protonmail.com> Date: Fri Oct 16 23:43:39 2020 +0200 bad assignment commit 84f66e4bf1050ce8a316a27f8b10dc2243e35406 Author: Mads Buvik Sandvei <madssandvei@protonmail.com> Date: Fri Oct 16 21:08:27 2020 +0200 Removed the approach of abstracting xr paths with enums. It is not turning out to be useful. Use the explicit paths instead. Added some default bindings for most currently available controllers, except the xbox controller. commit ae525d0a239c087a7344528634a078e0812af66d Author: Mads Buvik Sandvei <madssandvei@protonmail.com> Date: Fri Oct 16 21:05:37 2020 +0200 Cleaned up openxr extensions code. Upgraded openxr to version 1.0.12 to enable support for certain controllers. commit 2d71a5ecbf699c59f1fcdbebcad867fd28552929 Author: Mads Buvik Sandvei <madssandvei@protonmail.com> Date: Thu Sep 24 22:18:25 2020 +0200 simple_controller
893 lines
39 KiB
C++
893 lines
39 KiB
C++
#include "vrinputmanager.hpp"
|
|
|
|
#include "vrviewer.hpp"
|
|
#include "vrgui.hpp"
|
|
#include "vranimation.hpp"
|
|
#include "openxrinput.hpp"
|
|
#include "vrenvironment.hpp"
|
|
#include "openxrmanager.hpp"
|
|
#include "openxrmanagerimpl.hpp"
|
|
#include "openxraction.hpp"
|
|
#include "realisticcombat.hpp"
|
|
|
|
#include <components/debug/debuglog.hpp>
|
|
|
|
#include <MyGUI_InputManager.h>
|
|
|
|
#include "../mwbase/world.hpp"
|
|
#include "../mwbase/windowmanager.hpp"
|
|
#include "../mwbase/statemanager.hpp"
|
|
#include "../mwbase/environment.hpp"
|
|
#include "../mwbase/mechanicsmanager.hpp"
|
|
|
|
#include "../mwgui/draganddrop.hpp"
|
|
|
|
#include "../mwinput/actionmanager.hpp"
|
|
#include "../mwinput/bindingsmanager.hpp"
|
|
#include "../mwinput/mousemanager.hpp"
|
|
#include "../mwinput/sdlmappings.hpp"
|
|
|
|
#include "../mwworld/player.hpp"
|
|
#include "../mwmechanics/actorutil.hpp"
|
|
|
|
#include "../mwrender/renderingmanager.hpp"
|
|
#include "../mwrender/camera.hpp"
|
|
|
|
#include <extern/oics/ICSInputControlSystem.h>
|
|
|
|
#include <iostream>
|
|
|
|
namespace MWVR
|
|
{
|
|
|
|
Pose VRInputManager::getLimbPose(int64_t time, TrackedLimb limb)
|
|
{
|
|
return activeActionSet().getLimbPose(time, limb);
|
|
}
|
|
|
|
OpenXRActionSet& VRInputManager::activeActionSet()
|
|
{
|
|
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
|
guiMode = guiMode || (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame);
|
|
if (guiMode)
|
|
{
|
|
return mXRInput->getActionSet(ActionSet::GUI);
|
|
}
|
|
return mXRInput->getActionSet(ActionSet::Gameplay);
|
|
}
|
|
|
|
void VRInputManager::updateActivationIndication(void)
|
|
{
|
|
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
|
bool show = guiMode | mActivationIndication;
|
|
auto* playerAnimation = Environment::get().getPlayerAnimation();
|
|
if (playerAnimation)
|
|
{
|
|
playerAnimation->setFingerPointingMode(show);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Makes it possible to use ItemModel::moveItem to move an item from an inventory to the world.
|
|
*/
|
|
class DropItemAtPointModel : public MWGui::ItemModel
|
|
{
|
|
public:
|
|
DropItemAtPointModel() {}
|
|
virtual ~DropItemAtPointModel() {}
|
|
virtual MWWorld::Ptr copyItem(const MWGui::ItemStack& item, size_t count, bool /*allowAutoEquip*/)
|
|
{
|
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
|
MWVR::VRAnimation* anim = MWVR::Environment::get().getPlayerAnimation();
|
|
|
|
MWWorld::Ptr dropped;
|
|
if (anim->canPlaceObject())
|
|
dropped = world->placeObject(item.mBase, anim->getPointerTarget(), count);
|
|
else
|
|
dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count);
|
|
dropped.getCellRef().setOwner("");
|
|
|
|
return dropped;
|
|
}
|
|
|
|
virtual void removeItem(const MWGui::ItemStack& item, size_t count) { throw std::runtime_error("removeItem not implemented"); }
|
|
virtual ModelIndex getIndex(MWGui::ItemStack item) { throw std::runtime_error("getIndex not implemented"); }
|
|
virtual void update() {}
|
|
virtual size_t getItemCount() { return 0; }
|
|
virtual MWGui::ItemStack getItem(ModelIndex index) { throw std::runtime_error("getItem not implemented"); }
|
|
|
|
private:
|
|
// Where to drop the item
|
|
MWRender::RayResult mIntersection;
|
|
};
|
|
|
|
void VRInputManager::pointActivation(bool onPress)
|
|
{
|
|
auto* world = MWBase::Environment::get().getWorld();
|
|
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
|
if (world && anim && anim->getPointerTarget().mHit)
|
|
{
|
|
auto* node = anim->getPointerTarget().mHitNode;
|
|
MWWorld::Ptr ptr = anim->getPointerTarget().mHitObject;
|
|
auto& dnd = MWBase::Environment::get().getWindowManager()->getDragAndDrop();
|
|
|
|
if (node && node->getName() == "VRGUILayer")
|
|
{
|
|
injectMousePress(SDL_BUTTON_LEFT, onPress);
|
|
}
|
|
else if (onPress)
|
|
{
|
|
// Other actions should only happen on release;
|
|
return;
|
|
}
|
|
else if (dnd.mIsOnDragAndDrop)
|
|
{
|
|
// Intersected with the world while drag and drop is active
|
|
// Drop item into the world
|
|
MWBase::Environment::get().getWorld()->breakInvisibility(
|
|
MWMechanics::getPlayer());
|
|
DropItemAtPointModel drop;
|
|
dnd.drop(&drop, nullptr);
|
|
}
|
|
else if (!ptr.isEmpty())
|
|
{
|
|
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
|
|
player.activate(ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VRInputManager::injectMousePress(int sdlButton, bool onPress)
|
|
{
|
|
SDL_MouseButtonEvent arg;
|
|
if (onPress)
|
|
mMouseManager->mousePressed(arg, sdlButton);
|
|
else
|
|
mMouseManager->mouseReleased(arg, sdlButton);
|
|
}
|
|
|
|
void VRInputManager::injectChannelValue(
|
|
MWInput::Actions action,
|
|
float value)
|
|
{
|
|
auto channel = mBindingsManager->ics().getChannel(MWInput::A_MoveLeftRight);// ->setValue(value);
|
|
channel->setEnabled(true);
|
|
}
|
|
|
|
void VRInputManager::applyHapticsLeftHand(float intensity)
|
|
{
|
|
if (mHapticsEnabled)
|
|
activeActionSet().applyHaptics(TrackedLimb::LEFT_HAND, intensity);
|
|
}
|
|
|
|
void VRInputManager::applyHapticsRightHand(float intensity)
|
|
{
|
|
if (mHapticsEnabled)
|
|
activeActionSet().applyHaptics(TrackedLimb::RIGHT_HAND, intensity);
|
|
}
|
|
|
|
void VRInputManager::suggestBindingsSimple()
|
|
{
|
|
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
|
|
{MWInput::A_GameMenu, "/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_Inventory, "/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 controllerProfilePath = "/interaction_profiles/oculus/touch_controller";
|
|
|
|
// In-game character controls
|
|
SuggestedBindings gameplayBindings{
|
|
{A_ActivateTouch, "/user/hand/right/input/squeeze/value"},
|
|
{A_Recenter, "/user/hand/left/input/menu/click"},
|
|
{MWInput::A_Activate, "/user/hand/right/input/squeeze/value"},
|
|
{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
|
|
{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
|
|
{MWInput::A_GameMenu, "/user/hand/left/input/menu/click"},
|
|
{MWInput::A_Inventory, "/user/hand/right/input/b/click"},
|
|
{MWInput::A_Journal, "/user/hand/right/input/b/click"},
|
|
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
|
|
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
|
|
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
|
|
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
|
|
{MWInput::A_QuickSave, "/user/hand/left/input/y/click"},
|
|
{MWInput::A_Rest, "/user/hand/left/input/y/click"},
|
|
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/value"},
|
|
{MWInput::A_ToggleDebug, "/user/hand/right/input/thumbstick/click"},
|
|
{MWInput::A_ToggleHUD, "/user/hand/left/input/thumbstick/click"},
|
|
{MWInput::A_ToggleSpell, "/user/hand/left/input/x/click"},
|
|
{MWInput::A_ToggleWeapon, "/user/hand/right/input/a/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_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::suggestBindingsHpMixedReality()
|
|
{
|
|
std::string controllerProfilePath = "/interaction_profiles/hp/mixed_reality_controller";
|
|
|
|
// In-game character controls
|
|
SuggestedBindings gameplayBindings{
|
|
{A_ActivateTouch, "/user/hand/right/input/squeeze/value"},
|
|
{A_Recenter, "/user/hand/left/input/menu/click"},
|
|
{MWInput::A_Activate, "/user/hand/right/input/squeeze/value"},
|
|
{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
|
|
{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
|
|
{MWInput::A_GameMenu, "/user/hand/left/input/menu/click"},
|
|
{MWInput::A_Inventory, "/user/hand/right/input/b/click"},
|
|
{MWInput::A_Journal, "/user/hand/right/input/b/click"},
|
|
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
|
|
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
|
|
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
|
|
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
|
|
{MWInput::A_QuickSave, "/user/hand/left/input/y/click"},
|
|
{MWInput::A_Rest, "/user/hand/left/input/y/click"},
|
|
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/value"},
|
|
{MWInput::A_ToggleDebug, "/user/hand/right/input/thumbstick/click"},
|
|
{MWInput::A_ToggleHUD, "/user/hand/left/input/thumbstick/click"},
|
|
{MWInput::A_ToggleSpell, "/user/hand/left/input/x/click"},
|
|
{MWInput::A_ToggleWeapon, "/user/hand/right/input/a/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_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::suggestBindingsMicrosoftMixedReality()
|
|
{
|
|
std::string controllerProfilePath = "/interaction_profiles/microsoft/motion_controller";
|
|
|
|
// TODO: Slightly better than the vive wands, but still not good.
|
|
|
|
// In-game character controls
|
|
SuggestedBindings gameplayBindings{
|
|
//{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
|
|
//{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
|
|
//{MWInput::A_ToggleDebug, "/user/hand/right/input/thumbstick/click"},
|
|
//{MWInput::A_ToggleHUD, "/user/hand/left/input/thumbstick/click"},
|
|
{A_ActivateTouch, "/user/hand/right/input/squeeze/click"},
|
|
{A_Recenter, "/user/hand/left/input/menu/click"},
|
|
{MWInput::A_Activate, "/user/hand/right/input/squeeze/click"},
|
|
{MWInput::A_GameMenu, "/user/hand/left/input/menu/click"},
|
|
{MWInput::A_Inventory, "/user/hand/right/input/thumbstick/click"},
|
|
{MWInput::A_Journal, "/user/hand/right/input/thumbstick/click"},
|
|
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
|
|
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
|
|
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
|
|
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
|
|
{MWInput::A_QuickSave, "/user/hand/left/input/thumbstick/click"},
|
|
{MWInput::A_Rest, "/user/hand/left/input/thumbstick/click"},
|
|
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/click"},
|
|
{MWInput::A_ToggleSpell, "/user/hand/left/input/trackpad/click"},
|
|
{MWInput::A_ToggleWeapon, "/user/hand/right/input/trackpad/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/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::suggestBindingsIndex()
|
|
{
|
|
std::string controllerProfilePath = "/interaction_profiles/valve/index_controller";
|
|
// In-game character controls
|
|
SuggestedBindings gameplayBindings{
|
|
{A_ActivateTouch, "/user/hand/right/input/squeeze/value"},
|
|
{A_Recenter, "/user/hand/left/input/thumbstick/click"},
|
|
{MWInput::A_Activate, "/user/hand/right/input/squeeze/value"},
|
|
//{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
|
|
//{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
|
|
{MWInput::A_GameMenu, "/user/hand/left/input/thumbstick/click"},
|
|
{MWInput::A_Inventory, "/user/hand/right/input/b/click"},
|
|
{MWInput::A_Journal, "/user/hand/right/input/b/click"},
|
|
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
|
|
{MWInput::A_LookLeftRight, "/user/hand/right/input/thumbstick/x"},
|
|
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/thumbstick/y"},
|
|
{MWInput::A_MoveLeftRight, "/user/hand/left/input/thumbstick/x"},
|
|
{MWInput::A_QuickSave, "/user/hand/left/input/b/click"},
|
|
{MWInput::A_Rest, "/user/hand/left/input/b/click"},
|
|
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/value"},
|
|
//{MWInput::A_ToggleDebug, "/user/hand/right/input/thumbstick/click"},
|
|
//{MWInput::A_ToggleHUD, "/user/hand/left/input/thumbstick/click"},
|
|
{MWInput::A_ToggleSpell, "/user/hand/left/input/a/click"},
|
|
{MWInput::A_ToggleWeapon, "/user/hand/right/input/a/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_GameMenu, "/user/hand/left/input/thumbstick/click"},
|
|
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
|
|
{A_Recenter, "/user/hand/left/input/thumbstick/click"},
|
|
};
|
|
|
|
mXRInput->suggestBindings(ActionSet::Gameplay, controllerProfilePath, gameplayBindings);
|
|
mXRInput->suggestBindings(ActionSet::GUI, controllerProfilePath, GUIBindings);
|
|
}
|
|
|
|
void VRInputManager::suggestBindingsVive()
|
|
{
|
|
std::string controllerProfilePath = "/interaction_profiles/htc/vive_controller";
|
|
|
|
// TODO: I Didn't realize the vive wands were so bad. We don't have NEARLY enough actions available.
|
|
|
|
// In-game character controls
|
|
SuggestedBindings gameplayBindings{
|
|
//{MWInput::A_AlwaysRun, "/user/hand/left/input/thumbstick/click"},
|
|
//{MWInput::A_AutoMove, "/user/hand/right/input/thumbstick/click"},
|
|
//{MWInput::A_Inventory, "/user/hand/right/input/b/click"},
|
|
//{MWInput::A_Journal, "/user/hand/right/input/b/click"},
|
|
//{MWInput::A_QuickSave, "/user/hand/left/input/b/click"},
|
|
//{MWInput::A_Rest, "/user/hand/left/input/b/click"},
|
|
//{MWInput::A_ToggleDebug, "/user/hand/right/input/thumbstick/click"},
|
|
//{MWInput::A_ToggleHUD, "/user/hand/left/input/thumbstick/click"},
|
|
{A_ActivateTouch, "/user/hand/right/input/squeeze/click"},
|
|
{A_Recenter, "/user/hand/left/input/menu/click"},
|
|
{MWInput::A_Activate, "/user/hand/right/input/squeeze/click"},
|
|
{MWInput::A_GameMenu, "/user/hand/left/input/menu/click"},
|
|
{MWInput::A_Jump, "/user/hand/left/input/trigger/value"},
|
|
{MWInput::A_LookLeftRight, "/user/hand/right/input/trackpad/x"},
|
|
{MWInput::A_MoveForwardBackward,"/user/hand/left/input/trackpad/y"},
|
|
{MWInput::A_MoveLeftRight, "/user/hand/left/input/trackpad/x"},
|
|
{MWInput::A_Sneak, "/user/hand/left/input/squeeze/click"},
|
|
{MWInput::A_ToggleSpell, "/user/hand/left/input/trackpad/click"},
|
|
{MWInput::A_ToggleWeapon, "/user/hand/right/input/trackpad/click"},
|
|
{MWInput::A_Use, "/user/hand/right/input/trigger/value"},
|
|
};
|
|
|
|
// 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::suggestBindingsXboxController()
|
|
{
|
|
//TODO
|
|
}
|
|
|
|
void VRInputManager::requestRecenter()
|
|
{
|
|
mShouldRecenter = true;
|
|
}
|
|
|
|
VRInputManager::VRInputManager(
|
|
SDL_Window* window,
|
|
osg::ref_ptr<osgViewer::Viewer> viewer,
|
|
osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,
|
|
osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
|
|
const std::string& userFile,
|
|
bool userFileExists,
|
|
const std::string& userControllerBindingsFile,
|
|
const std::string& controllerBindingsFile,
|
|
bool grab)
|
|
: MWInput::InputManager(
|
|
window,
|
|
viewer,
|
|
screenCaptureHandler,
|
|
screenCaptureOperation,
|
|
userFile,
|
|
userFileExists,
|
|
userControllerBindingsFile,
|
|
controllerBindingsFile,
|
|
grab)
|
|
, mXRInput(new OpenXRInput)
|
|
, 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();
|
|
|
|
mXRInput->attachActionSets();
|
|
}
|
|
|
|
VRInputManager::~VRInputManager()
|
|
{
|
|
}
|
|
|
|
void VRInputManager::changeInputMode(bool mode)
|
|
{
|
|
// VR mode has no concept of these
|
|
//mGuiCursorEnabled = false;
|
|
MWInput::InputManager::changeInputMode(mode);
|
|
MWBase::Environment::get().getWindowManager()->showCrosshair(false);
|
|
MWBase::Environment::get().getWindowManager()->setCursorVisible(false);
|
|
}
|
|
|
|
void VRInputManager::update(
|
|
float dt,
|
|
bool disableControls,
|
|
bool disableEvents)
|
|
{
|
|
auto begin = std::chrono::steady_clock::now();
|
|
auto& actionSet = activeActionSet();
|
|
actionSet.updateControls();
|
|
|
|
auto* vrGuiManager = Environment::get().getGUIManager();
|
|
if (vrGuiManager)
|
|
{
|
|
bool vrHasFocus = vrGuiManager->updateFocus();
|
|
auto guiCursor = vrGuiManager->guiCursor();
|
|
if (vrHasFocus)
|
|
{
|
|
mMouseManager->setMousePosition(guiCursor.x(), guiCursor.y());
|
|
}
|
|
}
|
|
|
|
while (auto* action = actionSet.nextAction())
|
|
{
|
|
processAction(action, dt, disableControls);
|
|
}
|
|
|
|
updateActivationIndication();
|
|
|
|
MWInput::InputManager::update(dt, disableControls, disableEvents);
|
|
// This is the first update that needs openxr tracking, so i begin the next frame here.
|
|
auto* session = Environment::get().getSession();
|
|
if (session)
|
|
session->beginPhase(VRSession::FramePhase::Update);
|
|
|
|
// The rest of this code assumes the game is running
|
|
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
|
|
|
// OpenMW assumes all input will come via SDL which i often violate.
|
|
// This keeps player controls correctly enabled for my purposes.
|
|
mBindingsManager->setPlayerControlsEnabled(!guiMode);
|
|
|
|
updateHead();
|
|
|
|
if (!guiMode)
|
|
{
|
|
auto* world = MWBase::Environment::get().getWorld();
|
|
|
|
auto& player = world->getPlayer();
|
|
auto playerPtr = world->getPlayerPtr();
|
|
if (!mRealisticCombat || mRealisticCombat->ptr() != playerPtr)
|
|
mRealisticCombat.reset(new RealisticCombat::StateMachine(playerPtr));
|
|
bool enabled = !guiMode && player.getDrawState() == MWMechanics::DrawState_Weapon && !player.isDisabled();
|
|
mRealisticCombat->update(dt, enabled);
|
|
}
|
|
else if (mRealisticCombat)
|
|
mRealisticCombat->update(dt, false);
|
|
|
|
|
|
// Update tracking every frame if player is not currently in GUI mode.
|
|
// This ensures certain widgets like Notifications will be visible.
|
|
if (!guiMode)
|
|
{
|
|
vrGuiManager->updateTracking();
|
|
}
|
|
auto end = std::chrono::steady_clock::now();
|
|
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - begin);
|
|
}
|
|
|
|
void VRInputManager::processAction(const Action* action, float dt, bool disableControls)
|
|
{
|
|
static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input");
|
|
auto* vrGuiManager = Environment::get().getGUIManager();
|
|
|
|
|
|
// OpenMW does not currently provide any way to directly request skipping a video.
|
|
// This is copied from the controller manager and is used to skip videos,
|
|
// and works because mygui only consumes the escape press if a video is currently playing.
|
|
auto kc = MWInput::sdlKeyToMyGUI(SDLK_ESCAPE);
|
|
if (action->onActivate())
|
|
{
|
|
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(kc, 0));
|
|
}
|
|
else if (action->onDeactivate())
|
|
{
|
|
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(kc));
|
|
}
|
|
|
|
if (disableControls)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
|
|
|
if (guiMode)
|
|
{
|
|
MyGUI::KeyCode key = MyGUI::KeyCode::None;
|
|
|
|
// Axis actions
|
|
switch (action->openMWActionCode())
|
|
{
|
|
case A_MenuLeftRight:
|
|
if (action->value() > 0.6f && action->previousValue() < 0.6f)
|
|
{
|
|
key = MyGUI::KeyCode::ArrowRight;
|
|
}
|
|
if (action->value() < -0.6f && action->previousValue() > -0.6f)
|
|
{
|
|
key = MyGUI::KeyCode::ArrowLeft;
|
|
}
|
|
break;
|
|
case A_MenuUpDown:
|
|
if (action->value() > 0.6f && action->previousValue() < 0.6f)
|
|
{
|
|
key = MyGUI::KeyCode::ArrowUp;
|
|
}
|
|
if (action->value() < -0.6f && action->previousValue() > -0.6f)
|
|
{
|
|
key = MyGUI::KeyCode::ArrowDown;
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
// OnActivate actions
|
|
if (action->onActivate())
|
|
{
|
|
switch (action->openMWActionCode())
|
|
{
|
|
case MWInput::A_GameMenu:
|
|
mActionManager->toggleMainMenu();
|
|
break;
|
|
case MWInput::A_Screenshot:
|
|
mActionManager->screenshot();
|
|
break;
|
|
case A_Recenter:
|
|
vrGuiManager->updateTracking();
|
|
break;
|
|
case A_MenuSelect:
|
|
if (!MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Space, 0, 0))
|
|
executeAction(MWInput::A_Activate);
|
|
break;
|
|
case A_MenuBack:
|
|
if (MyGUI::InputManager::getInstance().isModalAny())
|
|
MWBase::Environment::get().getWindowManager()->exitCurrentModal();
|
|
else
|
|
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
|
|
break;
|
|
case MWInput::A_Use:
|
|
pointActivation(true);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// A few actions need to fire on deactivation
|
|
if (action->onDeactivate())
|
|
{
|
|
switch (action->openMWActionCode())
|
|
{
|
|
case MWInput::A_Use:
|
|
mBindingsManager->ics().getChannel(MWInput::A_Use)->setValue(0.f);
|
|
pointActivation(false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (key != MyGUI::KeyCode::None)
|
|
{
|
|
MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, 0);
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
// Hold actions
|
|
switch (action->openMWActionCode())
|
|
{
|
|
case A_ActivateTouch:
|
|
resetIdleTime();
|
|
mActivationIndication = action->isActive();
|
|
break;
|
|
case MWInput::A_LookLeftRight:
|
|
mYaw += osg::DegreesToRadians(action->value()) * 200.f * dt;
|
|
break;
|
|
case MWInput::A_MoveLeftRight:
|
|
mBindingsManager->ics().getChannel(MWInput::A_MoveLeftRight)->setValue(action->value() / 2.f + 0.5f);
|
|
break;
|
|
case MWInput::A_MoveForwardBackward:
|
|
mBindingsManager->ics().getChannel(MWInput::A_MoveForwardBackward)->setValue(-action->value() / 2.f + 0.5f);
|
|
break;
|
|
case MWInput::A_Sneak:
|
|
{
|
|
if (!isToggleSneak)
|
|
mBindingsManager->ics().getChannel(MWInput::A_Sneak)->setValue(action->isActive() ? 1.f : 0.f);
|
|
break;
|
|
}
|
|
case MWInput::A_Use:
|
|
if (!(mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode()))
|
|
mBindingsManager->ics().getChannel(MWInput::A_Use)->setValue(action->value());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// OnActivate actions
|
|
if (action->onActivate())
|
|
{
|
|
switch (action->openMWActionCode())
|
|
{
|
|
case MWInput::A_GameMenu:
|
|
mActionManager->toggleMainMenu();
|
|
break;
|
|
case MWInput::A_Screenshot:
|
|
mActionManager->screenshot();
|
|
break;
|
|
case MWInput::A_Inventory:
|
|
mActionManager->toggleInventory();
|
|
//injectMousePress(SDL_BUTTON_RIGHT, true);
|
|
//mBindingsManager->ics().getChannel(MWInput::A_Inventory)->setValue(0.f);
|
|
break;
|
|
case MWInput::A_Console:
|
|
mActionManager->toggleConsole();
|
|
break;
|
|
case MWInput::A_Journal:
|
|
mActionManager->toggleJournal();
|
|
break;
|
|
case MWInput::A_AutoMove:
|
|
mActionManager->toggleAutoMove();
|
|
break;
|
|
case MWInput::A_AlwaysRun:
|
|
mActionManager->toggleWalking();
|
|
break;
|
|
case MWInput::A_ToggleWeapon:
|
|
mActionManager->toggleWeapon();
|
|
break;
|
|
case MWInput::A_Rest:
|
|
mActionManager->rest();
|
|
break;
|
|
case MWInput::A_ToggleSpell:
|
|
mActionManager->toggleSpell();
|
|
break;
|
|
case MWInput::A_QuickKey1:
|
|
mActionManager->quickKey(1);
|
|
break;
|
|
case MWInput::A_QuickKey2:
|
|
mActionManager->quickKey(2);
|
|
break;
|
|
case MWInput::A_QuickKey3:
|
|
mActionManager->quickKey(3);
|
|
break;
|
|
case MWInput::A_QuickKey4:
|
|
mActionManager->quickKey(4);
|
|
break;
|
|
case MWInput::A_QuickKey5:
|
|
mActionManager->quickKey(5);
|
|
break;
|
|
case MWInput::A_QuickKey6:
|
|
mActionManager->quickKey(6);
|
|
break;
|
|
case MWInput::A_QuickKey7:
|
|
mActionManager->quickKey(7);
|
|
break;
|
|
case MWInput::A_QuickKey8:
|
|
mActionManager->quickKey(8);
|
|
break;
|
|
case MWInput::A_QuickKey9:
|
|
mActionManager->quickKey(9);
|
|
break;
|
|
case MWInput::A_QuickKey10:
|
|
mActionManager->quickKey(10);
|
|
break;
|
|
case MWInput::A_QuickKeysMenu:
|
|
mActionManager->showQuickKeysMenu();
|
|
break;
|
|
case MWInput::A_ToggleHUD:
|
|
Log(Debug::Verbose) << "Toggle HUD";
|
|
MWBase::Environment::get().getWindowManager()->toggleHud();
|
|
break;
|
|
case MWInput::A_ToggleDebug:
|
|
Log(Debug::Verbose) << "Toggle Debug";
|
|
MWBase::Environment::get().getWindowManager()->toggleDebugWindow();
|
|
break;
|
|
case MWInput::A_QuickSave:
|
|
mActionManager->quickSave();
|
|
break;
|
|
case MWInput::A_QuickLoad:
|
|
mActionManager->quickLoad();
|
|
break;
|
|
case MWInput::A_CycleSpellLeft:
|
|
if (mActionManager->checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic))
|
|
MWBase::Environment::get().getWindowManager()->cycleSpell(false);
|
|
break;
|
|
case MWInput::A_CycleSpellRight:
|
|
if (mActionManager->checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic))
|
|
MWBase::Environment::get().getWindowManager()->cycleSpell(true);
|
|
break;
|
|
case MWInput::A_CycleWeaponLeft:
|
|
if (mActionManager->checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))
|
|
MWBase::Environment::get().getWindowManager()->cycleWeapon(false);
|
|
break;
|
|
case MWInput::A_CycleWeaponRight:
|
|
if (mActionManager->checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))
|
|
MWBase::Environment::get().getWindowManager()->cycleWeapon(true);
|
|
break;
|
|
case MWInput::A_Jump:
|
|
mActionManager->setAttemptJump(true);
|
|
break;
|
|
case A_Recenter:
|
|
vrGuiManager->updateTracking();
|
|
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
|
|
requestRecenter();
|
|
break;
|
|
case MWInput::A_Use:
|
|
if (mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode())
|
|
pointActivation(true);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// A few actions need to fire on deactivation
|
|
if (action->onDeactivate())
|
|
{
|
|
switch (action->openMWActionCode())
|
|
{
|
|
case MWInput::A_Use:
|
|
mBindingsManager->ics().getChannel(MWInput::A_Use)->setValue(0.f);
|
|
if (mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode())
|
|
pointActivation(false);
|
|
break;
|
|
case MWInput::A_Sneak:
|
|
if (isToggleSneak)
|
|
mActionManager->toggleSneaking();
|
|
break;
|
|
case MWInput::A_Inventory:
|
|
//injectMousePress(SDL_BUTTON_RIGHT, false);
|
|
//mBindingsManager->ics().getChannel(MWInput::A_Inventory)->setValue(0.f);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
osg::Quat VRInputManager::stageRotation()
|
|
{
|
|
return osg::Quat(mYaw, osg::Vec3(0, 0, -1));
|
|
}
|
|
|
|
void VRInputManager::updateHead()
|
|
{
|
|
auto* session = Environment::get().getSession();
|
|
auto currentHeadPose = session->predictedPoses(VRSession::FramePhase::Update).head;
|
|
currentHeadPose.position *= Environment::get().unitsPerMeter();
|
|
osg::Vec3 vrMovement = currentHeadPose.position - mHeadPose.position;
|
|
mHeadPose = currentHeadPose;
|
|
mHeadOffset += stageRotation() * vrMovement;
|
|
|
|
if (mShouldRecenter)
|
|
{
|
|
// Move position of head to center of character
|
|
// Z should not be affected
|
|
mHeadOffset = osg::Vec3(0, 0, 0);
|
|
mHeadOffset.z() = mHeadPose.position.z();
|
|
|
|
// Adjust orientation to zero yaw
|
|
float yaw = 0.f;
|
|
float pitch = 0.f;
|
|
float roll = 0.f;
|
|
getEulerAngles(mHeadPose.orientation, yaw, pitch, roll);
|
|
mYaw = -yaw;
|
|
|
|
mShouldRecenter = false;
|
|
Log(Debug::Verbose) << "Recentered (" << mYaw << ")";
|
|
}
|
|
else
|
|
{
|
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
|
auto& player = world->getPlayer();
|
|
auto playerPtr = player.getPlayer();
|
|
|
|
float yaw = 0.f;
|
|
float pitch = 0.f;
|
|
float roll = 0.f;
|
|
getEulerAngles(mHeadPose.orientation, yaw, pitch, roll);
|
|
|
|
yaw += mYaw;
|
|
|
|
mVrAngles[0] = pitch;
|
|
mVrAngles[1] = roll;
|
|
mVrAngles[2] = yaw;
|
|
|
|
if (!player.isDisabled())
|
|
{
|
|
world->rotateObject(playerPtr, mVrAngles[0], mVrAngles[1], mVrAngles[2], MWBase::RotationFlag_none);
|
|
}
|
|
else {
|
|
// Update the camera directly to avoid rotating the disabled player
|
|
world->getRenderingManager().getCamera()->rotateCamera(-pitch, -roll, -yaw, false);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|