Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
33 kB · 849 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850#include "multiview.hpp"
#include <osg/FrameBufferObject>#include <osg/GLExtensions>#include <osg/Texture2D>#include <osg/Texture2DArray>#include <osg/Texture2DMultisample>#include <osgUtil/CullVisitor>#include <osgUtil/RenderStage>
#ifdef OSG_HAS_MULTIVIEW#include <osg/Texture2DMultisampleArray>#endif
#include <components/debug/debuglog.hpp>#include <components/sceneutil/nodecallback.hpp>#include <components/settings/settings.hpp>#include <components/stereo/stereomanager.hpp>
#include <algorithm>
namespace Stereo{ namespace { bool getMultiviewSupportedImpl(unsigned int contextID) {#ifdef OSG_HAS_MULTIVIEW if (!osg::isGLExtensionSupported(contextID, "GL_OVR_multiview")) { Log(Debug::Verbose) << "Disabling Multiview (opengl extension \"GL_OVR_multiview\" not supported)"; return false; }
if (!osg::isGLExtensionSupported(contextID, "GL_OVR_multiview2")) { Log(Debug::Verbose) << "Disabling Multiview (opengl extension \"GL_OVR_multiview2\" not supported)"; return false; } return true;#else Log(Debug::Verbose) << "Disabling Multiview (OSG does not support multiview)"; return false;#endif }
bool getMultiviewSupported(unsigned int contextID) { static bool supported = getMultiviewSupportedImpl(contextID); return supported; }
bool getTextureViewSupportedImpl(unsigned int contextID) { if (!osg::isGLExtensionOrVersionSupported(contextID, "ARB_texture_view", 4.3f)) { Log(Debug::Verbose) << "Disabling texture views (opengl extension \"ARB_texture_view\" not supported)"; return false; } return true; }
bool getTextureViewSupported(unsigned int contextID) { static bool supported = getTextureViewSupportedImpl(contextID); return supported; }
bool getMultiviewImpl(unsigned int contextID) { if (!Stereo::getStereo()) { Log(Debug::Verbose) << "Disabling Multiview (disabled by config)"; return false; }
if (!getMultiviewSupported(contextID)) { return false; }
if (!getTextureViewSupported(contextID)) { Log(Debug::Verbose) << "Disabling Multiview (texture views not supported)"; return false; }
Log(Debug::Verbose) << "Enabling Multiview"; return true; }
static bool sMultiview = false;
bool getMultiview(unsigned int contextID) { static bool multiView = getMultiviewImpl(contextID); return multiView; } }
bool getTextureViewSupported() { return getTextureViewSupported(0); }
bool getMultiview() { return getMultiview(0); }
void configureExtensions(unsigned int contextID, bool enableMultiview) { getTextureViewSupported(contextID); getMultiviewSupported(contextID);
if (enableMultiview) { sMultiview = getMultiview(contextID); } else { Log(Debug::Verbose) << "Disabling Multiview (disabled by config)"; sMultiview = false; } }
void setVertexBufferHint(bool enableMultiview, bool allowDisplayListsForMultiview) { if (getStereo() && enableMultiview) { auto* ds = osg::DisplaySettings::instance().get(); if (!allowDisplayListsForMultiview && ds->getVertexBufferHint() == osg::DisplaySettings::VertexBufferHint::NO_PREFERENCE) { // Note that this only works if this code is executed before realize() is called on the viewer. // The hint is read by the state object only once, before the user realize operations are run. // Therefore we have to set this hint without access to a graphics context to let us determine // if multiview will actually be supported or not. So if the user has requested multiview, we // will just have to set it regardless. ds->setVertexBufferHint(osg::DisplaySettings::VertexBufferHint::VERTEX_BUFFER_OBJECT); Log(Debug::Verbose) << "Disabling display lists"; } } }
class Texture2DViewSubloadCallback : public osg::Texture2D::SubloadCallback { public: Texture2DViewSubloadCallback(osg::Texture2DArray* textureArray, int layer);
void load(const osg::Texture2D& texture, osg::State& state) const override; void subload(const osg::Texture2D& texture, osg::State& state) const override;
private: osg::ref_ptr<osg::Texture2DArray> mTextureArray; int mLayer; };
Texture2DViewSubloadCallback::Texture2DViewSubloadCallback(osg::Texture2DArray* textureArray, int layer) : mTextureArray(textureArray) , mLayer(layer) { }
void Texture2DViewSubloadCallback::load(const osg::Texture2D& texture, osg::State& state) const { state.checkGLErrors("before Texture2DViewSubloadCallback::load()");
auto contextId = state.getContextID(); auto* gl = osg::GLExtensions::Get(contextId, false); mTextureArray->apply(state);
auto sourceTextureObject = mTextureArray->getTextureObject(contextId); if (!sourceTextureObject) { Log(Debug::Error) << "Texture2DViewSubloadCallback: Texture2DArray did not have a texture object"; return; }
osg::Texture::TextureObject* const targetTextureObject = texture.getTextureObject(contextId); if (targetTextureObject == nullptr) { Log(Debug::Error) << "Texture2DViewSubloadCallback: Texture2D did not have a texture object"; return; }
// OSG already bound this texture ID, giving it a target. // Delete it and make a new texture ID. glBindTexture(GL_TEXTURE_2D, 0); glDeleteTextures(1, &targetTextureObject->_id); glGenTextures(1, &targetTextureObject->_id);
auto sourceId = sourceTextureObject->_id; auto targetId = targetTextureObject->_id; auto internalFormat = sourceTextureObject->_profile._internalFormat; auto levels = std::max(1, sourceTextureObject->_profile._numMipmapLevels);
{ ////// OSG BUG // Texture views require immutable storage. // OSG should always give immutable storage to sized internal formats, but does not do so for depth formats. // Fortunately, we can just call glTexStorage3D here to make it immutable. This probably discards depth info // for that frame, but whatever.#ifndef GL_TEXTURE_IMMUTABLE_FORMAT#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F#endif // Store any current binding and re-apply it after so i don't mess with state. GLint oldBinding = 0; glGetIntegerv(GL_TEXTURE_BINDING_2D_ARRAY, &oldBinding);
// Bind the source texture and check if it's immutable. glBindTexture(GL_TEXTURE_2D_ARRAY, sourceId); GLint immutable = 0; glGetTexParameteriv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_IMMUTABLE_FORMAT, &immutable); if (!immutable) { // It wasn't immutable, so make it immutable. gl->glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, internalFormat, sourceTextureObject->_profile._width, sourceTextureObject->_profile._height, 2); state.checkGLErrors("after Texture2DViewSubloadCallback::load()::glTexStorage3D"); } glBindTexture(GL_TEXTURE_2D_ARRAY, oldBinding); }
gl->glTextureView(targetId, GL_TEXTURE_2D, sourceId, internalFormat, 0, levels, mLayer, 1); state.checkGLErrors("after Texture2DViewSubloadCallback::load()::glTextureView"); glBindTexture(GL_TEXTURE_2D, targetId); }
void Texture2DViewSubloadCallback::subload(const osg::Texture2D& texture, osg::State& state) const { // Nothing to do }
osg::ref_ptr<osg::Texture2D> createTextureView_Texture2DFromTexture2DArray( osg::Texture2DArray* textureArray, int layer) { if (!getTextureViewSupported()) { Log(Debug::Error) << "createTextureView_Texture2DFromTexture2DArray: Tried to use a texture view but " "glTextureView is not supported"; return nullptr; }
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D; texture2d->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture2d->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); texture2d->setSubloadCallback(new Texture2DViewSubloadCallback(textureArray, layer)); texture2d->setTextureSize(textureArray->getTextureWidth(), textureArray->getTextureHeight()); texture2d->setBorderColor(textureArray->getBorderColor()); texture2d->setBorderWidth(textureArray->getBorderWidth()); texture2d->setLODBias(textureArray->getLODBias()); texture2d->setFilter(osg::Texture::FilterParameter::MAG_FILTER, textureArray->getFilter(osg::Texture::FilterParameter::MAG_FILTER)); texture2d->setFilter(osg::Texture::FilterParameter::MIN_FILTER, textureArray->getFilter(osg::Texture::FilterParameter::MIN_FILTER)); texture2d->setInternalFormat(textureArray->getInternalFormat()); texture2d->setNumMipmapLevels(textureArray->getNumMipmapLevels()); return texture2d; }
#ifdef OSG_HAS_MULTIVIEW //! Draw callback that, if set on a RenderStage, resolves MSAA after draw. Needed when using custom fbo/resolve fbos //! on renderstages in combination with multiview. struct MultiviewMSAAResolveCallback : public osgUtil::RenderBin::DrawCallback { void drawImplementation( osgUtil::RenderBin* bin, osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous) override { osgUtil::RenderStage* stage = static_cast<osgUtil::RenderStage*>(bin); auto msaaFbo = stage->getFrameBufferObject(); auto resolveFbo = stage->getMultisampleResolveFramebufferObject(); if (msaaFbo != mMsaaFbo) { mMsaaFbo = msaaFbo; setupMsaaLayers(); } if (resolveFbo != mFbo) { mFbo = resolveFbo; setupLayers(); }
// Null the resolve framebuffer to keep osg from doing redundant work. stage->setMultisampleResolveFramebufferObject(nullptr);
// Do the actual render work bin->drawImplementation(renderInfo, previous);
// Blit layers osg::State& state = *renderInfo.getState(); osg::GLExtensions* ext = state.get<osg::GLExtensions>(); for (int i = 0; i < 2; i++) { mLayers[i]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); mMsaaLayers[i]->apply(state, osg::FrameBufferObject::READ_FRAMEBUFFER); ext->glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT, GL_NEAREST); } msaaFbo->apply(state, osg::FrameBufferObject::READ_DRAW_FRAMEBUFFER); }
void setupLayers() { const auto& attachments = mFbo->getAttachmentMap(); for (int i = 0; i < 2; i++) { mLayers[i] = new osg::FrameBufferObject; // Intentionally not using ref& so attachment can be non-const for (auto [component, attachment] : attachments) { osg::Texture2DArray* texture = static_cast<osg::Texture2DArray*>(attachment.getTexture()); mLayers[i]->setAttachment(component, osg::FrameBufferAttachment(texture, i)); mWidth = texture->getTextureWidth(); mHeight = texture->getTextureHeight(); } } }
void setupMsaaLayers() { const auto& attachments = mMsaaFbo->getAttachmentMap(); for (int i = 0; i < 2; i++) { mMsaaLayers[i] = new osg::FrameBufferObject; // Intentionally not using ref& so attachment can be non-const for (auto [component, attachment] : attachments) { osg::Texture2DMultisampleArray* texture = static_cast<osg::Texture2DMultisampleArray*>(attachment.getTexture()); mMsaaLayers[i]->setAttachment(component, osg::FrameBufferAttachment(texture, i)); mWidth = texture->getTextureWidth(); mHeight = texture->getTextureHeight(); } } }
osg::ref_ptr<osg::FrameBufferObject> mFbo; osg::ref_ptr<osg::FrameBufferObject> mMsaaFbo; osg::ref_ptr<osg::FrameBufferObject> mLayers[2]; osg::ref_ptr<osg::FrameBufferObject> mMsaaLayers[2]; int mWidth; int mHeight; };#endif
void setMultiviewMSAAResolveCallback(osgUtil::RenderStage* renderStage) {#ifdef OSG_HAS_MULTIVIEW if (Stereo::getMultiview()) { renderStage->setDrawCallback(new MultiviewMSAAResolveCallback); }#endif }
void setMultiviewMatrices( osg::StateSet* stateset, const std::array<osg::Matrix, 2>& projection, bool createInverseMatrices) { auto* projUniform = stateset->getUniform("projectionMatrixMultiView"); if (!projUniform) { projUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projectionMatrixMultiView", 2); stateset->addUniform(projUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); }
projUniform->setElement(0, projection[0]); projUniform->setElement(1, projection[1]);
if (createInverseMatrices) { auto* invUniform = stateset->getUniform("invProjectionMatrixMultiView"); if (!invUniform) { invUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "invProjectionMatrixMultiView", 2); stateset->addUniform(invUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); }
invUniform->setElement(0, osg::Matrix::inverse(projection[0])); invUniform->setElement(1, osg::Matrix::inverse(projection[1])); } }
void setMultiviewCompatibleTextureSize(osg::Texture* tex, int w, int h) { switch (tex->getTextureTarget()) { case GL_TEXTURE_2D: static_cast<osg::Texture2D*>(tex)->setTextureSize(w, h); break; case GL_TEXTURE_2D_ARRAY: static_cast<osg::Texture2DArray*>(tex)->setTextureSize(w, h, 2); break; case GL_TEXTURE_2D_MULTISAMPLE: static_cast<osg::Texture2DMultisample*>(tex)->setTextureSize(w, h); break;#ifdef OSG_HAS_MULTIVIEW case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: static_cast<osg::Texture2DMultisampleArray*>(tex)->setTextureSize(w, h, 2); break;#endif default: throw std::logic_error("Invalid texture type received"); } }
osg::ref_ptr<osg::Texture> createMultiviewCompatibleTexture(int width, int height, int samples) {#ifdef OSG_HAS_MULTIVIEW if (Stereo::getMultiview()) { if (samples > 1) { auto tex = new osg::Texture2DMultisampleArray(); tex->setTextureSize(width, height, 2); tex->setNumSamples(samples); return tex; } else { auto tex = new osg::Texture2DArray(); tex->setTextureSize(width, height, 2); return tex; } } else#endif { if (samples > 1) { auto tex = new osg::Texture2DMultisample(); tex->setTextureSize(width, height); tex->setNumSamples(samples); tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); return tex; } else { auto tex = new osg::Texture2D(); tex->setTextureSize(width, height); tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); return tex; } } }
osg::FrameBufferAttachment createMultiviewCompatibleAttachment(osg::Texture* tex) { switch (tex->getTextureTarget()) { case GL_TEXTURE_2D: { auto* tex2d = static_cast<osg::Texture2D*>(tex); return osg::FrameBufferAttachment(tex2d); } case GL_TEXTURE_2D_MULTISAMPLE: { auto* tex2dMsaa = static_cast<osg::Texture2DMultisample*>(tex); return osg::FrameBufferAttachment(tex2dMsaa); }#ifdef OSG_HAS_MULTIVIEW case GL_TEXTURE_2D_ARRAY: { auto* tex2dArray = static_cast<osg::Texture2DArray*>(tex); return osg::FrameBufferAttachment(tex2dArray, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0); } case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: { auto* tex2dMsaaArray = static_cast<osg::Texture2DMultisampleArray*>(tex); return osg::FrameBufferAttachment(tex2dMsaaArray, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0); }#endif default: throw std::logic_error("Invalid texture type received"); } }
unsigned int osgFaceControlledByMultiviewShader() {#ifdef OSG_HAS_MULTIVIEW return osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER;#else return 0;#endif }
class UpdateRenderStagesCallback : public SceneUtil::NodeCallback<UpdateRenderStagesCallback, osg::Node*, osgUtil::CullVisitor*> { public: UpdateRenderStagesCallback(Stereo::MultiviewFramebuffer* multiviewFramebuffer) : mMultiviewFramebuffer(multiviewFramebuffer) { mViewport = new osg::Viewport(0, 0, multiviewFramebuffer->width(), multiviewFramebuffer->height()); mViewportStateset = new osg::StateSet(); mViewportStateset->setAttribute(mViewport.get()); }
void operator()(osg::Node* node, osgUtil::CullVisitor* cv) { osgUtil::RenderStage* renderStage = cv->getCurrentRenderStage();
bool msaa = mMultiviewFramebuffer->samples() > 1;
if (!Stereo::getMultiview()) { auto eye = static_cast<int>(Stereo::Manager::instance().getEye(cv)); if (eye < 2) { if (msaa) { renderStage->setFrameBufferObject(mMultiviewFramebuffer->layerMsaaFbo(eye)); renderStage->setMultisampleResolveFramebufferObject(mMultiviewFramebuffer->layerFbo(eye)); } else { renderStage->setFrameBufferObject(mMultiviewFramebuffer->layerFbo(eye)); } } }
// OSG tries to do a horizontal split, but we want to render to separate framebuffers instead. renderStage->setViewport(mViewport); cv->pushStateSet(mViewportStateset.get()); traverse(node, cv); cv->popStateSet(); }
private: Stereo::MultiviewFramebuffer* mMultiviewFramebuffer; osg::ref_ptr<osg::Viewport> mViewport; osg::ref_ptr<osg::StateSet> mViewportStateset; };
MultiviewFramebuffer::MultiviewFramebuffer(int width, int height, int samples) : mWidth(width) , mHeight(height) , mSamples(samples) , mMultiview(getMultiview()) , mMultiviewFbo{ new osg::FrameBufferObject } , mLayerFbo{ new osg::FrameBufferObject, new osg::FrameBufferObject } , mLayerMsaaFbo{ new osg::FrameBufferObject, new osg::FrameBufferObject } { }
MultiviewFramebuffer::~MultiviewFramebuffer() = default;
void MultiviewFramebuffer::attachColorComponent(GLint sourceFormat, GLint sourceType, GLint internalFormat) { if (mMultiview) {#ifdef OSG_HAS_MULTIVIEW mMultiviewColorTexture = createTextureArray(sourceFormat, sourceType, internalFormat); mMultiviewFbo->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment( mMultiviewColorTexture, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0)); for (unsigned i = 0; i < 2; i++) { mColorTexture[i] = createTextureView_Texture2DFromTexture2DArray(mMultiviewColorTexture.get(), i); mLayerFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(mColorTexture[i])); }#endif } else { for (unsigned i = 0; i < 2; i++) { if (mSamples > 1) mLayerMsaaFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(new osg::RenderBuffer(mWidth, mHeight, internalFormat, mSamples))); mColorTexture[i] = createTexture(sourceFormat, sourceType, internalFormat); mLayerFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(mColorTexture[i])); } } }
void MultiviewFramebuffer::attachDepthComponent(GLint sourceFormat, GLint sourceType, GLint internalFormat) { if (mMultiview) {#ifdef OSG_HAS_MULTIVIEW mMultiviewDepthTexture = createTextureArray(sourceFormat, sourceType, internalFormat); mMultiviewFbo->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment( mMultiviewDepthTexture, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0)); for (unsigned i = 0; i < 2; i++) { mDepthTexture[i] = createTextureView_Texture2DFromTexture2DArray(mMultiviewDepthTexture.get(), i); mLayerFbo[i]->setAttachment( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mDepthTexture[i])); }#endif } else { for (unsigned i = 0; i < 2; i++) { if (mSamples > 1) mLayerMsaaFbo[i]->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(new osg::RenderBuffer(mWidth, mHeight, internalFormat, mSamples))); mDepthTexture[i] = createTexture(sourceFormat, sourceType, internalFormat); mLayerFbo[i]->setAttachment( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mDepthTexture[i])); } } }
osg::FrameBufferObject* MultiviewFramebuffer::multiviewFbo() { return mMultiviewFbo; }
osg::FrameBufferObject* MultiviewFramebuffer::layerFbo(int i) { return mLayerFbo[i]; }
osg::FrameBufferObject* MultiviewFramebuffer::layerMsaaFbo(int i) { return mLayerMsaaFbo[i]; }
osg::Texture2DArray* MultiviewFramebuffer::multiviewColorBuffer() { return mMultiviewColorTexture; }
osg::Texture2DArray* MultiviewFramebuffer::multiviewDepthBuffer() { return mMultiviewDepthTexture; }
osg::Texture2D* MultiviewFramebuffer::layerColorBuffer(int i) { return mColorTexture[i]; }
osg::Texture2D* MultiviewFramebuffer::layerDepthBuffer(int i) { return mDepthTexture[i]; } void MultiviewFramebuffer::attachTo(osg::Camera* camera) {#ifdef OSG_HAS_MULTIVIEW if (mMultiview) { if (mMultiviewColorTexture) { camera->attach(osg::Camera::COLOR_BUFFER, mMultiviewColorTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, false, mSamples); camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._internalFormat = mMultiviewColorTexture->getInternalFormat(); camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._mipMapGeneration = false; } if (mMultiviewDepthTexture) { camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, mMultiviewDepthTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, false, mSamples); camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._internalFormat = mMultiviewDepthTexture->getInternalFormat(); camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._mipMapGeneration = false; } }#endif camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
if (!mCullCallback) mCullCallback = new UpdateRenderStagesCallback(this); camera->addCullCallback(mCullCallback); }
void MultiviewFramebuffer::detachFrom(osg::Camera* camera) {#ifdef OSG_HAS_MULTIVIEW if (mMultiview) { if (mMultiviewColorTexture) { camera->detach(osg::Camera::COLOR_BUFFER); } if (mMultiviewDepthTexture) { camera->detach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER); } }#endif camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER); if (mCullCallback) camera->removeCullCallback(mCullCallback); }
osg::Texture2D* MultiviewFramebuffer::createTexture(GLint sourceFormat, GLint sourceType, GLint internalFormat) { osg::Texture2D* texture = new osg::Texture2D; texture->setTextureSize(mWidth, mHeight); texture->setSourceFormat(sourceFormat); texture->setSourceType(sourceType); texture->setInternalFormat(internalFormat); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE); return texture; }
osg::Texture2DArray* MultiviewFramebuffer::createTextureArray( GLint sourceFormat, GLint sourceType, GLint internalFormat) { osg::Texture2DArray* textureArray = new osg::Texture2DArray; textureArray->setTextureSize(mWidth, mHeight, 2); textureArray->setSourceFormat(sourceFormat); textureArray->setSourceType(sourceType); textureArray->setInternalFormat(internalFormat); textureArray->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); textureArray->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); textureArray->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); textureArray->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); textureArray->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE); return textureArray; }
osg::FrameBufferAttachment makeSingleLayerAttachmentFromMultilayerAttachment( osg::FrameBufferAttachment attachment, int layer) { osg::Texture* tex = attachment.getTexture();
if (tex->getTextureTarget() == GL_TEXTURE_2D_ARRAY) return osg::FrameBufferAttachment(static_cast<osg::Texture2DArray*>(tex), layer, 0);
#ifdef OSG_HAS_MULTIVIEW if (tex->getTextureTarget() == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) return osg::FrameBufferAttachment(static_cast<osg::Texture2DMultisampleArray*>(tex), layer, 0);#endif
Log(Debug::Error) << "Attempted to extract a layer from an unlayered texture";
return osg::FrameBufferAttachment(); }
MultiviewFramebufferResolve::MultiviewFramebufferResolve( osg::FrameBufferObject* msaaFbo, osg::FrameBufferObject* resolveFbo, GLbitfield blitMask) : mResolveFbo(resolveFbo) , mMsaaFbo(msaaFbo) , mBlitMask(blitMask) { }
void MultiviewFramebufferResolve::setResolveFbo(osg::FrameBufferObject* resolveFbo) { if (resolveFbo != mResolveFbo) dirty(); mResolveFbo = resolveFbo; }
void MultiviewFramebufferResolve::setMsaaFbo(osg::FrameBufferObject* msaaFbo) { if (msaaFbo != mMsaaFbo) dirty(); mMsaaFbo = msaaFbo; }
void MultiviewFramebufferResolve::resolveImplementation(osg::State& state) { if (mDirtyLayers) setupLayers();
osg::GLExtensions* ext = state.get<osg::GLExtensions>();
for (int view : { 0, 1 }) { mResolveLayers[view]->apply(state, osg::FrameBufferObject::BindTarget::DRAW_FRAMEBUFFER); mMsaaLayers[view]->apply(state, osg::FrameBufferObject::BindTarget::READ_FRAMEBUFFER); ext->glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); } } void MultiviewFramebufferResolve::setupLayers() { mDirtyLayers = false; std::vector<osg::FrameBufferObject::BufferComponent> components; if (mBlitMask & GL_DEPTH_BUFFER_BIT) components.push_back(osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER); if (mBlitMask & GL_COLOR_BUFFER_BIT) components.push_back(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER);
mMsaaLayers = { new osg::FrameBufferObject, new osg::FrameBufferObject }; mResolveLayers = { new osg::FrameBufferObject, new osg::FrameBufferObject }; for (auto component : components) { const auto& msaaAttachment = mMsaaFbo->getAttachment(component); mMsaaLayers[0]->setAttachment( component, makeSingleLayerAttachmentFromMultilayerAttachment(msaaAttachment, 0)); mMsaaLayers[1]->setAttachment( component, makeSingleLayerAttachmentFromMultilayerAttachment(msaaAttachment, 1));
const auto& resolveAttachment = mResolveFbo->getAttachment(component); mResolveLayers[0]->setAttachment( component, makeSingleLayerAttachmentFromMultilayerAttachment(resolveAttachment, 0)); mResolveLayers[1]->setAttachment( component, makeSingleLayerAttachmentFromMultilayerAttachment(resolveAttachment, 1));
mWidth = msaaAttachment.getTexture()->getTextureWidth(); mHeight = msaaAttachment.getTexture()->getTextureHeight(); } }
#ifdef OSG_HAS_MULTIVIEW namespace { struct MultiviewFrustumCallback final : public osg::CullSettings::InitialFrustumCallback { MultiviewFrustumCallback(Stereo::InitialFrustumCallback* ifc) : mIfc(ifc) { }
void setInitialFrustum(osg::CullStack& cullStack, osg::Polytope& frustum) const override { bool nearCulling = false; bool farCulling = false; osg::BoundingBoxd bb; mIfc->setInitialFrustum(cullStack, bb, nearCulling, farCulling); frustum.setToBoundingBox(bb, nearCulling, farCulling); }
Stereo::InitialFrustumCallback* mIfc; }; }#endif
InitialFrustumCallback::InitialFrustumCallback(osg::Camera* camera) : mCamera(camera) {#ifdef OSG_HAS_MULTIVIEW camera->setInitialFrustumCallback(new MultiviewFrustumCallback(this));#endif }
InitialFrustumCallback::~InitialFrustumCallback() {#ifdef OSG_HAS_MULTIVIEW osg::ref_ptr<osg::Camera> camera; if (mCamera.lock(camera)) camera->setInitialFrustumCallback(nullptr);#endif }}