Misc new stereo integration fixes.

pull/615/head
Mads Buvik Sandvei 4 years ago
parent 5a25691435
commit 1b193deeac

@ -56,6 +56,7 @@
#include "mwworld/worldimp.hpp"
#include "mwrender/vismask.hpp"
#include "mwrender/camera.hpp"
#include "mwclass/classes.hpp"
@ -71,6 +72,7 @@
#include "mwvr/vrinputmanager.hpp"
#include "mwvr/vrviewer.hpp"
#include "mwvr/vrgui.hpp"
#include "mwvr/vrcamera.hpp"
#endif
namespace
@ -673,6 +675,20 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
Settings::Manager::getInt("anisotropy", "General")
);
// geometry shader must be enabled before the RenderingManager sets up any shaders
// therefore this part is separate from the rest of stereo setup.
mStereoEnabled = mEnvironment.getVrMode() || Settings::Manager::getBool("stereo enabled", "Stereo");
if (mStereoEnabled)
{
mResourceSystem->getSceneManager()->getShaderManager().setStereoGeometryShaderEnabled(Misc::getStereoTechnique() == Misc::StereoView::Technique::GeometryShader_IndexedViewports);
// Mask in everything that does not currently use shaders.
// Remove that altogether when the sky finally uses them.
auto noShaderMask = MWRender::VisMask::Mask_Sky | MWRender::VisMask::Mask_Sun | MWRender::VisMask::Mask_WeatherParticles;
// Since shaders are not yet created, we need to use the brute force technique initially
mStereoView.reset(new Misc::StereoView(mViewer, Misc::StereoView::Technique::BruteForce, noShaderMask, MWRender::VisMask::Mask_Scene));
}
int numThreads = Settings::Manager::getInt("preload num threads", "Cells");
if (numThreads <= 0)
throw std::runtime_error("Invalid setting: 'preload num threads' must be >0");
@ -755,35 +771,28 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
// VR mode will override this setting by setting mStereoOverride.
mStereoEnabled = mEnvironment.getVrMode() || Settings::Manager::getBool("stereo enabled", "Stereo");
// geometry shader must be enabled before the RenderingManager sets up any shaders
// therefore this part is separate from the rest of stereo setup.
if (mStereoEnabled)
{
mResourceSystem->getSceneManager()->getShaderManager().setStereoGeometryShaderEnabled(Misc::getStereoTechnique() == Misc::StereoView::Technique::GeometryShader_IndexedViewports);
// Mask in everything that does not currently use shaders.
// Remove that altogether when the sky finally uses them.
auto noShaderMask = MWRender::VisMask::Mask_Sky | MWRender::VisMask::Mask_Sun | MWRender::VisMask::Mask_WeatherParticles;
auto geometryShaderMask = mViewer->getCamera()->getCullMask() & ~noShaderMask;
mStereoView.reset(new Misc::StereoView(mViewer, Misc::getStereoTechnique(), geometryShaderMask, noShaderMask | MWRender::VisMask::Mask_Scene));
}
#ifdef USE_OPENXR
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer, mResourceSystem.get()));
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer, mResourceSystem.get(), rootNode));
mXrEnvironment.getViewer()->configureCallbacks();
#endif
std::unique_ptr<MWRender::Camera> camera(
#ifdef USE_OPENXR
new MWVR::VRCamera(mViewer->getCamera())
#else
new Camera(mViewer->getCamera())
#endif
);
if (!mSkipMenu)
{
const std::string& logo = Fallback::Map::getString("Movies_Company_Logo");
if (!logo.empty())
window->playVideo(logo, true);
mEnvironment.getWindowManager()->playVideo(logo, true);
}
// Create the world
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, std::move(camera), mResourceSystem.get(), mWorkQueue.get(),
mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName,
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
mEnvironment.getWorld()->setupPlayer();
@ -791,7 +800,9 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Set up stereo
if (mStereoEnabled)
{
mStereoView->setStereoTechnique(Misc::getStereoTechnique());
mStereoView->initializeScene();
mStereoView->setCullMask(mStereoView->getCullMask());
}
window->setStore(mEnvironment.getWorld()->getStore());

@ -592,7 +592,14 @@ namespace MWGui
void WindowManager::enableScene(bool enable)
{
unsigned int disablemask = MWRender::Mask_GUI|MWRender::Mask_PreCompile;
// In VR mode we have a scene to render.
// TODO: We should nonetheless disable the scene outside of the 3D GUI
if (MWBase::Environment::get().getVrMode())
return;
if (!enable && mViewer->getCamera()->getCullMask() != disablemask)
{
mOldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();

@ -200,7 +200,7 @@ namespace MWRender
Resource::ResourceSystem* mResourceSystem;
};
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, std::unique_ptr<Camera> camera,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::string& resourcePath, DetourNavigator::Navigator& navigator)
: mViewer(viewer)
@ -316,11 +316,8 @@ namespace MWRender
// water goes after terrain for correct waterculling order
mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
#ifdef USE_OPENXR
mCamera.reset(new MWVR::VRCamera(mViewer->getCamera()));
#else
mCamera.reset(new Camera(mViewer->getCamera()));
#endif
mCamera = std::move(camera);
if (Settings::Manager::getBool("view over shoulder", "Camera"))
mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get()));

