projectedShadow.cpp 19 KB

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