projectedShadow.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  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. float ProjectedShadow::smFadeStartPixelSize = 200.0f;
  52. float 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. lightDir.normalize();
  193. // No light... no shadow.
  194. if ( !lights[0] )
  195. return false;
  196. // Has the light direction
  197. // changed since last update?
  198. bool lightDirChanged = !mLastLightDir.equal( lightDir );
  199. // Has the parent object moved
  200. // or scaled since the last update?
  201. bool hasMoved = !mLastObjectPosition.equal( mParentObject->getRenderPosition() );
  202. bool hasScaled = !mLastObjectScale.equal( mParentObject->getScale() );
  203. // Set the last light direction
  204. // to the current light direction.
  205. mLastLightDir = lightDir;
  206. mLastObjectPosition = mParentObject->getRenderPosition();
  207. mLastObjectScale = mParentObject->getScale();
  208. // Temps used to generate
  209. // tangent vector for DecalInstance below.
  210. VectorF right( 0, 0, 0 );
  211. VectorF fwd( 0, 0, 0 );
  212. VectorF tmpFwd( 0, 0, 0 );
  213. U32 idx = lightDir.getLeastComponentIndex();
  214. tmpFwd[idx] = 1.0f;
  215. right = mCross( tmpFwd, lightDir );
  216. fwd = mCross( lightDir, right );
  217. right = mCross( fwd, lightDir );
  218. right.normalize();
  219. // Set up the world to light space
  220. // matrix, along with proper position
  221. // and rotation to be used as the world
  222. // matrix for the render to texture later on.
  223. static MatrixF sRotMat(EulerF( 0.0f, -(M_PI_F/2.0f), 0.0f));
  224. mWorldToLight.identity();
  225. MathUtils::getMatrixFromForwardVector( lightDir, &mWorldToLight );
  226. mWorldToLight.setPosition( ( pos + boxCenter ) - ( ( (mRadius * smDepthAdjust) + 0.001f ) * lightDir ) );
  227. mWorldToLight.mul( sRotMat );
  228. mWorldToLight.inverse();
  229. // Get the shapebase datablock if we have one.
  230. ShapeBaseData *data = NULL;
  231. if ( mShapeBase )
  232. data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
  233. // We use the object box's extents multiplied
  234. // by the object's scale divided by 2 for the radius
  235. // because the object's worldsphere radius is not
  236. // rotationally invariant.
  237. mRadius = (objBox.getExtents() * mParentObject->getScale()).len() * 0.5f;
  238. if ( data )
  239. mRadius *= data->shadowSphereAdjust;
  240. // Create the decal if we don't have one yet.
  241. if ( !mDecalInstance )
  242. mDecalInstance = gDecalManager->addDecal( decalPos,
  243. lightDir,
  244. right,
  245. mDecalData,
  246. 1.0f,
  247. 0,
  248. PermanentDecal | ClipDecal | CustomDecal );
  249. if ( !mDecalInstance )
  250. return false;
  251. mDecalInstance->mVisibility = fade;
  252. // Setup decal parameters.
  253. mDecalInstance->mSize = mRadius * 2.0f;
  254. mDecalInstance->mNormal = -lightDir;
  255. mDecalInstance->mTangent = -right;
  256. mDecalInstance->mRotAroundNormal = 0;
  257. mDecalInstance->mPosition = decalPos;
  258. mDecalInstance->mDataBlock = mDecalData;
  259. // If the position of the world
  260. // space box center is the same
  261. // as the decal's position, and
  262. // the light direction has not
  263. // changed, we don't need to clip.
  264. bool shouldClip = lightDirChanged || hasMoved || hasScaled;
  265. // Now, check and see if the object is visible.
  266. const Frustum &frust = state->getCullingFrustum();
  267. if ( frust.isCulled( SphereF( mDecalInstance->mPosition, mDecalInstance->mSize * mDecalInstance->mSize ) ) && !shouldClip )
  268. return false;
  269. F32 shadowLen = 10.0f;
  270. if ( data )
  271. shadowLen = data->shadowProjectionDistance;
  272. const Point3F &boxExtents = objBox.getExtents();
  273. mShadowLength = shadowLen * mParentObject->getScale().z;
  274. // Set up clip depth, and box half
  275. // offset for decal clipping.
  276. Point2F clipParams( mShadowLength, (boxExtents.x + boxExtents.y) * 0.25f );
  277. bool render = false;
  278. bool clipSucceeded = true;
  279. // Clip!
  280. if ( shouldClip )
  281. {
  282. clipSucceeded = gDecalManager->clipDecal( mDecalInstance,
  283. NULL,
  284. &clipParams );
  285. }
  286. // If the clip failed,
  287. // we'll return false in
  288. // order to keep from
  289. // unnecessarily rendering
  290. // into the texture. If
  291. // there was no reason to clip
  292. // on this update, we'll assume we
  293. // should update the texture.
  294. render = clipSucceeded;
  295. // Tell the DecalManager we've changed this decal.
  296. gDecalManager->notifyDecalModified( mDecalInstance );
  297. return render;
  298. }
  299. void ProjectedShadow::_calcScore( const SceneRenderState *state )
  300. {
  301. if ( !mDecalInstance )
  302. return;
  303. F32 pixRadius = mDecalInstance->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y );
  304. F32 pct = pixRadius / mDecalInstance->mDataBlock->fadeStartPixelSize;
  305. U32 msSinceLastRender = Platform::getVirtualMilliseconds() - getLastRenderTime();
  306. ShapeBaseData *data = NULL;
  307. if ( mShapeBase )
  308. data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
  309. // For every 1s this shadow hasn't been
  310. // updated we'll add 10 to the score.
  311. F32 secs = mFloor( (F32)msSinceLastRender / 1000.0f );
  312. mScore = pct + secs;
  313. mClampF( mScore, 0.0f, 2000.0f );
  314. }
  315. void ProjectedShadow::update( const SceneRenderState *state )
  316. {
  317. mUpdateTexture = true;
  318. // Set the decal lod settings.
  319. mDecalData->fadeStartPixelSize = smFadeStartPixelSize;
  320. mDecalData->fadeEndPixelSize = smFadeEndPixelSize;
  321. // Update our decal before
  322. // we render to texture.
  323. // If it fails, something bad happened
  324. // (no light to grab/failed clip) and we should return.
  325. if ( !_updateDecal( state ) )
  326. {
  327. // Release our shadow texture
  328. // so that others can grab it out
  329. // of the pool.
  330. mShadowTexture = NULL;
  331. mUpdateTexture = false;
  332. return;
  333. }
  334. _calcScore( state );
  335. if ( !mCasterPositionSC || !mCasterPositionSC->isValid() )
  336. mCasterPositionSC = mDecalData->matInst->getMaterialParameterHandle( "$shadowCasterPosition" );
  337. if ( !mShadowLengthSC || !mShadowLengthSC->isValid() )
  338. mShadowLengthSC = mDecalData->matInst->getMaterialParameterHandle( "$shadowLength" );
  339. MaterialParameters *matParams = mDecalData->matInst->getMaterialParameters();
  340. matParams->setSafe( mCasterPositionSC, mParentObject->getRenderPosition() );
  341. matParams->setSafe( mShadowLengthSC, mShadowLength / 4.0f );
  342. }
  343. void ProjectedShadow::render( F32 camDist, const TSRenderState &rdata )
  344. {
  345. if ( !mUpdateTexture )
  346. return;
  347. // Do the render to texture,
  348. // DecalManager handles rendering
  349. // the shadow onto the world.
  350. _renderToTexture( camDist, rdata );
  351. }
  352. BaseMatInstance* ProjectedShadow::_getShadowMaterial( BaseMatInstance *inMat )
  353. {
  354. // See if we have an existing material hook.
  355. ShadowMaterialHook *hook = static_cast<ShadowMaterialHook*>( inMat->getHook( ShadowMaterialHook::Type ) );
  356. if ( !hook )
  357. {
  358. // Create a hook and initialize it using the incoming material.
  359. hook = new ShadowMaterialHook;
  360. hook->init( inMat );
  361. inMat->addHook( hook );
  362. }
  363. return hook->getShadowMat( ShadowType_Spot );
  364. }
  365. void ProjectedShadow::_renderToTexture( F32 camDist, const TSRenderState &rdata )
  366. {
  367. PROFILE_SCOPE( ProjectedShadow_RenderToTexture );
  368. GFXDEBUGEVENT_SCOPE( ProjectedShadow_RenderToTexture, ColorI( 255, 0, 0 ) );
  369. RenderPassManager *renderPass = _getRenderPass();
  370. if ( !renderPass )
  371. return;
  372. GFXTransformSaver saver;
  373. // NOTE: GFXTransformSaver does not save/restore the frustum
  374. // so we must save it here before we modify it.
  375. F32 l, r, b, t, n, f;
  376. bool ortho;
  377. GFX->getFrustum( &l, &r, &b, &t, &n, &f, &ortho );
  378. // Set the orthographic projection
  379. // matrix up, to be based on the radius
  380. // generated based on our shape.
  381. GFX->setOrtho( -mRadius, mRadius, -mRadius, mRadius, 0.001f, (mRadius * 2) * smDepthAdjust, true );
  382. // Set the world to light space
  383. // matrix set up in shouldRender().
  384. GFX->setWorldMatrix( mWorldToLight );
  385. // Get the shapebase datablock if we have one.
  386. ShapeBaseData *data = NULL;
  387. if ( mShapeBase )
  388. data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
  389. // Init or update the shadow texture size.
  390. if ( mShadowTexture.isNull() || ( data && data->shadowSize != mShadowTexture.getWidth() ) )
  391. {
  392. U32 texSize = getNextPow2( data ? data->shadowSize : 256 * LightShadowMap::smShadowTexScalar );
  393. mShadowTexture.set( texSize, texSize, GFXFormatR8G8B8A8, &PostFxTargetProfile, "BLShadow" );
  394. }
  395. GFX->pushActiveRenderTarget();
  396. if ( !mRenderTarget )
  397. mRenderTarget = GFX->allocRenderToTextureTarget();
  398. mRenderTarget->attachTexture( GFXTextureTarget::DepthStencil, _getDepthTarget( mShadowTexture->getWidth(), mShadowTexture->getHeight() ) );
  399. mRenderTarget->attachTexture( GFXTextureTarget::Color0, mShadowTexture );
  400. GFX->setActiveRenderTarget( mRenderTarget );
  401. GFX->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, 0, 0, 0 ), 1.0f, 0 );
  402. const SceneRenderState *diffuseState = rdata.getSceneState();
  403. SceneManager *sceneManager = diffuseState->getSceneManager();
  404. SceneRenderState baseState
  405. (
  406. sceneManager,
  407. SPT_Shadow,
  408. SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ),
  409. renderPass
  410. );
  411. baseState.getMaterialDelegate().bind( &ProjectedShadow::_getShadowMaterial );
  412. baseState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
  413. baseState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
  414. baseState.getCullingState().disableZoneCulling( true );
  415. mParentObject->prepRenderImage( &baseState );
  416. renderPass->renderPass( &baseState );
  417. // Delete the SceneRenderState we allocated.
  418. mRenderTarget->resolve();
  419. GFX->popActiveRenderTarget();
  420. // If we're close enough then filter the shadow.
  421. if ( camDist < BasicLightManager::getShadowFilterDistance() )
  422. {
  423. if ( !smShadowFilter )
  424. {
  425. PostEffect *filter = NULL;
  426. if ( !Sim::findObject( "BL_ShadowFilterPostFx", filter ) )
  427. Con::errorf( "ProjectedShadow::_renderToTexture() - 'BL_ShadowFilterPostFx' not found!" );
  428. smShadowFilter = filter;
  429. }
  430. if ( smShadowFilter )
  431. smShadowFilter->process( NULL, mShadowTexture );
  432. }
  433. // Restore frustum
  434. if (!ortho)
  435. GFX->setFrustum(l, r, b, t, n, f);
  436. else
  437. GFX->setOrtho(l, r, b, t, n, f);
  438. // Set the last render time.
  439. mLastRenderTime = Platform::getVirtualMilliseconds();
  440. // HACK: Will remove in future release!
  441. mDecalInstance->mCustomTex = &mShadowTexture;
  442. }
  443. RenderPassManager* ProjectedShadow::_getRenderPass()
  444. {
  445. if ( smRenderPass.isNull() )
  446. {
  447. SimObject* renderPass = NULL;
  448. if ( !Sim::findObject( "BL_ProjectedShadowRPM", renderPass ) )
  449. Con::errorf( "ProjectedShadow::init() - 'BL_ProjectedShadowRPM' not initialized" );
  450. else
  451. smRenderPass = dynamic_cast<RenderPassManager*>(renderPass);
  452. }
  453. return smRenderPass;
  454. }
  455. GFXTextureObject* ProjectedShadow::_getDepthTarget( U32 width, U32 height )
  456. {
  457. // Get a depth texture target from the pooled profile
  458. // which is returned as a temporary.
  459. GFXTexHandle depthTex( width, height, GFXFormatD24S8, &BLProjectedShadowZProfile,
  460. "ProjectedShadow::_getDepthTarget()" );
  461. return depthTex;
  462. }