@ -106,7 +106,7 @@ namespace MWRender
class RenderingManager : public MWRender::RenderingInterface
{
public:
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, std::unique_ptr<Camera> camera,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::string& resourcePath, DetourNavigator::Navigator& navigator);
~RenderingManager();

@ -35,6 +35,9 @@ namespace MWVR
void VRCamera::recenter()
{
if (!mHasTrackingData)
return;
// Move position of head to center of character
// Z should not be affected
mHeadOffset = osg::Vec3(0, 0, 0);
@ -87,6 +90,7 @@ namespace MWVR
osg::Vec3 vrMovement = currentHeadPose.position - mHeadPose.position;
mHeadPose = currentHeadPose;
mHeadOffset += stageRotation() * vrMovement;
mHasTrackingData = true;
}
}

@ -73,6 +73,7 @@ namespace MWVR
Pose mHeadPose{};
osg::Vec3 mHeadOffset{ 0,0,0 };
bool mShouldRecenter{ true };
bool mHasTrackingData{ false };
float mYawOffset{ 0.f };
};
}

@ -497,16 +497,17 @@ namespace MWVR
VRGUIManager::VRGUIManager(
osg::ref_ptr<osgViewer::Viewer> viewer,
Resource::ResourceSystem* resourceSystem)
Resource::ResourceSystem* resourceSystem,
osg::Group* rootNode)
: mOsgViewer(viewer)
, mResourceSystem(resourceSystem)
, mRootNode(rootNode)
{
mGUIGeometriesRoot->setName("VR GUI Geometry Root");
mGUIGeometriesRoot->setUpdateCallback(new VRGUIManagerUpdateCallback(this));
mGUICamerasRoot->setName("VR GUI Cameras Root");
auto* root = viewer->getSceneData();
root->asGroup()->addChild(mGUICamerasRoot);
root->asGroup()->addChild(mGUIGeometriesRoot);
mRootNode->asGroup()->addChild(mGUICamerasRoot);
mRootNode->asGroup()->addChild(mGUIGeometriesRoot);
mGUIGeometriesRoot->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
LayerConfig defaultConfig = createDefaultConfig(1);

@ -137,7 +137,8 @@ namespace MWVR
public:
VRGUIManager(
osg::ref_ptr<osgViewer::Viewer> viewer,
Resource::ResourceSystem* resourceSystem);
Resource::ResourceSystem* resourceSystem,
osg::Group* rootNode);
~VRGUIManager(void);
@ -190,6 +191,7 @@ namespace MWVR
osg::ref_ptr<osgViewer::Viewer> mOsgViewer{ nullptr };
Resource::ResourceSystem* mResourceSystem;
osg::ref_ptr<osg::Group> mRootNode{ nullptr };
osg::ref_ptr<osg::Group> mGUIGeometriesRoot{ new osg::Group };
osg::ref_ptr<osg::Group> mGUICamerasRoot{ new osg::Group };

