1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-04-01 12:36:42 +00:00

Basic input management

This commit is contained in:
Mads Buvik Sandvei 2020-02-15 20:01:11 +01:00
parent 49e0c6f17b
commit da73df1707
15 changed files with 585 additions and 241 deletions

View file

@ -59,6 +59,10 @@
#include "mwstate/statemanagerimp.hpp"
#ifdef USE_OPENXR
#include "mwvr/openxrinputmanager.hpp"
#endif
namespace
{
void checkSDLError(int ret)
@ -542,8 +546,12 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
gameControllerdb = globaldefault;
else
gameControllerdb = ""; //if it doesn't exist, pass in an empty string
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
MWInput::InputManager* input =
#ifdef USE_OPENXR
new MWVR::OpenXRInputManager(mWindow, mXRViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
#else
new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
#endif
mEnvironment.setInputManager (input);
std::string myguiResources = (mResDir / "mygui").string();

View file

@ -1,5 +1,7 @@
#include "engine.hpp"
#include "mwbase/environment.hpp"
#include "mwvr/openxrmanager.hpp"
#include "mwvr/openxrsession.hpp"
#ifndef USE_OPENXR
#error "USE_OPENXR not defined"
@ -11,6 +13,7 @@ void OMW::Engine::initVr()
throw std::logic_error("mViewer must be initialized before calling initVr()");
mXR = new MWVR::OpenXRManager();
mEnvironment.setXRSession(new MWVR::OpenXRSession(mXR));
// Ref: https://wiki.openmw.org/index.php?title=Measurement_Units
float unitsPerYard = 64.f;

View file

@ -14,6 +14,7 @@
#include "windowmanager.hpp"
#include "statemanager.hpp"
MWBase::Environment *MWBase::Environment::sThis = 0;
MWBase::Environment::Environment()
@ -198,3 +199,27 @@ const MWBase::Environment& MWBase::Environment::get()
assert (sThis);
return *sThis;
}
#ifdef USE_OPENXR
#include "../mwvr/openxrinputmanager.hpp"
#include "../mwvr/openxrsession.hpp"
MWVR::OpenXRInputManager* MWBase::Environment::getXRInputManager() const
{
assert(mInputManager);
auto xrInputManager = dynamic_cast<MWVR::OpenXRInputManager*>(mInputManager);
assert(xrInputManager);
return xrInputManager;
}
MWVR::OpenXRSession* MWBase::Environment::getXRSession() const
{
return mXrSession;
}
void MWBase::Environment::setXRSession(MWVR::OpenXRSession* xrSession)
{
mXrSession = xrSession;
}
#endif

View file

@ -1,6 +1,14 @@
#ifndef GAME_BASE_ENVIRONMENT_H
#define GAME_BASE_ENVIRONMENT_H
#ifdef USE_OPENXR
namespace MWVR
{
class OpenXRInputManager;
class OpenXRSession;
}
#endif
namespace MWBase
{
class World;
@ -21,82 +29,94 @@ namespace MWBase
/// the set* functions.
class Environment
{
static Environment *sThis;
World *mWorld;
SoundManager *mSoundManager;
ScriptManager *mScriptManager;
WindowManager *mWindowManager;
MechanicsManager *mMechanicsManager;
DialogueManager *mDialogueManager;
Journal *mJournal;
InputManager *mInputManager;
StateManager *mStateManager;
float mFrameDuration;
float mFrameRateLimit;
static Environment *sThis;
Environment (const Environment&);
///< not implemented
World *mWorld;
SoundManager *mSoundManager;
ScriptManager *mScriptManager;
WindowManager *mWindowManager;
MechanicsManager *mMechanicsManager;
DialogueManager *mDialogueManager;
Journal *mJournal;
InputManager *mInputManager;
StateManager *mStateManager;
float mFrameDuration;
float mFrameRateLimit;
Environment& operator= (const Environment&);
///< not implemented
Environment (const Environment&);
///< not implemented
public:
Environment& operator= (const Environment&);
///< not implemented
Environment();
public:
~Environment();
Environment();
void setWorld (World *world);
~Environment();
void setSoundManager (SoundManager *soundManager);
void setWorld (World *world);
void setScriptManager (MWBase::ScriptManager *scriptManager);
void setSoundManager (SoundManager *soundManager);
void setWindowManager (WindowManager *windowManager);
void setScriptManager (MWBase::ScriptManager *scriptManager);
void setMechanicsManager (MechanicsManager *mechanicsManager);
void setWindowManager (WindowManager *windowManager);
void setDialogueManager (DialogueManager *dialogueManager);
void setMechanicsManager (MechanicsManager *mechanicsManager);
void setJournal (Journal *journal);
void setDialogueManager (DialogueManager *dialogueManager);
void setInputManager (InputManager *inputManager);
void setJournal (Journal *journal);
void setStateManager (StateManager *stateManager);
void setInputManager (InputManager *inputManager);
void setFrameDuration (float duration);
///< Set length of current frame in seconds.
void setStateManager (StateManager *stateManager);
void setFrameRateLimit(float frameRateLimit);
float getFrameRateLimit() const;
void limitFrameRate(double dt) const;
void setFrameDuration (float duration);
///< Set length of current frame in seconds.
World *getWorld() const;
void setFrameRateLimit(float frameRateLimit);
float getFrameRateLimit() const;
void limitFrameRate(double dt) const;
SoundManager *getSoundManager() const;
World *getWorld() const;
ScriptManager *getScriptManager() const;
SoundManager *getSoundManager() const;
WindowManager *getWindowManager() const;
ScriptManager *getScriptManager() const;
MechanicsManager *getMechanicsManager() const;
WindowManager *getWindowManager() const;
DialogueManager *getDialogueManager() const;
MechanicsManager *getMechanicsManager() const;
Journal *getJournal() const;
DialogueManager *getDialogueManager() const;
InputManager *getInputManager() const;
Journal *getJournal() const;
StateManager *getStateManager() const;
InputManager* getInputManager() const;
float getFrameDuration() const;
void cleanup();
///< Delete all mw*-subsystems.
StateManager *getStateManager() const;
static const Environment& get();
///< Return instance of this class.
float getFrameDuration() const;
void cleanup();
///< Delete all mw*-subsystems.
static const Environment& get();
///< Return instance of this class.
// VR Extensions
#ifdef USE_OPENXR
MWVR::OpenXRInputManager* getXRInputManager() const;
MWVR::OpenXRSession* getXRSession() const;
void setXRSession(MWVR::OpenXRSession* xrSession);
private:
MWVR::OpenXRSession* mXrSession;
#endif
};
}

View file

@ -126,6 +126,7 @@ namespace MWInput
// Open all presently connected sticks
int numSticks = SDL_NumJoysticks();
Log(Debug::Info) << "Detected " << numSticks << " joysticks";
for(int i = 0; i < numSticks; i++)
{
if(SDL_IsGameController(i))

View file

@ -161,7 +161,7 @@ namespace MWInput
virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress);
virtual void readRecord(ESM::ESMReader& reader, uint32_t type);
private:
protected:
SDL_Window* mWindow;
bool mWindowVisible;
osg::ref_ptr<osgViewer::Viewer> mViewer;
@ -218,7 +218,7 @@ namespace MWInput
float mInvUiScalingFactor;
float mGamepadCursorSpeed;
private:
protected:
void convertMousePosForMyGUI(int& x, int& y);
MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button);
@ -239,7 +239,7 @@ namespace MWInput
bool checkAllowedToUseItems() const;
private:
protected:
void toggleMainMenu();
void toggleSpell();
void toggleWeapon();

