projectedShadow.cpp 19 KB

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