@ -21,6 +21,7 @@
#include <iostream>
#include <time.h>
#include <thread>
#include <chrono>
#ifdef max
#undef max
@ -192,24 +193,17 @@ namespace MWVR
void VRSession::beginPhase(FramePhase phase)
{
Log(Debug::Debug) << "beginPhase(" << ((int)phase) << ") " << std::this_thread::get_id();
{
std::unique_lock<std::mutex> lock(mMutex);
while (getFrame(phase))
{
Log(Debug::Verbose) << "Warning: beginPhase called with a frame already in the target phase";
mCondition.wait(lock);
}
}
mCondition.notify_all();
auto& frame = getFrame(phase);
//if (frame)
//{
// // Happens once during startup but can be ignored that time.
// // TODO: This issue would be cleaned up if beginPhase(Update) was called at a more appropriate location.
// Log(Debug::Warning) << "advanceFramePhase called with a frame alreay in the target phase";
// return;
//}
if (phase == FramePhase::Update)
{
@ -224,6 +218,8 @@ namespace MWVR
frame = std::move(getFrame(previousPhase));
}
mCondition.notify_all();
if (phase == mXrSyncPhase && frame->mShouldSyncFrameLoop)
{
// We may reach this point before xrEndFrame of the previous frame
@ -237,7 +233,6 @@ namespace MWVR
Environment::get().getManager()->beginFrame();
}
mCondition.notify_all();
}
std::unique_ptr<VRSession::VRFrameMeta>& VRSession::getFrame(FramePhase phase)
@ -348,7 +343,7 @@ namespace MWVR
void VRSession::movementAngles(float& yaw, float& pitch)
{
if (!getFrame(FramePhase::Update))
beginPhase(FramePhase::Update);
return;
if (mHandDirectedMovement)
{

@ -4,103 +4,103 @@
namespace MWVR
{
Pose Pose::operator+(const Pose& rhs)
{
Pose pose = *this;
pose.position += this->orientation * rhs.position;
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;
return pose;
}
const Pose& Pose::operator*=(float scalar)
{
*this = *this * scalar;
return *this;
}
Pose Pose::operator/(float scalar)
{
Pose pose = *this;
pose.position /= scalar;
return pose;
}
const Pose& Pose::operator/=(float scalar)
{
*this = *this / scalar;
return *this;
}
bool Pose::operator==(const Pose& rhs) const
{
return position == rhs.position && orientation == rhs.orientation;
}
bool FieldOfView::operator==(const FieldOfView& rhs) const
{
return angleDown == rhs.angleDown
&& angleUp == rhs.angleUp
&& angleLeft == rhs.angleLeft
&& angleRight == rhs.angleRight;
}
// near and far named with an underscore because of windows' headers galaxy brain defines.
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);
}
//Pose Pose::operator+(const Pose& rhs)
//{
// Pose pose = *this;
// pose.position += this->orientation * rhs.position;
// 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;
// return pose;
//}
//const Pose& Pose::operator*=(float scalar)
//{
// *this = *this * scalar;
// return *this;
//}
//Pose Pose::operator/(float scalar)
//{
// Pose pose = *this;
// pose.position /= scalar;
// return pose;
//}
//const Pose& Pose::operator/=(float scalar)
//{
// *this = *this / scalar;
// return *this;
//}
//bool Pose::operator==(const Pose& rhs) const
//{
// return position == rhs.position && orientation == rhs.orientation;
//}
//bool FieldOfView::operator==(const FieldOfView& rhs) const
//{
// return angleDown == rhs.angleDown
// && angleUp == rhs.angleUp
// && angleLeft == rhs.angleLeft
// && angleRight == rhs.angleRight;
//}
//// near and far named with an underscore because of windows' headers galaxy brain defines.
//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);
//}
bool PoseSet::operator==(const PoseSet& rhs) const
{
return eye[0] == rhs.eye[0]
@ -112,10 +112,10 @@ namespace MWVR
&& head == rhs.head;
}
bool View::operator==(const View& rhs) const
{
return pose == rhs.pose && fov == rhs.fov;
}
//bool View::operator==(const View& rhs) const
//{
// return pose == rhs.pose && fov == rhs.fov;
//}
std::ostream& operator <<(
std::ostream& os,

@ -7,6 +7,7 @@
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
#include <components/settings/settings.hpp>
#include <components/misc/stereo.hpp>
#include <osg/Camera>
#include <osgViewer/Viewer>
@ -37,47 +38,51 @@ namespace MWVR
RIGHT_SIDE = 1
};
//! Represents the relative pose in space of some limb or eye.
struct Pose
{
//! Position in space
osg::Vec3 position{ 0,0,0 };
//! Orientation in space.
osg::Quat orientation{ 0,0,0,1 };
//! 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);
Pose operator/(float scalar);
const Pose& operator/=(float scalar);
bool operator==(const Pose& rhs) const;
};
//! Fov of a single eye
struct FieldOfView {
float angleLeft;
float angleRight;
float angleUp;
float angleDown;
bool operator==(const FieldOfView& rhs) const;
//! Generate a perspective matrix from this fov
osg::Matrix perspectiveMatrix(float near, float far);
};
//! Represents an eye in VR including both pose and fov. A view's pose is relative to the head.
struct View
{
Pose pose;
FieldOfView fov;
bool operator==(const View& rhs) const;
};
////! Represents the relative pose in space of some limb or eye.
//struct Pose
//{
// //! Position in space
// osg::Vec3 position{ 0,0,0 };
// //! Orientation in space.
// osg::Quat orientation{ 0,0,0,1 };
// //! 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);
// Pose operator/(float scalar);
// const Pose& operator/=(float scalar);
// bool operator==(const Pose& rhs) const;
//};
////! Fov of a single eye
//struct FieldOfView {
// float angleLeft;
// float angleRight;
// float angleUp;
// float angleDown;
// bool operator==(const FieldOfView& rhs) const;
// //! Generate a perspective matrix from this fov
// osg::Matrix perspectiveMatrix(float near, float far);
//};
////! Represents an eye in VR including both pose and fov. A view's pose is relative to the head.
//struct View
//{
// Pose pose;
// FieldOfView fov;
// bool operator==(const View& rhs) const;
//};
using Pose = Misc::Pose;
using FieldOfView = Misc::FieldOfView;
using View = Misc::View;
//! The complete set of poses tracked each frame by MWVR.
struct PoseSet

@ -189,6 +189,11 @@ namespace MWVR
Misc::StereoView::instance().setPredrawCallback(mPreDraw);
Misc::StereoView::instance().setPostdrawCallback(mPostDraw);
Misc::StereoView::instance().setCullCallback(new CullCallback);
//auto cullMask = Misc::StereoView::instance().getCullMask();
auto cullMask = ~(MWRender::VisMask::Mask_UpdateVisitor | MWRender::VisMask::Mask_SimpleWater);
cullMask &= ~MWRender::VisMask::Mask_GUI;
cullMask |= MWRender::VisMask::Mask_3DGUI;
Misc::StereoView::instance().setCullMask(cullMask);
mCallbacksConfigured = true;
}
@ -347,7 +352,73 @@ namespace MWVR
Log(Debug::Warning) << ("osg overwrote predraw");
}
}
VRViewer::InitialDrawCallback::~InitialDrawCallback()
{
}
void VRViewer::updateView(Misc::View& left, Misc::View& right)
{
auto phase = VRSession::FramePhase::Update;
auto session = Environment::get().getSession();
auto& frame = session->getFrame(phase);
if (frame->mShouldRender)
{
//left.fov.angleLeft = frame->mPredictedPoses.view->fov.angleLeft;
//left.fov.angleLeft = frame->mPredictedPoses.view->fov.angleDown;
//left.fov.angleLeft = frame->mPredictedPoses.view->fov.angleRight;
//left.fov.angleLeft = frame->mPredictedPoses.view->fov.angleUp;
//frame->mPredictedPoses.eye;
left = frame->mPredictedPoses.view[static_cast<int>(Side::LEFT_SIDE)];
right = frame->mPredictedPoses.view[static_cast<int>(Side::RIGHT_SIDE)];
}
// auto* camera = slave._camera.get();
//
// // Update current cached cull mask of camera if it is active
// auto mask = camera->getCullMask();
// if (mask == 0)
// camera->setCullMask(mCullMask);
// else
// mCullMask = mask;
//
// // If the session is not active, we do not want to waste resources rendering frames.
// if (Environment::get().getSession()->getFrame(VRSession::FramePhase::Update)->mShouldRender)
// {
// Side side = Side::RIGHT_SIDE;
// if (mName == "LeftEye")
// {
//
// Environment::get().getViewer()->vrShadow().updateShadowConfig(view);
// side = Side::LEFT_SIDE;
// }
//
// auto* session = Environment::get().getSession();
// auto viewMatrix = view.getCamera()->getViewMatrix();
//
// // If the camera does not have a view, use the VR stage directly
// bool useStage = !(viewMatrix.getTrans().length() > 0.01);
//
// // If the view matrix is still the identity matrix, conventions have to be swapped around.
// bool swapConventions = viewMatrix.isIdentity();
//
// viewMatrix = viewMatrix * session->viewMatrix(VRSession::FramePhase::Update, side, !useStage, !swapConventions);
//
// camera->setViewMatrix(viewMatrix);
//
// auto projectionMatrix = session->projectionMatrix(VRSession::FramePhase::Update, side);
// camera->setProjectionMatrix(projectionMatrix);
// }
// else
// {
// camera->setCullMask(0);
// }
// slave.updateSlaveImplementation(view);
}
void VRViewer::UpdateViewCallback::updateView(Misc::View& left, Misc::View& right)
{
mViewer->updateView(left, right);
}
}

