Caelum
DepthComposer.cpp
1 /*
2 This file is part of Caelum.
3 See http://www.ogre3d.org/wiki/index.php/Caelum
4 
5 Copyright (c) 2008 Caelum team. See Contributors.txt for details.
6 
7 Caelum is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Caelum is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public License
18 along with Caelum. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "CaelumPrecompiled.h"
22 #include "CaelumExceptions.h"
23 #include "DepthComposer.h"
24 
25 using namespace Ogre;
26 
27 namespace Caelum
28 {
29  DepthComposer::DepthComposer
30  (
31  Ogre::SceneManager *sceneMgr
32  ):
33  mSceneMgr (sceneMgr),
34  mDebugDepthRender (false),
35  mSkyDomeHazeEnabled (false),
36  mGroundFogEnabled (false),
37  mGroundFogDensity (0.1),
38  mGroundFogBaseLevel (5),
39  mGroundFogVerticalDecay (0.2),
40  mGroundFogColour (ColourValue::Black)
41  {
42  }
43 
44  DepthComposer::~DepthComposer()
45  {
46  destroyAllViewportInstances();
47  }
48 
50  {
51  if (mDebugDepthRender == value) {
52  return;
53  }
54  mDebugDepthRender = value;
55  onCompositorMaterialChanged ();
56  }
57 
59  {
60  if (mSkyDomeHazeEnabled == value) {
61  return;
62  }
63  mSkyDomeHazeEnabled = value;
64  onCompositorMaterialChanged ();
65  }
66 
68  {
69  if (mGroundFogEnabled == value) {
70  return;
71  }
72  mGroundFogEnabled = value;
73  onCompositorMaterialChanged ();
74  }
75 
76  const String& DepthComposer::getCompositorName ()
77  {
78  // Constant Ogre::Strings for names.
79  static const Ogre::String CompositorName_DebugDepthRender =
80  "Caelum/DepthComposer_DebugDepthRender";
81  static const Ogre::String CompositorName_Dummy =
82  "Caelum/DepthComposer_Dummy";
83  static const Ogre::String CompositorName_ExpGroundFog =
84  "Caelum/DepthComposer_ExpGroundFog";
85  static const Ogre::String CompositorName_SkyDomeHaze =
86  "Caelum/DepthComposer_SkyDomeHaze";
87  static const Ogre::String CompositorName_SkyDomeHaze_ExpGroundFog =
88  "Caelum/DepthComposer_SkyDomeHaze_ExpGroundFog";
89 
90  // Should probably build materials and compositors by hand.
91  if (mDebugDepthRender) {
92  return CompositorName_DebugDepthRender;
93  } else if (mSkyDomeHazeEnabled == false && mGroundFogEnabled == false) {
94  return CompositorName_Dummy;
95  } else if (mSkyDomeHazeEnabled == false && mGroundFogEnabled == true) {
96  return CompositorName_ExpGroundFog;
97  } else if (mSkyDomeHazeEnabled == true && mGroundFogEnabled == false) {
98  return CompositorName_SkyDomeHaze;
99  } else if (mSkyDomeHazeEnabled == true && mGroundFogEnabled == true) {
100  return CompositorName_SkyDomeHaze_ExpGroundFog;
101  } else {
102  assert (0);
103  return CompositorName_Dummy;
104  }
105  }
106 
107  void DepthComposer::onCompositorMaterialChanged ()
108  {
109  ViewportInstanceMap::const_iterator it;
110  ViewportInstanceMap::const_iterator begin = mViewportInstanceMap.begin();
111  ViewportInstanceMap::const_iterator end = mViewportInstanceMap.end();
112  for (it = begin; it != end; ++it) {
113  it->second->removeCompositor ();
114  it->second->addCompositor ();
115  }
116  }
117 
118  void DepthComposer::update ()
119  {
120  ViewportInstanceMap::const_iterator it;
121  ViewportInstanceMap::const_iterator begin = mViewportInstanceMap.begin();
122  ViewportInstanceMap::const_iterator end = mViewportInstanceMap.end();
123  for (it = begin; it != end; ++it) {
124  assert(it->first == it->second->getViewport());
125  it->second->_update ();
126  }
127  }
128 
129  DepthComposerInstance::DepthComposerInstance
130  (
131  DepthComposer* parent,
132  Ogre::Viewport* viewport
133  ):
134  mParent(parent),
135  mViewport(viewport),
136  mCompInst(0)
137  {
138  LogManager::getSingleton().logMessage (
139  "Caelum::DepthComposer: Attaching screen-space fog instance"
140  " to viewport \'" + StringConverter::toString ((long)getViewport ()) + "\'"
141  " of render target \'" + getViewport()->getTarget ()->getName () + "\'");
142 
143  addCompositor ();
144  mDepthRenderer.reset (new DepthRenderer (getViewport ()));
145  }
146 
147  DepthComposerInstance::~DepthComposerInstance()
148  {
149  removeCompositor ();
150  mDepthRenderer.reset ();
151 
152  LogManager::getSingleton().logMessage (
153  "Caelum::DepthComposer: Detached screen-space fog instance"
154  " from viewport \'" + StringConverter::toString ((long)getViewport ()) + "\'"
155  " of render target \'" + getViewport()->getTarget ()->getName () + "\'");
156  }
157 
158  void DepthComposerInstance::addCompositor ()
159  {
160  CompositorManager* compMgr = CompositorManager::getSingletonPtr();
161 
162  const String& compositorName = getParent ()->getCompositorName ();
163  mCompInst = compMgr->addCompositor(mViewport, compositorName);
164  if (!mCompInst) {
165  CAELUM_THROW_UNSUPPORTED_EXCEPTION (
166  "Can't add \'" + compositorName + "\' compositor.",
167  "DepthComposer");
168  }
169  assert(mCompInst);
170  mCompInst->setEnabled (true);
171  mCompInst->addListener (this);
172  }
173 
174  void DepthComposerInstance::removeCompositor ()
175  {
176  CompositorManager* compMgr = CompositorManager::getSingletonPtr();
177  compMgr->removeCompositor (mViewport, mCompInst->getCompositor ()->getName ());
178  mCompInst = 0;
179  }
180 
181  void DepthComposerInstance::notifyMaterialSetup(uint pass_id, Ogre::MaterialPtr &mat)
182  {
183  //LogManager::getSingleton ().logMessage (
184  // "Caelum::DepthComposer: Material setup");
185 
186  Pass* pass = mat->getBestTechnique ()->getPass (0);
187 
188  TextureUnitState *depthTus = pass->getTextureUnitState(1);
189  if (depthTus->getTextureName () != mDepthRenderer->getDepthRenderTexture ()->getName()) {
190  depthTus->setTextureName (mDepthRenderer->getDepthRenderTexture ()->getName ());
191  LogManager::getSingleton ().logMessage (
192  "Caelum::DepthComposer: Assigned depth texture in compositor material");
193  }
194 
195  mParams.setup(pass->getFragmentProgramParameters ());
196  }
197 
198  void DepthComposerInstance::Params::setup(Ogre::GpuProgramParametersSharedPtr fpParams)
199  {
200  this->fpParams = fpParams;
201  invViewProjMatrix.bind(fpParams, "invViewProjMatrix");
202  worldCameraPos.bind(fpParams, "worldCameraPos");
203  groundFogDensity.bind(fpParams, "groundFogDensity");
204  groundFogVerticalDecay.bind(fpParams, "groundFogVerticalDecay");
205  groundFogBaseLevel.bind(fpParams, "groundFogBaseLevel");
206  groundFogColour.bind(fpParams, "groundFogColour");
207  sunDirection.bind(fpParams, "sunDirection");
208  hazeColour.bind(fpParams, "hazeColour");
209  }
210 
211  void DepthComposerInstance::notifyMaterialRender(uint pass_id, Ogre::MaterialPtr &mat)
212  {
213  Camera* camera = getViewport ()->getCamera ();
214 
215  assert(mParams.fpParams == mat->getBestTechnique ()->getPass (0)->getFragmentProgramParameters ());
216 
217  // Auto param in a compositor does not use the external camera.
218  // This means that sending matrices as auto_param will not work as expected.
219  // Do it manually instead.
220  Matrix4 projMatrix = camera->getProjectionMatrixWithRSDepth();
221  Matrix4 viewMatrix = camera->getViewMatrix();
222 
223  mParams.invViewProjMatrix.set(mParams.fpParams, (projMatrix * viewMatrix).inverse());
224 
225  mParams.worldCameraPos.set(mParams.fpParams, camera->getDerivedPosition ());
226 
227  mParams.groundFogDensity.set(mParams.fpParams, getParent ()->getGroundFogDensity ());
228  mParams.groundFogVerticalDecay.set(mParams.fpParams, getParent ()->getGroundFogVerticalDecay ());
229  mParams.groundFogBaseLevel.set(mParams.fpParams, getParent ()->getGroundFogBaseLevel ());
230  mParams.groundFogColour.set(mParams.fpParams, getParent ()->getGroundFogColour ());
231 
232  mParams.sunDirection.set(mParams.fpParams, getParent ()->getSunDirection ());
233  mParams.hazeColour.set(mParams.fpParams, getParent ()->getHazeColour ());
234  }
235 
237  {
238  mDepthRenderer->update ();
239  }
240 
241  DepthComposerInstance* DepthComposer::createViewportInstance(Ogre::Viewport* vp)
242  {
243  ViewportInstanceMap::const_iterator it = mViewportInstanceMap.find(vp);
244  if (it == mViewportInstanceMap.end()) {
245  std::auto_ptr<DepthComposerInstance> inst(new DepthComposerInstance(this, vp));
246  mViewportInstanceMap.insert(std::make_pair(vp, inst.get()));
247  // hold instance until successfully added to map.
248  return inst.release();
249  } else {
250  return it->second;
251  }
252  }
253 
254  DepthComposerInstance* DepthComposer::getViewportInstance(Ogre::Viewport* vp) {
255  ViewportInstanceMap::iterator it = mViewportInstanceMap.find(vp);
256  if (it != mViewportInstanceMap.end()) {
257  return it->second;
258  } else {
259  return 0;
260  }
261  }
262 
263  void DepthComposer::destroyViewportInstance(Viewport* vp)
264  {
265  ViewportInstanceMap::iterator it = mViewportInstanceMap.find(vp);
266  if (it != mViewportInstanceMap.end()) {
267  DepthComposerInstance* inst = it->second;
268  delete inst;
269  mViewportInstanceMap.erase(it);
270  }
271  }
272 
273  void DepthComposer::destroyAllViewportInstances() {
274  ViewportInstanceMap::const_iterator it;
275  ViewportInstanceMap::const_iterator begin = mViewportInstanceMap.begin();
276  ViewportInstanceMap::const_iterator end = mViewportInstanceMap.end();
277  for (it = begin; it != end; ++it) {
278  assert(it->first == it->second->getViewport());
279  delete it->second;
280  }
281  mViewportInstanceMap.clear();
282  }
283 
284  const String DepthRenderer::DEFAULT_CUSTOM_DEPTH_SCHEME_NAME = "CaelumDepth";
285 
286  DepthRenderer::DepthRenderer
287  (
288  Viewport* masterViewport
289  ):
290  mMasterViewport (masterViewport),
291  mDepthRenderViewport (0),
292  mDepthRenderingNow (false),
293  mViewportVisibilityMask (~0),
294  mUseCustomDepthScheme (true),
295  mCustomDepthSchemeName (DEFAULT_CUSTOM_DEPTH_SCHEME_NAME)
296  {
297  disableRenderGroupRangeFilter ();
298 
299  Ogre::String uniqueId = Ogre::StringConverter::toString ((size_t)this);
300 
301  // Not cloned!
302  mDepthRenderMaterial = MaterialManager::getSingleton ().getByName ("Caelum/DepthRender");
303  mDepthRenderMaterial->load();
304  if (!mDepthRenderMaterial->getBestTechnique ()) {
305  CAELUM_THROW_UNSUPPORTED_EXCEPTION (
306  "Can't load depth render material: " +
307  mDepthRenderMaterial->getUnsupportedTechniquesExplanation(),
308  "DepthComposer");
309  }
310 
311  TextureManager* texMgr = TextureManager::getSingletonPtr();
312 
313  int width = getMasterViewport ()->getActualWidth ();
314  int height = getMasterViewport ()->getActualHeight ();
315  LogManager::getSingleton ().logMessage (
316  "Caelum::DepthRenderer: Creating depth render texture size " +
317  StringConverter::toString (width) +
318  "x" +
319  StringConverter::toString (height));
320 
321  PixelFormat desiredFormat = PF_FLOAT32_R;
322  PixelFormat requestFormat = desiredFormat;
323  if (texMgr->isFormatSupported (TEX_TYPE_2D, desiredFormat, TU_RENDERTARGET)) {
324  LogManager::getSingleton ().logMessage (
325  "Caelum::DepthRenderer: RenderSystem has native support for " +
326  PixelUtil::getFormatName (desiredFormat));
327  } else if (texMgr->isEquivalentFormatSupported (TEX_TYPE_2D, desiredFormat, TU_RENDERTARGET)) {
328  PixelFormat equivFormat = texMgr->getNativeFormat (TEX_TYPE_2D, desiredFormat, TU_RENDERTARGET);
329  LogManager::getSingleton ().logMessage (
330  "Caelum::DepthRenderer: RenderSystem supports " +
331  PixelUtil::getFormatName (equivFormat) +
332  " instead of " +
333  PixelUtil::getFormatName (desiredFormat));
334  requestFormat = equivFormat;
335  } else {
336  CAELUM_THROW_UNSUPPORTED_EXCEPTION (
337  PixelUtil::getFormatName(desiredFormat) + " or equivalent not supported",
338  "DepthRenderer");
339  }
340 
341  if (texMgr->isHardwareFilteringSupported (TEX_TYPE_2D, requestFormat, TU_RENDERTARGET)) {
342  LogManager::getSingleton ().logMessage (
343  "Caelum::DepthRenderer: RenderSystem supports hardware filtering for " +
344  PixelUtil::getFormatName (requestFormat));
345  } else {
346  LogManager::getSingleton ().logMessage (
347  "Caelum::DepthRenderer: RenderSystem does not support hardware filtering for " +
348  PixelUtil::getFormatName (requestFormat));
349  }
350 
351  // Create depth texture.
352  // This depends on the size of the viewport.
353  mDepthRenderTexture = texMgr->createManual(
354  "Caelum/DepthComposer/" + uniqueId + "/DepthTexture",
356  TEX_TYPE_2D,
357  width, height, 1,
358  0,
359  requestFormat,
360  TU_RENDERTARGET,
361  0);
362 
363  assert(getDepthRenderTarget());
364 
365  // Should be the same format
366  LogManager::getSingleton().logMessage (
367  "Caelum::DepthRenderer: Created depth render texture"
368  " actual format " + PixelUtil::getFormatName (getDepthRenderTexture()->getFormat ()) +
369  " desired format " + PixelUtil::getFormatName (getDepthRenderTexture()->getDesiredFormat ()));
370 
371  // We do our updates by hand.
372  getDepthRenderTarget()->setAutoUpdated (false);
373 
374  // Viewport for the depth rtt. Don't set camera here; it can mess Camera::getViewport();
375  mDepthRenderViewport = getDepthRenderTarget()->addViewport(0);
376  getDepthRenderViewport ()->setShadowsEnabled (false);
377  getDepthRenderViewport ()->setOverlaysEnabled (false);
378  getDepthRenderViewport ()->setClearEveryFrame (true);
379 
380  // Depth buffer values range from 0 to 1 in both OpenGL and Directx; unless depth ranges are used.
381  // Clear to the maximum value.
382  getDepthRenderViewport ()->setBackgroundColour (Ogre::ColourValue (1, 1, 1, 1));
383  }
384 
385  DepthRenderer::~DepthRenderer()
386  {
387  TextureManager* texMgr = TextureManager::getSingletonPtr();
388 
389  // Destroy render texture.
390  if (!mDepthRenderTexture.isNull ()) {
391  texMgr->remove (mDepthRenderTexture->getHandle ());
392  mDepthRenderTexture.setNull ();
393  }
394  }
395 
397  {
398  Camera* camera = getMasterViewport ()->getCamera ();
399  Viewport* oldCameraViewport = camera->getViewport ();
400  SceneManager *sceneManager = camera->getSceneManager ();
401 
402  assert (oldCameraViewport == getMasterViewport ());
403  assert (getDepthRenderViewport ()->getActualWidth () == getMasterViewport()->getActualWidth ());
404  assert (getDepthRenderViewport ()->getActualHeight () == getMasterViewport()->getActualHeight ());
405 
406  getDepthRenderViewport ()->setVisibilityMask (mViewportVisibilityMask);
407  getDepthRenderViewport ()->setCamera (camera);
408  if (this->getUseCustomDepthScheme ()) {
409  getDepthRenderViewport ()->setMaterialScheme (this->getCustomDepthSchemeName ());
410  }
411 
412  // Restore old listener after we're done.
413  // Hopefully this will not break horribly.
414  RenderQueue::RenderableListener* oldListener = sceneManager->getRenderQueue ()->getRenderableListener();
415  if (oldListener) {
416  //LogManager::getSingleton ().logMessage (
417  // "Caelum: Found another render queue listener. This could be bad.");
418  }
419  sceneManager->getRenderQueue ()->setRenderableListener (this);
420 
421  mDepthRenderingNow = true;
422  //LogManager::getSingleton ().logMessage ("Caelum: Begin depth rendering");
423  getDepthRenderTarget ()->update ();
424  //LogManager::getSingleton ().logMessage ("Caelum: End depth rendering");
425  mDepthRenderingNow = false;
426 
427  sceneManager->getRenderQueue ()->setRenderableListener (oldListener);
428  oldListener = 0;
429 
430  // Restore the camera's viewport. Ogre compositors do the same thing.
431  camera->_notifyViewport (oldCameraViewport);
432  }
433 
434 #if OGRE_VERSION < 0x00010600
435  bool DepthRenderer::renderableQueued(
436  Ogre::Renderable* rend,
437  Ogre::uint8 groupId,
438  Ogre::ushort priority,
439  Ogre::Technique** ppTech)
440 #else
441  bool DepthRenderer::renderableQueued(
442  Ogre::Renderable* rend,
443  Ogre::uint8 groupId,
444  Ogre::ushort priority,
445  Ogre::Technique** ppTech,
446  Ogre::RenderQueue* pQueue)
447 #endif // OGRE_VERSION
448  {
449  assert (mDepthRenderingNow);
450 
451  /*
452  LogManager::getSingleton ().logMessage (
453  "Caelum: Renderable queued"
454  " group " + StringConverter::toString (groupId) +
455  " priority " + StringConverter::toString (priority));
456  */
457  if (groupId < mMinRenderGroupId || groupId > mMaxRenderGroupId) {
458  return false;
459  }
460 
461  if (this->getUseCustomDepthScheme () && (*ppTech)->getSchemeName () == this->getCustomDepthSchemeName ()) {
462  /*
463  LogManager::getSingleton().getDefaultLog()->logMessage (
464  "Custom scheme with tech " + (*ppTech)->getName () +
465  " passCount " + StringConverter::toString ((*ppTech)->getNumPasses ()) +
466  " vp " + (*ppTech)->getPass (0)->getVertexProgramName () +
467  " fp " + (*ppTech)->getPass (0)->getFragmentProgramName ());
468  */
469  return true;
470  }
471 
472  // Get depth material
473  Material* depthMaterial = getDepthRenderMaterial ();
474  Technique* tech = depthMaterial->getBestTechnique ();
475 
476  // Replace ALL techniques.
477  *ppTech = tech;
478  return true;
479  }
480 
481  void DepthRenderer::setRenderGroupRangeFilter (int minGroup, int maxGroup)
482  {
483  mMinRenderGroupId = minGroup;
484  mMaxRenderGroupId = maxGroup;
485  }
486 
488  {
489  setRenderGroupRangeFilter(Ogre::RENDER_QUEUE_BACKGROUND, Ogre::RENDER_QUEUE_MAX);
490  }
491 }
void disableRenderGroupRangeFilter()
Disable the effects of.
Compositor-based precipitation controller.
Definition: DepthComposer.h:35
void setDebugDepthRender(bool value)
Enables drawing the depth buffer.
Per-viewport instance of.
static const String DEFAULT_CUSTOM_DEPTH_SCHEME_NAME
Default name of the custom scheme.
void setGroundFogEnabled(bool value)
Enables exponential ground fog.
void _update()
Called from DepthComposer::update.
static const String RESOURCE_GROUP_NAME
Resource group name for caelum resources.
Render the depth buffer to a texture.
void setRenderGroupRangeFilter(int minGroup, int maxGroup)
Render only the render groups in a certain range.
void setSkyDomeHazeEnabled(bool value)
Enables skydome haze.
Caelum namespace.
Definition: Astronomy.cpp:24
void update()
Render the depth buffer now!