mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 14:56:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			266 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			266 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "rtt.hpp"
 | 
						|
#include "util.hpp"
 | 
						|
 | 
						|
#include <osg/Texture2D>
 | 
						|
#include <osg/Texture2DArray>
 | 
						|
#include <osgUtil/CullVisitor>
 | 
						|
 | 
						|
#include <components/sceneutil/color.hpp>
 | 
						|
#include <components/sceneutil/depth.hpp>
 | 
						|
#include <components/sceneutil/nodecallback.hpp>
 | 
						|
#include <components/stereo/multiview.hpp>
 | 
						|
#include <components/stereo/stereomanager.hpp>
 | 
						|
 | 
						|
namespace SceneUtil
 | 
						|
{
 | 
						|
    class CullCallback : public SceneUtil::NodeCallback<CullCallback, RTTNode*, osgUtil::CullVisitor*>
 | 
						|
    {
 | 
						|
    public:
 | 
						|
        void operator()(RTTNode* node, osgUtil::CullVisitor* cv) { node->cull(cv); }
 | 
						|
    };
 | 
						|
 | 
						|
    RTTNode::RTTNode(uint32_t textureWidth, uint32_t textureHeight, uint32_t samples, bool generateMipmaps,
 | 
						|
        int renderOrderNum, StereoAwareness stereoAwareness, bool addMSAAIntermediateTarget)
 | 
						|
        : mTextureWidth(textureWidth)
 | 
						|
        , mTextureHeight(textureHeight)
 | 
						|
        , mSamples(samples)
 | 
						|
        , mGenerateMipmaps(generateMipmaps)
 | 
						|
        , mColorBufferInternalFormat(Color::colorInternalFormat())
 | 
						|
        , mDepthBufferInternalFormat(SceneUtil::AutoDepth::depthInternalFormat())
 | 
						|
        , mRenderOrderNum(renderOrderNum)
 | 
						|
        , mStereoAwareness(stereoAwareness)
 | 
						|
        , mAddMSAAIntermediateTarget(addMSAAIntermediateTarget)
 | 
						|
    {
 | 
						|
        addCullCallback(new CullCallback);
 | 
						|
        setCullingActive(false);
 | 
						|
    }
 | 
						|
 | 
						|
    RTTNode::~RTTNode()
 | 
						|
    {
 | 
						|
        for (auto& vdd : mViewDependentDataMap)
 | 
						|
        {
 | 
						|
            auto* camera = vdd.second->mCamera.get();
 | 
						|
            if (camera)
 | 
						|
            {
 | 
						|
                camera->removeChildren(0, camera->getNumChildren());
 | 
						|
            }
 | 
						|
        }
 | 
						|
        mViewDependentDataMap.clear();
 | 
						|
    }
 | 
						|
 | 
						|
    void RTTNode::cull(osgUtil::CullVisitor* cv)
 | 
						|
    {
 | 
						|
        auto frameNumber = cv->getFrameStamp()->getFrameNumber();
 | 
						|
        auto* vdd = getViewDependentData(cv);
 | 
						|
        if (frameNumber > vdd->mFrameNumber)
 | 
						|
        {
 | 
						|
            apply(vdd->mCamera);
 | 
						|
            if (Stereo::getStereo())
 | 
						|
            {
 | 
						|
                auto& sm = Stereo::Manager::instance();
 | 
						|
                if (sm.getEye(cv) == Stereo::Eye::Left)
 | 
						|
                    applyLeft(vdd->mCamera);
 | 
						|
                if (sm.getEye(cv) == Stereo::Eye::Right)
 | 
						|
                    applyRight(vdd->mCamera);
 | 
						|
            }
 | 
						|
            vdd->mCamera->accept(*cv);
 | 
						|
        }
 | 
						|
        vdd->mFrameNumber = frameNumber;
 | 
						|
    }
 | 
						|
 | 
						|
    void RTTNode::setColorBufferInternalFormat(GLint internalFormat)
 | 
						|
    {
 | 
						|
        mColorBufferInternalFormat = internalFormat;
 | 
						|
    }
 | 
						|
 | 
						|
    void RTTNode::setDepthBufferInternalFormat(GLint internalFormat)
 | 
						|
    {
 | 
						|
        mDepthBufferInternalFormat = internalFormat;
 | 
						|
    }
 | 
						|
 | 
						|
    bool RTTNode::shouldDoPerViewMapping()
 | 
						|
    {
 | 
						|
        if (mStereoAwareness != StereoAwareness::Aware)
 | 
						|
            return false;
 | 
						|
        if (!Stereo::getMultiview())
 | 
						|
            return true;
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    bool RTTNode::shouldDoTextureArray()
 | 
						|
    {
 | 
						|
        if (mStereoAwareness == StereoAwareness::Unaware)
 | 
						|
            return false;
 | 
						|
        if (Stereo::getMultiview())
 | 
						|
            return true;
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    bool RTTNode::shouldDoTextureView()
 | 
						|
    {
 | 
						|
        if (mStereoAwareness != StereoAwareness::Unaware_MultiViewShaders)
 | 
						|
            return false;
 | 
						|
        if (Stereo::getMultiview())
 | 
						|
            return true;
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    osg::Texture2DArray* RTTNode::createTextureArray(GLint internalFormat)
 | 
						|
    {
 | 
						|
        osg::Texture2DArray* textureArray = new osg::Texture2DArray;
 | 
						|
        textureArray->setTextureSize(mTextureWidth, mTextureHeight, 2);
 | 
						|
        textureArray->setInternalFormat(internalFormat);
 | 
						|
        GLenum sourceFormat = 0;
 | 
						|
        GLenum sourceType = 0;
 | 
						|
        if (SceneUtil::isDepthFormat(internalFormat))
 | 
						|
        {
 | 
						|
            SceneUtil::getDepthFormatSourceFormatAndType(internalFormat, sourceFormat, sourceType);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            SceneUtil::getColorFormatSourceFormatAndType(internalFormat, sourceFormat, sourceType);
 | 
						|
        }
 | 
						|
        textureArray->setSourceFormat(sourceFormat);
 | 
						|
        textureArray->setSourceType(sourceType);
 | 
						|
        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::Texture2D* RTTNode::createTexture(GLint internalFormat)
 | 
						|
    {
 | 
						|
        osg::Texture2D* texture = new osg::Texture2D;
 | 
						|
        texture->setTextureSize(mTextureWidth, mTextureHeight);
 | 
						|
        texture->setInternalFormat(internalFormat);
 | 
						|
        GLenum sourceFormat = 0;
 | 
						|
        GLenum sourceType = 0;
 | 
						|
        if (SceneUtil::isDepthFormat(internalFormat))
 | 
						|
        {
 | 
						|
            SceneUtil::getDepthFormatSourceFormatAndType(internalFormat, sourceFormat, sourceType);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            SceneUtil::getColorFormatSourceFormatAndType(internalFormat, sourceFormat, sourceType);
 | 
						|
        }
 | 
						|
        texture->setSourceFormat(sourceFormat);
 | 
						|
        texture->setSourceType(sourceType);
 | 
						|
        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::Texture* RTTNode::getColorTexture(osgUtil::CullVisitor* cv)
 | 
						|
    {
 | 
						|
        return getViewDependentData(cv)->mColorTexture;
 | 
						|
    }
 | 
						|
 | 
						|
    osg::Texture* RTTNode::getDepthTexture(osgUtil::CullVisitor* cv)
 | 
						|
    {
 | 
						|
        return getViewDependentData(cv)->mDepthTexture;
 | 
						|
    }
 | 
						|
 | 
						|
    osg::Camera* RTTNode::getCamera(osgUtil::CullVisitor* cv)
 | 
						|
    {
 | 
						|
        return getViewDependentData(cv)->mCamera;
 | 
						|
    }
 | 
						|
 | 
						|
    RTTNode::ViewDependentData* RTTNode::getViewDependentData(osgUtil::CullVisitor* cv)
 | 
						|
    {
 | 
						|
        if (!shouldDoPerViewMapping())
 | 
						|
            // Always setting it to null is an easy way to disable per-view mapping when mDoPerViewMapping is false.
 | 
						|
            // This is safe since the visitor is never dereferenced.
 | 
						|
            cv = nullptr;
 | 
						|
 | 
						|
        if (mViewDependentDataMap.count(cv) == 0)
 | 
						|
        {
 | 
						|
            auto camera = new osg::Camera();
 | 
						|
            auto vdd = std::make_shared<ViewDependentData>();
 | 
						|
            mViewDependentDataMap[cv] = vdd;
 | 
						|
            mViewDependentDataMap[cv]->mCamera = camera;
 | 
						|
 | 
						|
            camera->setRenderOrder(osg::Camera::PRE_RENDER, mRenderOrderNum);
 | 
						|
            camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 | 
						|
            camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
 | 
						|
            camera->setViewport(0, 0, mTextureWidth, mTextureHeight);
 | 
						|
            SceneUtil::setCameraClearDepth(camera);
 | 
						|
 | 
						|
            setDefaults(camera);
 | 
						|
 | 
						|
            if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER))
 | 
						|
                vdd->mColorTexture = camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._texture;
 | 
						|
            if (camera->getBufferAttachmentMap().count(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER))
 | 
						|
                vdd->mDepthTexture
 | 
						|
                    = camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._texture;
 | 
						|
 | 
						|
            if (shouldDoTextureArray())
 | 
						|
            {
 | 
						|
                // Create any buffer attachments not added in setDefaults
 | 
						|
                if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER) == 0)
 | 
						|
                {
 | 
						|
                    vdd->mColorTexture = createTextureArray(mColorBufferInternalFormat);
 | 
						|
                    camera->attach(osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0,
 | 
						|
                        Stereo::osgFaceControlledByMultiviewShader(), mGenerateMipmaps, mSamples);
 | 
						|
                    SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER,
 | 
						|
                        vdd->mColorTexture, 0, Stereo::osgFaceControlledByMultiviewShader(), mGenerateMipmaps,
 | 
						|
                        mAddMSAAIntermediateTarget);
 | 
						|
                }
 | 
						|
 | 
						|
                if (camera->getBufferAttachmentMap().count(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER) == 0)
 | 
						|
                {
 | 
						|
                    vdd->mDepthTexture = createTextureArray(mDepthBufferInternalFormat);
 | 
						|
                    camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, vdd->mDepthTexture, 0,
 | 
						|
                        Stereo::osgFaceControlledByMultiviewShader(), false, mSamples);
 | 
						|
                }
 | 
						|
 | 
						|
                if (shouldDoTextureView())
 | 
						|
                {
 | 
						|
                    // In this case, shaders being set to multiview forces us to render to a multiview framebuffer even
 | 
						|
                    // though we don't need that. This forces us to make Texture2DArray. To make this possible to sample
 | 
						|
                    // as a Texture2D, make a Texture2D view into the texture array.
 | 
						|
                    vdd->mColorTexture = Stereo::createTextureView_Texture2DFromTexture2DArray(
 | 
						|
                        static_cast<osg::Texture2DArray*>(vdd->mColorTexture.get()), 0);
 | 
						|
                    vdd->mDepthTexture = Stereo::createTextureView_Texture2DFromTexture2DArray(
 | 
						|
                        static_cast<osg::Texture2DArray*>(vdd->mDepthTexture.get()), 0);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Create any buffer attachments not added in setDefaults
 | 
						|
                if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER) == 0)
 | 
						|
                {
 | 
						|
                    vdd->mColorTexture = createTexture(mColorBufferInternalFormat);
 | 
						|
                    camera->attach(osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0, 0, mGenerateMipmaps, mSamples);
 | 
						|
                    SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER,
 | 
						|
                        vdd->mColorTexture, 0, 0, mGenerateMipmaps, mAddMSAAIntermediateTarget);
 | 
						|
                }
 | 
						|
 | 
						|
                if (camera->getBufferAttachmentMap().count(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER) == 0)
 | 
						|
                {
 | 
						|
                    vdd->mDepthTexture = createTexture(mDepthBufferInternalFormat);
 | 
						|
                    camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, vdd->mDepthTexture, 0, 0, false, mSamples);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // OSG appears not to properly initialize this metadata. So when multisampling is enabled, OSG will use
 | 
						|
            // incorrect formats for the resolve buffers.
 | 
						|
            if (mSamples > 1)
 | 
						|
            {
 | 
						|
                camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._internalFormat
 | 
						|
                    = mColorBufferInternalFormat;
 | 
						|
                camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._mipMapGeneration = mGenerateMipmaps;
 | 
						|
                camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._internalFormat
 | 
						|
                    = mDepthBufferInternalFormat;
 | 
						|
                camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._mipMapGeneration
 | 
						|
                    = mGenerateMipmaps;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return mViewDependentDataMap[cv].get();
 | 
						|
    }
 | 
						|
}
 |