@ -13,6 +13,7 @@
#include "vrshadow.hpp"
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/misc/stereo.hpp>
namespace MWVR
{
@ -27,6 +28,16 @@ namespace MWVR
class VRViewer
{
public:
struct UpdateViewCallback : public Misc::StereoView::UpdateViewCallback
{
UpdateViewCallback(VRViewer* viewer) : mViewer(viewer) {};
//! Called during the update traversal of every frame to source updated stereo values.
virtual void updateView(Misc::View& left, Misc::View& right) override;
VRViewer* mViewer;
};
class SwapBuffersCallback : public osg::GraphicsContext::SwapCallback
{
public:
@ -104,6 +115,7 @@ namespace MWVR
void configureCallbacks();
void setupMirrorTexture();
void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed);
void updateView(Misc::View& left, Misc::View& right);
SubImage subImage(Side side);

@ -144,6 +144,7 @@ namespace MWWorld
World::World (
osgViewer::Viewer* viewer,
osg::ref_ptr<osg::Group> rootNode,
std::unique_ptr<MWRender::Camera> camera,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const Files::Collections& fileCollections,
const std::vector<std::string>& contentFiles,
@ -204,7 +205,7 @@ namespace MWWorld
mNavigator.reset(new DetourNavigator::NavigatorStub());
}
mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, resourcePath, *mNavigator));
mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, std::move(camera), resourceSystem, workQueue, resourcePath, *mNavigator));
mProjectileManager.reset(new ProjectileManager(mRendering->getLightRoot(), resourceSystem, mRendering.get(), mPhysics.get()));
mRendering->preloadCommonAssets();

