mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-22 18:39:40 +00:00
Configurability. Brute force option.
This commit is contained in:
parent
943fe06173
commit
2952463112
12 changed files with 255 additions and 118 deletions
|
@ -20,6 +20,7 @@
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
|
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||||
|
|
||||||
#include "../widget/scenetoolmode.hpp"
|
#include "../widget/scenetoolmode.hpp"
|
||||||
|
|
||||||
|
@ -106,7 +107,7 @@ RenderWidget::~RenderWidget()
|
||||||
// before OSG 3.6.4, the default font was a static object, and if it wasn't attached to the scene when a graphics context was destroyed, it's program wouldn't be released.
|
// before OSG 3.6.4, the default font was a static object, and if it wasn't attached to the scene when a graphics context was destroyed, it's program wouldn't be released.
|
||||||
// 3.6.4 moved it into the object cache, which meant it usually got released, but not here.
|
// 3.6.4 moved it into the object cache, which meant it usually got released, but not here.
|
||||||
// 3.6.5 improved cleanup with osgViewer::CompositeViewer::removeView so it more reliably released associated state for objects in the object cache.
|
// 3.6.5 improved cleanup with osgViewer::CompositeViewer::removeView so it more reliably released associated state for objects in the object cache.
|
||||||
osg::ref_ptr<osg::GraphicsContext> graphicsContext = mView->getCamera()->getGraphicsContext();
|
osg::ref_ptr<osg::GraphicsContext> graphicsContext = SDLUtil::GraphicsWindowSDL2::findContext(*mView);
|
||||||
osgText::Font::getDefaultFont()->releaseGLObjects(graphicsContext->getState());
|
osgText::Font::getDefaultFont()->releaseGLObjects(graphicsContext->getState());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -134,7 +135,7 @@ osg::Camera *RenderWidget::getCamera()
|
||||||
void RenderWidget::toggleRenderStats()
|
void RenderWidget::toggleRenderStats()
|
||||||
{
|
{
|
||||||
osgViewer::GraphicsWindow* window =
|
osgViewer::GraphicsWindow* window =
|
||||||
static_cast<osgViewer::GraphicsWindow*>(mView->getCamera()->getGraphicsContext());
|
static_cast<osgViewer::GraphicsWindow*>(SDLUtil::GraphicsWindowSDL2::findContext(*mView));
|
||||||
|
|
||||||
window->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_S);
|
window->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_S);
|
||||||
window->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_S);
|
window->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_S);
|
||||||
|
@ -246,7 +247,7 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
|
||||||
SceneWidget::~SceneWidget()
|
SceneWidget::~SceneWidget()
|
||||||
{
|
{
|
||||||
// Since we're holding on to the resources past the existence of this graphics context, we'll need to manually release the created objects
|
// Since we're holding on to the resources past the existence of this graphics context, we'll need to manually release the created objects
|
||||||
mResourceSystem->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
|
mResourceSystem->releaseGLObjects(SDLUtil::GraphicsWindowSDL2::findContext(*mView)->getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneWidget::setLighting(Lighting *lighting)
|
void SceneWidget::setLighting(Lighting *lighting)
|
||||||
|
|
|
@ -365,6 +365,8 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
||||||
, mEncoding(ToUTF8::WINDOWS_1252)
|
, mEncoding(ToUTF8::WINDOWS_1252)
|
||||||
, mEncoder(nullptr)
|
, mEncoder(nullptr)
|
||||||
, mScreenCaptureOperation(nullptr)
|
, mScreenCaptureOperation(nullptr)
|
||||||
|
, mStereoEnabled(false)
|
||||||
|
, mStereoOverride(false)
|
||||||
, mSkipMenu (false)
|
, mSkipMenu (false)
|
||||||
, mUseSound (true)
|
, mUseSound (true)
|
||||||
, mCompileAll (false)
|
, mCompileAll (false)
|
||||||
|
@ -398,6 +400,8 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
||||||
|
|
||||||
OMW::Engine::~Engine()
|
OMW::Engine::~Engine()
|
||||||
{
|
{
|
||||||
|
mStereoView = nullptr;
|
||||||
|
|
||||||
mEnvironment.cleanup();
|
mEnvironment.cleanup();
|
||||||
|
|
||||||
delete mScriptContext;
|
delete mScriptContext;
|
||||||
|
@ -696,13 +700,14 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
window->playVideo(logo, true);
|
window->playVideo(logo, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
mStereoEnabled = true; //!< TODO: TEMP
|
// VR mode will override this setting by setting mStereoOverride.
|
||||||
|
mStereoEnabled = mStereoOverride || Settings::Manager::getBool("stereo enabled", "Stereo");
|
||||||
|
|
||||||
// geometry shader must be enabled before the RenderingManager sets up any shaders
|
// geometry shader must be enabled before the RenderingManager sets up any shaders
|
||||||
// therefore this part is separate from the rest of stereo setup.
|
// therefore this part is separate from the rest of stereo setup.
|
||||||
if (mStereoEnabled)
|
if (mStereoEnabled)
|
||||||
{
|
{
|
||||||
mResourceSystem->getSceneManager()->getShaderManager().enableGeometryShader(true);
|
mResourceSystem->getSceneManager()->getShaderManager().setStereoGeometryShaderEnabled(Misc::getStereoTechnique() == Misc::StereoView::Technique::GeometryShader_IndexedViewports);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the world
|
// Create the world
|
||||||
|
@ -718,7 +723,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
// Remove that altogether when the sky finally uses them.
|
// Remove that altogether when the sky finally uses them.
|
||||||
auto noShaderMask = MWRender::VisMask::Mask_Sky | MWRender::VisMask::Mask_Sun | MWRender::VisMask::Mask_WeatherParticles;
|
auto noShaderMask = MWRender::VisMask::Mask_Sky | MWRender::VisMask::Mask_Sun | MWRender::VisMask::Mask_WeatherParticles;
|
||||||
auto geometryShaderMask = mViewer->getCamera()->getCullMask() & ~noShaderMask;
|
auto geometryShaderMask = mViewer->getCamera()->getCullMask() & ~noShaderMask;
|
||||||
mStereoView.reset(new Misc::StereoView(mViewer, geometryShaderMask, noShaderMask | MWRender::VisMask::Mask_Scene));
|
mStereoView.reset(new Misc::StereoView(mViewer, Misc::getStereoTechnique(), geometryShaderMask, noShaderMask | MWRender::VisMask::Mask_Scene));
|
||||||
}
|
}
|
||||||
|
|
||||||
window->setStore(mEnvironment.getWorld()->getStore());
|
window->setStore(mEnvironment.getWorld()->getStore());
|
||||||
|
|
|
@ -92,6 +92,7 @@ namespace OMW
|
||||||
std::vector<std::string> mContentFiles;
|
std::vector<std::string> mContentFiles;
|
||||||
|
|
||||||
bool mStereoEnabled;
|
bool mStereoEnabled;
|
||||||
|
bool mStereoOverride;
|
||||||
std::unique_ptr<Misc::StereoView> mStereoView;
|
std::unique_ptr<Misc::StereoView> mStereoView;
|
||||||
|
|
||||||
bool mSkipMenu;
|
bool mSkipMenu;
|
||||||
|
|
|
@ -248,7 +248,6 @@ public:
|
||||||
setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Static|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting);
|
setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Static|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting);
|
||||||
setNodeMask(Mask_RenderToTexture);
|
setNodeMask(Mask_RenderToTexture);
|
||||||
setViewport(0, 0, rttSize, rttSize);
|
setViewport(0, 0, rttSize, rttSize);
|
||||||
Misc::enableStereoForCamera(this, true);
|
|
||||||
|
|
||||||
// No need for Update traversal since the scene is already updated as part of the main scene graph
|
// No need for Update traversal since the scene is already updated as part of the main scene graph
|
||||||
// A double update would mess with the light collection (in addition to being plain redundant)
|
// A double update would mess with the light collection (in addition to being plain redundant)
|
||||||
|
@ -344,7 +343,6 @@ public:
|
||||||
|
|
||||||
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
|
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
|
||||||
setViewport(0, 0, rttSize, rttSize);
|
setViewport(0, 0, rttSize, rttSize);
|
||||||
Misc::enableStereoForCamera(this, true);
|
|
||||||
|
|
||||||
|
|
||||||
// No need for Update traversal since the mSceneRoot is already updated as part of the main scene graph
|
// No need for Update traversal since the mSceneRoot is already updated as part of the main scene graph
|
||||||
|
@ -598,7 +596,19 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
|
||||||
// use a define map to conditionally compile the shader
|
// use a define map to conditionally compile the shader
|
||||||
std::map<std::string, std::string> defineMap;
|
std::map<std::string, std::string> defineMap;
|
||||||
defineMap.insert(std::make_pair(std::string("refraction_enabled"), std::string(refraction ? "1" : "0")));
|
defineMap.insert(std::make_pair(std::string("refraction_enabled"), std::string(refraction ? "1" : "0")));
|
||||||
|
|
||||||
|
if (mResourceSystem->getSceneManager()->getShaderManager().stereoGeometryShaderEnabled())
|
||||||
|
{
|
||||||
defineMap["geometryShader"] = "1";
|
defineMap["geometryShader"] = "1";
|
||||||
|
if (reflection)
|
||||||
|
{
|
||||||
|
Misc::enableStereoForCamera(reflection, true);
|
||||||
|
}
|
||||||
|
if (refraction)
|
||||||
|
{
|
||||||
|
Misc::enableStereoForCamera(refraction, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
|
Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
|
||||||
osg::ref_ptr<osg::Shader> vertexShader (shaderMgr.getShader("water_vertex.glsl", defineMap, osg::Shader::VERTEX));
|
osg::ref_ptr<osg::Shader> vertexShader (shaderMgr.getShader("water_vertex.glsl", defineMap, osg::Shader::VERTEX));
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "stereo.hpp"
|
#include "stereo.hpp"
|
||||||
|
#include "stringops.hpp"
|
||||||
|
|
||||||
#include <osg/io_utils>
|
#include <osg/io_utils>
|
||||||
#include <osg/ViewportIndexed>
|
#include <osg/ViewportIndexed>
|
||||||
|
@ -10,9 +11,12 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
#include <components/sceneutil/statesetupdater.hpp>
|
#include <components/sceneutil/statesetupdater.hpp>
|
||||||
#include <components/sceneutil/visitor.hpp>
|
#include <components/sceneutil/visitor.hpp>
|
||||||
|
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
namespace Misc
|
namespace Misc
|
||||||
{
|
{
|
||||||
Pose Pose::operator+(const Pose& rhs)
|
Pose Pose::operator+(const Pose& rhs)
|
||||||
|
@ -221,65 +225,110 @@ namespace Misc
|
||||||
StereoView* stereoView;
|
StereoView* stereoView;
|
||||||
};
|
};
|
||||||
|
|
||||||
StereoView::StereoView(osgViewer::Viewer* viewer, osg::Node::NodeMask geometryShaderMask, osg::Node::NodeMask bruteForceMask)
|
StereoView::StereoView(osgViewer::Viewer* viewer, Technique technique, osg::Node::NodeMask geometryShaderMask, osg::Node::NodeMask noShaderMask)
|
||||||
: osg::Group()
|
: osg::Group()
|
||||||
, mViewer(viewer)
|
, mViewer(viewer)
|
||||||
, mMainCamera(mViewer->getCamera())
|
, mMainCamera(mViewer->getCamera())
|
||||||
, mRoot(viewer->getSceneData()->asGroup())
|
, mRoot(viewer->getSceneData()->asGroup())
|
||||||
|
, mTechnique(technique)
|
||||||
, mGeometryShaderMask(geometryShaderMask)
|
, mGeometryShaderMask(geometryShaderMask)
|
||||||
, mBruteForceMask(bruteForceMask)
|
, mNoShaderMask(noShaderMask)
|
||||||
{
|
{
|
||||||
|
if (technique == Technique::None)
|
||||||
|
// Do nothing
|
||||||
|
return;
|
||||||
|
|
||||||
SceneUtil::FindByNameVisitor findScene("Scene Root");
|
SceneUtil::FindByNameVisitor findScene("Scene Root");
|
||||||
mRoot->accept(findScene);
|
mRoot->accept(findScene);
|
||||||
mScene = findScene.mFoundNode;
|
mScene = findScene.mFoundNode;
|
||||||
if (!mScene)
|
if (!mScene)
|
||||||
throw std::logic_error("Couldn't find scene root");
|
throw std::logic_error("Couldn't find scene root");
|
||||||
|
|
||||||
setName("Sky Root");
|
setName("Stereo Root");
|
||||||
mRoot->setDataVariance(osg::Object::STATIC);
|
mRoot->setDataVariance(osg::Object::STATIC);
|
||||||
setDataVariance(osg::Object::STATIC);
|
setDataVariance(osg::Object::STATIC);
|
||||||
mLeftCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
mLeftCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
||||||
mLeftCamera->setProjectionResizePolicy(osg::Camera::FIXED);
|
mLeftCamera->setProjectionResizePolicy(osg::Camera::FIXED);
|
||||||
mLeftCamera->setProjectionMatrix(osg::Matrix::identity());
|
mLeftCamera->setProjectionMatrix(osg::Matrix::identity());
|
||||||
mLeftCamera->setViewMatrix(osg::Matrix::identity());
|
mLeftCamera->setViewMatrix(osg::Matrix::identity());
|
||||||
mLeftCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
|
|
||||||
mLeftCamera->setClearMask(GL_NONE);
|
|
||||||
mLeftCamera->setCullMask(bruteForceMask);
|
|
||||||
mLeftCamera->setName("Stereo Left");
|
mLeftCamera->setName("Stereo Left");
|
||||||
mLeftCamera->setDataVariance(osg::Object::STATIC);
|
mLeftCamera->setDataVariance(osg::Object::STATIC);
|
||||||
mRightCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
mRightCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
||||||
mRightCamera->setProjectionResizePolicy(osg::Camera::FIXED);
|
mRightCamera->setProjectionResizePolicy(osg::Camera::FIXED);
|
||||||
mRightCamera->setProjectionMatrix(osg::Matrix::identity());
|
mRightCamera->setProjectionMatrix(osg::Matrix::identity());
|
||||||
mRightCamera->setViewMatrix(osg::Matrix::identity());
|
mRightCamera->setViewMatrix(osg::Matrix::identity());
|
||||||
mRightCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
|
|
||||||
mRightCamera->setClearMask(GL_NONE);
|
|
||||||
mRightCamera->setCullMask(bruteForceMask);
|
|
||||||
mRightCamera->setName("Stereo Right");
|
mRightCamera->setName("Stereo Right");
|
||||||
mRightCamera->setDataVariance(osg::Object::STATIC);
|
mRightCamera->setDataVariance(osg::Object::STATIC);
|
||||||
|
|
||||||
mMainCamera->setCullMask(geometryShaderMask);
|
|
||||||
|
|
||||||
// Inject self as the root of the scene graph, and split into geometry-shader stereo and brute force stereo.
|
|
||||||
addChild(mStereoGeometryShaderRoot);
|
|
||||||
mStereoGeometryShaderRoot->addChild(mRoot);
|
|
||||||
addChild(mStereoBruteForceRoot);
|
|
||||||
mStereoBruteForceRoot->addChild(mLeftCamera);
|
|
||||||
mLeftCamera->addChild(mScene); // Use scene directly to avoid redundant shadow computation.
|
|
||||||
mStereoBruteForceRoot->addChild(mRightCamera);
|
|
||||||
mRightCamera->addChild(mScene);
|
|
||||||
viewer->setSceneData(this);
|
|
||||||
|
|
||||||
// Do a blank double buffering of camera statesets on update. Actual stateset updates are performed in StereoView::Update()
|
|
||||||
mLeftCamera->setUpdateCallback(new SceneUtil::StateSetUpdater());
|
|
||||||
mRightCamera->setUpdateCallback(new SceneUtil::StateSetUpdater());
|
|
||||||
|
|
||||||
// Update stereo statesets/matrices, but after the main camera updates.
|
// Update stereo statesets/matrices, but after the main camera updates.
|
||||||
auto mainCameraCB = mMainCamera->getUpdateCallback();
|
auto mainCameraCB = mMainCamera->getUpdateCallback();
|
||||||
mMainCamera->removeUpdateCallback(mainCameraCB);
|
mMainCamera->removeUpdateCallback(mainCameraCB);
|
||||||
mMainCamera->addUpdateCallback(new StereoUpdateCallback(this));
|
mMainCamera->addUpdateCallback(new StereoUpdateCallback(this));
|
||||||
mMainCamera->addUpdateCallback(mainCameraCB);
|
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());
|
||||||
|
|
||||||
|
|
||||||
|
if (mTechnique == Technique::GeometryShader_IndexedViewports)
|
||||||
|
{
|
||||||
|
setupGeometryShaderIndexedViewportTechnique();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setupBruteForceTechnique();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
// Slave cameras must have their viewports defined immediately
|
||||||
|
auto width = mMainCamera->getViewport()->width();
|
||||||
|
auto height = mMainCamera->getViewport()->height();
|
||||||
|
mLeftCamera->setViewport(0, 0, width / 2, height);
|
||||||
|
mRightCamera->setViewport(width / 2, 0, width / 2, height);
|
||||||
|
|
||||||
|
mViewer->stopThreading();
|
||||||
|
mViewer->addSlave(mLeftCamera, true);
|
||||||
|
mViewer->addSlave(mRightCamera, true);
|
||||||
|
mRightCamera->setGraphicsContext(mViewer->getCamera()->getGraphicsContext());
|
||||||
|
mLeftCamera->setGraphicsContext(mViewer->getCamera()->getGraphicsContext());
|
||||||
|
mViewer->getCamera()->setGraphicsContext(nullptr);
|
||||||
|
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);
|
||||||
|
|
||||||
|
addChild(mStereoGeometryShaderRoot);
|
||||||
|
mStereoGeometryShaderRoot->addChild(mRoot);
|
||||||
|
addChild(mStereoBruteForceRoot);
|
||||||
|
mStereoBruteForceRoot->addChild(mLeftCamera);
|
||||||
|
mLeftCamera->addChild(mScene); // Use scene directly to avoid redundant shadow computation.
|
||||||
|
mStereoBruteForceRoot->addChild(mRightCamera);
|
||||||
|
mRightCamera->addChild(mScene);
|
||||||
|
|
||||||
addCullCallback(new StereoStatesetUpdateCallback(this));
|
addCullCallback(new StereoStatesetUpdateCallback(this));
|
||||||
|
|
||||||
|
// Inject self as the root of the scene graph
|
||||||
|
mViewer->setSceneData(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StereoView::update()
|
void StereoView::update()
|
||||||
|
@ -314,11 +363,18 @@ namespace Misc
|
||||||
mRightCamera->setProjectionMatrix(rightProjectionMatrix);
|
mRightCamera->setProjectionMatrix(rightProjectionMatrix);
|
||||||
mLeftCamera->setProjectionMatrix(leftProjectionMatrix);
|
mLeftCamera->setProjectionMatrix(leftProjectionMatrix);
|
||||||
|
|
||||||
// The persepctive frustum will be computed from a position P slightly behind the eyes L and R
|
auto width = mMainCamera->getViewport()->width();
|
||||||
|
auto height = mMainCamera->getViewport()->height();
|
||||||
|
|
||||||
|
if (mTechnique == Technique::GeometryShader_IndexedViewports)
|
||||||
|
{
|
||||||
|
// To correctly cull when drawing stereo using the geometry shader, the main camera must
|
||||||
|
// draw a fake view+perspective that includes the full frustums of both the left and right eyes.
|
||||||
|
// This frustum will be computed as a perspective frustum from a position P slightly behind the eyes L and R
|
||||||
// where it creates the minimum frustum encompassing both eyes' frustums.
|
// where it creates the minimum frustum encompassing both eyes' frustums.
|
||||||
// NOTE: I make an assumption that the eyes lie in a horizontal plane relative to the base view,
|
// NOTE: I make an assumption that the eyes lie in a horizontal plane relative to the base view,
|
||||||
// and lie mirrored around the Y axis (straight ahead).
|
// and lie mirrored around the Y axis (straight ahead).
|
||||||
// Re-think this if that turns out to be a bad assumption
|
// Re-think this if that turns out to be a bad assumption.
|
||||||
View frustumView;
|
View frustumView;
|
||||||
|
|
||||||
// Compute Frustum angles. A simple min/max.
|
// Compute Frustum angles. A simple min/max.
|
||||||
|
@ -373,12 +429,18 @@ namespace Misc
|
||||||
// Update camera with frustum matrices
|
// Update camera with frustum matrices
|
||||||
mMainCamera->setViewMatrix(frustumViewMatrix);
|
mMainCamera->setViewMatrix(frustumViewMatrix);
|
||||||
mMainCamera->setProjectionMatrix(frustumProjectionMatrix);
|
mMainCamera->setProjectionMatrix(frustumProjectionMatrix);
|
||||||
|
|
||||||
auto width = mMainCamera->getViewport()->width();
|
|
||||||
auto height = mMainCamera->getViewport()->height();
|
|
||||||
mLeftCamera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, 0, 0, width / 2, height), osg::StateAttribute::OVERRIDE);
|
mLeftCamera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, 0, 0, width / 2, height), osg::StateAttribute::OVERRIDE);
|
||||||
mRightCamera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, width / 2, 0, width / 2, height), osg::StateAttribute::OVERRIDE);
|
mRightCamera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, width / 2, 0, width / 2, height), osg::StateAttribute::OVERRIDE);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mLeftCamera->setClearColor(mMainCamera->getClearColor());
|
||||||
|
mRightCamera->setClearColor(mMainCamera->getClearColor());
|
||||||
|
|
||||||
|
mLeftCamera->setViewport(0, 0, width / 2, height);
|
||||||
|
mRightCamera->setViewport(width / 2, 0, width / 2, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void StereoView::updateStateset(osg::StateSet * stateset)
|
void StereoView::updateStateset(osg::StateSet * stateset)
|
||||||
{
|
{
|
||||||
|
@ -405,18 +467,6 @@ namespace Misc
|
||||||
this->cb = cb;
|
this->cb = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StereoView::useSlaveCameraAtIndex(int index)
|
|
||||||
{
|
|
||||||
if (mViewer->getNumSlaves() <= index)
|
|
||||||
{
|
|
||||||
Log(Debug::Error) << "Requested slave at index " << index << " but no such slave exists";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mMainCamera = mViewer->getSlave(index)._camera;
|
|
||||||
mMainCamera->setCullMask(mGeometryShaderMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
void disableStereoForCamera(osg::Camera* camera)
|
void disableStereoForCamera(osg::Camera* camera)
|
||||||
{
|
{
|
||||||
auto* viewport = camera->getViewport();
|
auto* viewport = camera->getViewport();
|
||||||
|
@ -450,6 +500,23 @@ namespace Misc
|
||||||
camera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(1, x2, y2, width, height));
|
camera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(1, x2, y2, width, height));
|
||||||
camera->getOrCreateStateSet()->addUniform(new osg::Uniform("geometryPassthrough", false));
|
camera->getOrCreateStateSet()->addUniform(new osg::Uniform("geometryPassthrough", false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StereoView::Technique getStereoTechnique(void)
|
||||||
|
{
|
||||||
|
auto stereoMethodString = Settings::Manager::getString("stereo method", "Stereo");
|
||||||
|
auto stereoMethodStringLowerCase = Misc::StringUtils::lowerCase(stereoMethodString);
|
||||||
|
if (stereoMethodStringLowerCase == "geometryshader")
|
||||||
|
{
|
||||||
|
return Misc::StereoView::Technique::GeometryShader_IndexedViewports;
|
||||||
|
}
|
||||||
|
if (stereoMethodStringLowerCase == "bruteforce")
|
||||||
|
{
|
||||||
|
return Misc::StereoView::Technique::BruteForce;
|
||||||
|
}
|
||||||
|
Log(Debug::Warning) << "Unknown stereo technique \"" << stereoMethodString << "\", defaulting to BruteForce";
|
||||||
|
return StereoView::Technique::BruteForce;
|
||||||
|
}
|
||||||
|
|
||||||
void StereoView::DefaultUpdateViewCallback::updateView(View& left, View& right, double& near, double& far)
|
void StereoView::DefaultUpdateViewCallback::updateView(View& left, View& right, double& near, double& far)
|
||||||
{
|
{
|
||||||
left.pose.position = osg::Vec3(-2.2, 0, 0);
|
left.pose.position = osg::Vec3(-2.2, 0, 0);
|
||||||
|
@ -457,6 +524,6 @@ namespace Misc
|
||||||
left.fov = { -0.767549932, 0.620896876, -0.837898076, 0.726982594 };
|
left.fov = { -0.767549932, 0.620896876, -0.837898076, 0.726982594 };
|
||||||
right.fov = { -0.620896876, 0.767549932, -0.837898076, 0.726982594 };
|
right.fov = { -0.620896876, 0.767549932, -0.837898076, 0.726982594 };
|
||||||
near = 1;
|
near = 1;
|
||||||
far = 10000;
|
far = 6656;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,11 +78,19 @@ namespace Misc
|
||||||
virtual void updateView(View& left, View& right, double& near, double& far);
|
virtual void updateView(View& left, View& right, double& near, double& far);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Technique
|
||||||
|
{
|
||||||
|
None = 0, //!< Stereo disabled (do nothing).
|
||||||
|
BruteForce, //!< Two slave cameras culling and drawing everything.
|
||||||
|
GeometryShader_IndexedViewports, //!< Frustum camera culls and draws stereo into indexed viewports using an automatically generated geometry shader.
|
||||||
|
};
|
||||||
|
|
||||||
//! Adds two cameras in stereo to the mainCamera.
|
//! 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.
|
//! 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.
|
||||||
//! \note The mask is removed from the mainCamera, so do not put Scene in this mask.
|
//! \param geometryShaderMask should mask in all nodes that use shaders.
|
||||||
//! \note Brute force does not support shadows. But that's fine because currently this only applies to things that don't use shaders and that's only the sky, which will use shaders in the future.
|
//! \param noShaderMask mask in all nodes that do not use shaders and must be rendered brute force.
|
||||||
StereoView(osgViewer::Viewer* viewer, osg::Node::NodeMask geometryShaderMask, osg::Node::NodeMask bruteForceMask);
|
//! \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);
|
||||||
|
|
||||||
//! Updates uniforms with the view and projection matrices of each stereo view, and replaces the camera's view and projection matrix
|
//! 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.
|
//! with a view and projection that closely envelopes the frustums of the two eyes.
|
||||||
|
@ -92,21 +100,23 @@ namespace Misc
|
||||||
//! Callback that updates stereo configuration during the update pass
|
//! Callback that updates stereo configuration during the update pass
|
||||||
void setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb);
|
void setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb);
|
||||||
|
|
||||||
//! Use the slave camera at index instead of the main viewer camera.
|
private:
|
||||||
void useSlaveCameraAtIndex(int index);
|
void setupBruteForceTechnique();
|
||||||
|
void setupGeometryShaderIndexedViewportTechnique();
|
||||||
|
|
||||||
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
||||||
osg::ref_ptr<osg::Camera> mMainCamera;
|
osg::ref_ptr<osg::Camera> mMainCamera;
|
||||||
osg::ref_ptr<osg::Group> mRoot;
|
osg::ref_ptr<osg::Group> mRoot;
|
||||||
osg::ref_ptr<osg::Group> mScene;
|
osg::ref_ptr<osg::Group> mScene;
|
||||||
|
Technique mTechnique;
|
||||||
|
|
||||||
// Keeps state relevant to doing stereo via the geometry shader
|
// Keeps state relevant to doing stereo via the geometry shader
|
||||||
osg::ref_ptr<osg::Group> mStereoGeometryShaderRoot{ new osg::Group };
|
osg::ref_ptr<osg::Group> mStereoGeometryShaderRoot{ new osg::Group };
|
||||||
osg::Node::NodeMask mGeometryShaderMask;
|
osg::Node::NodeMask mGeometryShaderMask;
|
||||||
|
osg::Node::NodeMask mNoShaderMask;
|
||||||
|
|
||||||
// Keeps state and cameras relevant to doing stereo via brute force
|
// Keeps state and cameras relevant to doing stereo via brute force
|
||||||
osg::ref_ptr<osg::Group> mStereoBruteForceRoot{ new osg::Group };
|
osg::ref_ptr<osg::Group> mStereoBruteForceRoot{ new osg::Group };
|
||||||
osg::Node::NodeMask mBruteForceMask;
|
|
||||||
osg::ref_ptr<osg::Camera> mLeftCamera{ new osg::Camera };
|
osg::ref_ptr<osg::Camera> mLeftCamera{ new osg::Camera };
|
||||||
osg::ref_ptr<osg::Camera> mRightCamera{ new osg::Camera };
|
osg::ref_ptr<osg::Camera> mRightCamera{ new osg::Camera };
|
||||||
|
|
||||||
|
@ -122,6 +132,9 @@ namespace Misc
|
||||||
|
|
||||||
//! Overrides all stereo-related states/uniforms to enable stereo for the scene rendered by camera
|
//! Overrides all stereo-related states/uniforms to enable stereo for the scene rendered by camera
|
||||||
void enableStereoForCamera(osg::Camera* camera, bool horizontalSplit);
|
void enableStereoForCamera(osg::Camera* camera, bool horizontalSplit);
|
||||||
|
|
||||||
|
//! Reads settings to determine stereo technique
|
||||||
|
StereoView::Technique getStereoTechnique(void);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "sdlgraphicswindow.hpp"
|
#include "sdlgraphicswindow.hpp"
|
||||||
|
|
||||||
|
#include <osgViewer/viewer>
|
||||||
|
|
||||||
#include <SDL_video.h>
|
#include <SDL_video.h>
|
||||||
|
|
||||||
namespace SDLUtil
|
namespace SDLUtil
|
||||||
|
@ -202,6 +204,23 @@ void GraphicsWindowSDL2::setSyncToVBlank(bool on)
|
||||||
SDL_GL_MakeCurrent(oldWin, oldCtx);
|
SDL_GL_MakeCurrent(oldWin, oldCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::GraphicsContext* GraphicsWindowSDL2::findContext(osgViewer::View& view)
|
||||||
|
{
|
||||||
|
view.getCamera();
|
||||||
|
if (view.getCamera()->getGraphicsContext())
|
||||||
|
{
|
||||||
|
return view.getCamera()->getGraphicsContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0; i < view.getNumSlaves(); i++)
|
||||||
|
{
|
||||||
|
if (view.getSlave(i)._camera->getGraphicsContext())
|
||||||
|
return view.getSlave(i)._camera->getGraphicsContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void GraphicsWindowSDL2::setSwapInterval(bool enable)
|
void GraphicsWindowSDL2::setSwapInterval(bool enable)
|
||||||
{
|
{
|
||||||
if (enable)
|
if (enable)
|
||||||
|
|
|
@ -81,6 +81,9 @@ public:
|
||||||
SDL_Window *mWindow;
|
SDL_Window *mWindow;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Convenience function for finding the context among the main camera or slaves */
|
||||||
|
static osg::GraphicsContext* findContext(osgViewer::View& view);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setSwapInterval(bool enable);
|
void setSwapInterval(bool enable);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "sdlinputwrapper.hpp"
|
#include "sdlinputwrapper.hpp"
|
||||||
|
#include "sdlgraphicswindow.hpp"
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
@ -213,7 +214,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> v
|
||||||
SDL_GetWindowSize(mSDLWindow, &w, &h);
|
SDL_GetWindowSize(mSDLWindow, &w, &h);
|
||||||
int x,y;
|
int x,y;
|
||||||
SDL_GetWindowPosition(mSDLWindow, &x,&y);
|
SDL_GetWindowPosition(mSDLWindow, &x,&y);
|
||||||
mViewer->getCamera()->getGraphicsContext()->resized(x,y,w,h);
|
GraphicsWindowSDL2::findContext(*mViewer)->resized(x,y,w,h);
|
||||||
|
|
||||||
mViewer->getEventQueue()->windowResize(x,y,w,h);
|
mViewer->getEventQueue()->windowResize(x,y,w,h);
|
||||||
|
|
||||||
|
|
|
@ -515,11 +515,16 @@ namespace Shader
|
||||||
return DefineMap(mGlobalDefines);
|
return DefineMap(mGlobalDefines);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderManager::enableGeometryShader(bool enabled)
|
void ShaderManager::setStereoGeometryShaderEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
mGeometryShadersEnabled = enabled;
|
mGeometryShadersEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShaderManager::stereoGeometryShaderEnabled() const
|
||||||
|
{
|
||||||
|
return mGeometryShadersEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
void ShaderManager::setGlobalDefines(DefineMap& globalDefines)
|
void ShaderManager::setGlobalDefines(DefineMap& globalDefines)
|
||||||
{
|
{
|
||||||
mGlobalDefines = globalDefines;
|
mGlobalDefines = globalDefines;
|
||||||
|
|
|
@ -41,7 +41,8 @@ namespace Shader
|
||||||
/// whose defines include "geometryShader" set to "1".
|
/// whose defines include "geometryShader" set to "1".
|
||||||
/// This geometry shader is automatically included in any program using that vertex shader.
|
/// This geometry shader is automatically included in any program using that vertex shader.
|
||||||
/// \note Does not affect programs that have already been created, set this during startup.
|
/// \note Does not affect programs that have already been created, set this during startup.
|
||||||
void enableGeometryShader(bool enabled);
|
void setStereoGeometryShaderEnabled(bool enabled);
|
||||||
|
bool stereoGeometryShaderEnabled() const;
|
||||||
|
|
||||||
/// Set the DefineMap used to construct all shaders
|
/// Set the DefineMap used to construct all shaders
|
||||||
/// @param defines The DefineMap to use
|
/// @param defines The DefineMap to use
|
||||||
|
|
|
@ -901,3 +901,14 @@ object shadows = false
|
||||||
|
|
||||||
# Allow shadows indoors. Due to limitations with Morrowind's data, only actors can cast shadows indoors, which some might feel is distracting.
|
# Allow shadows indoors. Due to limitations with Morrowind's data, only actors can cast shadows indoors, which some might feel is distracting.
|
||||||
enable indoor shadows = true
|
enable indoor shadows = true
|
||||||
|
|
||||||
|
[Stereo]
|
||||||
|
|
||||||
|
# Enable/disable stereo view. This setting is ignored in VR.
|
||||||
|
stereo enabled = false
|
||||||
|
|
||||||
|
# Method used to render stereo if enabled
|
||||||
|
# Must be one of the following: BruteForce, GeometryShader
|
||||||
|
# BruteForce: Generates stereo using two cameras and two cull/render passes. Choose this if your game is GPU-bound.
|
||||||
|
# GeometryShader: Generates stereo in a single pass using automatically generated geometry shaders. May break custom shaders. Choose this if your game is CPU-bound.
|
||||||
|
stereo method = GeometryShader
|
||||||
|
|
Loading…
Reference in a new issue