Torque3D Documentation / _generateds / projectedShadow.cpp

projectedShadow.cpp

Engine/source/lighting/common/projectedShadow.cpp

More...

Detailed Description

Public Functions

GFX_ImplementTextureProfile(BLProjectedShadowProfile , GFXTextureProfile::DiffuseMap , GFXTextureProfile::PreserveSize</a>|<a href="/coding/class/classgfxtextureprofile/#classgfxtextureprofile_1a09105f0bf717a1f2ae49ceda21b8e82da75302d480adb239daa0dd3fec9fd7a0b">GFXTextureProfile::RenderTarget</a>|<a href="/coding/class/classgfxtextureprofile/#classgfxtextureprofile_1a09105f0bf717a1f2ae49ceda21b8e82da8e64fa44eaa9b6444614dac7bdb1dcf8">GFXTextureProfile::Pooled , GFXTextureProfile::NONE )

GFX_ImplementTextureProfile(BLProjectedShadowZProfile , GFXTextureProfile::DiffuseMap , GFXTextureProfile::PreserveSize</a>|<a href="/coding/class/classgfxtextureprofile/#classgfxtextureprofile_1a09105f0bf717a1f2ae49ceda21b8e82daa3a2027753aec009ced4f0818a231296">GFXTextureProfile::ZTarget</a>|<a href="/coding/class/classgfxtextureprofile/#classgfxtextureprofile_1a09105f0bf717a1f2ae49ceda21b8e82da8e64fa44eaa9b6444614dac7bdb1dcf8">GFXTextureProfile::Pooled , GFXTextureProfile::NONE )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "platform/platform.h"
 25#include "lighting/common/projectedShadow.h"
 26
 27#include "gfx/primBuilder.h"
 28#include "gfx/gfxTextureManager.h"
 29#include "gfx/bitmap/gBitmap.h"
 30#include "gfx/gfxDebugEvent.h"
 31#include "math/mathUtils.h"
 32#include "lighting/lightInfo.h"
 33#include "lighting/lightingInterfaces.h"
 34#include "T3D/shapeBase.h"
 35#include "scene/sceneManager.h"
 36#include "lighting/lightManager.h"
 37#include "ts/tsMesh.h"
 38#include "T3D/decal/decalManager.h"
 39#include "T3D/decal/decalInstance.h"
 40#include "renderInstance/renderPassManager.h"
 41#include "renderInstance/renderMeshMgr.h"
 42#include "gfx/gfxTransformSaver.h"
 43#include "materials/customMaterialDefinition.h"
 44#include "materials/materialFeatureTypes.h"
 45#include "console/console.h"
 46#include "postFx/postEffect.h"
 47#ifdef TORQUE_BASIC_LIGHTING  
 48#include "lighting/basic/basicLightManager.h"  
 49#else  
 50#include "lighting/advanced/advancedLightManager.h"  
 51#endif 
 52#include "lighting/shadowMap/shadowMatHook.h"
 53#include "materials/materialManager.h"
 54#include "lighting/shadowMap/lightShadowMap.h"
 55
 56
 57SimObjectPtr<RenderPassManager> ProjectedShadow::smRenderPass = NULL;
 58SimObjectPtr<PostEffect> ProjectedShadow::smShadowFilter = NULL;
 59F32 ProjectedShadow::smDepthAdjust = 10.0f;
 60
 61F32 ProjectedShadow::smFadeStartPixelSize = 200.0f;
 62F32 ProjectedShadow::smFadeEndPixelSize = 35.0f;
 63
 64
 65GFX_ImplementTextureProfile( BLProjectedShadowProfile,
 66                              GFXTextureProfile::DiffuseMap,
 67                              GFXTextureProfile::PreserveSize | 
 68                              GFXTextureProfile::RenderTarget |
 69                              GFXTextureProfile::Pooled,
 70                              GFXTextureProfile::NONE );
 71
 72GFX_ImplementTextureProfile( BLProjectedShadowZProfile,
 73                              GFXTextureProfile::DiffuseMap,
 74                              GFXTextureProfile::PreserveSize | 
 75                              GFXTextureProfile::ZTarget |
 76                              GFXTextureProfile::Pooled,
 77                              GFXTextureProfile::NONE );
 78
 79
 80ProjectedShadow::ProjectedShadow( SceneObject *object )
 81{
 82   mParentObject = object;
 83   mShapeBase = dynamic_cast<ShapeBase*>( object );
 84
 85   mRadius = 0;
 86   mLastRenderTime = 0;
 87   mUpdateTexture = false;
 88
 89   mShadowLength = 10.0f;
 90
 91   mDecalData = new DecalData;
 92   mDecalData->skipVertexNormals = true;
 93
 94   mDecalInstance = NULL;
 95   
 96   mLastLightDir.set( 0, 0, 0 );
 97   mLastObjectPosition.set( object->getRenderPosition() );
 98   mLastObjectScale.set( object->getScale() );
 99
100   CustomMaterial *customMat = NULL;
101   Sim::findObject( "BL_ProjectedShadowMaterial", customMat );
102   if ( customMat )
103   {
104      mDecalData->material = customMat;
105      mDecalData->matInst = customMat->createMatInstance();
106   }
107   else
108      mDecalData->matInst = MATMGR->createMatInstance( "WarningMaterial" );
109
110   mDecalData->matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTT>() );
111
112   mCasterPositionSC = NULL;
113   mShadowLengthSC = NULL;
114}
115
116ProjectedShadow::~ProjectedShadow()
117{
118   if ( mDecalInstance )
119      gDecalManager->removeDecal( mDecalInstance );
120
121   delete mDecalData;
122   
123   mShadowTexture = NULL;
124   mRenderTarget = NULL;
125}
126
127bool ProjectedShadow::shouldRender( const SceneRenderState *state )
128{
129   // Don't render if our object has been removed from the
130   // scene graph.
131      
132   if( !mParentObject->getSceneManager() )
133      return false;
134
135   // Don't render if the ShapeBase 
136   // object's fade value is greater 
137   // than the visibility epsilon.
138   bool shapeFade = mShapeBase && mShapeBase->getFadeVal() < TSMesh::VISIBILITY_EPSILON;
139
140   // Get the shapebase datablock if we have one.
141   ShapeBaseData *data = NULL;
142   if ( mShapeBase )
143      data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
144   
145   // Also don't render if 
146   // the camera distance is greater
147   // than the shadow length.
148   if (  shapeFade || !mDecalData || 
149         (  mDecalInstance && 
150            mDecalInstance->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y ) < mDecalInstance->mDataBlock->fadeEndPixelSize ) )
151   {
152      // Release our shadow texture
153      // so that others can grab it out
154      // of the pool.
155      mShadowTexture = NULL;
156
157      return false;
158   }
159
160   return true;
161}
162
163bool ProjectedShadow::_updateDecal( const SceneRenderState *state )
164{
165   PROFILE_SCOPE( ProjectedShadow_UpdateDecal );
166
167   if ( !LIGHTMGR )
168      return false;
169
170   // Get the position of the decal first.   
171   const Box3F &objBox = mParentObject->getObjBox();
172   const Point3F boxCenter = objBox.getCenter();
173   Point3F decalPos = boxCenter;
174   const MatrixF &renderTransform = mParentObject->getRenderTransform();
175   {
176      // Set up the decal position.
177      // We use the object space box center
178      // multiplied by the render transform
179      // of the object to ensure we benefit
180      // from interpolation.
181      MatrixF t( renderTransform );   
182      t.setColumn(2,Point3F::UnitZ);
183      t.mulP( decalPos );
184   }
185
186   if ( mDecalInstance )
187   {
188      mDecalInstance->mPosition = decalPos;
189      if ( !shouldRender( state ) )
190         return false;
191   }
192
193   // Get the sunlight for the shadow projection.
194   // We want the LightManager to return NULL if it can't
195   // get the "real" sun, so we specify false for the useDefault parameter.
196   LightInfo *lights[4] = {0};
197   LightQuery query;
198   query.init( mParentObject->getWorldSphere() );
199   query.getLights( lights, 4 );
200
201   Point3F pos = renderTransform.getPosition();
202
203   Point3F lightDir( 0, 0, 0 );
204   Point3F tmp( 0, 0, 0 );
205   F32 weight = 0;
206   F32 range = 0;
207   U32 lightCount = 0;
208   F32 dist = 0;
209   F32 fade = 0;
210   for ( U32 i = 0; i < 4; i++ )
211   {
212      // If we got a NULL light, 
213      // we're at the end of the list.
214      if ( !lights[i] )
215         break;
216
217      if ( !lights[i]->getCastShadows() )
218         continue;
219
220      if ( lights[i]->getType() != LightInfo::Point )
221         tmp = lights[i]->getDirection();
222      else
223         tmp = pos - lights[i]->getPosition();
224
225      range = lights[i]->getRange().x;
226      dist = ( (tmp.lenSquared()) / ((range * range) * 0.5f));
227      weight = mClampF( 1.0f - ( tmp.lenSquared() / (range * range)), 0.00001f, 1.0f );
228
229      if ( lights[i]->getType() == LightInfo::Vector )
230         fade = getMax( fade, 1.0f );
231      else
232         fade = getMax( fade, mClampF( 1.0f - dist, 0.00001f, 1.0f ) );
233
234      lightDir += tmp * weight;
235      lightCount++;
236   }
237
238   if (mShapeBase)
239      fade *= mShapeBase->getFadeVal();
240
241   lightDir.normalize();
242   
243   // No light... no shadow.
244   if ( !lights[0] )
245      return false;
246     
247   // Has the light direction
248   // changed since last update?
249   bool lightDirChanged = !mLastLightDir.equal( lightDir );
250
251   // Has the parent object moved
252   // or scaled since the last update?
253   bool hasMoved = !mLastObjectPosition.equal( mParentObject->getRenderPosition() );
254   bool hasScaled = !mLastObjectScale.equal( mParentObject->getScale() );
255
256   // Set the last light direction
257   // to the current light direction.
258   mLastLightDir = lightDir;
259   mLastObjectPosition = mParentObject->getRenderPosition();
260   mLastObjectScale = mParentObject->getScale();
261
262
263   // Temps used to generate
264   // tangent vector for DecalInstance below.
265   VectorF right( 0, 0, 0 );
266   VectorF fwd( 0, 0, 0 );
267   VectorF tmpFwd( 0, 0, 0 );
268   
269   U32 idx = lightDir.getLeastComponentIndex();
270
271   tmpFwd[idx] = 1.0f;
272
273   right = mCross( tmpFwd, lightDir );
274   fwd = mCross( lightDir, right );
275   right = mCross( fwd, lightDir );
276
277   right.normalize();
278
279   // Set up the world to light space
280   // matrix, along with proper position
281   // and rotation to be used as the world
282   // matrix for the render to texture later on.
283   static MatrixF sRotMat(EulerF( 0.0f, -(M_PI_F/2.0f), 0.0f));
284   mWorldToLight.identity();
285   MathUtils::getMatrixFromForwardVector( lightDir, &mWorldToLight );
286   mWorldToLight.setPosition( ( pos + boxCenter ) - ( ( (mRadius * smDepthAdjust) + 0.001f ) * lightDir ) );
287   mWorldToLight.mul( sRotMat );
288   mWorldToLight.inverse();
289   
290   // Get the shapebase datablock if we have one.
291   ShapeBaseData *data = NULL;
292   if ( mShapeBase )
293      data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
294
295   // We use the object box's extents multiplied
296   // by the object's scale divided by 2 for the radius
297   // because the object's worldsphere radius is not
298   // rotationally invariant.
299   mRadius = (objBox.getExtents() * mParentObject->getScale()).len() * 0.5f;
300
301   if ( data )
302      mRadius *= data->shadowSphereAdjust;
303
304   // Create the decal if we don't have one yet.
305   if ( !mDecalInstance )
306      mDecalInstance = gDecalManager->addDecal( decalPos, 
307                                                lightDir, 
308                                                right, 
309                                                mDecalData, 
310                                                1.0f, 
311                                                0, 
312                                                PermanentDecal | ClipDecal | CustomDecal );
313
314   if ( !mDecalInstance )
315      return false;
316
317   mDecalInstance->mVisibility = fade;
318
319   // Setup decal parameters.
320   mDecalInstance->mSize = mRadius * 2.0f;
321   mDecalInstance->mNormal = -lightDir;
322   mDecalInstance->mTangent = -right;
323   mDecalInstance->mRotAroundNormal = 0;
324   mDecalInstance->mPosition = decalPos;
325   mDecalInstance->mDataBlock = mDecalData;
326   
327   // If the position of the world 
328   // space box center is the same
329   // as the decal's position, and
330   // the light direction has not 
331   // changed, we don't need to clip.
332   bool shouldClip = lightDirChanged || hasMoved || hasScaled;
333
334   // Now, check and see if the object is visible.
335   const Frustum &frust = state->getCullingFrustum();
336   if ( frust.isCulled( SphereF( mDecalInstance->mPosition, mDecalInstance->mSize * mDecalInstance->mSize ) ) && !shouldClip )
337      return false;
338
339   F32 shadowLen = 10.0f;
340   if ( data )
341      shadowLen = data->shadowProjectionDistance;
342
343   const Point3F &boxExtents = objBox.getExtents();
344   
345   
346   mShadowLength = shadowLen * mParentObject->getScale().z;
347
348   // Set up clip depth, and box half 
349   // offset for decal clipping.
350   Point2F clipParams(  mShadowLength, (boxExtents.x + boxExtents.y) * 0.25f );
351
352   bool render = false;
353   bool clipSucceeded = true;
354
355   // Clip!
356   if ( shouldClip )
357   {
358      clipSucceeded = gDecalManager->clipDecal( mDecalInstance, 
359                                                NULL, 
360                                                &clipParams );
361   }
362
363   // If the clip failed,
364   // we'll return false in
365   // order to keep from
366   // unnecessarily rendering
367   // into the texture.  If
368   // there was no reason to clip
369   // on this update, we'll assume we
370   // should update the texture.
371   render = clipSucceeded;
372
373   // Tell the DecalManager we've changed this decal.
374   gDecalManager->notifyDecalModified( mDecalInstance );
375
376   return render;
377}
378
379void ProjectedShadow::_calcScore( const SceneRenderState *state )
380{
381   if ( !mDecalInstance )
382      return;
383
384   F32 pixRadius = mDecalInstance->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y ); 
385 
386   F32 pct = pixRadius / mDecalInstance->mDataBlock->fadeStartPixelSize;
387
388   U32 msSinceLastRender = Platform::getVirtualMilliseconds() - getLastRenderTime();
389
390   ShapeBaseData *data = NULL;
391   if ( mShapeBase )
392      data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
393
394   // For every 1s this shadow hasn't been
395   // updated we'll add 10 to the score.
396   F32 secs = mFloor( (F32)msSinceLastRender / 1000.0f );
397   
398   mScore = pct + secs;
399   mClampF( mScore, 0.0f, 2000.0f );
400}
401
402void ProjectedShadow::update( const SceneRenderState *state )
403{
404   mUpdateTexture = true;
405
406   // Set the decal lod settings.
407   mDecalData->fadeStartPixelSize = smFadeStartPixelSize;
408   mDecalData->fadeEndPixelSize = smFadeEndPixelSize;
409
410   // Update our decal before
411   // we render to texture.
412   // If it fails, something bad happened
413   // (no light to grab/failed clip) and we should return.
414   if ( !_updateDecal( state ) )
415   {
416      // Release our shadow texture
417      // so that others can grab it out
418      // of the pool.
419      mShadowTexture = NULL;      
420      mUpdateTexture = false;
421      return;
422   }
423
424   _calcScore( state );
425
426   if ( !mCasterPositionSC || !mCasterPositionSC->isValid() )
427      mCasterPositionSC = mDecalData->matInst->getMaterialParameterHandle( "$shadowCasterPosition" );
428
429   if ( !mShadowLengthSC || !mShadowLengthSC->isValid() )
430      mShadowLengthSC = mDecalData->matInst->getMaterialParameterHandle( "$shadowLength" );
431
432   MaterialParameters *matParams = mDecalData->matInst->getMaterialParameters();
433
434   matParams->setSafe( mCasterPositionSC, mParentObject->getRenderPosition() );   
435   matParams->setSafe( mShadowLengthSC, mShadowLength / 4.0f );
436}
437
438void ProjectedShadow::render( F32 camDist, const TSRenderState &rdata )
439{
440   if ( !mUpdateTexture )
441      return;
442            
443   // Do the render to texture,
444   // DecalManager handles rendering
445   // the shadow onto the world.
446   _renderToTexture( camDist, rdata );
447}
448
449BaseMatInstance* ProjectedShadow::_getShadowMaterial( BaseMatInstance *inMat )
450{
451   // See if we have an existing material hook.
452   ShadowMaterialHook *hook = static_cast<ShadowMaterialHook*>( inMat->getHook( ShadowMaterialHook::Type ) );
453   if ( !hook )
454   {
455      // Create a hook and initialize it using the incoming material.
456      hook = new ShadowMaterialHook;
457      hook->init( inMat ); 
458      inMat->addHook( hook );
459   }
460
461   return hook->getShadowMat( ShadowType_Spot );
462}
463
464void ProjectedShadow::_renderToTexture( F32 camDist, const TSRenderState &rdata )
465{
466   PROFILE_SCOPE( ProjectedShadow_RenderToTexture );
467
468   GFXDEBUGEVENT_SCOPE( ProjectedShadow_RenderToTexture, ColorI( 255, 0, 0 ) );
469
470   RenderPassManager *renderPass = _getRenderPass();
471   if ( !renderPass )
472      return;
473
474   GFXTransformSaver saver;
475   
476   // NOTE: GFXTransformSaver does not save/restore the frustum
477   // so we must save it here before we modify it.
478   F32 l, r, b, t, n, f;
479   bool ortho;
480   GFX->getFrustum( &l, &r, &b, &t, &n, &f, &ortho );  
481
482   // Set the orthographic projection
483   // matrix up, to be based on the radius
484   // generated based on our shape.
485   GFX->setOrtho( -mRadius, mRadius, -mRadius, mRadius, 0.001f, (mRadius * 2) * smDepthAdjust, true );
486
487   // Set the world to light space
488   // matrix set up in shouldRender().
489   GFX->setWorldMatrix( mWorldToLight );
490
491   // Get the shapebase datablock if we have one.
492   ShapeBaseData *data = NULL;
493   if ( mShapeBase )
494      data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
495
496   // Init or update the shadow texture size.
497   if ( mShadowTexture.isNull() || ( data && data->shadowSize != mShadowTexture.getWidth() ) )
498   {
499      U32 texSize = getNextPow2( data ? data->shadowSize : 256 * LightShadowMap::smShadowTexScalar );
500      mShadowTexture.set( texSize, texSize, GFXFormatR8G8B8A8, &PostFxTargetProfile, "BLShadow" );
501   }
502
503   GFX->pushActiveRenderTarget();
504
505   if ( !mRenderTarget )
506      mRenderTarget = GFX->allocRenderToTextureTarget();
507
508   mRenderTarget->attachTexture( GFXTextureTarget::DepthStencil, _getDepthTarget( mShadowTexture->getWidth(), mShadowTexture->getHeight() ) );
509   mRenderTarget->attachTexture( GFXTextureTarget::Color0, mShadowTexture );
510   GFX->setActiveRenderTarget( mRenderTarget );
511
512   GFX->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, 0, 0, 0 ), 1.0f, 0 );
513
514   const SceneRenderState *diffuseState = rdata.getSceneState();
515   SceneManager *sceneManager = diffuseState->getSceneManager();
516
517   SceneRenderState baseState
518   (
519      sceneManager,
520      SPT_Shadow,
521      SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ),
522      renderPass
523   );
524
525   baseState.getMaterialDelegate().bind( &ProjectedShadow::_getShadowMaterial );
526   baseState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
527   baseState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
528   baseState.getCullingState().disableZoneCulling( true );
529   
530   mParentObject->prepRenderImage( &baseState );
531   renderPass->renderPass( &baseState );
532
533   // Delete the SceneRenderState we allocated.
534   mRenderTarget->resolve();
535   GFX->popActiveRenderTarget();
536
537   // If we're close enough then filter the shadow.  
538#ifdef TORQUE_BASIC_LIGHTING  
539   if (camDist < BasicLightManager::getShadowFilterDistance())
540#else  
541   if (camDist < AdvancedLightManager::getShadowFilterDistance())
542#endif  
543   {
544      if ( !smShadowFilter )
545      {
546         PostEffect *filter = NULL;
547
548         if ( !Sim::findObject( "BL_ShadowFilterPostFx", filter ) )
549            Con::errorf( "ProjectedShadow::_renderToTexture() - 'BL_ShadowFilterPostFx' not found!" );
550
551         smShadowFilter = filter;
552      }
553
554      if ( smShadowFilter )
555         smShadowFilter->process( NULL, mShadowTexture );
556   }
557
558   // Restore frustum
559   if (!ortho)
560      GFX->setFrustum(l, r, b, t, n, f);
561   else
562      GFX->setOrtho(l, r, b, t, n, f);
563
564   // Set the last render time.
565   mLastRenderTime = Platform::getVirtualMilliseconds();
566
567   // HACK: Will remove in future release!
568   mDecalInstance->mCustomTex = &mShadowTexture;
569}
570
571RenderPassManager* ProjectedShadow::_getRenderPass()
572{
573   if ( smRenderPass.isNull() )
574   {
575      SimObject* renderPass = NULL;
576
577      if ( !Sim::findObject( "BL_ProjectedShadowRPM", renderPass ) )
578         Con::errorf( "ProjectedShadow::init() - 'BL_ProjectedShadowRPM' not initialized" );
579      else
580         smRenderPass = dynamic_cast<RenderPassManager*>(renderPass);
581   }
582
583   return smRenderPass;
584}
585
586GFXTextureObject* ProjectedShadow::_getDepthTarget( U32 width, U32 height )
587{
588   // Get a depth texture target from the pooled profile
589   // which is returned as a temporary.
590   GFXTexHandle depthTex( width, height, GFXFormatD24S8, &BLProjectedShadowZProfile, 
591      "ProjectedShadow::_getDepthTarget()" );
592
593   return depthTex;
594}
595