@ -195,6 +195,7 @@ namespace MWWorld
World (
osgViewer::Viewer* viewer,
osg::ref_ptr<osg::Group> rootNode,
std::unique_ptr<MWRender::Camera> camera,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const Files::Collections& fileCollections,
const std::vector<std::string>& contentFiles,

@ -234,14 +234,34 @@ namespace Misc
return *sInstance;
}
StereoView::StereoView(osgViewer::Viewer* viewer, Technique technique, osg::Node::NodeMask geometryShaderMask, osg::Node::NodeMask noShaderMask)
static osg::Camera*
createCamera(std::string name, GLbitfield clearMask)
{
auto* camera = new osg::Camera;
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
camera->setProjectionResizePolicy(osg::Camera::FIXED);
camera->setProjectionMatrix(osg::Matrix::identity());
camera->setViewMatrix(osg::Matrix::identity());
camera->setName(name);
camera->setDataVariance(osg::Object::STATIC);
camera->setRenderOrder(osg::Camera::NESTED_RENDER);
camera->setClearMask(clearMask);
camera->setUpdateCallback(new SceneUtil::StateSetUpdater());
return camera;
}
StereoView::StereoView(osgViewer::Viewer* viewer, Technique technique, osg::Node::NodeMask noShaderMask, osg::Node::NodeMask sceneMask)
: mViewer(viewer)
, mMainCamera(mViewer->getCamera())
, mRoot(mViewer->getSceneData()->asGroup())
, mStereoRoot(new osg::Group)
, mTechnique(technique)
, mGeometryShaderMask(geometryShaderMask)
, mUpdateCallback(new StereoUpdateCallback(this))
, mTechnique(Technique::None)
, mNoShaderMask(noShaderMask)
, mSceneMask(sceneMask)
, mCullMask(mMainCamera->getCullMask())
, mMasterConfig(new SharedShadowMapConfig)
, mSlaveConfig(new SharedShadowMapConfig)
, mSharedShadowMaps(Settings::Manager::getBool("shared shadow maps", "Stereo"))
@ -257,38 +277,13 @@ namespace Misc
mSlaveConfig->_id = "STEREO";
mSlaveConfig->_master = false;
mLeftCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
mLeftCamera->setProjectionResizePolicy(osg::Camera::FIXED);
mLeftCamera->setProjectionMatrix(osg::Matrix::identity());
mLeftCamera->setViewMatrix(osg::Matrix::identity());
mLeftCamera->setName("Stereo Left");
mLeftCamera->setDataVariance(osg::Object::STATIC);
mRightCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
mRightCamera->setProjectionResizePolicy(osg::Camera::FIXED);
mRightCamera->setProjectionMatrix(osg::Matrix::identity());
mRightCamera->setViewMatrix(osg::Matrix::identity());
mRightCamera->setName("Stereo Right");
mRightCamera->setDataVariance(osg::Object::STATIC);
// Update stereo statesets/matrices, but after the main camera updates.
auto mainCameraCB = mMainCamera->getUpdateCallback();
mMainCamera->removeUpdateCallback(mainCameraCB);
mMainCamera->addUpdateCallback(new StereoUpdateCallback(this));
mMainCamera->addUpdateCallback(mainCameraCB);
// Do a blank double buffering of camera statesets on update. Actual state updates are performed in StereoView::Update()
mLeftCamera->setUpdateCallback(new SceneUtil::StateSetUpdater());
mRightCamera->setUpdateCallback(new SceneUtil::StateSetUpdater());
mStereoRoot->setName("Stereo Root");
mStereoRoot->setDataVariance(osg::Object::STATIC);
mStereoRoot->addChild(mStereoGeometryShaderRoot);
mStereoRoot->addChild(mStereoBruteForceRoot);
mStereoRoot->addCullCallback(new StereoStatesetUpdateCallback(this));
if (mTechnique == Technique::GeometryShader_IndexedViewports)
{
setupGeometryShaderIndexedViewportTechnique();
}
else
{
setupBruteForceTechnique();
}
setStereoTechnique(technique);
if (sInstance)
throw std::logic_error("Double instance og StereoView");
@ -297,14 +292,8 @@ namespace Misc
void StereoView::setupBruteForceTechnique()
{
mLeftCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
mLeftCamera->setClearColor(mMainCamera->getClearColor());
mLeftCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
mLeftCamera->setCullMask(mMainCamera->getCullMask());
mRightCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
mRightCamera->setClearColor(mMainCamera->getClearColor());
mRightCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
mRightCamera->setCullMask(mMainCamera->getCullMask());
mLeftCamera = createCamera("Stereo Left", GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
mRightCamera = createCamera("Stereo Right", GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (mSharedShadowMaps)
{
@ -318,39 +307,137 @@ namespace Misc
mLeftCamera->setViewport(0, 0, width / 2, height);
mRightCamera->setViewport(width / 2, 0, width / 2, height);
// Threading should be stopped before adding new slave cameras
mViewer->stopThreading();
mViewer->addSlave(mRightCamera, true);
mViewer->addSlave(mLeftCamera, true);
mRightCamera->setGraphicsContext(mViewer->getCamera()->getGraphicsContext());
mLeftCamera->setGraphicsContext(mViewer->getCamera()->getGraphicsContext());
// Remove main camera's graphics context to ensure it does not do any work
mViewer->getCamera()->setGraphicsContext(nullptr);
// Re-realize to ensure slave cameras are set up with appropriate settings
mViewer->realize();
}
void StereoView::setupGeometryShaderIndexedViewportTechnique()
{
mLeftCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
mLeftCamera->setClearMask(GL_NONE);
mLeftCamera->setCullMask(mNoShaderMask);
mRightCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
mRightCamera->setClearMask(GL_NONE);
mRightCamera->setCullMask(mNoShaderMask);
mMainCamera->setCullMask(mGeometryShaderMask);
mStereoRoot->setName("Stereo Root");
mStereoRoot->setDataVariance(osg::Object::STATIC);
mStereoRoot->addChild(mStereoGeometryShaderRoot);
mStereoRoot->addChild(mStereoBruteForceRoot);
mLeftCamera = createCamera("Stereo Left", GL_NONE);
mRightCamera = createCamera("Stereo Right", GL_NONE);
mStereoBruteForceRoot->addChild(mLeftCamera);
mStereoBruteForceRoot->addChild(mRightCamera);
mStereoRoot->addCullCallback(new StereoStatesetUpdateCallback(this));
// Inject self as the root of the scene graph
mStereoGeometryShaderRoot->addChild(mRoot);
mViewer->setSceneData(mStereoRoot);
}
static void removeSlave(osgViewer::Viewer* viewer, osg::Camera* camera)
{
for (unsigned int i = 0; i < viewer->getNumSlaves(); i++)
{
auto& slave = viewer->getSlave(i);
if (slave._camera == camera);
{
viewer->removeSlave(i);
return;
}
}
}
void StereoView::removeBruteForceTechnique()
{
mViewer->stopThreading();
removeSlave(mViewer, mRightCamera);
removeSlave(mViewer, mLeftCamera);
mLeftCamera->setUserData(nullptr);
mRightCamera->setUserData(nullptr);
mMainCamera->setGraphicsContext(mRightCamera->getGraphicsContext());
mLeftCamera = nullptr;
mRightCamera = nullptr;
mViewer->realize();
}
void StereoView::removeGeometryShaderIndexedViewportTechnique()
{
mStereoGeometryShaderRoot->removeChild(mRoot);
mViewer->setSceneData(mRoot);
mStereoBruteForceRoot->removeChild(mLeftCamera);
mStereoBruteForceRoot->removeChild(mRightCamera);
mLeftCamera = nullptr;
mRightCamera = nullptr;
}
void StereoView::disableStereo()
{
if (mTechnique == Technique::None)
return;
mMainCamera->removeUpdateCallback(mUpdateCallback);
switch (mTechnique)
{
case Technique::GeometryShader_IndexedViewports:
removeGeometryShaderIndexedViewportTechnique(); break;
case Technique::BruteForce:
removeBruteForceTechnique(); break;
default: break;
}
mMainCamera->setCullMask(mCullMask);
}
void StereoView::enableStereo()
{
if (mTechnique == Technique::None)
return;
// Update stereo statesets/matrices, but after the main camera updates.
auto mainCameraCB = mMainCamera->getUpdateCallback();
mMainCamera->removeUpdateCallback(mainCameraCB);
mMainCamera->addUpdateCallback(mUpdateCallback);
mMainCamera->addUpdateCallback(mainCameraCB);
switch (mTechnique)
{
case Technique::GeometryShader_IndexedViewports:
setupGeometryShaderIndexedViewportTechnique(); break;
case Technique::BruteForce:
setupBruteForceTechnique(); break;
default: break;
}
setCullMask(mCullMask);
}
void StereoView::setStereoTechnique(Technique technique)
{
if (technique == mTechnique)
return;
auto cullCB = mCullCallback;
auto initialDrawCB = mInitialDrawCallback;
auto predrawCB = mPreDrawCallback;
auto postDrawCB = mPostDrawCallback;
setCullCallback(nullptr);
setInitialDrawCallback(nullptr);
setPostdrawCallback(nullptr);
setPredrawCallback(nullptr);
disableStereo();
mTechnique = technique;
enableStereo();
setCullCallback(cullCB);
setInitialDrawCallback(initialDrawCB);
setPostdrawCallback(predrawCB);
setPredrawCallback(postDrawCB);
}
void StereoView::update()
{
auto viewMatrix = mViewer->getCamera()->getViewMatrix();
@ -358,13 +445,15 @@ namespace Misc
View left{};
View right{};
double near = 1.f;
double far = 10000.f;
if (!cb)
double near_ = 1.f;
double far_ = 10000.f;
if (!mUpdateViewCallback)
{
Log(Debug::Error) << "No update view callback. Stereo rendering will not work.";
}
cb->updateView(left, right, near, far);
mUpdateViewCallback->updateView(left, right);
near_ = Settings::Manager::getFloat("near clip", "Camera");
far_ = Settings::Manager::getFloat("viewing distance", "Camera");
osg::Vec3d leftEye = left.pose.position;
osg::Vec3d rightEye = right.pose.position;
@ -375,8 +464,8 @@ namespace Misc
osg::Matrix leftViewMatrix = viewMatrix * leftViewOffset;
osg::Matrix rightViewMatrix = viewMatrix * rightViewOffset;
osg::Matrix leftProjectionMatrix = left.fov.perspectiveMatrix(near, far);
osg::Matrix rightProjectionMatrix = right.fov.perspectiveMatrix(near, far);
osg::Matrix leftProjectionMatrix = left.fov.perspectiveMatrix(near_, far_);
osg::Matrix rightProjectionMatrix = right.fov.perspectiveMatrix(near_, far_);
mRightCamera->setViewMatrix(rightViewMatrix);
mLeftCamera->setViewMatrix(leftViewMatrix);
@ -442,7 +531,7 @@ namespace Misc
// Generate the frustum matrices
auto frustumViewMatrix = viewMatrix * frustumView.pose.viewMatrix(true);
auto frustumProjectionMatrix = frustumView.fov.perspectiveMatrix(near + nearFarOffset, far + nearFarOffset);
auto frustumProjectionMatrix = frustumView.fov.perspectiveMatrix(near_ + nearFarOffset, far_ + nearFarOffset);
if (mTechnique == Technique::GeometryShader_IndexedViewports)
{
@ -475,7 +564,7 @@ namespace Misc
}
}
void StereoView::updateStateset(osg::StateSet * stateset)
void StereoView::updateStateset(osg::StateSet* stateset)
{
// Manage viewports in update to automatically catch window/resolution changes.
auto width = mMainCamera->getViewport()->width();
@ -495,9 +584,9 @@ namespace Misc
stereoViewProjectionsUniform->setElement(1, frustumViewMatrixInverse * mRightCamera->getViewMatrix() * mRightCamera->getProjectionMatrix());
}
void StereoView::setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb_)
void StereoView::setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb)
{
cb = cb_;
mUpdateViewCallback = cb;
}
void StereoView::initializeScene()
@ -565,63 +654,85 @@ namespace Misc
return StereoView::Technique::BruteForce;
}
void StereoView::DefaultUpdateViewCallback::updateView(View& left, View& right, double& near, double& far)
void StereoView::DefaultUpdateViewCallback::updateView(View& left, View& right)
{
left.pose.position = osg::Vec3(-2.2, 0, 0);
right.pose.position = osg::Vec3(2.2, 0, 0);
left.fov = { -0.767549932, 0.620896876, -0.837898076, 0.726982594 };
right.fov = { -0.620896876, 0.767549932, -0.837898076, 0.726982594 };
near = 1;
far = 6656;
}
void StereoView::setInitialDrawCallback(osg::ref_ptr<osg::Camera::DrawCallback> cb)
{
if (mTechnique == Technique::GeometryShader_IndexedViewports)
{
mMainCamera->setInitialDrawCallback(cb);
}
else
mInitialDrawCallback = cb;
switch (mTechnique)
{
mRightCamera->setInitialDrawCallback(cb);
case Technique::GeometryShader_IndexedViewports:
mMainCamera->setInitialDrawCallback(cb); break;
case Technique::BruteForce:
mRightCamera->setInitialDrawCallback(cb); break;
default: break;
}
}
void StereoView::setPredrawCallback(osg::ref_ptr<osg::Camera::DrawCallback> cb)
{
if (mTechnique == Technique::GeometryShader_IndexedViewports)
{
mMainCamera->setPreDrawCallback(cb);
}
else
mPreDrawCallback = cb;
switch (mTechnique)
{
case Technique::GeometryShader_IndexedViewports:
mMainCamera->setPreDrawCallback(cb); break;
case Technique::BruteForce:
mLeftCamera->setPreDrawCallback(cb);
mRightCamera->setPreDrawCallback(cb);
mRightCamera->setPreDrawCallback(cb); break;
default: break;
}
}
void StereoView::setPostdrawCallback(osg::ref_ptr<osg::Camera::DrawCallback> cb)
{
if (mTechnique == Technique::GeometryShader_IndexedViewports)
{
mMainCamera->setPostDrawCallback(cb);
}
else
mPostDrawCallback = cb;
switch (mTechnique)
{
case Technique::GeometryShader_IndexedViewports:
mMainCamera->setPostDrawCallback(cb); break;
case Technique::BruteForce:
mLeftCamera->setPostDrawCallback(cb);
mRightCamera->setPostDrawCallback(cb);
mRightCamera->setPostDrawCallback(cb); break;
default: break;
}
}
void StereoView::setCullCallback(osg::ref_ptr<osg::NodeCallback> cb)
{
mCullCallback = cb;
switch (mTechnique)
{
case Technique::GeometryShader_IndexedViewports:
mMainCamera->setCullCallback(cb); break;
case Technique::BruteForce:
mRightCamera->setCullCallback(cb); break;
default: break;
}
}
void StereoView::setCullMask(osg::Node::NodeMask cullMask)
{
mCullMask = cullMask;
if (mTechnique == Technique::GeometryShader_IndexedViewports)
{
mMainCamera->setCullCallback(cb);
mMainCamera->setCullMask(cullMask & ~mNoShaderMask);
mLeftCamera->setCullMask((cullMask & mNoShaderMask) | mSceneMask);
mRightCamera->setCullMask((cullMask & mNoShaderMask) | mSceneMask);
}
else
{
mRightCamera->setCullCallback(cb);
mLeftCamera->setCullMask(cullMask);
mRightCamera->setCullMask(cullMask);
}
}
osg::Node::NodeMask StereoView::getCullMask()
{
return mCullMask;
}
}

