From e0b51af395fab89e2605a3ce7a173f0c7d674e63 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Mon, 20 Jul 2020 13:03:48 +0000 Subject: [PATCH] Sharing shadow maps across eyes. Does not yet expand frustum. --- apps/openmw/CMakeLists.txt | 2 + apps/openmw/mwvr/openxrmanagerimpl.hpp | 2 +- apps/openmw/mwvr/vrsession.cpp | 47 ++++----- apps/openmw/mwvr/vrsession.hpp | 1 + apps/openmw/mwvr/vrshadow.cpp | 111 +++++++++++++++++++++ apps/openmw/mwvr/vrshadow.hpp | 38 +++++++ apps/openmw/mwvr/vrview.cpp | 4 +- apps/openmw/mwvr/vrview.hpp | 4 +- apps/openmw/mwvr/vrviewer.cpp | 22 +++- apps/openmw/mwvr/vrviewer.hpp | 9 +- components/sceneutil/mwshadowtechnique.cpp | 93 +++++++++++++++++ components/sceneutil/mwshadowtechnique.hpp | 23 +++++ files/settings-default.cfg | 3 + 13 files changed, 322 insertions(+), 37 deletions(-) create mode 100644 apps/openmw/mwvr/vrshadow.cpp create mode 100644 apps/openmw/mwvr/vrshadow.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 0c2e2913c..ce2f28d86 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -173,6 +173,8 @@ if(BUILD_VR_OPENXR) mwvr/vrsession.cpp mwvr/vrframebuffer.hpp mwvr/vrframebuffer.cpp + mwvr/vrshadow.hpp + mwvr/vrshadow.cpp mwvr/vrtypes.hpp mwvr/vrtypes.cpp mwvr/vrview.hpp diff --git a/apps/openmw/mwvr/openxrmanagerimpl.hpp b/apps/openmw/mwvr/openxrmanagerimpl.hpp index cf7b69d14..8161450f2 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.hpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.hpp @@ -100,7 +100,7 @@ namespace MWVR bool mSessionRunning = false; std::mutex mFrameStateMutex{}; std::mutex mEventMutex{}; - std::set mEnabledExtensions; + std::set mEnabledExtensions; std::array mLayerDepth; }; diff --git a/apps/openmw/mwvr/vrsession.cpp b/apps/openmw/mwvr/vrsession.cpp index f2fbf14f1..c37e1bcba 100644 --- a/apps/openmw/mwvr/vrsession.cpp +++ b/apps/openmw/mwvr/vrsession.cpp @@ -71,12 +71,31 @@ namespace MWVR return fov.perspectiveMatrix(near_, far_); } + osg::Matrix VRSession::viewMatrix(osg::Vec3 position, osg::Quat orientation) + { + position = position * Environment::get().unitsPerMeter(); + + 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; + } + osg::Matrix VRSession::viewMatrix(FramePhase phase, Side side) { MWVR::Pose pose{}; pose = predictedPoses(phase).view[(int)side].pose; - if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame) { pose = predictedPoses(phase).eye[(int)side]; @@ -89,25 +108,8 @@ namespace MWVR return viewMatrix; } - - 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; + + return viewMatrix(pose.position, pose.orientation); } bool VRSession::isRunning() const { @@ -228,10 +230,8 @@ namespace MWVR void VRSession::prepareFrame() { - std::unique_lock lock(mMutex); mFrames++; - assert(!mPredrawFrame); auto* xr = Environment::get().getManager(); xr->handleEvents(); @@ -347,7 +347,6 @@ namespace MWVR void VRSession::movementAngles(float& yaw, float& pitch) { - assert(mPredrawFrame); if (!getFrame(FramePhase::Update)) beginPhase(FramePhase::Update); auto frameMeta = getFrame(FramePhase::Update).get(); @@ -363,8 +362,6 @@ namespace MWVR getEulerAngles(frameMeta->mPredictedPoses.head.orientation, headYaw, headPitch, headsWillRoll); getEulerAngles(frameMeta->mPredictedPoses.hands[(int)Side::LEFT_SIDE].orientation, handYaw, handPitch, handRoll); - //Log(Debug::Verbose) << "lhandViewYaw=" << yaw << ", headYaw=" << headYaw << ", handYaw=" << handYaw << ", diff=" << (handYaw - headYaw); - //Log(Debug::Verbose) << "lhandViewPitch=" << pitch << ", headPitch=" << headPitch << ", handPitch=" << handPitch << ", diff=" << (handPitch - headPitch); yaw = handYaw - headYaw; pitch = handPitch - headPitch; } diff --git a/apps/openmw/mwvr/vrsession.hpp b/apps/openmw/mwvr/vrsession.hpp index 5b60c67b2..46443437d 100644 --- a/apps/openmw/mwvr/vrsession.hpp +++ b/apps/openmw/mwvr/vrsession.hpp @@ -73,6 +73,7 @@ namespace MWVR float playerScale() const { return mPlayerScale; } float setPlayerScale(float scale) { return mPlayerScale = scale; } + osg::Matrix viewMatrix(osg::Vec3 position, osg::Quat orientation); osg::Matrix viewMatrix(FramePhase phase, Side side); osg::Matrix projectionMatrix(FramePhase phase, Side side); diff --git a/apps/openmw/mwvr/vrshadow.cpp b/apps/openmw/mwvr/vrshadow.cpp new file mode 100644 index 000000000..7431d2ae4 --- /dev/null +++ b/apps/openmw/mwvr/vrshadow.cpp @@ -0,0 +1,111 @@ +#include "vrenvironment.hpp" +#include "vrsession.hpp" +#include "vrshadow.hpp" + +#include "../mwrender/vismask.hpp" + +#include + +#include + +namespace MWVR +{ + VrShadow::VrShadow(osgViewer::Viewer* viewer, int renderOrder) + : mViewer(viewer) + , mRenderOrder(renderOrder) + , mShadowMapCamera(nullptr) + , mUpdateCallback(new UpdateShadowMapSlaveCallback) + , mMasterConfig(new SharedShadowMapConfig) + , mSlaveConfig(new SharedShadowMapConfig) + { + mMasterConfig->_id = "VR"; + mMasterConfig->_master = true; + mSlaveConfig->_id = "VR"; + mSlaveConfig->_master = false; + } + + void VrShadow::configureShadowsForCamera(osg::Camera* camera) + { + camera->setUserData(mSlaveConfig); + if (camera->getRenderOrderNum() < mRenderOrder) + camera->setRenderOrder(camera->getRenderOrder(), mRenderOrder + 1); + } + + void VrShadow::configureShadows(bool enabled) + { + if (enabled) + { + if (!mShadowMapCamera) + { + mShadowMapCamera = new osg::Camera(); + mShadowMapCamera->setName("ShadowMap"); + mShadowMapCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + mShadowMapCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + mShadowMapCamera->setRenderOrder(osg::Camera::PRE_RENDER, mRenderOrder); + mShadowMapCamera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); + mShadowMapCamera->setAllowEventFocus(false); + mShadowMapCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + mShadowMapCamera->setViewport(0, 0, 640, 360); + mShadowMapCamera->setGraphicsContext(mViewer->getCamera()->getGraphicsContext()); + mShadowMapCamera->setCullMask(0); + mShadowMapCamera->setUserData(mMasterConfig); + mViewer->addSlave(mShadowMapCamera, true); + auto* slave = mViewer->findSlaveForCamera(mShadowMapCamera); + assert(slave); + slave->_updateSlaveCallback = mUpdateCallback; + } + + } + else + { + if (mShadowMapCamera) + { + mViewer->removeSlave(mViewer->findSlaveIndexForCamera(mShadowMapCamera)); + mShadowMapCamera = nullptr; + } + } + } + + void UpdateShadowMapSlaveCallback::updateSlave(osg::View& view, osg::View::Slave& slave) + { + auto* camera = slave._camera.get(); + auto* session = Environment::get().getSession(); + auto viewMatrix = view.getCamera()->getViewMatrix(); + + auto& poses = session->predictedPoses(VRSession::FramePhase::Update); + auto& leftView = poses.view[(int)Side::LEFT_SIDE]; + auto& rightView = poses.view[(int)Side::RIGHT_SIDE]; + osg::Vec3d leftEye = leftView.pose.position; + osg::Vec3d rightEye = rightView.pose.position; + + // The shadow map will be computed from a position P slightly behind the eyes L and R + // where it creates the minimum frustum encompassing both eyes' frustums. + + // Compute Frustum angles. A simple min/max. + FieldOfView fov; + fov.angleLeft = std::min(leftView.fov.angleLeft, rightView.fov.angleLeft); + fov.angleRight = std::max(leftView.fov.angleRight, rightView.fov.angleRight); + fov.angleDown = std::min(leftView.fov.angleDown, rightView.fov.angleDown); + fov.angleUp = std::max(leftView.fov.angleUp, rightView.fov.angleUp); + + // Use the law of sines on the triangle spanning PLR to determine P + double angleLeft = std::abs(fov.angleLeft); + double angleRight = std::abs(fov.angleRight); + double lengthRL = (rightEye - leftEye).length(); + double ratioRL = lengthRL / std::sin(osg::PI - angleLeft - angleRight); + double lengthLP = ratioRL * std::sin(angleRight); + osg::Vec3d directionLP = osg::Vec3(std::cos(-angleLeft), std::sin(-angleLeft), 0); + osg::Vec3d P = leftEye + directionLP * lengthLP; + + // Generate the matrices + float near_ = Settings::Manager::getFloat("near clip", "Camera"); + float far_ = Settings::Manager::getFloat("viewing distance", "Camera"); + + auto modifiedViewMatrix = viewMatrix * session->viewMatrix(P, osg::Quat(0, 0, 0, 1)); + auto projectionMatrix = fov.perspectiveMatrix(near_, far_); + + camera->setViewMatrix(modifiedViewMatrix); + camera->setProjectionMatrix(projectionMatrix); + slave.updateSlaveImplementation(view); + } +} diff --git a/apps/openmw/mwvr/vrshadow.hpp b/apps/openmw/mwvr/vrshadow.hpp new file mode 100644 index 000000000..c619b2703 --- /dev/null +++ b/apps/openmw/mwvr/vrshadow.hpp @@ -0,0 +1,38 @@ +#ifndef MWVR_VRSHADOW_H +#define MWVR_VRSHADOW_H + +#include +#include + +#include + +namespace MWVR +{ + + class UpdateShadowMapSlaveCallback : public osg::View::Slave::UpdateSlaveCallback + { + public: + void updateSlave(osg::View& view, osg::View::Slave& slave) override; + }; + + class VrShadow + { + using SharedShadowMapConfig = SceneUtil::MWShadowTechnique::SharedShadowMapConfig; + public: + VrShadow(osgViewer::Viewer* viewer, int renderOrder = 0); + + void configureShadowsForCamera(osg::Camera* camera); + + void configureShadows(bool enabled); + + private: + osgViewer::Viewer* mViewer; + int mRenderOrder; + osg::ref_ptr mShadowMapCamera; + osg::ref_ptr< UpdateShadowMapSlaveCallback > mUpdateCallback; + osg::ref_ptr mMasterConfig; + osg::ref_ptr mSlaveConfig; + }; +} + +#endif diff --git a/apps/openmw/mwvr/vrview.cpp b/apps/openmw/mwvr/vrview.cpp index 9d9a7938d..3071574d8 100644 --- a/apps/openmw/mwvr/vrview.cpp +++ b/apps/openmw/mwvr/vrview.cpp @@ -36,13 +36,13 @@ namespace MWVR { } }; - osg::Camera* VRView::createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc) + osg::Camera* VRView::createCamera(int order, const osg::Vec4& clearColor, osg::GraphicsContext* gc) { osg::ref_ptr 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->setRenderOrder(osg::Camera::PRE_RENDER, order); camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); camera->setAllowEventFocus(false); camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); diff --git a/apps/openmw/mwvr/vrview.hpp b/apps/openmw/mwvr/vrview.hpp index 432c236bb..4094afb7f 100644 --- a/apps/openmw/mwvr/vrview.hpp +++ b/apps/openmw/mwvr/vrview.hpp @@ -1,5 +1,5 @@ -#ifndef OPENXR_VIEW_HPP -#define OPENXR_VIEW_HPP +#ifndef MWVR_VRVIEW_H +#define MWVR_VRVIEW_H #include #include "openxrmanager.hpp" diff --git a/apps/openmw/mwvr/vrviewer.cpp b/apps/openmw/mwvr/vrviewer.cpp index 38b1d3d6f..a424d78c9 100644 --- a/apps/openmw/mwvr/vrviewer.cpp +++ b/apps/openmw/mwvr/vrviewer.cpp @@ -8,6 +8,10 @@ #include "../mwrender/vismask.hpp" +#include + +#include + namespace MWVR { @@ -32,6 +36,7 @@ namespace MWVR : mViewer(viewer) , mPreDraw(new PredrawCallback(this)) , mPostDraw(new PostdrawCallback(this)) + , mVrShadow(viewer, 1) , mConfigured(false) { mViewer->setRealizeOperation(new RealizeOperation()); @@ -81,14 +86,17 @@ namespace MWVR osg::Vec4 clearColor = mainCamera->getClearColor(); auto config = xr->getRecommendedSwapchainConfig(); bool mirror = Settings::Manager::getBool("mirror texture", "VR"); + mFlipMirrorTextureOrder = Settings::Manager::getBool("flip mirror texture order", "VR"); // TODO: If mirror is false either hide the window or paste something meaningful into it. // E.g. Fanart of Dagoth UR wearing a VR headset + mVrShadow.configureShadows(Settings::Manager::getBool("enable shadows", "Shadows")); + for (unsigned i = 0; i < sViewNames.size(); i++) { auto view = new VRView(sViewNames[i], config[i], context->getState()); mViews[sViewNames[i]] = view; - auto camera = mCameras[sViewNames[i]] = view->createCamera(i, clearColor, context); + auto camera = mCameras[sViewNames[i]] = view->createCamera(i + 2, clearColor, context); camera->setPreDrawCallback(mPreDraw); camera->setFinalDrawCallback(mPostDraw); camera->setCullMask(~MWRender::Mask_GUI & ~MWRender::Mask_SimpleWater & ~MWRender::Mask_UpdateVisitor); @@ -97,13 +105,17 @@ namespace MWVR camera->setSmallFeatureCullingPixelSize(smallFeatureCullingPixelSize); camera->setCullingMode(cullingMode); mViewer->addSlave(camera, true); - mViewer->getSlave(i)._updateSlaveCallback = new VRView::UpdateSlaveCallback(view, context); + auto* slave = mViewer->findSlaveForCamera(camera); + assert(slave); + slave->_updateSlaveCallback = new VRView::UpdateSlaveCallback(view, context); if (mirror) mMsaaResolveMirrorTexture[i].reset(new VRFramebuffer(context->getState(), view->swapchain().width(), view->swapchain().height(), 0)); + + mVrShadow.configureShadowsForCamera(camera); } if (mirror) @@ -156,8 +168,10 @@ namespace MWVR resolveTexture.bindFramebuffer(gc, GL_FRAMEBUFFER_EXT); mViews[sViewNames[i]]->swapchain().renderBuffer()->blit(gc, 0, 0, resolveTexture.width(), resolveTexture.height()); mMirrorTexture->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT); - // Mirror the index when rendering to the mirror texture to allow cross eye mirror textures. - unsigned mirrorIndex = sViewNames.size() - 1 - i; + + unsigned mirrorIndex = i; + if (mFlipMirrorTextureOrder) + mirrorIndex = sViewNames.size() - 1 - i; resolveTexture.blit(gc, mirrorIndex * mirrorWidth, 0, (mirrorIndex + 1) * mirrorWidth, screenHeight); } diff --git a/apps/openmw/mwvr/vrviewer.hpp b/apps/openmw/mwvr/vrviewer.hpp index 83c203ccd..d29aad35a 100644 --- a/apps/openmw/mwvr/vrviewer.hpp +++ b/apps/openmw/mwvr/vrviewer.hpp @@ -1,5 +1,5 @@ -#ifndef MWVR_OPENRXVIEWER_H -#define MWVR_OPENRXVIEWER_H +#ifndef MWVR_VRVIEWER_H +#define MWVR_VRVIEWER_H #include #include @@ -10,6 +10,7 @@ #include #include "openxrmanager.hpp" +#include "vrshadow.hpp" #include @@ -92,10 +93,12 @@ namespace MWVR osg::GraphicsContext* mMainCameraGC{ nullptr }; std::unique_ptr mMsaaResolveMirrorTexture[2]{ }; std::unique_ptr mMirrorTexture{ nullptr }; + VrShadow mVrShadow; - std::mutex mMutex; + std::mutex mMutex{}; bool mConfigured{ false }; + bool mFlipMirrorTextureOrder{ false }; }; } diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 98771bbb4..5cdf3f319 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -900,6 +900,75 @@ MWShadowTechnique::ViewDependentData* MWShadowTechnique::getViewDependentData(os return vdd.release(); } +MWShadowTechnique::ViewDependentData* MWShadowTechnique::getSharedVdd(const SharedShadowMapConfig& config) +{ + auto it = _viewDependentDataShareMap.find(config._id); + if (it != _viewDependentDataShareMap.end()) + return it->second; + + return nullptr; +} + +void MWShadowTechnique::addSharedVdd(const SharedShadowMapConfig& config, ViewDependentData* vdd) +{ + _viewDependentDataShareMap[config._id] = vdd; +} + +void SceneUtil::MWShadowTechnique::shareShadowMap(osgUtil::CullVisitor& cv, ViewDependentData* lhs, ViewDependentData* rhs) +{ + // Prepare for rendering shadows using the shadow map owned by rhs. + + // To achieve this i first copy all data that is not specific to this cv's camera and thus read-only, + // trusting openmw and osg won't overwrite that data before this frame is done rendering. + // This works due to the double buffering of CullVisitors by osg, but also requires that cull passes are serialized (relative to one another). + // Then initialize new copies of the data that will be written with view-specific data + // (the stateset and the texgens). + + lhs->_viewDependentShadowMap = rhs->_viewDependentShadowMap; + lhs->_stateset->clear(); + lhs->_lightDataList = rhs->_lightDataList; + lhs->_numValidShadows = rhs->_numValidShadows; + + ShadowDataList& sdl = lhs->getShadowDataList(); + ShadowDataList previous_sdl; + previous_sdl.swap(sdl); + for (auto rhs_sd : rhs->getShadowDataList()) + { + osg::ref_ptr lhs_sd; + + if (previous_sdl.empty()) + { + OSG_INFO << "Create new ShadowData" << std::endl; + lhs_sd = new ShadowData(lhs); + } + else + { + OSG_INFO << "Taking ShadowData from from of previous_sdl" << std::endl; + lhs_sd = previous_sdl.front(); + previous_sdl.erase(previous_sdl.begin()); + } + lhs_sd->_camera = rhs_sd->_camera; + lhs_sd->_textureUnit = rhs_sd->_textureUnit; + lhs_sd->_texture = rhs_sd->_texture; + sdl.push_back(lhs_sd); + } + + + cv.pushStateSet(_shadowRecievingPlaceholderStateSet.get()); + osg::ref_ptr decoratorStateGraph = cv.getCurrentStateGraph(); + cullShadowReceivingScene(&cv); + cv.popStateSet(); + + for (auto sd : sdl) + { + assignTexGenSettings(&cv, sd->_camera, sd->_textureUnit, sd->_texgen.get()); + } + if (lhs->numValidShadows() > 0) + { + decoratorStateGraph->setStateSet(selectStateSetForRenderingShadow(*lhs)); + } +} + void MWShadowTechnique::update(osg::NodeVisitor& nv) { OSG_INFO<<"MWShadowTechnique::update(osg::NodeVisitor& "<<&nv<<")"<(cv.getCurrentCamera()->getUserData()); + if (sharedConfig) + { + if (sharedConfig->_master) + { + addSharedVdd(*sharedConfig, vdd); + } + else + { + auto* sharedVdd = getSharedVdd(*sharedConfig); + if (sharedVdd) + { + OSG_INFO << "Using shared shadow map" << std::endl; + return shareShadowMap(cv, vdd, sharedVdd); + } + else + { + OSG_INFO << "Warning, view configured to reuse shared shadow map but no shadow map has been shared. Shadows will be generated instead." << std::endl; + } + } + } + if (!vdd) { OSG_INFO<<"Warning, now ViewDependentData created, unable to create shadows."<draw(sd->_texture, sm_i, camera->getViewMatrix() * camera->getProjectionMatrix(), cv); } } + vdd->setNumValidShadows(numValidShadows); if (numValidShadows>0) { diff --git a/components/sceneutil/mwshadowtechnique.hpp b/components/sceneutil/mwshadowtechnique.hpp index 85e548b4b..ceaa10ebf 100644 --- a/components/sceneutil/mwshadowtechnique.hpp +++ b/components/sceneutil/mwshadowtechnique.hpp @@ -139,6 +139,17 @@ namespace SceneUtil { // forward declare class ViewDependentData; + /// Configuration of shadow maps shared by multiple views + struct SharedShadowMapConfig : public osg::Referenced + { + virtual ~SharedShadowMapConfig() {} + + /// String identifier of the shared shadow map + std::string _id; + + bool _master; + }; + struct LightData : public osg::Referenced { LightData(ViewDependentData* vdd); @@ -193,7 +204,12 @@ namespace SceneUtil { virtual void releaseGLObjects(osg::State* = 0) const; + unsigned int numValidShadows(void) const { return _numValidShadows; } + + void setNumValidShadows(unsigned int numValidShadows) { _numValidShadows = numValidShadows; } + protected: + friend class MWShadowTechnique; virtual ~ViewDependentData() {} MWShadowTechnique* _viewDependentShadowMap; @@ -202,11 +218,16 @@ namespace SceneUtil { LightDataList _lightDataList; ShadowDataList _shadowDataList; + + unsigned int _numValidShadows; }; virtual ViewDependentData* createViewDependentData(osgUtil::CullVisitor* cv); ViewDependentData* getViewDependentData(osgUtil::CullVisitor* cv); + ViewDependentData* getSharedVdd(const SharedShadowMapConfig& config); + void addSharedVdd(const SharedShadowMapConfig& config, ViewDependentData* vdd); + void shareShadowMap(osgUtil::CullVisitor& cv, ViewDependentData* lhs, ViewDependentData* rhs); @@ -234,8 +255,10 @@ namespace SceneUtil { virtual ~MWShadowTechnique(); typedef std::map< osgUtil::CullVisitor*, osg::ref_ptr > ViewDependentDataMap; + typedef std::map< std::string, osg::ref_ptr > ViewDependentDataShareMap; mutable OpenThreads::Mutex _viewDependentDataMapMutex; ViewDependentDataMap _viewDependentDataMap; + ViewDependentDataShareMap _viewDependentDataShareMap; osg::ref_ptr _shadowRecievingPlaceholderStateSet; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index cf4503cd3..cee17e7af 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -896,3 +896,6 @@ realistic combat maximum swing velocity = 4.0 # Enables controller vibrations when you hit or are hit. haptics enabled = true + +# Flip the order of eyes in the mirror texture (to allow cross eyed view) +flip mirror texture order = false \ No newline at end of file