mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-29 22:45:34 +00:00
First commit. Wrote an osgviewer, achieved display.
This commit is contained in:
parent
2693598d82
commit
2778775070
19 changed files with 2723 additions and 11 deletions
|
@ -9,7 +9,8 @@ option(BUILD_BSATOOL "Build BSA extractor" ON)
|
|||
option(BUILD_ESMTOOL "Build ESM inspector" ON)
|
||||
option(BUILD_NIFTEST "Build nif file tester" ON)
|
||||
option(BUILD_MYGUI_PLUGIN "Build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON)
|
||||
option(BUILD_DOCS "Build documentation." OFF )
|
||||
option(BUILD_DOCS "Build documentation." OFF)
|
||||
option(BUILD_VR_OPENXR "Build VR support using OpenXR" on)
|
||||
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
||||
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
|
||||
|
||||
|
@ -331,6 +332,12 @@ include_directories("."
|
|||
${Bullet_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(BUILD_VR_OPENXR)
|
||||
find_package(OpenXR REQUIRED)
|
||||
message(STATUS "OpenXR_FOUND: ${OpenXR_FOUND}")
|
||||
include_directories(${OpenXR_INCLUDE_DIR})
|
||||
endif(BUILD_VR_OPENXR)
|
||||
|
||||
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS})
|
||||
|
||||
if(MYGUI_STATIC)
|
||||
|
@ -688,6 +695,23 @@ if (WIN32)
|
|||
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||
endif()
|
||||
|
||||
# TODO: properties and link targets should be copied from openmw to openmw_vr instead of duplicating every line
|
||||
if (USE_DEBUG_CONSOLE AND BUILD_VR_OPENXR)
|
||||
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
|
||||
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
|
||||
set_target_properties(openmw_vr PROPERTIES COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_CONSOLE>)
|
||||
elseif (BUILD_VR_OPENXR)
|
||||
# Turn off debug console, debug output will be written to visual studio output instead
|
||||
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")
|
||||
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
|
||||
endif()
|
||||
|
||||
if (BUILD_VR_OPENXR)
|
||||
# Release builds don't use the debug console
|
||||
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
||||
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||
endif()
|
||||
|
||||
# Play a bit with the warning levels
|
||||
|
||||
set(WARNINGS "/Wall") # Since windows can only disable specific warnings, not enable them
|
||||
|
@ -787,6 +811,14 @@ if (WIN32)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if (BUILD_VR_OPENXR)
|
||||
if (OPENMW_UNITY_BUILD)
|
||||
set_target_properties(openmw_vr PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD} /bigobj")
|
||||
else()
|
||||
set_target_properties(openmw_vr PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (BUILD_WIZARD)
|
||||
set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
endif()
|
||||
|
|
|
@ -111,13 +111,48 @@ else ()
|
|||
)
|
||||
endif ()
|
||||
|
||||
if(BUILD_VR_OPENXR)
|
||||
set(OPENMW_VR_FILES
|
||||
engine_vr.cpp
|
||||
mwvr/openxrmanager.hpp
|
||||
mwvr/openxrmanager.cpp
|
||||
mwvr/openxrviewer.hpp
|
||||
mwvr/openxrviewer.cpp
|
||||
mwvr/openxrmanagerimpl.hpp
|
||||
mwvr/openxrmanagerimpl.cpp
|
||||
mwvr/openxrview.hpp
|
||||
mwvr/openxrview.cpp
|
||||
mwvr/openxrinputmanager.hpp
|
||||
mwvr/openxrinputmanager.cpp
|
||||
mwvr/openxrtexture.hpp
|
||||
mwvr/openxrtexture.cpp
|
||||
)
|
||||
|
||||
openmw_add_executable(openmw_vr
|
||||
${OPENMW_FILES}
|
||||
${OPENMW_VR_FILES}
|
||||
${GAME} ${GAME_HEADER}
|
||||
${APPLE_BUNDLE_RESOURCES}
|
||||
)
|
||||
|
||||
# Preprocessor variable used to control code paths in engine.hpp/engine.cpp
|
||||
# to enable VR output and controls.
|
||||
|
||||
# Bizarrely, this doesn't do anything.
|
||||
# target_compile_definitions(openmw_vr PUBLIC USE_OPENXR XR_USE_GRAPHICS_API_OPENGL XR_USE_PLATFORM_WIN32)
|
||||
|
||||
target_compile_options(openmw_vr PUBLIC -DUSE_OPENXR -DXR_USE_GRAPHICS_API_OPENGL -DXR_USE_PLATFORM_WIN32)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
# Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING
|
||||
# when we change the backend.
|
||||
include_directories(
|
||||
${FFmpeg_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(openmw
|
||||
set(OPENMW_LINK_TARGETS
|
||||
${OSG_LIBRARIES}
|
||||
${OPENTHREADS_LIBRARIES}
|
||||
${OSGPARTICLE_LIBRARIES}
|
||||
|
@ -140,6 +175,9 @@ target_link_libraries(openmw
|
|||
components
|
||||
)
|
||||
|
||||
target_link_libraries(openmw ${OPENMW_LINK_TARGETS})
|
||||
target_link_libraries(openmw_vr ${OPENMW_LINK_TARGETS} ${OpenXR_LIBRARIES})
|
||||
|
||||
if (ANDROID)
|
||||
set (OSG_PLUGINS
|
||||
-Wl,--whole-archive
|
||||
|
@ -152,7 +190,7 @@ if (ANDROID)
|
|||
${OSG_PLUGINS} -Wl,--no-whole-archive
|
||||
)
|
||||
|
||||
target_link_libraries(openmw
|
||||
target_link_libraries(openmw
|
||||
EGL
|
||||
android
|
||||
log
|
||||
|
@ -166,16 +204,19 @@ if (ANDROID)
|
|||
endif (ANDROID)
|
||||
|
||||
if (USE_SYSTEM_TINYXML)
|
||||
target_link_libraries(openmw ${TinyXML_LIBRARIES})
|
||||
target_link_libraries(openmw ${TinyXML_LIBRARIES})
|
||||
target_link_libraries(openmw_vr ${TinyXML_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if (NOT UNIX)
|
||||
target_link_libraries(openmw ${SDL2MAIN_LIBRARY})
|
||||
target_link_libraries(openmw ${SDL2MAIN_LIBRARY})
|
||||
target_link_libraries(openmw_vr ${SDL2MAIN_LIBRARY})
|
||||
endif()
|
||||
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(openmw_vr ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
|
@ -196,20 +237,27 @@ if(APPLE)
|
|||
POST_BUILD
|
||||
COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${BUNDLE_RESOURCES_DIR}/resources")
|
||||
|
||||
add_custom_command(TARGET openmw_vr
|
||||
POST_BUILD
|
||||
COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${BUNDLE_RESOURCES_DIR}/resources")
|
||||
|
||||
find_library(COCOA_FRAMEWORK Cocoa)
|
||||
find_library(IOKIT_FRAMEWORK IOKit)
|
||||
target_link_libraries(openmw ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK})
|
||||
target_link_libraries(openmw ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK})
|
||||
target_link_libraries(openmw_vr ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK})
|
||||
|
||||
if (FFmpeg_FOUND)
|
||||
find_library(COREVIDEO_FRAMEWORK CoreVideo)
|
||||
find_library(VDA_FRAMEWORK VideoDecodeAcceleration)
|
||||
target_link_libraries(openmw z ${COREVIDEO_FRAMEWORK} ${VDA_FRAMEWORK})
|
||||
target_link_libraries(openmw z ${COREVIDEO_FRAMEWORK} ${VDA_FRAMEWORK})
|
||||
target_link_libraries(openmw_vr z ${COREVIDEO_FRAMEWORK} ${VDA_FRAMEWORK})
|
||||
endif()
|
||||
endif(APPLE)
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions (--coverage)
|
||||
target_link_libraries(openmw gcov)
|
||||
target_link_libraries(openmw gcov)
|
||||
target_link_libraries(openmw_vr gcov)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
|
@ -220,5 +268,6 @@ if (MSVC)
|
|||
endif (MSVC)
|
||||
|
||||
if (WIN32)
|
||||
INSTALL(TARGETS openmw RUNTIME DESTINATION ".")
|
||||
INSTALL(TARGETS openmw RUNTIME DESTINATION ".")
|
||||
INSTALL(TARGETS openmw_vr RUNTIME DESTINATION ".")
|
||||
endif (WIN32)
|
||||
|
|
|
@ -432,6 +432,10 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
|||
camera->setGraphicsContext(graphicsWindow);
|
||||
camera->setViewport(0, 0, traits->width, traits->height);
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
initVr();
|
||||
#endif
|
||||
|
||||
mViewer->realize();
|
||||
|
||||
mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, traits->width, traits->height);
|
||||
|
@ -469,6 +473,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
createWindow(settings);
|
||||
|
||||
osg::ref_ptr<osg::Group> rootNode (new osg::Group);
|
||||
|
||||
mViewer->setSceneData(rootNode);
|
||||
|
||||
mVFS.reset(new VFS::Manager(mFSStrict));
|
||||
|
@ -687,6 +692,7 @@ void OMW::Engine::go()
|
|||
// Setup viewer
|
||||
mViewer = new osgViewer::Viewer;
|
||||
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
||||
mViewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
|
||||
|
||||
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,5)
|
||||
// Do not try to outsmart the OS thread scheduler (see bug #4785).
|
||||
|
@ -745,6 +751,13 @@ void OMW::Engine::go()
|
|||
mEnvironment.getWindowManager()->executeInConsole(mStartupScript);
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
auto* root = mViewer->getSceneData();
|
||||
mXRViewer->addChild(root);
|
||||
mViewer->setSceneData(mXRViewer);
|
||||
#endif
|
||||
|
||||
// Start the main rendering loop
|
||||
osg::Timer frameTimer;
|
||||
double simulationTime = 0.0;
|
||||
|
|
|
@ -13,6 +13,11 @@
|
|||
|
||||
#include "mwworld/ptr.hpp"
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
#include "mwvr/openxrmanager.hpp"
|
||||
#include "mwvr/openxrviewer.hpp"
|
||||
#endif
|
||||
|
||||
namespace Resource
|
||||
{
|
||||
class ResourceSystem;
|
||||
|
@ -63,6 +68,7 @@ namespace osgViewer
|
|||
class ScreenCaptureHandler;
|
||||
}
|
||||
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace OMW
|
||||
|
@ -205,6 +211,13 @@ namespace OMW
|
|||
|
||||
private:
|
||||
Files::ConfigurationManager& mCfgMgr;
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
osg::ref_ptr<MWVR::OpenXRManager> mXR;
|
||||
osg::ref_ptr<MWVR::OpenXRViewer> mXRViewer;
|
||||
|
||||
void initVr();
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
|
|
20
apps/openmw/engine_vr.cpp
Normal file
20
apps/openmw/engine_vr.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include "engine.hpp"
|
||||
#include "mwvr/openxrmanager.hpp"
|
||||
|
||||
#ifndef USE_OPENXR
|
||||
#error "USE_OPENXR not defined"
|
||||
#endif
|
||||
|
||||
void OMW::Engine::initVr()
|
||||
{
|
||||
if (!mViewer)
|
||||
throw std::logic_error("mViewer must be initialized before calling initVr()");
|
||||
|
||||
mXR = new MWVR::OpenXRManager();
|
||||
osg::ref_ptr<MWVR::OpenXRManager::RealizeOperation> realizeOperation = new MWVR::OpenXRManager::RealizeOperation(mXR);
|
||||
mViewer->setRealizeOperation(realizeOperation);
|
||||
mXRViewer = new MWVR::OpenXRViewer(mXR, realizeOperation, mViewer, 1.f);
|
||||
|
||||
// Viewers must be the top node of the scene.
|
||||
//mViewer->setSceneData(mXRViewer);
|
||||
}
|
|
@ -265,7 +265,7 @@ namespace MWInput
|
|||
|
||||
int mFakeDeviceID; //As we only support one controller at a time, use a fake deviceID so we don't lose bindings when switching controllers
|
||||
|
||||
private:
|
||||
public:
|
||||
enum Actions
|
||||
{
|
||||
// please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files
|
||||
|
|
738
apps/openmw/mwvr/openxrinputmanager.cpp
Normal file
738
apps/openmw/mwvr/openxrinputmanager.cpp
Normal file
|
@ -0,0 +1,738 @@
|
|||
#include "openxrinputmanager.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
#include <osg/Camera>
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
struct OpenXRAction
|
||||
{
|
||||
|
||||
// I want these to manage XrAction objects but i also didn't want to wrap these in unique pointers (useless dynamic allocation).
|
||||
// So i implemented move for them instead.
|
||||
OpenXRAction() = delete;
|
||||
OpenXRAction(OpenXRAction&& rhs) { mAction = nullptr; *this = std::move(rhs); }
|
||||
void operator=(OpenXRAction&& rhs);
|
||||
private:
|
||||
OpenXRAction(const OpenXRAction&) = default;
|
||||
OpenXRAction& operator=(const OpenXRAction&) = default;
|
||||
public:
|
||||
OpenXRAction(osg::ref_ptr<OpenXRManager> XR, XrAction action, XrActionType actionType, const std::string& actionName, const std::string& localName);
|
||||
~OpenXRAction();
|
||||
|
||||
|
||||
operator XrAction() { return mAction; }
|
||||
|
||||
bool getFloat(XrPath subactionPath, float& value);
|
||||
bool getBool(XrPath subactionPath, bool& value);
|
||||
bool getPose(XrPath subactionPath);
|
||||
bool applyHaptics(XrPath subactionPath, float amplitude);
|
||||
|
||||
osg::ref_ptr<OpenXRManager> mXR = nullptr;
|
||||
XrAction mAction = XR_NULL_HANDLE;
|
||||
XrActionType mType;
|
||||
std::string mName;
|
||||
std::string mLocalName;
|
||||
};
|
||||
|
||||
//! Wrapper around bool type input actions, ignoring all subactions
|
||||
struct OpenXRAction_Bool
|
||||
{
|
||||
OpenXRAction_Bool(OpenXRAction action);
|
||||
operator XrAction() { return mAction; }
|
||||
|
||||
void update();
|
||||
|
||||
//! True if action changed to being pressed in the last update
|
||||
bool actionOnPress() { return actionIsPressed() && actionChanged(); }
|
||||
//! True if action changed to being released in the last update
|
||||
bool actionOnRelease() { return actionIsReleased() && actionChanged(); };
|
||||
//! True if the action is currently being pressed
|
||||
bool actionIsPressed() { return mPressed; }
|
||||
//! True if the action is currently not being pressed
|
||||
bool actionIsReleased() { return !mPressed; }
|
||||
//! True if the action changed state in the last update
|
||||
bool actionChanged() { return mChanged; }
|
||||
|
||||
OpenXRAction mAction;
|
||||
bool mPressed = false;
|
||||
bool mChanged = false;
|
||||
};
|
||||
|
||||
//! Wrapper around float type input actions, ignoring all subactions
|
||||
struct OpenXRAction_Float
|
||||
{
|
||||
OpenXRAction_Float(OpenXRAction action);
|
||||
operator XrAction() { return mAction; }
|
||||
|
||||
void update();
|
||||
|
||||
//! Current value of the control, from -1.f to 1.f for sticks or 0.f to 1.f for controls
|
||||
float value() { return mValue; }
|
||||
|
||||
OpenXRAction mAction;
|
||||
float mValue = 0.f;
|
||||
};
|
||||
|
||||
struct OpenXRInputManagerImpl
|
||||
{
|
||||
using Actions = MWInput::InputManager::Actions;
|
||||
|
||||
enum SubAction : signed
|
||||
{
|
||||
NONE = -1, //!< Used to ignore subaction or when action has no subaction. Not a valid input to createAction().
|
||||
// hands should be 0 and 1 as they are the typical use case of needing to index between 2 actions or subactions.
|
||||
LEFT_HAND = 0, //!< Read action from left-hand controller
|
||||
RIGHT_HAND = 1, //!< Read action from right-hand controller
|
||||
GAMEPAD = 2, //!< Read action from a gamepad
|
||||
HEAD = 3, //!< Read action from HMD
|
||||
USER = 4, //!< Read action from other devices
|
||||
|
||||
SUBACTION_MAX = USER, //!< Used to size subaction arrays. Not a valid input.
|
||||
};
|
||||
|
||||
template<size_t Size>
|
||||
using ActionPaths = std::array<XrPath, Size>;
|
||||
using SubActionPaths = ActionPaths<SUBACTION_MAX + 1>;
|
||||
using ControllerActionPaths = ActionPaths<3>; // left hand, right hand, and gamepad
|
||||
|
||||
XrPath generateXrPath(const std::string& path);
|
||||
|
||||
ControllerActionPaths generateControllerActionPaths(const std::string& controllerAction);
|
||||
|
||||
OpenXRInputManagerImpl(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();
|
||||
void updateHands();
|
||||
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
SubActionPaths mSubactionPath;
|
||||
XrActionSet mActionSet = XR_NULL_HANDLE;
|
||||
|
||||
ControllerActionPaths mSelectPath;
|
||||
ControllerActionPaths mSqueezeValuePath;
|
||||
ControllerActionPaths mSqueezeClickPath;
|
||||
ControllerActionPaths mPosePath;
|
||||
ControllerActionPaths mHapticPath;
|
||||
ControllerActionPaths mMenuClickPath;
|
||||
ControllerActionPaths mThumbstickPath;
|
||||
ControllerActionPaths mThumbstickXPath;
|
||||
ControllerActionPaths mThumbstickYPath;
|
||||
ControllerActionPaths mThumbstickClickPath;
|
||||
ControllerActionPaths mXPath;
|
||||
ControllerActionPaths mYPath;
|
||||
ControllerActionPaths mAPath;
|
||||
ControllerActionPaths mBPath;
|
||||
ControllerActionPaths mTriggerClickPath;
|
||||
ControllerActionPaths mTriggerValuePath;
|
||||
|
||||
OpenXRAction_Bool mGameMenu;
|
||||
OpenXRAction_Bool mInventory;
|
||||
OpenXRAction_Bool mActivate;
|
||||
OpenXRAction_Bool mUse;
|
||||
OpenXRAction_Bool mJump;
|
||||
OpenXRAction_Bool mWeapon;
|
||||
OpenXRAction_Bool mSpell;
|
||||
OpenXRAction_Bool mCycleSpellLeft;
|
||||
OpenXRAction_Bool mCycleSpellRight;
|
||||
OpenXRAction_Bool mCycleWeaponLeft;
|
||||
OpenXRAction_Bool mCycleWeaponRight;
|
||||
OpenXRAction_Bool mSneak;
|
||||
OpenXRAction_Bool mQuickMenu;
|
||||
OpenXRAction_Float mLookLeftRight;
|
||||
OpenXRAction_Float mMoveForwardBackward;
|
||||
OpenXRAction_Float mMoveLeftRight;
|
||||
//OpenXRAction mUnused;
|
||||
//OpenXRAction mScreenshot;
|
||||
//OpenXRAction mConsole;
|
||||
//OpenXRAction mMoveLeft;
|
||||
//OpenXRAction mMoveRight;
|
||||
//OpenXRAction mMoveForward;
|
||||
//OpenXRAction mMoveBackward;
|
||||
//OpenXRAction mAutoMove;
|
||||
//OpenXRAction mRest;
|
||||
//OpenXRAction mJournal;
|
||||
//OpenXRAction mRun;
|
||||
//OpenXRAction mToggleSneak;
|
||||
//OpenXRAction mAlwaysRun;
|
||||
//OpenXRAction mQuickSave;
|
||||
//OpenXRAction mQuickLoad;
|
||||
//OpenXRAction mToggleWeapon;
|
||||
//OpenXRAction mToggleSpell;
|
||||
//OpenXRAction mTogglePOV;
|
||||
//OpenXRAction mQuickKey1;
|
||||
//OpenXRAction mQuickKey2;
|
||||
//OpenXRAction mQuickKey3;
|
||||
//OpenXRAction mQuickKey4;
|
||||
//OpenXRAction mQuickKey5;
|
||||
//OpenXRAction mQuickKey6;
|
||||
//OpenXRAction mQuickKey7;
|
||||
//OpenXRAction mQuickKey8;
|
||||
//OpenXRAction mQuickKey9;
|
||||
//OpenXRAction mQuickKey10;
|
||||
//OpenXRAction mQuickKeysMenu;
|
||||
//OpenXRAction mToggleHUD;
|
||||
//OpenXRAction mToggleDebug;
|
||||
//OpenXRAction mLookUpDown;
|
||||
//OpenXRAction mZoomIn;
|
||||
//OpenXRAction mZoomOut;
|
||||
|
||||
//! Needed to access all the actions that don't fit on the controllers
|
||||
OpenXRAction_Bool mActionsMenu;
|
||||
//! Economize buttons by accessing spell actions and weapon actions on the same keys, but with/without this modifier
|
||||
OpenXRAction_Bool mSpellModifier;
|
||||
|
||||
// Hand tracking
|
||||
OpenXRAction mHandPoseAction;
|
||||
OpenXRAction mHapticsAction;
|
||||
std::array<XrSpace, 2> mHandSpace;
|
||||
std::array<float, 2> mHandScale;
|
||||
std::array<XrBool32, 2> mHandActive;
|
||||
std::array<XrSpaceLocation, 2> mHandSpaceLocation{ { {XR_TYPE_SPACE_LOCATION}, {XR_TYPE_SPACE_LOCATION } } };
|
||||
|
||||
// Head tracking
|
||||
osg::Vec3f mHeadForward;
|
||||
osg::Vec3f mHeadUpward;
|
||||
osg::Vec3f mHeadRightward;
|
||||
|
||||
osg::Vec3f mHeadStagePosition;
|
||||
osg::Quat mHeadStageOrientation;
|
||||
osg::Vec3f mHeadWorldPosition;
|
||||
osg::Quat mHeadWorldOrientation;
|
||||
};
|
||||
|
||||
XrActionSet
|
||||
OpenXRInputManagerImpl::createActionSet()
|
||||
{
|
||||
XrActionSet actionSet = XR_NULL_HANDLE;
|
||||
XrActionSetCreateInfo createInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
|
||||
strcpy_s(createInfo.actionSetName, "gameplay");
|
||||
strcpy_s(createInfo.localizedActionSetName, "Gameplay");
|
||||
createInfo.priority = 0;
|
||||
CHECK_XRCMD(xrCreateActionSet(mXR->mPrivate->mInstance, &createInfo, &actionSet));
|
||||
return actionSet;
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRAction::operator=(
|
||||
OpenXRAction&& rhs)
|
||||
{
|
||||
if (mAction)
|
||||
xrDestroyAction(mAction);
|
||||
*this = static_cast<OpenXRAction&>(rhs);
|
||||
rhs.mAction = XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
|
||||
OpenXRAction::OpenXRAction(
|
||||
osg::ref_ptr<OpenXRManager> XR,
|
||||
XrAction action,
|
||||
XrActionType actionType,
|
||||
const std::string& actionName,
|
||||
const std::string& localName)
|
||||
: mXR(XR)
|
||||
, mAction(action)
|
||||
, mType(actionType)
|
||||
, mName(actionName)
|
||||
, mLocalName(localName)
|
||||
{
|
||||
};
|
||||
|
||||
OpenXRAction::~OpenXRAction() {
|
||||
if (mAction)
|
||||
{
|
||||
xrDestroyAction(mAction);
|
||||
std::cout << "I destroyed your \"" << mLocalName << "\" action" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenXRAction::getFloat(XrPath subactionPath, float& value)
|
||||
{
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = mAction;
|
||||
getInfo.subactionPath = subactionPath;
|
||||
|
||||
XrActionStateFloat xrValue{ XR_TYPE_ACTION_STATE_FLOAT };
|
||||
CHECK_XRCMD(xrGetActionStateFloat(mXR->mPrivate->mSession, &getInfo, &xrValue));
|
||||
|
||||
if (xrValue.isActive)
|
||||
value = xrValue.currentState;
|
||||
return xrValue.isActive;
|
||||
}
|
||||
|
||||
bool OpenXRAction::getBool(XrPath subactionPath, bool& value)
|
||||
{
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = mAction;
|
||||
getInfo.subactionPath = subactionPath;
|
||||
|
||||
XrActionStateBoolean xrValue{ XR_TYPE_ACTION_STATE_BOOLEAN };
|
||||
CHECK_XRCMD(xrGetActionStateBoolean(mXR->mPrivate->mSession, &getInfo, &xrValue));
|
||||
|
||||
if (xrValue.isActive)
|
||||
value = xrValue.currentState;
|
||||
return xrValue.isActive;
|
||||
}
|
||||
|
||||
// Pose action only checks if the pose is active or not
|
||||
bool OpenXRAction::getPose(XrPath subactionPath)
|
||||
{
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = mAction;
|
||||
getInfo.subactionPath = subactionPath;
|
||||
|
||||
XrActionStatePose xrValue{ XR_TYPE_ACTION_STATE_POSE };
|
||||
CHECK_XRCMD(xrGetActionStatePose(mXR->mPrivate->mSession, &getInfo, &xrValue));
|
||||
|
||||
return xrValue.isActive;
|
||||
}
|
||||
|
||||
bool OpenXRAction::applyHaptics(XrPath subactionPath, float amplitude)
|
||||
{
|
||||
XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
|
||||
vibration.amplitude = amplitude;
|
||||
vibration.duration = XR_MIN_HAPTIC_DURATION;
|
||||
vibration.frequency = XR_FREQUENCY_UNSPECIFIED;
|
||||
|
||||
XrHapticActionInfo hapticActionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
|
||||
hapticActionInfo.action = mAction;
|
||||
hapticActionInfo.subactionPath = subactionPath;
|
||||
CHECK_XRCMD(xrApplyHapticFeedback(mXR->mPrivate->mSession, &hapticActionInfo, (XrHapticBaseHeader*)&vibration));
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenXRAction_Bool::OpenXRAction_Bool(
|
||||
OpenXRAction action)
|
||||
: mAction(std::move(action))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRAction_Bool::update()
|
||||
{
|
||||
bool old = mPressed;
|
||||
mAction.getBool(0, mPressed);
|
||||
mChanged = mPressed != old;
|
||||
}
|
||||
|
||||
OpenXRAction_Float::OpenXRAction_Float(
|
||||
OpenXRAction action)
|
||||
: mAction(std::move(action))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRAction_Float::update()
|
||||
{
|
||||
mAction.getFloat(0, mValue);
|
||||
}
|
||||
|
||||
OpenXRInputManagerImpl::ControllerActionPaths
|
||||
OpenXRInputManagerImpl::generateControllerActionPaths(
|
||||
const std::string& controllerAction)
|
||||
{
|
||||
ControllerActionPaths actionPaths;
|
||||
|
||||
std::string left = std::string("/user/hand/left") + controllerAction;
|
||||
std::string right = std::string("/user/hand/right") + controllerAction;
|
||||
std::string pad = std::string("/user/gamepad") + controllerAction;
|
||||
|
||||
CHECK_XRCMD(xrStringToPath(mXR->mPrivate->mInstance, left.c_str(), &actionPaths[LEFT_HAND]));
|
||||
CHECK_XRCMD(xrStringToPath(mXR->mPrivate->mInstance, right.c_str(), &actionPaths[RIGHT_HAND]));
|
||||
CHECK_XRCMD(xrStringToPath(mXR->mPrivate->mInstance, pad.c_str(), &actionPaths[GAMEPAD]));
|
||||
|
||||
return actionPaths;
|
||||
}
|
||||
|
||||
OpenXRInputManagerImpl::OpenXRInputManagerImpl(
|
||||
osg::ref_ptr<OpenXRManager> XR)
|
||||
: mXR(XR)
|
||||
, mSubactionPath{ {
|
||||
generateXrPath("/user/hand/left"),
|
||||
generateXrPath("/user/hand/right"),
|
||||
generateXrPath("/user/gamepad"),
|
||||
generateXrPath("/user/head"),
|
||||
generateXrPath("/user")
|
||||
} }
|
||||
, mActionSet(createActionSet())
|
||||
, mSelectPath(generateControllerActionPaths("/input/select/click"))
|
||||
, mSqueezeValuePath(generateControllerActionPaths("/input/squeeze/value"))
|
||||
, mSqueezeClickPath(generateControllerActionPaths("/input/squeeze/click"))
|
||||
, mPosePath(generateControllerActionPaths("/input/grip/pose"))
|
||||
, mHapticPath(generateControllerActionPaths("/output/haptic"))
|
||||
, mMenuClickPath(generateControllerActionPaths("/input/menu/click"))
|
||||
, mThumbstickPath(generateControllerActionPaths("/input/thumbstick/value"))
|
||||
, mThumbstickXPath(generateControllerActionPaths("/input/thumbstick/x"))
|
||||
, mThumbstickYPath(generateControllerActionPaths("/input/thumbstick/y"))
|
||||
, mThumbstickClickPath(generateControllerActionPaths("/input/thumbstick/click"))
|
||||
, mXPath(generateControllerActionPaths("/input/x/click"))
|
||||
, mYPath(generateControllerActionPaths("/input/y/click"))
|
||||
, mAPath(generateControllerActionPaths("/input/a/click"))
|
||||
, mBPath(generateControllerActionPaths("/input/b/click"))
|
||||
, mTriggerValuePath(generateControllerActionPaths("/input/trigger/value"))
|
||||
, mTriggerClickPath(generateControllerActionPaths("/input/trigger/click"))
|
||||
, mActionsMenu(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "actions_menu", "Actions Menu", { })))
|
||||
, mSpellModifier(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "spell_modifier", "Spell Modifier", { })))
|
||||
, mGameMenu(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "game_menu", "GameMenu", { })))
|
||||
, mInventory(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "inventory", "Inventory", { })))
|
||||
, mActivate(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "activate", "Activate", { })))
|
||||
, mUse(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "use", "Use", { })))
|
||||
, mJump(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "jump", "Jump", { })))
|
||||
, mWeapon(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "weapon", "Weapon", { })))
|
||||
, mSpell(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "spell", "Spell", { })))
|
||||
, mCycleSpellLeft(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "cycle_spell_left", "CycleSpellLeft", { })))
|
||||
, mCycleSpellRight(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "cycle_spell_right", "CycleSpellRight", { })))
|
||||
, mCycleWeaponLeft(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "cycle_weapon_left", "CycleWeaponLeft", { })))
|
||||
, mCycleWeaponRight(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "cycle_weapon_right", "CycleWeaponRight", { })))
|
||||
, mSneak(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "sneak", "Sneak", { })))
|
||||
, mQuickMenu(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "quick_menu", "QuickMenu", { })))
|
||||
, mLookLeftRight(std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "look_left_right", "LookLeftRight", { })))
|
||||
, mMoveForwardBackward(std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "move_forward_backward", "MoveForwardBackward", { })))
|
||||
, mMoveLeftRight(std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "move_left_right", "MoveLeftRight", { })))
|
||||
, mHandPoseAction(std::move(createAction(XR_ACTION_TYPE_POSE_INPUT, "hand_pose", "Hand Pose", { LEFT_HAND, RIGHT_HAND })))
|
||||
, mHapticsAction(std::move(createAction(XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibrate_hand", "Vibrate Hand", { LEFT_HAND, RIGHT_HAND })))
|
||||
{
|
||||
|
||||
|
||||
|
||||
//{ mHandPoseAction, mPosePath[LEFT_HAND]},
|
||||
//{ mHandPoseAction, mPosePath[RIGHT_HAND] },
|
||||
//{ mHapticsAction, mHapticPath[LEFT_HAND] },
|
||||
//{ mHapticsAction, mHapticPath[RIGHT_HAND] },
|
||||
|
||||
//{ mLookLeftRight, mThumbstickXPath[RIGHT_HAND] },
|
||||
//{ mMoveLeftRight, mThumbstickXPath[LEFT_HAND] },
|
||||
//{ mMoveForwardBackward, mThumbstickYPath[LEFT_HAND] },
|
||||
//{ mActivate, mSqueezeClickPath[RIGHT_HAND] },
|
||||
//{ mUse, mTriggerClickPath[RIGHT_HAND] },
|
||||
//{ mJump, mTriggerValuePath[LEFT_HAND] },
|
||||
//{ mWeapon, mAPath[RIGHT_HAND] },
|
||||
//{ mSpell, mAPath[RIGHT_HAND] },
|
||||
//{ mCycleSpellLeft, mThumbstickClickPath[LEFT_HAND] },
|
||||
//{ mCycleSpellRight, mThumbstickClickPath[RIGHT_HAND] },
|
||||
//{ mCycleWeaponLeft, mThumbstickClickPath[LEFT_HAND] },
|
||||
//{ mCycleWeaponRight, mThumbstickClickPath[RIGHT_HAND] },
|
||||
//{ mSneak, mXPath[LEFT_HAND] },
|
||||
//{ mInventory, mBPath[RIGHT_HAND] },
|
||||
//{ mQuickMenu, mYPath[LEFT_HAND] },
|
||||
//{ mSpellModifier, mSqueezeClickPath[LEFT_HAND] },
|
||||
//{ mGameMenu, mMenuClickPath[LEFT_HAND] },
|
||||
|
||||
// There are not enough actions on controllers to assign everything.
|
||||
//mUnused = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "Unused", "Unused", { }));
|
||||
//mScreenshot = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "Screenshot", "Screenshot", { }));
|
||||
//mConsole = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "Console", "Console", { }));
|
||||
//mMoveLeft = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "MoveLeft", "MoveLeft", { }));
|
||||
//mMoveRight = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "MoveRight", "MoveRight", { }));
|
||||
//mMoveForward = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "MoveForward", "MoveForward", { }));
|
||||
//mMoveBackward = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "MoveBackward", "MoveBackward", { }));
|
||||
//mAutoMove = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "AutoMove", "AutoMove", { }));
|
||||
//mRest = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "Rest", "Rest", { }));
|
||||
//mJournal = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "Journal", "Journal", { }));
|
||||
//mRun = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "Run", "Run", { }));
|
||||
//mAlwaysRun = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "AlwaysRun", "AlwaysRun", { }));
|
||||
//mQuickSave = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickSave", "QuickSave", { }));
|
||||
//mQuickLoad = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickLoad", "QuickLoad", { }));
|
||||
//mToggleWeapon = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "ToggleWeapon", "ToggleWeapon", { }));
|
||||
//mToggleSpell = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "ToggleSpell", "ToggleSpell", { }));
|
||||
//mTogglePOV = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "TogglePOV", "TogglePOV", { }));
|
||||
//mQuickKey1 = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickKey1", "QuickKey1", { }));
|
||||
//mQuickKey2 = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickKey2", "QuickKey2", { }));
|
||||
//mQuickKey3 = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickKey3", "QuickKey3", { }));
|
||||
//mQuickKey4 = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickKey4", "QuickKey4", { }));
|
||||
//mQuickKey5 = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickKey5", "QuickKey5", { }));
|
||||
//mQuickKey6 = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickKey6", "QuickKey6", { }));
|
||||
//mQuickKey7 = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickKey7", "QuickKey7", { }));
|
||||
//mQuickKey8 = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickKey8", "QuickKey8", { }));
|
||||
//mQuickKey9 = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickKey9", "QuickKey9", { }));
|
||||
//mQuickKey10 = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickKey10", "QuickKey10", { }));
|
||||
//mQuickKeysMenu = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "QuickKeysMenu", "QuickKeysMenu", { }));
|
||||
//mToggleHUD = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "ToggleHUD", "ToggleHUD", { }));
|
||||
//mToggleDebug = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "ToggleDebug", "ToggleDebug", { }));
|
||||
//mToggleSneak = std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "ToggleSneak", "ToggleSneak", { }));
|
||||
//mLookUpDown = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "LookUpDown", "LookUpDown", { }));
|
||||
//mZoomIn = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "ZoomIn", "ZoomIn", { }));
|
||||
//mZoomOut = std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "ZoomOut", "ZoomOut", { }));
|
||||
|
||||
{ // Set up default bindings for the oculus
|
||||
XrPath oculusTouchInteractionProfilePath;
|
||||
CHECK_XRCMD(
|
||||
xrStringToPath(XR->mPrivate->mInstance, "/interaction_profiles/oculus/touch_controller", &oculusTouchInteractionProfilePath));
|
||||
std::vector<XrActionSuggestedBinding> bindings{ {
|
||||
{mHandPoseAction, mPosePath[LEFT_HAND]},
|
||||
{mHandPoseAction, mPosePath[RIGHT_HAND]},
|
||||
{mHapticsAction, mHapticPath[LEFT_HAND]},
|
||||
{mHapticsAction, mHapticPath[RIGHT_HAND]},
|
||||
{mLookLeftRight, mThumbstickXPath[RIGHT_HAND]},
|
||||
{mMoveLeftRight, mThumbstickXPath[LEFT_HAND]},
|
||||
{mMoveForwardBackward, mThumbstickYPath[LEFT_HAND]},
|
||||
{mActivate, mSqueezeClickPath[RIGHT_HAND]},
|
||||
{mUse, mTriggerClickPath[RIGHT_HAND]},
|
||||
{mJump, mTriggerValuePath[LEFT_HAND]},
|
||||
{mWeapon, mAPath[RIGHT_HAND]},
|
||||
{mSpell, mAPath[RIGHT_HAND]},
|
||||
{mCycleSpellLeft, mThumbstickClickPath[LEFT_HAND]},
|
||||
{mCycleSpellRight, mThumbstickClickPath[RIGHT_HAND]},
|
||||
{mCycleWeaponLeft, mThumbstickClickPath[LEFT_HAND]},
|
||||
{mCycleWeaponRight, mThumbstickClickPath[RIGHT_HAND]},
|
||||
{mSneak, mXPath[LEFT_HAND]},
|
||||
{mInventory, mBPath[RIGHT_HAND]},
|
||||
{mQuickMenu, mYPath[LEFT_HAND]},
|
||||
{mSpellModifier, mSqueezeClickPath[LEFT_HAND]},
|
||||
{mGameMenu, mMenuClickPath[LEFT_HAND]},
|
||||
} };
|
||||
XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
|
||||
suggestedBindings.interactionProfile = oculusTouchInteractionProfilePath;
|
||||
suggestedBindings.suggestedBindings = bindings.data();
|
||||
suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
|
||||
CHECK_XRCMD(xrSuggestInteractionProfileBindings(XR->mPrivate->mInstance, &suggestedBindings));
|
||||
|
||||
/*
|
||||
mSpellModifier; // L-Squeeze
|
||||
mActivate; // R-Squeeze
|
||||
mUse; // R-Trigger
|
||||
mJump; // L-Trigger. L-trigger has value, can be used to make measured jumps
|
||||
mWeapon; // A
|
||||
mSpell; // A + SpellModifier
|
||||
mRun; // Based on movement thumbstick value: broken line ( 0 (stand), 0.5 (walk), 1.0 (run) ). Remember to scale fatigue use.
|
||||
mCycleSpellLeft; // L-ThumbstickClick + SpellModifier
|
||||
mCycleSpellRight; // R-ThumbstickClick + SpellModifier
|
||||
mCycleWeaponLeft; // L-ThumbstickClick
|
||||
mCycleWeaponRight; // R-ThumbstickClick
|
||||
mSneak; // X
|
||||
mInventory; // B
|
||||
mQuickMenu; // Y
|
||||
mGameMenu; // Menu
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
{ // Set up action spaces
|
||||
XrActionSpaceCreateInfo createInfo{ XR_TYPE_ACTION_SPACE_CREATE_INFO };
|
||||
createInfo.action = mHandPoseAction;
|
||||
createInfo.poseInActionSpace.orientation.w = 1.f;
|
||||
createInfo.subactionPath = mSubactionPath[LEFT_HAND];
|
||||
CHECK_XRCMD(xrCreateActionSpace(XR->mPrivate->mSession, &createInfo, &mHandSpace[LEFT_HAND]));
|
||||
createInfo.subactionPath = mSubactionPath[RIGHT_HAND];
|
||||
CHECK_XRCMD(xrCreateActionSpace(XR->mPrivate->mSession, &createInfo, &mHandSpace[RIGHT_HAND]));
|
||||
}
|
||||
|
||||
{ // Set up the action set
|
||||
XrSessionActionSetsAttachInfo attachInfo{ XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO };
|
||||
attachInfo.countActionSets = 1;
|
||||
attachInfo.actionSets = &mActionSet;
|
||||
CHECK_XRCMD(xrAttachSessionActionSets(XR->mPrivate->mSession, &attachInfo));
|
||||
}
|
||||
};
|
||||
|
||||
OpenXRAction
|
||||
OpenXRInputManagerImpl::createAction(
|
||||
XrActionType actionType,
|
||||
const std::string& actionName,
|
||||
const std::string& localName,
|
||||
const std::vector<SubAction>& subActions)
|
||||
{
|
||||
std::vector<XrPath> subactionPaths;
|
||||
XrActionCreateInfo createInfo{ XR_TYPE_ACTION_CREATE_INFO };
|
||||
createInfo.actionType = actionType;
|
||||
strcpy_s(createInfo.actionName, actionName.c_str());
|
||||
strcpy_s(createInfo.localizedActionName, localName.c_str());
|
||||
|
||||
if (!subActions.empty())
|
||||
{
|
||||
for (auto subAction : subActions)
|
||||
subactionPaths.push_back(subactionPath(subAction));
|
||||
createInfo.countSubactionPaths = subactionPaths.size();
|
||||
createInfo.subactionPaths = subactionPaths.data();
|
||||
}
|
||||
|
||||
XrAction action = XR_NULL_HANDLE;
|
||||
CHECK_XRCMD(xrCreateAction(mActionSet, &createInfo, &action));
|
||||
return OpenXRAction(mXR, action, actionType, actionName, localName);
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRInputManagerImpl::updateHandTracking()
|
||||
{
|
||||
for (auto hand : { LEFT_HAND, RIGHT_HAND }) {
|
||||
CHECK_XRCMD(xrLocateSpace(mHandSpace[hand], mXR->mPrivate->mReferenceSpaceStage, mXR->mPrivate->mFrameState.predictedDisplayTime, &mHandSpaceLocation[hand]));
|
||||
}
|
||||
}
|
||||
|
||||
XrPath
|
||||
OpenXRInputManagerImpl::subactionPath(
|
||||
SubAction subAction)
|
||||
{
|
||||
if (subAction == NONE)
|
||||
return 0;
|
||||
return mSubactionPath[subAction];
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRInputManagerImpl::updateControls()
|
||||
{
|
||||
if (!mXR->mPrivate->mSessionRunning)
|
||||
return;
|
||||
|
||||
// TODO: Should be in OpenXRViewer
|
||||
//mXR->waitFrame();
|
||||
//mXR->beginFrame();
|
||||
|
||||
updateHead();
|
||||
updateHands();
|
||||
|
||||
// This set of actions only care about on-press
|
||||
if (mActionsMenu.actionOnPress())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mGameMenu.actionOnPress())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mInventory.actionOnPress())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mActivate.actionOnPress())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mUse.actionOnPress())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mJump.actionOnPress())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mSneak.actionOnPress())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mQuickMenu.actionOnPress())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
|
||||
// Weapon/Spell actions
|
||||
if (mWeapon.actionOnPress() && !mSpellModifier.actionIsPressed())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mCycleWeaponLeft.actionOnPress() && !mSpellModifier.actionIsPressed())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mCycleWeaponRight.actionOnPress() && !mSpellModifier.actionIsPressed())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mSpell.actionOnPress() && mSpellModifier.actionIsPressed())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mCycleSpellLeft.actionOnPress() && mSpellModifier.actionIsPressed())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
if (mCycleSpellRight.actionOnPress() && mSpellModifier.actionIsPressed())
|
||||
{
|
||||
// Generate on press action
|
||||
}
|
||||
|
||||
|
||||
float lookLeftRight = mLookLeftRight.value();
|
||||
float moveLeftRight = mMoveLeftRight.value();
|
||||
float moveForwardBackward = mMoveForwardBackward.value();
|
||||
|
||||
// Propagate movement to openmw
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void OpenXRInputManagerImpl::updateHands()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
XrPath OpenXRInputManagerImpl::generateXrPath(const std::string& path)
|
||||
{
|
||||
XrPath xrpath = 0;
|
||||
CHECK_XRCMD(xrStringToPath(mXR->mPrivate->mInstance, path.c_str(), &xrpath));
|
||||
return xrpath;
|
||||
}
|
||||
|
||||
OpenXRInputManager::OpenXRInputManager(
|
||||
osg::ref_ptr<OpenXRManager> XR)
|
||||
: mPrivate(new OpenXRInputManagerImpl(XR))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OpenXRInputManager::~OpenXRInputManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRInputManager::updateControls()
|
||||
{
|
||||
mPrivate->updateControls();
|
||||
}
|
||||
}
|
25
apps/openmw/mwvr/openxrinputmanager.hpp
Normal file
25
apps/openmw/mwvr/openxrinputmanager.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef OPENXR_INPUT_MANAGER_HPP
|
||||
#define OPENXR_INPUT_MANAGER_HPP
|
||||
|
||||
#include "openxrmanager.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
struct OpenXRInputManagerImpl;
|
||||
struct OpenXRInputManager
|
||||
{
|
||||
OpenXRInputManager(osg::ref_ptr<OpenXRManager> XR);
|
||||
~OpenXRInputManager();
|
||||
|
||||
void updateControls();
|
||||
|
||||
std::unique_ptr<OpenXRInputManagerImpl> mPrivate;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
160
apps/openmw/mwvr/openxrmanager.cpp
Normal file
160
apps/openmw/mwvr/openxrmanager.cpp
Normal file
|
@ -0,0 +1,160 @@
|
|||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
#include <openxr/openxr_platform.h>
|
||||
#include <openxr/openxr_platform_defines.h>
|
||||
#include <openxr/openxr_reflection.h>
|
||||
|
||||
#include <osg/Camera>
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
|
||||
|
||||
OpenXRManager::OpenXRManager()
|
||||
: mPrivate(nullptr)
|
||||
, mMutex()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OpenXRManager::~OpenXRManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
OpenXRManager::realized()
|
||||
{
|
||||
return !!mPrivate;
|
||||
}
|
||||
|
||||
long long
|
||||
OpenXRManager::frameIndex()
|
||||
{
|
||||
if (realized())
|
||||
return mPrivate->frameIndex;
|
||||
}
|
||||
|
||||
bool OpenXRManager::sessionRunning()
|
||||
{
|
||||
if (realized())
|
||||
return mPrivate->mSessionRunning;
|
||||
return false;
|
||||
}
|
||||
|
||||
void OpenXRManager::handleEvents()
|
||||
{
|
||||
if (realized())
|
||||
return mPrivate->handleEvents();
|
||||
}
|
||||
|
||||
void OpenXRManager::waitFrame()
|
||||
{
|
||||
if (realized())
|
||||
return mPrivate->waitFrame();
|
||||
}
|
||||
|
||||
void OpenXRManager::beginFrame()
|
||||
{
|
||||
if (realized())
|
||||
return mPrivate->beginFrame();
|
||||
}
|
||||
|
||||
void OpenXRManager::endFrame()
|
||||
{
|
||||
if (realized())
|
||||
return mPrivate->endFrame();
|
||||
}
|
||||
|
||||
void OpenXRManager::updateControls()
|
||||
{
|
||||
if (realized())
|
||||
return mPrivate->updateControls();
|
||||
}
|
||||
|
||||
void OpenXRManager::updatePoses()
|
||||
{
|
||||
if (realized())
|
||||
return mPrivate->updatePoses();
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManager::realize(
|
||||
osg::GraphicsContext* gc)
|
||||
{
|
||||
lock_guard lock(mMutex);
|
||||
if (!realized())
|
||||
{
|
||||
gc->makeCurrent();
|
||||
try {
|
||||
mPrivate = std::make_shared<OpenXRManagerImpl>();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Log(Debug::Error) << "Exception thrown by OpenXR: " << e.what();
|
||||
osg::ref_ptr<osg::State> state = gc->getState();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenXRManager::setPoseUpdateCallback(PoseUpdateCallback::TrackedLimb limb, PoseUpdateCallback::TrackingMode mode, osg::ref_ptr<PoseUpdateCallback> cb)
|
||||
{
|
||||
if (realized())
|
||||
return mPrivate->setPoseUpdateCallback(limb, mode, cb);
|
||||
}
|
||||
|
||||
void OpenXRManager::setViewSubImage(int eye, const ::XrSwapchainSubImage& subImage)
|
||||
{
|
||||
if (realized())
|
||||
return mPrivate->setViewSubImage(eye, subImage);
|
||||
}
|
||||
|
||||
int OpenXRManager::eyes()
|
||||
{
|
||||
if (realized())
|
||||
return mPrivate->eyes();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManager::RealizeOperation::operator()(
|
||||
osg::GraphicsContext* gc)
|
||||
{
|
||||
osg::ref_ptr<OpenXRManager> XR;
|
||||
mXR->realize(gc);
|
||||
}
|
||||
|
||||
bool
|
||||
OpenXRManager::RealizeOperation::realized()
|
||||
{
|
||||
return mXR->realized();
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManager::CleanupOperation::operator()(
|
||||
osg::GraphicsContext* gc)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManager::SwapBuffersCallback::swapBuffersImplementation(
|
||||
osg::GraphicsContext* gc)
|
||||
{
|
||||
gc->swapBuffersImplementation();
|
||||
mXR->endFrame();
|
||||
}
|
||||
}
|
116
apps/openmw/mwvr/openxrmanager.hpp
Normal file
116
apps/openmw/mwvr/openxrmanager.hpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
#ifndef MWVR_OPENRXMANAGER_H
|
||||
#define MWVR_OPENRXMANAGER_H
|
||||
#ifndef USE_OPENXR
|
||||
#error "openxrmanager.hpp included without USE_OPENXR defined"
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <osg/Camera>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
|
||||
struct XrSwapchainSubImage;
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
|
||||
// Use the pimpl pattern to avoid cluttering the namespace with openxr dependencies.
|
||||
class OpenXRManagerImpl;
|
||||
|
||||
class OpenXRManager : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
class PoseUpdateCallback: public osg::Referenced
|
||||
{
|
||||
public:
|
||||
enum TrackedLimb
|
||||
{
|
||||
LEFT_HAND,
|
||||
RIGHT_HAND,
|
||||
HEAD
|
||||
};
|
||||
|
||||
//! Describes how position is tracked. Orientation is always absolute.
|
||||
enum TrackingMode
|
||||
{
|
||||
STAGE_ABSOLUTE, //!< Position in VR stage. Meaning relative to some floor level origin
|
||||
STAGE_RELATIVE, //!< Same as STAGE_ABSOLUTE but receive only changes in position
|
||||
};
|
||||
|
||||
virtual void operator()(osg::Vec3 position, osg::Quat orientation) = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
class RealizeOperation : public osg::GraphicsOperation
|
||||
{
|
||||
public:
|
||||
RealizeOperation(osg::ref_ptr<OpenXRManager> XR) : osg::GraphicsOperation("OpenXRRealizeOperation", false), mXR(XR) {};
|
||||
void operator()(osg::GraphicsContext* gc) override;
|
||||
bool realized();
|
||||
|
||||
private:
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
};
|
||||
|
||||
class CleanupOperation : public osg::GraphicsOperation
|
||||
{
|
||||
public:
|
||||
CleanupOperation(osg::ref_ptr<OpenXRManager> XR) : osg::GraphicsOperation("OpenXRCleanupOperation", false), mXR(XR) {};
|
||||
void operator()(osg::GraphicsContext* gc) override;
|
||||
|
||||
private:
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
};
|
||||
|
||||
class SwapBuffersCallback : public osg::GraphicsContext::SwapCallback
|
||||
{
|
||||
public:
|
||||
SwapBuffersCallback(osg::ref_ptr<OpenXRManager> XR) : mXR(XR) {};
|
||||
void swapBuffersImplementation(osg::GraphicsContext* gc) override;
|
||||
|
||||
private:
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
};
|
||||
|
||||
public:
|
||||
OpenXRManager();
|
||||
|
||||
~OpenXRManager();
|
||||
|
||||
bool realized();
|
||||
|
||||
long long frameIndex();
|
||||
bool sessionRunning();
|
||||
|
||||
void handleEvents();
|
||||
void waitFrame();
|
||||
void beginFrame();
|
||||
void endFrame();
|
||||
void updateControls();
|
||||
void updatePoses();
|
||||
|
||||
void realize(osg::GraphicsContext* gc);
|
||||
|
||||
void setPoseUpdateCallback(PoseUpdateCallback::TrackedLimb limb, PoseUpdateCallback::TrackingMode mode, osg::ref_ptr<PoseUpdateCallback> cb);
|
||||
|
||||
void setViewSubImage(int eye, const ::XrSwapchainSubImage& subImage);
|
||||
|
||||
int eyes();
|
||||
|
||||
private:
|
||||
friend class OpenXRViewImpl;
|
||||
friend class OpenXRInputManagerImpl;
|
||||
friend class OpenXRAction;
|
||||
friend class OpenXRViewer;
|
||||
|
||||
std::shared_ptr<OpenXRManagerImpl> mPrivate;
|
||||
std::mutex mMutex;
|
||||
using lock_guard = std::lock_guard<std::mutex>;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
535
apps/openmw/mwvr/openxrmanagerimpl.cpp
Normal file
535
apps/openmw/mwvr/openxrmanagerimpl.cpp
Normal file
|
@ -0,0 +1,535 @@
|
|||
#include "openxrmanagerimpl.hpp"
|
||||
#include "openxrtexture.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
|
||||
// The OpenXR SDK assumes we've included Windows.h
|
||||
#include <Windows.h>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
#include <openxr/openxr_platform.h>
|
||||
#include <openxr/openxr_platform_defines.h>
|
||||
#include <openxr/openxr_reflection.h>
|
||||
|
||||
#include <osg/Camera>
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
#define ENUM_CASE_STR(name, val) case name: return #name;
|
||||
#define MAKE_TO_STRING_FUNC(enumType) \
|
||||
inline const char* to_string(enumType e) { \
|
||||
switch (e) { \
|
||||
XR_LIST_ENUM_##enumType(ENUM_CASE_STR) \
|
||||
default: return "Unknown " #enumType; \
|
||||
} \
|
||||
}
|
||||
|
||||
MAKE_TO_STRING_FUNC(XrReferenceSpaceType);
|
||||
MAKE_TO_STRING_FUNC(XrViewConfigurationType);
|
||||
MAKE_TO_STRING_FUNC(XrEnvironmentBlendMode);
|
||||
MAKE_TO_STRING_FUNC(XrSessionState);
|
||||
MAKE_TO_STRING_FUNC(XrResult);
|
||||
MAKE_TO_STRING_FUNC(XrFormFactor);
|
||||
MAKE_TO_STRING_FUNC(XrStructureType);
|
||||
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
OpenXRManagerImpl::OpenXRManagerImpl()
|
||||
{
|
||||
std::vector<const char*> extensions = {
|
||||
XR_KHR_OPENGL_ENABLE_EXTENSION_NAME
|
||||
};
|
||||
|
||||
{ // Create Instance
|
||||
XrInstanceCreateInfo createInfo{ XR_TYPE_INSTANCE_CREATE_INFO };
|
||||
createInfo.next = nullptr;
|
||||
createInfo.enabledExtensionCount = extensions.size();
|
||||
createInfo.enabledExtensionNames = extensions.data();
|
||||
|
||||
strcpy(createInfo.applicationInfo.applicationName, "Boo");
|
||||
createInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
|
||||
CHECK_XRCMD(xrCreateInstance(&createInfo, &mInstance));
|
||||
assert(mInstance);
|
||||
}
|
||||
|
||||
{ // Get system ID
|
||||
XrSystemGetInfo systemInfo{ XR_TYPE_SYSTEM_GET_INFO };
|
||||
systemInfo.formFactor = mFormFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
|
||||
CHECK_XRCMD(xrGetSystem(mInstance, &systemInfo, &mSystemId));
|
||||
assert(mSystemId);
|
||||
}
|
||||
|
||||
{ // Initialize OpenGL device
|
||||
|
||||
// This doesn't appear to be intended to do anything of consequence, only return requirements to me. But xrCreateSession fails if xrGetOpenGLGraphicsRequirementsKHR is not called.
|
||||
// Oculus Bug?
|
||||
PFN_xrGetOpenGLGraphicsRequirementsKHR p_getRequirements = nullptr;
|
||||
xrGetInstanceProcAddr(mInstance, "xrGetOpenGLGraphicsRequirementsKHR", reinterpret_cast<PFN_xrVoidFunction*>(&p_getRequirements));
|
||||
XrGraphicsRequirementsOpenGLKHR requirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR };
|
||||
CHECK_XRCMD(p_getRequirements(mInstance, mSystemId, &requirements));
|
||||
|
||||
//GLint major = 0;
|
||||
//GLint minor = 0;
|
||||
//glGetIntegerv(GL_MAJOR_VERSION, &major);
|
||||
//glGetIntegerv(GL_MINOR_VERSION, &minor);
|
||||
|
||||
const XrVersion desiredApiVersion = XR_MAKE_VERSION(4, 6, 0);
|
||||
if (requirements.minApiVersionSupported > desiredApiVersion) {
|
||||
std::cout << "Runtime does not support desired Graphics API and/or version" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
{ // Create Session
|
||||
// TODO: Platform dependent
|
||||
mGraphicsBinding.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
|
||||
mGraphicsBinding.next = nullptr;
|
||||
mGraphicsBinding.hDC = wglGetCurrentDC();
|
||||
mGraphicsBinding.hGLRC = wglGetCurrentContext();
|
||||
|
||||
if (!mGraphicsBinding.hDC)
|
||||
std::cout << "Missing DC" << std::endl;
|
||||
if (!mGraphicsBinding.hGLRC)
|
||||
std::cout << "Missing GLRC" << std::endl;
|
||||
|
||||
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
|
||||
createInfo.next = &mGraphicsBinding;
|
||||
createInfo.systemId = mSystemId;
|
||||
createInfo.createFlags;
|
||||
CHECK_XRCMD(xrCreateSession(mInstance, &createInfo, &mSession));
|
||||
assert(mSession);
|
||||
}
|
||||
|
||||
LogLayersAndExtensions();
|
||||
LogInstanceInfo();
|
||||
LogReferenceSpaces();
|
||||
|
||||
{ // Set up reference space
|
||||
XrReferenceSpaceCreateInfo createInfo{ XR_TYPE_REFERENCE_SPACE_CREATE_INFO };
|
||||
createInfo.poseInReferenceSpace.orientation.w = 1.f; // Identity pose
|
||||
createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
|
||||
CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceView));
|
||||
createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
|
||||
CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceStage));
|
||||
}
|
||||
|
||||
{ // Set up layers
|
||||
|
||||
mLayer.space = mReferenceSpaceStage;
|
||||
mLayer.viewCount = (uint32_t)mProjectionLayerViews.size();
|
||||
mLayer.views = mProjectionLayerViews.data();
|
||||
}
|
||||
|
||||
{ // Read and log graphics properties for the swapchain
|
||||
xrGetSystemProperties(mInstance, mSystemId, &mSystemProperties);
|
||||
|
||||
// Log system properties.
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "System Properties: Name=" << mSystemProperties.systemName << " VendorId=" << mSystemProperties.vendorId << std::endl;
|
||||
ss << "System Graphics Properties: MaxWidth=" << mSystemProperties.graphicsProperties.maxSwapchainImageWidth;
|
||||
ss << " MaxHeight=" << mSystemProperties.graphicsProperties.maxSwapchainImageHeight;
|
||||
ss << " MaxLayers=" << mSystemProperties.graphicsProperties.maxLayerCount << std::endl;
|
||||
ss << "System Tracking Properties: OrientationTracking=" << mSystemProperties.trackingProperties.orientationTracking ? "True" : "False";
|
||||
ss << " PositionTracking=" << mSystemProperties.trackingProperties.positionTracking ? "True" : "False";
|
||||
Log(Debug::Verbose) << ss.str();
|
||||
}
|
||||
|
||||
|
||||
uint32_t viewCount = 0;
|
||||
CHECK_XRCMD(xrEnumerateViewConfigurationViews(mInstance, mSystemId, mViewConfigType, 2, &viewCount, mConfigViews.data()));
|
||||
if (viewCount != 2)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "xrEnumerateViewConfigurationViews returned " << viewCount << " views";
|
||||
Log(Debug::Verbose) << ss.str();
|
||||
}
|
||||
|
||||
// TODO: This, including the projection layer views, should be moved to openxrviewer
|
||||
//for (unsigned i = 0; i < 2; i++)
|
||||
//{
|
||||
// mEyes[i].reset(new OpenXRView(this, mConfigViews[i]));
|
||||
|
||||
// mProjectionLayerViews[i].subImage.swapchain = mEyes[i]->mSwapchain;
|
||||
// mProjectionLayerViews[i].subImage.imageRect.offset = { 0, 0 };
|
||||
// mProjectionLayerViews[i].subImage.imageRect.extent = { mEyes[i]->mWidth, mEyes[i]->mHeight };
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
inline XrResult CheckXrResult(XrResult res, const char* originator, const char* sourceLocation) {
|
||||
if (XR_FAILED(res)) {
|
||||
Log(Debug::Error) << sourceLocation << ": OpenXR[" << to_string(res) << "]: " << originator;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << to_string(res) << "][" << std::this_thread::get_id() << "][" << wglGetCurrentDC() << "][" << wglGetCurrentContext() << "]: " << originator;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
OpenXRManagerImpl::~OpenXRManagerImpl()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
OpenXRManagerImpl::LogLayersAndExtensions() {
|
||||
// Write out extension properties for a given layer.
|
||||
const auto logExtensions = [](const char* layerName, int indent = 0) {
|
||||
uint32_t instanceExtensionCount;
|
||||
CHECK_XRCMD(xrEnumerateInstanceExtensionProperties(layerName, 0, &instanceExtensionCount, nullptr));
|
||||
|
||||
std::vector<XrExtensionProperties> extensions(instanceExtensionCount);
|
||||
for (XrExtensionProperties& extension : extensions) {
|
||||
extension.type = XR_TYPE_EXTENSION_PROPERTIES;
|
||||
}
|
||||
|
||||
CHECK_XRCMD(xrEnumerateInstanceExtensionProperties(layerName, (uint32_t)extensions.size(), &instanceExtensionCount,
|
||||
extensions.data()));
|
||||
|
||||
const std::string indentStr(indent, ' ');
|
||||
|
||||
std::stringstream ss;
|
||||
ss << indentStr.c_str() << "Available Extensions: (" << instanceExtensionCount << ")" << std::endl;
|
||||
|
||||
|
||||
for (const XrExtensionProperties& extension : extensions) {
|
||||
ss << indentStr << " Name=" << std::string(extension.extensionName) << " SpecVersion=" << extension.extensionVersion << std::endl;
|
||||
}
|
||||
|
||||
Log(Debug::Verbose) << ss.str();
|
||||
};
|
||||
|
||||
// Log non-layer extensions (layerName==nullptr).
|
||||
logExtensions(nullptr);
|
||||
|
||||
// Log layers and any of their extensions.
|
||||
{
|
||||
uint32_t layerCount;
|
||||
CHECK_XRCMD(xrEnumerateApiLayerProperties(0, &layerCount, nullptr));
|
||||
|
||||
std::vector<XrApiLayerProperties> layers(layerCount);
|
||||
for (XrApiLayerProperties& layer : layers) {
|
||||
layer.type = XR_TYPE_API_LAYER_PROPERTIES;
|
||||
}
|
||||
|
||||
CHECK_XRCMD(xrEnumerateApiLayerProperties((uint32_t)layers.size(), &layerCount, layers.data()));
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Available Layers: (" << layerCount << ")" << std::endl;
|
||||
|
||||
for (const XrApiLayerProperties& layer : layers) {
|
||||
ss << " Name=" << layer.layerName << " SpecVersion=" << layer.layerVersion << std::endl;
|
||||
logExtensions(layer.layerName, 2);
|
||||
}
|
||||
|
||||
Log(Debug::Verbose) << ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManagerImpl::LogInstanceInfo() {
|
||||
|
||||
XrInstanceProperties instanceProperties{ XR_TYPE_INSTANCE_PROPERTIES };
|
||||
xrGetInstanceProperties(mInstance, &instanceProperties);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Instance RuntimeName=" << instanceProperties.runtimeName << " RuntimeVersion=" << instanceProperties.runtimeVersion;
|
||||
Log(Debug::Verbose) << ss.str();
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManagerImpl::LogReferenceSpaces() {
|
||||
|
||||
uint32_t spaceCount;
|
||||
xrEnumerateReferenceSpaces(mSession, 0, &spaceCount, nullptr);
|
||||
std::vector<XrReferenceSpaceType> spaces(spaceCount);
|
||||
xrEnumerateReferenceSpaces(mSession, spaceCount, &spaceCount, spaces.data());
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Available reference spaces=" << spaceCount << std::endl;
|
||||
|
||||
for (XrReferenceSpaceType space : spaces)
|
||||
ss << " Name: " << to_string(space) << std::endl;
|
||||
Log(Debug::Verbose) << ss.str();
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManagerImpl::waitFrame()
|
||||
{
|
||||
if (!mSessionRunning)
|
||||
return;
|
||||
|
||||
XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO };
|
||||
XrFrameState frameState{ XR_TYPE_FRAME_STATE };
|
||||
CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState));
|
||||
mFrameState = frameState;
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManagerImpl::beginFrame()
|
||||
{
|
||||
if (!mSessionRunning)
|
||||
return;
|
||||
|
||||
XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO };
|
||||
CHECK_XRCMD(xrBeginFrame(mSession, &frameBeginInfo));
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManagerImpl::endFrame()
|
||||
{
|
||||
if (!mSessionRunning)
|
||||
return;
|
||||
|
||||
XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO };
|
||||
frameEndInfo.displayTime = mFrameState.predictedDisplayTime;
|
||||
frameEndInfo.environmentBlendMode = mEnvironmentBlendMode;
|
||||
frameEndInfo.layerCount = (uint32_t)1;
|
||||
frameEndInfo.layers = &mLayer_p;
|
||||
CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo));
|
||||
}
|
||||
|
||||
std::array<XrView, 2> OpenXRManagerImpl::getStageViews()
|
||||
{
|
||||
// Get the pose of each eye in the "stage" reference space.
|
||||
// TODO: I likely won't ever use this, since it is only useful if the game world and the
|
||||
// stage space have matching orientation, which is extremely unlikely. Instead we have
|
||||
// to keep track of the head pose separately and move our world position based on progressive
|
||||
// changes.
|
||||
|
||||
// In more detail:
|
||||
// When we apply yaw to the game world to allow free rotation of the character, the orientation
|
||||
// of the stage space and our world space deviates which breaks free movement.
|
||||
//
|
||||
// If we align the orientations by yawing the head pose, that yaw will happen around the origin
|
||||
// of the stage space rather than the character, which will not be comfortable (or make any sense) to the player.
|
||||
//
|
||||
// If we align the orientations by yawing the view poses, the yaw will happen around the character
|
||||
// as intended, but physically walking will move the player in the wrong direction.
|
||||
//
|
||||
// The solution that solves both problems is to yaw the view pose *and* progressively track changes in head pose
|
||||
// in the stage space and yaw that change before adding it to our separately tracked pose.
|
||||
|
||||
std::array<XrView, 2> views{ {{XR_TYPE_VIEW}, {XR_TYPE_VIEW}} };
|
||||
XrViewState viewState{ XR_TYPE_VIEW_STATE };
|
||||
uint32_t viewCount = 2;
|
||||
|
||||
XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO };
|
||||
viewLocateInfo.viewConfigurationType = mViewConfigType;
|
||||
viewLocateInfo.displayTime = mFrameState.predictedDisplayTime;
|
||||
|
||||
viewLocateInfo.space = mReferenceSpaceStage;
|
||||
CHECK_XRCMD(xrLocateViews(mSession, &viewLocateInfo, &viewState, viewCount, &viewCount, views.data()));
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
std::array<XrView, 2> OpenXRManagerImpl::getHmdViews()
|
||||
{
|
||||
// Eye poses relative to the HMD rarely change. But they might
|
||||
// if the user reconfigures his or her hmd during runtime, so we
|
||||
// re-read this every frame anyway.
|
||||
std::array<XrView, 2> views{ {{XR_TYPE_VIEW}, {XR_TYPE_VIEW}} };
|
||||
XrViewState viewState{ XR_TYPE_VIEW_STATE };
|
||||
uint32_t viewCount = 2;
|
||||
|
||||
XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO };
|
||||
viewLocateInfo.viewConfigurationType = mViewConfigType;
|
||||
viewLocateInfo.displayTime = mFrameState.predictedDisplayTime;
|
||||
|
||||
viewLocateInfo.space = mReferenceSpaceView;
|
||||
CHECK_XRCMD(xrLocateViews(mSession, &viewLocateInfo, &viewState, viewCount, &viewCount, views.data()));
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
XrSpaceLocation OpenXRManagerImpl::getHeadLocation()
|
||||
{
|
||||
// The pose of the "View" reference space is the pose of the HMD.
|
||||
XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION };
|
||||
CHECK_XRCMD(xrLocateSpace(mReferenceSpaceView, mReferenceSpaceStage, mFrameState.predictedDisplayTime, &location));
|
||||
return location;
|
||||
}
|
||||
|
||||
int OpenXRManagerImpl::eyes()
|
||||
{
|
||||
return mConfigViews.size();
|
||||
}
|
||||
|
||||
void OpenXRManagerImpl::handleEvents()
|
||||
{
|
||||
// React to events
|
||||
while (auto* event = nextEvent())
|
||||
{
|
||||
Log(Debug::Verbose) << "OpenXR: Event received: " << to_string(event->type);
|
||||
switch (event->type)
|
||||
{
|
||||
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
|
||||
{
|
||||
const auto* stateChangeEvent = reinterpret_cast<const XrEventDataSessionStateChanged*>(event);
|
||||
HandleSessionStateChanged(*stateChangeEvent);
|
||||
break;
|
||||
}
|
||||
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
|
||||
case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
|
||||
case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING:
|
||||
default: {
|
||||
Log(Debug::Verbose) << "OpenXR: Event ignored";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenXRManagerImpl::updateControls()
|
||||
{
|
||||
}
|
||||
void
|
||||
OpenXRManagerImpl::HandleSessionStateChanged(
|
||||
const XrEventDataSessionStateChanged& stateChangedEvent)
|
||||
{
|
||||
auto oldState = mSessionState;
|
||||
auto newState = stateChangedEvent.state;
|
||||
mSessionState = newState;
|
||||
|
||||
Log(Debug::Verbose) << "XrEventDataSessionStateChanged: state " << to_string(oldState) << "->" << to_string(newState);
|
||||
|
||||
|
||||
switch (newState)
|
||||
{
|
||||
case XR_SESSION_STATE_READY:
|
||||
case XR_SESSION_STATE_IDLE:
|
||||
{
|
||||
XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO };
|
||||
beginInfo.primaryViewConfigurationType = mViewConfigType;
|
||||
CHECK_XRCMD(xrBeginSession(mSession, &beginInfo));
|
||||
mSessionRunning = true;
|
||||
break;
|
||||
}
|
||||
case XR_SESSION_STATE_STOPPING:
|
||||
{
|
||||
mSessionRunning = false;
|
||||
CHECK_XRCMD(xrEndSession(mSession));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Log(Debug::Verbose) << "XrEventDataSessionStateChanged: Ignoring new strate " << to_string(newState);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRManagerImpl::setViewSubImage(
|
||||
int eye,
|
||||
const ::XrSwapchainSubImage& subImage)
|
||||
{
|
||||
if (eye >= mProjectionLayerViews.size())
|
||||
throw std::out_of_range("OpenXRManagerImpl::setViewSubImage: Eye index out of range");
|
||||
mProjectionLayerViews[eye].subImage = subImage;
|
||||
}
|
||||
|
||||
static void
|
||||
poseCallbacks(
|
||||
const XrPosef& newPose,
|
||||
const XrPosef& oldPose,
|
||||
OpenXRManager::PoseUpdateCallback* absoluteCB,
|
||||
OpenXRManager::PoseUpdateCallback* relativeCB
|
||||
)
|
||||
{
|
||||
osg::Quat quat = osg::Quat(
|
||||
newPose.orientation.x,
|
||||
newPose.orientation.y,
|
||||
newPose.orientation.z,
|
||||
newPose.orientation.w
|
||||
);
|
||||
|
||||
osg::Vec3 oldPos = osg::Vec3(
|
||||
oldPose.position.x,
|
||||
oldPose.position.y,
|
||||
oldPose.position.z
|
||||
);
|
||||
|
||||
osg::Vec3 newPos = osg::Vec3(
|
||||
oldPose.position.x,
|
||||
oldPose.position.y,
|
||||
oldPose.position.z
|
||||
);
|
||||
|
||||
if (absoluteCB)
|
||||
(*absoluteCB)(newPos, quat);
|
||||
if (relativeCB)
|
||||
(*relativeCB)(newPos - oldPos, quat);
|
||||
}
|
||||
|
||||
void OpenXRManagerImpl::updatePoses()
|
||||
{
|
||||
auto newHeadTrackedPose = getHeadLocation();
|
||||
poseCallbacks(newHeadTrackedPose.pose, mHeadTrackedPose, mHeadAbsoluteCB, mHeadRelativeCB);
|
||||
mHeadTrackedPose = newHeadTrackedPose.pose;
|
||||
|
||||
auto stageViews = getStageViews();
|
||||
mProjectionLayerViews[0].pose = stageViews[0].pose;
|
||||
mProjectionLayerViews[1].pose = stageViews[1].pose;
|
||||
mProjectionLayerViews[0].fov = stageViews[0].fov;
|
||||
mProjectionLayerViews[1].fov = stageViews[1].fov;
|
||||
}
|
||||
|
||||
void OpenXRManagerImpl::setPoseUpdateCallback(
|
||||
OpenXRManager::PoseUpdateCallback::TrackedLimb limb,
|
||||
OpenXRManager::PoseUpdateCallback::TrackingMode mode,
|
||||
osg::ref_ptr<OpenXRManager::PoseUpdateCallback> cb)
|
||||
{
|
||||
// This can clearly be made more generic if that ever becomes relevant
|
||||
switch (limb) {
|
||||
case OpenXRManager::PoseUpdateCallback::HEAD:
|
||||
if (mode == OpenXRManager::PoseUpdateCallback::STAGE_ABSOLUTE)
|
||||
mHeadAbsoluteCB = cb;
|
||||
else
|
||||
mHeadRelativeCB = cb;
|
||||
break;
|
||||
case OpenXRManager::PoseUpdateCallback::LEFT_HAND:
|
||||
if (mode == OpenXRManager::PoseUpdateCallback::STAGE_ABSOLUTE)
|
||||
mLeftHandAbsoluteCB = cb;
|
||||
else
|
||||
mLeftHandRelativeCB = cb;
|
||||
break;
|
||||
case OpenXRManager::PoseUpdateCallback::RIGHT_HAND:
|
||||
if (mode == OpenXRManager::PoseUpdateCallback::STAGE_ABSOLUTE)
|
||||
mRightHandAbsoluteCB = cb;
|
||||
else
|
||||
mRightHandRelativeCB = cb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const XrEventDataBaseHeader*
|
||||
OpenXRManagerImpl::nextEvent()
|
||||
{
|
||||
XrEventDataBaseHeader* baseHeader = reinterpret_cast<XrEventDataBaseHeader*>(&mEventDataBuffer);
|
||||
*baseHeader = { XR_TYPE_EVENT_DATA_BUFFER };
|
||||
const XrResult result = xrPollEvent(mInstance, &mEventDataBuffer);
|
||||
if (result == XR_SUCCESS)
|
||||
{
|
||||
if (baseHeader->type == XR_TYPE_EVENT_DATA_EVENTS_LOST) {
|
||||
const XrEventDataEventsLost* const eventsLost = reinterpret_cast<const XrEventDataEventsLost*>(baseHeader);
|
||||
Log(Debug::Warning) << "OpenXRManagerImpl: Lost " << eventsLost->lostEventCount << " events";
|
||||
}
|
||||
|
||||
return baseHeader;
|
||||
}
|
||||
|
||||
if (result != XR_EVENT_UNAVAILABLE)
|
||||
CHECK_XRRESULT(result, "xrPollEvent");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
94
apps/openmw/mwvr/openxrmanagerimpl.hpp
Normal file
94
apps/openmw/mwvr/openxrmanagerimpl.hpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
#ifndef OPENXR_MANAGER_IMPL_HPP
|
||||
#define OPENXR_MANAGER_IMPL_HPP
|
||||
|
||||
#include "openxrmanager.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
|
||||
// The OpenXR SDK assumes we've included Windows.h
|
||||
#include <Windows.h>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
#include <openxr/openxr_platform.h>
|
||||
#include <openxr/openxr_platform_defines.h>
|
||||
#include <openxr/openxr_reflection.h>
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
|
||||
#define CHK_STRINGIFY(x) #x
|
||||
#define TOSTRING(x) CHK_STRINGIFY(x)
|
||||
#define FILE_AND_LINE __FILE__ ":" TOSTRING(__LINE__)
|
||||
#define CHECK_XRCMD(cmd) CheckXrResult(cmd, #cmd, FILE_AND_LINE);
|
||||
#define CHECK_XRRESULT(res, cmdStr) CheckXrResult(res, cmdStr, FILE_AND_LINE);
|
||||
|
||||
XrResult CheckXrResult(XrResult res, const char* originator = nullptr, const char* sourceLocation = nullptr);
|
||||
|
||||
struct OpenXRManagerImpl
|
||||
{
|
||||
|
||||
OpenXRManagerImpl(void);
|
||||
~OpenXRManagerImpl(void);
|
||||
|
||||
void LogLayersAndExtensions();
|
||||
void LogInstanceInfo();
|
||||
void LogReferenceSpaces();
|
||||
|
||||
const XrEventDataBaseHeader* nextEvent();
|
||||
void waitFrame();
|
||||
void beginFrame();
|
||||
void endFrame();
|
||||
std::array<XrView, 2> getStageViews();
|
||||
std::array<XrView, 2> getHmdViews();
|
||||
XrSpaceLocation getHeadLocation();
|
||||
int eyes();
|
||||
void handleEvents();
|
||||
void updateControls();
|
||||
void updatePoses();
|
||||
void setPoseUpdateCallback(OpenXRManager::PoseUpdateCallback::TrackedLimb limb, OpenXRManager::PoseUpdateCallback::TrackingMode mode, osg::ref_ptr<OpenXRManager::PoseUpdateCallback> cb);
|
||||
void HandleSessionStateChanged(const XrEventDataSessionStateChanged& stateChangedEvent);
|
||||
void setViewSubImage(int eye, const ::XrSwapchainSubImage& subImage);
|
||||
|
||||
bool initialized = false;
|
||||
long long frameIndex = 0;
|
||||
XrInstance mInstance = XR_NULL_HANDLE;
|
||||
XrSession mSession = XR_NULL_HANDLE;
|
||||
XrSpace mSpace = XR_NULL_HANDLE;
|
||||
XrFormFactor mFormFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
|
||||
XrViewConfigurationType mViewConfigType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
||||
XrEnvironmentBlendMode mEnvironmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
|
||||
XrSystemId mSystemId = XR_NULL_SYSTEM_ID;
|
||||
XrGraphicsBindingOpenGLWin32KHR mGraphicsBinding{ XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR };
|
||||
XrSystemProperties mSystemProperties{ XR_TYPE_SYSTEM_PROPERTIES };
|
||||
std::array<XrViewConfigurationView, 2> mConfigViews{ { {XR_TYPE_VIEW_CONFIGURATION_VIEW}, {XR_TYPE_VIEW_CONFIGURATION_VIEW} } };
|
||||
XrSpace mReferenceSpaceView = XR_NULL_HANDLE;
|
||||
XrSpace mReferenceSpaceStage = XR_NULL_HANDLE;
|
||||
XrEventDataBuffer mEventDataBuffer{ XR_TYPE_EVENT_DATA_BUFFER };
|
||||
XrCompositionLayerProjection mLayer{ XR_TYPE_COMPOSITION_LAYER_PROJECTION };
|
||||
XrCompositionLayerBaseHeader const* mLayer_p = reinterpret_cast<XrCompositionLayerBaseHeader*>(&mLayer);
|
||||
std::array<XrCompositionLayerProjectionView, 2> mProjectionLayerViews{ { {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW} } };
|
||||
XrFrameState mFrameState{ XR_TYPE_FRAME_STATE };
|
||||
XrSessionState mSessionState = XR_SESSION_STATE_UNKNOWN;
|
||||
bool mSessionRunning = false;
|
||||
|
||||
XrPosef mHeadTrackedPose{};
|
||||
XrPosef mLeftHandTrackedPose{};
|
||||
XrPosef mRightHandTrackedPose{};
|
||||
|
||||
osg::ref_ptr<OpenXRManager::PoseUpdateCallback> mHeadAbsoluteCB = nullptr;
|
||||
osg::ref_ptr<OpenXRManager::PoseUpdateCallback> mHeadRelativeCB = nullptr;
|
||||
osg::ref_ptr<OpenXRManager::PoseUpdateCallback> mLeftHandAbsoluteCB = nullptr;
|
||||
osg::ref_ptr<OpenXRManager::PoseUpdateCallback> mLeftHandRelativeCB = nullptr;
|
||||
osg::ref_ptr<OpenXRManager::PoseUpdateCallback> mRightHandAbsoluteCB = nullptr;
|
||||
osg::ref_ptr<OpenXRManager::PoseUpdateCallback> mRightHandRelativeCB = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
172
apps/openmw/mwvr/openxrtexture.cpp
Normal file
172
apps/openmw/mwvr/openxrtexture.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
#include "openxrviewer.hpp"
|
||||
#include "openxrtexture.hpp"
|
||||
#include <osg/Texture2D>
|
||||
#include <osgViewer/Renderer>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <osgDB/Registry>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
#ifndef GL_TEXTURE_MAX_LEVEL
|
||||
#define GL_TEXTURE_MAX_LEVEL 0x813D
|
||||
#endif
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
OpenXRTextureBuffer::OpenXRTextureBuffer(
|
||||
osg::ref_ptr<OpenXRManager> XR,
|
||||
osg::ref_ptr<osg::State> state,
|
||||
uint32_t XRColorBuffer,
|
||||
std::size_t width,
|
||||
std::size_t height,
|
||||
uint32_t msaaSamples)
|
||||
: mXR(XR)
|
||||
, mState(state)
|
||||
, mWidth(width)
|
||||
, mHeight(height)
|
||||
, mXRColorBuffer(XRColorBuffer)
|
||||
, mMSAASamples(msaaSamples)
|
||||
{
|
||||
|
||||
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
||||
glBindTexture(GL_TEXTURE_2D, XRColorBuffer);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
GLint w = 0;
|
||||
GLint h = 0;
|
||||
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WIDTH, &w);
|
||||
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_HEIGHT, &h);
|
||||
|
||||
gl->glGenFramebuffers(1, &mFBO);
|
||||
|
||||
if (mMSAASamples == 0)
|
||||
{
|
||||
glGenTextures(1, &mDepthBuffer);
|
||||
glBindTexture(GL_TEXTURE_2D, mDepthBuffer);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, mWidth, mHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
|
||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO);
|
||||
gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, mXRColorBuffer, 0);
|
||||
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, mDepthBuffer, 0);
|
||||
|
||||
if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
|
||||
throw std::runtime_error("Failed to create OpenXR framebuffer");
|
||||
}
|
||||
else
|
||||
{
|
||||
gl->glGenFramebuffers(1, &mMSAAFBO);
|
||||
|
||||
// Create MSAA color buffer
|
||||
glGenTextures(1, &mMSAAColorBuffer);
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mMSAAColorBuffer);
|
||||
gl->glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, mMSAASamples, GL_RGBA, mWidth, mHeight, false);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER_ARB);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
|
||||
// Create MSAA depth buffer
|
||||
glGenTextures(1, &mMSAADepthBuffer);
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mMSAADepthBuffer);
|
||||
gl->glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, mMSAASamples, GL_DEPTH_COMPONENT, mWidth, mHeight, false);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
|
||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mMSAAFBO);
|
||||
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D_MULTISAMPLE, mMSAAColorBuffer, 0);
|
||||
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D_MULTISAMPLE, mMSAADepthBuffer, 0);
|
||||
if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
|
||||
throw std::runtime_error("Failed to create MSAA framebuffer");
|
||||
|
||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO);
|
||||
gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, mXRColorBuffer, 0);
|
||||
gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);
|
||||
if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
|
||||
throw std::runtime_error("Failed to create OpenXR framebuffer");
|
||||
}
|
||||
|
||||
|
||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
||||
}
|
||||
|
||||
OpenXRTextureBuffer::~OpenXRTextureBuffer()
|
||||
{
|
||||
destroy(nullptr);
|
||||
}
|
||||
|
||||
void OpenXRTextureBuffer::destroy(osg::State* state)
|
||||
{
|
||||
if (!state)
|
||||
{
|
||||
// Try re-using the state received during construction
|
||||
state = mState.get();
|
||||
}
|
||||
|
||||
if (state)
|
||||
{
|
||||
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
||||
if (mFBO)
|
||||
gl->glDeleteFramebuffers(1, &mFBO);
|
||||
if (mMSAAFBO)
|
||||
gl->glDeleteFramebuffers(1, &mMSAAFBO);
|
||||
}
|
||||
else if(mFBO || mMSAAFBO)
|
||||
// Without access to opengl methods, i'll just let the FBOs leak.
|
||||
Log(Debug::Warning) << "destroy() called without a State. Leaking FBOs";
|
||||
|
||||
if (mDepthBuffer)
|
||||
glDeleteTextures(1, &mDepthBuffer);
|
||||
if (mMSAAColorBuffer)
|
||||
glDeleteTextures(1, &mMSAAColorBuffer);
|
||||
if (mMSAADepthBuffer)
|
||||
glDeleteTextures(1, &mMSAADepthBuffer);
|
||||
|
||||
mFBO = mMSAAFBO = mDepthBuffer = mMSAAColorBuffer = mMSAADepthBuffer = 0;
|
||||
}
|
||||
|
||||
void OpenXRTextureBuffer::beginFrame(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
auto state = renderInfo.getState();
|
||||
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
||||
|
||||
|
||||
if (mMSAASamples == 0)
|
||||
{
|
||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO);
|
||||
}
|
||||
else
|
||||
{
|
||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mMSAAFBO);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenXRTextureBuffer::endFrame(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
auto* state = renderInfo.getState();
|
||||
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
||||
if (mMSAASamples == 0)
|
||||
{
|
||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, mMSAAFBO);
|
||||
gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, mFBO);
|
||||
gl->glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
||||
gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
|
||||
}
|
||||
}
|
||||
}
|
55
apps/openmw/mwvr/openxrtexture.hpp
Normal file
55
apps/openmw/mwvr/openxrtexture.hpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#ifndef MWVR_OPENRXTEXTURE_H
|
||||
#define MWVR_OPENRXTEXTURE_H
|
||||
|
||||
#include <memory>
|
||||
#include <osg/Group>
|
||||
#include <osg/Camera>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include "openxrmanager.hpp"
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
class OpenXRTextureBuffer : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
OpenXRTextureBuffer(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, uint32_t XRColorBuffer, std::size_t width, std::size_t height, uint32_t msaaSamples);
|
||||
~OpenXRTextureBuffer();
|
||||
|
||||
void destroy(osg::State* state);
|
||||
|
||||
auto width() const { return mWidth; }
|
||||
auto height() const { return mHeight; }
|
||||
auto msaaSamples() const { return mMSAASamples; }
|
||||
|
||||
void beginFrame(osg::RenderInfo& renderInfo);
|
||||
void endFrame(osg::RenderInfo& renderInfo);
|
||||
|
||||
void writeToJpg(osg::State& state, std::string filename);
|
||||
|
||||
private:
|
||||
osg::observer_ptr<OpenXRManager> mXR;
|
||||
|
||||
// Set aside a weak pointer to the constructor state to use when freeing FBOs, if no state is given to destroy()
|
||||
osg::observer_ptr<osg::State> mState;
|
||||
|
||||
// Metadata
|
||||
std::size_t mWidth = 0;
|
||||
std::size_t mHeight = 0;
|
||||
|
||||
// Swapchain buffer
|
||||
uint32_t mXRColorBuffer = 0;
|
||||
|
||||
// FBO target for swapchain buffer
|
||||
uint32_t mDepthBuffer = 0;
|
||||
uint32_t mFBO = 0;
|
||||
|
||||
// Render targets for MSAA
|
||||
uint32_t mMSAASamples = 0;
|
||||
uint32_t mMSAAFBO = 0;
|
||||
uint32_t mMSAAColorBuffer = 0;
|
||||
uint32_t mMSAADepthBuffer = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
375
apps/openmw/mwvr/openxrview.cpp
Normal file
375
apps/openmw/mwvr/openxrview.cpp
Normal file
|
@ -0,0 +1,375 @@
|
|||
#include "openxrview.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
#include <openxr/openxr_platform.h>
|
||||
#include <openxr/openxr_platform_defines.h>
|
||||
#include <openxr/openxr_reflection.h>
|
||||
|
||||
|
||||
#include <osg/Camera>
|
||||
#include <osgViewer/Renderer>
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
namespace osg {
|
||||
Quat fromXR(XrQuaternionf quat)
|
||||
{
|
||||
return Quat{ quat.x, quat.y, quat.z, quat.w };
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
class OpenXRViewImpl
|
||||
{
|
||||
public:
|
||||
enum SubView
|
||||
{
|
||||
LEFT_VIEW = 0,
|
||||
RIGHT_VIEW = 1,
|
||||
SUBVIEW_MAX = RIGHT_VIEW, //!< Used to size subview arrays. Not a valid input.
|
||||
};
|
||||
|
||||
OpenXRViewImpl(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, float metersPerUnit, unsigned int viewIndex);
|
||||
~OpenXRViewImpl();
|
||||
|
||||
osg::ref_ptr<OpenXRTextureBuffer> prepareNextSwapchainImage();
|
||||
void releaseSwapchainImage();
|
||||
void prerenderCallback(osg::RenderInfo& renderInfo);
|
||||
void postrenderCallback(osg::RenderInfo& renderInfo);
|
||||
osg::Camera* createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc);
|
||||
osg::Matrix projectionMatrix();
|
||||
osg::Matrix viewMatrix();
|
||||
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
XrSwapchain mSwapchain = XR_NULL_HANDLE;
|
||||
std::vector<XrSwapchainImageOpenGLKHR> mSwapchainImageBuffers{};
|
||||
std::vector<osg::ref_ptr<OpenXRTextureBuffer> > mTextureBuffers{};
|
||||
int32_t mWidth = -1;
|
||||
int32_t mHeight = -1;
|
||||
int64_t mSwapchainColorFormat = -1;
|
||||
XrViewConfigurationView mConfig{ XR_TYPE_VIEW_CONFIGURATION_VIEW };
|
||||
XrSwapchainSubImage mSubImage{};
|
||||
OpenXRTextureBuffer* mCurrentBuffer = nullptr;
|
||||
float mMetersPerUnit = 1.f;
|
||||
int mViewIndex = -1;
|
||||
};
|
||||
|
||||
OpenXRViewImpl::OpenXRViewImpl(
|
||||
osg::ref_ptr<OpenXRManager> XR,
|
||||
osg::ref_ptr<osg::State> state,
|
||||
float metersPerUnit,
|
||||
unsigned int viewIndex)
|
||||
: mXR(XR)
|
||||
, mConfig()
|
||||
, mMetersPerUnit(metersPerUnit)
|
||||
, mViewIndex(viewIndex)
|
||||
{
|
||||
auto& pXR = *(mXR->mPrivate);
|
||||
if (viewIndex >= pXR.mConfigViews.size())
|
||||
throw std::logic_error("viewIndex out of range");
|
||||
mConfig = pXR.mConfigViews[viewIndex];
|
||||
|
||||
// Select a swapchain format.
|
||||
uint32_t swapchainFormatCount;
|
||||
CHECK_XRCMD(xrEnumerateSwapchainFormats(pXR.mSession, 0, &swapchainFormatCount, nullptr));
|
||||
std::vector<int64_t> swapchainFormats(swapchainFormatCount);
|
||||
CHECK_XRCMD(xrEnumerateSwapchainFormats(pXR.mSession, (uint32_t)swapchainFormats.size(), &swapchainFormatCount, swapchainFormats.data()));
|
||||
|
||||
// List of supported color swapchain formats.
|
||||
constexpr int64_t SupportedColorSwapchainFormats[] = {
|
||||
GL_RGBA8,
|
||||
GL_RGBA8_SNORM,
|
||||
};
|
||||
|
||||
auto swapchainFormatIt =
|
||||
std::find_first_of(swapchainFormats.begin(), swapchainFormats.end(), std::begin(SupportedColorSwapchainFormats),
|
||||
std::end(SupportedColorSwapchainFormats));
|
||||
if (swapchainFormatIt == swapchainFormats.end()) {
|
||||
Log(Debug::Error) << "No swapchain format supported at runtime";
|
||||
}
|
||||
|
||||
mSwapchainColorFormat = *swapchainFormatIt;
|
||||
|
||||
Log(Debug::Verbose) << "Creating swapchain with dimensions Width=" << mConfig.recommendedImageRectWidth << " Heigh=" << mConfig.recommendedImageRectHeight << " SampleCount=" << mConfig.recommendedSwapchainSampleCount;
|
||||
|
||||
// Create the swapchain.
|
||||
XrSwapchainCreateInfo swapchainCreateInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO };
|
||||
swapchainCreateInfo.arraySize = 1;
|
||||
swapchainCreateInfo.format = mSwapchainColorFormat;
|
||||
swapchainCreateInfo.width = mConfig.recommendedImageRectWidth;
|
||||
swapchainCreateInfo.height = mConfig.recommendedImageRectHeight;
|
||||
swapchainCreateInfo.mipCount = 1;
|
||||
swapchainCreateInfo.faceCount = 1;
|
||||
swapchainCreateInfo.sampleCount = mConfig.recommendedSwapchainSampleCount;
|
||||
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
mWidth = mConfig.recommendedImageRectWidth;
|
||||
mHeight = mConfig.recommendedImageRectHeight;
|
||||
CHECK_XRCMD(xrCreateSwapchain(pXR.mSession, &swapchainCreateInfo, &mSwapchain));
|
||||
|
||||
uint32_t imageCount = 0;
|
||||
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, 0, &imageCount, nullptr));
|
||||
|
||||
mSwapchainImageBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
|
||||
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, imageCount, &imageCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(mSwapchainImageBuffers.data())));
|
||||
for (const auto& swapchainImage : mSwapchainImageBuffers)
|
||||
mTextureBuffers.push_back(new OpenXRTextureBuffer(mXR, state, swapchainImage.image, mWidth, mHeight, 0));
|
||||
|
||||
mSubImage.swapchain = mSwapchain;
|
||||
mSubImage.imageRect.offset = { 0, 0 };
|
||||
mSubImage.imageRect.extent = { mWidth, mHeight };
|
||||
mXR->setViewSubImage(viewIndex, mSubImage);
|
||||
}
|
||||
|
||||
OpenXRViewImpl::~OpenXRViewImpl()
|
||||
{
|
||||
if (mSwapchain)
|
||||
CHECK_XRCMD(xrDestroySwapchain(mSwapchain));
|
||||
}
|
||||
|
||||
osg::ref_ptr<OpenXRTextureBuffer>
|
||||
OpenXRViewImpl::prepareNextSwapchainImage()
|
||||
{
|
||||
if (!mXR->sessionRunning())
|
||||
return nullptr;
|
||||
|
||||
XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO };
|
||||
uint32_t swapchainImageIndex = 0;
|
||||
CHECK_XRCMD(xrAcquireSwapchainImage(mSwapchain, &acquireInfo, &swapchainImageIndex));
|
||||
|
||||
XrSwapchainImageWaitInfo waitInfo{ XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO };
|
||||
waitInfo.timeout = XR_INFINITE_DURATION;
|
||||
CHECK_XRCMD(xrWaitSwapchainImage(mSwapchain, &waitInfo));
|
||||
|
||||
return mTextureBuffers[swapchainImageIndex];
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRViewImpl::releaseSwapchainImage()
|
||||
{
|
||||
if (!mXR->sessionRunning())
|
||||
return;
|
||||
|
||||
XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
|
||||
CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo));
|
||||
}
|
||||
|
||||
void OpenXRViewImpl::prerenderCallback(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
mCurrentBuffer = prepareNextSwapchainImage();
|
||||
if(mCurrentBuffer)
|
||||
mCurrentBuffer->beginFrame(renderInfo);
|
||||
}
|
||||
|
||||
void OpenXRViewImpl::postrenderCallback(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
if (mCurrentBuffer)
|
||||
mCurrentBuffer->endFrame(renderInfo);
|
||||
releaseSwapchainImage();
|
||||
}
|
||||
|
||||
// Some headers like to define these.
|
||||
#ifdef near
|
||||
#undef near
|
||||
#endif
|
||||
|
||||
#ifdef far
|
||||
#undef far
|
||||
#endif
|
||||
|
||||
static osg::Matrix
|
||||
perspectiveFovMatrix(float near, float far, XrFovf fov)
|
||||
{
|
||||
const float tanLeft = tanf(fov.angleLeft);
|
||||
const float tanRight = tanf(fov.angleRight);
|
||||
const float tanDown = tanf(fov.angleDown);
|
||||
const float tanUp = tanf(fov.angleUp);
|
||||
|
||||
const float tanWidth = tanRight - tanLeft;
|
||||
const float tanHeight = tanUp - tanDown;
|
||||
|
||||
// Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES).
|
||||
// Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal).
|
||||
const float offset = near;
|
||||
|
||||
float matrix[16] = {};
|
||||
|
||||
if (far <= near) {
|
||||
// place the far plane at infinity
|
||||
matrix[0] = 2 / tanWidth;
|
||||
matrix[4] = 0;
|
||||
matrix[8] = (tanRight + tanLeft) / tanWidth;
|
||||
matrix[12] = 0;
|
||||
|
||||
matrix[1] = 0;
|
||||
matrix[5] = 2 / tanHeight;
|
||||
matrix[9] = (tanUp + tanDown) / tanHeight;
|
||||
matrix[13] = 0;
|
||||
|
||||
matrix[2] = 0;
|
||||
matrix[6] = 0;
|
||||
matrix[10] = -1;
|
||||
matrix[14] = -(near + offset);
|
||||
|
||||
matrix[3] = 0;
|
||||
matrix[7] = 0;
|
||||
matrix[11] = -1;
|
||||
matrix[15] = 0;
|
||||
}
|
||||
else {
|
||||
// normal projection
|
||||
matrix[0] = 2 / tanWidth;
|
||||
matrix[4] = 0;
|
||||
matrix[8] = (tanRight + tanLeft) / tanWidth;
|
||||
matrix[12] = 0;
|
||||
|
||||
matrix[1] = 0;
|
||||
matrix[5] = 2 / tanHeight;
|
||||
matrix[9] = (tanUp + tanDown) / tanHeight;
|
||||
matrix[13] = 0;
|
||||
|
||||
matrix[2] = 0;
|
||||
matrix[6] = 0;
|
||||
matrix[10] = -(far + offset) / (far - near);
|
||||
matrix[14] = -(far * (near + offset)) / (far - near);
|
||||
|
||||
matrix[3] = 0;
|
||||
matrix[7] = 0;
|
||||
matrix[11] = -1;
|
||||
matrix[15] = 0;
|
||||
}
|
||||
return osg::Matrix(matrix);
|
||||
}
|
||||
|
||||
osg::Matrix OpenXRViewImpl::projectionMatrix()
|
||||
{
|
||||
auto hmdViews = mXR->mPrivate->getHmdViews();
|
||||
|
||||
float near = Settings::Manager::getFloat("near clip", "Camera");
|
||||
float far = Settings::Manager::getFloat("viewing distance", "Camera");
|
||||
//return perspectiveFovMatrix()
|
||||
|
||||
return perspectiveFovMatrix(near, far, hmdViews[mViewIndex].fov);
|
||||
}
|
||||
|
||||
|
||||
|
||||
osg::Matrix OpenXRViewImpl::viewMatrix()
|
||||
{
|
||||
osg::Matrix viewMatrix;
|
||||
auto hmdViews = mXR->mPrivate->getHmdViews();
|
||||
auto pose = hmdViews[mViewIndex].pose;
|
||||
osg::Vec3 position = osg::Vec3(pose.position.x, pose.position.y, pose.position.z);
|
||||
|
||||
|
||||
// invert orientation (conjugate of Quaternion) and position to apply to the view matrix as offset
|
||||
// TODO: Why invert/conjugate?
|
||||
viewMatrix.setTrans(position);
|
||||
viewMatrix.postMultRotate(osg::fromXR(pose.orientation));
|
||||
|
||||
// Scale to world units
|
||||
viewMatrix.postMultScale(osg::Vec3d(mMetersPerUnit, mMetersPerUnit, mMetersPerUnit));
|
||||
|
||||
return viewMatrix;
|
||||
}
|
||||
|
||||
osg::Camera* OpenXRViewImpl::createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc)
|
||||
{
|
||||
osg::ref_ptr<osg::Camera> camera = new osg::Camera();
|
||||
camera->setClearColor(clearColor);
|
||||
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
||||
camera->setRenderOrder(osg::Camera::PRE_RENDER, eye);
|
||||
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
camera->setAllowEventFocus(false);
|
||||
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
||||
camera->setViewport(0, 0, mWidth, mHeight);
|
||||
camera->setGraphicsContext(gc);
|
||||
|
||||
camera->setInitialDrawCallback(new OpenXRView::InitialDrawCallback());
|
||||
camera->setPreDrawCallback(new OpenXRView::PredrawCallback(camera.get(), this));
|
||||
camera->setFinalDrawCallback(new OpenXRView::PostdrawCallback(camera.get(), this));
|
||||
|
||||
return camera.release();
|
||||
}
|
||||
|
||||
OpenXRView::OpenXRView(
|
||||
osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, float metersPerUnit, unsigned int viewIndex)
|
||||
: mPrivate(new OpenXRViewImpl(XR, state, metersPerUnit, viewIndex))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OpenXRView::~OpenXRView()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//! Get the next color buffer
|
||||
osg::ref_ptr<OpenXRTextureBuffer> OpenXRView::prepareNextSwapchainImage()
|
||||
{
|
||||
return mPrivate->prepareNextSwapchainImage();
|
||||
}
|
||||
|
||||
//! Release current color buffer. Do not forget to call this after rendering to the color buffer.
|
||||
void OpenXRView::releaseSwapchainImage()
|
||||
{
|
||||
mPrivate->releaseSwapchainImage();
|
||||
}
|
||||
|
||||
void OpenXRView::PredrawCallback::operator()(osg::RenderInfo& info) const
|
||||
{
|
||||
mView->prerenderCallback(info);
|
||||
}
|
||||
|
||||
void OpenXRView::PostdrawCallback::operator()(osg::RenderInfo& info) const
|
||||
{
|
||||
mView->postrenderCallback(info);
|
||||
}
|
||||
|
||||
void OpenXRView::prerenderCallback(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
mPrivate->prerenderCallback(renderInfo);
|
||||
}
|
||||
|
||||
void OpenXRView::postrenderCallback(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
mPrivate->postrenderCallback(renderInfo);
|
||||
}
|
||||
|
||||
osg::Camera* OpenXRView::createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc)
|
||||
{
|
||||
return mPrivate->createCamera(eye, clearColor, gc);
|
||||
}
|
||||
|
||||
osg::Matrix OpenXRView::projectionMatrix()
|
||||
{
|
||||
return mPrivate->projectionMatrix();
|
||||
}
|
||||
|
||||
osg::Matrix OpenXRView::viewMatrix()
|
||||
{
|
||||
return mPrivate->viewMatrix();
|
||||
}
|
||||
|
||||
void OpenXRView::InitialDrawCallback::operator()(osg::RenderInfo& renderInfo) const
|
||||
{
|
||||
osg::GraphicsOperation* graphicsOperation = renderInfo.getCurrentCamera()->getRenderer();
|
||||
osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation);
|
||||
if (renderer != nullptr)
|
||||
{
|
||||
// Disable normal OSG FBO camera setup because it will undo the MSAA FBO configuration.
|
||||
renderer->setCameraRequiresSetUp(false);
|
||||
}
|
||||
}
|
||||
}
|
77
apps/openmw/mwvr/openxrview.hpp
Normal file
77
apps/openmw/mwvr/openxrview.hpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
#ifndef OPENXR_VIEW_HPP
|
||||
#define OPENXR_VIEW_HPP
|
||||
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrtexture.hpp"
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
|
||||
|
||||
class OpenXRViewImpl;
|
||||
|
||||
class OpenXRView : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
class PredrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
PredrawCallback(osg::Camera* camera, OpenXRViewImpl* view)
|
||||
: mCamera(camera)
|
||||
, mView(view)
|
||||
{}
|
||||
|
||||
void operator()(osg::RenderInfo& info) const override;
|
||||
|
||||
private:
|
||||
|
||||
osg::observer_ptr<osg::Camera> mCamera;
|
||||
OpenXRViewImpl* mView;
|
||||
};
|
||||
class PostdrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
PostdrawCallback(osg::Camera* camera, OpenXRViewImpl* view)
|
||||
: mCamera(camera)
|
||||
, mView(view)
|
||||
{}
|
||||
|
||||
void operator()(osg::RenderInfo& info) const override;
|
||||
|
||||
private:
|
||||
|
||||
osg::observer_ptr<osg::Camera> mCamera;
|
||||
OpenXRViewImpl* mView;
|
||||
};
|
||||
|
||||
class InitialDrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
virtual void operator()(osg::RenderInfo& renderInfo) const;
|
||||
};
|
||||
|
||||
public:
|
||||
OpenXRView(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, float metersPerUnit, unsigned int viewIndex);
|
||||
~OpenXRView();
|
||||
|
||||
//! Get the next color buffer.
|
||||
//! \return The GL texture ID of the now current swapchain image
|
||||
osg::ref_ptr<OpenXRTextureBuffer> prepareNextSwapchainImage();
|
||||
//! Release current color buffer. Do not forget to call this after rendering to the color buffer.
|
||||
void releaseSwapchainImage();
|
||||
//! Prepare for render
|
||||
void prerenderCallback(osg::RenderInfo& renderInfo);
|
||||
//! Finalize render
|
||||
void postrenderCallback(osg::RenderInfo& renderInfo);
|
||||
//! Create camera for this view
|
||||
osg::Camera* createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc);
|
||||
//! Projection offset for this view
|
||||
osg::Matrix projectionMatrix();
|
||||
//! View offset for this view
|
||||
osg::Matrix viewMatrix();
|
||||
|
||||
std::unique_ptr<OpenXRViewImpl> mPrivate;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
126
apps/openmw/mwvr/openxrviewer.cpp
Normal file
126
apps/openmw/mwvr/openxrviewer.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
#include "openxrviewer.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "Windows.h"
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
|
||||
OpenXRViewer::OpenXRViewer(
|
||||
osg::ref_ptr<OpenXRManager> XR,
|
||||
osg::ref_ptr<OpenXRManager::RealizeOperation> realizeOperation,
|
||||
osg::ref_ptr<osgViewer::Viewer> viewer,
|
||||
float metersPerUnit)
|
||||
: mXR(XR)
|
||||
, mRealizeOperation(realizeOperation)
|
||||
, mViewer(viewer)
|
||||
, mMetersPerUnit(metersPerUnit)
|
||||
, mConfigured(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OpenXRViewer::~OpenXRViewer(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRViewer::traverse(
|
||||
osg::NodeVisitor& visitor)
|
||||
{
|
||||
osg::ref_ptr<OpenXRManager::RealizeOperation> realizeOperation = nullptr;
|
||||
|
||||
if (mRealizeOperation.lock(realizeOperation))
|
||||
if (mRealizeOperation->realized())
|
||||
if (configure())
|
||||
osg::Group::traverse(visitor);
|
||||
}
|
||||
|
||||
bool
|
||||
OpenXRViewer::configure()
|
||||
{
|
||||
if (mConfigured)
|
||||
return true;
|
||||
|
||||
auto context = mViewer->getCamera()->getGraphicsContext();
|
||||
|
||||
if (!context->makeCurrent())
|
||||
{
|
||||
Log(Debug::Warning) << "OpenXRViewer::configure() failed to make graphics context current.";
|
||||
return false;
|
||||
}
|
||||
|
||||
context->setSwapCallback(new OpenXRManager::SwapBuffersCallback(mXR));
|
||||
|
||||
auto DC = wglGetCurrentDC();
|
||||
auto GLRC = wglGetCurrentContext();
|
||||
|
||||
if (DC != mXR->mPrivate->mGraphicsBinding.hDC)
|
||||
{
|
||||
Log(Debug::Warning) << "Graphics DC does not match DC used to construct XR context";
|
||||
}
|
||||
|
||||
if (GLRC != mXR->mPrivate->mGraphicsBinding.hGLRC)
|
||||
{
|
||||
Log(Debug::Warning) << "Graphics GLRC does not match GLRC used to construct XR context";
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();
|
||||
camera->setName("Main");
|
||||
osg::Vec4 clearColor = camera->getClearColor();
|
||||
|
||||
mViews[LEFT_VIEW] = new OpenXRView(mXR, context->getState(), mMetersPerUnit, 0);
|
||||
mViews[RIGHT_VIEW] = new OpenXRView(mXR, context->getState(), mMetersPerUnit, 1);
|
||||
|
||||
osg::Camera* leftCamera = mViews[LEFT_VIEW]->createCamera(LEFT_VIEW, clearColor, context);
|
||||
osg::Camera* rightCamera = mViews[RIGHT_VIEW]->createCamera(RIGHT_VIEW, clearColor, context);
|
||||
|
||||
leftCamera->setName("LeftEye");
|
||||
rightCamera->setName("RightEye");
|
||||
|
||||
mViewer->addSlave(leftCamera, mViews[LEFT_VIEW]->projectionMatrix(), mViews[LEFT_VIEW]->viewMatrix(), true);
|
||||
mViewer->addSlave(rightCamera, mViews[RIGHT_VIEW]->projectionMatrix(), mViews[RIGHT_VIEW]->viewMatrix(), true);
|
||||
|
||||
mViewer->getSlave(LEFT_VIEW)._updateSlaveCallback = new UpdateSlaveCallback(mXR, mViews[LEFT_VIEW]);
|
||||
mViewer->getSlave(RIGHT_VIEW)._updateSlaveCallback = new UpdateSlaveCallback(mXR, mViews[RIGHT_VIEW]);
|
||||
|
||||
mViewer->setLightingMode(osg::View::SKY_LIGHT);
|
||||
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
||||
|
||||
// Rendering main camera is a waste of time with VR enabled
|
||||
//camera->setGraphicsContext(nullptr);
|
||||
mXRInput.reset(new OpenXRInputManager(mXR));
|
||||
|
||||
mConfigured = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRViewer::UpdateSlaveCallback::updateSlave(
|
||||
osg::View& view,
|
||||
osg::View::Slave& slave)
|
||||
{
|
||||
mXR->handleEvents();
|
||||
if (!mXR->sessionRunning())
|
||||
return;
|
||||
|
||||
auto* camera = slave._camera.get();
|
||||
auto name = camera->getName();
|
||||
Log(Debug::Debug) << "Updating camera " << name;
|
||||
|
||||
if (camera->getName() == "LeftEye")
|
||||
{
|
||||
mXR->handleEvents();
|
||||
mXR->waitFrame();
|
||||
mXR->beginFrame();
|
||||
mXR->updateControls();
|
||||
mXR->updatePoses();
|
||||
}
|
||||
|
||||
auto viewMatrix = view.getCamera()->getViewMatrix() * mView->viewMatrix();
|
||||
auto projMatrix = mView->projectionMatrix();
|
||||
|
||||
camera->setViewMatrix(viewMatrix);
|
||||
camera->setProjectionMatrix(projMatrix);
|
||||
|
||||
}
|
||||
}
|
67
apps/openmw/mwvr/openxrviewer.hpp
Normal file
67
apps/openmw/mwvr/openxrviewer.hpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#ifndef MWVR_OPENRXVIEWER_H
|
||||
#define MWVR_OPENRXVIEWER_H
|
||||
|
||||
#include <memory>
|
||||
#include <array>
|
||||
#include <osg/Group>
|
||||
#include <osg/Camera>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrview.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
class OpenXRViewer : public osg::Group
|
||||
{
|
||||
public:
|
||||
class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
|
||||
{
|
||||
public:
|
||||
UpdateSlaveCallback(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<OpenXRView> view)
|
||||
: mXR(XR), mView(view)
|
||||
{}
|
||||
|
||||
void updateSlave(osg::View& view, osg::View::Slave& slave) override;
|
||||
|
||||
private:
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
osg::ref_ptr<OpenXRView> mView;
|
||||
};
|
||||
|
||||
public:
|
||||
enum Views
|
||||
{
|
||||
LEFT_VIEW = 0,
|
||||
RIGHT_VIEW = 1
|
||||
};
|
||||
|
||||
public:
|
||||
//! Create an OpenXR manager based on the graphics context from the given window.
|
||||
//! The OpenXRManager will make its own context with shared resources.
|
||||
OpenXRViewer(
|
||||
osg::ref_ptr<OpenXRManager> XR,
|
||||
osg::ref_ptr<OpenXRManager::RealizeOperation> realizeOperation,
|
||||
osg::ref_ptr<osgViewer::Viewer> viewer,
|
||||
float metersPerUnit = 1.f);
|
||||
|
||||
~OpenXRViewer(void);
|
||||
|
||||
virtual void traverse(osg::NodeVisitor& visitor) override;
|
||||
|
||||
protected:
|
||||
virtual bool configure();
|
||||
|
||||
osg::observer_ptr<OpenXRManager> mXR = nullptr;
|
||||
osg::observer_ptr<OpenXRManager::RealizeOperation> mRealizeOperation = nullptr;
|
||||
osg::observer_ptr<osgViewer::Viewer> mViewer = nullptr;
|
||||
std::unique_ptr<MWVR::OpenXRInputManager> mXRInput = nullptr;
|
||||
std::array<osg::ref_ptr<OpenXRView>, 2> mViews{};
|
||||
float mMetersPerUnit = 1.f;
|
||||
bool mConfigured = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
45
cmake/FindOpenXR.cmake
Normal file
45
cmake/FindOpenXR.cmake
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Locate OpenXR library
|
||||
# This module defines
|
||||
# OpenXR_LIBRARY, the OpenXR library, with no other libraries
|
||||
# OpenXR_LIBRARIES, the OpenXR library and required components with compiler flags
|
||||
# OpenXR_FOUND, if false, do not try to link to OpenXR
|
||||
# OpenXR_INCLUDE_DIR, where to find openxr.h
|
||||
# OpenXR_VERSION, the version of the found library
|
||||
#
|
||||
# This module accepts the following env variables
|
||||
# OPENXR_ROOT
|
||||
# This module responds to the the flag:
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2003-2009 Kitware, Inc.
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
# see accompanying file Copyright.txt for details.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
#=============================================================================
|
||||
# (To distribute this file outside of CMake, substitute the full
|
||||
# License text for the above reference.)
|
||||
|
||||
|
||||
if(WIN32)
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_openxr_default_folder "C:/Program Files/OPENXR")
|
||||
else()
|
||||
set(_openxr_default_folder "C:/Program Files (x86)/OPENXR")
|
||||
endif()
|
||||
endif(WIN32)
|
||||
|
||||
libfind_pkg_detect(OpenXR openxr
|
||||
FIND_PATH openxr/openxr.h
|
||||
HINTS $ENV{OPENXR_ROOT} ${_openxr_default_folder}
|
||||
PATH_SUFFIXES include
|
||||
FIND_LIBRARY openxr_loader.lib
|
||||
HINTS $ENV{OPENXR_ROOT} ${_openxr_default_folder}
|
||||
PATH_SUFFIXES lib lib32 lib64
|
||||
)
|
||||
libfind_version_n_header(OpenXR NAMES openxr/openxr.h DEFINES XR_VERSION_MAJOR XR_VERSION_MINOR XR_VERSION_PATCH)
|
||||
|
||||
libfind_process(OpenXR)
|
Loading…
Reference in a new issue