mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-04-02 03:06:43 +00:00
Attempting to fix gamma by properly managing state.
This commit is contained in:
parent
2923174f9a
commit
b2445e4a89
5 changed files with 212 additions and 93 deletions
|
@ -163,7 +163,6 @@ namespace MWVR
|
||||||
{
|
{
|
||||||
if (frameMeta->mShouldRender)
|
if (frameMeta->mShouldRender)
|
||||||
{
|
{
|
||||||
viewer.blit(gc);
|
|
||||||
gc->swapBuffersImplementation();
|
gc->swapBuffersImplementation();
|
||||||
std::array<CompositionLayerProjectionView, 2> layerStack{};
|
std::array<CompositionLayerProjectionView, 2> layerStack{};
|
||||||
layerStack[(int)Side::LEFT_SIDE].subImage = viewer.subImage(Side::LEFT_SIDE);
|
layerStack[(int)Side::LEFT_SIDE].subImage = viewer.subImage(Side::LEFT_SIDE);
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
#include "../mwrender/vismask.hpp"
|
#include "../mwrender/vismask.hpp"
|
||||||
|
|
||||||
#include <osgViewer/Renderer>
|
#include <osgViewer/Renderer>
|
||||||
|
#include <osg/StateAttribute>
|
||||||
|
#include <osg/BufferObject>
|
||||||
|
#include <osg/VertexArrayState>
|
||||||
|
|
||||||
#include <components/debug/gldebug.hpp>
|
#include <components/debug/gldebug.hpp>
|
||||||
|
|
||||||
|
@ -44,6 +47,7 @@ namespace MWVR
|
||||||
: mViewer(viewer)
|
: mViewer(viewer)
|
||||||
, mPreDraw(new PredrawCallback(this))
|
, mPreDraw(new PredrawCallback(this))
|
||||||
, mPostDraw(new PostdrawCallback(this))
|
, mPostDraw(new PostdrawCallback(this))
|
||||||
|
, mFinalDraw(new FinaldrawCallback(this))
|
||||||
, mUpdateViewCallback(new UpdateViewCallback(this))
|
, mUpdateViewCallback(new UpdateViewCallback(this))
|
||||||
, mMsaaResolveTexture{}
|
, mMsaaResolveTexture{}
|
||||||
, mMirrorTexture{ nullptr }
|
, mMirrorTexture{ nullptr }
|
||||||
|
@ -126,13 +130,13 @@ namespace MWVR
|
||||||
mSwapchainConfig[i].selectedWidth = parseResolution(xConfString[i], mSwapchainConfig[i].recommendedWidth, mSwapchainConfig[i].maxWidth);
|
mSwapchainConfig[i].selectedWidth = parseResolution(xConfString[i], mSwapchainConfig[i].recommendedWidth, mSwapchainConfig[i].maxWidth);
|
||||||
mSwapchainConfig[i].selectedHeight = parseResolution(yConfString[i], mSwapchainConfig[i].recommendedHeight, mSwapchainConfig[i].maxHeight);
|
mSwapchainConfig[i].selectedHeight = parseResolution(yConfString[i], mSwapchainConfig[i].recommendedHeight, mSwapchainConfig[i].maxHeight);
|
||||||
|
|
||||||
mSwapchainConfig[i].selectedSamples =
|
mSwapchainConfig[i].selectedSamples =
|
||||||
std::max(1, // OpenXR requires a non-zero value
|
std::max(1, // OpenXR requires a non-zero value
|
||||||
std::min(mSwapchainConfig[i].maxSamples,
|
std::min(mSwapchainConfig[i].maxSamples,
|
||||||
Settings::Manager::getInt("antialiasing", "Video")
|
Settings::Manager::getInt("antialiasing", "Video")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
Log(Debug::Verbose) << name << " resolution: Recommended x=" << mSwapchainConfig[i].recommendedWidth << ", y=" << mSwapchainConfig[i].recommendedHeight;
|
Log(Debug::Verbose) << name << " resolution: Recommended x=" << mSwapchainConfig[i].recommendedWidth << ", y=" << mSwapchainConfig[i].recommendedHeight;
|
||||||
Log(Debug::Verbose) << name << " resolution: Max x=" << mSwapchainConfig[i].maxWidth << ", y=" << mSwapchainConfig[i].maxHeight;
|
Log(Debug::Verbose) << name << " resolution: Max x=" << mSwapchainConfig[i].maxWidth << ", y=" << mSwapchainConfig[i].maxHeight;
|
||||||
Log(Debug::Verbose) << name << " resolution: Selected x=" << mSwapchainConfig[i].selectedWidth << ", y=" << mSwapchainConfig[i].selectedHeight;
|
Log(Debug::Verbose) << name << " resolution: Selected x=" << mSwapchainConfig[i].selectedWidth << ", y=" << mSwapchainConfig[i].selectedHeight;
|
||||||
|
@ -152,10 +156,10 @@ namespace MWVR
|
||||||
int height = std::max(mSubImages[0].height, mSubImages[1].height);
|
int height = std::max(mSubImages[0].height, mSubImages[1].height);
|
||||||
int samples = std::max(mSwapchainConfig[0].selectedSamples, mSwapchainConfig[1].selectedSamples);
|
int samples = std::max(mSwapchainConfig[0].selectedSamples, mSwapchainConfig[1].selectedSamples);
|
||||||
|
|
||||||
mFramebuffer.reset(new VRFramebuffer(gc->getState(),width,height, samples));
|
mFramebuffer.reset(new VRFramebuffer(gc->getState(), width, height, samples));
|
||||||
mFramebuffer->createColorBuffer(gc);
|
mFramebuffer->createColorBuffer(gc);
|
||||||
mFramebuffer->createDepthBuffer(gc);
|
mFramebuffer->createDepthBuffer(gc);
|
||||||
mMsaaResolveTexture.reset(new VRFramebuffer(gc->getState(),width,height,0));
|
mMsaaResolveTexture.reset(new VRFramebuffer(gc->getState(), width, height, 0));
|
||||||
mMsaaResolveTexture->createColorBuffer(gc);
|
mMsaaResolveTexture->createColorBuffer(gc);
|
||||||
mGammaResolveTexture.reset(new VRFramebuffer(gc->getState(), width, height, 0));
|
mGammaResolveTexture.reset(new VRFramebuffer(gc->getState(), width, height, 0));
|
||||||
mGammaResolveTexture->createColorBuffer(gc);
|
mGammaResolveTexture->createColorBuffer(gc);
|
||||||
|
@ -179,6 +183,7 @@ namespace MWVR
|
||||||
Misc::StereoView::instance().setInitialDrawCallback(new InitialDrawCallback(this));
|
Misc::StereoView::instance().setInitialDrawCallback(new InitialDrawCallback(this));
|
||||||
Misc::StereoView::instance().setPredrawCallback(mPreDraw);
|
Misc::StereoView::instance().setPredrawCallback(mPreDraw);
|
||||||
Misc::StereoView::instance().setPostdrawCallback(mPostDraw);
|
Misc::StereoView::instance().setPostdrawCallback(mPostDraw);
|
||||||
|
Misc::StereoView::instance().setFinaldrawCallback(mFinalDraw);
|
||||||
//auto cullMask = Misc::StereoView::instance().getCullMask();
|
//auto cullMask = Misc::StereoView::instance().getCullMask();
|
||||||
auto cullMask = ~(MWRender::VisMask::Mask_UpdateVisitor | MWRender::VisMask::Mask_SimpleWater);
|
auto cullMask = ~(MWRender::VisMask::Mask_UpdateVisitor | MWRender::VisMask::Mask_SimpleWater);
|
||||||
cullMask &= ~MWRender::VisMask::Mask_GUI;
|
cullMask &= ~MWRender::VisMask::Mask_GUI;
|
||||||
|
@ -257,108 +262,106 @@ namespace MWVR
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool applyGamma(osg::GraphicsContext* gc, VRFramebuffer& target, VRFramebuffer& source)
|
static bool applyGamma(osg::RenderInfo& info, VRFramebuffer& target, VRFramebuffer& source)
|
||||||
{
|
{
|
||||||
// TODO: Temporary solution for applying gamma and contrast modifications
|
osg::State* state = info.getState();
|
||||||
// When OpenMW implements post processing, this will be performed there instead.
|
static const char* vSource = "#version 120\n varying vec2 uv; void main(){ gl_Position = vec4(gl_Vertex.xy*2.0 - 1, 0, 1); uv = gl_Vertex.xy;}";
|
||||||
// I'm just throwing things into static locals since this is temporary code that will be trashed later.
|
static const char* fSource = "#version 120\n varying vec2 uv; uniform sampler2D t; uniform float gamma; uniform float contrast;"
|
||||||
static bool first = true;
|
"void main() {"
|
||||||
static GLuint vShader = 0;
|
"vec4 color1 = texture2D(t, uv);"
|
||||||
static GLuint fShader = 0;
|
"vec3 rgb = color1.rgb;"
|
||||||
static GLuint program = 0;
|
"rgb = (rgb - 0.5f) * contrast + 0.5f;"
|
||||||
static GLuint vbo = 0;
|
"rgb = pow(rgb, vec3(1.0/gamma));"
|
||||||
static GLuint gammaUniform = 0;
|
"gl_FragColor = vec4(rgb, color1.a);"
|
||||||
static GLuint contrastUniform = 0;
|
"}";
|
||||||
|
|
||||||
auto* state = gc->getState();
|
static bool first = true;
|
||||||
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
static osg::ref_ptr<osg::Program> program = nullptr;
|
||||||
|
static osg::ref_ptr<osg::Shader> vShader = nullptr;
|
||||||
|
static osg::ref_ptr<osg::Shader> fShader = nullptr;
|
||||||
|
static osg::ref_ptr<osg::Uniform> gammaUniform = nullptr;
|
||||||
|
static GLint gammaUniformLocation = 0;
|
||||||
|
static osg::ref_ptr<osg::Uniform> contrastUniform = nullptr;
|
||||||
|
static GLint contrastUniformLocation = 0;
|
||||||
|
osg::Viewport* viewport = nullptr;
|
||||||
|
static osg::ref_ptr<osg::StateSet> stateset = nullptr;
|
||||||
|
static osg::ref_ptr<osg::Geometry> geometry = nullptr;
|
||||||
|
static osg::ref_ptr<osg::Texture2D> texture = nullptr;
|
||||||
|
static osg::ref_ptr<osg::Texture::TextureObject> textureObject = nullptr;
|
||||||
|
|
||||||
|
static std::vector<osg::Vec4> vertices =
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
{1, 0, 0, 0},
|
||||||
|
{1, 1, 0, 0},
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
{1, 1, 0, 0},
|
||||||
|
{0, 1, 0, 0}
|
||||||
|
};
|
||||||
|
static osg::ref_ptr<osg::Vec4Array> vertexArray = new osg::Vec4Array(vertices.begin(), vertices.end());
|
||||||
|
|
||||||
if (first)
|
if (first)
|
||||||
{
|
{
|
||||||
|
geometry = new osg::Geometry();
|
||||||
|
geometry->setVertexArray(vertexArray);
|
||||||
|
geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLES, 0, 6));
|
||||||
|
geometry->setUseDisplayList(false);
|
||||||
|
stateset = geometry->getOrCreateStateSet();
|
||||||
|
|
||||||
|
vShader = new osg::Shader(osg::Shader::Type::VERTEX, vSource);
|
||||||
|
fShader = new osg::Shader(osg::Shader::Type::FRAGMENT, fSource);
|
||||||
|
program = new osg::Program();
|
||||||
|
program->addShader(vShader);
|
||||||
|
program->addShader(fShader);
|
||||||
|
program->compileGLObjects(*state);
|
||||||
|
stateset->setAttributeAndModes(program, osg::StateAttribute::ON);
|
||||||
|
|
||||||
|
texture = new osg::Texture2D();
|
||||||
|
texture->setName("diffuseMap");
|
||||||
|
textureObject = new osg::Texture::TextureObject(texture, source.colorBuffer(), GL_TEXTURE_2D);
|
||||||
|
texture->setTextureObject(state->getContextID(), textureObject);
|
||||||
|
stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::PROTECTED);
|
||||||
|
stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::PROTECTED);
|
||||||
|
|
||||||
|
gammaUniform = new osg::Uniform("gamma", Settings::Manager::getFloat("gamma", "Video"));
|
||||||
|
contrastUniform = new osg::Uniform("contrast", Settings::Manager::getFloat("contrast", "Video"));
|
||||||
|
stateset->addUniform(gammaUniform);
|
||||||
|
stateset->addUniform(contrastUniform);
|
||||||
|
|
||||||
|
geometry->compileGLObjects(info);
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
|
|
||||||
const char* vSource = "#version 120\n varying vec2 uv; void main(){ gl_Position = vec4(gl_Vertex.xy*2.0 - 1, 0, 1); uv = gl_Vertex.xy;}";
|
|
||||||
const char* fSource = "#version 120\n varying vec2 uv; uniform sampler2D t; uniform float gamma; uniform float contrast;"
|
|
||||||
"void main() {"
|
|
||||||
"vec4 color1 = texture2D(t, uv);"
|
|
||||||
"vec3 rgb = color1.rgb;"
|
|
||||||
"rgb = (rgb - 0.5f) * contrast + 0.5f;"
|
|
||||||
"rgb = pow(rgb, vec3(1.0/gamma));"
|
|
||||||
"gl_FragColor = vec4(rgb, color1.a);"
|
|
||||||
"}";
|
|
||||||
|
|
||||||
vShader = createShader(gl, vSource, GL_VERTEX_SHADER);
|
|
||||||
fShader = createShader(gl, fSource, GL_FRAGMENT_SHADER);
|
|
||||||
|
|
||||||
program = gl->glCreateProgram();
|
|
||||||
gl->glAttachShader(program, vShader);
|
|
||||||
gl->glAttachShader(program, fShader);
|
|
||||||
gl->glLinkProgram(program);
|
|
||||||
|
|
||||||
GLint isCompiled = 0;
|
|
||||||
gl->glGetProgramiv(program, GL_LINK_STATUS, &isCompiled);
|
|
||||||
if (isCompiled == GL_FALSE)
|
|
||||||
{
|
|
||||||
GLint maxLength = 0;
|
|
||||||
gl->glGetProgramInfoLog(program, 0, &maxLength, nullptr);
|
|
||||||
std::vector<GLchar> infoLog(maxLength);
|
|
||||||
gl->glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
|
|
||||||
gl->glDeleteProgram(program);
|
|
||||||
program = 0;
|
|
||||||
Log(Debug::Error) << "Failed to link program: " << infoLog.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (program)
|
|
||||||
{
|
|
||||||
GLfloat vertices[] =
|
|
||||||
{
|
|
||||||
0, 0, 0, 0,
|
|
||||||
1, 0, 0, 0,
|
|
||||||
1, 1, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
1, 1, 0, 0,
|
|
||||||
0, 1, 0, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
gl->glGenBuffers(1, &vbo);
|
|
||||||
gl->glBindBuffer(GL_ARRAY_BUFFER_ARB, vbo);
|
|
||||||
gl->glBufferData(GL_ARRAY_BUFFER_ARB, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
gammaUniform = gl->glGetUniformLocation(program, "gamma");
|
|
||||||
contrastUniform = gl->glGetUniformLocation(program, "contrast");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target.bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
|
target.bindFramebuffer(state->getGraphicsContext(), GL_FRAMEBUFFER_EXT);
|
||||||
|
|
||||||
if (program > 0)
|
if (program > 0)
|
||||||
{
|
{
|
||||||
gl->glUseProgram(program);
|
// OSG does not pop statesets until after the final draw callback. Unrelated statesets may therefore still be on the stack at this point.
|
||||||
gl->glBindVertexArray(0);
|
// Pop these to avoid inheriting arbitrary state from these. They will not be used more in this frame.
|
||||||
glViewport(0, 0, target.width(), target.height());
|
state->popAllStateSets();
|
||||||
|
state->apply();
|
||||||
|
|
||||||
gl->glUniform1f(gammaUniform, Settings::Manager::getFloat("gamma", "Video"));
|
gammaUniform->set(Settings::Manager::getFloat("gamma", "Video"));
|
||||||
gl->glUniform1f(contrastUniform, Settings::Manager::getFloat("contrast", "Video"));
|
contrastUniform->set(Settings::Manager::getFloat("contrast", "Video"));
|
||||||
|
state->pushStateSet(stateset);
|
||||||
|
state->apply();
|
||||||
|
|
||||||
|
if(!viewport)
|
||||||
|
viewport = new osg::Viewport(0, 0, target.width(), target.height());
|
||||||
|
viewport->setViewport(0, 0, target.width(), target.height());
|
||||||
|
viewport->apply(*state);
|
||||||
|
|
||||||
gl->glBindBuffer(GL_ARRAY_BUFFER_ARB, vbo);
|
|
||||||
glVertexPointer(4, GL_FLOAT, 0, 0);
|
|
||||||
gl->glActiveTexture(GL_TEXTURE0);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, source.colorBuffer());
|
|
||||||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||||
glDisable(GL_BLEND);
|
geometry->draw(info);
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
state->popStateSet();
|
||||||
|
|
||||||
gl->glUseProgram(0);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
gl->glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VRViewer::blit(osg::GraphicsContext* gc)
|
void VRViewer::blit(osg::RenderInfo& info)
|
||||||
{
|
{
|
||||||
if (mMirrorTextureShouldBeCleanedUp)
|
if (mMirrorTextureShouldBeCleanedUp)
|
||||||
{
|
{
|
||||||
|
@ -368,21 +371,23 @@ namespace MWVR
|
||||||
if (!mMirrorTextureEnabled)
|
if (!mMirrorTextureEnabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto* state = info.getState();
|
||||||
|
auto* gc = state->getGraphicsContext();
|
||||||
|
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
||||||
|
|
||||||
auto* traits = SDLUtil::GraphicsWindowSDL2::findContext(*mViewer)->getTraits();
|
auto* traits = SDLUtil::GraphicsWindowSDL2::findContext(*mViewer)->getTraits();
|
||||||
int screenWidth = traits->width;
|
int screenWidth = traits->width;
|
||||||
int screenHeight = traits->height;
|
int screenHeight = traits->height;
|
||||||
if (!mMirrorTexture)
|
if (!mMirrorTexture)
|
||||||
{
|
{
|
||||||
;
|
;
|
||||||
mMirrorTexture.reset(new VRFramebuffer(gc->getState(),
|
mMirrorTexture.reset(new VRFramebuffer(state,
|
||||||
screenWidth,
|
screenWidth,
|
||||||
screenHeight,
|
screenHeight,
|
||||||
0));
|
0));
|
||||||
mMirrorTexture->createColorBuffer(gc);
|
mMirrorTexture->createColorBuffer(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* state = gc->getState();
|
|
||||||
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
|
||||||
|
|
||||||
int mirrorWidth = screenWidth / mMirrorTextureViews.size();
|
int mirrorWidth = screenWidth / mMirrorTextureViews.size();
|
||||||
|
|
||||||
|
@ -393,7 +398,7 @@ namespace MWVR
|
||||||
mFramebuffer->blit(gc, 0, 0, mFramebuffer->width(), mFramebuffer->height(), 0, 0, mMsaaResolveTexture->width(), mMsaaResolveTexture->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
mFramebuffer->blit(gc, 0, 0, mFramebuffer->width(), mFramebuffer->height(), 0, 0, mMsaaResolveTexture->width(), mMsaaResolveTexture->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
|
||||||
bool shouldDoGamma = Settings::Manager::getBool("gamma postprocessing", "VR Debug");
|
bool shouldDoGamma = Settings::Manager::getBool("gamma postprocessing", "VR Debug");
|
||||||
if (!shouldDoGamma || !applyGamma(gc, *mGammaResolveTexture, *mMsaaResolveTexture))
|
if (!shouldDoGamma || !applyGamma(info, *mGammaResolveTexture, *mMsaaResolveTexture))
|
||||||
{
|
{
|
||||||
mGammaResolveTexture->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
|
mGammaResolveTexture->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
|
||||||
mMsaaResolveTexture->blit(gc, 0, 0, mMsaaResolveTexture->width(), mMsaaResolveTexture->height(), 0, 0, mGammaResolveTexture->width(), mGammaResolveTexture->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
mMsaaResolveTexture->blit(gc, 0, 0, mMsaaResolveTexture->width(), mMsaaResolveTexture->height(), 0, 0, mGammaResolveTexture->width(), mGammaResolveTexture->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
@ -484,6 +489,20 @@ namespace MWVR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VRViewer::finalDrawCallback(osg::RenderInfo& info)
|
||||||
|
{
|
||||||
|
auto* session = Environment::get().getSession();
|
||||||
|
auto* frameMeta = session->getFrame(VRSession::FramePhase::Draw).get();
|
||||||
|
|
||||||
|
if (frameMeta->mShouldSyncFrameLoop)
|
||||||
|
{
|
||||||
|
if (frameMeta->mShouldRender)
|
||||||
|
{
|
||||||
|
blit(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void VRViewer::swapBuffersCallback(osg::GraphicsContext* gc)
|
void VRViewer::swapBuffersCallback(osg::GraphicsContext* gc)
|
||||||
{
|
{
|
||||||
auto* session = Environment::get().getSession();
|
auto* session = Environment::get().getSession();
|
||||||
|
@ -508,4 +527,10 @@ namespace MWVR
|
||||||
{
|
{
|
||||||
mViewer->updateView(left, right);
|
mViewer->updateView(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VRViewer::FinaldrawCallback::operator()(osg::RenderInfo& info, Misc::StereoView::StereoDrawCallback::View view) const
|
||||||
|
{
|
||||||
|
if (view != Misc::StereoView::StereoDrawCallback::View::Left)
|
||||||
|
mViewer->finalDrawCallback(info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,20 @@ namespace MWVR
|
||||||
VRViewer* mViewer;
|
VRViewer* mViewer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FinaldrawCallback : public Misc::StereoView::StereoDrawCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FinaldrawCallback(VRViewer* viewer)
|
||||||
|
: mViewer(viewer)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void operator()(osg::RenderInfo& info, Misc::StereoView::StereoDrawCallback::View view) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
VRViewer* mViewer;
|
||||||
|
};
|
||||||
|
|
||||||
static const std::array<const char*, 2> sViewNames;
|
static const std::array<const char*, 2> sViewNames;
|
||||||
enum class MirrorTextureEye
|
enum class MirrorTextureEye
|
||||||
{
|
{
|
||||||
|
@ -108,7 +122,8 @@ namespace MWVR
|
||||||
void initialDrawCallback(osg::RenderInfo& info);
|
void initialDrawCallback(osg::RenderInfo& info);
|
||||||
void preDrawCallback(osg::RenderInfo& info);
|
void preDrawCallback(osg::RenderInfo& info);
|
||||||
void postDrawCallback(osg::RenderInfo& info);
|
void postDrawCallback(osg::RenderInfo& info);
|
||||||
void blit(osg::GraphicsContext* gc);
|
void finalDrawCallback(osg::RenderInfo& info);
|
||||||
|
void blit(osg::RenderInfo& gc);
|
||||||
void configureXR(osg::GraphicsContext* gc);
|
void configureXR(osg::GraphicsContext* gc);
|
||||||
void configureCallbacks();
|
void configureCallbacks();
|
||||||
void setupMirrorTexture();
|
void setupMirrorTexture();
|
||||||
|
@ -128,6 +143,7 @@ namespace MWVR
|
||||||
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
|
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
|
||||||
osg::ref_ptr<PredrawCallback> mPreDraw{ nullptr };
|
osg::ref_ptr<PredrawCallback> mPreDraw{ nullptr };
|
||||||
osg::ref_ptr<PostdrawCallback> mPostDraw{ nullptr };
|
osg::ref_ptr<PostdrawCallback> mPostDraw{ nullptr };
|
||||||
|
osg::ref_ptr<FinaldrawCallback> mFinalDraw{ nullptr };
|
||||||
std::shared_ptr<UpdateViewCallback> mUpdateViewCallback{ nullptr };
|
std::shared_ptr<UpdateViewCallback> mUpdateViewCallback{ nullptr };
|
||||||
bool mRenderingReady{ false };
|
bool mRenderingReady{ false };
|
||||||
|
|
||||||
|
|
|
@ -461,11 +461,13 @@ namespace Misc
|
||||||
auto initialDrawCB = mInitialDrawCallback;
|
auto initialDrawCB = mInitialDrawCallback;
|
||||||
auto predrawCB = mPreDrawCallback;
|
auto predrawCB = mPreDrawCallback;
|
||||||
auto postDrawCB = mPostDrawCallback;
|
auto postDrawCB = mPostDrawCallback;
|
||||||
|
auto finalDrawCB = mFinalDrawCallback;
|
||||||
|
|
||||||
setCullCallback(nullptr);
|
setCullCallback(nullptr);
|
||||||
setInitialDrawCallback(nullptr);
|
setInitialDrawCallback(nullptr);
|
||||||
setPostdrawCallback(nullptr);
|
setPostdrawCallback(nullptr);
|
||||||
setPredrawCallback(nullptr);
|
setPredrawCallback(nullptr);
|
||||||
|
setFinaldrawCallback(nullptr);
|
||||||
|
|
||||||
disableStereo();
|
disableStereo();
|
||||||
mTechnique = technique;
|
mTechnique = technique;
|
||||||
|
@ -475,6 +477,7 @@ namespace Misc
|
||||||
setInitialDrawCallback(initialDrawCB);
|
setInitialDrawCallback(initialDrawCB);
|
||||||
setPostdrawCallback(predrawCB);
|
setPostdrawCallback(predrawCB);
|
||||||
setPredrawCallback(postDrawCB);
|
setPredrawCallback(postDrawCB);
|
||||||
|
setFinaldrawCallback(finalDrawCB);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StereoView::update()
|
void StereoView::update()
|
||||||
|
@ -705,6 +708,12 @@ namespace Misc
|
||||||
mMainCamera->setPostDrawCallback(cb);
|
mMainCamera->setPostDrawCallback(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StereoView::setFinaldrawCallback(osg::ref_ptr<osg::Camera::DrawCallback> cb)
|
||||||
|
{
|
||||||
|
mFinalDrawCallback = cb;
|
||||||
|
mMainCamera->setFinalDrawCallback(cb);
|
||||||
|
}
|
||||||
|
|
||||||
void StereoView::setCullCallback(osg::ref_ptr<osg::NodeCallback> cb)
|
void StereoView::setCullCallback(osg::ref_ptr<osg::NodeCallback> cb)
|
||||||
{
|
{
|
||||||
mMainCamera->setCullCallback(cb);
|
mMainCamera->setCullCallback(cb);
|
||||||
|
@ -746,4 +755,47 @@ namespace Misc
|
||||||
{
|
{
|
||||||
return mRightCamera->getViewMatrix();
|
return mRightCamera->getViewMatrix();
|
||||||
}
|
}
|
||||||
|
void StereoView::StereoDrawCallback::operator()(osg::RenderInfo& info) const
|
||||||
|
{
|
||||||
|
// OSG does not give any information about stereo in these callbacks so i have to infer this myself.
|
||||||
|
// And hopefully OSG won't change this behaviour.
|
||||||
|
|
||||||
|
View view = View::Both;
|
||||||
|
|
||||||
|
auto camera = info.getCurrentCamera();
|
||||||
|
auto viewport = camera->getViewport();
|
||||||
|
|
||||||
|
// Find the current scene view.
|
||||||
|
osg::GraphicsOperation* graphicsOperation = info.getCurrentCamera()->getRenderer();
|
||||||
|
osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation);
|
||||||
|
for (int i = 0; i < 2; i++) // OSG alternates between two sceneviews.
|
||||||
|
{
|
||||||
|
auto* sceneView = renderer->getSceneView(i);
|
||||||
|
// The render info argument is a member of scene view, allowing me to identify it.
|
||||||
|
if (&sceneView->getRenderInfo() == &info)
|
||||||
|
{
|
||||||
|
// Now i can simply examine the viewport.
|
||||||
|
auto activeViewport = static_cast<osg::Viewport*>(sceneView->getLocalStateSet()->getAttribute(osg::StateAttribute::Type::VIEWPORT, 0));
|
||||||
|
if (activeViewport)
|
||||||
|
{
|
||||||
|
if (activeViewport->width() == viewport->width() && activeViewport->height() == viewport->height())
|
||||||
|
view = View::Both;
|
||||||
|
else if (activeViewport->x() == viewport->x() && activeViewport->y() == viewport->y())
|
||||||
|
view = View::Left;
|
||||||
|
else
|
||||||
|
view = View::Right;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// OSG always sets a viewport in the local stateset if osg's stereo is enabled.
|
||||||
|
// If it isn't, assume both.
|
||||||
|
view = View::Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator()(info, view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,29 @@ namespace Misc
|
||||||
GeometryShader_IndexedViewports, //!< Frustum camera culls and draws stereo into indexed viewports using an automatically generated geometry shader.
|
GeometryShader_IndexedViewports, //!< Frustum camera culls and draws stereo into indexed viewports using an automatically generated geometry shader.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! A draw callback that adds stereo information to the operator.
|
||||||
|
//! The stereo information is an enum describing which of the views the callback concerns.
|
||||||
|
//! With some stereo methods, there is only one callback, in which case the enum will be 'Both'.
|
||||||
|
//!
|
||||||
|
//! A typical use case of this callback is to prevent firing callbacks twice and correctly identifying the last/first callback.
|
||||||
|
struct StereoDrawCallback : public osg::Camera::DrawCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class View
|
||||||
|
{
|
||||||
|
Both, Left, Right
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
StereoDrawCallback()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void operator()(osg::RenderInfo& info) const override;
|
||||||
|
|
||||||
|
virtual void operator()(osg::RenderInfo& info, View view) const = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
static StereoView& instance();
|
static StereoView& instance();
|
||||||
|
|
||||||
//! Adds two cameras in stereo to the mainCamera.
|
//! Adds two cameras in stereo to the mainCamera.
|
||||||
|
@ -121,6 +144,9 @@ namespace Misc
|
||||||
//! Set the postdraw callback on the appropriate camera object
|
//! Set the postdraw callback on the appropriate camera object
|
||||||
void setPostdrawCallback(osg::ref_ptr<osg::Camera::DrawCallback> cb);
|
void setPostdrawCallback(osg::ref_ptr<osg::Camera::DrawCallback> cb);
|
||||||
|
|
||||||
|
//! Set the final draw callback on the appropriate camera object
|
||||||
|
void setFinaldrawCallback(osg::ref_ptr<osg::Camera::DrawCallback> cb);
|
||||||
|
|
||||||
//! Set the cull callback on the appropriate camera object
|
//! Set the cull callback on the appropriate camera object
|
||||||
void setCullCallback(osg::ref_ptr<osg::NodeCallback> cb);
|
void setCullCallback(osg::ref_ptr<osg::NodeCallback> cb);
|
||||||
|
|
||||||
|
@ -180,6 +206,7 @@ namespace Misc
|
||||||
osg::ref_ptr<osg::Camera::DrawCallback> mInitialDrawCallback{ nullptr };
|
osg::ref_ptr<osg::Camera::DrawCallback> mInitialDrawCallback{ nullptr };
|
||||||
osg::ref_ptr<osg::Camera::DrawCallback> mPreDrawCallback{ nullptr };
|
osg::ref_ptr<osg::Camera::DrawCallback> mPreDrawCallback{ nullptr };
|
||||||
osg::ref_ptr<osg::Camera::DrawCallback> mPostDrawCallback{ nullptr };
|
osg::ref_ptr<osg::Camera::DrawCallback> mPostDrawCallback{ nullptr };
|
||||||
|
osg::ref_ptr<osg::Camera::DrawCallback> mFinalDrawCallback{ nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Overrides all stereo-related states/uniforms to disable stereo for the scene rendered by camera
|
//! Overrides all stereo-related states/uniforms to disable stereo for the scene rendered by camera
|
||||||
|
|
Loading…
Reference in a new issue