View file

@ -40,6 +40,10 @@
#include "../mwworld/class.hpp"
#ifdef USE_OPENXR
#include "../mwvr/openxrsession.hpp"
#endif
#include "collisiontype.hpp"
#include "actor.hpp"
#include "trace.h"
@ -271,14 +275,29 @@ namespace MWPhysics
static osg::Vec3f move(osg::Vec3f position, const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time,
bool isFlying, float waterlevel, float slowFall, const btCollisionWorld* collisionWorld,
std::map<MWWorld::Ptr, MWWorld::Ptr>& standingCollisionTracker)
std::map<MWWorld::Ptr, MWWorld::Ptr>& standingCollisionTracker, bool isPlayer)
{
const ESM::Position& refpos = ptr.getRefData().getPosition();
ESM::Position refpos = ptr.getRefData().getPosition();
// Early-out for totally static creatures
// (Not sure if gravity should still apply?)
if (!ptr.getClass().isMobile(ptr))
return position;
// In VR, player should move according to current direction of
// a selected limb, rather than current orientation of camera.
#ifdef USE_OPENXR
if (isPlayer)
{
auto session = MWBase::Environment::get().getXRSession();
if (session)
{
float yaw = session->movementYaw();
refpos.rot[2] += yaw;
Log(Debug::Verbose) << "yaw = " << yaw;
}
}
#endif
// Reset per-frame data
physicActor->setWalkingOnWater(false);
// Anything to collide with?
@ -1275,6 +1294,7 @@ namespace MWPhysics
PtrVelocityList::iterator iter = mMovementQueue.begin();
for(;iter != mMovementQueue.end();++iter)
{
bool isPlayer = iter->first == MWMechanics::getPlayer();
ActorMap::iterator foundActor = mActors.find(iter->first);
if (foundActor == mActors.end()) // actor was already removed from the scene
continue;
@ -1314,7 +1334,7 @@ namespace MWPhysics
for (int i=0; i<numSteps; ++i)
{
position = MovementSolver::move(position, physicActor->getPtr(), physicActor, iter->second, mPhysicsDt,
flying, waterlevel, slowFall, mCollisionWorld, mStandingCollisions);
flying, waterlevel, slowFall, mCollisionWorld, mStandingCollisions, isPlayer);
if (position != physicActor->getPosition())
positionChanged = true;
physicActor->setPosition(position); // always set even if unchanged to make sure interpolation is correct

View file

@ -1,11 +1,34 @@
#include "openxrinputmanager.hpp"
#include "openxrmanager.hpp"
#include "openxrmanagerimpl.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
#include <MyGUI_InputManager.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_Widget.h>
#include <MyGUI_Button.h>
#include <MyGUI_EditBox.h>
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlinputwrapper.hpp>
#include <components/sdlutil/sdlvideowrapper.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/controlsstate.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/esmstore.hpp"
#include <openxr/openxr.h>
#include <osg/Camera>
@ -17,6 +40,13 @@
namespace MWVR
{
struct OpenXRActionEvent
{
MWInput::InputManager::Actions action;
bool onPress;
float value = 0.f;
};
struct OpenXRAction
{
@ -86,7 +116,7 @@ namespace MWVR
float mValue = 0.f;
};
struct OpenXRInputManagerImpl
struct OpenXRInput
{
using Actions = MWInput::InputManager::Actions;
@ -112,18 +142,16 @@ namespace MWVR
ControllerActionPaths generateControllerActionPaths(const std::string& controllerAction);
OpenXRInputManagerImpl(osg::ref_ptr<OpenXRManager> XR);
OpenXRInput(osg::ref_ptr<OpenXRManager> XR);
OpenXRAction createAction(XrActionType actionType, const std::string& actionName, const std::string& localName, const std::vector<SubAction>& subActions = {});
XrActionSet createActionSet(void);
void updateHandTracking();
XrPath subactionPath(SubAction subAction);
void updateControls();
void updateHead();
bool nextActionEvent(OpenXRActionEvent& action);
PoseSet getHandPoses(int64_t time, TrackedSpace space);
osg::ref_ptr<OpenXRManager> mXR;
@ -226,7 +254,7 @@ namespace MWVR
};
XrActionSet
OpenXRInputManagerImpl::createActionSet()
OpenXRInput::createActionSet()
{
XrActionSet actionSet = XR_NULL_HANDLE;
XrActionSetCreateInfo createInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
@ -353,8 +381,8 @@ namespace MWVR
mAction.getFloat(0, mValue);
}
OpenXRInputManagerImpl::ControllerActionPaths
OpenXRInputManagerImpl::generateControllerActionPaths(
OpenXRInput::ControllerActionPaths
OpenXRInput::generateControllerActionPaths(
const std::string& controllerAction)
{
ControllerActionPaths actionPaths;
@ -370,7 +398,7 @@ namespace MWVR
return actionPaths;
}
OpenXRInputManagerImpl::OpenXRInputManagerImpl(
OpenXRInput::OpenXRInput(
osg::ref_ptr<OpenXRManager> XR)
: mXR(XR)
, mSubactionPath{ {
@ -430,8 +458,8 @@ namespace MWVR
{mLookLeftRight, mThumbstickXPath[RIGHT_HAND]},
{mMoveLeftRight, mThumbstickXPath[LEFT_HAND]},
{mMoveForwardBackward, mThumbstickYPath[LEFT_HAND]},
{mActivate, mSqueezeClickPath[RIGHT_HAND]},
{mUse, mTriggerClickPath[RIGHT_HAND]},
{mActivate, mSqueezeValuePath[RIGHT_HAND]},
{mUse, mTriggerValuePath[RIGHT_HAND]},
{mJump, mTriggerValuePath[LEFT_HAND]},
{mWeapon, mAPath[RIGHT_HAND]},
{mSpell, mAPath[RIGHT_HAND]},
@ -442,7 +470,7 @@ namespace MWVR
{mSneak, mXPath[LEFT_HAND]},
{mInventory, mBPath[RIGHT_HAND]},
{mQuickMenu, mYPath[LEFT_HAND]},
{mSpellModifier, mSqueezeClickPath[LEFT_HAND]},
{mSpellModifier, mSqueezeValuePath[LEFT_HAND]},
{mGameMenu, mMenuClickPath[LEFT_HAND]},
} };
XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
@ -490,7 +518,7 @@ namespace MWVR
};
OpenXRAction
OpenXRInputManagerImpl::createAction(
OpenXRInput::createAction(
XrActionType actionType,
const std::string& actionName,
const std::string& localName,
@ -515,17 +543,8 @@ namespace MWVR
return OpenXRAction(mXR, action, actionType, actionName, localName);
}
void
OpenXRInputManagerImpl::updateHandTracking()
{
// TODO
//for (auto hand : { LEFT_HAND, RIGHT_HAND }) {
// CHECK_XRCMD(xrLocateSpace(mHandSpace[hand], mXR->impl().mReferenceSpaceStage, mXR->impl().predictedDisplayTime(OpenXRFrameIndexer::instance().updateIndex()), &mHandSpaceLocation[hand]));
//}
}
XrPath
OpenXRInputManagerImpl::subactionPath(
OpenXRInput::subactionPath(
SubAction subAction)
{
if (subAction == NONE)
@ -534,7 +553,7 @@ namespace MWVR
}
void
OpenXRInputManagerImpl::updateControls()
OpenXRInput::updateControls()
{
if (!mXR->impl().mSessionRunning)
return;
@ -546,9 +565,24 @@ namespace MWVR
syncInfo.activeActionSets = &activeActionSet;
CHECK_XRCMD(xrSyncActions(mXR->impl().mSession, &syncInfo));
updateHead();
mGameMenu.update();
mInventory.update();
mActivate.update();
mUse.update();
mJump.update();
mWeapon.update();
mSpell.update();
mCycleSpellLeft.update();
mCycleSpellRight.update();
mCycleWeaponLeft.update();
mCycleWeaponRight.update();
mSneak.update();
mQuickMenu.update();
mLookLeftRight.update();
mMoveForwardBackward.update();
mMoveLeftRight.update();
mActionsMenu.update();
mSpellModifier.update();
// This set of actions only care about on-press
if (mActionsMenu.actionOnPress())
@ -567,9 +601,9 @@ namespace MWVR
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Activate, true });
}
if (mUse.actionOnPress())
if (mUse.actionChanged())
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Use, true });
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Use, mUse.actionOnPress() });
}
if (mJump.actionOnPress())
{
@ -591,7 +625,7 @@ namespace MWVR
// Weapon/Spell actions
if (mWeapon.actionOnPress() && !mSpellModifier.actionIsPressed())
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Weapon, true });
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_ToggleWeapon, true });
}
if (mCycleWeaponLeft.actionOnPress() && !mSpellModifier.actionIsPressed())
{
@ -603,7 +637,7 @@ namespace MWVR
}
if (mSpell.actionOnPress() && mSpellModifier.actionIsPressed())
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Spell, true });
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_ToggleSpell, true });
}
if (mCycleSpellLeft.actionOnPress() && mSpellModifier.actionIsPressed())
{
@ -619,97 +653,33 @@ namespace MWVR
float moveLeftRight = mMoveLeftRight.value();
float moveForwardBackward = mMoveForwardBackward.value();
// Until i implement the rest
(void)lookLeftRight;
(void)moveLeftRight;
(void)moveForwardBackward;
// TODO: Propagate movement to openmw
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_LookLeftRight, false, lookLeftRight });
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_MoveLeftRight, false, moveLeftRight });
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_MoveForwardBackward, false, moveForwardBackward });
}
void OpenXRInputManagerImpl::updateHead()
{
//auto location = mXR->getHeadLocation();
////std::stringstream ss;
////ss << "Head.pose=< position=<"
//// << location.pose.position.x << ", " << location.pose.position.y << ", " << location.pose.position.z << "> orientation=<"
//// << location.pose.orientation.x << ", " << location.pose.orientation.y << ", " << location.pose.orientation.z << ", " << location.pose.orientation.w << "> >";
////mOpenXRLogger.log(ss.str(), 5, "Tracking", "OpenXR");
//// To keep world movement from physical walking properly oriented, world position must be tracked in differentials from stage position, as orientation between
//// stage and world may differ.
//osg::Vec3f newHeadStagePosition{ location.pose.position.x, location.pose.position.y, location.pose.position.z };
//osg::Vec3f headStageMovement = newHeadStagePosition - mHeadStagePosition;
//osg::Vec3f headWorldMovement = yaw() * headStageMovement;
//// Update positions
//mHeadStagePosition = newHeadStagePosition;
//mHeadWorldPosition = mHeadWorldPosition + headWorldMovement;
//// Update orientations
//mHeadStageOrientation = osg::fromXR(location.pose.orientation);
//mHeadWorldOrientation = yaw() * mHeadStageOrientation;
//osg::Vec3f up(0.f, 1.f, 0.f);
//up = mHeadStageOrientation * up;
//mHeadUpward = (mHeadWorldOrientation * osg::Vec3f(0.f, 1.f, 0.f));
//mHeadUpward.normalize();
//mHeadForward = (mHeadWorldOrientation * osg::Vec3f(0.f, 0.f, -1.f));
//mHeadForwardmHeadForward.normalize();
//mHeadRightward = mHeadForward ^ mHeadUpward;
//mHeadRightward.normalize();
}
XrPath OpenXRInputManagerImpl::generateXrPath(const std::string& path)
XrPath OpenXRInput::generateXrPath(const std::string& path)
{
XrPath xrpath = 0;
CHECK_XRCMD(xrStringToPath(mXR->impl().mInstance, path.c_str(), &xrpath));
return xrpath;
}
OpenXRInputManager::OpenXRInputManager(
osg::ref_ptr<OpenXRManager> XR)
: mPrivate(new OpenXRInputManagerImpl(XR))
bool OpenXRInput::nextActionEvent(OpenXRActionEvent& action)
{
action = {};
}
OpenXRInputManager::~OpenXRInputManager()
{
}
void
OpenXRInputManager::updateControls()
{
impl().updateControls();
}
PoseSet
OpenXRInputManager::getHandPoses(
int64_t time,
TrackedSpace space)
{
return mPrivate->getHandPoses(time, space);
}
bool OpenXRInputManager::nextActionEvent(OpenXRActionEvent& action)
{
if (mPrivate->mActionEvents.empty())
if (mActionEvents.empty())
return false;
action = mPrivate->mActionEvents.front();
mPrivate->mActionEvents.pop_front();
action = mActionEvents.front();
mActionEvents.pop_front();
return true;
}
PoseSet
OpenXRInputManagerImpl::getHandPoses(
OpenXRInput::getHandPoses(
int64_t time,
TrackedSpace space)
{
@ -724,8 +694,7 @@ namespace MWVR
XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY };
location.next = &velocity;
CHECK_XRCMD(xrLocateSpace(mHandSpace[(int)Chirality::LEFT_HAND], referenceSpace, time, &location));
//if (!(velocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT))
// Log(Debug::Warning) << "Unable to acquire linear velocity";
handPoses[(int)Chirality::LEFT_HAND] = MWVR::Pose{
osg::fromXR(location.pose.position),
osg::fromXR(location.pose.orientation),
@ -741,4 +710,220 @@ namespace MWVR
return handPoses;
}
PoseSet OpenXRInputManager::getHandPoses(int64_t time, TrackedSpace space)
{
return mXRInput->getHandPoses(time, space);
}
OpenXRInputManager::OpenXRInputManager(
SDL_Window* window,
osg::ref_ptr<OpenXRViewer> 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->mViewer,
screenCaptureHandler,
screenCaptureOperation,
userFile,
userFileExists,
userControllerBindingsFile,
controllerBindingsFile,
grab)
, mXRInput(new OpenXRInput(viewer->mXR))
{
// VR mode has no concept of these
mControlSwitch["vanitymode"] = false;
mGuiCursorEnabled = false;
}
OpenXRInputManager::~OpenXRInputManager()
{
}
void OpenXRInputManager::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 OpenXRInputManager::update(
float dt,
bool disableControls,
bool disableEvents)
{
mXRInput->updateControls();
auto* session = MWBase::Environment::get().getXRSession();
OpenXRActionEvent event{};
while (mXRInput->nextActionEvent(event))
{
//Log(Debug::Verbose) << "ActionEvent action=" << event.action << " onPress=" << event.onPress;
processEvent(event);
}
MWInput::InputManager::update(dt, disableControls, disableEvents);
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
session->showMenu(guiMode);
}
void OpenXRInputManager::processEvent(const OpenXRActionEvent& event)
{
switch (event.action)
{
case A_GameMenu:
Log(Debug::Verbose) << "A_GameMenu";
toggleMainMenu();
// Explicitly request position update here so that the player can move the menu
// using the menu key when the menu can't be toggled.
MWBase::Environment::get().getXRSession()->updateMenuPosition();
break;
case A_Screenshot:
screenshot();
break;
case A_Inventory:
toggleInventory();
break;
case A_Console:
toggleConsole();
break;
case A_Activate:
resetIdleTime();
activate();
break;
// TODO: Movement
//case A_MoveLeft:
//case A_MoveRight:
//case A_MoveForward:
//case A_MoveBackward:
// handleGuiArrowKey(action);
// break;
case A_Journal:
toggleJournal();
break;
case A_AutoMove:
toggleAutoMove();
break;
case A_AlwaysRun:
toggleWalking();
break;
case A_ToggleWeapon:
toggleWeapon();
break;
case A_Rest:
rest();
break;
case A_ToggleSpell:
toggleSpell();
break;
case A_QuickKey1:
quickKey(1);
break;
case A_QuickKey2:
quickKey(2);
break;
case A_QuickKey3:
quickKey(3);
break;
case A_QuickKey4:
quickKey(4);
break;
case A_QuickKey5:
quickKey(5);
break;
case A_QuickKey6:
quickKey(6);
break;
case A_QuickKey7:
quickKey(7);
break;
case A_QuickKey8:
quickKey(8);
break;
case A_QuickKey9:
quickKey(9);
break;
case A_QuickKey10:
quickKey(10);
break;
case A_QuickKeysMenu:
showQuickKeysMenu();
break;
case A_ToggleHUD:
MWBase::Environment::get().getWindowManager()->toggleHud();
break;
case A_ToggleDebug:
MWBase::Environment::get().getWindowManager()->toggleDebugWindow();
break;
// Does not apply in VR
//case A_ZoomIn:
// if (mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"] && !MWBase::Environment::get().getWindowManager()->isGuiMode())
// MWBase::Environment::get().getWorld()->setCameraDistance(ZOOM_SCALE, true, true);
// break;
//case A_ZoomOut:
// if (mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"] && !MWBase::Environment::get().getWindowManager()->isGuiMode())
// MWBase::Environment::get().getWorld()->setCameraDistance(-ZOOM_SCALE, true, true);
// break;
case A_QuickSave:
quickSave();
break;
case A_QuickLoad:
quickLoad();
break;
case A_CycleSpellLeft:
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic))
MWBase::Environment::get().getWindowManager()->cycleSpell(false);
break;
case A_CycleSpellRight:
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic))
MWBase::Environment::get().getWindowManager()->cycleSpell(true);
break;
case A_CycleWeaponLeft:
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))
MWBase::Environment::get().getWindowManager()->cycleWeapon(false);
break;
case A_CycleWeaponRight:
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))
MWBase::Environment::get().getWindowManager()->cycleWeapon(true);
break;
case A_Sneak:
if (mSneakToggles)
{
toggleSneaking();
}
break;
case A_LookLeftRight:
mInputBinder->getChannel(A_LookLeftRight)->setValue(event.value / 2.f + 0.5f);
break;
case A_MoveLeftRight:
mInputBinder->getChannel(A_MoveLeftRight)->setValue(event.value / 2.f + 0.5f);
break;
case A_MoveForwardBackward:
mInputBinder->getChannel(A_MoveForwardBackward)->setValue(-event.value / 2.f + 0.5f);
break;
case A_Jump:
mAttemptJump = true;
break;
case A_Use:
//MWMechanics::DrawState_ state = MWBase::Environment::get().getWorld()->getPlayer().getDrawState();
//mPlayer->setAttackingOrSpell(currentValue != 0 && state != MWMechanics::DrawState_Nothing);
mInputBinder->getChannel(A_Use)->setValue(event.onPress);
break;
default:
Log(Debug::Warning) << "Unhandled XR action " << event.action;
}
}
}

