mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-30 03:06:43 +00:00
Fixed performance issues related to xr synchronization as well as osg pipelining. Some code refactoring / cleanup.
This commit is contained in:
parent
3e3ed7ecee
commit
480ce82217
36 changed files with 794 additions and 1014 deletions
|
@ -124,32 +124,28 @@ if(BUILD_VR_OPENXR)
|
|||
vrengine.cpp
|
||||
mwvr/openxrinputmanager.hpp
|
||||
mwvr/openxrinputmanager.cpp
|
||||
mwvr/openxrlayer.hpp
|
||||
mwvr/openxrlayer.cpp
|
||||
mwvr/openxrmanager.hpp
|
||||
mwvr/openxrmanager.cpp
|
||||
mwvr/openxrmanagerimpl.hpp
|
||||
mwvr/openxrmanagerimpl.cpp
|
||||
mwvr/vrgui.hpp
|
||||
mwvr/vrgui.cpp
|
||||
mwvr/openxrsession.hpp
|
||||
mwvr/openxrsession.cpp
|
||||
mwvr/openxrswapchain.hpp
|
||||
mwvr/openxrswapchain.cpp
|
||||
mwvr/vrtexture.hpp
|
||||
mwvr/vrtexture.cpp
|
||||
mwvr/openxrview.hpp
|
||||
mwvr/openxrview.cpp
|
||||
mwvr/openxrviewer.hpp
|
||||
mwvr/openxrviewer.cpp
|
||||
mwvr/openxrworldview.hpp
|
||||
mwvr/openxrworldview.cpp
|
||||
mwvr/realisticcombat.hpp
|
||||
mwvr/realisticcombat.cpp
|
||||
mwvr/vranimation.hpp
|
||||
mwvr/vranimation.cpp
|
||||
mwvr/vrenvironment.hpp
|
||||
mwvr/vrenvironment.cpp
|
||||
mwvr/vrgui.hpp
|
||||
mwvr/vrgui.cpp
|
||||
mwvr/vrsession.hpp
|
||||
mwvr/vrsession.cpp
|
||||
mwvr/vrtexture.hpp
|
||||
mwvr/vrtexture.cpp
|
||||
mwvr/vrview.hpp
|
||||
mwvr/vrview.cpp
|
||||
mwvr/vrviewer.hpp
|
||||
mwvr/vrviewer.cpp
|
||||
)
|
||||
|
||||
openmw_add_executable(openmw_vr
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
#ifdef USE_OPENXR
|
||||
#include "mwvr/openxrinputmanager.hpp"
|
||||
#include "mwvr/openxrviewer.hpp"
|
||||
#include "mwvr/vrviewer.hpp"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
|
@ -502,6 +502,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
);
|
||||
|
||||
int numThreads = Settings::Manager::getInt("preload num threads", "Cells");
|
||||
Log(Debug::Verbose) << "Num threads: " << numThreads;
|
||||
if (numThreads <= 0)
|
||||
throw std::runtime_error("Invalid setting: 'preload num threads' must be >0");
|
||||
mWorkQueue = new SceneUtil::WorkQueue(numThreads);
|
||||
|
@ -733,10 +734,6 @@ void OMW::Engine::go()
|
|||
mViewer->addEventHandler(resourceshandler);
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
auto* root = mViewer->getSceneData();
|
||||
auto* xrViewer = MWVR::Environment::get().getViewer();
|
||||
xrViewer->addChild(root);
|
||||
mViewer->setSceneData(xrViewer);
|
||||
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer));
|
||||
#endif
|
||||
|
||||
|
@ -764,8 +761,6 @@ void OMW::Engine::go()
|
|||
mEnvironment.getWindowManager()->executeInConsole(mStartupScript);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Start the main rendering loop
|
||||
osg::Timer frameTimer;
|
||||
double simulationTime = 0.0;
|
||||
|
@ -789,13 +784,8 @@ void OMW::Engine::go()
|
|||
|
||||
mEnvironment.getWorld()->updateWindowManager();
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
xrViewer->traversals();
|
||||
#else
|
||||
mViewer->updateTraversal();
|
||||
|
||||
mViewer->renderingTraversals();
|
||||
#endif
|
||||
|
||||
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
||||
if (!guiActive)
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "../mwworld/refdata.hpp"
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
#include "../mwvr/openxrsession.hpp"
|
||||
#include "../mwvr/vrsession.hpp"
|
||||
#include "../mwvr/openxrinputmanager.hpp"
|
||||
#include "../mwvr/vrenvironment.hpp"
|
||||
#endif
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
|
||||
#ifdef USE_OPENXR
|
||||
#include "../mwvr/vranimation.hpp"
|
||||
#include "../mwvr/openxrviewer.hpp"
|
||||
#include "../mwvr/vrviewer.hpp"
|
||||
#include "../mwvr/vrenvironment.hpp"
|
||||
#endif
|
||||
|
||||
|
@ -1015,8 +1015,11 @@ namespace MWRender
|
|||
osg::Camera* camera = mViewer->getCamera();
|
||||
#ifdef USE_OPENXR
|
||||
// In VR mode, the main camera is disabled.
|
||||
auto& slave = mViewer->getSlave(1);
|
||||
camera = slave._camera;
|
||||
if (mViewer->getNumSlaves() > 0)
|
||||
{
|
||||
auto& slave = mViewer->getSlave(mViewer->getNumSlaves()-1);
|
||||
camera = slave._camera;
|
||||
}
|
||||
#endif
|
||||
osg::ref_ptr<osg::Drawable> tempDrw = new osg::Drawable;
|
||||
tempDrw->setDrawCallback(new ReadImageFromFramebufferCallback(image, w, h));
|
||||
|
|
|
@ -443,6 +443,8 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
|
|||
mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900);
|
||||
mWaterGeom->setDrawCallback(new DepthClampCallback);
|
||||
mWaterGeom->setNodeMask(Mask_Water);
|
||||
mWaterGeom->setName("Water Geometry");
|
||||
//mWaterGeom->setDataVariance(osg::Object::STATIC);
|
||||
|
||||
mWaterNode = new osg::PositionAttitudeTransform;
|
||||
mWaterNode->setName("Water Root");
|
||||
|
@ -453,6 +455,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
|
|||
osg::ref_ptr<osg::Geometry> geom2 (osg::clone(mWaterGeom.get(), osg::CopyOp::DEEP_COPY_NODES));
|
||||
createSimpleWaterStateSet(geom2, Fallback::Map::getFloat("Water_Map_Alpha"));
|
||||
geom2->setNodeMask(Mask_SimpleWater);
|
||||
geom2->setName("Water Geometry Fallback");
|
||||
mWaterNode->addChild(geom2);
|
||||
|
||||
mSceneRoot->addChild(mWaterNode);
|
||||
|
|
|
@ -330,7 +330,7 @@ struct OpenXRInput
|
|||
|
||||
void updateControls();
|
||||
const Action* nextAction();
|
||||
PoseSet getHandPoses(int64_t time, TrackedSpace space);
|
||||
Pose getHandPose(int64_t time, TrackedSpace space, Side side);
|
||||
|
||||
void applyHaptics(SubAction subAction, float intensity)
|
||||
{
|
||||
|
@ -798,15 +798,6 @@ OpenXRInput::updateControls()
|
|||
mAutoMove->updateAndQueue(mActionQueue);
|
||||
mToggleHUD->updateAndQueue(mActionQueue);
|
||||
mToggleDebug->updateAndQueue(mActionQueue);
|
||||
|
||||
//if (mActivateTouch->isActive())
|
||||
//{
|
||||
// mHapticsAction.applyHaptics(mSubactionPath[RIGHT_HAND], 0.5);
|
||||
//}
|
||||
//if (mSneak->isActive())
|
||||
//{
|
||||
// mHapticsAction.applyHaptics(mSubactionPath[LEFT_HAND], 0.5);
|
||||
//}
|
||||
}
|
||||
|
||||
XrPath OpenXRInput::generateXrPath(const std::string& path)
|
||||
|
@ -828,53 +819,35 @@ const Action* OpenXRInput::nextAction()
|
|||
|
||||
}
|
||||
|
||||
PoseSet
|
||||
OpenXRInput::getHandPoses(
|
||||
Pose
|
||||
OpenXRInput::getHandPose(
|
||||
int64_t time,
|
||||
TrackedSpace space)
|
||||
TrackedSpace space,
|
||||
Side side)
|
||||
{
|
||||
auto* xr = Environment::get().getManager();
|
||||
PoseSet handPoses{};
|
||||
XrSpace referenceSpace = XR_NULL_HANDLE;
|
||||
if (space == TrackedSpace::STAGE)
|
||||
referenceSpace = xr->impl().mReferenceSpaceStage;
|
||||
if (space == TrackedSpace::VIEW)
|
||||
referenceSpace = xr->impl().mReferenceSpaceView;
|
||||
XrSpace referenceSpace = xr->impl().getReferenceSpace(space);
|
||||
|
||||
XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION };
|
||||
XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY };
|
||||
location.next = &velocity;
|
||||
CHECK_XRCMD(xrLocateSpace(mHandSpace[(int)Side::LEFT_HAND], referenceSpace, time, &location));
|
||||
CHECK_XRCMD(xrLocateSpace(mHandSpace[(int)side], referenceSpace, time, &location));
|
||||
if (!location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)
|
||||
// Quat must have a magnitude of 1 but openxr sets it to 0 when tracking is unavailable.
|
||||
// I want a no-track pose to still be a valid quat so osg won't throw errors
|
||||
location.pose.orientation.w = 1;
|
||||
|
||||
handPoses[(int)Side::LEFT_HAND] = MWVR::Pose{
|
||||
return MWVR::Pose{
|
||||
osg::fromXR(location.pose.position),
|
||||
osg::fromXR(location.pose.orientation),
|
||||
osg::fromXR(velocity.linearVelocity)
|
||||
};
|
||||
|
||||
CHECK_XRCMD(xrLocateSpace(mHandSpace[(int)Side::RIGHT_HAND], referenceSpace, time, &location));
|
||||
if (!location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)
|
||||
// Quat must have a magnitude of 1 but openxr sets it to 0 when tracking is unavailable.
|
||||
// I want a no-track pose to still be a valid quat so osg won't throw errors
|
||||
location.pose.orientation.w = 1;
|
||||
|
||||
handPoses[(int)Side::RIGHT_HAND] = MWVR::Pose{
|
||||
osg::fromXR(location.pose.position),
|
||||
osg::fromXR(location.pose.orientation),
|
||||
osg::fromXR(velocity.linearVelocity)
|
||||
};
|
||||
|
||||
return handPoses;
|
||||
}
|
||||
|
||||
|
||||
PoseSet OpenXRInputManager::getHandPoses(int64_t time, TrackedSpace space)
|
||||
Pose OpenXRInputManager::getHandPose(int64_t time, TrackedSpace space, Side side)
|
||||
{
|
||||
return mXRInput->getHandPoses(time, space);
|
||||
return mXRInput->getHandPose(time, space, side);
|
||||
}
|
||||
|
||||
void OpenXRInputManager::updateActivationIndication(void)
|
||||
|
@ -1030,12 +1003,9 @@ private:
|
|||
bool disableControls,
|
||||
bool disableEvents)
|
||||
{
|
||||
auto begin = std::chrono::steady_clock::now();
|
||||
mXRInput->updateControls();
|
||||
|
||||
// Annoyingly, this is called at least once before the World object has been instantiated.
|
||||
auto* world = MWBase::Environment::get().getWorld();
|
||||
if (!world)
|
||||
return;
|
||||
|
||||
auto* vrGuiManager = Environment::get().getGUIManager();
|
||||
bool vrHasFocus = vrGuiManager->updateFocus();
|
||||
|
@ -1054,27 +1024,42 @@ private:
|
|||
|
||||
MWInput::InputManager::update(dt, disableControls, disableEvents);
|
||||
|
||||
// The rest of this code assumes the game is running
|
||||
if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)
|
||||
return;
|
||||
|
||||
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
||||
|
||||
// OpenMW assumes all input will come via SDL which i often violate.
|
||||
// This keeps player controls correctly enabled for my purposes.
|
||||
mBindingsManager->setPlayerControlsEnabled(!guiMode);
|
||||
|
||||
auto& player = world->getPlayer();
|
||||
auto playerPtr = player.getPlayer();
|
||||
if (!mRealisticCombat || mRealisticCombat->ptr != playerPtr)
|
||||
mRealisticCombat.reset(new RealisticCombat::StateMachine(playerPtr));
|
||||
bool enabled = !guiMode && player.getDrawState() == MWMechanics::DrawState_Weapon && !player.isDisabled();
|
||||
mRealisticCombat->update(dt, enabled);
|
||||
|
||||
updateHead();
|
||||
|
||||
if (!guiMode)
|
||||
{
|
||||
auto* world = MWBase::Environment::get().getWorld();
|
||||
|
||||
auto& player = world->getPlayer();
|
||||
auto playerPtr = world->getPlayerPtr();
|
||||
if (!mRealisticCombat || mRealisticCombat->ptr != playerPtr)
|
||||
mRealisticCombat.reset(new RealisticCombat::StateMachine(playerPtr));
|
||||
bool enabled = !guiMode && player.getDrawState() == MWMechanics::DrawState_Weapon && !player.isDisabled();
|
||||
mRealisticCombat->update(dt, enabled);
|
||||
}
|
||||
else if(mRealisticCombat)
|
||||
mRealisticCombat->update(dt, false);
|
||||
|
||||
|
||||
// Update tracking every frame if player is not currently in GUI mode.
|
||||
// This ensures certain widgets like Notifications will be visible.
|
||||
if (!guiMode)
|
||||
{
|
||||
vrGuiManager->updateTracking();
|
||||
}
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - begin);
|
||||
}
|
||||
|
||||
void OpenXRInputManager::processAction(const Action* action)
|
||||
|
@ -1249,34 +1234,31 @@ private:
|
|||
|
||||
void OpenXRInputManager::updateHead()
|
||||
{
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
auto& player = world->getPlayer();
|
||||
auto playerPtr = player.getPlayer();
|
||||
|
||||
auto* xr = Environment::get().getManager();
|
||||
auto* session = Environment::get().getSession();
|
||||
auto currentHeadPose = session->predictedPoses(OpenXRSession::PredictionSlice::Predraw).head[(int)TrackedSpace::STAGE];
|
||||
xr->playerScale(currentHeadPose);
|
||||
auto currentHeadPose = session->predictedPoses(VRSession::FramePhase::Predraw).head;
|
||||
currentHeadPose.position *= Environment::get().unitsPerMeter();
|
||||
osg::Vec3 vrMovement = currentHeadPose.position - mPreviousHeadPose.position;
|
||||
mPreviousHeadPose = currentHeadPose;
|
||||
osg::Vec3 vrMovement = currentHeadPose.position - mHeadPose.position;
|
||||
mHeadPose = currentHeadPose;
|
||||
osg::Quat gameworldYaw = osg::Quat(mYaw, osg::Vec3(0, 0, -1));
|
||||
mHeadOffset += gameworldYaw * vrMovement;
|
||||
|
||||
if (mRecenter)
|
||||
{
|
||||
mHeadOffset = osg::Vec3(0, 0, 0);
|
||||
// Z should not be affected
|
||||
mHeadOffset.z() = currentHeadPose.position.z();
|
||||
mHeadOffset.z() = mHeadPose.position.z();
|
||||
mRecenter = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::Quat gameworldYaw = osg::Quat(mYaw, osg::Vec3(0, 0, -1));
|
||||
mHeadOffset += gameworldYaw * vrMovement;
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
auto& player = world->getPlayer();
|
||||
auto playerPtr = player.getPlayer();
|
||||
|
||||
float yaw = 0.f;
|
||||
float pitch = 0.f;
|
||||
float roll = 0.f;
|
||||
getEulerAngles(currentHeadPose.orientation, yaw, pitch, roll);
|
||||
getEulerAngles(mHeadPose.orientation, yaw, pitch, roll);
|
||||
|
||||
yaw += mYaw;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef OPENXR_INPUT_MANAGER_HPP
|
||||
#define OPENXR_INPUT_MANAGER_HPP
|
||||
|
||||
#include "openxrviewer.hpp"
|
||||
#include "vrviewer.hpp"
|
||||
#include "realisticcombat.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
|
||||
|
@ -42,7 +42,7 @@ namespace MWVR
|
|||
|
||||
void processAction(const class Action* action);
|
||||
|
||||
PoseSet getHandPoses(int64_t time, TrackedSpace space);
|
||||
Pose getHandPose(int64_t time, TrackedSpace space, Side side);
|
||||
|
||||
void updateActivationIndication(void);
|
||||
void pointActivation(bool onPress);
|
||||
|
@ -55,7 +55,7 @@ namespace MWVR
|
|||
|
||||
std::unique_ptr<OpenXRInput> mXRInput;
|
||||
std::unique_ptr<RealisticCombat::StateMachine> mRealisticCombat;
|
||||
Pose mPreviousHeadPose{};
|
||||
Pose mHeadPose{};
|
||||
osg::Vec3 mHeadOffset{ 0,0,0 };
|
||||
bool mRecenter{ true };
|
||||
bool mActivationIndication{ false };
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
#include "openxrlayer.hpp"
|
||||
|
||||
|
||||
namespace MWVR {
|
||||
|
||||
void OpenXRLayerStack::setLayer(Layer layer, OpenXRLayer* layerObj)
|
||||
{
|
||||
mLayerObjects[layer] = layerObj;
|
||||
}
|
||||
|
||||
OpenXRLayerStack::LayerHeaders OpenXRLayerStack::layerHeaders()
|
||||
{
|
||||
LayerHeaders headers{};
|
||||
for (auto* layerObject: mLayerObjects)
|
||||
{
|
||||
if (layerObject)
|
||||
{
|
||||
auto* header = layerObject->layer();
|
||||
if (header)
|
||||
headers.push_back(header);
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
#ifndef OPENXR_LAYER_HPP
|
||||
#define OPENXR_LAYER_HPP
|
||||
|
||||
#include <array>
|
||||
#include "openxrmanager.hpp"
|
||||
#include "vrtexture.hpp"
|
||||
|
||||
struct XrCompositionLayerBaseHeader;
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
class OpenXRLayer
|
||||
{
|
||||
public:
|
||||
OpenXRLayer(void) = default;
|
||||
virtual ~OpenXRLayer(void) = default;
|
||||
|
||||
virtual const XrCompositionLayerBaseHeader* layer() = 0;
|
||||
virtual void swapBuffers(osg::GraphicsContext* gc) = 0;
|
||||
|
||||
bool isVisible() const { return mVisible; };
|
||||
void setVisible(bool visible) { mVisible = visible; };
|
||||
|
||||
protected:
|
||||
|
||||
bool mVisible{ false };
|
||||
};
|
||||
|
||||
class OpenXRLayerStack
|
||||
{
|
||||
public:
|
||||
enum Layer {
|
||||
WORLD_VIEW_LAYER = 0,
|
||||
MENU_VIEW_LAYER = 1,
|
||||
LAYER_MAX = MENU_VIEW_LAYER //!< Used to size layer arrays. Not a valid input.
|
||||
};
|
||||
using LayerObjectStack = std::array< OpenXRLayer*, LAYER_MAX + 1>;
|
||||
using LayerHeaders = std::vector<const XrCompositionLayerBaseHeader*>;
|
||||
|
||||
OpenXRLayerStack() = default;
|
||||
~OpenXRLayerStack() = default;
|
||||
|
||||
|
||||
void setLayer(Layer layer, OpenXRLayer* layerObj);
|
||||
LayerHeaders layerHeaders();
|
||||
LayerObjectStack& layerObjects() { return mLayerObjects; };
|
||||
|
||||
private:
|
||||
LayerObjectStack mLayerObjects;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -31,7 +31,6 @@ namespace MWVR
|
|||
|
||||
static void statsThreadRun()
|
||||
{
|
||||
return;
|
||||
while (statsThreadRunning)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
@ -156,10 +155,10 @@ namespace MWVR
|
|||
return impl().beginFrame();
|
||||
}
|
||||
|
||||
void OpenXRManager::endFrame(int64_t displayTime, class OpenXRLayerStack* layerStack)
|
||||
void OpenXRManager::endFrame(int64_t displayTime, int layerCount, XrCompositionLayerBaseHeader** layerStack)
|
||||
{
|
||||
if (realized())
|
||||
return impl().endFrame(displayTime, layerStack);
|
||||
return impl().endFrame(displayTime, layerCount, layerStack);
|
||||
}
|
||||
|
||||
void OpenXRManager::updateControls()
|
||||
|
@ -217,11 +216,6 @@ namespace MWVR
|
|||
|
||||
}
|
||||
|
||||
void OpenXRManager::playerScale(MWVR::Pose& stagePose)
|
||||
{
|
||||
if (mPrivate)
|
||||
mPrivate->playerScale(stagePose);
|
||||
}
|
||||
Pose Pose::operator+(const Pose& rhs)
|
||||
{
|
||||
Pose pose = *this;
|
||||
|
@ -229,11 +223,72 @@ namespace MWVR
|
|||
pose.orientation = rhs.orientation * this->orientation;
|
||||
return pose;
|
||||
}
|
||||
|
||||
const Pose& Pose::operator+=(const Pose& rhs)
|
||||
{
|
||||
*this = *this + rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Pose Pose::operator*(float scalar)
|
||||
{
|
||||
Pose pose = *this;
|
||||
pose.position *= scalar;
|
||||
pose.velocity *= scalar;
|
||||
return pose;
|
||||
}
|
||||
|
||||
const Pose& Pose::operator*=(float scalar)
|
||||
{
|
||||
*this = *this * scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// near and far named with an underscore because of galaxy brain defines in included windows headers.
|
||||
osg::Matrix FieldOfView::perspectiveMatrix(float near_, float far_)
|
||||
{
|
||||
const float tanLeft = tanf(angleLeft);
|
||||
const float tanRight = tanf(angleRight);
|
||||
const float tanDown = tanf(angleDown);
|
||||
const float tanUp = tanf(angleUp);
|
||||
|
||||
const float tanWidth = tanRight - tanLeft;
|
||||
const float tanHeight = tanUp - tanDown;
|
||||
|
||||
const float offset = near_;
|
||||
|
||||
float matrix[16] = {};
|
||||
|
||||
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;
|
||||
|
||||
if (far_ <= near_) {
|
||||
matrix[2] = 0;
|
||||
matrix[6] = 0;
|
||||
matrix[10] = -1;
|
||||
matrix[14] = -(near_ + offset);
|
||||
}
|
||||
else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator <<(
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
|
||||
struct XrSwapchainSubImage;
|
||||
|
||||
struct XrCompositionLayerBaseHeader;
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
|
@ -45,17 +45,36 @@ namespace MWVR
|
|||
//! Speed of movement in VR space, expressed in meters per second
|
||||
osg::Vec3 velocity{ 0,0,0 };
|
||||
|
||||
//! Add one pose to another
|
||||
Pose operator+(const Pose& rhs);
|
||||
const Pose& operator+=(const Pose& rhs);
|
||||
|
||||
//! Scale a pose (does not affect orientation)
|
||||
Pose operator*(float scalar);
|
||||
const Pose& operator*=(float scalar);
|
||||
};
|
||||
|
||||
using PoseSet = std::array<Pose, 2>;
|
||||
struct FieldOfView {
|
||||
float angleLeft;
|
||||
float angleRight;
|
||||
float angleUp;
|
||||
float angleDown;
|
||||
|
||||
struct PoseSets
|
||||
osg::Matrix perspectiveMatrix(float near, float far);
|
||||
};
|
||||
|
||||
struct View
|
||||
{
|
||||
PoseSet eye[2]{};
|
||||
PoseSet hands[2]{};
|
||||
PoseSet head{};
|
||||
Pose pose;
|
||||
FieldOfView fov;
|
||||
};
|
||||
|
||||
struct PoseSet
|
||||
{
|
||||
View view[2]{};
|
||||
Pose eye[2]{};
|
||||
Pose hands[2]{};
|
||||
Pose head{};
|
||||
};
|
||||
|
||||
//! Describes what limb to track.
|
||||
|
@ -117,9 +136,8 @@ namespace MWVR
|
|||
void handleEvents();
|
||||
void waitFrame();
|
||||
void beginFrame();
|
||||
void endFrame(int64_t displayTime, class OpenXRLayerStack* layerStack);
|
||||
void endFrame(int64_t displayTime, int layerCount, XrCompositionLayerBaseHeader** layerStack);
|
||||
void updateControls();
|
||||
void playerScale(MWVR::Pose& stagePose);
|
||||
|
||||
void realize(osg::GraphicsContext* gc);
|
||||
|
||||
|
|
|
@ -295,34 +295,17 @@ namespace MWVR
|
|||
}
|
||||
|
||||
void
|
||||
OpenXRManagerImpl::endFrame(int64_t displayTime, OpenXRLayerStack* layerStack)
|
||||
OpenXRManagerImpl::endFrame(int64_t displayTime, int layerCount, XrCompositionLayerBaseHeader** layerStack)
|
||||
{
|
||||
Timer timer("endFrame()");
|
||||
if (!mSessionRunning)
|
||||
return;
|
||||
|
||||
OpenXRLayerStack::LayerHeaders headers;
|
||||
|
||||
XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO };
|
||||
frameEndInfo.displayTime = displayTime;
|
||||
frameEndInfo.environmentBlendMode = mEnvironmentBlendMode;
|
||||
if (layerStack)
|
||||
{
|
||||
headers = layerStack->layerHeaders();
|
||||
frameEndInfo.layerCount = headers.size();
|
||||
frameEndInfo.layers = headers.data();
|
||||
}
|
||||
else {
|
||||
frameEndInfo.layerCount = 0;
|
||||
frameEndInfo.layers = nullptr;
|
||||
}
|
||||
//Log(Debug::Verbose) << "LayerStack<" << frameEndInfo.layerCount << ", " << frameEndInfo.layers << ">";
|
||||
|
||||
static std::chrono::steady_clock::time_point last = std::chrono::steady_clock::now();
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed = now -last;
|
||||
last = now;
|
||||
frameEndInfo.layerCount = layerCount;
|
||||
frameEndInfo.layers = layerStack;
|
||||
|
||||
CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo));
|
||||
}
|
||||
|
@ -498,26 +481,24 @@ namespace MWVR
|
|||
return XrPosef{ osg::toXR(pose.orientation), osg::toXR(pose.position) };
|
||||
}
|
||||
|
||||
|
||||
void OpenXRManagerImpl::playerScale(MWVR::Pose& stagePose)
|
||||
MWVR::FieldOfView fromXR(XrFovf fov)
|
||||
{
|
||||
auto worldPtr = MWBase::Environment::get().getWorld();
|
||||
if (worldPtr)
|
||||
{
|
||||
auto playerPtr = MWMechanics::getPlayer();
|
||||
const MWWorld::LiveCellRef<ESM::NPC>* ref = playerPtr.get<ESM::NPC>();
|
||||
const ESM::Race* race =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
|
||||
bool isMale = ref->mBase->isMale();
|
||||
float charHeightFactor = isMale ? race->mData.mHeight.mMale : race->mData.mHeight.mFemale;
|
||||
float charHeightBase = 1.8f;
|
||||
float charHeight = charHeightBase * charHeightFactor;
|
||||
// TODO: Player height should be configurable
|
||||
// For now i'm just using my own
|
||||
float sizeFactor = 1.85f / charHeight;
|
||||
stagePose.position /= sizeFactor;
|
||||
stagePose.velocity /= sizeFactor;
|
||||
}
|
||||
return MWVR::FieldOfView{ fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown };
|
||||
}
|
||||
|
||||
XrFovf toXR(MWVR::FieldOfView fov)
|
||||
{
|
||||
return XrFovf{ fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown };
|
||||
}
|
||||
|
||||
XrSpace OpenXRManagerImpl::getReferenceSpace(TrackedSpace space)
|
||||
{
|
||||
XrSpace referenceSpace = XR_NULL_HANDLE;
|
||||
if (space == TrackedSpace::STAGE)
|
||||
referenceSpace = mReferenceSpaceStage;
|
||||
if (space == TrackedSpace::VIEW)
|
||||
referenceSpace = mReferenceSpaceView;
|
||||
return referenceSpace;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#define OPENXR_MANAGER_IMPL_HPP
|
||||
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrlayer.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
@ -42,6 +41,8 @@ namespace MWVR
|
|||
XrResult CheckXrResult(XrResult res, const char* originator = nullptr, const char* sourceLocation = nullptr);
|
||||
MWVR::Pose fromXR(XrPosef pose);
|
||||
XrPosef toXR(MWVR::Pose pose);
|
||||
MWVR::FieldOfView fromXR(XrFovf fov);
|
||||
XrFovf toXR(MWVR::FieldOfView fov);
|
||||
|
||||
struct OpenXRManagerImpl
|
||||
{
|
||||
|
@ -55,7 +56,7 @@ namespace MWVR
|
|||
const XrEventDataBaseHeader* nextEvent();
|
||||
void waitFrame();
|
||||
void beginFrame();
|
||||
void endFrame(int64_t displayTime, class OpenXRLayerStack* layerStack);
|
||||
void endFrame(int64_t displayTime, int layerCount, XrCompositionLayerBaseHeader** layerStack);
|
||||
std::array<XrView, 2> getPredictedViews(int64_t predictedDisplayTime, TrackedSpace mSpace);
|
||||
MWVR::Pose getPredictedLimbPose(int64_t predictedDisplayTime, TrackedLimb limb, TrackedSpace space);
|
||||
int eyes();
|
||||
|
@ -63,7 +64,7 @@ namespace MWVR
|
|||
void updateControls();
|
||||
void HandleSessionStateChanged(const XrEventDataSessionStateChanged& stateChangedEvent);
|
||||
XrFrameState frameState();
|
||||
void playerScale(MWVR::Pose& stagePose);
|
||||
XrSpace getReferenceSpace(TrackedSpace space);
|
||||
|
||||
bool initialized = false;
|
||||
long long mFrameIndex = 0;
|
||||
|
|
|
@ -1,212 +0,0 @@
|
|||
#include "vrenvironment.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
#include "openxrswapchain.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
|
||||
#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>
|
||||
#include "openxrsession.hpp"
|
||||
#include "vrgui.hpp"
|
||||
#include <time.h>
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
OpenXRSession::OpenXRSession()
|
||||
{
|
||||
}
|
||||
|
||||
OpenXRSession::~OpenXRSession()
|
||||
{
|
||||
}
|
||||
|
||||
void OpenXRSession::setLayer(
|
||||
OpenXRLayerStack::Layer layerType,
|
||||
OpenXRLayer* layer)
|
||||
{
|
||||
mLayerStack.setLayer(layerType, layer);
|
||||
}
|
||||
|
||||
void OpenXRSession::swapBuffers(osg::GraphicsContext* gc)
|
||||
{
|
||||
Timer timer("OpenXRSession::SwapBuffers");
|
||||
|
||||
auto* xr = Environment::get().getManager();
|
||||
|
||||
if (!mShouldRender)
|
||||
return;
|
||||
|
||||
if (mRenderedFrames != mRenderFrame - 1)
|
||||
{
|
||||
Log(Debug::Warning) << "swapBuffers called out of order";
|
||||
waitFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto layer : mLayerStack.layerObjects())
|
||||
if (layer)
|
||||
layer->swapBuffers(gc);
|
||||
|
||||
timer.checkpoint("Rendered");
|
||||
xr->endFrame(mDrawPredictedDisplayTime, &mLayerStack);
|
||||
mRenderedFrames++;
|
||||
}
|
||||
|
||||
const PoseSets& OpenXRSession::predictedPoses(PredictionSlice slice)
|
||||
{
|
||||
if (slice == PredictionSlice::Predraw)
|
||||
waitFrame();
|
||||
return mPredictedPoses[(int)slice];
|
||||
}
|
||||
|
||||
static std::chrono::steady_clock::time_point gLast{};
|
||||
|
||||
void OpenXRSession::waitFrame()
|
||||
{
|
||||
if (mPredictedFrames < mPredictionFrame)
|
||||
{
|
||||
Timer timer("OpenXRSession::waitFrame");
|
||||
auto* xr = Environment::get().getManager();
|
||||
xr->handleEvents();
|
||||
mIsRunning = xr->sessionRunning();
|
||||
if (!mIsRunning)
|
||||
return;
|
||||
|
||||
// Until OpenXR allows us to get a prediction without waiting
|
||||
// we make our own (bad) prediction and let openxr wait asynchronously.
|
||||
auto frameState = xr->impl().frameState();
|
||||
|
||||
std::thread waitThread([]{
|
||||
Environment::get().getManager()->waitFrame();
|
||||
});
|
||||
|
||||
if (mPredrawPredictedDisplayTime == 0)
|
||||
{
|
||||
// First time
|
||||
waitThread.join();
|
||||
mPredrawPredictedDisplayTime = xr->impl().frameState().predictedDisplayTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
waitThread.detach();
|
||||
mPredrawPredictedDisplayTime = frameState.predictedDisplayTime + frameState.predictedDisplayPeriod;
|
||||
}
|
||||
|
||||
timer.checkpoint("waitFrame");
|
||||
predictNext(0);
|
||||
mPredictedFrames++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// OSG doesn't provide API to extract euler angles from a quat, but i need it.
|
||||
// Credits goes to Dennis Bunfield, i just copied his formula https://narkive.com/v0re6547.4
|
||||
void getEulerAngles(const osg::Quat& quat, float& yaw, float& pitch, float& roll)
|
||||
{
|
||||
// Now do the computation
|
||||
osg::Matrixd m2(osg::Matrixd::rotate(quat));
|
||||
double* mat = (double*)m2.ptr();
|
||||
double angle_x = 0.0;
|
||||
double angle_y = 0.0;
|
||||
double angle_z = 0.0;
|
||||
double D, C, tr_x, tr_y;
|
||||
angle_y = D = asin(mat[2]); /* Calculate Y-axis angle */
|
||||
C = cos(angle_y);
|
||||
if (fabs(C) > 0.005) /* Test for Gimball lock? */
|
||||
{
|
||||
tr_x = mat[10] / C; /* No, so get X-axis angle */
|
||||
tr_y = -mat[6] / C;
|
||||
angle_x = atan2(tr_y, tr_x);
|
||||
tr_x = mat[0] / C; /* Get Z-axis angle */
|
||||
tr_y = -mat[1] / C;
|
||||
angle_z = atan2(tr_y, tr_x);
|
||||
}
|
||||
else /* Gimball lock has occurred */
|
||||
{
|
||||
angle_x = 0; /* Set X-axis angle to zero
|
||||
*/
|
||||
tr_x = mat[5]; /* And calculate Z-axis angle
|
||||
*/
|
||||
tr_y = mat[4];
|
||||
angle_z = atan2(tr_y, tr_x);
|
||||
}
|
||||
|
||||
yaw = angle_z;
|
||||
pitch = angle_x;
|
||||
roll = angle_y;
|
||||
}
|
||||
|
||||
void OpenXRSession::movementAngles(float& yaw, float& pitch)
|
||||
{
|
||||
auto lhandquat = predictedPoses(PredictionSlice::Predraw).hands[(int)TrackedSpace::VIEW][(int)MWVR::Side::LEFT_HAND].orientation;
|
||||
float roll = 0.f;
|
||||
getEulerAngles(lhandquat, yaw, pitch, roll);
|
||||
|
||||
}
|
||||
|
||||
void OpenXRSession::advanceFrame(void)
|
||||
{
|
||||
auto* xr = Environment::get().getManager();
|
||||
mShouldRender = mIsRunning;
|
||||
if (!mIsRunning)
|
||||
return;
|
||||
if (mPredictedFrames != mPredictionFrame)
|
||||
{
|
||||
Log(Debug::Warning) << "advanceFrame() called out of order";
|
||||
return;
|
||||
}
|
||||
|
||||
// For the same reason we do waitFrame() asynchronously,
|
||||
// we do beginFrame async and just blit to the swapchains later.
|
||||
//std::thread([=] {
|
||||
xr->beginFrame();
|
||||
//}).detach();
|
||||
mPredictionFrame++;
|
||||
mRenderFrame++;
|
||||
mPredictedPoses[(int)PredictionSlice::Draw] = mPredictedPoses[(int)PredictionSlice::Predraw];
|
||||
mDrawPredictedDisplayTime = mPredrawPredictedDisplayTime;
|
||||
}
|
||||
|
||||
void OpenXRSession::predictNext(int extraPeriods)
|
||||
{
|
||||
auto* xr = Environment::get().getManager();
|
||||
auto* input = Environment::get().getInputManager();
|
||||
|
||||
PoseSets newPoses{};
|
||||
newPoses.head[(int)TrackedSpace::STAGE] = xr->impl().getPredictedLimbPose(mPredrawPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE);
|
||||
newPoses.head[(int)TrackedSpace::VIEW] = xr->impl().getPredictedLimbPose(mPredrawPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::VIEW);
|
||||
newPoses.hands[(int)TrackedSpace::STAGE] = input->getHandPoses(mPredrawPredictedDisplayTime, TrackedSpace::STAGE);
|
||||
newPoses.hands[(int)TrackedSpace::VIEW] = input->getHandPoses(mPredrawPredictedDisplayTime, TrackedSpace::VIEW);
|
||||
auto stageViews = xr->impl().getPredictedViews(mPredrawPredictedDisplayTime, TrackedSpace::STAGE);
|
||||
auto hmdViews = xr->impl().getPredictedViews(mPredrawPredictedDisplayTime, TrackedSpace::VIEW);
|
||||
newPoses.eye[(int)TrackedSpace::STAGE][(int)Side::LEFT_HAND] = fromXR(stageViews[(int)Side::LEFT_HAND].pose);
|
||||
newPoses.eye[(int)TrackedSpace::VIEW][(int)Side::LEFT_HAND] = fromXR(hmdViews[(int)Side::LEFT_HAND].pose);
|
||||
newPoses.eye[(int)TrackedSpace::STAGE][(int)Side::RIGHT_HAND] = fromXR(stageViews[(int)Side::RIGHT_HAND].pose);
|
||||
newPoses.eye[(int)TrackedSpace::VIEW][(int)Side::RIGHT_HAND] = fromXR(hmdViews[(int)Side::RIGHT_HAND].pose);
|
||||
mPredictedPoses[(int)PredictionSlice::Predraw] = newPoses;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator <<(
|
||||
std::ostream& os,
|
||||
const MWVR::Pose& pose)
|
||||
{
|
||||
os << "position=" << pose.position << " orientation=" << pose.orientation << " velocity=" << pose.velocity;
|
||||
return os;
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#ifndef MWVR_OPENRXSESSION_H
|
||||
#define MWVR_OPENRXSESSION_H
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrlayer.hpp"
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
|
||||
extern void getEulerAngles(const osg::Quat& quat, float& yaw, float& pitch, float& roll);
|
||||
|
||||
class OpenXRSession
|
||||
{
|
||||
public:
|
||||
using seconds = std::chrono::duration<double>;
|
||||
using nanoseconds = std::chrono::nanoseconds;
|
||||
using clock = std::chrono::steady_clock;
|
||||
using time_point = clock::time_point;
|
||||
|
||||
enum class PredictionSlice
|
||||
{
|
||||
Predraw = 0, //!< Get poses predicted for the next frame to be drawn
|
||||
Draw = 1, //!< Get poses predicted for the current rendering
|
||||
NumSlices
|
||||
};
|
||||
|
||||
public:
|
||||
OpenXRSession();
|
||||
~OpenXRSession();
|
||||
|
||||
void setLayer(OpenXRLayerStack::Layer layerType, OpenXRLayer* layer);
|
||||
void swapBuffers(osg::GraphicsContext* gc);
|
||||
|
||||
const PoseSets& predictedPoses(PredictionSlice slice);
|
||||
|
||||
//! Call before updating poses and other inputs
|
||||
void waitFrame();
|
||||
|
||||
//! Update predictions
|
||||
void predictNext(int extraPeriods);
|
||||
|
||||
//! Angles to be used for overriding movement direction
|
||||
void movementAngles(float& yaw, float& pitch);
|
||||
|
||||
void advanceFrame(void);
|
||||
|
||||
bool isRunning() { return mIsRunning; }
|
||||
bool shouldRender() { return mShouldRender; }
|
||||
|
||||
OpenXRLayerStack mLayerStack{};
|
||||
|
||||
PoseSets mPredictedPoses[(int)PredictionSlice::NumSlices]{};
|
||||
|
||||
int mRenderFrame{ 0 };
|
||||
int mRenderedFrames{ 0 };
|
||||
int mPredictionFrame{ 1 };
|
||||
int mPredictedFrames{ 0 };
|
||||
|
||||
long long mPredrawPredictedDisplayTime{ 0 };
|
||||
long long mDrawPredictedDisplayTime{ 0 };
|
||||
|
||||
bool mIsRunning{ false };
|
||||
bool mShouldRender{ false };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,6 +1,7 @@
|
|||
#include "openxrswapchain.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
|
@ -23,13 +24,12 @@ namespace MWVR {
|
|||
SUBVIEW_MAX = RIGHT_VIEW, //!< Used to size subview arrays. Not a valid input.
|
||||
};
|
||||
|
||||
OpenXRSwapchainImpl(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config);
|
||||
OpenXRSwapchainImpl(osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config);
|
||||
~OpenXRSwapchainImpl();
|
||||
|
||||
void beginFrame(osg::GraphicsContext* gc);
|
||||
int endFrame(osg::GraphicsContext* gc);
|
||||
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
XrSwapchain mSwapchain = XR_NULL_HANDLE;
|
||||
std::vector<XrSwapchainImageOpenGLKHR> mSwapchainImageBuffers{};
|
||||
//std::vector<osg::ref_ptr<VRTexture> > mTextureBuffers{};
|
||||
|
@ -42,10 +42,8 @@ namespace MWVR {
|
|||
VRTexture* mRenderBuffer = nullptr;
|
||||
};
|
||||
|
||||
OpenXRSwapchainImpl::OpenXRSwapchainImpl(
|
||||
osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config)
|
||||
: mXR(XR)
|
||||
, mWidth(config.width)
|
||||
OpenXRSwapchainImpl::OpenXRSwapchainImpl(osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config)
|
||||
: mWidth(config.width)
|
||||
, mHeight(config.height)
|
||||
, mSamples(config.samples)
|
||||
{
|
||||
|
@ -56,13 +54,13 @@ namespace MWVR {
|
|||
if (mSamples <= 0)
|
||||
throw std::invalid_argument("Samples must be a positive integer");
|
||||
|
||||
auto& pXR = mXR->impl();
|
||||
auto* xr = Environment::get().getManager();
|
||||
|
||||
// Select a swapchain format.
|
||||
uint32_t swapchainFormatCount;
|
||||
CHECK_XRCMD(xrEnumerateSwapchainFormats(pXR.mSession, 0, &swapchainFormatCount, nullptr));
|
||||
CHECK_XRCMD(xrEnumerateSwapchainFormats(xr->impl().mSession, 0, &swapchainFormatCount, nullptr));
|
||||
std::vector<int64_t> swapchainFormats(swapchainFormatCount);
|
||||
CHECK_XRCMD(xrEnumerateSwapchainFormats(pXR.mSession, (uint32_t)swapchainFormats.size(), &swapchainFormatCount, swapchainFormats.data()));
|
||||
CHECK_XRCMD(xrEnumerateSwapchainFormats(xr->impl().mSession, (uint32_t)swapchainFormats.size(), &swapchainFormatCount, swapchainFormats.data()));
|
||||
|
||||
// List of supported color swapchain formats.
|
||||
constexpr int64_t SupportedColorSwapchainFormats[] = {
|
||||
|
@ -91,7 +89,7 @@ namespace MWVR {
|
|||
swapchainCreateInfo.faceCount = 1;
|
||||
swapchainCreateInfo.sampleCount = mSamples;
|
||||
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
CHECK_XRCMD(xrCreateSwapchain(pXR.mSession, &swapchainCreateInfo, &mSwapchain));
|
||||
CHECK_XRCMD(xrCreateSwapchain(xr->impl().mSession, &swapchainCreateInfo, &mSwapchain));
|
||||
|
||||
uint32_t imageCount = 0;
|
||||
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, 0, &imageCount, nullptr));
|
||||
|
@ -140,9 +138,8 @@ namespace MWVR {
|
|||
return swapchainImageIndex;
|
||||
}
|
||||
|
||||
OpenXRSwapchain::OpenXRSwapchain(
|
||||
osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config)
|
||||
: mPrivate(new OpenXRSwapchainImpl(XR, state, config))
|
||||
OpenXRSwapchain::OpenXRSwapchain(osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config)
|
||||
: mPrivate(new OpenXRSwapchainImpl(state, config))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace MWVR
|
|||
};
|
||||
|
||||
public:
|
||||
OpenXRSwapchain(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, Config config);
|
||||
OpenXRSwapchain(osg::ref_ptr<osg::State> state, Config config);
|
||||
~OpenXRSwapchain();
|
||||
|
||||
public:
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
#include "openxrview.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwrender/renderingmanager.hpp"
|
||||
#include "../mwrender/water.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>
|
||||
|
||||
namespace MWVR {
|
||||
|
||||
OpenXRView::OpenXRView(
|
||||
osg::ref_ptr<OpenXRManager> XR,
|
||||
std::string name,
|
||||
OpenXRSwapchain::Config config,
|
||||
osg::ref_ptr<osg::State> state)
|
||||
: mXR(XR)
|
||||
, mSwapchainConfig{ config }
|
||||
, mSwapchain(new OpenXRSwapchain(mXR, state, mSwapchainConfig))
|
||||
, mName(name)
|
||||
, mTimer(mName.c_str())
|
||||
{
|
||||
}
|
||||
|
||||
OpenXRView::~OpenXRView()
|
||||
{
|
||||
}
|
||||
|
||||
osg::Camera* OpenXRView::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 + 2);
|
||||
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
camera->setAllowEventFocus(false);
|
||||
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
||||
camera->setViewport(0, 0, mSwapchain->width(), mSwapchain->height());
|
||||
camera->setGraphicsContext(gc);
|
||||
|
||||
camera->setInitialDrawCallback(new OpenXRView::InitialDrawCallback());
|
||||
camera->setFinalDrawCallback(new OpenXRView::FinalDrawCallback());
|
||||
|
||||
return camera.release();
|
||||
}
|
||||
|
||||
void OpenXRView::prerenderCallback(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
if (mSwapchain)
|
||||
{
|
||||
mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext());
|
||||
}
|
||||
mTimer.checkpoint("Prerender");
|
||||
|
||||
}
|
||||
|
||||
void OpenXRView::postrenderCallback(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
auto name = renderInfo.getCurrentCamera()->getName();
|
||||
mTimer.checkpoint("Postrender");
|
||||
}
|
||||
|
||||
void OpenXRView::swapBuffers(osg::GraphicsContext* gc)
|
||||
{
|
||||
swapchain().endFrame(gc);
|
||||
}
|
||||
|
||||
void OpenXRView::setPredictedPose(const Pose& pose)
|
||||
{
|
||||
mPredictedPose = pose;
|
||||
};
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
#include "openxrworldview.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwrender/renderingmanager.hpp"
|
||||
#include "../mwrender/camera.hpp"
|
||||
|
||||
#include <osg/Camera>
|
||||
#include <osgViewer/Renderer>
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
// 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;
|
||||
|
||||
const float offset = near;
|
||||
|
||||
float matrix[16] = {};
|
||||
|
||||
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;
|
||||
|
||||
if (far <= near) {
|
||||
matrix[2] = 0;
|
||||
matrix[6] = 0;
|
||||
matrix[10] = -1;
|
||||
matrix[14] = -(near + offset);
|
||||
}
|
||||
else {
|
||||
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 OpenXRWorldView::projectionMatrix()
|
||||
{
|
||||
// TODO: Get this from the session predictions instead
|
||||
auto hmdViews = mXR->impl().getPredictedViews(mXR->impl().frameState().predictedDisplayTime, TrackedSpace::STAGE);
|
||||
|
||||
float near = Settings::Manager::getFloat("near clip", "Camera");
|
||||
float far = Settings::Manager::getFloat("viewing distance", "Camera");
|
||||
//return perspectiveFovMatrix()
|
||||
if(mName == "LeftEye")
|
||||
return perspectiveFovMatrix(near, far, hmdViews[0].fov);
|
||||
return perspectiveFovMatrix(near, far, hmdViews[1].fov);
|
||||
}
|
||||
|
||||
osg::Matrix OpenXRWorldView::viewMatrix()
|
||||
{
|
||||
MWVR::Pose pose = predictedPose();
|
||||
mXR->playerScale(pose);
|
||||
osg::Vec3 position = pose.position * Environment::get().unitsPerMeter();
|
||||
osg::Quat orientation = pose.orientation;
|
||||
|
||||
float y = position.y();
|
||||
float z = position.z();
|
||||
position.y() = z;
|
||||
position.z() = -y;
|
||||
|
||||
y = orientation.y();
|
||||
z = orientation.z();
|
||||
orientation.y() = z;
|
||||
orientation.z() = -y;
|
||||
|
||||
osg::Matrix viewMatrix;
|
||||
viewMatrix.setTrans(-position);
|
||||
viewMatrix.postMultRotate(orientation.conj());
|
||||
|
||||
return viewMatrix;
|
||||
}
|
||||
|
||||
OpenXRWorldView::OpenXRWorldView(
|
||||
osg::ref_ptr<OpenXRManager> XR,
|
||||
std::string name,
|
||||
osg::ref_ptr<osg::State> state,
|
||||
OpenXRSwapchain::Config config)
|
||||
: OpenXRView(XR, name, config, state)
|
||||
{
|
||||
}
|
||||
|
||||
OpenXRWorldView::~OpenXRWorldView()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void OpenXRWorldView::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);
|
||||
}
|
||||
auto name = renderInfo.getCurrentCamera()->getName();
|
||||
}
|
||||
|
||||
// TODO: This would have been useful but OSG never calls it.
|
||||
void OpenXRWorldView::FinalDrawCallback::operator()(osg::RenderInfo& renderInfo) const
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRWorldView::UpdateSlaveCallback::updateSlave(
|
||||
osg::View& view,
|
||||
osg::View::Slave& slave)
|
||||
{
|
||||
mView->mTimer.checkpoint("UpdateSlave");
|
||||
|
||||
auto* camera = slave._camera.get();
|
||||
auto name = camera->getName();
|
||||
|
||||
int side = 0;
|
||||
|
||||
if (name == "LeftEye")
|
||||
{
|
||||
side = (int)Side::LEFT_HAND;
|
||||
}
|
||||
else
|
||||
{
|
||||
side = (int)Side::RIGHT_HAND;
|
||||
}
|
||||
|
||||
auto& poses = mSession->predictedPoses(OpenXRSession::PredictionSlice::Predraw);
|
||||
Pose viewPose{};
|
||||
Pose stagePose{};
|
||||
stagePose = poses.eye[(int)TrackedSpace::STAGE][side];
|
||||
viewPose = poses.eye[(int)TrackedSpace::VIEW][side];
|
||||
mView->setPredictedPose(viewPose);
|
||||
|
||||
auto viewMatrix = view.getCamera()->getViewMatrix();
|
||||
auto modifiedViewMatrix = viewMatrix * mView->viewMatrix();
|
||||
auto projectionMatrix = mView->projectionMatrix();
|
||||
|
||||
camera->setViewMatrix(modifiedViewMatrix);
|
||||
camera->setProjectionMatrix(projectionMatrix);
|
||||
slave.updateSlaveImplementation(view);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
#ifndef OPENXRWORLDVIEW_HPP
|
||||
#define OPENXRWORLDVIEW_HPP
|
||||
|
||||
#include "openxrview.hpp"
|
||||
#include "openxrsession.hpp"
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
class OpenXRWorldView : public OpenXRView
|
||||
{
|
||||
public:
|
||||
class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
|
||||
{
|
||||
public:
|
||||
UpdateSlaveCallback(osg::ref_ptr<OpenXRManager> XR, OpenXRSession* session, osg::ref_ptr<OpenXRWorldView> view, osg::GraphicsContext* gc)
|
||||
: mXR(XR), mSession(session), mView(view), mGC(gc)
|
||||
{}
|
||||
|
||||
void updateSlave(osg::View& view, osg::View::Slave& slave) override;
|
||||
|
||||
private:
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
OpenXRSession* mSession;
|
||||
osg::ref_ptr<OpenXRWorldView> mView;
|
||||
osg::ref_ptr<osg::GraphicsContext> mGC;
|
||||
};
|
||||
|
||||
public:
|
||||
OpenXRWorldView(osg::ref_ptr<OpenXRManager> XR, std::string name, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config);
|
||||
~OpenXRWorldView();
|
||||
|
||||
//! Projection offset for this view
|
||||
osg::Matrix projectionMatrix();
|
||||
//! View offset for this view
|
||||
osg::Matrix viewMatrix();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -103,8 +103,8 @@ void StateMachine::update(float dt, bool enabled)
|
|||
{
|
||||
auto* session = Environment::get().getSession();
|
||||
auto* world = MWBase::Environment::get().getWorld();
|
||||
auto& predictedPoses = session->predictedPoses(OpenXRSession::PredictionSlice::Predraw);
|
||||
auto& handPose = predictedPoses.hands[(int)MWVR::TrackedSpace::STAGE][(int)MWVR::Side::RIGHT_HAND];
|
||||
auto& predictedPoses = session->predictedPoses(VRSession::FramePhase::Predraw);
|
||||
auto& handPose = predictedPoses.hands[(int)MWVR::Side::RIGHT_HAND];
|
||||
auto weaponType = world->getActiveWeaponType();
|
||||
|
||||
enabled = enabled && isMeleeWeapon(weaponType);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "vrenvironment.hpp"
|
||||
#include "openxrsession.hpp"
|
||||
#include "vrsession.hpp"
|
||||
|
||||
namespace MWVR { namespace RealisticCombat {
|
||||
enum SwingState
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
#include "../mwphysics/physicssystem.hpp"
|
||||
|
||||
#include "vrenvironment.hpp"
|
||||
#include "openxrviewer.hpp"
|
||||
#include "vrviewer.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
|
||||
namespace MWVR
|
||||
|
@ -102,11 +102,8 @@ void ForearmController::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||
|
||||
osg::MatrixTransform* transform = static_cast<osg::MatrixTransform*>(node);
|
||||
|
||||
auto* xr = Environment::get().getManager();
|
||||
auto* session = Environment::get().getSession();
|
||||
auto* xrViewer = Environment::get().getViewer();
|
||||
auto& poses = session->predictedPoses(OpenXRSession::PredictionSlice::Predraw);
|
||||
auto handPosesStage = poses.hands[(int)TrackedSpace::STAGE];
|
||||
int side = (int)Side::RIGHT_HAND;
|
||||
if (node->getName().find_first_of("L") != std::string::npos)
|
||||
{
|
||||
|
@ -117,10 +114,8 @@ void ForearmController::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||
MWBase::Environment::get().getWorld()->getRenderingManager().getCamera()->updateCamera();
|
||||
}
|
||||
|
||||
MWVR::Pose handStage = handPosesStage[side];
|
||||
MWVR::Pose headStage = poses.head[(int)TrackedSpace::STAGE];
|
||||
xr->playerScale(handStage);
|
||||
xr->playerScale(headStage);
|
||||
MWVR::Pose handStage = session->predictedPoses(VRSession::FramePhase::Predraw).hands[side];
|
||||
MWVR::Pose headStage = session->predictedPoses(VRSession::FramePhase::Predraw).head;
|
||||
|
||||
auto orientation = handStage.orientation;
|
||||
|
||||
|
@ -426,7 +421,7 @@ void WeaponPointerController::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||
|
||||
VRAnimation::VRAnimation(
|
||||
const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
|
||||
bool disableSounds, std::shared_ptr<OpenXRSession> xrSession)
|
||||
bool disableSounds, std::shared_ptr<VRSession> xrSession)
|
||||
// Note that i let it construct as 3rd person and then later update it to VM_VRHeadless
|
||||
// when the character controller updates
|
||||
: MWRender::NpcAnimation(ptr, parentNode, resourceSystem, disableSounds, VM_Normal, 55.f)
|
||||
|
@ -516,6 +511,20 @@ void VRAnimation::updateParts()
|
|||
removeIndividualPart(ESM::PartReferenceType::PRT_RForearm);
|
||||
removeIndividualPart(ESM::PartReferenceType::PRT_RWrist);
|
||||
}
|
||||
|
||||
|
||||
auto playerPtr = MWMechanics::getPlayer();
|
||||
const MWWorld::LiveCellRef<ESM::NPC>* ref = playerPtr.get<ESM::NPC>();
|
||||
const ESM::Race* race =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
|
||||
bool isMale = ref->mBase->isMale();
|
||||
float charHeightFactor = isMale ? race->mData.mHeight.mMale : race->mData.mHeight.mFemale;
|
||||
float charHeightBase = 1.8f;
|
||||
float charHeight = charHeightBase * charHeightFactor;
|
||||
// TODO: Player height should be configurable
|
||||
// For now i'm just using my own
|
||||
float sizeFactor = 1.85f / charHeight;
|
||||
Environment::get().getSession()->setPlayerScale(sizeFactor);
|
||||
}
|
||||
|
||||
void VRAnimation::setPointForward(bool enabled)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "../mwrender/npcanimation.hpp"
|
||||
#include "../mwrender/renderingmanager.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrsession.hpp"
|
||||
#include "vrsession.hpp"
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ public:
|
|||
* @param xrSession The XR session that shall be used to track limbs
|
||||
*/
|
||||
VRAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
|
||||
bool disableSounds, std::shared_ptr<OpenXRSession> xrSession );
|
||||
bool disableSounds, std::shared_ptr<VRSession> xrSession );
|
||||
virtual ~VRAnimation();
|
||||
|
||||
/// Overridden to always be false
|
||||
|
@ -74,7 +74,7 @@ protected:
|
|||
float getVelocity(const std::string& groupname) const override;
|
||||
|
||||
public:
|
||||
std::shared_ptr<OpenXRSession> mSession;
|
||||
std::shared_ptr<VRSession> mSession;
|
||||
osg::ref_ptr<ForearmController> mForearmControllers[2];
|
||||
osg::ref_ptr<HandController> mHandControllers[2];
|
||||
osg::ref_ptr<FingerController> mIndexFingerControllers[2];
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include "vranimation.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
#include "openxrsession.hpp"
|
||||
#include "vrsession.hpp"
|
||||
#include "vrgui.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -55,12 +55,12 @@ MWVR::OpenXRInputManager* MWVR::Environment::getInputManager() const
|
|||
return xrInputManager;
|
||||
}
|
||||
|
||||
MWVR::OpenXRSession* MWVR::Environment::getSession() const
|
||||
MWVR::VRSession* MWVR::Environment::getSession() const
|
||||
{
|
||||
return mSession;
|
||||
}
|
||||
|
||||
void MWVR::Environment::setSession(MWVR::OpenXRSession* xrSession)
|
||||
void MWVR::Environment::setSession(MWVR::VRSession* xrSession)
|
||||
{
|
||||
mSession = xrSession;
|
||||
}
|
||||
|
@ -86,12 +86,12 @@ void MWVR::Environment::setPlayerAnimation(MWVR::VRAnimation* xrAnimation)
|
|||
}
|
||||
|
||||
|
||||
MWVR::OpenXRViewer* MWVR::Environment::getViewer() const
|
||||
MWVR::VRViewer* MWVR::Environment::getViewer() const
|
||||
{
|
||||
return mViewer;
|
||||
}
|
||||
|
||||
void MWVR::Environment::setViewer(MWVR::OpenXRViewer* xrViewer)
|
||||
void MWVR::Environment::setViewer(MWVR::VRViewer* xrViewer)
|
||||
{
|
||||
mViewer = xrViewer;
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ namespace MWVR
|
|||
{
|
||||
class VRAnimation;
|
||||
class OpenXRInputManager;
|
||||
class OpenXRSession;
|
||||
class VRSession;
|
||||
class VRGUIManager;
|
||||
class OpenXRViewer;
|
||||
class VRViewer;
|
||||
class OpenXRManager;
|
||||
|
||||
/// \brief Central hub for mw openxr subsystems
|
||||
|
@ -51,11 +51,11 @@ namespace MWVR
|
|||
MWVR::VRAnimation* getPlayerAnimation() const;
|
||||
void setPlayerAnimation(MWVR::VRAnimation* xrAnimation);
|
||||
|
||||
MWVR::OpenXRSession* getSession() const;
|
||||
void setSession(MWVR::OpenXRSession* xrSession);
|
||||
MWVR::VRSession* getSession() const;
|
||||
void setSession(MWVR::VRSession* xrSession);
|
||||
|
||||
MWVR::OpenXRViewer* getViewer() const;
|
||||
void setViewer(MWVR::OpenXRViewer* xrViewer);
|
||||
MWVR::VRViewer* getViewer() const;
|
||||
void setViewer(MWVR::VRViewer* xrViewer);
|
||||
|
||||
MWVR::OpenXRManager* getManager() const;
|
||||
void setManager(MWVR::OpenXRManager* xrManager);
|
||||
|
@ -64,10 +64,10 @@ namespace MWVR
|
|||
void setUnitsPerMeter(float unitsPerMeter);
|
||||
|
||||
private:
|
||||
MWVR::OpenXRSession* mSession{ nullptr };
|
||||
MWVR::VRSession* mSession{ nullptr };
|
||||
MWVR::VRGUIManager* mGUIManager{ nullptr };
|
||||
MWVR::VRAnimation* mPlayerAnimation{ nullptr };
|
||||
MWVR::OpenXRViewer* mViewer{ nullptr };
|
||||
MWVR::VRViewer* mViewer{ nullptr };
|
||||
MWVR::OpenXRManager* mOpenXRManager{ nullptr };
|
||||
float mUnitsPerMeter{ 1.f };
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "vrgui.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
#include "openxrsession.hpp"
|
||||
#include "vrsession.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
#include "vranimation.hpp"
|
||||
|
@ -182,6 +182,8 @@ VRGUILayer::VRGUILayer(
|
|||
mGUICamera = new GUICamera(config.pixelResolution.x(), config.pixelResolution.y(), config.backgroundColor);
|
||||
osgMyGUI::RenderManager& renderManager = static_cast<osgMyGUI::RenderManager&>(MyGUI::RenderManager::getInstance());
|
||||
mMyGUICamera = renderManager.createGUICamera(osg::Camera::NESTED_RENDER, filter);
|
||||
//myGUICamera->setViewport(0, 0, 256, 256);
|
||||
//mMyGUICamera->setProjectionMatrixAsOrtho2D(-1, 1, -1, 1);
|
||||
mGUICamera->setScene(mMyGUICamera);
|
||||
|
||||
// Define state set that allows rendering with transparency
|
||||
|
@ -543,7 +545,7 @@ void VRGUIManager::updateSideBySideLayers()
|
|||
float span = sSideBySideAzimuthInterval * (n - 1); // zero index, places lone layers straight ahead
|
||||
float low = -span / 2;
|
||||
|
||||
for (int i = 0; i < mSideBySideLayers.size(); i++)
|
||||
for (unsigned i = 0; i < mSideBySideLayers.size(); i++)
|
||||
mSideBySideLayers[i]->setAngle(low + static_cast<float>(i) * sSideBySideAzimuthInterval);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
#include <osg/Camera>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
|
||||
#include "openxrview.hpp"
|
||||
#include "openxrlayer.hpp"
|
||||
#include "vrview.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
|
||||
namespace MyGUI
|
||||
{
|
||||
|
|
271
apps/openmw/mwvr/vrsession.cpp
Normal file
271
apps/openmw/mwvr/vrsession.cpp
Normal file
|
@ -0,0 +1,271 @@
|
|||
#include "vrenvironment.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
#include "openxrswapchain.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
|
||||
#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>
|
||||
#include "vrsession.hpp"
|
||||
#include "vrgui.hpp"
|
||||
#include <time.h>
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
VRSession::VRSession()
|
||||
{
|
||||
}
|
||||
|
||||
VRSession::~VRSession()
|
||||
{
|
||||
}
|
||||
|
||||
osg::Matrix VRSession::projectionMatrix(FramePhase phase, Side side)
|
||||
{
|
||||
assert(((int)side) < 2);
|
||||
auto fov = predictedPoses(VRSession::FramePhase::Predraw).view[(int)side].fov;
|
||||
float near_ = Settings::Manager::getFloat("near clip", "Camera");
|
||||
float far_ = Settings::Manager::getFloat("viewing distance", "Camera");
|
||||
return fov.perspectiveMatrix(near_, far_);
|
||||
}
|
||||
|
||||
osg::Matrix VRSession::viewMatrix(FramePhase phase, Side side)
|
||||
{
|
||||
MWVR::Pose pose = predictedPoses(VRSession::FramePhase::Predraw).view[(int)side].pose;
|
||||
osg::Vec3 position = pose.position * Environment::get().unitsPerMeter();
|
||||
osg::Quat orientation = pose.orientation;
|
||||
|
||||
float y = position.y();
|
||||
float z = position.z();
|
||||
position.y() = z;
|
||||
position.z() = -y;
|
||||
|
||||
y = orientation.y();
|
||||
z = orientation.z();
|
||||
orientation.y() = z;
|
||||
orientation.z() = -y;
|
||||
|
||||
osg::Matrix viewMatrix;
|
||||
viewMatrix.setTrans(-position);
|
||||
viewMatrix.postMultRotate(orientation.conj());
|
||||
|
||||
return viewMatrix;
|
||||
}
|
||||
|
||||
bool VRSession::isRunning() const {
|
||||
auto* xr = Environment::get().getManager();
|
||||
return xr->sessionRunning();
|
||||
}
|
||||
|
||||
void VRSession::swapBuffers(osg::GraphicsContext* gc, VRViewer& viewer)
|
||||
{
|
||||
Timer timer("VRSession::SwapBuffers");
|
||||
auto* xr = Environment::get().getManager();
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
|
||||
xr->handleEvents();
|
||||
|
||||
mPostdrawFrame = std::move(mDrawFrame);
|
||||
assert(mPostdrawFrame);
|
||||
}
|
||||
|
||||
// TODO: Should blit mirror texture regardless
|
||||
if (!isRunning())
|
||||
return;
|
||||
|
||||
xr->waitFrame();
|
||||
xr->beginFrame();
|
||||
viewer.swapBuffers(gc);
|
||||
|
||||
auto leftView = viewer.mViews["LeftEye"];
|
||||
auto rightView = viewer.mViews["RightEye"];
|
||||
|
||||
leftView->swapBuffers(gc);
|
||||
rightView->swapBuffers(gc);
|
||||
|
||||
std::array<XrCompositionLayerProjectionView, 2> compositionLayerProjectionViews{};
|
||||
compositionLayerProjectionViews[0].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
||||
compositionLayerProjectionViews[1].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
||||
compositionLayerProjectionViews[0].subImage = leftView->swapchain().subImage();
|
||||
compositionLayerProjectionViews[1].subImage = rightView->swapchain().subImage();
|
||||
compositionLayerProjectionViews[0].pose = toXR(mPostdrawFrame->mPredictedPoses.eye[(int)Side::LEFT_HAND]);
|
||||
compositionLayerProjectionViews[1].pose = toXR(mPostdrawFrame->mPredictedPoses.eye[(int)Side::RIGHT_HAND]);
|
||||
compositionLayerProjectionViews[0].fov = toXR(mPostdrawFrame->mPredictedPoses.view[(int)Side::LEFT_HAND].fov);
|
||||
compositionLayerProjectionViews[1].fov = toXR(mPostdrawFrame->mPredictedPoses.view[(int)Side::RIGHT_HAND].fov);
|
||||
XrCompositionLayerProjection layer{};
|
||||
layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
|
||||
layer.space = xr->impl().getReferenceSpace(TrackedSpace::STAGE);
|
||||
layer.viewCount = 2;
|
||||
layer.views = compositionLayerProjectionViews.data();
|
||||
auto* layerStack = reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer);
|
||||
|
||||
xr->endFrame(mPostdrawFrame->mPredictedDisplayTime, 1, &layerStack);
|
||||
mLastRenderedFrame = mPostdrawFrame->mFrameNo;
|
||||
}
|
||||
|
||||
void VRSession::advanceFramePhase(void)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
Timer timer("VRSession::advanceFrame");
|
||||
|
||||
assert(!mDrawFrame);
|
||||
mDrawFrame = std::move(mPredrawFrame);
|
||||
}
|
||||
|
||||
void VRSession::startFrame()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
assert(!mPredrawFrame);
|
||||
|
||||
Timer timer("VRSession::startFrame");
|
||||
auto* xr = Environment::get().getManager();
|
||||
auto* input = Environment::get().getInputManager();
|
||||
|
||||
// Until OpenXR allows us to get a prediction without waiting
|
||||
// we make our own (bad) prediction and let openxr wait
|
||||
auto frameState = xr->impl().frameState();
|
||||
long long predictedDisplayTime = 0;
|
||||
if (mFrames == mLastRenderedFrame)
|
||||
{
|
||||
predictedDisplayTime = frameState.predictedDisplayTime;
|
||||
}
|
||||
else if (mFrames > mLastRenderedFrame)
|
||||
{
|
||||
predictedDisplayTime = frameState.predictedDisplayTime + frameState.predictedDisplayPeriod;
|
||||
}
|
||||
|
||||
if (mFrames == 0 || predictedDisplayTime == 0)
|
||||
{
|
||||
// First time, need to populate the frame state struct
|
||||
predictedDisplayTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
PoseSet predictedPoses{};
|
||||
if (isRunning())
|
||||
{
|
||||
predictedPoses.head = xr->impl().getPredictedLimbPose(predictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE) * mPlayerScale;
|
||||
predictedPoses.hands[(int)Side::LEFT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::LEFT_HAND) * mPlayerScale;
|
||||
predictedPoses.hands[(int)Side::RIGHT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::RIGHT_HAND) * mPlayerScale;
|
||||
auto hmdViews = xr->impl().getPredictedViews(predictedDisplayTime, TrackedSpace::VIEW);
|
||||
predictedPoses.view[(int)Side::LEFT_HAND].pose = fromXR(hmdViews[(int)Side::LEFT_HAND].pose) * mPlayerScale;
|
||||
predictedPoses.view[(int)Side::RIGHT_HAND].pose = fromXR(hmdViews[(int)Side::RIGHT_HAND].pose) * mPlayerScale;
|
||||
predictedPoses.view[(int)Side::LEFT_HAND].fov = fromXR(hmdViews[(int)Side::LEFT_HAND].fov);
|
||||
predictedPoses.view[(int)Side::RIGHT_HAND].fov = fromXR(hmdViews[(int)Side::RIGHT_HAND].fov);
|
||||
auto stageViews = xr->impl().getPredictedViews(predictedDisplayTime, TrackedSpace::STAGE);
|
||||
predictedPoses.eye[(int)Side::LEFT_HAND] = fromXR(stageViews[(int)Side::LEFT_HAND].pose) * mPlayerScale;
|
||||
predictedPoses.eye[(int)Side::RIGHT_HAND] = fromXR(stageViews[(int)Side::RIGHT_HAND].pose) * mPlayerScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If session is not running, copy poses from the last frame
|
||||
if (mPostdrawFrame)
|
||||
predictedPoses = mPostdrawFrame->mPredictedPoses;
|
||||
}
|
||||
|
||||
mFrames++;
|
||||
|
||||
mPredrawFrame.reset(new VRFrame);
|
||||
mPredrawFrame->mPredictedDisplayTime = predictedDisplayTime;
|
||||
mPredrawFrame->mFrameNo = mFrames;
|
||||
mPredrawFrame->mPredictedPoses = predictedPoses;
|
||||
}
|
||||
|
||||
const PoseSet& VRSession::predictedPoses(FramePhase phase)
|
||||
{
|
||||
|
||||
switch (phase)
|
||||
{
|
||||
case FramePhase::Predraw:
|
||||
if (!mPredrawFrame)
|
||||
startFrame();
|
||||
assert(mPredrawFrame);
|
||||
return mPredrawFrame->mPredictedPoses;
|
||||
|
||||
case FramePhase::Draw:
|
||||
assert(mDrawFrame);
|
||||
return mDrawFrame->mPredictedPoses;
|
||||
|
||||
case FramePhase::Postdraw:
|
||||
assert(mPostdrawFrame);
|
||||
return mPostdrawFrame->mPredictedPoses;
|
||||
}
|
||||
|
||||
assert(0);
|
||||
return mPredrawFrame->mPredictedPoses;
|
||||
}
|
||||
|
||||
// OSG doesn't provide API to extract euler angles from a quat, but i need it.
|
||||
// Credits goes to Dennis Bunfield, i just copied his formula https://narkive.com/v0re6547.4
|
||||
void getEulerAngles(const osg::Quat& quat, float& yaw, float& pitch, float& roll)
|
||||
{
|
||||
// Now do the computation
|
||||
osg::Matrixd m2(osg::Matrixd::rotate(quat));
|
||||
double* mat = (double*)m2.ptr();
|
||||
double angle_x = 0.0;
|
||||
double angle_y = 0.0;
|
||||
double angle_z = 0.0;
|
||||
double D, C, tr_x, tr_y;
|
||||
angle_y = D = asin(mat[2]); /* Calculate Y-axis angle */
|
||||
C = cos(angle_y);
|
||||
if (fabs(C) > 0.005) /* Test for Gimball lock? */
|
||||
{
|
||||
tr_x = mat[10] / C; /* No, so get X-axis angle */
|
||||
tr_y = -mat[6] / C;
|
||||
angle_x = atan2(tr_y, tr_x);
|
||||
tr_x = mat[0] / C; /* Get Z-axis angle */
|
||||
tr_y = -mat[1] / C;
|
||||
angle_z = atan2(tr_y, tr_x);
|
||||
}
|
||||
else /* Gimball lock has occurred */
|
||||
{
|
||||
angle_x = 0; /* Set X-axis angle to zero
|
||||
*/
|
||||
tr_x = mat[5]; /* And calculate Z-axis angle
|
||||
*/
|
||||
tr_y = mat[4];
|
||||
angle_z = atan2(tr_y, tr_x);
|
||||
}
|
||||
|
||||
yaw = angle_z;
|
||||
pitch = angle_x;
|
||||
roll = angle_y;
|
||||
}
|
||||
|
||||
void VRSession::movementAngles(float& yaw, float& pitch)
|
||||
{
|
||||
assert(mPredrawFrame);
|
||||
auto* input = Environment::get().getInputManager();
|
||||
auto lhandquat = input->getHandPose(mPredrawFrame->mPredictedDisplayTime, TrackedSpace::VIEW, Side::LEFT_HAND).orientation;
|
||||
//predictedPoses(FramePhase::Predraw).hands[(int)TrackedSpace::VIEW][(int)MWVR::Side::LEFT_HAND].orientation;
|
||||
float roll = 0.f;
|
||||
getEulerAngles(lhandquat, yaw, pitch, roll);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::ostream& operator <<(
|
||||
std::ostream& os,
|
||||
const MWVR::Pose& pose)
|
||||
{
|
||||
os << "position=" << pose.position << " orientation=" << pose.orientation << " velocity=" << pose.velocity;
|
||||
return os;
|
||||
}
|
||||
|
81
apps/openmw/mwvr/vrsession.hpp
Normal file
81
apps/openmw/mwvr/vrsession.hpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
#ifndef MWVR_OPENRXSESSION_H
|
||||
#define MWVR_OPENRXSESSION_H
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <queue>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include "openxrmanager.hpp"
|
||||
#include "vrviewer.hpp"
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
|
||||
extern void getEulerAngles(const osg::Quat& quat, float& yaw, float& pitch, float& roll);
|
||||
|
||||
class VRSession
|
||||
{
|
||||
public:
|
||||
using seconds = std::chrono::duration<double>;
|
||||
using nanoseconds = std::chrono::nanoseconds;
|
||||
using clock = std::chrono::steady_clock;
|
||||
using time_point = clock::time_point;
|
||||
|
||||
enum class FramePhase
|
||||
{
|
||||
Predraw = 0, //!< Get poses predicted for the next frame to be drawn
|
||||
Draw = 1, //!< Get poses predicted for the current rendering frame
|
||||
Postdraw = 2, //!< Get poses predicted for the last rendered frame
|
||||
};
|
||||
|
||||
struct VRFrame
|
||||
{
|
||||
long long mFrameNo{ 0 };
|
||||
long long mPredictedDisplayTime{ 0 };
|
||||
PoseSet mPredictedPoses{};
|
||||
};
|
||||
|
||||
public:
|
||||
VRSession();
|
||||
~VRSession();
|
||||
|
||||
void swapBuffers(osg::GraphicsContext* gc, VRViewer& viewer);
|
||||
|
||||
const PoseSet& predictedPoses(FramePhase phase);
|
||||
|
||||
//! Starts a new frame
|
||||
void startFrame();
|
||||
|
||||
//! Angles to be used for overriding movement direction
|
||||
void movementAngles(float& yaw, float& pitch);
|
||||
|
||||
void advanceFramePhase(void);
|
||||
|
||||
bool isRunning() const;
|
||||
|
||||
float playerScale() const { return mPlayerScale; }
|
||||
float setPlayerScale(float scale) { return mPlayerScale = scale; }
|
||||
|
||||
osg::Matrix viewMatrix(FramePhase phase, Side side);
|
||||
osg::Matrix projectionMatrix(FramePhase phase, Side side);
|
||||
|
||||
std::unique_ptr<VRFrame> mPredrawFrame{ nullptr };
|
||||
std::unique_ptr<VRFrame> mDrawFrame{ nullptr };
|
||||
std::unique_ptr<VRFrame> mPostdrawFrame{ nullptr };
|
||||
|
||||
std::mutex mMutex{};
|
||||
std::condition_variable mCondition{};
|
||||
|
||||
long long mFrames{ 0 };
|
||||
long long mLastRenderedFrame{ 0 };
|
||||
|
||||
float mPlayerScale{ 1.f };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
#include "openxrviewer.hpp"
|
||||
#include "vrviewer.hpp"
|
||||
#include "vrtexture.hpp"
|
||||
#include <osg/Texture2D>
|
||||
#include <osgViewer/Renderer>
|
||||
|
|
114
apps/openmw/mwvr/vrview.cpp
Normal file
114
apps/openmw/mwvr/vrview.cpp
Normal file
|
@ -0,0 +1,114 @@
|
|||
#include "vrview.hpp"
|
||||
#include "vrsession.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwrender/renderingmanager.hpp"
|
||||
#include "../mwrender/water.hpp"
|
||||
#include "vrenvironment.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 <osgViewer/Renderer>
|
||||
|
||||
namespace MWVR {
|
||||
|
||||
VRView::VRView(
|
||||
std::string name,
|
||||
OpenXRSwapchain::Config config,
|
||||
osg::ref_ptr<osg::State> state)
|
||||
: mSwapchainConfig{ config }
|
||||
, mSwapchain(new OpenXRSwapchain(state, mSwapchainConfig))
|
||||
, mName(name)
|
||||
, mTimer(mName.c_str())
|
||||
{
|
||||
}
|
||||
|
||||
VRView::~VRView()
|
||||
{
|
||||
}
|
||||
|
||||
osg::Camera* VRView::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 + 2);
|
||||
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
camera->setAllowEventFocus(false);
|
||||
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
||||
camera->setViewport(0, 0, mSwapchain->width(), mSwapchain->height());
|
||||
camera->setGraphicsContext(gc);
|
||||
|
||||
camera->setInitialDrawCallback(new VRView::InitialDrawCallback());
|
||||
|
||||
return camera.release();
|
||||
}
|
||||
|
||||
void VRView::prerenderCallback(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
if (mSwapchain)
|
||||
{
|
||||
mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext());
|
||||
}
|
||||
mTimer.checkpoint("Prerender");
|
||||
|
||||
}
|
||||
|
||||
void VRView::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
|
||||
renderer->setCameraRequiresSetUp(false);
|
||||
}
|
||||
auto name = renderInfo.getCurrentCamera()->getName();
|
||||
}
|
||||
void VRView::UpdateSlaveCallback::updateSlave(
|
||||
osg::View& view,
|
||||
osg::View::Slave& slave)
|
||||
{
|
||||
mView->mTimer.checkpoint("UpdateSlave");
|
||||
|
||||
auto* camera = slave._camera.get();
|
||||
auto name = camera->getName();
|
||||
|
||||
Side side = Side::RIGHT_HAND;
|
||||
if (name == "LeftEye")
|
||||
side = Side::LEFT_HAND;
|
||||
|
||||
auto* session = Environment::get().getSession();
|
||||
auto viewMatrix = view.getCamera()->getViewMatrix();
|
||||
auto modifiedViewMatrix = viewMatrix * session->viewMatrix(VRSession::FramePhase::Predraw, side);
|
||||
auto projectionMatrix = session->projectionMatrix(VRSession::FramePhase::Predraw, side);
|
||||
|
||||
camera->setViewMatrix(modifiedViewMatrix);
|
||||
camera->setProjectionMatrix(projectionMatrix);
|
||||
slave.updateSlaveImplementation(view);
|
||||
}
|
||||
|
||||
void VRView::postrenderCallback(osg::RenderInfo& renderInfo)
|
||||
{
|
||||
auto name = renderInfo.getCurrentCamera()->getName();
|
||||
mTimer.checkpoint("Postrender");
|
||||
}
|
||||
|
||||
void VRView::swapBuffers(osg::GraphicsContext* gc)
|
||||
{
|
||||
swapchain().endFrame(gc);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ struct XrSwapchainSubImage;
|
|||
namespace MWVR
|
||||
{
|
||||
|
||||
class OpenXRView : public osg::Referenced
|
||||
class VRView : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
|
@ -19,15 +19,25 @@ namespace MWVR
|
|||
public:
|
||||
virtual void operator()(osg::RenderInfo& renderInfo) const;
|
||||
};
|
||||
class FinalDrawCallback : public osg::Camera::DrawCallback
|
||||
|
||||
class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
|
||||
{
|
||||
public:
|
||||
virtual void operator()(osg::RenderInfo& renderInfo) const;
|
||||
UpdateSlaveCallback(osg::ref_ptr<VRView> view, osg::GraphicsContext* gc)
|
||||
: mView(view), mGC(gc)
|
||||
{}
|
||||
|
||||
void updateSlave(osg::View& view, osg::View::Slave& slave) override;
|
||||
|
||||
private:
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
osg::ref_ptr<VRView> mView;
|
||||
osg::ref_ptr<osg::GraphicsContext> mGC;
|
||||
};
|
||||
|
||||
protected:
|
||||
OpenXRView(osg::ref_ptr<OpenXRManager> XR, std::string name, OpenXRSwapchain::Config config, osg::ref_ptr<osg::State> state);
|
||||
virtual ~OpenXRView();
|
||||
public:
|
||||
VRView(std::string name, OpenXRSwapchain::Config config, osg::ref_ptr<osg::State> state);
|
||||
virtual ~VRView();
|
||||
|
||||
public:
|
||||
//! Prepare for render (set FBO)
|
||||
|
@ -41,18 +51,12 @@ namespace MWVR
|
|||
|
||||
void swapBuffers(osg::GraphicsContext* gc);
|
||||
|
||||
void setPredictedPose(const Pose& pose);
|
||||
Pose& predictedPose() { return mPredictedPose; };
|
||||
|
||||
public:
|
||||
osg::ref_ptr<OpenXRManager> mXR;
|
||||
OpenXRSwapchain::Config mSwapchainConfig;
|
||||
std::unique_ptr<OpenXRSwapchain> mSwapchain;
|
||||
std::string mName{};
|
||||
bool mRendering{ false };
|
||||
Timer mTimer;
|
||||
|
||||
Pose mPredictedPose{ {}, {0,0,0,1}, {} };
|
||||
};
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include "openxrviewer.hpp"
|
||||
#include "openxrsession.hpp"
|
||||
#include "vrviewer.hpp"
|
||||
#include "vrsession.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
|
@ -17,70 +17,47 @@
|
|||
namespace MWVR
|
||||
{
|
||||
|
||||
OpenXRViewer::OpenXRViewer(
|
||||
VRViewer::VRViewer(
|
||||
osg::ref_ptr<osgViewer::Viewer> viewer)
|
||||
: osg::Group()
|
||||
, mCompositionLayerProjectionViews(2, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW})
|
||||
, mRealizeOperation(new RealizeOperation())
|
||||
: mRealizeOperation(new RealizeOperation())
|
||||
, mViewer(viewer)
|
||||
, mPreDraw(new PredrawCallback(this))
|
||||
, mPostDraw(new PostdrawCallback(this))
|
||||
, mConfigured(false)
|
||||
{
|
||||
mViewer->setRealizeOperation(mRealizeOperation);
|
||||
mCompositionLayerProjectionViews[0].pose.orientation.w = 1;
|
||||
mCompositionLayerProjectionViews[1].pose.orientation.w = 1;
|
||||
this->setName("OpenXRRoot");
|
||||
//this->setName("OpenXRRoot");
|
||||
}
|
||||
|
||||
OpenXRViewer::~OpenXRViewer(void)
|
||||
VRViewer::~VRViewer(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRViewer::traverse(
|
||||
osg::NodeVisitor& visitor)
|
||||
{
|
||||
if (mRealizeOperation->realized())
|
||||
osg::Group::traverse(visitor);
|
||||
}
|
||||
|
||||
const XrCompositionLayerBaseHeader*
|
||||
OpenXRViewer::layer()
|
||||
{
|
||||
// Does not check for visible, since this should always render
|
||||
return reinterpret_cast<XrCompositionLayerBaseHeader*>(mLayer.get());
|
||||
}
|
||||
|
||||
void OpenXRViewer::traversals()
|
||||
void VRViewer::traversals()
|
||||
{
|
||||
mViewer->updateTraversal();
|
||||
mViewer->renderingTraversals();
|
||||
}
|
||||
|
||||
void OpenXRViewer::realize(osg::GraphicsContext* context)
|
||||
void VRViewer::realize(osg::GraphicsContext* context)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
|
||||
|
||||
if (mConfigured)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context->isCurrent())
|
||||
if (!context->makeCurrent())
|
||||
{
|
||||
throw std::logic_error("OpenXRViewer::configure() failed to make graphics context current.");
|
||||
throw std::logic_error("VRViewer::configure() failed to make graphics context current.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
auto mainCamera = mCameras["MainCamera"] = mViewer->getCamera();
|
||||
mainCamera->setName("Main");
|
||||
mainCamera->setInitialDrawCallback(new OpenXRView::InitialDrawCallback());
|
||||
|
||||
// Use the main camera to render any GUI to the OpenXR GUI quad's swapchain.
|
||||
// (When swapping the window buffer we'll blit the mirror texture to it instead.)
|
||||
mainCamera->setCullMask(MWRender::Mask_GUI);
|
||||
mainCamera->setInitialDrawCallback(new VRView::InitialDrawCallback());
|
||||
|
||||
osg::Vec4 clearColor = mainCamera->getClearColor();
|
||||
|
||||
|
@ -88,7 +65,7 @@ namespace MWVR
|
|||
if (!xr->realized())
|
||||
xr->realize(context);
|
||||
|
||||
auto* session = Environment::get().getSession();
|
||||
xr->handleEvents();
|
||||
|
||||
OpenXRSwapchain::Config leftConfig;
|
||||
leftConfig.width = xr->impl().mConfigViews[(int)Side::LEFT_HAND].recommendedImageRectWidth;
|
||||
|
@ -99,8 +76,8 @@ namespace MWVR
|
|||
rightConfig.height = xr->impl().mConfigViews[(int)Side::RIGHT_HAND].recommendedImageRectHeight;
|
||||
rightConfig.samples = xr->impl().mConfigViews[(int)Side::RIGHT_HAND].recommendedSwapchainSampleCount;
|
||||
|
||||
auto leftView = new OpenXRWorldView(xr, "LeftEye", context->getState(), leftConfig);
|
||||
auto rightView = new OpenXRWorldView(xr, "RightEye", context->getState(), rightConfig);
|
||||
auto leftView = new VRView("LeftEye", leftConfig, context->getState());
|
||||
auto rightView = new VRView("RightEye", rightConfig, context->getState());
|
||||
|
||||
mViews["LeftEye"] = leftView;
|
||||
mViews["RightEye"] = rightView;
|
||||
|
@ -111,28 +88,36 @@ namespace MWVR
|
|||
leftCamera->setPreDrawCallback(mPreDraw);
|
||||
rightCamera->setPreDrawCallback(mPreDraw);
|
||||
|
||||
//leftCamera->setPostDrawCallback(mPostDraw);
|
||||
//rightCamera->setPostDrawCallback(mPostDraw);
|
||||
leftCamera->setFinalDrawCallback(mPostDraw);
|
||||
rightCamera->setFinalDrawCallback(mPostDraw);
|
||||
|
||||
// Stereo cameras should only draw the scene (AR layers should later add minimap, health, etc.)
|
||||
leftCamera->setCullMask(~MWRender::Mask_GUI);
|
||||
rightCamera->setCullMask(~MWRender::Mask_GUI);
|
||||
// Stereo cameras should only draw the scene
|
||||
leftCamera->setCullMask(~MWRender::Mask_GUI & ~MWRender::Mask_SimpleWater & ~MWRender::Mask_UpdateVisitor);
|
||||
rightCamera->setCullMask(~MWRender::Mask_GUI & ~MWRender::Mask_SimpleWater & ~MWRender::Mask_UpdateVisitor);
|
||||
|
||||
leftCamera->setName("LeftEye");
|
||||
rightCamera->setName("RightEye");
|
||||
|
||||
mViewer->addSlave(leftCamera, leftView->projectionMatrix(), leftView->viewMatrix(), true);
|
||||
mViewer->addSlave(rightCamera, rightView->projectionMatrix(), rightView->viewMatrix(), true);
|
||||
|
||||
osg::Camera::CullingMode cullingMode = osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING;
|
||||
|
||||
if (!Settings::Manager::getBool("small feature culling", "Camera"))
|
||||
cullingMode &= ~(osg::CullStack::SMALL_FEATURE_CULLING);
|
||||
else
|
||||
{
|
||||
auto smallFeatureCullingPixelSize = Settings::Manager::getFloat("small feature culling pixel size", "Camera");
|
||||
leftCamera->setSmallFeatureCullingPixelSize(smallFeatureCullingPixelSize);
|
||||
rightCamera->setSmallFeatureCullingPixelSize(smallFeatureCullingPixelSize);
|
||||
cullingMode |= osg::CullStack::SMALL_FEATURE_CULLING;
|
||||
}
|
||||
leftCamera->setCullingMode(cullingMode);
|
||||
rightCamera->setCullingMode(cullingMode);
|
||||
|
||||
mViewer->addSlave(leftCamera, true);
|
||||
mViewer->addSlave(rightCamera, true);
|
||||
|
||||
mViewer->setLightingMode(osg::View::SKY_LIGHT);
|
||||
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
||||
|
||||
mCompositionLayerProjectionViews[0].subImage = leftView->swapchain().subImage();
|
||||
mCompositionLayerProjectionViews[1].subImage = rightView->swapchain().subImage();
|
||||
|
||||
|
||||
OpenXRSwapchain::Config config;
|
||||
config.width = mainCamera->getViewport()->width();
|
||||
|
@ -140,32 +125,31 @@ namespace MWVR
|
|||
config.samples = 1;
|
||||
|
||||
// Mirror texture doesn't have to be an OpenXR swapchain.
|
||||
// It's just convenient.
|
||||
mMirrorTextureSwapchain.reset(new OpenXRSwapchain(xr, context->getState(), config));
|
||||
// It was just convenient at the time.
|
||||
mMirrorTextureSwapchain.reset(new OpenXRSwapchain(context->getState(), config));
|
||||
|
||||
//mViewer->addSlave(menuCamera, true);
|
||||
mViewer->getSlave(0)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(xr, session, leftView, context);
|
||||
mViewer->getSlave(1)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(xr, session, rightView, context);
|
||||
mViewer->getSlave(0)._updateSlaveCallback = new VRView::UpdateSlaveCallback(leftView, context);
|
||||
mViewer->getSlave(1)._updateSlaveCallback = new VRView::UpdateSlaveCallback(rightView, context);
|
||||
|
||||
mMainCameraGC = mainCamera->getGraphicsContext();
|
||||
mMainCameraGC->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this));
|
||||
mMainCameraGC->setSwapCallback(new VRViewer::SwapBuffersCallback(this));
|
||||
mainCamera->setGraphicsContext(nullptr);
|
||||
session->setLayer(OpenXRLayerStack::WORLD_VIEW_LAYER, this);
|
||||
mConfigured = true;
|
||||
|
||||
Log(Debug::Verbose) << "Realized";
|
||||
}
|
||||
|
||||
void OpenXRViewer::enableMainCamera(void)
|
||||
void VRViewer::enableMainCamera(void)
|
||||
{
|
||||
mCameras["MainCamera"]->setGraphicsContext(mMainCameraGC);
|
||||
}
|
||||
|
||||
void OpenXRViewer::disableMainCamera(void)
|
||||
void VRViewer::disableMainCamera(void)
|
||||
{
|
||||
mCameras["MainCamera"]->setGraphicsContext(nullptr);
|
||||
}
|
||||
|
||||
void OpenXRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc)
|
||||
void VRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc)
|
||||
{
|
||||
//includeMenu = false;
|
||||
mMirrorTextureSwapchain->beginFrame(gc);
|
||||
|
@ -189,60 +173,33 @@ namespace MWVR
|
|||
}
|
||||
|
||||
void
|
||||
OpenXRViewer::SwapBuffersCallback::swapBuffersImplementation(
|
||||
VRViewer::SwapBuffersCallback::swapBuffersImplementation(
|
||||
osg::GraphicsContext* gc)
|
||||
{
|
||||
auto* session = Environment::get().getSession();
|
||||
session->swapBuffers(gc);
|
||||
session->swapBuffers(gc, *mViewer);
|
||||
gc->swapBuffersImplementation();
|
||||
}
|
||||
|
||||
void OpenXRViewer::swapBuffers(osg::GraphicsContext* gc)
|
||||
void VRViewer::swapBuffers(osg::GraphicsContext* gc)
|
||||
{
|
||||
if (!mConfigured)
|
||||
return;
|
||||
|
||||
auto* session = Environment::get().getSession();
|
||||
auto* xr = Environment::get().getManager();
|
||||
Timer timer("VRViewer::SwapBuffers");
|
||||
|
||||
Timer timer("OpenXRViewer::SwapBuffers");
|
||||
for (auto& view : mViews)
|
||||
view.second->swapBuffers(gc);
|
||||
|
||||
auto leftView = mViews["LeftEye"];
|
||||
auto rightView = mViews["RightEye"];
|
||||
|
||||
leftView->swapBuffers(gc);
|
||||
rightView->swapBuffers(gc);
|
||||
timer.checkpoint("Views");
|
||||
|
||||
auto& drawPoses = session->predictedPoses(OpenXRSession::PredictionSlice::Draw);
|
||||
|
||||
mCompositionLayerProjectionViews[0].pose = toXR(drawPoses.eye[(int)TrackedSpace::STAGE][(int)Side::LEFT_HAND]);
|
||||
mCompositionLayerProjectionViews[1].pose = toXR(drawPoses.eye[(int)TrackedSpace::STAGE][(int)Side::RIGHT_HAND]);
|
||||
|
||||
timer.checkpoint("Poses");
|
||||
|
||||
// TODO: Keep track of these in the session too.
|
||||
auto stageViews = xr->impl().getPredictedViews(xr->impl().frameState().predictedDisplayTime, TrackedSpace::STAGE);
|
||||
mCompositionLayerProjectionViews[0].fov = stageViews[0].fov;
|
||||
mCompositionLayerProjectionViews[1].fov = stageViews[1].fov;
|
||||
timer.checkpoint("Fovs");
|
||||
|
||||
|
||||
if (!mLayer)
|
||||
{
|
||||
mLayer.reset(new XrCompositionLayerProjection);
|
||||
mLayer->type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
|
||||
mLayer->space = xr->impl().mReferenceSpaceStage;
|
||||
mLayer->viewCount = 2;
|
||||
mLayer->views = mCompositionLayerProjectionViews.data();
|
||||
}
|
||||
|
||||
blitEyesToMirrorTexture(gc);
|
||||
|
||||
gc->swapBuffersImplementation();
|
||||
}
|
||||
|
||||
void
|
||||
OpenXRViewer::RealizeOperation::operator()(
|
||||
VRViewer::RealizeOperation::operator()(
|
||||
osg::GraphicsContext* gc)
|
||||
{
|
||||
OpenXRManager::RealizeOperation::operator()(gc);
|
||||
|
@ -251,24 +208,24 @@ namespace MWVR
|
|||
}
|
||||
|
||||
bool
|
||||
OpenXRViewer::RealizeOperation::realized()
|
||||
VRViewer::RealizeOperation::realized()
|
||||
{
|
||||
return Environment::get().getViewer()->realized();
|
||||
}
|
||||
|
||||
void OpenXRViewer::preDrawCallback(osg::RenderInfo& info)
|
||||
void VRViewer::preDrawCallback(osg::RenderInfo& info)
|
||||
{
|
||||
auto* camera = info.getCurrentCamera();
|
||||
auto name = camera->getName();
|
||||
auto& view = mViews[name];
|
||||
|
||||
if (name == "LeftEye")
|
||||
Environment::get().getSession()->advanceFrame();
|
||||
Environment::get().getSession()->advanceFramePhase();
|
||||
|
||||
view->prerenderCallback(info);
|
||||
}
|
||||
|
||||
void OpenXRViewer::postDrawCallback(osg::RenderInfo& info)
|
||||
void VRViewer::postDrawCallback(osg::RenderInfo& info)
|
||||
{
|
||||
auto* camera = info.getCurrentCamera();
|
||||
auto name = camera->getName();
|
||||
|
@ -282,5 +239,8 @@ namespace MWVR
|
|||
camera->setPreDrawCallback(mPreDraw);
|
||||
Log(Debug::Warning) << ("osg overwrote predraw");
|
||||
}
|
||||
|
||||
//if(mDelay && name == "RightEye")
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(25));
|
||||
}
|
||||
}
|
|
@ -9,17 +9,12 @@
|
|||
#include <osgViewer/Viewer>
|
||||
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrsession.hpp"
|
||||
#include "openxrlayer.hpp"
|
||||
#include "openxrworldview.hpp"
|
||||
#include "vrgui.hpp"
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
|
||||
struct XrCompositionLayerProjection;
|
||||
struct XrCompositionLayerProjectionView;
|
||||
namespace MWVR
|
||||
{
|
||||
class OpenXRViewer : public osg::Group, public OpenXRLayer
|
||||
class VRViewer
|
||||
{
|
||||
public:
|
||||
class RealizeOperation : public OpenXRManager::RealizeOperation
|
||||
|
@ -35,17 +30,17 @@ namespace MWVR
|
|||
class SwapBuffersCallback : public osg::GraphicsContext::SwapCallback
|
||||
{
|
||||
public:
|
||||
SwapBuffersCallback(OpenXRViewer* viewer) : mViewer(viewer) {};
|
||||
SwapBuffersCallback(VRViewer* viewer) : mViewer(viewer) {};
|
||||
void swapBuffersImplementation(osg::GraphicsContext* gc) override;
|
||||
|
||||
private:
|
||||
OpenXRViewer* mViewer;
|
||||
VRViewer* mViewer;
|
||||
};
|
||||
|
||||
class PredrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
PredrawCallback(OpenXRViewer* viewer)
|
||||
PredrawCallback(VRViewer* viewer)
|
||||
: mViewer(viewer)
|
||||
{}
|
||||
|
||||
|
@ -53,13 +48,13 @@ namespace MWVR
|
|||
|
||||
private:
|
||||
|
||||
OpenXRViewer* mViewer;
|
||||
VRViewer* mViewer;
|
||||
};
|
||||
|
||||
class PostdrawCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
PostdrawCallback(OpenXRViewer* viewer)
|
||||
PostdrawCallback(VRViewer* viewer)
|
||||
: mViewer(viewer)
|
||||
{}
|
||||
|
||||
|
@ -67,38 +62,34 @@ namespace MWVR
|
|||
|
||||
private:
|
||||
|
||||
OpenXRViewer* mViewer;
|
||||
VRViewer* mViewer;
|
||||
};
|
||||
|
||||
public:
|
||||
OpenXRViewer(
|
||||
VRViewer(
|
||||
osg::ref_ptr<osgViewer::Viewer> viewer);
|
||||
|
||||
~OpenXRViewer(void);
|
||||
~VRViewer(void);
|
||||
|
||||
virtual void traverse(osg::NodeVisitor& visitor) override;
|
||||
//virtual void traverse(osg::NodeVisitor& visitor) override;
|
||||
|
||||
const XrCompositionLayerBaseHeader* layer() override;
|
||||
const XrCompositionLayerBaseHeader* layer();
|
||||
|
||||
void traversals();
|
||||
void preDrawCallback(osg::RenderInfo& info);
|
||||
void postDrawCallback(osg::RenderInfo& info);
|
||||
void blitEyesToMirrorTexture(osg::GraphicsContext* gc);
|
||||
void swapBuffers(osg::GraphicsContext* gc) override;
|
||||
void swapBuffers(osg::GraphicsContext* gc);
|
||||
void realize(osg::GraphicsContext* gc);
|
||||
bool realized() { return mConfigured; }
|
||||
void updateTransformNode(osg::Object* object, osg::Object* data);
|
||||
|
||||
void enableMainCamera(void);
|
||||
void disableMainCamera(void);
|
||||
|
||||
public:
|
||||
|
||||
std::unique_ptr<XrCompositionLayerProjection> mLayer = nullptr;
|
||||
std::vector<XrCompositionLayerProjectionView> mCompositionLayerProjectionViews;
|
||||
osg::ref_ptr<OpenXRManager::RealizeOperation> mRealizeOperation = nullptr;
|
||||
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
|
||||
std::map<std::string, osg::ref_ptr<OpenXRView> > mViews{};
|
||||
std::map<std::string, osg::ref_ptr<VRView> > mViews{};
|
||||
std::map<std::string, osg::ref_ptr<osg::Camera> > mCameras{};
|
||||
osg::ref_ptr<PredrawCallback> mPreDraw{ nullptr };
|
||||
osg::ref_ptr<PostdrawCallback> mPostDraw{ nullptr };
|
|
@ -1,7 +1,7 @@
|
|||
#include "engine.hpp"
|
||||
#include "mwvr/openxrmanager.hpp"
|
||||
#include "mwvr/openxrsession.hpp"
|
||||
#include "mwvr/openxrviewer.hpp"
|
||||
#include "mwvr/vrsession.hpp"
|
||||
#include "mwvr/vrviewer.hpp"
|
||||
#include "mwvr/vrgui.hpp"
|
||||
|
||||
#ifndef USE_OPENXR
|
||||
|
@ -20,6 +20,6 @@ void OMW::Engine::initVr()
|
|||
float yardsPerMeter = 0.9144f;
|
||||
float unitsPerMeter = unitsPerYard / yardsPerMeter;
|
||||
mXrEnvironment.setUnitsPerMeter(unitsPerMeter);
|
||||
mXrEnvironment.setSession(new MWVR::OpenXRSession());
|
||||
mXrEnvironment.setViewer(new MWVR::OpenXRViewer(mViewer));
|
||||
mXrEnvironment.setSession(new MWVR::VRSession());
|
||||
mXrEnvironment.setViewer(new MWVR::VRViewer(mViewer));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue