123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "lighting/common/projectedShadow.h"
- #include "gfx/primBuilder.h"
- #include "gfx/gfxTextureManager.h"
- #include "gfx/bitmap/gBitmap.h"
- #include "gfx/gfxDebugEvent.h"
- #include "math/mathUtils.h"
- #include "lighting/lightInfo.h"
- #include "lighting/lightingInterfaces.h"
- #include "T3D/shapeBase.h"
- #include "scene/sceneManager.h"
- #include "lighting/lightManager.h"
- #include "ts/tsMesh.h"
- #include "T3D/decal/decalManager.h"
- #include "T3D/decal/decalInstance.h"
- #include "renderInstance/renderPassManager.h"
- #include "renderInstance/renderMeshMgr.h"
- #include "gfx/gfxTransformSaver.h"
- #include "materials/customMaterialDefinition.h"
- #include "materials/materialFeatureTypes.h"
- #include "console/console.h"
- #include "postFx/postEffect.h"
- #ifdef TORQUE_BASIC_LIGHTING
- #include "lighting/basic/basicLightManager.h"
- #else
- #include "lighting/advanced/advancedLightManager.h"
- #endif
- #include "lighting/shadowMap/shadowMatHook.h"
- #include "materials/materialManager.h"
- #include "lighting/shadowMap/lightShadowMap.h"
- SimObjectPtr<RenderPassManager> ProjectedShadow::smRenderPass = NULL;
- SimObjectPtr<PostEffect> ProjectedShadow::smShadowFilter = NULL;
- F32 ProjectedShadow::smDepthAdjust = 10.0f;
- F32 ProjectedShadow::smFadeStartPixelSize = 200.0f;
- F32 ProjectedShadow::smFadeEndPixelSize = 35.0f;
- GFX_ImplementTextureProfile( BLProjectedShadowProfile,
- GFXTextureProfile::DiffuseMap,
- GFXTextureProfile::PreserveSize |
- GFXTextureProfile::RenderTarget |
- GFXTextureProfile::Pooled,
- GFXTextureProfile::NONE );
- GFX_ImplementTextureProfile( BLProjectedShadowZProfile,
- GFXTextureProfile::DiffuseMap,
- GFXTextureProfile::PreserveSize |
- GFXTextureProfile::ZTarget |
- GFXTextureProfile::Pooled,
- GFXTextureProfile::NONE );
- ProjectedShadow::ProjectedShadow( SceneObject *object )
- {
- mParentObject = object;
- mShapeBase = dynamic_cast<ShapeBase*>( object );
- mRadius = 0;
- mLastRenderTime = 0;
- mUpdateTexture = false;
- mShadowLength = 10.0f;
- mDecalData = new DecalData;
- mDecalData->skipVertexNormals = true;
- mDecalInstance = NULL;
-
- mLastLightDir.set( 0, 0, 0 );
- mLastObjectPosition.set( object->getRenderPosition() );
- mLastObjectScale.set( object->getScale() );
- CustomMaterial *customMat = NULL;
- Sim::findObject( "BL_ProjectedShadowMaterial", customMat );
- if ( customMat )
- {
- mDecalData->mMaterial = customMat;
- mDecalData->matInst = customMat->createMatInstance();
- }
- else
- mDecalData->matInst = MATMGR->createMatInstance( "WarningMaterial" );
- mDecalData->matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTT>() );
- mCasterPositionSC = NULL;
- mShadowLengthSC = NULL;
- }
- ProjectedShadow::~ProjectedShadow()
- {
- if ( mDecalInstance )
- gDecalManager->removeDecal( mDecalInstance );
- delete mDecalData;
-
- mShadowTexture = NULL;
- mRenderTarget = NULL;
- }
- bool ProjectedShadow::shouldRender( const SceneRenderState *state )
- {
- // Don't render if our object has been removed from the
- // scene graph.
-
- if( !mParentObject->getSceneManager() )
- return false;
- // Don't render if the ShapeBase
- // object's fade value is greater
- // than the visibility epsilon.
- bool shapeFade = mShapeBase && mShapeBase->getFadeVal() < TSMesh::VISIBILITY_EPSILON;
- // Get the shapebase datablock if we have one.
- ShapeBaseData *data = NULL;
- if ( mShapeBase )
- data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
-
- // Also don't render if
- // the camera distance is greater
- // than the shadow length.
- if ( shapeFade || !mDecalData ||
- ( mDecalInstance &&
- mDecalInstance->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y ) < mDecalInstance->mDataBlock->fadeEndPixelSize ) )
- {
- // Release our shadow texture
- // so that others can grab it out
- // of the pool.
- mShadowTexture = NULL;
- return false;
- }
- return true;
- }
- bool ProjectedShadow::_updateDecal( const SceneRenderState *state )
- {
- PROFILE_SCOPE( ProjectedShadow_UpdateDecal );
- if ( !LIGHTMGR )
- return false;
- // Get the position of the decal first.
- const Box3F &objBox = mParentObject->getObjBox();
- const Point3F boxCenter = objBox.getCenter();
- Point3F decalPos = boxCenter;
- const MatrixF &renderTransform = mParentObject->getRenderTransform();
- {
- // Set up the decal position.
- // We use the object space box center
- // multiplied by the render transform
- // of the object to ensure we benefit
- // from interpolation.
- MatrixF t( renderTransform );
- t.setColumn(2,Point3F::UnitZ);
- t.mulP( decalPos );
- }
- if ( mDecalInstance )
- {
- mDecalInstance->mPosition = decalPos;
- if ( !shouldRender( state ) )
- return false;
- }
- // Get the sunlight for the shadow projection.
- // We want the LightManager to return NULL if it can't
- // get the "real" sun, so we specify false for the useDefault parameter.
- LightInfo *lights[4] = {0};
- LightQuery query;
- query.init( mParentObject->getWorldSphere() );
- query.getLights( lights, 4 );
- Point3F pos = renderTransform.getPosition();
- Point3F lightDir( 0, 0, 0 );
- Point3F tmp( 0, 0, 0 );
- F32 weight = 0;
- F32 range = 0;
- U32 lightCount = 0;
- F32 dist = 0;
- F32 fade = 0;
- for ( U32 i = 0; i < 4; i++ )
- {
- // If we got a NULL light,
- // we're at the end of the list.
- if ( !lights[i] )
- break;
- if ( !lights[i]->getCastShadows() )
- continue;
- if ( lights[i]->getType() != LightInfo::Point )
- tmp = lights[i]->getDirection();
- else
- tmp = pos - lights[i]->getPosition();
- range = lights[i]->getRange().x;
- dist = ( (tmp.lenSquared()) / ((range * range) * 0.5f));
- weight = mClampF( 1.0f - ( tmp.lenSquared() / (range * range)), 0.00001f, 1.0f );
- if ( lights[i]->getType() == LightInfo::Vector )
- fade = getMax( fade, 1.0f );
- else
- fade = getMax( fade, mClampF( 1.0f - dist, 0.00001f, 1.0f ) );
- lightDir += tmp * weight;
- lightCount++;
- }
- if (mShapeBase)
- fade *= mShapeBase->getFadeVal();
- lightDir.normalize();
-
- // No light... no shadow.
- if ( !lights[0] )
- return false;
-
- // Has the light direction
- // changed since last update?
- bool lightDirChanged = !mLastLightDir.equal( lightDir );
- // Has the parent object moved
- // or scaled since the last update?
- bool hasMoved = !mLastObjectPosition.equal( mParentObject->getRenderPosition() );
- bool hasScaled = !mLastObjectScale.equal( mParentObject->getScale() );
- // Set the last light direction
- // to the current light direction.
- mLastLightDir = lightDir;
- mLastObjectPosition = mParentObject->getRenderPosition();
- mLastObjectScale = mParentObject->getScale();
- // Temps used to generate
- // tangent vector for DecalInstance below.
- VectorF right( 0, 0, 0 );
- VectorF fwd( 0, 0, 0 );
- VectorF tmpFwd( 0, 0, 0 );
-
- U32 idx = lightDir.getLeastComponentIndex();
- tmpFwd[idx] = 1.0f;
- right = mCross( tmpFwd, lightDir );
- fwd = mCross( lightDir, right );
- right = mCross( fwd, lightDir );
- right.normalize();
- // Set up the world to light space
- // matrix, along with proper position
- // and rotation to be used as the world
- // matrix for the render to texture later on.
- static MatrixF sRotMat(EulerF( 0.0f, -(M_PI_F/2.0f), 0.0f));
- mWorldToLight.identity();
- MathUtils::getMatrixFromForwardVector( lightDir, &mWorldToLight );
- mWorldToLight.setPosition( ( pos + boxCenter ) - ( ( (mRadius * smDepthAdjust) + 0.001f ) * lightDir ) );
- mWorldToLight.mul( sRotMat );
- mWorldToLight.inverse();
-
- // Get the shapebase datablock if we have one.
- ShapeBaseData *data = NULL;
- if ( mShapeBase )
- data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
- // We use the object box's extents multiplied
- // by the object's scale divided by 2 for the radius
- // because the object's worldsphere radius is not
- // rotationally invariant.
- mRadius = (objBox.getExtents() * mParentObject->getScale()).len() * 0.5f;
- if ( data )
- mRadius *= data->shadowSphereAdjust;
- // Create the decal if we don't have one yet.
- if ( !mDecalInstance )
- mDecalInstance = gDecalManager->addDecal( decalPos,
- lightDir,
- right,
- mDecalData,
- 1.0f,
- 0,
- PermanentDecal | ClipDecal | CustomDecal );
- if ( !mDecalInstance )
- return false;
- mDecalInstance->mVisibility = fade;
- // Setup decal parameters.
- mDecalInstance->mSize = mRadius * 2.0f;
- mDecalInstance->mNormal = -lightDir;
- mDecalInstance->mTangent = -right;
- mDecalInstance->mRotAroundNormal = 0;
- mDecalInstance->mPosition = decalPos;
- mDecalInstance->mDataBlock = mDecalData;
-
- // If the position of the world
- // space box center is the same
- // as the decal's position, and
- // the light direction has not
- // changed, we don't need to clip.
- bool shouldClip = lightDirChanged || hasMoved || hasScaled;
- // Now, check and see if the object is visible.
- const Frustum &frust = state->getCullingFrustum();
- if ( frust.isCulled( SphereF( mDecalInstance->mPosition, mDecalInstance->mSize * mDecalInstance->mSize ) ) && !shouldClip )
- return false;
- F32 shadowLen = 10.0f;
- if ( data )
- shadowLen = data->shadowProjectionDistance;
- const Point3F &boxExtents = objBox.getExtents();
-
-
- mShadowLength = shadowLen * mParentObject->getScale().z;
- // Set up clip depth, and box half
- // offset for decal clipping.
- Point2F clipParams( mShadowLength, (boxExtents.x + boxExtents.y) * 0.25f );
- bool render = false;
- bool clipSucceeded = true;
- // Clip!
- if ( shouldClip )
- {
- clipSucceeded = gDecalManager->clipDecal( mDecalInstance,
- NULL,
- &clipParams );
- }
- // If the clip failed,
- // we'll return false in
- // order to keep from
- // unnecessarily rendering
- // into the texture. If
- // there was no reason to clip
- // on this update, we'll assume we
- // should update the texture.
- render = clipSucceeded;
- // Tell the DecalManager we've changed this decal.
- gDecalManager->notifyDecalModified( mDecalInstance );
- return render;
- }
- void ProjectedShadow::_calcScore( const SceneRenderState *state )
- {
- if ( !mDecalInstance )
- return;
- F32 pixRadius = mDecalInstance->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y );
-
- F32 pct = pixRadius / mDecalInstance->mDataBlock->fadeStartPixelSize;
- U32 msSinceLastRender = Platform::getVirtualMilliseconds() - getLastRenderTime();
- ShapeBaseData *data = NULL;
- if ( mShapeBase )
- data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
- // For every 1s this shadow hasn't been
- // updated we'll add 10 to the score.
- F32 secs = mFloor( (F32)msSinceLastRender / 1000.0f );
-
- mScore = pct + secs;
- mClampF( mScore, 0.0f, 2000.0f );
- }
- void ProjectedShadow::update( const SceneRenderState *state )
- {
- mUpdateTexture = true;
- // Set the decal lod settings.
- mDecalData->fadeStartPixelSize = smFadeStartPixelSize;
- mDecalData->fadeEndPixelSize = smFadeEndPixelSize;
- // Update our decal before
- // we render to texture.
- // If it fails, something bad happened
- // (no light to grab/failed clip) and we should return.
- if ( !_updateDecal( state ) )
- {
- // Release our shadow texture
- // so that others can grab it out
- // of the pool.
- mShadowTexture = NULL;
- mUpdateTexture = false;
- return;
- }
- _calcScore( state );
- if ( !mCasterPositionSC || !mCasterPositionSC->isValid() )
- mCasterPositionSC = mDecalData->matInst->getMaterialParameterHandle( "$shadowCasterPosition" );
- if ( !mShadowLengthSC || !mShadowLengthSC->isValid() )
- mShadowLengthSC = mDecalData->matInst->getMaterialParameterHandle( "$shadowLength" );
- MaterialParameters *matParams = mDecalData->matInst->getMaterialParameters();
- matParams->setSafe( mCasterPositionSC, mParentObject->getRenderPosition() );
- matParams->setSafe( mShadowLengthSC, mShadowLength / 4.0f );
- }
- void ProjectedShadow::render( F32 camDist, const TSRenderState &rdata )
- {
- if ( !mUpdateTexture )
- return;
-
- // Do the render to texture,
- // DecalManager handles rendering
- // the shadow onto the world.
- _renderToTexture( camDist, rdata );
- }
- BaseMatInstance* ProjectedShadow::_getShadowMaterial( BaseMatInstance *inMat )
- {
- // See if we have an existing material hook.
- ShadowMaterialHook *hook = static_cast<ShadowMaterialHook*>( inMat->getHook( ShadowMaterialHook::Type ) );
- if ( !hook )
- {
- // Create a hook and initialize it using the incoming material.
- hook = new ShadowMaterialHook;
- hook->init( inMat );
- inMat->addHook( hook );
- }
- return hook->getShadowMat( ShadowType_Spot );
- }
- void ProjectedShadow::_renderToTexture( F32 camDist, const TSRenderState &rdata )
- {
- PROFILE_SCOPE( ProjectedShadow_RenderToTexture );
- GFXDEBUGEVENT_SCOPE( ProjectedShadow_RenderToTexture, ColorI( 255, 0, 0 ) );
- RenderPassManager *renderPass = _getRenderPass();
- if ( !renderPass )
- return;
- GFXTransformSaver saver;
-
- // NOTE: GFXTransformSaver does not save/restore the frustum
- // so we must save it here before we modify it.
- F32 l, r, b, t, n, f;
- bool ortho;
- GFX->getFrustum( &l, &r, &b, &t, &n, &f, &ortho );
- // Set the orthographic projection
- // matrix up, to be based on the radius
- // generated based on our shape.
- GFX->setOrtho( -mRadius, mRadius, -mRadius, mRadius, 0.001f, (mRadius * 2) * smDepthAdjust, true );
- // Set the world to light space
- // matrix set up in shouldRender().
- GFX->setWorldMatrix( mWorldToLight );
- // Get the shapebase datablock if we have one.
- ShapeBaseData *data = NULL;
- if ( mShapeBase )
- data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
- // Init or update the shadow texture size.
- if ( mShadowTexture.isNull() || ( data && data->shadowSize != mShadowTexture.getWidth() ) )
- {
- U32 texSize = getNextPow2( data ? data->shadowSize : 256 * LightShadowMap::smShadowTexScalar );
- mShadowTexture.set( texSize, texSize, GFXFormatR8G8B8A8, &PostFxTargetProfile, "BLShadow" );
- }
- GFX->pushActiveRenderTarget();
- if ( !mRenderTarget )
- mRenderTarget = GFX->allocRenderToTextureTarget();
- mRenderTarget->attachTexture( GFXTextureTarget::DepthStencil, _getDepthTarget( mShadowTexture->getWidth(), mShadowTexture->getHeight() ) );
- mRenderTarget->attachTexture( GFXTextureTarget::Color0, mShadowTexture );
- GFX->setActiveRenderTarget( mRenderTarget );
- GFX->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, 0, 0, 0 ), 1.0f, 0 );
- const SceneRenderState *diffuseState = rdata.getSceneState();
- SceneManager *sceneManager = diffuseState->getSceneManager();
- SceneRenderState baseState
- (
- sceneManager,
- SPT_Shadow,
- SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ),
- renderPass
- );
- baseState.getMaterialDelegate().bind( &ProjectedShadow::_getShadowMaterial );
- baseState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
- baseState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
- baseState.getCullingState().disableZoneCulling( true );
-
- mParentObject->prepRenderImage( &baseState );
- renderPass->renderPass( &baseState );
- // Delete the SceneRenderState we allocated.
- mRenderTarget->resolve();
- GFX->popActiveRenderTarget();
- // If we're close enough then filter the shadow.
- #ifdef TORQUE_BASIC_LIGHTING
- if (camDist < BasicLightManager::getShadowFilterDistance())
- #else
- if (camDist < AdvancedLightManager::getShadowFilterDistance())
- #endif
- {
- if ( !smShadowFilter )
- {
- PostEffect *filter = NULL;
- if ( !Sim::findObject( "BL_ShadowFilterPostFx", filter ) )
- Con::errorf( "ProjectedShadow::_renderToTexture() - 'BL_ShadowFilterPostFx' not found!" );
- smShadowFilter = filter;
- }
- if ( smShadowFilter )
- smShadowFilter->process( NULL, mShadowTexture );
- }
- // Restore frustum
- if (!ortho)
- GFX->setFrustum(l, r, b, t, n, f);
- else
- GFX->setOrtho(l, r, b, t, n, f);
- // Set the last render time.
- mLastRenderTime = Platform::getVirtualMilliseconds();
- // HACK: Will remove in future release!
- mDecalInstance->mCustomTex = &mShadowTexture;
- }
- RenderPassManager* ProjectedShadow::_getRenderPass()
- {
- if ( smRenderPass.isNull() )
- {
- SimObject* renderPass = NULL;
- if ( !Sim::findObject( "BL_ProjectedShadowRPM", renderPass ) )
- Con::errorf( "ProjectedShadow::init() - 'BL_ProjectedShadowRPM' not initialized" );
- else
- smRenderPass = dynamic_cast<RenderPassManager*>(renderPass);
- }
- return smRenderPass;
- }
- GFXTextureObject* ProjectedShadow::_getDepthTarget( U32 width, U32 height )
- {
- // Get a depth texture target from the pooled profile
- // which is returned as a temporary.
- GFXTexHandle depthTex( width, height, GFXFormatD24S8, &BLProjectedShadowZProfile,
- "ProjectedShadow::_getDepthTarget()" );
- return depthTex;
- }
|