mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-30 11:36:42 +00:00
Merge remote tracking branch multiview_test_branch
This commit is contained in:
parent
85033bb647
commit
c974e1dde3
23 changed files with 1064 additions and 60 deletions
|
@ -20,6 +20,7 @@
|
|||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/sdlutil/sdlgraphicswindow.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.
|
||||
// 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.
|
||||
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());
|
||||
#endif
|
||||
}
|
||||
|
@ -134,7 +135,7 @@ osg::Camera *RenderWidget::getCamera()
|
|||
void RenderWidget::toggleRenderStats()
|
||||
{
|
||||
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()->keyRelease(osgGA::GUIEventAdapter::KEY_S);
|
||||
|
@ -246,7 +247,7 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
|
|||
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
|
||||
mResourceSystem->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
|
||||
mResourceSystem->releaseGLObjects(SDLUtil::GraphicsWindowSDL2::findContext(*mView)->getState());
|
||||
}
|
||||
|
||||
void SceneWidget::setLighting(Lighting *lighting)
|
||||
|
|
|
@ -24,12 +24,16 @@
|
|||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
#include <components/sdlutil/imagetosurface.hpp>
|
||||
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/resource/stats.hpp>
|
||||
|
||||
#include <components/compiler/extensions0.hpp>
|
||||
|
||||
#include <components/misc/stereo.hpp>
|
||||
|
||||
#include <components/sceneutil/workqueue.hpp>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
@ -376,6 +380,9 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
|||
, mEncoding(ToUTF8::WINDOWS_1252)
|
||||
, mEncoder(nullptr)
|
||||
, mScreenCaptureOperation(nullptr)
|
||||
, mStereoEnabled(false)
|
||||
, mStereoOverride(false)
|
||||
, mStereoView(nullptr)
|
||||
, mSkipMenu (false)
|
||||
, mUseSound (true)
|
||||
, mCompileAll (false)
|
||||
|
@ -409,6 +416,8 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
|||
|
||||
OMW::Engine::~Engine()
|
||||
{
|
||||
mStereoView = nullptr;
|
||||
|
||||
mEnvironment.cleanup();
|
||||
|
||||
delete mScriptContext;
|
||||
|
@ -761,12 +770,32 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
window->playVideo(logo, true);
|
||||
}
|
||||
|
||||
// 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
|
||||
// therefore this part is separate from the rest of stereo setup.
|
||||
if (mStereoEnabled)
|
||||
{
|
||||
mResourceSystem->getSceneManager()->getShaderManager().setStereoGeometryShaderEnabled(Misc::getStereoTechnique() == Misc::StereoView::Technique::GeometryShader_IndexedViewports);
|
||||
}
|
||||
|
||||
// Create the world
|
||||
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
|
||||
mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName,
|
||||
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
|
||||
mEnvironment.getWorld()->setupPlayer();
|
||||
|
||||
// Set up stereo
|
||||
if (mStereoEnabled)
|
||||
{
|
||||
// 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 = new Misc::StereoView(mViewer, Misc::getStereoTechnique(), geometryShaderMask, noShaderMask | MWRender::VisMask::Mask_Scene);
|
||||
}
|
||||
|
||||
window->setStore(mEnvironment.getWorld()->getStore());
|
||||
window->initUI();
|
||||
|
||||
|
|
|
@ -37,6 +37,11 @@ namespace Compiler
|
|||
class Context;
|
||||
}
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
class StereoView;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
class ScriptManager;
|
||||
|
@ -89,6 +94,11 @@ namespace OMW
|
|||
osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;
|
||||
std::string mCellName;
|
||||
std::vector<std::string> mContentFiles;
|
||||
|
||||
bool mStereoEnabled;
|
||||
bool mStereoOverride;
|
||||
osg::ref_ptr<Misc::StereoView> mStereoView;
|
||||
|
||||
bool mSkipMenu;
|
||||
bool mUseSound;
|
||||
bool mCompileAll;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <osg/ComputeBoundsVisitor>
|
||||
#include <osg/ShapeDrawable>
|
||||
#include <osg/TextureCubeMap>
|
||||
#include <osg/ViewportIndexed>
|
||||
|
||||
#include <osgUtil/LineSegmentIntersector>
|
||||
|
||||
|
@ -215,8 +216,9 @@ namespace MWRender
|
|||
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
|
||||
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
|
||||
// Shadows and radial fog have problems with fixed-function mode
|
||||
bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders") || Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows");
|
||||
resourceSystem->getSceneManager()->setForceShaders(forceShaders);
|
||||
//bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders") || Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows");
|
||||
//resourceSystem->getSceneManager()->setForceShaders(forceShaders);
|
||||
resourceSystem->getSceneManager()->setForceShaders(true);
|
||||
// FIXME: calling dummy method because terrain needs to know whether lighting is clamped
|
||||
resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders"));
|
||||
resourceSystem->getSceneManager()->setAutoUseNormalMaps(Settings::Manager::getBool("auto use object normal maps", "Shaders"));
|
||||
|
@ -383,12 +385,14 @@ namespace MWRender
|
|||
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
|
||||
mStateUpdater->setFogEnd(mViewDistance);
|
||||
|
||||
////// Near far uniforms
|
||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
|
||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
|
||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("simpleWater", false));
|
||||
|
||||
mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near");
|
||||
mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far");
|
||||
|
||||
updateProjectionMatrix();
|
||||
}
|
||||
|
||||
|
|
|
@ -123,6 +123,8 @@ namespace MWRender
|
|||
|
||||
osg::Uniform* mUniformNear;
|
||||
osg::Uniform* mUniformFar;
|
||||
osg::Uniform* mUniformStereoViewOffsets;
|
||||
osg::Uniform* mUniformStereoProjections;
|
||||
|
||||
void preloadCommonAssets();
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/ClipNode>
|
||||
#include <osg/FrontFace>
|
||||
#include <osg/ViewportIndexed>
|
||||
|
||||
#include <osgDB/ReadFile>
|
||||
|
||||
|
@ -29,6 +30,7 @@
|
|||
#include <components/sceneutil/waterutil.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
#include <components/misc/stereo.hpp>
|
||||
|
||||
#include <components/nifosg/controller.hpp>
|
||||
|
||||
|
@ -343,6 +345,7 @@ public:
|
|||
|
||||
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
|
||||
setViewport(0, 0, rttSize, rttSize);
|
||||
|
||||
|
||||
// No need for Update traversal since the mSceneRoot 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)
|
||||
|
@ -596,6 +599,19 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
|
|||
std::map<std::string, std::string> defineMap;
|
||||
defineMap.insert(std::make_pair(std::string("refraction_enabled"), std::string(refraction ? "1" : "0")));
|
||||
|
||||
if (mResourceSystem->getSceneManager()->getShaderManager().stereoGeometryShaderEnabled())
|
||||
{
|
||||
defineMap["geometryShader"] = "1";
|
||||
if (reflection)
|
||||
{
|
||||
Misc::enableStereoForCamera(reflection, true);
|
||||
}
|
||||
if (refraction)
|
||||
{
|
||||
Misc::enableStereoForCamera(refraction, true);
|
||||
}
|
||||
}
|
||||
|
||||
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> fragmentShader (shaderMgr.getShader("water_fragment.glsl", defineMap, osg::Shader::FRAGMENT));
|
||||
|
@ -640,9 +656,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
|
|||
|
||||
shaderStateset->addUniform(mRainIntensityUniform.get());
|
||||
|
||||
osg::ref_ptr<osg::Program> program (new osg::Program);
|
||||
program->addShader(vertexShader);
|
||||
program->addShader(fragmentShader);
|
||||
auto program = shaderMgr.getProgram(vertexShader, fragmentShader);
|
||||
shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);
|
||||
|
||||
node->setStateSet(shaderStateset);
|
||||
|
|
|
@ -87,7 +87,7 @@ add_component_dir (esmterrain
|
|||
)
|
||||
|
||||
add_component_dir (misc
|
||||
constants utf8stream stringops resourcehelpers rng messageformatparser weakcache
|
||||
gcd constants utf8stream stringops resourcehelpers rng messageformatparser weakcache stereo
|
||||
)
|
||||
|
||||
add_component_dir (debug
|
||||
|
|
529
components/misc/stereo.cpp
Normal file
529
components/misc/stereo.cpp
Normal file
|
@ -0,0 +1,529 @@
|
|||
#include "stereo.hpp"
|
||||
#include "stringops.hpp"
|
||||
|
||||
#include <osg/io_utils>
|
||||
#include <osg/ViewportIndexed>
|
||||
|
||||
#include <osgUtil/CullVisitor>
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/sceneutil/statesetupdater.hpp>
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
osg::Matrix Pose::viewMatrix(bool useGLConventions)
|
||||
{
|
||||
if (useGLConventions)
|
||||
{
|
||||
// When applied as an offset to an existing view matrix,
|
||||
// that view matrix will already convert points to a camera space
|
||||
// with opengl conventions. So we need to convert offsets to opengl
|
||||
// conventions.
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::Vec3d forward = orientation * osg::Vec3d(0, 1, 0);
|
||||
osg::Vec3d up = orientation * osg::Vec3d(0, 0, 1);
|
||||
osg::Matrix viewMatrix;
|
||||
viewMatrix.makeLookAt(position, position + forward, up);
|
||||
|
||||
return viewMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
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 View::operator==(const View& rhs) const
|
||||
{
|
||||
return pose == rhs.pose && fov == rhs.fov;
|
||||
}
|
||||
|
||||
std::ostream& operator <<(
|
||||
std::ostream& os,
|
||||
const Pose& pose)
|
||||
{
|
||||
os << "position=" << pose.position << ", orientation=" << pose.orientation;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator <<(
|
||||
std::ostream& os,
|
||||
const FieldOfView& fov)
|
||||
{
|
||||
os << "left=" << fov.angleLeft << ", right=" << fov.angleRight << ", down=" << fov.angleDown << ", up=" << fov.angleUp;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator <<(
|
||||
std::ostream& os,
|
||||
const View& view)
|
||||
{
|
||||
os << "pose=< " << view.pose << " >, fov=< " << view.fov << " >";
|
||||
return os;
|
||||
}
|
||||
|
||||
// Update stereo view/projection during update
|
||||
class StereoUpdateCallback : public osg::Callback
|
||||
{
|
||||
public:
|
||||
StereoUpdateCallback(StereoView* stereoView) : stereoView(stereoView) {}
|
||||
|
||||
bool run(osg::Object* object, osg::Object* data) override
|
||||
{
|
||||
auto b = traverse(object, data);
|
||||
stereoView->update();
|
||||
return b;
|
||||
}
|
||||
|
||||
StereoView* stereoView;
|
||||
};
|
||||
|
||||
// Update states during cull
|
||||
class StereoStatesetUpdateCallback : public SceneUtil::StateSetUpdater
|
||||
{
|
||||
public:
|
||||
StereoStatesetUpdateCallback(StereoView* view)
|
||||
: stereoView(view)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void setDefaults(osg::StateSet* stateset)
|
||||
{
|
||||
auto stereoViewMatrixUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "stereoViewMatrices", 2);
|
||||
stateset->addUniform(stereoViewMatrixUniform, osg::StateAttribute::OVERRIDE);
|
||||
auto stereoViewProjectionsUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "stereoViewProjections", 2);
|
||||
stateset->addUniform(stereoViewProjectionsUniform);
|
||||
auto geometryPassthroughUniform = new osg::Uniform("geometryPassthrough", false);
|
||||
stateset->addUniform(geometryPassthroughUniform);
|
||||
}
|
||||
|
||||
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/)
|
||||
{
|
||||
stereoView->updateStateset(stateset);
|
||||
}
|
||||
|
||||
private:
|
||||
StereoView* stereoView;
|
||||
};
|
||||
|
||||
StereoView::StereoView(osgViewer::Viewer* viewer, Technique technique, osg::Node::NodeMask geometryShaderMask, osg::Node::NodeMask noShaderMask)
|
||||
: osg::Group()
|
||||
, mViewer(viewer)
|
||||
, mMainCamera(mViewer->getCamera())
|
||||
, mRoot(viewer->getSceneData()->asGroup())
|
||||
, mTechnique(technique)
|
||||
, mGeometryShaderMask(geometryShaderMask)
|
||||
, mNoShaderMask(noShaderMask)
|
||||
{
|
||||
if (technique == Technique::None)
|
||||
// Do nothing
|
||||
return;
|
||||
|
||||
SceneUtil::FindByNameVisitor findScene("Scene Root");
|
||||
mRoot->accept(findScene);
|
||||
mScene = findScene.mFoundNode;
|
||||
if (!mScene)
|
||||
throw std::logic_error("Couldn't find scene root");
|
||||
|
||||
setName("Stereo Root");
|
||||
mRoot->setDataVariance(osg::Object::STATIC);
|
||||
setDataVariance(osg::Object::STATIC);
|
||||
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());
|
||||
|
||||
|
||||
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));
|
||||
|
||||
// Inject self as the root of the scene graph
|
||||
mViewer->setSceneData(this);
|
||||
}
|
||||
|
||||
void StereoView::update()
|
||||
{
|
||||
auto viewMatrix = mViewer->getCamera()->getViewMatrix();
|
||||
auto projectionMatrix = mViewer->getCamera()->getProjectionMatrix();
|
||||
|
||||
View left{};
|
||||
View right{};
|
||||
double near = 1.f;
|
||||
double far = 10000.f;
|
||||
if (!cb)
|
||||
{
|
||||
Log(Debug::Error) << "No update view callback. Stereo rendering will not work.";
|
||||
}
|
||||
cb->updateView(left, right, near, far);
|
||||
|
||||
osg::Vec3d leftEye = left.pose.position;
|
||||
osg::Vec3d rightEye = right.pose.position;
|
||||
|
||||
osg::Matrix leftViewOffset = left.pose.viewMatrix(true);
|
||||
osg::Matrix rightViewOffset = right.pose.viewMatrix(true);
|
||||
|
||||
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);
|
||||
|
||||
mRightCamera->setViewMatrix(rightViewMatrix);
|
||||
mLeftCamera->setViewMatrix(leftViewMatrix);
|
||||
mRightCamera->setProjectionMatrix(rightProjectionMatrix);
|
||||
mLeftCamera->setProjectionMatrix(leftProjectionMatrix);
|
||||
|
||||
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.
|
||||
// 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).
|
||||
// Re-think this if that turns out to be a bad assumption.
|
||||
View frustumView;
|
||||
|
||||
// Compute Frustum angles. A simple min/max.
|
||||
/* Example values for reference:
|
||||
Left:
|
||||
angleLeft -0.767549932 float
|
||||
angleRight 0.620896876 float
|
||||
angleDown -0.837898076 float
|
||||
angleUp 0.726982594 float
|
||||
|
||||
Right:
|
||||
angleLeft -0.620896876 float
|
||||
angleRight 0.767549932 float
|
||||
angleDown -0.837898076 float
|
||||
angleUp 0.726982594 float
|
||||
*/
|
||||
frustumView.fov.angleLeft = std::min(left.fov.angleLeft, right.fov.angleLeft);
|
||||
frustumView.fov.angleRight = std::max(left.fov.angleRight, right.fov.angleRight);
|
||||
frustumView.fov.angleDown = std::min(left.fov.angleDown, right.fov.angleDown);
|
||||
frustumView.fov.angleUp = std::max(left.fov.angleUp, right.fov.angleUp);
|
||||
|
||||
// Check that the case works for this approach
|
||||
auto maxAngle = std::max(frustumView.fov.angleRight - frustumView.fov.angleLeft, frustumView.fov.angleUp - frustumView.fov.angleDown);
|
||||
if (maxAngle > osg::PI)
|
||||
{
|
||||
Log(Debug::Error) << "Total FOV exceeds 180 degrees. Case cannot be culled in single-pass VR. Disabling culling to cope. Consider switching to dual-pass VR.";
|
||||
mMainCamera->setCullingActive(false);
|
||||
return;
|
||||
// TODO: An explicit frustum projection could cope, so implement that later. Guarantee you there will be VR headsets with total fov > 180 in the future. Maybe already.
|
||||
}
|
||||
|
||||
// Use the law of sines on the triangle spanning PLR to determine P
|
||||
double angleLeft = std::abs(frustumView.fov.angleLeft);
|
||||
double angleRight = std::abs(frustumView.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 LP = directionLP * lengthLP;
|
||||
frustumView.pose.position = leftEye + LP;
|
||||
//frustumView.pose.position.x() += 1000;
|
||||
|
||||
// Base view position is 0.0, by definition.
|
||||
// The length of the vector P is therefore the required offset to near/far.
|
||||
auto nearFarOffset = frustumView.pose.position.length();
|
||||
|
||||
// Generate the frustum matrices
|
||||
auto frustumViewMatrix = viewMatrix * frustumView.pose.viewMatrix(true);
|
||||
auto frustumProjectionMatrix = frustumView.fov.perspectiveMatrix(near + nearFarOffset, far + nearFarOffset);
|
||||
|
||||
// Update camera with frustum matrices
|
||||
mMainCamera->setViewMatrix(frustumViewMatrix);
|
||||
mMainCamera->setProjectionMatrix(frustumProjectionMatrix);
|
||||
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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
// Manage viewports in update to automatically catch window/resolution changes.
|
||||
auto width = mMainCamera->getViewport()->width();
|
||||
auto height = mMainCamera->getViewport()->height();
|
||||
stateset->setAttribute(new osg::ViewportIndexed(0, 0, 0, width / 2, height));
|
||||
stateset->setAttribute(new osg::ViewportIndexed(1, width / 2, 0, width / 2, height));
|
||||
|
||||
// Update stereo uniforms
|
||||
auto frustumViewMatrixInverse = osg::Matrix::inverse(mMainCamera->getViewMatrix());
|
||||
//auto frustumViewProjectionMatrixInverse = osg::Matrix::inverse(mMainCamera->getProjectionMatrix()) * osg::Matrix::inverse(mMainCamera->getViewMatrix());
|
||||
auto* stereoViewMatrixUniform = stateset->getUniform("stereoViewMatrices");
|
||||
auto* stereoViewProjectionsUniform = stateset->getUniform("stereoViewProjections");
|
||||
|
||||
stereoViewMatrixUniform->setElement(0, frustumViewMatrixInverse * mLeftCamera->getViewMatrix());
|
||||
stereoViewMatrixUniform->setElement(1, frustumViewMatrixInverse * mRightCamera->getViewMatrix());
|
||||
stereoViewProjectionsUniform->setElement(0, frustumViewMatrixInverse * mLeftCamera->getViewMatrix() * mLeftCamera->getProjectionMatrix());
|
||||
stereoViewProjectionsUniform->setElement(1, frustumViewMatrixInverse * mRightCamera->getViewMatrix() * mRightCamera->getProjectionMatrix());
|
||||
}
|
||||
|
||||
void StereoView::setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb)
|
||||
{
|
||||
this->cb = cb;
|
||||
}
|
||||
|
||||
void disableStereoForCamera(osg::Camera* camera)
|
||||
{
|
||||
auto* viewport = camera->getViewport();
|
||||
camera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, viewport->x(), viewport->y(), viewport->width(), viewport->height()), osg::StateAttribute::OVERRIDE);
|
||||
camera->getOrCreateStateSet()->addUniform(new osg::Uniform("geometryPassthrough", true), osg::StateAttribute::OVERRIDE);
|
||||
}
|
||||
|
||||
void enableStereoForCamera(osg::Camera* camera, bool horizontalSplit)
|
||||
{
|
||||
auto* viewport = camera->getViewport();
|
||||
auto x1 = viewport->x();
|
||||
auto y1 = viewport->y();
|
||||
auto width = viewport->width();
|
||||
auto height = viewport->height();
|
||||
|
||||
auto x2 = x1;
|
||||
auto y2 = y1;
|
||||
|
||||
if (horizontalSplit)
|
||||
{
|
||||
width /= 2;
|
||||
x2 += width;
|
||||
}
|
||||
else
|
||||
{
|
||||
height /= 2;
|
||||
y2 += height;
|
||||
}
|
||||
|
||||
camera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, x1, y1, width, height));
|
||||
camera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(1, x2, y2, width, height));
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
142
components/misc/stereo.hpp
Normal file
142
components/misc/stereo.hpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
#ifndef MISC_STEREO_H
|
||||
#define MISC_STEREO_H
|
||||
|
||||
#include <osg/Matrix>
|
||||
#include <osg/Vec3>
|
||||
#include <osg/Camera>
|
||||
#include <osg/StateSet>
|
||||
|
||||
#include <memory>
|
||||
|
||||
// Some cursed headers like to define these
|
||||
#if defined(near) || defined(far)
|
||||
#undef near
|
||||
#undef far
|
||||
#endif
|
||||
|
||||
namespace osgViewer
|
||||
{
|
||||
class Viewer;
|
||||
}
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
//! Represents the relative pose in space of some object
|
||||
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;
|
||||
|
||||
osg::Matrix viewMatrix(bool useGLConventions);
|
||||
};
|
||||
|
||||
//! Fov that defines all 4 angles from center
|
||||
struct FieldOfView {
|
||||
float angleLeft{ -osg::PI_2 };
|
||||
float angleRight{ osg::PI_2 };
|
||||
float angleDown{ -osg::PI_2 };
|
||||
float angleUp{ osg::PI_2 };
|
||||
|
||||
bool operator==(const FieldOfView& rhs) const;
|
||||
|
||||
//! Generate a perspective matrix from this fov
|
||||
osg::Matrix perspectiveMatrix(float near, float far);
|
||||
};
|
||||
|
||||
//! Represents an eye including both pose and fov.
|
||||
struct View
|
||||
{
|
||||
Pose pose;
|
||||
FieldOfView fov;
|
||||
bool operator==(const View& rhs) const;
|
||||
};
|
||||
|
||||
//! Represent two eyes. The eyes are in relative terms, and are assumed to lie on the horizon plane.
|
||||
struct StereoView : public osg::Group
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
//! 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);
|
||||
};
|
||||
|
||||
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.
|
||||
//! 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.
|
||||
//! \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
|
||||
//! with a view and projection that closely envelopes the frustums of the two eyes.
|
||||
void update();
|
||||
void updateStateset(osg::StateSet* stateset);
|
||||
|
||||
//! Callback that updates stereo configuration during the update pass
|
||||
void setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb);
|
||||
|
||||
private:
|
||||
void setupBruteForceTechnique();
|
||||
void setupGeometryShaderIndexedViewportTechnique();
|
||||
|
||||
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
||||
osg::ref_ptr<osg::Camera> mMainCamera;
|
||||
osg::ref_ptr<osg::Group> mRoot;
|
||||
osg::ref_ptr<osg::Group> mScene;
|
||||
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;
|
||||
|
||||
// Keeps state and cameras relevant to doing stereo via brute force
|
||||
osg::ref_ptr<osg::Group> mStereoBruteForceRoot{ new osg::Group };
|
||||
osg::ref_ptr<osg::Camera> mLeftCamera{ new osg::Camera };
|
||||
osg::ref_ptr<osg::Camera> mRightCamera{ new osg::Camera };
|
||||
|
||||
// Camera viewports
|
||||
bool flipViewOrder{ true };
|
||||
|
||||
// Updates stereo configuration during the update pass
|
||||
std::shared_ptr<UpdateViewCallback> cb{ new DefaultUpdateViewCallback };
|
||||
};
|
||||
|
||||
//! Overrides all stereo-related states/uniforms to disable stereo for the scene rendered by camera
|
||||
void disableStereoForCamera(osg::Camera* camera);
|
||||
|
||||
//! Overrides all stereo-related states/uniforms to enable stereo for the scene rendered by camera
|
||||
void enableStereoForCamera(osg::Camera* camera, bool horizontalSplit);
|
||||
|
||||
//! Reads settings to determine stereo technique
|
||||
StereoView::Technique getStereoTechnique(void);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -380,18 +380,30 @@ namespace Resource
|
|||
return false;
|
||||
|
||||
static std::vector<std::string> reservedNames;
|
||||
if (reservedNames.empty())
|
||||
static std::mutex reservedNamesMutex;
|
||||
{
|
||||
const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm",
|
||||
"Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle",
|
||||
"Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "Arrow", "Camera"};
|
||||
std::lock_guard<std::mutex> lock(reservedNamesMutex);
|
||||
if (reservedNames.empty())
|
||||
{
|
||||
// This keeps somehow accessing garbage so i rewrote it using safer types.
|
||||
//const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm",
|
||||
// "Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle",
|
||||
// "Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "Arrow", "Camera"};
|
||||
|
||||
reservedNames = std::vector<std::string>(reserved, reserved + sizeof(reserved)/sizeof(reserved[0]));
|
||||
//reservedNames = std::vector<std::string>(reserved, reserved + sizeof(reserved)/sizeof(const char*));
|
||||
|
||||
for (unsigned int i=0; i<sizeof(reserved)/sizeof(reserved[0]); ++i)
|
||||
reservedNames.push_back(std::string("Tri ") + reserved[i]);
|
||||
//for (unsigned int i=0; i<sizeof(reserved)/sizeof(const char*); ++i)
|
||||
// reservedNames.push_back(std::string("Tri ") + reserved[i]);
|
||||
|
||||
std::sort(reservedNames.begin(), reservedNames.end(), Misc::StringUtils::ciLess);
|
||||
std::vector<std::string> r = { "Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm",
|
||||
"Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle",
|
||||
"Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "Arrow", "Camera" };
|
||||
reservedNames = std::vector<std::string>(r.begin(), r.end());
|
||||
for (auto& reservedName : r)
|
||||
reservedNames.emplace_back(std::string("Tri ") + reservedName);
|
||||
|
||||
std::sort(reservedNames.begin(), reservedNames.end(), Misc::StringUtils::ciLess);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string>::iterator it = Misc::StringUtils::partialBinarySearch(reservedNames.begin(), reservedNames.end(), name);
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include "mwshadowtechnique.hpp"
|
||||
|
||||
#include <components/misc/stereo.hpp>
|
||||
|
||||
#include <osgShadow/ShadowedScene>
|
||||
#include <osg/CullFace>
|
||||
#include <osg/Geometry>
|
||||
|
@ -587,6 +589,9 @@ MWShadowTechnique::ShadowData::ShadowData(MWShadowTechnique::ViewDependentData*
|
|||
// set viewport
|
||||
_camera->setViewport(0,0,textureSize.x(),textureSize.y());
|
||||
|
||||
// Shadow casting should not obey indexed viewports
|
||||
Misc::disableStereoForCamera(_camera);
|
||||
|
||||
|
||||
if (debug)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "sdlgraphicswindow.hpp"
|
||||
|
||||
#include <osgViewer/viewer>
|
||||
|
||||
#include <SDL_video.h>
|
||||
|
||||
namespace SDLUtil
|
||||
|
@ -225,6 +227,23 @@ void GraphicsWindowSDL2::setSyncToVBlank(bool on)
|
|||
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)
|
||||
{
|
||||
if (enable)
|
||||
|
|
|
@ -81,6 +81,9 @@ public:
|
|||
SDL_Window *mWindow;
|
||||
};
|
||||
|
||||
/** Convenience function for finding the context among the main camera or slaves */
|
||||
static osg::GraphicsContext* findContext(osgViewer::View& view);
|
||||
|
||||
private:
|
||||
void setSwapInterval(bool enable);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "sdlinputwrapper.hpp"
|
||||
#include "sdlgraphicswindow.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
@ -213,11 +214,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> v
|
|||
SDL_GetWindowSize(mSDLWindow, &w, &h);
|
||||
int x,y;
|
||||
SDL_GetWindowPosition(mSDLWindow, &x,&y);
|
||||
{
|
||||
auto* gc = mViewer->getCamera()->getGraphicsContext();
|
||||
if (gc)
|
||||
gc->resized(x, y, w, h);
|
||||
}
|
||||
GraphicsWindowSDL2::findContext(*mViewer)->resized(x,y,w,h);
|
||||
|
||||
mViewer->getEventQueue()->windowResize(x,y,w,h);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <regex>
|
||||
|
||||
#include <osg/Program>
|
||||
|
||||
|
@ -15,7 +16,7 @@
|
|||
namespace Shader
|
||||
{
|
||||
|
||||
void ShaderManager::setShaderPath(const std::string &path)
|
||||
void ShaderManager::setShaderPath(const std::string& path)
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
|
@ -138,6 +139,109 @@ namespace Shader
|
|||
return true;
|
||||
}
|
||||
|
||||
struct DeclarationMeta
|
||||
{
|
||||
std::string interpolationType;
|
||||
std::string interfaceKeyword;
|
||||
std::string type;
|
||||
std::string identifier;
|
||||
std::string mangledIdentifier;
|
||||
};
|
||||
|
||||
// Mangle identifiers of the interface declarations of this shader source, updating all identifiers, and returning a list of the declarations for use in generating
|
||||
// a geometry shader.
|
||||
// IN/OUT source: The source to mangle
|
||||
// IN interfaceKeywordPattern: A regular expression matching all interface keywords to look for (e.g. "out|varying" when mangling output variables). Must not contain subexpressions.
|
||||
// IN mangleString: Identifiers are mangled by prepending this string. Must be a valid identifier prefix.
|
||||
// OUT declarations: All mangled declarations are added to this vector. Includes interpolation, interface, and type information as well as both the mangled and unmangled identifier.
|
||||
static void mangleInterface(std::string& source, const std::string& interfaceKeywordPattern, const std::string& mangleString, std::vector<DeclarationMeta>& declarations)
|
||||
{
|
||||
std::string commentPattern = "//.*";
|
||||
std::regex commentRegex(commentPattern);
|
||||
std::string commentlessSource = std::regex_replace(source, commentRegex, "");
|
||||
|
||||
std::string identifierPattern = "[a-zA-Z_][0-9a-zA-Z_]*";
|
||||
std::string declarationPattern = "(centroid|flat)?\\s*\\b(" + interfaceKeywordPattern + ")\\s+(" + identifierPattern + ")\\s+(" + identifierPattern + ")\\s*;";
|
||||
std::regex declarationRegex(declarationPattern);
|
||||
|
||||
std::vector<std::smatch> matches(std::sregex_iterator(commentlessSource.begin(), commentlessSource.end(), declarationRegex), std::sregex_iterator());
|
||||
std::string replacementPattern;
|
||||
for (auto& match : matches)
|
||||
{
|
||||
declarations.emplace_back(DeclarationMeta{ match[1].str(), match[2].str(), match[3].str(), match[4].str(), mangleString + match[4].str() });
|
||||
if (!replacementPattern.empty())
|
||||
replacementPattern += "|";
|
||||
replacementPattern = replacementPattern + "(" + declarations.back().identifier + "\\b)";
|
||||
}
|
||||
|
||||
if (!replacementPattern.empty())
|
||||
{
|
||||
std::regex replacementRegex(replacementPattern);
|
||||
source = std::regex_replace(source, replacementRegex, mangleString + "$&");
|
||||
}
|
||||
}
|
||||
|
||||
static std::string generateGeometryShader(const std::string& geometryTemplate, const std::vector<DeclarationMeta>& declarations)
|
||||
{
|
||||
if (geometryTemplate.empty())
|
||||
return "";
|
||||
|
||||
static std::map<std::string, std::string> overriddenForwardStatements =
|
||||
{
|
||||
{"linearDepth", "linearDepth = gl_Position.z;"},
|
||||
{"euclideanDepth", "euclideanDepth = length(viewPos.xyz);"},
|
||||
{"passViewPos", "passViewPos = viewPos.xyz;"},
|
||||
{
|
||||
"screenCoordsPassthrough",
|
||||
" mat4 scalemat = mat4(0.25, 0.0, 0.0, 0.0,\n"
|
||||
" 0.0, -0.5, 0.0, 0.0,\n"
|
||||
" 0.0, 0.0, 0.5, 0.0,\n"
|
||||
" 0.25, 0.5, 0.5, 1.0);\n"
|
||||
" vec4 texcoordProj = ((scalemat) * (gl_Position));\n"
|
||||
" screenCoordsPassthrough = texcoordProj.xyw;\n"
|
||||
" if(viewport == 1)\n"
|
||||
" screenCoordsPassthrough.x += 0.5 * screenCoordsPassthrough.z;\n"
|
||||
}
|
||||
};
|
||||
|
||||
std::stringstream ssInputDeclarations;
|
||||
std::stringstream ssOutputDeclarations;
|
||||
std::stringstream ssForwardStatements;
|
||||
std::stringstream ssExtraStatements;
|
||||
std::set<std::string> identifiers;
|
||||
for (auto& declaration : declarations)
|
||||
{
|
||||
if (!declaration.interpolationType.empty())
|
||||
{
|
||||
ssInputDeclarations << declaration.interpolationType << " ";
|
||||
ssOutputDeclarations << declaration.interpolationType << " ";
|
||||
}
|
||||
ssInputDeclarations << "in " << declaration.type << " " << declaration.mangledIdentifier << "[3];\n";
|
||||
ssOutputDeclarations << "out " << declaration.type << " " << declaration.identifier << ";\n";
|
||||
|
||||
if (overriddenForwardStatements.count(declaration.identifier) > 0)
|
||||
ssForwardStatements << overriddenForwardStatements[declaration.identifier] << ";\n";
|
||||
else
|
||||
ssForwardStatements << " " << declaration.identifier << " = " << declaration.mangledIdentifier << "[vertex];\n";
|
||||
|
||||
identifiers.insert(declaration.identifier);
|
||||
}
|
||||
|
||||
// passViewPos output is required
|
||||
if (identifiers.find("passViewPos") == identifiers.end())
|
||||
{
|
||||
Log(Debug::Error) << "Vertex shader is missing 'vec3 passViewPos' on its interface. Geometry shader will NOT work.";
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string geometryShader = geometryTemplate;
|
||||
geometryShader = std::regex_replace(geometryShader, std::regex("@INPUTS"), ssInputDeclarations.str());
|
||||
geometryShader = std::regex_replace(geometryShader, std::regex("@OUTPUTS"), ssOutputDeclarations.str());
|
||||
geometryShader = std::regex_replace(geometryShader, std::regex("@FORWARDING"), ssForwardStatements.str());
|
||||
|
||||
return geometryShader;
|
||||
}
|
||||
|
||||
bool parseFors(std::string& source, const std::string& templateName)
|
||||
{
|
||||
const char escapeCharacter = '$';
|
||||
|
@ -176,7 +280,7 @@ namespace Shader
|
|||
std::string list = source.substr(listStart, listEnd - listStart);
|
||||
std::vector<std::string> listElements;
|
||||
if (list != "")
|
||||
Misc::StringUtils::split (list, listElements, ",");
|
||||
Misc::StringUtils::split(list, listElements, ",");
|
||||
|
||||
size_t contentStart = source.find_first_not_of("\n\r", listEnd);
|
||||
size_t contentEnd = source.find("$endforeach", contentStart);
|
||||
|
@ -235,7 +339,7 @@ namespace Shader
|
|||
Log(Debug::Error) << "Shader " << templateName << " error: Unexpected EOF";
|
||||
return false;
|
||||
}
|
||||
std::string define = source.substr(foundPos+1, endPos - (foundPos+1));
|
||||
std::string define = source.substr(foundPos + 1, endPos - (foundPos + 1));
|
||||
ShaderManager::DefineMap::const_iterator defineFound = defines.find(define);
|
||||
ShaderManager::DefineMap::const_iterator globalDefineFound = globalDefines.find(define);
|
||||
if (define == "foreach")
|
||||
|
@ -282,39 +386,17 @@ namespace Shader
|
|||
return true;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Shader> ShaderManager::getShader(const std::string &templateName, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType)
|
||||
osg::ref_ptr<osg::Shader> ShaderManager::getShader(const std::string& templateName, const ShaderManager::DefineMap& defines, osg::Shader::Type shaderType)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
// read the template if we haven't already
|
||||
TemplateMap::iterator templateIt = mShaderTemplates.find(templateName);
|
||||
if (templateIt == mShaderTemplates.end())
|
||||
{
|
||||
boost::filesystem::path path = (boost::filesystem::path(mPath) / templateName);
|
||||
boost::filesystem::ifstream stream;
|
||||
stream.open(path);
|
||||
if (stream.fail())
|
||||
{
|
||||
Log(Debug::Error) << "Failed to open " << path.string();
|
||||
return nullptr;
|
||||
}
|
||||
std::stringstream buffer;
|
||||
buffer << stream.rdbuf();
|
||||
|
||||
// parse includes
|
||||
int fileNumber = 1;
|
||||
std::string source = buffer.str();
|
||||
if (!addLineDirectivesAfterConditionalBlocks(source)
|
||||
|| !parseIncludes(boost::filesystem::path(mPath), source, templateName, fileNumber, {}))
|
||||
return nullptr;
|
||||
|
||||
templateIt = mShaderTemplates.insert(std::make_pair(templateName, source)).first;
|
||||
}
|
||||
|
||||
ShaderMap::iterator shaderIt = mShaders.find(std::make_pair(templateName, defines));
|
||||
if (shaderIt == mShaders.end())
|
||||
{
|
||||
std::string shaderSource = templateIt->second;
|
||||
std::string shaderSource = getTemplateSource(templateName);
|
||||
if (shaderSource.empty())
|
||||
return nullptr;
|
||||
|
||||
if (!parseDefines(shaderSource, defines, mGlobalDefines, templateName) || !parseFors(shaderSource, templateName))
|
||||
{
|
||||
// Add to the cache anyway to avoid logging the same error over and over.
|
||||
|
@ -322,12 +404,32 @@ namespace Shader
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Shader> shader (new osg::Shader(shaderType));
|
||||
shader->setShaderSource(shaderSource);
|
||||
osg::ref_ptr<osg::Shader> shader(new osg::Shader(shaderType));
|
||||
// Assign a unique name to allow the SharedStateManager to compare shaders efficiently
|
||||
static unsigned int counter = 0;
|
||||
shader->setName(std::to_string(counter++));
|
||||
|
||||
if (mGeometryShadersEnabled && defines.count("geometryShader") && defines.find("geometryShader")->second == "1" && shaderType == osg::Shader::VERTEX)
|
||||
{
|
||||
std::vector<DeclarationMeta> declarations;
|
||||
mangleInterface(shaderSource, "out|varying", "vertex_", declarations);
|
||||
std::string geometryTemplate = getTemplateSource("stereo_geometry.glsl");
|
||||
std::string geometryShaderSource = generateGeometryShader(geometryTemplate, declarations);
|
||||
if (!geometryShaderSource.empty())
|
||||
{
|
||||
osg::ref_ptr<osg::Shader> geometryShader(new osg::Shader(osg::Shader::GEOMETRY));
|
||||
geometryShader->setShaderSource(geometryShaderSource);
|
||||
geometryShader->setName(shader->getName() + ".geom");
|
||||
mGeometryShaders[shader] = geometryShader;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(Debug::Error) << "Failed to generate geometry shader for " << templateName;
|
||||
}
|
||||
}
|
||||
|
||||
shader->setShaderSource(shaderSource);
|
||||
|
||||
shaderIt = mShaders.insert(std::make_pair(std::make_pair(templateName, defines), shader)).first;
|
||||
}
|
||||
return shaderIt->second;
|
||||
|
@ -339,9 +441,16 @@ namespace Shader
|
|||
ProgramMap::iterator found = mPrograms.find(std::make_pair(vertexShader, fragmentShader));
|
||||
if (found == mPrograms.end())
|
||||
{
|
||||
osg::ref_ptr<osg::Program> program (new osg::Program);
|
||||
osg::ref_ptr<osg::Program> program(new osg::Program);
|
||||
program->addShader(vertexShader);
|
||||
program->addShader(fragmentShader);
|
||||
|
||||
auto git = mGeometryShaders.find(vertexShader);
|
||||
if (git != mGeometryShaders.end())
|
||||
{
|
||||
program->addShader(git->second);
|
||||
}
|
||||
|
||||
found = mPrograms.insert(std::make_pair(std::make_pair(vertexShader, fragmentShader), program)).first;
|
||||
}
|
||||
return found->second;
|
||||
|
@ -352,10 +461,20 @@ namespace Shader
|
|||
return DefineMap(mGlobalDefines);
|
||||
}
|
||||
|
||||
void ShaderManager::setGlobalDefines(DefineMap & globalDefines)
|
||||
void ShaderManager::setStereoGeometryShaderEnabled(bool enabled)
|
||||
{
|
||||
mGeometryShadersEnabled = enabled;
|
||||
}
|
||||
|
||||
bool ShaderManager::stereoGeometryShaderEnabled() const
|
||||
{
|
||||
return mGeometryShadersEnabled;
|
||||
}
|
||||
|
||||
void ShaderManager::setGlobalDefines(DefineMap& globalDefines)
|
||||
{
|
||||
mGlobalDefines = globalDefines;
|
||||
for (auto shaderMapElement: mShaders)
|
||||
for (auto shaderMapElement : mShaders)
|
||||
{
|
||||
std::string templateId = shaderMapElement.first.first;
|
||||
ShaderManager::DefineMap defines = shaderMapElement.first.second;
|
||||
|
@ -372,7 +491,7 @@ namespace Shader
|
|||
}
|
||||
}
|
||||
|
||||
void ShaderManager::releaseGLObjects(osg::State *state)
|
||||
void ShaderManager::releaseGLObjects(osg::State* state)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
for (auto shader : mShaders)
|
||||
|
@ -384,4 +503,33 @@ namespace Shader
|
|||
program.second->releaseGLObjects(state);
|
||||
}
|
||||
|
||||
std::string ShaderManager::getTemplateSource(const std::string& templateName)
|
||||
{
|
||||
// read the template if we haven't already
|
||||
TemplateMap::iterator templateIt = mShaderTemplates.find(templateName);
|
||||
if (templateIt == mShaderTemplates.end())
|
||||
{
|
||||
boost::filesystem::path path = (boost::filesystem::path(mPath) / templateName);
|
||||
boost::filesystem::ifstream stream;
|
||||
stream.open(path);
|
||||
if (stream.fail())
|
||||
{
|
||||
Log(Debug::Error) << "Failed to open " << path.string();
|
||||
return std::string();
|
||||
}
|
||||
std::stringstream buffer;
|
||||
buffer << stream.rdbuf();
|
||||
|
||||
// parse includes
|
||||
int fileNumber = 1;
|
||||
std::string source = buffer.str();
|
||||
if (!addLineDirectivesAfterConditionalBlocks(source)
|
||||
|| !parseIncludes(boost::filesystem::path(mPath), source, templateName, fileNumber, {}))
|
||||
return std::string();
|
||||
|
||||
templateIt = mShaderTemplates.insert(std::make_pair(templateName, source)).first;
|
||||
}
|
||||
return templateIt->second;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,6 +36,14 @@ namespace Shader
|
|||
/// Get (a copy of) the DefineMap used to construct all shaders
|
||||
DefineMap getGlobalDefines();
|
||||
|
||||
/// Enable or disable automatic stereo geometry shader.
|
||||
/// If enabled, a stereo geometry shader will be automatically generated for any vertex shader
|
||||
/// whose defines include "geometryShader" set to "1".
|
||||
/// 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.
|
||||
void setStereoGeometryShaderEnabled(bool enabled);
|
||||
bool stereoGeometryShaderEnabled() const;
|
||||
|
||||
/// Set the DefineMap used to construct all shaders
|
||||
/// @param defines The DefineMap to use
|
||||
/// @note This will change the source code for any shaders already created, potentially causing problems if they're being used to render a frame. It is recommended that any associated Viewers have their threading stopped while this function is running if any shaders are in use.
|
||||
|
@ -44,6 +52,8 @@ namespace Shader
|
|||
void releaseGLObjects(osg::State* state);
|
||||
|
||||
private:
|
||||
std::string getTemplateSource(const std::string& templateName);
|
||||
|
||||
std::string mPath;
|
||||
|
||||
DefineMap mGlobalDefines;
|
||||
|
@ -56,6 +66,10 @@ namespace Shader
|
|||
typedef std::map<MapKey, osg::ref_ptr<osg::Shader> > ShaderMap;
|
||||
ShaderMap mShaders;
|
||||
|
||||
typedef std::map<osg::ref_ptr<osg::Shader>, osg::ref_ptr<osg::Shader> > GeometryShaderMap;
|
||||
GeometryShaderMap mGeometryShaders;
|
||||
bool mGeometryShadersEnabled{ false };
|
||||
|
||||
typedef std::map<std::pair<osg::ref_ptr<osg::Shader>, osg::ref_ptr<osg::Shader> >, osg::ref_ptr<osg::Program> > ProgramMap;
|
||||
ProgramMap mPrograms;
|
||||
|
||||
|
|
|
@ -319,6 +319,7 @@ namespace Shader
|
|||
}
|
||||
|
||||
defineMap["parallax"] = reqs.mNormalHeight ? "1" : "0";
|
||||
defineMap["geometryShader"] = "1";
|
||||
|
||||
writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode));
|
||||
|
||||
|
|
|
@ -232,6 +232,7 @@ namespace Terrain
|
|||
defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
|
||||
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
|
||||
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
|
||||
defineMap["geometryShader"] = "1";
|
||||
|
||||
osg::ref_ptr<osg::Shader> vertexShader = shaderManager->getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX);
|
||||
osg::ref_ptr<osg::Shader> fragmentShader = shaderManager->getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
|
||||
|
|
|
@ -932,6 +932,17 @@ 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.
|
||||
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
|
||||
|
||||
[Physics]
|
||||
# Set the number of background threads used for physics.
|
||||
# If no background threads are used, physics calculations are processed in the main thread
|
||||
|
|
|
@ -22,6 +22,7 @@ set(SHADER_FILES
|
|||
shadows_fragment.glsl
|
||||
shadowcasting_vertex.glsl
|
||||
shadowcasting_fragment.glsl
|
||||
stereo_geometry.glsl
|
||||
)
|
||||
|
||||
copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}")
|
||||
|
|
58
files/shaders/stereo_geometry.glsl
Normal file
58
files/shaders/stereo_geometry.glsl
Normal file
|
@ -0,0 +1,58 @@
|
|||
#version 150 compatibility
|
||||
#extension GL_ARB_viewport_array : require
|
||||
//#ifdef GL_ARB_gpu_shader5 // Ref: AnyOldName3: This slightly faster path is broken on Vega 56
|
||||
#if 0
|
||||
#extension GL_ARB_gpu_shader5 : enable
|
||||
#define ENABLE_GL_ARB_gpu_shader5
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_GL_ARB_gpu_shader5
|
||||
layout (triangles, invocations = 2) in;
|
||||
layout (triangle_strip, max_vertices = 3) out;
|
||||
#else
|
||||
layout (triangles) in;
|
||||
layout (triangle_strip, max_vertices = 6) out;
|
||||
#endif
|
||||
|
||||
// Geometry Shader Inputs
|
||||
@INPUTS
|
||||
|
||||
// Geometry Shader Outputs
|
||||
@OUTPUTS
|
||||
|
||||
// Stereo matrices
|
||||
uniform mat4 stereoViewMatrices[2];
|
||||
uniform mat4 stereoViewProjections[2];
|
||||
|
||||
void perVertex(int vertex, int viewport)
|
||||
{
|
||||
gl_ViewportIndex = viewport;
|
||||
// Re-project
|
||||
gl_Position = stereoViewProjections[viewport] * vec4(vertex_passViewPos[vertex],1);
|
||||
vec4 viewPos = stereoViewMatrices[viewport] * vec4(vertex_passViewPos[vertex],1);
|
||||
gl_ClipVertex = vec4(viewPos.xyz,1);
|
||||
|
||||
// Input -> output
|
||||
@FORWARDING
|
||||
|
||||
EmitVertex();
|
||||
}
|
||||
|
||||
void perViewport(int viewport)
|
||||
{
|
||||
for(int vertex = 0; vertex < gl_in.length(); vertex++)
|
||||
{
|
||||
perVertex(vertex, viewport);
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef ENABLE_GL_ARB_gpu_shader5
|
||||
int viewport = gl_InvocationID;
|
||||
#else
|
||||
for(int viewport = 0; viewport < 2; viewport++)
|
||||
#endif
|
||||
perViewport(viewport);
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
#version 120
|
||||
#version 130
|
||||
|
||||
varying vec2 uv;
|
||||
varying float euclideanDepth;
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
varying vec3 screenCoordsPassthrough;
|
||||
varying vec4 position;
|
||||
varying float linearDepth;
|
||||
varying vec3 passViewPos;
|
||||
|
||||
#include "shadows_vertex.glsl"
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
vec4 viewPos = gl_ModelViewMatrix * gl_Vertex;
|
||||
passViewPos = viewPos.xyz;
|
||||
|
||||
mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0,
|
||||
0.0, -0.5, 0.0, 0.0,
|
||||
|
@ -22,5 +25,5 @@ void main(void)
|
|||
|
||||
linearDepth = gl_Position.z;
|
||||
|
||||
setupShadowCoords(gl_ModelViewMatrix * gl_Vertex, normalize((gl_NormalMatrix * gl_Normal).xyz));
|
||||
setupShadowCoords(viewPos, normalize((gl_NormalMatrix * gl_Normal).xyz));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue