From da73df1707a66ccede7b4d43656e7bcd35d6838a Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 15 Feb 2020 20:01:11 +0100 Subject: [PATCH] Basic input management --- apps/openmw/engine.cpp | 12 +- apps/openmw/engine_vr.cpp | 3 + apps/openmw/mwbase/environment.cpp | 25 ++ apps/openmw/mwbase/environment.hpp | 114 ++++--- apps/openmw/mwinput/inputmanagerimp.cpp | 1 + apps/openmw/mwinput/inputmanagerimp.hpp | 6 +- apps/openmw/mwphysics/physicssystem.cpp | 26 +- apps/openmw/mwvr/openxrinputmanager.cpp | 399 +++++++++++++++++------- apps/openmw/mwvr/openxrinputmanager.hpp | 42 ++- apps/openmw/mwvr/openxrmanagerimpl.cpp | 2 +- apps/openmw/mwvr/openxrmenu.cpp | 4 +- apps/openmw/mwvr/openxrsession.cpp | 109 +++++-- apps/openmw/mwvr/openxrsession.hpp | 50 +-- apps/openmw/mwvr/openxrviewer.cpp | 27 +- apps/openmw/mwvr/openxrviewer.hpp | 6 +- 15 files changed, 585 insertions(+), 241 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 376553f77..5a18eebe4 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -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(); diff --git a/apps/openmw/engine_vr.cpp b/apps/openmw/engine_vr.cpp index 49d5c46f0..bbadfb789 100644 --- a/apps/openmw/engine_vr.cpp +++ b/apps/openmw/engine_vr.cpp @@ -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; diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 5d01525b9..4d931f230 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -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(mInputManager); + assert(xrInputManager); + return xrInputManager; +} + +MWVR::OpenXRSession* MWBase::Environment::getXRSession() const +{ + return mXrSession; +} + +void MWBase::Environment::setXRSession(MWVR::OpenXRSession* xrSession) +{ + mXrSession = xrSession; +} + +#endif diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 9163b21f3..c9b2d5eca 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -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 }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 7d28b4bae..6839bf3b7 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -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)) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 161211a13..167842fd8 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -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 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(); diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 31325cf21..e1d4d805d 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -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& standingCollisionTracker) + std::map& 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; igetPtr(), 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 diff --git a/apps/openmw/mwvr/openxrinputmanager.cpp b/apps/openmw/mwvr/openxrinputmanager.cpp index c1536f0fc..6c2d12826 100644 --- a/apps/openmw/mwvr/openxrinputmanager.cpp +++ b/apps/openmw/mwvr/openxrinputmanager.cpp @@ -1,11 +1,34 @@ #include "openxrinputmanager.hpp" #include "openxrmanager.hpp" #include "openxrmanagerimpl.hpp" -#include "../mwinput/inputmanagerimp.hpp" #include #include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 #include @@ -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 XR); + OpenXRInput(osg::ref_ptr XR); OpenXRAction createAction(XrActionType actionType, const std::string& actionName, const std::string& localName, const std::vector& 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 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 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 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 viewer, + osg::ref_ptr 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; + } + } } diff --git a/apps/openmw/mwvr/openxrinputmanager.hpp b/apps/openmw/mwvr/openxrinputmanager.hpp index fa4f789ac..b831831f9 100644 --- a/apps/openmw/mwvr/openxrinputmanager.hpp +++ b/apps/openmw/mwvr/openxrinputmanager.hpp @@ -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 @@ -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 XR); - ~OpenXRInputManager(); + public: + OpenXRInputManager( + SDL_Window* window, + osg::ref_ptr viewer, + osg::ref_ptr 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 mPrivate; + osg::ref_ptr mXRViewer; + std::unique_ptr mXRInput; }; } diff --git a/apps/openmw/mwvr/openxrmanagerimpl.cpp b/apps/openmw/mwvr/openxrmanagerimpl.cpp index bc322856c..4245e4b92 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.cpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.cpp @@ -504,7 +504,7 @@ namespace MWVR MWBase::Environment::get().getWorld()->getStore().get().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 diff --git a/apps/openmw/mwvr/openxrmenu.cpp b/apps/openmw/mwvr/openxrmenu.cpp index 517f8f07e..862373b5b 100644 --- a/apps/openmw/mwvr/openxrmenu.cpp +++ b/apps/openmw/mwvr/openxrmenu.cpp @@ -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; diff --git a/apps/openmw/mwvr/openxrsession.cpp b/apps/openmw/mwvr/openxrsession.cpp index 96a6d745f..d4ce5c395 100644 --- a/apps/openmw/mwvr/openxrsession.cpp +++ b/apps/openmw/mwvr/openxrsession.cpp @@ -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 #include @@ -27,7 +29,7 @@ namespace MWVR OpenXRSession::OpenXRSession( osg::ref_ptr 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(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(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(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(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); diff --git a/apps/openmw/mwvr/openxrsession.hpp b/apps/openmw/mwvr/openxrsession.hpp index 7bb71e429..189ba7945 100644 --- a/apps/openmw/mwvr/openxrsession.hpp +++ b/apps/openmw/mwvr/openxrsession.hpp @@ -11,42 +11,46 @@ #include #include "openxrmanager.hpp" #include "openxrlayer.hpp" -#include "openxrinputmanager.hpp" namespace MWVR { +class OpenXRSession +{ + using seconds = std::chrono::duration; + using nanoseconds = std::chrono::nanoseconds; + using clock = std::chrono::steady_clock; + using time_point = clock::time_point; - class OpenXRSession - { - using seconds = std::chrono::duration; - using nanoseconds = std::chrono::nanoseconds; - using clock = std::chrono::steady_clock; - using time_point = clock::time_point; +public: + OpenXRSession(osg::ref_ptr XR); + ~OpenXRSession(); - public: - OpenXRSession(osg::ref_ptr 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 mXR; - std::unique_ptr 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 mXR; + OpenXRLayerStack mLayerStack{}; + + PoseSets mPredictedPoses{}; + + bool mPredictionsReady{ false }; +}; } diff --git a/apps/openmw/mwvr/openxrviewer.cpp b/apps/openmw/mwvr/openxrviewer.cpp index b895779f7..8264e7741 100644 --- a/apps/openmw/mwvr/openxrviewer.cpp +++ b/apps/openmw/mwvr/openxrviewer.cpp @@ -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(mViews["MenuView"].get())); + session->setLayer(OpenXRLayerStack::WORLD_VIEW_LAYER, this); + session->setLayer(OpenXRLayerStack::MENU_VIEW_LAYER, dynamic_cast(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; diff --git a/apps/openmw/mwvr/openxrviewer.hpp b/apps/openmw/mwvr/openxrviewer.hpp index 107f5e05e..16890398f 100644 --- a/apps/openmw/mwvr/openxrviewer.hpp +++ b/apps/openmw/mwvr/openxrviewer.hpp @@ -13,7 +13,6 @@ #include "openxrlayer.hpp" #include "openxrworldview.hpp" #include "openxrmenu.hpp" -#include "openxrinputmanager.hpp" #include struct XrCompositionLayerProjection; @@ -109,12 +108,11 @@ namespace MWVR public: + osg::observer_ptr mXR = nullptr; std::unique_ptr mLayer = nullptr; std::vector mCompositionLayerProjectionViews; - osg::observer_ptr mXR = nullptr; osg::ref_ptr mRealizeOperation = nullptr; - osg::observer_ptr mViewer = nullptr; - std::unique_ptr mXRSession = nullptr; + osg::ref_ptr mViewer = nullptr; std::map > mViews{}; std::map > mCameras{};