View file

@ -1,7 +1,7 @@
#ifndef OPENXR_INPUT_MANAGER_HPP
#define OPENXR_INPUT_MANAGER_HPP
#include "openxrmanager.hpp"
#include "openxrviewer.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include <vector>
@ -10,27 +10,37 @@
namespace MWVR
{
struct OpenXRActionEvent
{
MWInput::InputManager::Actions action;
bool onPress;
};
struct OpenXRInput;
struct OpenXRActionEvent;
struct OpenXRInputManagerImpl;
struct OpenXRInputManager
/// As far as I can tell, SDL does not support VR controllers.
/// So I subclass the input manager and insert VR controls.
class OpenXRInputManager : public MWInput::InputManager
{
OpenXRInputManager(osg::ref_ptr<OpenXRManager> XR);
~OpenXRInputManager();
public:
OpenXRInputManager(
SDL_Window* window,
osg::ref_ptr<OpenXRViewer> 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);
void updateControls();
virtual ~OpenXRInputManager();
/// Overriden to always disallow mouselook and similar.
virtual void changeInputMode(bool guiMode);
/// Overriden to update XR inputs
virtual void update(float dt, bool disableControls = false, bool disableEvents = false);
void processEvent(const OpenXRActionEvent& event);
PoseSet getHandPoses(int64_t time, TrackedSpace space);
bool nextActionEvent(OpenXRActionEvent& action);
OpenXRInputManagerImpl& impl() { return *mPrivate; }
std::unique_ptr<OpenXRInputManagerImpl> mPrivate;
osg::ref_ptr<OpenXRViewer> mXRViewer;
std::unique_ptr<OpenXRInput> mXRInput;
};
}

