mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-21 11:23:51 +00:00
Geometry shader stereo functional
This commit is contained in:
parent
b3f2373875
commit
9c171869cb
22 changed files with 925 additions and 201 deletions
|
@ -23,12 +23,16 @@
|
||||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||||
#include <components/sdlutil/imagetosurface.hpp>
|
#include <components/sdlutil/imagetosurface.hpp>
|
||||||
|
|
||||||
|
#include <components/shader/shadermanager.hpp>
|
||||||
|
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/resource/stats.hpp>
|
#include <components/resource/stats.hpp>
|
||||||
|
|
||||||
#include <components/compiler/extensions0.hpp>
|
#include <components/compiler/extensions0.hpp>
|
||||||
|
|
||||||
|
#include <components/misc/stereo.hpp>
|
||||||
|
|
||||||
#include <components/sceneutil/workqueue.hpp>
|
#include <components/sceneutil/workqueue.hpp>
|
||||||
|
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
@ -692,12 +696,31 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
window->playVideo(logo, true);
|
window->playVideo(logo, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mStereoEnabled = true; //!< TODO: TEMP
|
||||||
|
|
||||||
|
// 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().enableGeometryShader(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the world
|
// Create the world
|
||||||
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
|
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
|
||||||
mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName,
|
mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName,
|
||||||
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
|
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
|
||||||
mEnvironment.getWorld()->setupPlayer();
|
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.reset(new Misc::StereoView(mViewer, geometryShaderMask, noShaderMask | MWRender::VisMask::Mask_Scene));
|
||||||
|
}
|
||||||
|
|
||||||
window->setStore(mEnvironment.getWorld()->getStore());
|
window->setStore(mEnvironment.getWorld()->getStore());
|
||||||
window->initUI();
|
window->initUI();
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,11 @@ namespace Compiler
|
||||||
class Context;
|
class Context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Misc
|
||||||
|
{
|
||||||
|
class StereoView;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWScript
|
namespace MWScript
|
||||||
{
|
{
|
||||||
class ScriptManager;
|
class ScriptManager;
|
||||||
|
@ -85,6 +90,10 @@ namespace OMW
|
||||||
osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;
|
osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;
|
||||||
std::string mCellName;
|
std::string mCellName;
|
||||||
std::vector<std::string> mContentFiles;
|
std::vector<std::string> mContentFiles;
|
||||||
|
|
||||||
|
bool mStereoEnabled;
|
||||||
|
std::unique_ptr<Misc::StereoView> mStereoView;
|
||||||
|
|
||||||
bool mSkipMenu;
|
bool mSkipMenu;
|
||||||
bool mUseSound;
|
bool mUseSound;
|
||||||
bool mCompileAll;
|
bool mCompileAll;
|
||||||
|
|
|
@ -372,20 +372,6 @@ namespace MWRender
|
||||||
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
|
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
|
||||||
mStateUpdater->setFogEnd(mViewDistance);
|
mStateUpdater->setFogEnd(mViewDistance);
|
||||||
|
|
||||||
////// Indexed viewports and related uniforms
|
|
||||||
sceneRoot->getOrCreateStateSet()->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "stereoViewOffsets", 2));
|
|
||||||
sceneRoot->getOrCreateStateSet()->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "stereoProjections", 2));
|
|
||||||
mUniformStereoViewOffsets = sceneRoot->getOrCreateStateSet()->getUniform("stereoViewOffsets");
|
|
||||||
mUniformStereoProjections = sceneRoot->getOrCreateStateSet()->getUniform("stereoProjections");
|
|
||||||
mUniformStereoViewOffsets->setElement(0, osg::Matrix::identity());
|
|
||||||
mUniformStereoViewOffsets->setElement(1, osg::Matrix::identity());
|
|
||||||
|
|
||||||
auto width = mViewer->getCamera()->getViewport()->width();
|
|
||||||
auto height = mViewer->getCamera()->getViewport()->height();
|
|
||||||
|
|
||||||
sceneRoot->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, 0, 0, width / 2, height));
|
|
||||||
sceneRoot->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(1, width / 2, 0, width / 2, height));
|
|
||||||
|
|
||||||
////// Near far uniforms
|
////// Near far uniforms
|
||||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
|
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
|
||||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
|
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
|
||||||
|
@ -1243,9 +1229,6 @@ namespace MWRender
|
||||||
fov = mFieldOfViewOverride;
|
fov = mFieldOfViewOverride;
|
||||||
mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance);
|
mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance);
|
||||||
|
|
||||||
mUniformStereoProjections->setElement(0, mViewer->getCamera()->getProjectionMatrix());
|
|
||||||
mUniformStereoProjections->setElement(1, mViewer->getCamera()->getProjectionMatrix());
|
|
||||||
|
|
||||||
mUniformNear->set(mNearClip);
|
mUniformNear->set(mNearClip);
|
||||||
mUniformFar->set(mViewDistance);
|
mUniformFar->set(mViewDistance);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <osg/PositionAttitudeTransform>
|
#include <osg/PositionAttitudeTransform>
|
||||||
#include <osg/ClipNode>
|
#include <osg/ClipNode>
|
||||||
#include <osg/FrontFace>
|
#include <osg/FrontFace>
|
||||||
|
#include <osg/ViewportIndexed>
|
||||||
|
|
||||||
#include <osgDB/ReadFile>
|
#include <osgDB/ReadFile>
|
||||||
|
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
#include <components/sceneutil/waterutil.hpp>
|
#include <components/sceneutil/waterutil.hpp>
|
||||||
|
|
||||||
#include <components/misc/constants.hpp>
|
#include <components/misc/constants.hpp>
|
||||||
|
#include <components/misc/stereo.hpp>
|
||||||
|
|
||||||
#include <components/nifosg/controller.hpp>
|
#include <components/nifosg/controller.hpp>
|
||||||
|
|
||||||
|
@ -246,6 +248,7 @@ 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)
|
||||||
|
@ -341,6 +344,8 @@ 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
|
||||||
// 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)
|
||||||
|
@ -443,6 +448,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
|
||||||
mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900);
|
mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900);
|
||||||
mWaterGeom->setDrawCallback(new DepthClampCallback);
|
mWaterGeom->setDrawCallback(new DepthClampCallback);
|
||||||
mWaterGeom->setNodeMask(Mask_Water);
|
mWaterGeom->setNodeMask(Mask_Water);
|
||||||
|
mWaterGeom->setDataVariance(osg::Object::STATIC);
|
||||||
|
|
||||||
mWaterNode = new osg::PositionAttitudeTransform;
|
mWaterNode = new osg::PositionAttitudeTransform;
|
||||||
mWaterNode->setName("Water Root");
|
mWaterNode->setName("Water Root");
|
||||||
|
@ -592,6 +598,7 @@ 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")));
|
||||||
|
defineMap["geometryShader"] = "1";
|
||||||
|
|
||||||
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));
|
||||||
|
@ -637,9 +644,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
|
||||||
|
|
||||||
shaderStateset->addUniform(mRainIntensityUniform.get());
|
shaderStateset->addUniform(mRainIntensityUniform.get());
|
||||||
|
|
||||||
osg::ref_ptr<osg::Program> program (new osg::Program);
|
auto program = shaderMgr.getProgram(vertexShader, fragmentShader);
|
||||||
program->addShader(vertexShader);
|
|
||||||
program->addShader(fragmentShader);
|
|
||||||
shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);
|
shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);
|
||||||
|
|
||||||
node->setStateSet(shaderStateset);
|
node->setStateSet(shaderStateset);
|
||||||
|
|
|
@ -86,7 +86,7 @@ add_component_dir (esmterrain
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (misc
|
add_component_dir (misc
|
||||||
gcd constants utf8stream stringops resourcehelpers rng messageformatparser weakcache
|
gcd constants utf8stream stringops resourcehelpers rng messageformatparser weakcache stereo
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (debug
|
add_component_dir (debug
|
||||||
|
|
463
components/misc/stereo.cpp
Normal file
463
components/misc/stereo.cpp
Normal file
|
@ -0,0 +1,463 @@
|
||||||
|
#include "stereo.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>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Why are you like this
|
||||||
|
class TestCullCallback : public osg::NodeCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TestCullCallback() {}
|
||||||
|
|
||||||
|
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
//Log(Debug::Verbose) << "Cull: " << node->getName();
|
||||||
|
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
|
||||||
|
traverse(node, nv);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class StereoUpdateCallback : public osg::Callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StereoUpdateCallback(StereoView* node) : mNode(node) {}
|
||||||
|
|
||||||
|
bool run(osg::Object* object, osg::Object* data) override
|
||||||
|
{
|
||||||
|
//Log(Debug::Verbose) << "StereoUpdateCallback";
|
||||||
|
auto b = traverse(object, data);
|
||||||
|
//mNode->update();
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
StereoView* mNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StereoUpdater : public SceneUtil::StateSetUpdater
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StereoUpdater(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->update(stateset);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
StereoView* stereoView;
|
||||||
|
};
|
||||||
|
|
||||||
|
StereoView::StereoView(osgViewer::Viewer* viewer, osg::Node::NodeMask geometryShaderMask, osg::Node::NodeMask bruteForceMask)
|
||||||
|
: osg::Group()
|
||||||
|
, mViewer(viewer)
|
||||||
|
, mMainCamera(mViewer->getCamera())
|
||||||
|
, mRoot(viewer->getSceneData()->asGroup())
|
||||||
|
, mGeometryShaderMask(geometryShaderMask)
|
||||||
|
, mBruteForceMask(bruteForceMask)
|
||||||
|
{
|
||||||
|
SceneUtil::FindByNameVisitor findScene("Scene Root");
|
||||||
|
mRoot->accept(findScene);
|
||||||
|
mScene = findScene.mFoundNode;
|
||||||
|
if (!mScene)
|
||||||
|
throw std::logic_error("Couldn't find scene root");
|
||||||
|
|
||||||
|
setName("Sky 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->setRenderOrder(osg::Camera::NESTED_RENDER);
|
||||||
|
mLeftCamera->setClearMask(GL_NONE);
|
||||||
|
mLeftCamera->setCullMask(bruteForceMask);
|
||||||
|
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->setRenderOrder(osg::Camera::NESTED_RENDER);
|
||||||
|
mRightCamera->setClearMask(GL_NONE);
|
||||||
|
mRightCamera->setCullMask(bruteForceMask);
|
||||||
|
mRightCamera->setName("Stereo Right");
|
||||||
|
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);
|
||||||
|
addCullCallback(new StereoUpdater(this));
|
||||||
|
|
||||||
|
// Do a blank double buffering of camera statesets on update. StereoView::Update() apply actual changes during cull;
|
||||||
|
mLeftCamera->setUpdateCallback(new SceneUtil::StateSetUpdater());
|
||||||
|
mRightCamera->setUpdateCallback(new SceneUtil::StateSetUpdater());
|
||||||
|
}
|
||||||
|
|
||||||
|
void StereoView::update(osg::StateSet* stateset)
|
||||||
|
{
|
||||||
|
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(leftViewMatrix);
|
||||||
|
mLeftCamera->setViewMatrix(rightViewMatrix);
|
||||||
|
mRightCamera->setProjectionMatrix(leftProjectionMatrix);
|
||||||
|
mLeftCamera->setProjectionMatrix(rightProjectionMatrix);
|
||||||
|
|
||||||
|
// Manage viewports in update to automatically catch window/resolution changes.
|
||||||
|
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);
|
||||||
|
mRightCamera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, width / 2, 0, width / 2, height), osg::StateAttribute::OVERRIDE);
|
||||||
|
stateset->setAttribute(new osg::ViewportIndexed(0, 0, 0, width / 2, height));
|
||||||
|
stateset->setAttribute(new osg::ViewportIndexed(1, width / 2, 0, width / 2, height));
|
||||||
|
|
||||||
|
// The persepctive frustum will be computed 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;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
auto frustumViewMatrixInverse = osg::Matrix::inverse(projectionMatrix) * osg::Matrix::inverse(viewMatrix);
|
||||||
|
|
||||||
|
// Update camera with frustum matrices
|
||||||
|
mMainCamera->setViewMatrix(frustumViewMatrix);
|
||||||
|
mMainCamera->setProjectionMatrix(frustumProjectionMatrix);
|
||||||
|
|
||||||
|
mStereoGeometryShaderRoot->setStateSet(stateset);
|
||||||
|
|
||||||
|
|
||||||
|
// Create and/or update stereo uniforms
|
||||||
|
auto* stereoViewMatrixUniform = stateset->getUniform("stereoViewMatrices");
|
||||||
|
auto* stereoViewProjectionsUniform = stateset->getUniform("stereoViewProjections");
|
||||||
|
|
||||||
|
stereoViewMatrixUniform->setElement(1, frustumViewMatrixInverse * leftViewMatrix);
|
||||||
|
stereoViewMatrixUniform->setElement(0, frustumViewMatrixInverse * rightViewMatrix);
|
||||||
|
stereoViewProjectionsUniform->setElement(1, frustumViewMatrixInverse * rightViewMatrix * leftProjectionMatrix);
|
||||||
|
stereoViewProjectionsUniform->setElement(0, frustumViewMatrixInverse * rightViewMatrix * rightProjectionMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StereoView::setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> 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)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
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 = 10000;
|
||||||
|
}
|
||||||
|
}
|
126
components/misc/stereo.hpp
Normal file
126
components/misc/stereo.hpp
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
#ifndef MISC_STEREO_H
|
||||||
|
#define MISC_STEREO_H
|
||||||
|
|
||||||
|
#include <osg/Matrix>
|
||||||
|
#include <osg/Vec3>
|
||||||
|
#include <osg/Camera>
|
||||||
|
#include <osg/StateSet>
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! 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.
|
||||||
|
//! \note The mask is removed from the mainCamera, so do not put Scene in this mask.
|
||||||
|
//! \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.
|
||||||
|
StereoView(osgViewer::Viewer* viewer, osg::Node::NodeMask geometryShaderMask, osg::Node::NodeMask bruteForceMask);
|
||||||
|
|
||||||
|
//! 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(osg::StateSet* stateset);
|
||||||
|
|
||||||
|
//! Callback that updates stereo configuration during the update pass
|
||||||
|
void setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb);
|
||||||
|
|
||||||
|
//! Use the slave camera at index instead of the main viewer camera.
|
||||||
|
void useSlaveCameraAtIndex(int index);
|
||||||
|
|
||||||
|
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
||||||
|
osg::ref_ptr<osg::Camera> mMainCamera;
|
||||||
|
osg::ref_ptr<osg::Group> mRoot;
|
||||||
|
osg::ref_ptr<osg::Group> mScene;
|
||||||
|
|
||||||
|
// Keeps state relevant to doing stereo via the geometry shader
|
||||||
|
osg::ref_ptr<osg::Group> mStereoGeometryShaderRoot{ new osg::Group };
|
||||||
|
osg::Node::NodeMask mGeometryShaderMask;
|
||||||
|
|
||||||
|
// Keeps state and cameras relevant to doing stereo via brute force
|
||||||
|
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> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -382,18 +382,30 @@ namespace Resource
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
static std::vector<std::string> reservedNames;
|
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",
|
std::lock_guard<std::mutex> lock(reservedNamesMutex);
|
||||||
"Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle",
|
if (reservedNames.empty())
|
||||||
"Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "Arrow", "Camera"};
|
{
|
||||||
|
// 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)
|
//for (unsigned int i=0; i<sizeof(reserved)/sizeof(const char*); ++i)
|
||||||
reservedNames.push_back(std::string("Tri ") + reserved[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);
|
std::vector<std::string>::iterator it = Misc::StringUtils::partialBinarySearch(reservedNames.begin(), reservedNames.end(), name);
|
||||||
|
@ -763,7 +775,7 @@ namespace Resource
|
||||||
|
|
||||||
Shader::ShaderVisitor *SceneManager::createShaderVisitor()
|
Shader::ShaderVisitor *SceneManager::createShaderVisitor()
|
||||||
{
|
{
|
||||||
Shader::ShaderVisitor* shaderVisitor = new Shader::ShaderVisitor(*mShaderManager.get(), *mImageManager, "objects_vertex.glsl", "objects_fragment.glsl", "objects_geometry.glsl");
|
Shader::ShaderVisitor* shaderVisitor = new Shader::ShaderVisitor(*mShaderManager.get(), *mImageManager, "objects_vertex.glsl", "objects_fragment.glsl");
|
||||||
shaderVisitor->setForceShaders(mForceShaders);
|
shaderVisitor->setForceShaders(mForceShaders);
|
||||||
shaderVisitor->setAutoUseNormalMaps(mAutoUseNormalMaps);
|
shaderVisitor->setAutoUseNormalMaps(mAutoUseNormalMaps);
|
||||||
shaderVisitor->setNormalMapPattern(mNormalMapPattern);
|
shaderVisitor->setNormalMapPattern(mNormalMapPattern);
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include "mwshadowtechnique.hpp"
|
#include "mwshadowtechnique.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/stereo.hpp>
|
||||||
|
|
||||||
#include <osgShadow/ShadowedScene>
|
#include <osgShadow/ShadowedScene>
|
||||||
#include <osg/CullFace>
|
#include <osg/CullFace>
|
||||||
#include <osg/Geometry>
|
#include <osg/Geometry>
|
||||||
|
@ -575,6 +577,9 @@ MWShadowTechnique::ShadowData::ShadowData(MWShadowTechnique::ViewDependentData*
|
||||||
// set viewport
|
// set viewport
|
||||||
_camera->setViewport(0,0,textureSize.x(),textureSize.y());
|
_camera->setViewport(0,0,textureSize.x(),textureSize.y());
|
||||||
|
|
||||||
|
// Shadow casting should not obey indexed viewports
|
||||||
|
Misc::disableStereoForCamera(_camera);
|
||||||
|
|
||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
#include <osg/Program>
|
#include <osg/Program>
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@
|
||||||
namespace Shader
|
namespace Shader
|
||||||
{
|
{
|
||||||
|
|
||||||
void ShaderManager::setShaderPath(const std::string &path)
|
void ShaderManager::setShaderPath(const std::string& path)
|
||||||
{
|
{
|
||||||
mPath = path;
|
mPath = path;
|
||||||
}
|
}
|
||||||
|
@ -34,6 +35,9 @@ namespace Shader
|
||||||
foundPos = source.find_first_of("\n\r", foundPos);
|
foundPos = source.find_first_of("\n\r", foundPos);
|
||||||
foundPos = source.find_first_not_of("\n\r", foundPos);
|
foundPos = source.find_first_not_of("\n\r", foundPos);
|
||||||
|
|
||||||
|
if (foundPos == std::string::npos)
|
||||||
|
break;
|
||||||
|
|
||||||
size_t lineDirectivePosition = source.rfind("#line", foundPos);
|
size_t lineDirectivePosition = source.rfind("#line", foundPos);
|
||||||
int lineNumber;
|
int lineNumber;
|
||||||
if (lineDirectivePosition != std::string::npos)
|
if (lineDirectivePosition != std::string::npos)
|
||||||
|
@ -58,45 +62,39 @@ namespace Shader
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& templateName)
|
// Recursively replaces include statements with the actual source of the included files.
|
||||||
|
// Adjusts #line statements accordingly and detects cyclic includes.
|
||||||
|
// includingFiles is the set of files that include this file directly or indirectly, and is intentionally not a reference to allow automatic cleanup.
|
||||||
|
static bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& fileName, int& fileNumber, std::set<boost::filesystem::path> includingFiles)
|
||||||
{
|
{
|
||||||
|
// An include is cyclic if it is being included by itself
|
||||||
|
if (includingFiles.insert(shaderPath / fileName).second == false)
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "Shader " << fileName << " error: Detected cyclic #includes";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Misc::StringUtils::replaceAll(source, "\r\n", "\n");
|
Misc::StringUtils::replaceAll(source, "\r\n", "\n");
|
||||||
|
|
||||||
std::set<boost::filesystem::path> includedFiles;
|
|
||||||
size_t foundPos = 0;
|
size_t foundPos = 0;
|
||||||
int fileNumber = 1;
|
|
||||||
while ((foundPos = source.find("#include")) != std::string::npos)
|
while ((foundPos = source.find("#include")) != std::string::npos)
|
||||||
{
|
{
|
||||||
size_t start = source.find('"', foundPos);
|
size_t start = source.find('"', foundPos);
|
||||||
if (start == std::string::npos || start == source.size()-1)
|
if (start == std::string::npos || start == source.size() - 1)
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "Shader " << templateName << " error: Invalid #include";
|
Log(Debug::Error) << "Shader " << fileName << " error: Invalid #include";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
size_t end = source.find('"', start+1);
|
size_t end = source.find('"', start + 1);
|
||||||
if (end == std::string::npos)
|
if (end == std::string::npos)
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "Shader " << templateName << " error: Invalid #include";
|
Log(Debug::Error) << "Shader " << fileName << " error: Invalid #include";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::string includeFilename = source.substr(start+1, end-(start+1));
|
std::string includeFilename = source.substr(start + 1, end - (start + 1));
|
||||||
boost::filesystem::path includePath = shaderPath / includeFilename;
|
boost::filesystem::path includePath = shaderPath / includeFilename;
|
||||||
boost::filesystem::ifstream includeFstream;
|
|
||||||
includeFstream.open(includePath);
|
|
||||||
if (includeFstream.fail())
|
|
||||||
{
|
|
||||||
Log(Debug::Error) << "Shader " << templateName << " error: Failed to open include " << includePath.string();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream buffer;
|
|
||||||
buffer << includeFstream.rdbuf();
|
|
||||||
std::string stringRepresentation = buffer.str();
|
|
||||||
addLineDirectivesAfterConditionalBlocks(stringRepresentation);
|
|
||||||
|
|
||||||
// insert #line directives so we get correct line numbers in compiler errors
|
|
||||||
int includedFileNumber = fileNumber++;
|
|
||||||
|
|
||||||
|
// Determine the line number that will be used for the #line directive following the included source
|
||||||
size_t lineDirectivePosition = source.rfind("#line", foundPos);
|
size_t lineDirectivePosition = source.rfind("#line", foundPos);
|
||||||
int lineNumber;
|
int lineNumber;
|
||||||
if (lineDirectivePosition != std::string::npos)
|
if (lineDirectivePosition != std::string::npos)
|
||||||
|
@ -113,20 +111,182 @@ namespace Shader
|
||||||
}
|
}
|
||||||
lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + foundPos, '\n');
|
lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + foundPos, '\n');
|
||||||
|
|
||||||
|
// Include the file recursively
|
||||||
|
boost::filesystem::ifstream includeFstream;
|
||||||
|
includeFstream.open(includePath);
|
||||||
|
if (includeFstream.fail())
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "Shader " << fileName << " error: Failed to open include " << includePath.string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int includedFileNumber = fileNumber++;
|
||||||
|
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << includeFstream.rdbuf();
|
||||||
|
std::string stringRepresentation = buffer.str();
|
||||||
|
if (!addLineDirectivesAfterConditionalBlocks(stringRepresentation)
|
||||||
|
|| !parseIncludes(shaderPath, stringRepresentation, includeFilename, fileNumber, includingFiles))
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "In file included from " << fileName << "." << lineNumber;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream toInsert;
|
std::stringstream toInsert;
|
||||||
toInsert << "#line 0 " << includedFileNumber << "\n" << stringRepresentation << "\n#line " << lineNumber << " 0\n";
|
toInsert << "#line 0 " << includedFileNumber << "\n" << stringRepresentation << "\n#line " << lineNumber << " 0\n";
|
||||||
|
|
||||||
source.replace(foundPos, (end-foundPos+1), toInsert.str());
|
source.replace(foundPos, (end - foundPos + 1), toInsert.str());
|
||||||
|
|
||||||
if (includedFiles.insert(includePath).second == false)
|
|
||||||
{
|
|
||||||
Log(Debug::Error) << "Shader " << templateName << " error: Detected cyclic #includes";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
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::vector<DeclarationMeta>& declarations)
|
||||||
|
{
|
||||||
|
static const char* geometryTemplate =
|
||||||
|
"#version 150 compatibility\n"
|
||||||
|
"#extension GL_NV_viewport_array : enable\n"
|
||||||
|
"#extension GL_ARB_gpu_shader5 : enable\n"
|
||||||
|
"layout (triangles, invocations = 2) in;\n"
|
||||||
|
"layout (triangle_strip, max_vertices = 3) out;\n"
|
||||||
|
"\n"
|
||||||
|
"// Geometry Shader Inputs\n"
|
||||||
|
"@INPUTS\n"
|
||||||
|
"\n"
|
||||||
|
"// Geometry Shader Outputs\n"
|
||||||
|
"@OUTPUTS\n"
|
||||||
|
"\n"
|
||||||
|
"// Stereo matrices\n"
|
||||||
|
"uniform mat4 stereoViewMatrices[2];\n"
|
||||||
|
"uniform mat4 stereoViewProjections[2];\n"
|
||||||
|
"\n"
|
||||||
|
"void main() {\n"
|
||||||
|
" for(int i = 0; i < gl_in.length(); i++)\n"
|
||||||
|
" {\n"
|
||||||
|
" gl_ViewportIndex = gl_InvocationID;\n"
|
||||||
|
" // Re-project\n"
|
||||||
|
" gl_Position = stereoViewProjections[gl_InvocationID] * gl_in[i].gl_Position;\n"
|
||||||
|
" vec4 viewPos = stereoViewMatrices[gl_InvocationID] * gl_in[i].gl_Position;\n"
|
||||||
|
" gl_ClipVertex = vec4(viewPos.xyz,1);\n"
|
||||||
|
"\n"
|
||||||
|
" // Input -> output\n"
|
||||||
|
"@FORWARDING\n"
|
||||||
|
"\n"
|
||||||
|
" // TODO: deal with passNormal, depths, etc.\n"
|
||||||
|
"@EXTRA\n"
|
||||||
|
" EmitVertex();\n"
|
||||||
|
" }\n"
|
||||||
|
"\n"
|
||||||
|
" EndPrimitive();\n"
|
||||||
|
"}\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
static std::map<std::string, std::string> overriddenForwardStatements =
|
||||||
|
{
|
||||||
|
{"linearDepth", "linearDepth = gl_Position.z;"},
|
||||||
|
{"euclideanDepth", "euclideanDepth = length(viewPos.xyz);"},
|
||||||
|
{"passViewPos", "passViewPos = viewPos.xyz;"},
|
||||||
|
};
|
||||||
|
|
||||||
|
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 << "[];\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 << "[i];\n";
|
||||||
|
|
||||||
|
identifiers.insert(declaration.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(Debug::Verbose) << "Forward statements: \n" << ssForwardStatements.str();
|
||||||
|
|
||||||
|
// 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 "";
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (identifiers.find("screenCoordsPassthrough") != identifiers.end())
|
||||||
|
{
|
||||||
|
// TODO: This corrects basic sampling but the screenCoordsOffset value in the fragment shader is still fucked.
|
||||||
|
static const char* screenCordsAssignmentCode =
|
||||||
|
" 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(gl_InvocationID == 1)\n"
|
||||||
|
" screenCoordsPassthrough.x += 0.5 * screenCoordsPassthrough.z;\n"
|
||||||
|
;
|
||||||
|
ssExtraStatements << screenCordsAssignmentCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if()
|
||||||
|
|
||||||
|
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());
|
||||||
|
geometryShader = std::regex_replace(geometryShader, std::regex("@EXTRA"), ssExtraStatements.str());
|
||||||
|
|
||||||
|
return geometryShader;
|
||||||
|
}
|
||||||
|
|
||||||
bool parseFors(std::string& source, const std::string& templateName)
|
bool parseFors(std::string& source, const std::string& templateName)
|
||||||
{
|
{
|
||||||
const char escapeCharacter = '$';
|
const char escapeCharacter = '$';
|
||||||
|
@ -165,7 +325,7 @@ namespace Shader
|
||||||
std::string list = source.substr(listStart, listEnd - listStart);
|
std::string list = source.substr(listStart, listEnd - listStart);
|
||||||
std::vector<std::string> listElements;
|
std::vector<std::string> listElements;
|
||||||
if (list != "")
|
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 contentStart = source.find_first_not_of("\n\r", listEnd);
|
||||||
size_t contentEnd = source.find("$endforeach", contentStart);
|
size_t contentEnd = source.find("$endforeach", contentStart);
|
||||||
|
@ -224,7 +384,7 @@ namespace Shader
|
||||||
Log(Debug::Error) << "Shader " << templateName << " error: Unexpected EOF";
|
Log(Debug::Error) << "Shader " << templateName << " error: Unexpected EOF";
|
||||||
return false;
|
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 defineFound = defines.find(define);
|
||||||
ShaderManager::DefineMap::const_iterator globalDefineFound = globalDefines.find(define);
|
ShaderManager::DefineMap::const_iterator globalDefineFound = globalDefines.find(define);
|
||||||
if (define == "foreach")
|
if (define == "foreach")
|
||||||
|
@ -271,7 +431,7 @@ namespace Shader
|
||||||
return true;
|
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);
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
@ -279,21 +439,22 @@ namespace Shader
|
||||||
TemplateMap::iterator templateIt = mShaderTemplates.find(templateName);
|
TemplateMap::iterator templateIt = mShaderTemplates.find(templateName);
|
||||||
if (templateIt == mShaderTemplates.end())
|
if (templateIt == mShaderTemplates.end())
|
||||||
{
|
{
|
||||||
boost::filesystem::path p = (boost::filesystem::path(mPath) / templateName);
|
boost::filesystem::path path = (boost::filesystem::path(mPath) / templateName);
|
||||||
boost::filesystem::ifstream stream;
|
boost::filesystem::ifstream stream;
|
||||||
stream.open(p);
|
stream.open(path);
|
||||||
if (stream.fail())
|
if (stream.fail())
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "Failed to open " << p.string();
|
Log(Debug::Error) << "Failed to open " << path.string();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
std::stringstream buffer;
|
std::stringstream buffer;
|
||||||
buffer << stream.rdbuf();
|
buffer << stream.rdbuf();
|
||||||
|
|
||||||
// parse includes
|
// parse includes
|
||||||
|
int fileNumber = 1;
|
||||||
std::string source = buffer.str();
|
std::string source = buffer.str();
|
||||||
if (!addLineDirectivesAfterConditionalBlocks(source)
|
if (!addLineDirectivesAfterConditionalBlocks(source)
|
||||||
|| !parseIncludes(boost::filesystem::path(mPath), source, templateName))
|
|| !parseIncludes(boost::filesystem::path(mPath), source, templateName, fileNumber, {}))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
templateIt = mShaderTemplates.insert(std::make_pair(templateName, source)).first;
|
templateIt = mShaderTemplates.insert(std::make_pair(templateName, source)).first;
|
||||||
|
@ -310,12 +471,31 @@ namespace Shader
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Shader> shader (new osg::Shader(shaderType));
|
osg::ref_ptr<osg::Shader> shader(new osg::Shader(shaderType));
|
||||||
shader->setShaderSource(shaderSource);
|
|
||||||
// Assign a unique name to allow the SharedStateManager to compare shaders efficiently
|
// Assign a unique name to allow the SharedStateManager to compare shaders efficiently
|
||||||
static unsigned int counter = 0;
|
static unsigned int counter = 0;
|
||||||
shader->setName(std::to_string(counter++));
|
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 geometryShaderSource = generateGeometryShader(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;
|
shaderIt = mShaders.insert(std::make_pair(std::make_pair(templateName, defines), shader)).first;
|
||||||
}
|
}
|
||||||
return shaderIt->second;
|
return shaderIt->second;
|
||||||
|
@ -327,9 +507,16 @@ namespace Shader
|
||||||
ProgramMap::iterator found = mPrograms.find(std::make_pair(vertexShader, fragmentShader));
|
ProgramMap::iterator found = mPrograms.find(std::make_pair(vertexShader, fragmentShader));
|
||||||
if (found == mPrograms.end())
|
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(vertexShader);
|
||||||
program->addShader(fragmentShader);
|
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;
|
found = mPrograms.insert(std::make_pair(std::make_pair(vertexShader, fragmentShader), program)).first;
|
||||||
}
|
}
|
||||||
return found->second;
|
return found->second;
|
||||||
|
@ -340,10 +527,15 @@ namespace Shader
|
||||||
return DefineMap(mGlobalDefines);
|
return DefineMap(mGlobalDefines);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderManager::setGlobalDefines(DefineMap & globalDefines)
|
void ShaderManager::enableGeometryShader(bool enabled)
|
||||||
|
{
|
||||||
|
mGeometryShadersEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderManager::setGlobalDefines(DefineMap& globalDefines)
|
||||||
{
|
{
|
||||||
mGlobalDefines = globalDefines;
|
mGlobalDefines = globalDefines;
|
||||||
for (auto shaderMapElement: mShaders)
|
for (auto shaderMapElement : mShaders)
|
||||||
{
|
{
|
||||||
std::string templateId = shaderMapElement.first.first;
|
std::string templateId = shaderMapElement.first.first;
|
||||||
ShaderManager::DefineMap defines = shaderMapElement.first.second;
|
ShaderManager::DefineMap defines = shaderMapElement.first.second;
|
||||||
|
@ -360,7 +552,7 @@ namespace Shader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderManager::releaseGLObjects(osg::State *state)
|
void ShaderManager::releaseGLObjects(osg::State* state)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
for (auto shader : mShaders)
|
for (auto shader : mShaders)
|
||||||
|
|
|
@ -36,6 +36,13 @@ namespace Shader
|
||||||
/// Get (a copy of) the DefineMap used to construct all shaders
|
/// Get (a copy of) the DefineMap used to construct all shaders
|
||||||
DefineMap getGlobalDefines();
|
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 enableGeometryShader(bool enabled);
|
||||||
|
|
||||||
/// 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
|
||||||
/// @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.
|
/// @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.
|
||||||
|
@ -59,6 +66,10 @@ namespace Shader
|
||||||
typedef std::map<MapKey, osg::ref_ptr<osg::Shader> > ShaderMap;
|
typedef std::map<MapKey, osg::ref_ptr<osg::Shader> > ShaderMap;
|
||||||
ShaderMap mShaders;
|
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;
|
typedef std::map<std::pair<osg::ref_ptr<osg::Shader>, osg::ref_ptr<osg::Shader> >, osg::ref_ptr<osg::Program> > ProgramMap;
|
||||||
ProgramMap mPrograms;
|
ProgramMap mPrograms;
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace Shader
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderVisitor::ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string &defaultVsTemplate, const std::string &defaultFsTemplate, const std::string& defaultGsTemplate)
|
ShaderVisitor::ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string &defaultVsTemplate, const std::string &defaultFsTemplate)
|
||||||
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||||
, mForceShaders(false)
|
, mForceShaders(false)
|
||||||
, mAllowedToModifyStateSets(true)
|
, mAllowedToModifyStateSets(true)
|
||||||
|
@ -47,7 +47,6 @@ namespace Shader
|
||||||
, mImageManager(imageManager)
|
, mImageManager(imageManager)
|
||||||
, mDefaultVsTemplate(defaultVsTemplate)
|
, mDefaultVsTemplate(defaultVsTemplate)
|
||||||
, mDefaultFsTemplate(defaultFsTemplate)
|
, mDefaultFsTemplate(defaultFsTemplate)
|
||||||
, mDefaultGsTemplate(defaultGsTemplate)
|
|
||||||
{
|
{
|
||||||
mRequirements.push_back(ShaderRequirements());
|
mRequirements.push_back(ShaderRequirements());
|
||||||
}
|
}
|
||||||
|
@ -345,17 +344,16 @@ namespace Shader
|
||||||
}
|
}
|
||||||
|
|
||||||
defineMap["parallax"] = reqs.mNormalHeight ? "1" : "0";
|
defineMap["parallax"] = reqs.mNormalHeight ? "1" : "0";
|
||||||
|
defineMap["geometryShader"] = "1";
|
||||||
|
|
||||||
writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode));
|
writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode));
|
||||||
|
|
||||||
osg::ref_ptr<osg::Shader> vertexShader (mShaderManager.getShader(mDefaultVsTemplate, defineMap, osg::Shader::VERTEX));
|
osg::ref_ptr<osg::Shader> vertexShader (mShaderManager.getShader(mDefaultVsTemplate, defineMap, osg::Shader::VERTEX));
|
||||||
osg::ref_ptr<osg::Shader> fragmentShader (mShaderManager.getShader(mDefaultFsTemplate, defineMap, osg::Shader::FRAGMENT));
|
osg::ref_ptr<osg::Shader> fragmentShader (mShaderManager.getShader(mDefaultFsTemplate, defineMap, osg::Shader::FRAGMENT));
|
||||||
osg::ref_ptr<osg::Shader> geometryShader (mShaderManager.getShader(mDefaultGsTemplate, defineMap, osg::Shader::GEOMETRY));
|
|
||||||
//osg::ref_ptr<osg::Shader> geometryShader = nullptr;
|
|
||||||
|
|
||||||
if (vertexShader && fragmentShader)
|
if (vertexShader && fragmentShader)
|
||||||
{
|
{
|
||||||
writableStateSet->setAttributeAndModes(mShaderManager.getProgram(vertexShader, fragmentShader, geometryShader), osg::StateAttribute::ON);
|
writableStateSet->setAttributeAndModes(mShaderManager.getProgram(vertexShader, fragmentShader), osg::StateAttribute::ON);
|
||||||
|
|
||||||
for (std::map<int, std::string>::const_iterator texIt = reqs.mTextures.begin(); texIt != reqs.mTextures.end(); ++texIt)
|
for (std::map<int, std::string>::const_iterator texIt = reqs.mTextures.begin(); texIt != reqs.mTextures.end(); ++texIt)
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace Shader
|
||||||
class ShaderVisitor : public osg::NodeVisitor
|
class ShaderVisitor : public osg::NodeVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string& defaultVsTemplate, const std::string& defaultFsTemplate, const std::string& defaultGsTemplate);
|
ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string& defaultVsTemplate, const std::string& defaultFsTemplate);
|
||||||
|
|
||||||
/// By default, only bump mapped objects will have a shader added to them.
|
/// By default, only bump mapped objects will have a shader added to them.
|
||||||
/// Setting force = true will cause all objects to render using shaders, regardless of having a bump map.
|
/// Setting force = true will cause all objects to render using shaders, regardless of having a bump map.
|
||||||
|
@ -89,7 +89,6 @@ namespace Shader
|
||||||
|
|
||||||
std::string mDefaultVsTemplate;
|
std::string mDefaultVsTemplate;
|
||||||
std::string mDefaultFsTemplate;
|
std::string mDefaultFsTemplate;
|
||||||
std::string mDefaultGsTemplate;
|
|
||||||
|
|
||||||
void createProgram(const ShaderRequirements& reqs);
|
void createProgram(const ShaderRequirements& reqs);
|
||||||
bool adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs);
|
bool adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs);
|
||||||
|
|
|
@ -232,10 +232,9 @@ namespace Terrain
|
||||||
defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
|
defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
|
||||||
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
|
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
|
||||||
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "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> vertexShader = shaderManager->getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX);
|
||||||
osg::ref_ptr<osg::Shader> geometryShader = shaderManager->getShader("terrain_geometry.glsl", defineMap, osg::Shader::GEOMETRY);
|
|
||||||
//osg::ref_ptr<osg::Shader> geometryShader = nullptr;
|
|
||||||
osg::ref_ptr<osg::Shader> fragmentShader = shaderManager->getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
|
osg::ref_ptr<osg::Shader> fragmentShader = shaderManager->getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
|
||||||
if (!vertexShader || !fragmentShader)
|
if (!vertexShader || !fragmentShader)
|
||||||
{
|
{
|
||||||
|
@ -243,7 +242,7 @@ namespace Terrain
|
||||||
return createPasses(false, shaderManager, layers, blendmaps, blendmapScale, layerTileSize);
|
return createPasses(false, shaderManager, layers, blendmaps, blendmapScale, layerTileSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
stateset->setAttributeAndModes(shaderManager->getProgram(vertexShader, fragmentShader, geometryShader));
|
stateset->setAttributeAndModes(shaderManager->getProgram(vertexShader, fragmentShader));
|
||||||
stateset->addUniform(new osg::Uniform("colorMode", 2));
|
stateset->addUniform(new osg::Uniform("colorMode", 2));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -11,21 +11,17 @@ set(SHADER_FILES
|
||||||
water_fragment.glsl
|
water_fragment.glsl
|
||||||
water_nm.png
|
water_nm.png
|
||||||
objects_vertex.glsl
|
objects_vertex.glsl
|
||||||
objects_geometry.glsl
|
|
||||||
objects_fragment.glsl
|
objects_fragment.glsl
|
||||||
terrain_vertex.glsl
|
terrain_vertex.glsl
|
||||||
terrain_geometry.glsl
|
|
||||||
terrain_fragment.glsl
|
terrain_fragment.glsl
|
||||||
lighting.glsl
|
lighting.glsl
|
||||||
parallax.glsl
|
parallax.glsl
|
||||||
s360_fragment.glsl
|
s360_fragment.glsl
|
||||||
s360_vertex.glsl
|
s360_vertex.glsl
|
||||||
shadows_vertex.glsl
|
shadows_vertex.glsl
|
||||||
shadows_geometry.glsl
|
|
||||||
shadows_fragment.glsl
|
shadows_fragment.glsl
|
||||||
shadowcasting_vertex.glsl
|
shadowcasting_vertex.glsl
|
||||||
shadowcasting_fragment.glsl
|
shadowcasting_fragment.glsl
|
||||||
interface_util.glsl
|
|
||||||
)
|
)
|
||||||
|
|
||||||
copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}")
|
copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}")
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
// Because GLSL is an abomination we have to mangle the names of all
|
|
||||||
// vertex outputs when using a geometry shader.
|
|
||||||
#ifndef INTERFACE_UTIL_GLSL
|
|
||||||
#define INTERFACE_UTIL_GLSL
|
|
||||||
|
|
||||||
#if 1 // Placeholder
|
|
||||||
#define VS_NAME(name) vertex_##name
|
|
||||||
#else
|
|
||||||
#define VS_NAME(name) name
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // INTERFACE_UTIL_GLSL
|
|
|
@ -1,17 +0,0 @@
|
||||||
#version 330 core
|
|
||||||
#extension GL_NV_viewport_array : enable
|
|
||||||
#extension GL_ARB_gpu_shader5 : enable
|
|
||||||
layout (triangles, invocations = 2) in;
|
|
||||||
layout (triangle_strip, max_vertices = 3) out;
|
|
||||||
|
|
||||||
#include "interface_util.glsl"
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
for(int i = 0; i < gl_in.length(); i++)
|
|
||||||
{
|
|
||||||
gl_ViewportIndex = gl_InvocationID;
|
|
||||||
gl_Position = gl_in[i].gl_Position;
|
|
||||||
EmitVertex();
|
|
||||||
}
|
|
||||||
EndPrimitive();
|
|
||||||
}
|
|
|
@ -1,7 +1,5 @@
|
||||||
#version 120
|
#version 120
|
||||||
|
|
||||||
#include "interface_util.glsl"
|
|
||||||
|
|
||||||
#if @diffuseMap
|
#if @diffuseMap
|
||||||
varying vec2 diffuseMapUV;
|
varying vec2 diffuseMapUV;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -110,6 +110,4 @@ void main()
|
||||||
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);
|
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);
|
||||||
|
|
||||||
applyShadowDebugOverlay();
|
applyShadowDebugOverlay();
|
||||||
|
|
||||||
//gl_FragData[0] = vec4(passNormal,1);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
#version 330 core
|
|
||||||
#extension GL_NV_viewport_array : enable
|
|
||||||
#extension GL_ARB_gpu_shader5 : enable
|
|
||||||
layout (triangles, invocations = 2) in;
|
|
||||||
layout (triangle_strip, max_vertices = 3) out;
|
|
||||||
|
|
||||||
#include "interface_util.glsl"
|
|
||||||
|
|
||||||
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
|
||||||
|
|
||||||
#if !PER_PIXEL_LIGHTING
|
|
||||||
centroid in vec4 VS_NAME(lighting)[];
|
|
||||||
centroid in vec3 VS_NAME(shadowDiffuseLighting)[];
|
|
||||||
centroid out vec4 lighting;
|
|
||||||
centroid out vec3 shadowDiffuseLighting;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TERRAIN INPUT
|
|
||||||
in vec2 VS_NAME(uv)[];
|
|
||||||
in float VS_NAME(euclideanDepth)[];
|
|
||||||
in float VS_NAME(linearDepth)[];
|
|
||||||
centroid in vec4 VS_NAME(passColor)[];
|
|
||||||
in vec3 VS_NAME(passViewPos)[];
|
|
||||||
in vec3 VS_NAME(passNormal)[];
|
|
||||||
|
|
||||||
#if(@shadows_enabled)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TERRAIN OUTPUT
|
|
||||||
out vec2 uv;
|
|
||||||
out float euclideanDepth;
|
|
||||||
out float linearDepth;
|
|
||||||
centroid out vec4 passColor;
|
|
||||||
out vec3 passViewPos;
|
|
||||||
out vec3 passNormal;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
for(int i = 0; i < gl_in.length(); i++)
|
|
||||||
{
|
|
||||||
gl_ViewportIndex = gl_InvocationID;
|
|
||||||
gl_Position = gl_in[i].gl_Position;
|
|
||||||
uv = VS_NAME(uv)[i];
|
|
||||||
euclideanDepth = VS_NAME(euclideanDepth)[i];
|
|
||||||
linearDepth = VS_NAME(linearDepth)[i];
|
|
||||||
|
|
||||||
#if !PER_PIXEL_LIGHTING
|
|
||||||
lighting = VS_NAME(lighting)[i];
|
|
||||||
shadowDiffuseLighting = VS_NAME(shadowDiffuseLighting)[i];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
passColor = VS_NAME(passColor)[i];
|
|
||||||
passViewPos = VS_NAME(passViewPos)[i];
|
|
||||||
passNormal = VS_NAME(passNormal)[i];
|
|
||||||
|
|
||||||
#if(@shadows_enabled)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EmitVertex();
|
|
||||||
}
|
|
||||||
EndPrimitive();
|
|
||||||
}
|
|
|
@ -1,47 +1,44 @@
|
||||||
#version 120
|
#version 130
|
||||||
|
|
||||||
#include "interface_util.glsl"
|
varying vec2 uv;
|
||||||
|
varying float euclideanDepth;
|
||||||
varying vec2 VS_NAME(uv);
|
varying float linearDepth;
|
||||||
varying float VS_NAME(euclideanDepth);
|
|
||||||
varying float VS_NAME(linearDepth);
|
|
||||||
|
|
||||||
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
||||||
|
|
||||||
#if !PER_PIXEL_LIGHTING
|
#if !PER_PIXEL_LIGHTING
|
||||||
centroid varying vec4 VS_NAME(lighting);
|
centroid varying vec4 lighting;
|
||||||
centroid varying vec3 VS_NAME(shadowDiffuseLighting);
|
centroid varying vec3 shadowDiffuseLighting;
|
||||||
#endif
|
#endif
|
||||||
centroid varying vec4 VS_NAME(passColor);
|
centroid varying vec4 passColor;
|
||||||
varying vec3 VS_NAME(passViewPos);
|
varying vec3 passViewPos;
|
||||||
varying vec3 VS_NAME(passNormal);
|
varying vec3 passNormal;
|
||||||
|
|
||||||
#include "shadows_vertex.glsl"
|
#include "shadows_vertex.glsl"
|
||||||
|
|
||||||
#include "lighting.glsl"
|
#include "lighting.glsl"
|
||||||
|
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||||
|
|
||||||
vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);
|
vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);
|
||||||
gl_ClipVertex = viewPos;
|
gl_ClipVertex = viewPos;
|
||||||
VS_NAME(euclideanDepth) = length(viewPos.xyz);
|
euclideanDepth = length(viewPos.xyz);
|
||||||
VS_NAME(linearDepth) = gl_Position.z;
|
linearDepth = gl_Position.z;
|
||||||
|
|
||||||
#if (!PER_PIXEL_LIGHTING || @shadows_enabled)
|
#if (!PER_PIXEL_LIGHTING || @shadows_enabled)
|
||||||
vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);
|
vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !PER_PIXEL_LIGHTING
|
#if !PER_PIXEL_LIGHTING
|
||||||
VS_NAME(lighting) = doLighting(viewPos.xyz, viewNormal, gl_Color, VS_NAME(shadowDiffuseLighting));
|
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting);
|
||||||
#endif
|
#endif
|
||||||
VS_NAME(passColor) = gl_Color;
|
passColor = gl_Color;
|
||||||
VS_NAME(passNormal) = gl_Normal.xyz;
|
passNormal = gl_Normal.xyz;
|
||||||
VS_NAME(passViewPos) = viewPos.xyz;
|
passViewPos = viewPos.xyz;
|
||||||
|
|
||||||
VS_NAME(uv) = gl_MultiTexCoord0.xy;
|
uv = gl_MultiTexCoord0.xy;
|
||||||
|
|
||||||
#if (@shadows_enabled)
|
#if (@shadows_enabled)
|
||||||
setupShadowCoords(viewPos, viewNormal);
|
setupShadowCoords(viewPos, viewNormal);
|
||||||
|
|
Loading…
Reference in a new issue