mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-20 07:53:51 +00:00
491 lines
19 KiB
C++
491 lines
19 KiB
C++
/*
|
|
This file is part of Caelum.
|
|
See http://www.ogre3d.org/wiki/index.php/Caelum
|
|
|
|
Copyright (c) 2008 Caelum team. See Contributors.txt for details.
|
|
|
|
Caelum is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published
|
|
by the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Caelum is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with Caelum. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "CaelumPrecompiled.h"
|
|
#include "CaelumExceptions.h"
|
|
#include "DepthComposer.h"
|
|
|
|
using namespace Ogre;
|
|
|
|
namespace Caelum
|
|
{
|
|
DepthComposer::DepthComposer
|
|
(
|
|
Ogre::SceneManager *sceneMgr
|
|
):
|
|
mSceneMgr (sceneMgr),
|
|
mDebugDepthRender (false),
|
|
mSkyDomeHazeEnabled (false),
|
|
mGroundFogEnabled (false),
|
|
mGroundFogDensity (0.1),
|
|
mGroundFogBaseLevel (5),
|
|
mGroundFogVerticalDecay (0.2),
|
|
mGroundFogColour (ColourValue::Black)
|
|
{
|
|
}
|
|
|
|
DepthComposer::~DepthComposer()
|
|
{
|
|
destroyAllViewportInstances();
|
|
}
|
|
|
|
void DepthComposer::setDebugDepthRender (bool value)
|
|
{
|
|
if (mDebugDepthRender == value) {
|
|
return;
|
|
}
|
|
mDebugDepthRender = value;
|
|
onCompositorMaterialChanged ();
|
|
}
|
|
|
|
void DepthComposer::setSkyDomeHazeEnabled (bool value)
|
|
{
|
|
if (mSkyDomeHazeEnabled == value) {
|
|
return;
|
|
}
|
|
mSkyDomeHazeEnabled = value;
|
|
onCompositorMaterialChanged ();
|
|
}
|
|
|
|
void DepthComposer::setGroundFogEnabled (bool value)
|
|
{
|
|
if (mGroundFogEnabled == value) {
|
|
return;
|
|
}
|
|
mGroundFogEnabled = value;
|
|
onCompositorMaterialChanged ();
|
|
}
|
|
|
|
const String& DepthComposer::getCompositorName ()
|
|
{
|
|
// Constant Ogre::Strings for names.
|
|
static const Ogre::String CompositorName_DebugDepthRender =
|
|
"Caelum/DepthComposer_DebugDepthRender";
|
|
static const Ogre::String CompositorName_Dummy =
|
|
"Caelum/DepthComposer_Dummy";
|
|
static const Ogre::String CompositorName_ExpGroundFog =
|
|
"Caelum/DepthComposer_ExpGroundFog";
|
|
static const Ogre::String CompositorName_SkyDomeHaze =
|
|
"Caelum/DepthComposer_SkyDomeHaze";
|
|
static const Ogre::String CompositorName_SkyDomeHaze_ExpGroundFog =
|
|
"Caelum/DepthComposer_SkyDomeHaze_ExpGroundFog";
|
|
|
|
// Should probably build materials and compositors by hand.
|
|
if (mDebugDepthRender) {
|
|
return CompositorName_DebugDepthRender;
|
|
} else if (mSkyDomeHazeEnabled == false && mGroundFogEnabled == false) {
|
|
return CompositorName_Dummy;
|
|
} else if (mSkyDomeHazeEnabled == false && mGroundFogEnabled == true) {
|
|
return CompositorName_ExpGroundFog;
|
|
} else if (mSkyDomeHazeEnabled == true && mGroundFogEnabled == false) {
|
|
return CompositorName_SkyDomeHaze;
|
|
} else if (mSkyDomeHazeEnabled == true && mGroundFogEnabled == true) {
|
|
return CompositorName_SkyDomeHaze_ExpGroundFog;
|
|
} else {
|
|
assert (0);
|
|
return CompositorName_Dummy;
|
|
}
|
|
}
|
|
|
|
void DepthComposer::onCompositorMaterialChanged ()
|
|
{
|
|
ViewportInstanceMap::const_iterator it;
|
|
ViewportInstanceMap::const_iterator begin = mViewportInstanceMap.begin();
|
|
ViewportInstanceMap::const_iterator end = mViewportInstanceMap.end();
|
|
for (it = begin; it != end; ++it) {
|
|
it->second->removeCompositor ();
|
|
it->second->addCompositor ();
|
|
}
|
|
}
|
|
|
|
void DepthComposer::update ()
|
|
{
|
|
ViewportInstanceMap::const_iterator it;
|
|
ViewportInstanceMap::const_iterator begin = mViewportInstanceMap.begin();
|
|
ViewportInstanceMap::const_iterator end = mViewportInstanceMap.end();
|
|
for (it = begin; it != end; ++it) {
|
|
assert(it->first == it->second->getViewport());
|
|
it->second->_update ();
|
|
}
|
|
}
|
|
|
|
DepthComposerInstance::DepthComposerInstance
|
|
(
|
|
DepthComposer* parent,
|
|
Ogre::Viewport* viewport
|
|
):
|
|
mParent(parent),
|
|
mViewport(viewport),
|
|
mCompInst(0)
|
|
{
|
|
LogManager::getSingleton().logMessage (
|
|
"Caelum::DepthComposer: Attaching screen-space fog instance"
|
|
" to viewport \'" + StringConverter::toString ((long)getViewport ()) + "\'"
|
|
" of render target \'" + getViewport()->getTarget ()->getName () + "\'");
|
|
|
|
addCompositor ();
|
|
mDepthRenderer.reset (new DepthRenderer (getViewport ()));
|
|
}
|
|
|
|
DepthComposerInstance::~DepthComposerInstance()
|
|
{
|
|
removeCompositor ();
|
|
mDepthRenderer.reset ();
|
|
|
|
LogManager::getSingleton().logMessage (
|
|
"Caelum::DepthComposer: Detached screen-space fog instance"
|
|
" from viewport \'" + StringConverter::toString ((long)getViewport ()) + "\'"
|
|
" of render target \'" + getViewport()->getTarget ()->getName () + "\'");
|
|
}
|
|
|
|
void DepthComposerInstance::addCompositor ()
|
|
{
|
|
CompositorManager* compMgr = CompositorManager::getSingletonPtr();
|
|
|
|
const String& compositorName = getParent ()->getCompositorName ();
|
|
mCompInst = compMgr->addCompositor(mViewport, compositorName);
|
|
if (!mCompInst) {
|
|
CAELUM_THROW_UNSUPPORTED_EXCEPTION (
|
|
"Can't add \'" + compositorName + "\' compositor.",
|
|
"DepthComposer");
|
|
}
|
|
assert(mCompInst);
|
|
mCompInst->setEnabled (true);
|
|
mCompInst->addListener (this);
|
|
}
|
|
|
|
void DepthComposerInstance::removeCompositor ()
|
|
{
|
|
CompositorManager* compMgr = CompositorManager::getSingletonPtr();
|
|
compMgr->removeCompositor (mViewport, mCompInst->getCompositor ()->getName ());
|
|
mCompInst = 0;
|
|
}
|
|
|
|
void DepthComposerInstance::notifyMaterialSetup(uint pass_id, Ogre::MaterialPtr &mat)
|
|
{
|
|
//LogManager::getSingleton ().logMessage (
|
|
// "Caelum::DepthComposer: Material setup");
|
|
|
|
Pass* pass = mat->getBestTechnique ()->getPass (0);
|
|
|
|
TextureUnitState *depthTus = pass->getTextureUnitState(1);
|
|
if (depthTus->getTextureName () != mDepthRenderer->getDepthRenderTexture ()->getName()) {
|
|
depthTus->setTextureName (mDepthRenderer->getDepthRenderTexture ()->getName ());
|
|
LogManager::getSingleton ().logMessage (
|
|
"Caelum::DepthComposer: Assigned depth texture in compositor material");
|
|
}
|
|
|
|
mParams.setup(pass->getFragmentProgramParameters ());
|
|
}
|
|
|
|
void DepthComposerInstance::Params::setup(Ogre::GpuProgramParametersSharedPtr fpParams)
|
|
{
|
|
this->fpParams = fpParams;
|
|
invViewProjMatrix.bind(fpParams, "invViewProjMatrix");
|
|
worldCameraPos.bind(fpParams, "worldCameraPos");
|
|
groundFogDensity.bind(fpParams, "groundFogDensity");
|
|
groundFogVerticalDecay.bind(fpParams, "groundFogVerticalDecay");
|
|
groundFogBaseLevel.bind(fpParams, "groundFogBaseLevel");
|
|
groundFogColour.bind(fpParams, "groundFogColour");
|
|
sunDirection.bind(fpParams, "sunDirection");
|
|
hazeColour.bind(fpParams, "hazeColour");
|
|
}
|
|
|
|
void DepthComposerInstance::notifyMaterialRender(uint pass_id, Ogre::MaterialPtr &mat)
|
|
{
|
|
Camera* camera = getViewport ()->getCamera ();
|
|
|
|
assert(mParams.fpParams == mat->getBestTechnique ()->getPass (0)->getFragmentProgramParameters ());
|
|
|
|
// Auto param in a compositor does not use the external camera.
|
|
// This means that sending matrices as auto_param will not work as expected.
|
|
// Do it manually instead.
|
|
Matrix4 projMatrix = camera->getProjectionMatrixWithRSDepth();
|
|
Matrix4 viewMatrix = camera->getViewMatrix();
|
|
|
|
mParams.invViewProjMatrix.set(mParams.fpParams, (projMatrix * viewMatrix).inverse());
|
|
|
|
mParams.worldCameraPos.set(mParams.fpParams, camera->getDerivedPosition ());
|
|
|
|
mParams.groundFogDensity.set(mParams.fpParams, getParent ()->getGroundFogDensity ());
|
|
mParams.groundFogVerticalDecay.set(mParams.fpParams, getParent ()->getGroundFogVerticalDecay ());
|
|
mParams.groundFogBaseLevel.set(mParams.fpParams, getParent ()->getGroundFogBaseLevel ());
|
|
mParams.groundFogColour.set(mParams.fpParams, getParent ()->getGroundFogColour ());
|
|
|
|
mParams.sunDirection.set(mParams.fpParams, getParent ()->getSunDirection ());
|
|
mParams.hazeColour.set(mParams.fpParams, getParent ()->getHazeColour ());
|
|
}
|
|
|
|
void DepthComposerInstance::_update ()
|
|
{
|
|
mDepthRenderer->update ();
|
|
}
|
|
|
|
DepthComposerInstance* DepthComposer::createViewportInstance(Ogre::Viewport* vp)
|
|
{
|
|
ViewportInstanceMap::const_iterator it = mViewportInstanceMap.find(vp);
|
|
if (it == mViewportInstanceMap.end()) {
|
|
std::auto_ptr<DepthComposerInstance> inst(new DepthComposerInstance(this, vp));
|
|
mViewportInstanceMap.insert(std::make_pair(vp, inst.get()));
|
|
// hold instance until successfully added to map.
|
|
return inst.release();
|
|
} else {
|
|
return it->second;
|
|
}
|
|
}
|
|
|
|
DepthComposerInstance* DepthComposer::getViewportInstance(Ogre::Viewport* vp) {
|
|
ViewportInstanceMap::iterator it = mViewportInstanceMap.find(vp);
|
|
if (it != mViewportInstanceMap.end()) {
|
|
return it->second;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void DepthComposer::destroyViewportInstance(Viewport* vp)
|
|
{
|
|
ViewportInstanceMap::iterator it = mViewportInstanceMap.find(vp);
|
|
if (it != mViewportInstanceMap.end()) {
|
|
DepthComposerInstance* inst = it->second;
|
|
delete inst;
|
|
mViewportInstanceMap.erase(it);
|
|
}
|
|
}
|
|
|
|
void DepthComposer::destroyAllViewportInstances() {
|
|
ViewportInstanceMap::const_iterator it;
|
|
ViewportInstanceMap::const_iterator begin = mViewportInstanceMap.begin();
|
|
ViewportInstanceMap::const_iterator end = mViewportInstanceMap.end();
|
|
for (it = begin; it != end; ++it) {
|
|
assert(it->first == it->second->getViewport());
|
|
delete it->second;
|
|
}
|
|
mViewportInstanceMap.clear();
|
|
}
|
|
|
|
const String DepthRenderer::DEFAULT_CUSTOM_DEPTH_SCHEME_NAME = "CaelumDepth";
|
|
|
|
DepthRenderer::DepthRenderer
|
|
(
|
|
Viewport* masterViewport
|
|
):
|
|
mMasterViewport (masterViewport),
|
|
mDepthRenderViewport (0),
|
|
mDepthRenderingNow (false),
|
|
mViewportVisibilityMask (~0),
|
|
mUseCustomDepthScheme (true),
|
|
mCustomDepthSchemeName (DEFAULT_CUSTOM_DEPTH_SCHEME_NAME)
|
|
{
|
|
disableRenderGroupRangeFilter ();
|
|
|
|
Ogre::String uniqueId = Ogre::StringConverter::toString ((size_t)this);
|
|
|
|
// Not cloned!
|
|
mDepthRenderMaterial = MaterialManager::getSingleton ().getByName ("Caelum/DepthRender");
|
|
mDepthRenderMaterial->load();
|
|
if (!mDepthRenderMaterial->getBestTechnique ()) {
|
|
CAELUM_THROW_UNSUPPORTED_EXCEPTION (
|
|
"Can't load depth render material: " +
|
|
mDepthRenderMaterial->getUnsupportedTechniquesExplanation(),
|
|
"DepthComposer");
|
|
}
|
|
|
|
TextureManager* texMgr = TextureManager::getSingletonPtr();
|
|
|
|
int width = getMasterViewport ()->getActualWidth ();
|
|
int height = getMasterViewport ()->getActualHeight ();
|
|
LogManager::getSingleton ().logMessage (
|
|
"Caelum::DepthRenderer: Creating depth render texture size " +
|
|
StringConverter::toString (width) +
|
|
"x" +
|
|
StringConverter::toString (height));
|
|
|
|
PixelFormat desiredFormat = PF_FLOAT32_R;
|
|
PixelFormat requestFormat = desiredFormat;
|
|
if (texMgr->isFormatSupported (TEX_TYPE_2D, desiredFormat, TU_RENDERTARGET)) {
|
|
LogManager::getSingleton ().logMessage (
|
|
"Caelum::DepthRenderer: RenderSystem has native support for " +
|
|
PixelUtil::getFormatName (desiredFormat));
|
|
} else if (texMgr->isEquivalentFormatSupported (TEX_TYPE_2D, desiredFormat, TU_RENDERTARGET)) {
|
|
PixelFormat equivFormat = texMgr->getNativeFormat (TEX_TYPE_2D, desiredFormat, TU_RENDERTARGET);
|
|
LogManager::getSingleton ().logMessage (
|
|
"Caelum::DepthRenderer: RenderSystem supports " +
|
|
PixelUtil::getFormatName (equivFormat) +
|
|
" instead of " +
|
|
PixelUtil::getFormatName (desiredFormat));
|
|
requestFormat = equivFormat;
|
|
} else {
|
|
CAELUM_THROW_UNSUPPORTED_EXCEPTION (
|
|
PixelUtil::getFormatName(desiredFormat) + " or equivalent not supported",
|
|
"DepthRenderer");
|
|
}
|
|
|
|
if (texMgr->isHardwareFilteringSupported (TEX_TYPE_2D, requestFormat, TU_RENDERTARGET)) {
|
|
LogManager::getSingleton ().logMessage (
|
|
"Caelum::DepthRenderer: RenderSystem supports hardware filtering for " +
|
|
PixelUtil::getFormatName (requestFormat));
|
|
} else {
|
|
LogManager::getSingleton ().logMessage (
|
|
"Caelum::DepthRenderer: RenderSystem does not support hardware filtering for " +
|
|
PixelUtil::getFormatName (requestFormat));
|
|
}
|
|
|
|
// Create depth texture.
|
|
// This depends on the size of the viewport.
|
|
mDepthRenderTexture = texMgr->createManual(
|
|
"Caelum/DepthComposer/" + uniqueId + "/DepthTexture",
|
|
Caelum::RESOURCE_GROUP_NAME,
|
|
TEX_TYPE_2D,
|
|
width, height, 1,
|
|
0,
|
|
requestFormat,
|
|
TU_RENDERTARGET,
|
|
0);
|
|
|
|
assert(getDepthRenderTarget());
|
|
|
|
// Should be the same format
|
|
LogManager::getSingleton().logMessage (
|
|
"Caelum::DepthRenderer: Created depth render texture"
|
|
" actual format " + PixelUtil::getFormatName (getDepthRenderTexture()->getFormat ()) +
|
|
" desired format " + PixelUtil::getFormatName (getDepthRenderTexture()->getDesiredFormat ()));
|
|
|
|
// We do our updates by hand.
|
|
getDepthRenderTarget()->setAutoUpdated (false);
|
|
|
|
// Viewport for the depth rtt. Don't set camera here; it can mess Camera::getViewport();
|
|
mDepthRenderViewport = getDepthRenderTarget()->addViewport(0);
|
|
getDepthRenderViewport ()->setShadowsEnabled (false);
|
|
getDepthRenderViewport ()->setOverlaysEnabled (false);
|
|
getDepthRenderViewport ()->setClearEveryFrame (true);
|
|
|
|
// Depth buffer values range from 0 to 1 in both OpenGL and Directx; unless depth ranges are used.
|
|
// Clear to the maximum value.
|
|
getDepthRenderViewport ()->setBackgroundColour (Ogre::ColourValue (1, 1, 1, 1));
|
|
}
|
|
|
|
DepthRenderer::~DepthRenderer()
|
|
{
|
|
TextureManager* texMgr = TextureManager::getSingletonPtr();
|
|
|
|
// Destroy render texture.
|
|
if (!mDepthRenderTexture.isNull ()) {
|
|
texMgr->remove (mDepthRenderTexture->getHandle ());
|
|
mDepthRenderTexture.setNull ();
|
|
}
|
|
}
|
|
|
|
void DepthRenderer::update ()
|
|
{
|
|
Camera* camera = getMasterViewport ()->getCamera ();
|
|
Viewport* oldCameraViewport = camera->getViewport ();
|
|
SceneManager *sceneManager = camera->getSceneManager ();
|
|
|
|
assert (oldCameraViewport == getMasterViewport ());
|
|
assert (getDepthRenderViewport ()->getActualWidth () == getMasterViewport()->getActualWidth ());
|
|
assert (getDepthRenderViewport ()->getActualHeight () == getMasterViewport()->getActualHeight ());
|
|
|
|
getDepthRenderViewport ()->setVisibilityMask (mViewportVisibilityMask);
|
|
getDepthRenderViewport ()->setCamera (camera);
|
|
if (this->getUseCustomDepthScheme ()) {
|
|
getDepthRenderViewport ()->setMaterialScheme (this->getCustomDepthSchemeName ());
|
|
}
|
|
|
|
// Restore old listener after we're done.
|
|
// Hopefully this will not break horribly.
|
|
RenderQueue::RenderableListener* oldListener = sceneManager->getRenderQueue ()->getRenderableListener();
|
|
if (oldListener) {
|
|
//LogManager::getSingleton ().logMessage (
|
|
// "Caelum: Found another render queue listener. This could be bad.");
|
|
}
|
|
sceneManager->getRenderQueue ()->setRenderableListener (this);
|
|
|
|
mDepthRenderingNow = true;
|
|
//LogManager::getSingleton ().logMessage ("Caelum: Begin depth rendering");
|
|
getDepthRenderTarget ()->update ();
|
|
//LogManager::getSingleton ().logMessage ("Caelum: End depth rendering");
|
|
mDepthRenderingNow = false;
|
|
|
|
sceneManager->getRenderQueue ()->setRenderableListener (oldListener);
|
|
oldListener = 0;
|
|
|
|
// Restore the camera's viewport. Ogre compositors do the same thing.
|
|
camera->_notifyViewport (oldCameraViewport);
|
|
}
|
|
|
|
#if OGRE_VERSION < 0x00010600
|
|
bool DepthRenderer::renderableQueued(
|
|
Ogre::Renderable* rend,
|
|
Ogre::uint8 groupId,
|
|
Ogre::ushort priority,
|
|
Ogre::Technique** ppTech)
|
|
#else
|
|
bool DepthRenderer::renderableQueued(
|
|
Ogre::Renderable* rend,
|
|
Ogre::uint8 groupId,
|
|
Ogre::ushort priority,
|
|
Ogre::Technique** ppTech,
|
|
Ogre::RenderQueue* pQueue)
|
|
#endif // OGRE_VERSION
|
|
{
|
|
assert (mDepthRenderingNow);
|
|
|
|
/*
|
|
LogManager::getSingleton ().logMessage (
|
|
"Caelum: Renderable queued"
|
|
" group " + StringConverter::toString (groupId) +
|
|
" priority " + StringConverter::toString (priority));
|
|
*/
|
|
if (groupId < mMinRenderGroupId || groupId > mMaxRenderGroupId) {
|
|
return false;
|
|
}
|
|
|
|
if (this->getUseCustomDepthScheme () && (*ppTech)->getSchemeName () == this->getCustomDepthSchemeName ()) {
|
|
/*
|
|
LogManager::getSingleton().getDefaultLog()->logMessage (
|
|
"Custom scheme with tech " + (*ppTech)->getName () +
|
|
" passCount " + StringConverter::toString ((*ppTech)->getNumPasses ()) +
|
|
" vp " + (*ppTech)->getPass (0)->getVertexProgramName () +
|
|
" fp " + (*ppTech)->getPass (0)->getFragmentProgramName ());
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
// Get depth material
|
|
Material* depthMaterial = getDepthRenderMaterial ();
|
|
Technique* tech = depthMaterial->getBestTechnique ();
|
|
|
|
// Replace ALL techniques.
|
|
*ppTech = tech;
|
|
return true;
|
|
}
|
|
|
|
void DepthRenderer::setRenderGroupRangeFilter (int minGroup, int maxGroup)
|
|
{
|
|
mMinRenderGroupId = minGroup;
|
|
mMaxRenderGroupId = maxGroup;
|
|
}
|
|
|
|
void DepthRenderer::disableRenderGroupRangeFilter()
|
|
{
|
|
setRenderGroupRangeFilter(Ogre::RENDER_QUEUE_BACKGROUND, Ogre::RENDER_QUEUE_MAX);
|
|
}
|
|
}
|