View file

@ -504,7 +504,7 @@ namespace MWVR
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
bool isMale = ref->mBase->isMale();
float charHeightFactor = isMale ? race->mData.mHeight.mMale : race->mData.mHeight.mFemale;
float charHeightBase = 1.9f;
float charHeightBase = 2.f;
float charHeight = charHeightBase * charHeightFactor;
// TODO: Player height should be configurable
// For now i'm just using my own

View file

@ -29,9 +29,9 @@ namespace MWVR
if (mPositionNeedsUpdate)
{
// Menus are position one meter in front of the player, facing the player.
// I position menus half a meter in front of the player, facing the player.
mPose = predictedPose();
mPose.position += mPose.orientation * osg::Vec3(0, 0, -1);
mPose.position += mPose.orientation * osg::Vec3(0, 0, -0.5);
mPose.orientation = -mPose.orientation;
Log(Debug::Verbose) << "Menu pose updated to: " << mPose;

View file

@ -1,7 +1,9 @@
#include "openxrmanager.hpp"
#include "openxrmanagerimpl.hpp"
#include "openxrinputmanager.hpp"
#include "openxrswapchain.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include "../mwbase/environment.hpp"
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
@ -27,7 +29,7 @@ namespace MWVR
OpenXRSession::OpenXRSession(
osg::ref_ptr<OpenXRManager> XR)
: mXR(XR)
, mInputManager(new OpenXRInputManager(mXR))
// , mInputManager(new OpenXRInput(mXR))
{
}
@ -71,39 +73,108 @@ namespace MWVR
Timer timer("OpenXRSession::waitFrame");
mXR->waitFrame();
timer.checkpoint("waitFrame");
mInputManager->updateControls();
// mInputManager->updateControls();
predictNext(0);
OpenXRActionEvent event{};
while (mInputManager->nextActionEvent(event))
{
Log(Debug::Verbose) << "ActionEvent action=" << event.action << " onPress=" << event.onPress;
if (event.action == MWInput::InputManager::A_GameMenu)
{
Log(Debug::Verbose) << "A_GameMenu";
auto* menuLayer = dynamic_cast<OpenXRMenu*>(mLayerStack.layerObjects()[OpenXRLayerStack::MENU_VIEW_LAYER]);
if (menuLayer)
{
menuLayer->setVisible(!menuLayer->isVisible());
menuLayer->updatePosition();
}
}
}
//OpenXRActionEvent event{};
//while (mInputManager->nextActionEvent(event))
//{
// Log(Debug::Verbose) << "ActionEvent action=" << event.action << " onPress=" << event.onPress;
// if (event.action == MWInput::InputManager::A_GameMenu)
// {
// Log(Debug::Verbose) << "A_GameMenu";
// auto* menuLayer = dynamic_cast<OpenXRMenu*>(mLayerStack.layerObjects()[OpenXRLayerStack::MENU_VIEW_LAYER]);
// if (menuLayer)
// {
// menuLayer->setVisible(!menuLayer->isVisible());
// menuLayer->updatePosition();
// }
// }
//}
mPredictionsReady = true;
}
void OpenXRSession::showMenu(bool show)
{
auto* menuLayer = dynamic_cast<OpenXRMenu*>(mLayerStack.layerObjects()[OpenXRLayerStack::MENU_VIEW_LAYER]);
if (menuLayer)
{
bool change = show != menuLayer->isVisible();
menuLayer->setVisible(show);
// Automatically update position of menu whenever the menu opens.
// This ensures menus are always opened near the player.
if(change)
menuLayer->updatePosition();
}
}
void OpenXRSession::updateMenuPosition(void)
{
auto* menuLayer = dynamic_cast<OpenXRMenu*>(mLayerStack.layerObjects()[OpenXRLayerStack::MENU_VIEW_LAYER]);
if (menuLayer)
{
menuLayer->updatePosition();
}
}
// OSG doesn't provide API to extract yaw from a quat, but i need it.
// Credits goes to Dennis Bunfield, i just copied his formula https://narkive.com/v0re6547.4
static float getYaw(const osg::Quat& quat)
{
osg::Matrixd m2(osg::Matrixd::rotate(quat));
double* mat = (double*)m2.ptr();
double angle_x = 0.0;
double angle_y = 0.0;
double angle_z = 0.0;
double D, C, tr_x, tr_y;
angle_y = D = asin(mat[2]); /* Calculate Y-axis angle */
C = cos(angle_y);
if (fabs(C) > 0.005) /* Test for Gimball lock? */
{
tr_x = mat[10] / C; /* No, so get X-axis angle */
tr_y = -mat[6] / C;
angle_x = atan2(tr_y, tr_x);
tr_x = mat[0] / C; /* Get Z-axis angle */
tr_y = -mat[1] / C;
angle_z = atan2(tr_y, tr_x);
}
else /* Gimball lock has occurred */
{
angle_x = 0; /* Set X-axis angle to zero
*/
tr_x = mat[5]; /* And calculate Z-axis angle
*/
tr_y = mat[4];
angle_z = atan2(tr_y, tr_x);
}
return angle_z;
}
float OpenXRSession::movementYaw(void)
{
osg::Matrix lookAt;
lookAt.makeLookAt(osg::Vec3(0, 0, 0), osg::Vec3(0, 1, 0), osg::Vec3(0, 0, 1));
lookAt = osg::Matrix::inverse(lookAt);
auto lhandquat = predictedPoses().hands[(int)MWVR::TrackedSpace::STAGE][(int)MWVR::Chirality::LEFT_HAND].orientation * lookAt.getRotate();
return getYaw(lhandquat);
}
void OpenXRSession::predictNext(int extraPeriods)
{
auto mPredictedDisplayTime = mXR->impl().frameState().predictedDisplayTime;
auto input = MWBase::Environment::get().getXRInputManager();
// Update pose predictions
mPredictedPoses.head[(int)TrackedSpace::STAGE] = mXR->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE);
mPredictedPoses.head[(int)TrackedSpace::VIEW] = mXR->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::VIEW);
mPredictedPoses.hands[(int)TrackedSpace::STAGE] = mInputManager->getHandPoses(mPredictedDisplayTime, TrackedSpace::STAGE);
mPredictedPoses.hands[(int)TrackedSpace::VIEW] = mInputManager->getHandPoses(mPredictedDisplayTime, TrackedSpace::VIEW);
mPredictedPoses.hands[(int)TrackedSpace::STAGE] = input->getHandPoses(mPredictedDisplayTime, TrackedSpace::STAGE);
mPredictedPoses.hands[(int)TrackedSpace::VIEW] = input->getHandPoses(mPredictedDisplayTime, TrackedSpace::VIEW);
auto stageViews = mXR->impl().getPredictedViews(mPredictedDisplayTime, TrackedSpace::STAGE);
auto hmdViews = mXR->impl().getPredictedViews(mPredictedDisplayTime, TrackedSpace::VIEW);
mPredictedPoses.eye[(int)TrackedSpace::STAGE][(int)Chirality::LEFT_HAND] = fromXR(stageViews[(int)Chirality::LEFT_HAND].pose);