@ -73,13 +73,13 @@ namespace Misc
struct UpdateViewCallback
{
//! Called during the update traversal of every frame to source updated stereo values.
virtual void updateView(View& left, View& right, double& near, double& far) = 0;
virtual void updateView(View& left, View& right) = 0;
};
//! Default implementation of UpdateViewCallback that just provides some hardcoded values for debugging purposes
struct DefaultUpdateViewCallback : public UpdateViewCallback
{
virtual void updateView(View& left, View& right, double& near, double& far);
virtual void updateView(View& left, View& right);
};
enum class Technique
@ -93,10 +93,10 @@ namespace Misc
//! Adds two cameras in stereo to the mainCamera.
//! All nodes matching the mask are rendered in stereo using brute force via two camera transforms, the rest are rendered in stereo via a geometry shader.
//! \param geometryShaderMask should mask in all nodes that use shaders.
//! \param noShaderMask mask in all nodes that do not use shaders and must be rendered brute force.
//! \param sceneMask must equal MWRender::VisMask::Mask_Scene. Necessary while VisMask is still not in components/
//! \note the masks apply only to the GeometryShader_IndexdViewports technique and can be 0 for the BruteForce technique.
StereoView(osgViewer::Viewer* viewer, Technique technique, osg::Node::NodeMask geometryShaderMask, osg::Node::NodeMask noShaderMask);
StereoView(osgViewer::Viewer* viewer, Technique technique, osg::Node::NodeMask noShaderMask, osg::Node::NodeMask sceneMask);
//! Updates uniforms with the view and projection matrices of each stereo view, and replaces the camera's view and projection matrix
//! with a view and projection that closely envelopes the frustums of the two eyes.
@ -106,6 +106,8 @@ namespace Misc
//! Initialized scene. Call when the "scene root" node has been created
void initializeScene();
void setStereoTechnique(Technique technique);
//! Callback that updates stereo configuration during the update pass
void setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb);
@ -121,21 +123,33 @@ namespace Misc
//! Set the cull callback on the appropriate camera object
void setCullCallback(osg::ref_ptr<osg::NodeCallback> cb);
//! Apply the cullmask to the appropriate camera objects
void setCullMask(osg::Node::NodeMask cullMask);
//! Get the last applied cullmask.
osg::Node::NodeMask getCullMask();
private:
void setupBruteForceTechnique();
void setupGeometryShaderIndexedViewportTechnique();
void removeBruteForceTechnique();
void removeGeometryShaderIndexedViewportTechnique();
void disableStereo();
void enableStereo();
osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osg::Camera> mMainCamera;
osg::ref_ptr<osg::Group> mRoot;
osg::ref_ptr<osg::Group> mScene;
osg::ref_ptr<osg::Group> mStereoRoot;
osg::ref_ptr<osg::Callback> mUpdateCallback;
Technique mTechnique;
// Keeps state relevant to doing stereo via the geometry shader
osg::ref_ptr<osg::Group> mStereoGeometryShaderRoot{ new osg::Group };
osg::Node::NodeMask mGeometryShaderMask;
osg::Node::NodeMask mNoShaderMask;
osg::Node::NodeMask mSceneMask;
osg::Node::NodeMask mCullMask;
// Keeps state and cameras relevant to doing stereo via brute force
osg::ref_ptr<osg::Group> mStereoBruteForceRoot{ new osg::Group };
@ -151,7 +165,13 @@ namespace Misc
bool flipViewOrder{ true };
// Updates stereo configuration during the update pass
std::shared_ptr<UpdateViewCallback> cb{ new DefaultUpdateViewCallback };
std::shared_ptr<UpdateViewCallback> mUpdateViewCallback{ new DefaultUpdateViewCallback };
// OSG camera callbacks set using set*callback. StereoView manages that these are always set on the appropriate camera(s);
osg::ref_ptr<osg::NodeCallback> mCullCallback{ nullptr };
osg::ref_ptr<osg::Camera::DrawCallback> mInitialDrawCallback{ nullptr };
osg::ref_ptr<osg::Camera::DrawCallback> mPreDrawCallback{ nullptr };
osg::ref_ptr<osg::Camera::DrawCallback> mPostDrawCallback{ nullptr };
};
//! Overrides all stereo-related states/uniforms to disable stereo for the scene rendered by camera

@ -375,6 +375,7 @@ public:
setName("GUI Camera");
mDrawable = new Drawable(filter, parent, this);
mDrawable->setName("GUI Drawable");
mDrawable->setDataVariance(osg::Object::STATIC);
addChild(mDrawable.get());
mDrawable->setCullingActive(false);
}

Loading…
Cancel
Save