mirror of https://github.com/OpenMW/openmw.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
11 KiB
C++
267 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();
|
|
}
|
|
}
|