View file

@ -11,42 +11,46 @@
#include <components/settings/settings.hpp>
#include "openxrmanager.hpp"
#include "openxrlayer.hpp"
#include "openxrinputmanager.hpp"
namespace MWVR
{
class OpenXRSession
{
using seconds = std::chrono::duration<double>;
using nanoseconds = std::chrono::nanoseconds;
using clock = std::chrono::steady_clock;
using time_point = clock::time_point;
class OpenXRSession
{
using seconds = std::chrono::duration<double>;
using nanoseconds = std::chrono::nanoseconds;
using clock = std::chrono::steady_clock;
using time_point = clock::time_point;
public:
OpenXRSession(osg::ref_ptr<OpenXRManager> XR);
~OpenXRSession();
public:
OpenXRSession(osg::ref_ptr<OpenXRManager> XR);
~OpenXRSession();
void setLayer(OpenXRLayerStack::Layer layerType, OpenXRLayer* layer);
void swapBuffers(osg::GraphicsContext* gc);
void setLayer(OpenXRLayerStack::Layer layerType, OpenXRLayer* layer);
void swapBuffers(osg::GraphicsContext* gc);
PoseSets& predictedPoses() { return mPredictedPoses; };
PoseSets& predictedPoses() { return mPredictedPoses; };
//! Call before updating poses and other inputs
void waitFrame();
//! Call before updating poses
void waitFrame();
//! Update predictions
void predictNext(int extraPeriods);
//! Update predictions
void predictNext(int extraPeriods);
void showMenu(bool show);
osg::ref_ptr<OpenXRManager> mXR;
std::unique_ptr<OpenXRInputManager> mInputManager = nullptr;
OpenXRLayerStack mLayerStack{};
void updateMenuPosition(void);
PoseSets mPredictedPoses{};
//! Yaw the movement if a tracking limb is configured
float movementYaw(void);
bool mPredictionsReady{ false };
};
osg::ref_ptr<OpenXRManager> mXR;
OpenXRLayerStack mLayerStack{};
PoseSets mPredictedPoses{};
bool mPredictionsReady{ false };
};
}

View file

@ -1,4 +1,5 @@
#include "openxrviewer.hpp"
#include "openxrsession.hpp"
#include "openxrmanagerimpl.hpp"
#include "Windows.h"
#include "../mwrender/vismask.hpp"
@ -20,14 +21,13 @@ namespace MWVR
float metersPerUnit)
: osg::Group()
, mXR(XR)
, mCompositionLayerProjectionViews(2, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW})
, mRealizeOperation(new RealizeOperation(XR, this))
, mViewer(viewer)
, mMetersPerUnit(metersPerUnit)
, mConfigured(false)
, mCompositionLayerProjectionViews(2, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW})
, mXRSession(nullptr)
, mPreDraw(new PredrawCallback(this))
, mPostDraw(new PostdrawCallback(this))
, mMetersPerUnit(metersPerUnit)
, mConfigured(false)
{
mViewer->setRealizeOperation(mRealizeOperation);
mCompositionLayerProjectionViews[0].pose.orientation.w = 1;
@ -160,16 +160,16 @@ namespace MWVR
menuCamera->setPreDrawCallback(mPreDraw);
menuCamera->setPostDrawCallback(mPostDraw);
mViewer->addSlave(menuCamera, true);
auto* session = MWBase::Environment::get().getXRSession();
mXRSession.reset(new OpenXRSession(mXR));
mViewer->getSlave(0)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, mXRSession.get(), leftView, context);
mViewer->getSlave(1)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, mXRSession.get(), rightView, context);
mViewer->addSlave(menuCamera, true);
mViewer->getSlave(0)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, session, leftView, context);
mViewer->getSlave(1)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, session, rightView, context);
mainCamera->getGraphicsContext()->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this));
mainCamera->setGraphicsContext(nullptr);
mXRSession->setLayer(OpenXRLayerStack::WORLD_VIEW_LAYER, this);
mXRSession->setLayer(OpenXRLayerStack::MENU_VIEW_LAYER, dynamic_cast<OpenXRLayer*>(mViews["MenuView"].get()));
session->setLayer(OpenXRLayerStack::WORLD_VIEW_LAYER, this);
session->setLayer(OpenXRLayerStack::MENU_VIEW_LAYER, dynamic_cast<OpenXRLayer*>(mViews["MenuView"].get()));
mConfigured = true;
}
@ -205,7 +205,7 @@ namespace MWVR
OpenXRViewer::SwapBuffersCallback::swapBuffersImplementation(
osg::GraphicsContext* gc)
{
mViewer->mXRSession->swapBuffers(gc);
MWBase::Environment::get().getXRSession()->swapBuffers(gc);
}
void OpenXRViewer::swapBuffers(osg::GraphicsContext* gc)
@ -220,7 +220,6 @@ namespace MWVR
mViews["RightEye"]->swapBuffers(gc);
timer.checkpoint("Views");
auto eyePoses = mXRSession->predictedPoses().eye;
auto leftEyePose = toXR(mViews["LeftEye"]->predictedPose());
auto rightEyePose = toXR(mViews["RightEye"]->predictedPose());
mCompositionLayerProjectionViews[0].pose = leftEyePose;
@ -273,7 +272,7 @@ namespace MWVR
if (mXR->sessionRunning())
{
mXR->beginFrame();
auto& poses = mXRSession->predictedPoses();
auto& poses = MWBase::Environment::get().getXRSession()->predictedPoses();
auto menuPose = poses.head[(int)TrackedSpace::STAGE];
mViews["MenuView"]->setPredictedPose(menuPose);
}
@ -307,7 +306,7 @@ namespace MWVR
return;
}
auto& poses = mXRSession->predictedPoses();
auto& poses = MWBase::Environment::get().getXRSession()->predictedPoses();
auto handPosesView = poses.hands[(int)TrackedSpace::VIEW];
auto handPosesStage = poses.hands[(int)TrackedSpace::STAGE];
int chirality = (int)Chirality::LEFT_HAND;

View file

@ -13,7 +13,6 @@
#include "openxrlayer.hpp"
#include "openxrworldview.hpp"
#include "openxrmenu.hpp"
#include "openxrinputmanager.hpp"
#include <components/sceneutil/positionattitudetransform.hpp>
struct XrCompositionLayerProjection;
@ -109,12 +108,11 @@ namespace MWVR
public:
osg::observer_ptr<OpenXRManager> mXR = nullptr;
std::unique_ptr<XrCompositionLayerProjection> mLayer = nullptr;
std::vector<XrCompositionLayerProjectionView> mCompositionLayerProjectionViews;
osg::observer_ptr<OpenXRManager> mXR = nullptr;
osg::ref_ptr<OpenXRManager::RealizeOperation> mRealizeOperation = nullptr;
osg::observer_ptr<osgViewer::Viewer> mViewer = nullptr;
std::unique_ptr<OpenXRSession> mXRSession = nullptr;
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
std::map<std::string, osg::ref_ptr<OpenXRView> > mViews{};
std::map<std::string, osg::ref_ptr<osg::Camera> > mCameras{};