Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View file

@ -0,0 +1,202 @@
//-----------------------------------------------------------------------------
// 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/shadowMap/cubeLightShadowMap.h"
#include "lighting/shadowMap/shadowMapManager.h"
#include "lighting/common/lightMapParams.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxTransformSaver.h"
#include "gfx/gfxDebugEvent.h"
#include "renderInstance/renderPassManager.h"
#include "materials/materialDefinition.h"
#include "gfx/util/gfxFrustumSaver.h"
#include "math/mathUtils.h"
CubeLightShadowMap::CubeLightShadowMap( LightInfo *light )
: Parent( light )
{
}
bool CubeLightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc )
{
if ( currTexFlag == Material::DynamicLight )
{
S32 reg = lsc->mShadowMapSC->getSamplerRegister();
if ( reg != -1 )
GFX->setCubeTexture( reg, mCubemap );
return true;
}
return false;
}
void CubeLightShadowMap::setShaderParameters( GFXShaderConstBuffer *params,
LightingShaderConstants *lsc )
{
if ( lsc->mTapRotationTexSC->isValid() )
GFX->setTexture( lsc->mTapRotationTexSC->getSamplerRegister(),
SHADOWMGR->getTapRotationTex() );
ShadowMapParams *p = mLight->getExtended<ShadowMapParams>();
if ( lsc->mLightParamsSC->isValid() )
{
Point4F lightParams( mLight->getRange().x,
p->overDarkFactor.x,
0.0f,
0.0f );
params->set(lsc->mLightParamsSC, lightParams);
}
// The softness is a factor of the texel size.
params->setSafe( lsc->mShadowSoftnessConst, p->shadowSoftness * ( 1.0f / mTexSize ) );
}
void CubeLightShadowMap::releaseTextures()
{
Parent::releaseTextures();
mCubemap = NULL;
}
void CubeLightShadowMap::_render( RenderPassManager* renderPass,
const SceneRenderState *diffuseState )
{
PROFILE_SCOPE( CubeLightShadowMap_Render );
const LightMapParams *lmParams = mLight->getExtended<LightMapParams>();
const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true;
const U32 texSize = getBestTexSize();
if ( mCubemap.isNull() ||
mTexSize != texSize )
{
mTexSize = texSize;
mCubemap = GFX->createCubemap();
mCubemap->initDynamic( mTexSize, LightShadowMap::ShadowMapFormat );
}
// Setup the world to light projection which is used
// in the shader to transform the light vector for the
// shadow lookup.
mWorldToLightProj = mLight->getTransform();
mWorldToLightProj.inverse();
// Set up frustum and visible distance
GFXFrustumSaver fsaver;
GFXTransformSaver saver;
{
F32 left, right, top, bottom;
MathUtils::makeFrustum( &left, &right, &top, &bottom, M_HALFPI_F, 1.0f, 0.1f );
GFX->setFrustum( left, right, bottom, top, 0.1f, mLight->getRange().x );
}
// Render the shadowmap!
GFX->pushActiveRenderTarget();
for( U32 i = 0; i < 6; i++ )
{
// Standard view that will be overridden below.
VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f);
switch( i )
{
case 0 : // D3DCUBEMAP_FACE_POSITIVE_X:
vLookatPt = VectorF(1.0f, 0.0f, 0.0f);
vUpVec = VectorF(0.0f, 1.0f, 0.0f);
break;
case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X:
vLookatPt = VectorF(-1.0f, 0.0f, 0.0f);
vUpVec = VectorF(0.0f, 1.0f, 0.0f);
break;
case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y:
vLookatPt = VectorF(0.0f, 1.0f, 0.0f);
vUpVec = VectorF(0.0f, 0.0f,-1.0f);
break;
case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y:
vLookatPt = VectorF(0.0f, -1.0f, 0.0f);
vUpVec = VectorF(0.0f, 0.0f, 1.0f);
break;
case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z:
vLookatPt = VectorF(0.0f, 0.0f, 1.0f);
vUpVec = VectorF(0.0f, 1.0f, 0.0f);
break;
case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
vLookatPt = VectorF(0.0f, 0.0f, -1.0f);
vUpVec = VectorF(0.0f, 1.0f, 0.0f);
break;
}
GFXDEBUGEVENT_START( CubeLightShadowMap_Render_Face, ColorI::RED );
// create camera matrix
VectorF cross = mCross(vUpVec, vLookatPt);
cross.normalizeSafe();
MatrixF lightMatrix(true);
lightMatrix.setColumn(0, cross);
lightMatrix.setColumn(1, vLookatPt);
lightMatrix.setColumn(2, vUpVec);
lightMatrix.setPosition( mLight->getPosition() );
lightMatrix.inverse();
GFX->setWorldMatrix( lightMatrix );
mTarget->attachTexture(GFXTextureTarget::Color0, mCubemap, i);
mTarget->attachTexture(GFXTextureTarget::DepthStencil, _getDepthTarget( mTexSize, mTexSize ));
GFX->setActiveRenderTarget(mTarget);
GFX->clear( GFXClearTarget | GFXClearStencil | GFXClearZBuffer, ColorI(255,255,255,255), 1.0f, 0 );
// Create scene state, prep it
SceneManager* sceneManager = diffuseState->getSceneManager();
SceneRenderState shadowRenderState
(
sceneManager,
SPT_Shadow,
SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ),
renderPass
);
shadowRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial );
shadowRenderState.renderNonLightmappedMeshes( true );
shadowRenderState.renderLightmappedMeshes( bUseLightmappedGeometry );
shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
sceneManager->renderSceneNoLights( &shadowRenderState, SHADOW_TYPEMASK );
_debugRender( &shadowRenderState );
// Resolve this face
mTarget->resolve();
GFXDEBUGEVENT_END();
}
GFX->popActiveRenderTarget();
}

View file

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _CUBELIGHTSHADOWMAP_H_
#define _CUBELIGHTSHADOWMAP_H_
#ifndef _LIGHTSHADOWMAP_H_
#include "lighting/shadowMap/lightShadowMap.h"
#endif
#ifndef _GFXCUBEMAP_H_
#include "gfx/gfxCubemap.h"
#endif
class CubeLightShadowMap : public LightShadowMap
{
typedef LightShadowMap Parent;
public:
CubeLightShadowMap( LightInfo *light );
// LightShadowMap
virtual bool hasShadowTex() const { return mCubemap.isValid(); }
virtual ShadowType getShadowType() const { return ShadowType_CubeMap; }
virtual void _render( RenderPassManager* renderPass, const SceneRenderState *diffuseState );
virtual void setShaderParameters( GFXShaderConstBuffer* params, LightingShaderConstants* lsc );
virtual void releaseTextures();
virtual bool setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc );
protected:
/// The shadow cubemap.
GFXCubemapHandle mCubemap;
};
#endif // _CUBELIGHTSHADOWMAP_H_

View file

@ -0,0 +1,187 @@
//-----------------------------------------------------------------------------
// 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/shadowMap/dualParaboloidLightShadowMap.h"
#include "lighting/common/lightMapParams.h"
#include "lighting/shadowMap/shadowMapManager.h"
#include "math/mathUtils.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
#include "gfx/gfxDebugEvent.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxTransformSaver.h"
#include "gfx/util/gfxFrustumSaver.h"
#include "renderInstance/renderPassManager.h"
#include "materials/materialDefinition.h"
#include "math/util/matrixSet.h"
DualParaboloidLightShadowMap::DualParaboloidLightShadowMap( LightInfo *light )
: Parent( light )
{
}
void DualParaboloidLightShadowMap::_render( RenderPassManager* renderPass,
const SceneRenderState *diffuseState )
{
PROFILE_SCOPE(DualParaboloidLightShadowMap_render);
const ShadowMapParams *p = mLight->getExtended<ShadowMapParams>();
const LightMapParams *lmParams = mLight->getExtended<LightMapParams>();
const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true;
const U32 texSize = getBestTexSize( 2 );
if ( mShadowMapTex.isNull() ||
mTexSize != texSize )
{
mTexSize = texSize;
mShadowMapTex.set( mTexSize * 2, mTexSize,
ShadowMapFormat, &ShadowMapProfile,
"DualParaboloidLightShadowMap" );
}
GFXFrustumSaver frustSaver;
GFXTransformSaver saver;
// Set and Clear target
GFX->pushActiveRenderTarget();
mTarget->attachTexture(GFXTextureTarget::Color0, mShadowMapTex);
mTarget->attachTexture( GFXTextureTarget::DepthStencil,
_getDepthTarget( mShadowMapTex->getWidth(), mShadowMapTex->getHeight() ) );
GFX->setActiveRenderTarget(mTarget);
GFX->clear(GFXClearTarget | GFXClearStencil | GFXClearZBuffer, ColorI::WHITE, 1.0f, 0);
const bool bUseSinglePassDPM = (p->shadowType == ShadowType_DualParaboloidSinglePass);
// Set up matrix and visible distance
mWorldToLightProj = mLight->getTransform();
mWorldToLightProj.inverse();
const F32 &lightRadius = mLight->getRange().x;
const F32 paraboloidNearPlane = 0.01f;
const F32 renderPosOffset = 0.01f;
// Alter for creation of scene state if this is a single pass map
if(bUseSinglePassDPM)
{
VectorF camDir;
MatrixF temp = mLight->getTransform();
temp.getColumn(1, &camDir);
temp.setPosition(mLight->getPosition() - camDir * (lightRadius + renderPosOffset));
temp.inverse();
GFX->setWorldMatrix(temp);
GFX->setOrtho(-lightRadius, lightRadius, -lightRadius, lightRadius, paraboloidNearPlane, 2.0f * lightRadius, true);
}
else
{
VectorF camDir;
MatrixF temp = mLight->getTransform();
temp.getColumn(1, &camDir);
temp.setPosition(mLight->getPosition() - camDir * renderPosOffset);
temp.inverse();
GFX->setWorldMatrix(temp);
GFX->setOrtho(-lightRadius, lightRadius, -lightRadius, lightRadius, paraboloidNearPlane, lightRadius, true);
}
SceneManager* sceneManager = diffuseState->getSceneManager();
// Front map render
{
SceneRenderState frontMapRenderState
(
sceneManager,
SPT_Shadow,
SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ),
renderPass
);
frontMapRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial );
frontMapRenderState.renderNonLightmappedMeshes( true );
frontMapRenderState.renderLightmappedMeshes( bUseLightmappedGeometry );
frontMapRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
frontMapRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
if(bUseSinglePassDPM)
{
GFX->setWorldMatrix(mWorldToLightProj);
frontMapRenderState.getRenderPass()->getMatrixSet().setSceneView(mWorldToLightProj);
GFX->setOrtho(-lightRadius, lightRadius, -lightRadius, lightRadius, paraboloidNearPlane, lightRadius, true);
}
GFXDEBUGEVENT_SCOPE( DualParaboloidLightShadowMap_Render_FrontFacingParaboloid, ColorI::RED );
mShadowMapScale.set(0.5f, 1.0f);
mShadowMapOffset.set(-0.5f, 0.0f);
sceneManager->renderSceneNoLights( &frontMapRenderState, SHADOW_TYPEMASK );
_debugRender( &frontMapRenderState );
}
// Back map render
if(!bUseSinglePassDPM)
{
GFXDEBUGEVENT_SCOPE( DualParaboloidLightShadowMap_Render_BackFacingParaboloid, ColorI::RED );
mShadowMapScale.set(0.5f, 1.0f);
mShadowMapOffset.set(0.5f, 0.0f);
// Invert direction on camera matrix
VectorF right, forward;
MatrixF temp = mLight->getTransform();
temp.getColumn( 1, &forward );
temp.getColumn( 0, &right );
forward *= -1.0f;
right *= -1.0f;
temp.setColumn( 1, forward );
temp.setColumn( 0, right );
temp.setPosition(mLight->getPosition() - forward * -renderPosOffset);
temp.inverse();
GFX->setWorldMatrix(temp);
// Create an inverted scene state for the back-map
SceneRenderState backMapRenderState
(
sceneManager,
SPT_Shadow,
SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ),
renderPass
);
backMapRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial );
backMapRenderState.renderNonLightmappedMeshes( true );
backMapRenderState.renderLightmappedMeshes( bUseLightmappedGeometry );
backMapRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
backMapRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
backMapRenderState.getRenderPass()->getMatrixSet().setSceneView(temp);
// Draw scene
sceneManager->renderSceneNoLights( &backMapRenderState );
_debugRender( &backMapRenderState );
}
mTarget->resolve();
GFX->popActiveRenderTarget();
}

View file

@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _DUALPARABOLOIDLIGHTSHADOWMAP_H_
#define _DUALPARABOLOIDLIGHTSHADOWMAP_H_
#ifndef _PARABOLOIDLIGHTSHADOWMAP_H_
#include "lighting/shadowMap/paraboloidLightShadowMap.h"
#endif
class DualParaboloidLightShadowMap : public ParaboloidLightShadowMap
{
typedef ParaboloidLightShadowMap Parent;
public:
DualParaboloidLightShadowMap( LightInfo *light );
virtual void _render( RenderPassManager* renderPass, const SceneRenderState *diffuseState );
};
#endif // _DUALPARABOLOIDLIGHTSHADOWMAP_H_

View file

@ -0,0 +1,752 @@
//-----------------------------------------------------------------------------
// 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/shadowMap/lightShadowMap.h"
#include "lighting/shadowMap/shadowMapManager.h"
#include "lighting/shadowMap/shadowMatHook.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxTextureManager.h"
#include "gfx/gfxOcclusionQuery.h"
#include "gfx/gfxCardProfile.h"
#include "gfx/sim/debugDraw.h"
#include "materials/materialDefinition.h"
#include "materials/baseMatInstance.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
#include "scene/zones/SceneZoneSpace.h"
#include "lighting/lightManager.h"
#include "math/mathUtils.h"
#include "shaderGen/shaderGenVars.h"
#include "core/util/safeDelete.h"
#include "core/stream/bitStream.h"
#include "math/mathIO.h"
#include "materials/shaderData.h"
// Used for creation in ShadowMapParams::getOrCreateShadowMap()
#include "lighting/shadowMap/singleLightShadowMap.h"
#include "lighting/shadowMap/pssmLightShadowMap.h"
#include "lighting/shadowMap/cubeLightShadowMap.h"
#include "lighting/shadowMap/dualParaboloidLightShadowMap.h"
// Remove this when the shader constants are reworked better
#include "lighting/advanced/advancedLightManager.h"
#include "lighting/advanced/advancedLightBinManager.h"
// TODO: Some cards (Justin's GeForce 7x series) barf on the integer format causing
// filtering artifacts. These can (sometimes) be resolved by switching the format
// to FP16 instead of Int16.
const GFXFormat LightShadowMap::ShadowMapFormat = GFXFormatR32F; // GFXFormatR8G8B8A8;
bool LightShadowMap::smDebugRenderFrustums;
F32 LightShadowMap::smShadowTexScalar = 1.0f;
Vector<LightShadowMap*> LightShadowMap::smUsedShadowMaps;
Vector<LightShadowMap*> LightShadowMap::smShadowMaps;
GFX_ImplementTextureProfile( ShadowMapProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::PreserveSize |
GFXTextureProfile::RenderTarget |
GFXTextureProfile::Pooled,
GFXTextureProfile::None );
GFX_ImplementTextureProfile( ShadowMapZProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::PreserveSize |
GFXTextureProfile::NoMipmap |
GFXTextureProfile::ZTarget |
GFXTextureProfile::Pooled,
GFXTextureProfile::None );
LightShadowMap::LightShadowMap( LightInfo *light )
: mWorldToLightProj( true ),
mLight( light ),
mTexSize( 0 ),
mLastShader( NULL ),
mLastUpdate( 0 ),
mLastCull( 0 ),
mIsViewDependent( false ),
mVizQuery( NULL ),
mWasOccluded( false ),
mLastScreenSize( 0.0f ),
mLastPriority( 0.0f )
{
GFXTextureManager::addEventDelegate( this, &LightShadowMap::_onTextureEvent );
mTarget = GFX->allocRenderToTextureTarget();
mVizQuery = GFX->createOcclusionQuery();
smShadowMaps.push_back( this );
}
LightShadowMap::~LightShadowMap()
{
mTarget = NULL;
SAFE_DELETE( mVizQuery );
releaseTextures();
smShadowMaps.remove( this );
smUsedShadowMaps.remove( this );
GFXTextureManager::removeEventDelegate( this, &LightShadowMap::_onTextureEvent );
}
void LightShadowMap::releaseAllTextures()
{
PROFILE_SCOPE( LightShadowMap_ReleaseAllTextures );
for ( U32 i=0; i < smShadowMaps.size(); i++ )
smShadowMaps[i]->releaseTextures();
}
U32 LightShadowMap::releaseUnusedTextures()
{
PROFILE_SCOPE( LightShadowMap_ReleaseUnusedTextures );
const U32 currTime = Sim::getCurrentTime();
const U32 purgeTime = 1000;
for ( U32 i=0; i < smUsedShadowMaps.size(); )
{
LightShadowMap *lsm = smUsedShadowMaps[i];
// If the shadow has not been culled in a while then
// release its textures for other shadows to use.
if ( currTime > ( lsm->mLastCull + purgeTime ) )
{
// Internally this will remove the map from the used
// list, so don't increment the loop.
lsm->releaseTextures();
continue;
}
i++;
}
return smUsedShadowMaps.size();
}
void LightShadowMap::_onTextureEvent( GFXTexCallbackCode code )
{
if ( code == GFXZombify )
releaseTextures();
// We don't initialize here as we want the textures
// to be reallocated when the shadow becomes visible.
}
void LightShadowMap::calcLightMatrices( MatrixF &outLightMatrix, const Frustum &viewFrustum )
{
// Create light matrix, set projection
switch ( mLight->getType() )
{
case LightInfo::Vector :
{
const ShadowMapParams *p = mLight->getExtended<ShadowMapParams>();
// Calculate the bonding box of the shadowed area
// we're interested in... this is the shadow box
// transformed by the frustum transform.
Box3F viewBB( -p->shadowDistance, -p->shadowDistance, -p->shadowDistance,
p->shadowDistance, p->shadowDistance, p->shadowDistance );
viewFrustum.getTransform().mul( viewBB );
// Calculate a light "projection" matrix.
MatrixF lightMatrix = MathUtils::createOrientFromDir(mLight->getDirection());
outLightMatrix = lightMatrix;
static MatrixF rotMat(EulerF( (M_PI_F / 2.0f), 0.0f, 0.0f));
lightMatrix.mul( rotMat );
// This is the box in lightspace
Box3F lightViewBB(viewBB);
lightMatrix.mul(lightViewBB);
// Now, let's position our light based on the lightViewBB
Point3F newLightPos(viewBB.getCenter());
F32 sceneDepth = lightViewBB.maxExtents.z - lightViewBB.minExtents.z;
newLightPos += mLight->getDirection() * ((-sceneDepth / 2.0f)-1.0f); // -1 for the nearplane
outLightMatrix.setPosition(newLightPos);
// Update light info
mLight->setRange( sceneDepth );
mLight->setPosition( newLightPos );
// Set our ortho projection
F32 width = (lightViewBB.maxExtents.x - lightViewBB.minExtents.x) / 2.0f;
F32 height = (lightViewBB.maxExtents.y - lightViewBB.minExtents.y) / 2.0f;
width = getMax(width, height);
GFX->setOrtho(-width, width, -width, width, 1.0f, sceneDepth, true);
// TODO: Width * 2... really isn't that pixels being used as
// meters? Is a real physical metric of scene depth better?
//SceneManager::setVisibleDistance(width * 2.0f);
#if 0
DebugDrawer::get()->drawFrustum(viewFrustum, ColorF(1.0f, 0.0f, 0.0f));
DebugDrawer::get()->drawBox(viewBB.minExtents, viewBB.maxExtents, ColorF(0.0f, 1.0f, 0.0f));
DebugDrawer::get()->drawBox(lightViewBB.minExtents, lightViewBB.maxExtents, ColorF(0.0f, 0.0f, 1.0f));
DebugDrawer::get()->drawBox(newLightPos - Point3F(1,1,1), newLightPos + Point3F(1,1,1), ColorF(1,1,0));
DebugDrawer::get()->drawLine(newLightPos, newLightPos + mLight.mDirection*3.0f, ColorF(0,1,1));
Point3F a(newLightPos);
Point3F b(newLightPos);
Point3F offset(width, height,0.0f);
a -= offset;
b += offset;
DebugDrawer::get()->drawBox(a, b, ColorF(0.5f, 0.5f, 0.5f));
#endif
}
break;
case LightInfo::Spot :
{
outLightMatrix = mLight->getTransform();
F32 fov = mDegToRad( mLight->getOuterConeAngle() );
F32 farDist = mLight->getRange().x;
F32 nearDist = farDist * 0.01f;
F32 left, right, top, bottom;
MathUtils::makeFrustum( &left, &right, &top, &bottom, fov, 1.0f, nearDist );
GFX->setFrustum( left, right, bottom, top, nearDist, farDist );
}
break;
default:
AssertFatal(false, "Unsupported light type!");
}
}
void LightShadowMap::releaseTextures()
{
mShadowMapTex = NULL;
mDebugTarget.setTexture( NULL );
mLastUpdate = 0;
smUsedShadowMaps.remove( this );
}
void LightShadowMap::setDebugTarget( const String &name )
{
mDebugTarget.registerWithName( name );
mDebugTarget.setTexture( mShadowMapTex );
}
GFXTextureObject* LightShadowMap::_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, &ShadowMapZProfile,
"LightShadowMap::_getDepthTarget()" );
return depthTex;
}
bool LightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc )
{
if ( currTexFlag == Material::DynamicLight )
{
S32 reg = lsc->mShadowMapSC->getSamplerRegister();
if ( reg != -1 )
GFX->setTexture( reg, mShadowMapTex);
return true;
}
else if ( currTexFlag == Material::DynamicLightMask )
{
S32 reg = lsc->mCookieMapSC->getSamplerRegister();
if ( reg != -1 )
{
ShadowMapParams *p = mLight->getExtended<ShadowMapParams>();
if ( lsc->mCookieMapSC->getType() == GFXSCT_SamplerCube )
GFX->setCubeTexture( reg, p->getCookieCubeTex() );
else
GFX->setTexture( reg, p->getCookieTex() );
}
return true;
}
return false;
}
void LightShadowMap::render( RenderPassManager* renderPass,
const SceneRenderState *diffuseState )
{
mDebugTarget.setTexture( NULL );
_render( renderPass, diffuseState );
mDebugTarget.setTexture( mShadowMapTex );
// Add it to the used list unless we're been updated.
if ( !mLastUpdate )
{
AssertFatal( !smUsedShadowMaps.contains( this ), "LightShadowMap::render - Used shadow map inserted twice!" );
smUsedShadowMaps.push_back( this );
}
mLastUpdate = Sim::getCurrentTime();
}
void LightShadowMap::preLightRender()
{
PROFILE_SCOPE( LightShadowMap_prepLightRender );
if ( mVizQuery )
{
mWasOccluded = mVizQuery->getStatus( true ) == GFXOcclusionQuery::Occluded;
mVizQuery->begin();
}
}
void LightShadowMap::postLightRender()
{
if ( mVizQuery )
mVizQuery->end();
}
BaseMatInstance* LightShadowMap::getShadowMaterial( BaseMatInstance *inMat ) const
{
// 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( getShadowType() );
}
U32 LightShadowMap::getBestTexSize( U32 scale ) const
{
const ShadowMapParams *params = mLight->getExtended<ShadowMapParams>();
// The view dependent shadows don't scale by screen size.
U32 texSize;
if ( isViewDependent() )
texSize = params->texSize;
else
texSize = params->texSize * getMin( 1.0f, mLastScreenSize );
// Apply the shadow texture scale and make
// sure this is a power of 2.
texSize = getNextPow2( texSize * smShadowTexScalar );
// Get the max texture size this card supports and
// scale it down... ensuring the final texSize can
// be scaled up that many times and not go over
// the card maximum.
U32 maxTexSize = GFX->getCardProfiler()->queryProfile( "maxTextureSize", 2048 );
if ( scale > 1 )
maxTexSize >>= ( scale - 1 );
// Never let the shadow texture get smaller than 16x16 as
// it just makes the pool bigger and the fillrate savings
// are less and leass as we get smaller.
texSize = mClamp( texSize, (U32)16, maxTexSize );
// Return it.
return texSize;
}
void LightShadowMap::updatePriority( const SceneRenderState *state, U32 currTimeMs )
{
PROFILE_SCOPE( LightShadowMap_updatePriority );
mLastCull = currTimeMs;
if ( isViewDependent() )
{
mLastScreenSize = 1.0f;
mLastPriority = F32_MAX;
return;
}
U32 timeSinceLastUpdate = currTimeMs - mLastUpdate;
const Point3F &camPt = state->getCameraPosition();
F32 range = mLight->getRange().x;
F32 dist;
if ( mLight->getType() == LightInfo::Spot )
{
// We treat the cone as a cylinder to get the
// approximate projection distance.
Point3F endPt = mLight->getPosition() + ( mLight->getDirection() * range );
Point3F nearPt = MathUtils::mClosestPointOnSegment( mLight->getPosition(), endPt, camPt );
dist = ( camPt - nearPt ).len();
F32 radius = range * mSin( mDegToRad( mLight->getOuterConeAngle() * 0.5f ) );
dist -= radius;
}
else
dist = SphereF( mLight->getPosition(), range ).distanceTo( camPt );
// Get the approximate screen size of the light.
mLastScreenSize = state->projectRadius( dist, range );
mLastScreenSize /= state->getViewport().extent.y;
// Update the priority.
mLastPriority = mPow( mLastScreenSize * 50.0f, 2.0f );
mLastPriority += timeSinceLastUpdate;
mLastPriority *= mLight->getPriority();
}
S32 QSORT_CALLBACK LightShadowMap::cmpPriority( LightShadowMap *const *lsm1, LightShadowMap *const *lsm2 )
{
F32 diff = (*lsm1)->getLastPriority() - (*lsm2)->getLastPriority();
return diff > 0.0f ? -1 : ( diff < 0.0f ? 1 : 0 );
}
void LightShadowMap::_debugRender( SceneRenderState* shadowRenderState )
{
#ifdef TORQUE_DEBUG
// Skip if light does not have debug rendering enabled.
if( !getLightInfo()->isDebugRenderingEnabled() )
return;
DebugDrawer* drawer = DebugDrawer::get();
if( !drawer )
return;
if( smDebugRenderFrustums )
shadowRenderState->getCullingState().debugRenderCullingVolumes();
#endif
}
LightingShaderConstants::LightingShaderConstants()
: mInit( false ),
mShader( NULL ),
mLightParamsSC(NULL),
mLightSpotParamsSC(NULL),
mLightPositionSC(NULL),
mLightDiffuseSC(NULL),
mLightAmbientSC(NULL),
mLightInvRadiusSqSC(NULL),
mLightSpotDirSC(NULL),
mLightSpotAngleSC(NULL),
mLightSpotFalloffSC(NULL),
mShadowMapSC(NULL),
mShadowMapSizeSC(NULL),
mCookieMapSC(NULL),
mRandomDirsConst(NULL),
mShadowSoftnessConst(NULL),
mWorldToLightProjSC(NULL),
mViewToLightProjSC(NULL),
mScaleXSC(NULL),
mScaleYSC(NULL),
mOffsetXSC(NULL),
mOffsetYSC(NULL),
mAtlasXOffsetSC(NULL),
mAtlasYOffsetSC(NULL),
mAtlasScaleSC(NULL),
mFadeStartLength(NULL),
mFarPlaneScalePSSM(NULL),
mOverDarkFactorPSSM(NULL),
mTapRotationTexSC(NULL)
{
}
LightingShaderConstants::~LightingShaderConstants()
{
if (mShader.isValid())
{
mShader->getReloadSignal().remove( this, &LightingShaderConstants::_onShaderReload );
mShader = NULL;
}
}
void LightingShaderConstants::init(GFXShader* shader)
{
if (mShader.getPointer() != shader)
{
if (mShader.isValid())
mShader->getReloadSignal().remove( this, &LightingShaderConstants::_onShaderReload );
mShader = shader;
mShader->getReloadSignal().notify( this, &LightingShaderConstants::_onShaderReload );
}
mLightParamsSC = shader->getShaderConstHandle("$lightParams");
mLightSpotParamsSC = shader->getShaderConstHandle("$lightSpotParams");
// NOTE: These are the shader constants used for doing lighting
// during the forward pass. Do not confuse these for the prepass
// lighting constants which are used from AdvancedLightBinManager.
mLightPositionSC = shader->getShaderConstHandle( ShaderGenVars::lightPosition );
mLightDiffuseSC = shader->getShaderConstHandle( ShaderGenVars::lightDiffuse );
mLightAmbientSC = shader->getShaderConstHandle( ShaderGenVars::lightAmbient );
mLightInvRadiusSqSC = shader->getShaderConstHandle( ShaderGenVars::lightInvRadiusSq );
mLightSpotDirSC = shader->getShaderConstHandle( ShaderGenVars::lightSpotDir );
mLightSpotAngleSC = shader->getShaderConstHandle( ShaderGenVars::lightSpotAngle );
mLightSpotFalloffSC = shader->getShaderConstHandle( ShaderGenVars::lightSpotFalloff );
mShadowMapSC = shader->getShaderConstHandle("$shadowMap");
mShadowMapSizeSC = shader->getShaderConstHandle("$shadowMapSize");
mCookieMapSC = shader->getShaderConstHandle("$cookieMap");
mShadowSoftnessConst = shader->getShaderConstHandle("$shadowSoftness");
mWorldToLightProjSC = shader->getShaderConstHandle("$worldToLightProj");
mViewToLightProjSC = shader->getShaderConstHandle("$viewToLightProj");
mScaleXSC = shader->getShaderConstHandle("$scaleX");
mScaleYSC = shader->getShaderConstHandle("$scaleY");
mOffsetXSC = shader->getShaderConstHandle("$offsetX");
mOffsetYSC = shader->getShaderConstHandle("$offsetY");
mAtlasXOffsetSC = shader->getShaderConstHandle("$atlasXOffset");
mAtlasYOffsetSC = shader->getShaderConstHandle("$atlasYOffset");
mAtlasScaleSC = shader->getShaderConstHandle("$atlasScale");
mFadeStartLength = shader->getShaderConstHandle("$fadeStartLength");
mFarPlaneScalePSSM = shader->getShaderConstHandle("$farPlaneScalePSSM");
mOverDarkFactorPSSM = shader->getShaderConstHandle("$overDarkPSSM");
mTapRotationTexSC = shader->getShaderConstHandle( "$gTapRotationTex" );
mInit = true;
}
void LightingShaderConstants::_onShaderReload()
{
if (mShader.isValid())
init( mShader );
}
const LightInfoExType ShadowMapParams::Type( "ShadowMapParams" );
ShadowMapParams::ShadowMapParams( LightInfo *light )
: mLight( light ),
mShadowMap( NULL )
{
attenuationRatio.set( 0.0f, 1.0f, 1.0f );
shadowType = ShadowType_Spot;
overDarkFactor.set(2000.0f, 1000.0f, 500.0f, 100.0f);
numSplits = 4;
logWeight = 0.91f;
texSize = 512;
shadowDistance = 400.0f;
shadowSoftness = 0.15f;
fadeStartDist = 0.0f;
lastSplitTerrainOnly = false;
_validate();
}
ShadowMapParams::~ShadowMapParams()
{
SAFE_DELETE( mShadowMap );
}
void ShadowMapParams::_validate()
{
switch ( mLight->getType() )
{
case LightInfo::Spot:
shadowType = ShadowType_Spot;
break;
case LightInfo::Vector:
shadowType = ShadowType_PSSM;
break;
case LightInfo::Point:
if ( shadowType < ShadowType_Paraboloid )
shadowType = ShadowType_DualParaboloidSinglePass;
break;
default:
break;
}
// The texture sizes for shadows should always
// be power of 2 in size.
texSize = getNextPow2( texSize );
// The maximum shadow texture size setting we're
// gonna allow... this doesn't use your hardware
// settings as you may be on a lower end system
// than your target machine.
//
// We apply the hardware specific limits during
// shadow rendering.
//
U32 maxTexSize = 4096;
if ( mLight->getType() == LightInfo::Vector )
{
numSplits = mClamp( numSplits, 1, 4 );
// Adjust the shadow texture size for the PSSM
// based on the split count to keep the total
// shadow texture size within 4096.
if ( numSplits == 2 || numSplits == 4 )
maxTexSize = 2048;
if ( numSplits == 3 )
maxTexSize = 1024;
}
else
numSplits = 1;
// Keep it in a valid range... less than 32 is dumb.
texSize = mClamp( texSize, 32, maxTexSize );
}
LightShadowMap* ShadowMapParams::getOrCreateShadowMap()
{
if ( mShadowMap )
return mShadowMap;
if ( !mLight->getCastShadows() )
return NULL;
switch ( mLight->getType() )
{
case LightInfo::Spot:
mShadowMap = new SingleLightShadowMap( mLight );
break;
case LightInfo::Vector:
mShadowMap = new PSSMLightShadowMap( mLight );
break;
case LightInfo::Point:
if ( shadowType == ShadowType_CubeMap )
mShadowMap = new CubeLightShadowMap( mLight );
else if ( shadowType == ShadowType_Paraboloid )
mShadowMap = new ParaboloidLightShadowMap( mLight );
else
mShadowMap = new DualParaboloidLightShadowMap( mLight );
break;
default:
break;
}
return mShadowMap;
}
GFXTextureObject* ShadowMapParams::getCookieTex()
{
if ( cookie.isNotEmpty() &&
( mCookieTex.isNull() ||
cookie != mCookieTex->getPath() ) )
{
mCookieTex.set( cookie,
&GFXDefaultStaticDiffuseProfile,
"ShadowMapParams::getCookieTex()" );
}
else if ( cookie.isEmpty() )
mCookieTex = NULL;
return mCookieTex.getPointer();
}
GFXCubemap* ShadowMapParams::getCookieCubeTex()
{
if ( cookie.isNotEmpty() &&
( mCookieCubeTex.isNull() ||
cookie != mCookieCubeTex->getPath() ) )
{
mCookieCubeTex.set( cookie );
}
else if ( cookie.isEmpty() )
mCookieCubeTex = NULL;
return mCookieCubeTex.getPointer();
}
void ShadowMapParams::set( const LightInfoEx *ex )
{
// TODO: Do we even need this?
}
void ShadowMapParams::packUpdate( BitStream *stream ) const
{
// HACK: We need to work out proper parameter
// validation when any field changes on the light.
((ShadowMapParams*)this)->_validate();
stream->writeInt( shadowType, 8 );
mathWrite( *stream, attenuationRatio );
stream->write( texSize );
stream->write( cookie );
stream->write( numSplits );
stream->write( logWeight );
mathWrite(*stream, overDarkFactor);
stream->write( fadeStartDist );
stream->writeFlag( lastSplitTerrainOnly );
stream->write( shadowDistance );
stream->write( shadowSoftness );
}
void ShadowMapParams::unpackUpdate( BitStream *stream )
{
ShadowType newType = (ShadowType)stream->readInt( 8 );
if ( shadowType != newType )
{
// If the shadow type changes delete the shadow
// map so it can be reallocated on the next render.
shadowType = newType;
SAFE_DELETE( mShadowMap );
}
mathRead( *stream, &attenuationRatio );
stream->read( &texSize );
stream->read( &cookie );
stream->read( &numSplits );
stream->read( &logWeight );
mathRead(*stream, &overDarkFactor);
stream->read( &fadeStartDist );
lastSplitTerrainOnly = stream->readFlag();
stream->read( &shadowDistance );
stream->read( &shadowSoftness );
}

View file

@ -0,0 +1,380 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _LIGHTSHADOWMAP_H_
#define _LIGHTSHADOWMAP_H_
#ifndef _GFXTEXTUREHANDLE_H_
#include "gfx/gfxTextureHandle.h"
#endif
#ifndef _GFXCUBEMAP_H_
#include "gfx/gfxCubemap.h"
#endif
#ifndef _GFXTARGET_H_
#include "gfx/gfxTarget.h"
#endif
#ifndef _LIGHTINFO_H_
#include "lighting/lightInfo.h"
#endif
#ifndef _MATHUTIL_FRUSTUM_H_
#include "math/util/frustum.h"
#endif
#ifndef _MATTEXTURETARGET_H_
#include "materials/matTextureTarget.h"
#endif
#ifndef _SHADOW_COMMON_H_
#include "lighting/shadowMap/shadowCommon.h"
#endif
#ifndef _GFXSHADER_H_
#include "gfx/gfxShader.h"
#endif
class ShadowMapManager;
class SceneManager;
class SceneRenderState;
class BaseMatInstance;
class MaterialParameters;
class SharedShadowMapObjects;
struct SceneData;
class GFXShaderConstBuffer;
class GFXShaderConstHandle;
class GFXShader;
class GFXOcclusionQuery;
class LightManager;
class RenderPassManager;
// Shader constant handle lookup
// This isn't broken up as much as it could be, we're mixing single light constants
// and pssm constants.
struct LightingShaderConstants
{
bool mInit;
GFXShaderRef mShader;
GFXShaderConstHandle* mLightParamsSC;
GFXShaderConstHandle* mLightSpotParamsSC;
// NOTE: These are the shader constants used for doing
// lighting during the forward pass. Do not confuse
// these for the prepass lighting constants which are
// used from AdvancedLightBinManager.
GFXShaderConstHandle *mLightPositionSC;
GFXShaderConstHandle *mLightDiffuseSC;
GFXShaderConstHandle *mLightAmbientSC;
GFXShaderConstHandle *mLightInvRadiusSqSC;
GFXShaderConstHandle *mLightSpotDirSC;
GFXShaderConstHandle *mLightSpotAngleSC;
GFXShaderConstHandle *mLightSpotFalloffSC;
GFXShaderConstHandle* mShadowMapSC;
GFXShaderConstHandle* mShadowMapSizeSC;
GFXShaderConstHandle* mCookieMapSC;
GFXShaderConstHandle* mRandomDirsConst;
GFXShaderConstHandle* mShadowSoftnessConst;
GFXShaderConstHandle* mWorldToLightProjSC;
GFXShaderConstHandle* mViewToLightProjSC;
GFXShaderConstHandle* mScaleXSC;
GFXShaderConstHandle* mScaleYSC;
GFXShaderConstHandle* mOffsetXSC;
GFXShaderConstHandle* mOffsetYSC;
GFXShaderConstHandle* mAtlasXOffsetSC;
GFXShaderConstHandle* mAtlasYOffsetSC;
GFXShaderConstHandle* mAtlasScaleSC;
// fadeStartLength.x = Distance in eye space to start fading shadows
// fadeStartLength.y = 1 / Length of fade
GFXShaderConstHandle* mFadeStartLength;
GFXShaderConstHandle* mFarPlaneScalePSSM;
GFXShaderConstHandle* mOverDarkFactorPSSM;
GFXShaderConstHandle* mTapRotationTexSC;
LightingShaderConstants();
~LightingShaderConstants();
void init(GFXShader* buffer);
void _onShaderReload();
};
typedef Map<GFXShader*, LightingShaderConstants*> LightConstantMap;
/// This represents everything we need to render
/// the shadowmap for one light.
class LightShadowMap
{
public:
const static GFXFormat ShadowMapFormat;
/// Used to scale the shadow texture size for performance tweaking.
static F32 smShadowTexScalar;
/// Whether to render shadow frustums for lights that have debug
/// rendering enabled.
static bool smDebugRenderFrustums;
public:
LightShadowMap( LightInfo *light );
virtual ~LightShadowMap();
void render( RenderPassManager* renderPass,
const SceneRenderState *diffuseState );
U32 getLastUpdate() const { return mLastUpdate; }
//U32 getLastVisible() const { return mLastVisible; }
bool isViewDependent() const { return mIsViewDependent; }
bool wasOccluded() const { return mWasOccluded; }
void preLightRender();
void postLightRender();
void updatePriority( const SceneRenderState *state, U32 currTimeMs );
F32 getLastScreenSize() const { return mLastScreenSize; }
F32 getLastPriority() const { return mLastPriority; }
virtual bool hasShadowTex() const { return mShadowMapTex.isValid(); }
virtual bool setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc );
LightInfo* getLightInfo() { return mLight; }
virtual void setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc) = 0;
U32 getTexSize() const { return mTexSize; }
/// Returns the best texture size based on the user
/// texture size, the last light screen size, and
/// global shadow tweak parameters.
U32 getBestTexSize( U32 scale = 1 ) const;
const MatrixF& getWorldToLightProj() const { return mWorldToLightProj; }
static GFXTextureObject* _getDepthTarget( U32 width, U32 height );
virtual ShadowType getShadowType() const = 0;
// Cleanup texture resources
virtual void releaseTextures();
///
GFXTextureObject* getTexture() const { return mShadowMapTex; }
///
void setDebugTarget( const String &name );
static void releaseAllTextures();
/// Releases any shadow maps that have not been culled
/// in a while and returns the count of the remaing
/// shadow maps in use.
static U32 releaseUnusedTextures();
///
static S32 QSORT_CALLBACK cmpPriority( LightShadowMap *const *lsm1, LightShadowMap *const *lsm2 );
/// Returns the correct shadow material this type of light
/// or NULL if no shadow material is possible.
BaseMatInstance* getShadowMaterial( BaseMatInstance *inMat ) const;
protected:
/// All the shadow maps in the system.
static Vector<LightShadowMap*> smShadowMaps;
/// All the shadow maps that have been recently rendered to.
static Vector<LightShadowMap*> smUsedShadowMaps;
virtual void _render( RenderPassManager* renderPass,
const SceneRenderState *diffuseState ) = 0;
/// If there is a LightDebugInfo attached to the light that owns this map,
/// then update its information from the given render state.
///
/// @note This method only does something in debug builds.
void _debugRender( SceneRenderState* shadowRenderState );
/// Helper for rendering shadow map for debugging.
NamedTexTarget mDebugTarget;
/// If true the shadow is view dependent and cannot
/// be skipped if visible and within active range.
bool mIsViewDependent;
/// The time this shadow was last updated.
U32 mLastUpdate;
/// The time this shadow was last culled and prioritized.
U32 mLastCull;
/// The shadow occlusion query used when the light is
/// rendered to determine if any pixel of it is visible.
GFXOcclusionQuery *mVizQuery;
/// If true the light was occluded by geometry the
/// last frame it was updated.
//the last frame.
bool mWasOccluded;
F32 mLastScreenSize;
F32 mLastPriority;
MatrixF mWorldToLightProj;
GFXTextureTargetRef mTarget;
U32 mTexSize;
GFXTexHandle mShadowMapTex;
// The light we are rendering.
LightInfo *mLight;
// Used for blur
GFXShader* mLastShader;
GFXShaderConstHandle* mBlurBoundaries;
// Calculate view matrices and set proper projection with GFX
void calcLightMatrices( MatrixF& outLightMatrix, const Frustum &viewFrustum );
/// The callback used to get texture events.
/// @see GFXTextureManager::addEventDelegate
void _onTextureEvent( GFXTexCallbackCode code );
};
GFX_DeclareTextureProfile( ShadowMapProfile );
GFX_DeclareTextureProfile( ShadowMapZProfile );
class ShadowMapParams : public LightInfoEx
{
public:
ShadowMapParams( LightInfo *light );
virtual ~ShadowMapParams();
/// The LightInfoEx hook type.
static const LightInfoExType Type;
// LightInfoEx
virtual void set( const LightInfoEx *ex );
virtual const LightInfoExType& getType() const { return Type; }
virtual void packUpdate( BitStream *stream ) const;
virtual void unpackUpdate( BitStream *stream );
LightShadowMap* getShadowMap() const { return mShadowMap; }
LightShadowMap* getOrCreateShadowMap();
bool hasCookieTex() const { return cookie.isNotEmpty(); }
GFXTextureObject* getCookieTex();
GFXCubemap* getCookieCubeTex();
// Validates the parameters after a field is changed.
void _validate();
protected:
void _initShadowMap();
///
LightShadowMap *mShadowMap;
LightInfo *mLight;
GFXTexHandle mCookieTex;
GFXCubemapHandle mCookieCubeTex;
public:
// We're leaving these public for easy access
// for console protected fields.
/// @name Shadow Map
/// @{
///
U32 texSize;
///
FileName cookie;
/// @}
Point3F attenuationRatio;
/// @name Point Lights
/// @{
///
ShadowType shadowType;
/// @}
/// @name Exponential Shadow Map Parameters
/// @{
Point4F overDarkFactor;
/// @}
/// @name Parallel Split Shadow Map
/// @{
///
F32 shadowDistance;
///
F32 shadowSoftness;
/// The number of splits in the shadow map.
U32 numSplits;
///
F32 logWeight;
/// At what distance do we start fading the shadows out completely.
F32 fadeStartDist;
/// This toggles only terrain being visible in the last
/// split of a PSSM shadow map.
bool lastSplitTerrainOnly;
/// @}
};
#endif // _LIGHTSHADOWMAP_H_

View file

@ -0,0 +1,141 @@
//-----------------------------------------------------------------------------
// 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/shadowMap/paraboloidLightShadowMap.h"
#include "lighting/common/lightMapParams.h"
#include "lighting/shadowMap/shadowMapManager.h"
#include "math/mathUtils.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
//#include "scene/sceneReflectPass.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxTransformSaver.h"
#include "gfx/util/gfxFrustumSaver.h"
#include "renderInstance/renderPassManager.h"
#include "materials/materialDefinition.h"
#include "gui/controls/guiBitmapCtrl.h"
ParaboloidLightShadowMap::ParaboloidLightShadowMap( LightInfo *light )
: Parent( light ),
mShadowMapScale( 1, 1 ),
mShadowMapOffset( 0, 0 )
{
}
ParaboloidLightShadowMap::~ParaboloidLightShadowMap()
{
releaseTextures();
}
ShadowType ParaboloidLightShadowMap::getShadowType() const
{
const ShadowMapParams *params = mLight->getExtended<ShadowMapParams>();
return params->shadowType;
}
void ParaboloidLightShadowMap::setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc)
{
if ( lsc->mTapRotationTexSC->isValid() )
GFX->setTexture( lsc->mTapRotationTexSC->getSamplerRegister(),
SHADOWMGR->getTapRotationTex() );
ShadowMapParams *p = mLight->getExtended<ShadowMapParams>();
if ( lsc->mLightParamsSC->isValid() )
{
Point4F lightParams( mLight->getRange().x, p->overDarkFactor.x, 0.0f, 0.0f);
params->set( lsc->mLightParamsSC, lightParams );
}
// Atlasing parameters (only used in the dual case, set here to use same shaders)
params->setSafe( lsc->mAtlasScaleSC, mShadowMapScale );
params->setSafe( lsc->mAtlasXOffsetSC, mShadowMapOffset );
// The softness is a factor of the texel size.
params->setSafe( lsc->mShadowSoftnessConst, p->shadowSoftness * ( 1.0f / mTexSize ) );
}
void ParaboloidLightShadowMap::_render( RenderPassManager* renderPass,
const SceneRenderState *diffuseState )
{
PROFILE_SCOPE(ParaboloidLightShadowMap_render);
const LightMapParams *lmParams = mLight->getExtended<LightMapParams>();
const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true;
const U32 texSize = getBestTexSize();
if ( mShadowMapTex.isNull() ||
mTexSize != texSize )
{
mTexSize = texSize;
mShadowMapTex.set( mTexSize, mTexSize,
ShadowMapFormat, &ShadowMapProfile,
"ParaboloidLightShadowMap" );
}
GFXFrustumSaver frustSaver;
GFXTransformSaver saver;
// Render the shadowmap!
GFX->pushActiveRenderTarget();
// Calc matrix and set up visible distance
mWorldToLightProj = mLight->getTransform();
mWorldToLightProj.inverse();
GFX->setWorldMatrix(mWorldToLightProj);
const F32 &lightRadius = mLight->getRange().x;
GFX->setOrtho(-lightRadius, lightRadius, -lightRadius, lightRadius, 1.0f, lightRadius, true);
// Set up target
mTarget->attachTexture( GFXTextureTarget::Color0, mShadowMapTex );
mTarget->attachTexture( GFXTextureTarget::DepthStencil,
_getDepthTarget( mShadowMapTex->getWidth(), mShadowMapTex->getHeight() ) );
GFX->setActiveRenderTarget(mTarget);
GFX->clear(GFXClearTarget | GFXClearStencil | GFXClearZBuffer, ColorI(255,255,255,255), 1.0f, 0);
// Create scene state, prep it
SceneManager* sceneManager = diffuseState->getSceneManager();
SceneRenderState shadowRenderState
(
sceneManager,
SPT_Shadow,
SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ),
renderPass
);
shadowRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial );
shadowRenderState.renderNonLightmappedMeshes( true );
shadowRenderState.renderLightmappedMeshes( bUseLightmappedGeometry );
shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
sceneManager->renderSceneNoLights( &shadowRenderState, SHADOW_TYPEMASK );
_debugRender( &shadowRenderState );
mTarget->resolve();
GFX->popActiveRenderTarget();
}

View file

@ -0,0 +1,48 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _PARABOLOIDLIGHTSHADOWMAP_H_
#define _PARABOLOIDLIGHTSHADOWMAP_H_
#ifndef _LIGHTSHADOWMAP_H_
#include "lighting/shadowMap/lightShadowMap.h"
#endif
class ParaboloidLightShadowMap : public LightShadowMap
{
typedef LightShadowMap Parent;
public:
ParaboloidLightShadowMap( LightInfo *light );
~ParaboloidLightShadowMap();
// LightShadowMap
virtual ShadowType getShadowType() const;
virtual void _render( RenderPassManager* renderPass, const SceneRenderState *diffuseState );
virtual void setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc);
protected:
Point2F mShadowMapScale;
Point2F mShadowMapOffset;
};
#endif

View file

@ -0,0 +1,459 @@
//-----------------------------------------------------------------------------
// 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/shadowMap/pssmLightShadowMap.h"
#include "lighting/common/lightMapParams.h"
#include "console/console.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
#include "lighting/lightManager.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxTransformSaver.h"
#include "gfx/util/gfxFrustumSaver.h"
#include "renderInstance/renderPassManager.h"
#include "gui/controls/guiBitmapCtrl.h"
#include "lighting/shadowMap/shadowMapManager.h"
#include "materials/shaderData.h"
#include "ts/tsShapeInstance.h"
#include "console/consoleTypes.h"
AFTER_MODULE_INIT( Sim )
{
Con::addVariable( "$pref::PSSM::detailAdjustScale",
TypeF32, &PSSMLightShadowMap::smDetailAdjustScale,
"@brief Scales the model LOD when rendering into the PSSM shadow.\n"
"Use this to reduce the draw calls when rendering the shadow by having "
"meshes LOD out nearer to the camera than normal.\n"
"@see $pref::TS::detailAdjust\n"
"@ingroup AdvancedLighting" );
Con::addVariable( "$pref::PSSM::smallestVisiblePixelSize",
TypeF32, &PSSMLightShadowMap::smSmallestVisiblePixelSize,
"@brief The smallest pixel size an object can be and still be rendered into the PSSM shadow.\n"
"Use this to force culling of small objects which contribute little to the final shadow.\n"
"@see $pref::TS::smallestVisiblePixelSize\n"
"@ingroup AdvancedLighting" );
}
F32 PSSMLightShadowMap::smDetailAdjustScale = 0.85f;
F32 PSSMLightShadowMap::smSmallestVisiblePixelSize = 25.0f;
PSSMLightShadowMap::PSSMLightShadowMap( LightInfo *light )
: LightShadowMap( light ),
mNumSplits( 0 )
{
mIsViewDependent = true;
}
void PSSMLightShadowMap::_setNumSplits( U32 numSplits, U32 texSize )
{
AssertFatal( numSplits > 0 && numSplits <= MAX_SPLITS,
"PSSMLightShadowMap::_setNumSplits() - Splits must be between 1 and 4!" );
releaseTextures();
mNumSplits = numSplits;
mTexSize = texSize;
F32 texWidth, texHeight;
// If the split count is less than 4 then do a
// 1xN layout of shadow maps...
if ( mNumSplits < 4 )
{
texHeight = texSize;
texWidth = texSize * mNumSplits;
for ( U32 i = 0; i < 4; i++ )
{
mViewports[i].extent.set(texSize, texSize);
mViewports[i].point.set(texSize*i, 0);
}
}
else
{
// ... with 4 splits do a 2x2.
texWidth = texHeight = texSize * 2;
for ( U32 i = 0; i < 4; i++ )
{
F32 xOff = (i == 1 || i == 3) ? 0.5f : 0.0f;
F32 yOff = (i > 1) ? 0.5f : 0.0f;
mViewports[i].extent.set( texSize, texSize );
mViewports[i].point.set( xOff * texWidth, yOff * texHeight );
}
}
mShadowMapTex.set( texWidth, texHeight,
ShadowMapFormat, &ShadowMapProfile,
"PSSMLightShadowMap" );
}
void PSSMLightShadowMap::_calcSplitPos(const Frustum& currFrustum)
{
const F32 nearDist = 0.01f; // TODO: Should this be adjustable or different?
const F32 farDist = currFrustum.getFarDist();
for ( U32 i = 1; i < mNumSplits; i++ )
{
F32 step = (F32) i / (F32) mNumSplits;
F32 logSplit = nearDist * mPow(farDist / nearDist, step);
F32 linearSplit = nearDist + (farDist - nearDist) * step;
mSplitDist[i] = mLerp( linearSplit, logSplit, mClampF( mLogWeight, 0.0f, 1.0f ) );
}
mSplitDist[0] = nearDist;
mSplitDist[mNumSplits] = farDist;
}
Box3F PSSMLightShadowMap::_calcClipSpaceAABB(const Frustum& f, const MatrixF& transform, F32 farDist)
{
// Calculate frustum center
Point3F center(0,0,0);
for (U32 i = 0; i < 8; i++)
{
const Point3F& pt = f.getPoints()[i];
center += pt;
}
center /= 8;
// Calculate frustum bounding sphere radius
F32 radius = 0.0f;
for (U32 i = 0; i < 8; i++)
radius = getMax(radius, (f.getPoints()[i] - center).lenSquared());
radius = mFloor( mSqrt(radius) );
// Now build box for sphere
Box3F result;
Point3F radiusBox(radius, radius, radius);
result.minExtents = center - radiusBox;
result.maxExtents = center + radiusBox;
// Transform to light projection space
transform.mul(result);
return result;
}
// This "rounds" the projection matrix to remove subtexel movement during shadow map
// rasterization. This is here to reduce shadow shimmering.
void PSSMLightShadowMap::_roundProjection(const MatrixF& lightMat, const MatrixF& cropMatrix, Point3F &offset, U32 splitNum)
{
// Round to the nearest shadowmap texel, this helps reduce shimmering
MatrixF currentProj = GFX->getProjectionMatrix();
currentProj = cropMatrix * currentProj * lightMat;
// Project origin to screen.
Point4F originShadow4F(0,0,0,1);
currentProj.mul(originShadow4F);
Point2F originShadow(originShadow4F.x / originShadow4F.w, originShadow4F.y / originShadow4F.w);
// Convert to texture space (0..shadowMapSize)
F32 t = mNumSplits < 4 ? mShadowMapTex->getWidth() / mNumSplits : mShadowMapTex->getWidth() / 2;
Point2F texelsToTexture(t / 2.0f, mShadowMapTex->getHeight() / 2.0f);
if (mNumSplits >= 4) texelsToTexture.y *= 0.5f;
originShadow.convolve(texelsToTexture);
// Clamp to texel boundary
Point2F originRounded;
originRounded.x = mFloor(originShadow.x + 0.5f);
originRounded.y = mFloor(originShadow.y + 0.5f);
// Subtract origin to get an offset to recenter everything on texel boundaries
originRounded -= originShadow;
// Convert back to texels (0..1) and offset
originRounded.convolveInverse(texelsToTexture);
offset.x += originRounded.x;
offset.y += originRounded.y;
}
void PSSMLightShadowMap::_render( RenderPassManager* renderPass,
const SceneRenderState *diffuseState )
{
PROFILE_SCOPE(PSSMLightShadowMap_render);
const ShadowMapParams *params = mLight->getExtended<ShadowMapParams>();
const LightMapParams *lmParams = mLight->getExtended<LightMapParams>();
const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true;
const U32 texSize = getBestTexSize( params->numSplits < 4 ? params->numSplits : 2 );
if ( mShadowMapTex.isNull() ||
mNumSplits != params->numSplits ||
mTexSize != texSize )
_setNumSplits( params->numSplits, texSize );
mLogWeight = params->logWeight;
Frustum fullFrustum( diffuseState->getFrustum() );
fullFrustum.cropNearFar(fullFrustum.getNearDist(), params->shadowDistance);
GFXFrustumSaver frustSaver;
GFXTransformSaver saver;
// Set our render target
GFX->pushActiveRenderTarget();
mTarget->attachTexture( GFXTextureTarget::Color0, mShadowMapTex );
mTarget->attachTexture( GFXTextureTarget::DepthStencil,
_getDepthTarget( mShadowMapTex->getWidth(), mShadowMapTex->getHeight() ) );
GFX->setActiveRenderTarget( mTarget );
GFX->clear( GFXClearStencil | GFXClearZBuffer | GFXClearTarget, ColorI(255,255,255), 1.0f, 0 );
// Calculate our standard light matrices
MatrixF lightMatrix;
calcLightMatrices( lightMatrix, diffuseState->getFrustum() );
lightMatrix.inverse();
MatrixF lightViewProj = GFX->getProjectionMatrix() * lightMatrix;
// TODO: This is just retrieving the near and far calculated
// in calcLightMatrices... we should make that clear.
F32 pnear, pfar;
GFX->getFrustum( NULL, NULL, NULL, NULL, &pnear, &pfar, NULL );
// Set our view up
GFX->setWorldMatrix(lightMatrix);
MatrixF toLightSpace = lightMatrix; // * invCurrentView;
_calcSplitPos(fullFrustum);
mWorldToLightProj = GFX->getProjectionMatrix() * toLightSpace;
// Apply the PSSM
const F32 savedSmallestVisible = TSShapeInstance::smSmallestVisiblePixelSize;
const F32 savedDetailAdjust = TSShapeInstance::smDetailAdjust;
TSShapeInstance::smDetailAdjust *= smDetailAdjustScale;
TSShapeInstance::smSmallestVisiblePixelSize = smSmallestVisiblePixelSize;
for (U32 i = 0; i < mNumSplits; i++)
{
GFXTransformSaver saver;
// Calculate a sub-frustum
Frustum subFrustum(fullFrustum);
subFrustum.cropNearFar(mSplitDist[i], mSplitDist[i+1]);
// Calculate our AABB in the light's clip space.
Box3F clipAABB = _calcClipSpaceAABB(subFrustum, lightViewProj, fullFrustum.getFarDist());
// Calculate our crop matrix
Point3F scale(2.0f / (clipAABB.maxExtents.x - clipAABB.minExtents.x),
2.0f / (clipAABB.maxExtents.y - clipAABB.minExtents.y),
1.0f);
// TODO: This seems to produce less "pops" of the
// shadow resolution as the camera spins around and
// it should produce pixels that are closer to being
// square.
//
// Still is it the right thing to do?
//
scale.y = scale.x = ( getMin( scale.x, scale.y ) );
//scale.x = mFloor(scale.x);
//scale.y = mFloor(scale.y);
Point3F offset( -0.5f * (clipAABB.maxExtents.x + clipAABB.minExtents.x) * scale.x,
-0.5f * (clipAABB.maxExtents.y + clipAABB.minExtents.y) * scale.y,
0.0f );
MatrixF cropMatrix(true);
cropMatrix.scale(scale);
cropMatrix.setPosition(offset);
_roundProjection(lightMatrix, cropMatrix, offset, i);
cropMatrix.setPosition(offset);
// Save scale/offset for shader computations
mScaleProj[i].set(scale);
mOffsetProj[i].set(offset);
// Adjust the far plane to the max z we got (maybe add a little to deal with split overlap)
bool isOrtho;
{
F32 left, right, bottom, top, nearDist, farDist;
GFX->getFrustum(&left, &right, &bottom, &top, &nearDist, &farDist,&isOrtho);
// BTRTODO: Fix me!
farDist = clipAABB.maxExtents.z;
if (!isOrtho)
GFX->setFrustum(left, right, bottom, top, nearDist, farDist);
else
{
// Calculate a new far plane, add a fudge factor to avoid bringing
// the far plane in too close.
F32 newFar = pfar * clipAABB.maxExtents.z + 1.0f;
mFarPlaneScalePSSM[i] = (pfar - pnear) / (newFar - pnear);
GFX->setOrtho(left, right, bottom, top, pnear, newFar, true);
}
}
// Crop matrix multiply needs to be post-projection.
MatrixF alightProj = GFX->getProjectionMatrix();
alightProj = cropMatrix * alightProj;
// Set our new projection
GFX->setProjectionMatrix(alightProj);
// Render into the quad of the shadow map we are using.
GFX->setViewport(mViewports[i]);
SceneManager* sceneManager = diffuseState->getSceneManager();
// The frustum is currently the full size and has not had
// cropping applied.
//
// We make that adjustment here.
const Frustum& uncroppedFrustum = GFX->getFrustum();
Frustum croppedFrustum;
scale *= 0.5f;
croppedFrustum.set(
isOrtho,
uncroppedFrustum.getNearLeft() / scale.x,
uncroppedFrustum.getNearRight() / scale.x,
uncroppedFrustum.getNearTop() / scale.y,
uncroppedFrustum.getNearBottom() / scale.y,
uncroppedFrustum.getNearDist(),
uncroppedFrustum.getFarDist(),
uncroppedFrustum.getTransform()
);
MatrixF camera = GFX->getWorldMatrix();
camera.inverse();
croppedFrustum.setTransform( camera );
// Setup the scene state and use the diffuse state
// camera position and screen metrics values so that
// lod is done the same as in the diffuse pass.
SceneRenderState shadowRenderState
(
sceneManager,
SPT_Shadow,
SceneCameraState( diffuseState->getViewport(), croppedFrustum,
GFX->getWorldMatrix(), GFX->getProjectionMatrix() ),
renderPass
);
shadowRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial );
shadowRenderState.renderNonLightmappedMeshes( true );
shadowRenderState.renderLightmappedMeshes( bUseLightmappedGeometry );
shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
U32 objectMask = SHADOW_TYPEMASK;
if ( i == mNumSplits-1 && params->lastSplitTerrainOnly )
objectMask = TerrainObjectType;
sceneManager->renderSceneNoLights( &shadowRenderState, objectMask );
_debugRender( &shadowRenderState );
}
// Restore the original TS lod settings.
TSShapeInstance::smSmallestVisiblePixelSize = savedSmallestVisible;
TSShapeInstance::smDetailAdjust = savedDetailAdjust;
// Release our render target
mTarget->resolve();
GFX->popActiveRenderTarget();
}
void PSSMLightShadowMap::setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc)
{
PROFILE_SCOPE( PSSMLightShadowMap_setShaderParameters );
if ( lsc->mTapRotationTexSC->isValid() )
GFX->setTexture( lsc->mTapRotationTexSC->getSamplerRegister(),
SHADOWMGR->getTapRotationTex() );
const ShadowMapParams *p = mLight->getExtended<ShadowMapParams>();
Point4F sx(Point4F::Zero),
sy(Point4F::Zero),
ox(Point4F::Zero),
oy(Point4F::Zero),
aXOff(Point4F::Zero),
aYOff(Point4F::Zero);
for (U32 i = 0; i < mNumSplits; i++)
{
sx[i] = mScaleProj[i].x;
sy[i] = mScaleProj[i].y;
ox[i] = mOffsetProj[i].x;
oy[i] = mOffsetProj[i].y;
}
Point2F shadowMapAtlas;
if (mNumSplits < 4)
{
shadowMapAtlas.x = 1.0f / (F32)mNumSplits;
shadowMapAtlas.y = 1.0f;
// 1xmNumSplits
for (U32 i = 0; i < mNumSplits; i++)
aXOff[i] = (F32)i * shadowMapAtlas.x;
}
else
{
shadowMapAtlas.set(0.5f, 0.5f);
// 2x2
for (U32 i = 0; i < mNumSplits; i++)
{
if (i == 1 || i == 3)
aXOff[i] = 0.5f;
if (i > 1)
aYOff[i] = 0.5f;
}
}
params->setSafe(lsc->mScaleXSC, sx);
params->setSafe(lsc->mScaleYSC, sy);
params->setSafe(lsc->mOffsetXSC, ox);
params->setSafe(lsc->mOffsetYSC, oy);
params->setSafe(lsc->mAtlasXOffsetSC, aXOff);
params->setSafe(lsc->mAtlasYOffsetSC, aYOff);
params->setSafe(lsc->mAtlasScaleSC, shadowMapAtlas);
Point4F lightParams( mLight->getRange().x, p->overDarkFactor.x, 0.0f, 0.0f );
params->setSafe( lsc->mLightParamsSC, lightParams );
params->setSafe( lsc->mFarPlaneScalePSSM, mFarPlaneScalePSSM);
Point2F fadeStartLength(p->fadeStartDist, 0.0f);
if (fadeStartLength.x == 0.0f)
{
// By default, lets fade the last half of the last split.
fadeStartLength.x = (mSplitDist[mNumSplits-1] + mSplitDist[mNumSplits]) / 2.0f;
}
fadeStartLength.y = 1.0f / (mSplitDist[mNumSplits] - fadeStartLength.x);
params->setSafe( lsc->mFadeStartLength, fadeStartLength);
params->setSafe( lsc->mOverDarkFactorPSSM, p->overDarkFactor);
// The softness is a factor of the texel size.
params->setSafe( lsc->mShadowSoftnessConst, p->shadowSoftness * ( 1.0f / mTexSize ) );
}

View file

@ -0,0 +1,71 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _PSSMLIGHTSHADOWMAP_H_
#define _PSSMLIGHTSHADOWMAP_H_
#ifndef _LIGHTSHADOWMAP_H_
#include "lighting/shadowMap/lightShadowMap.h"
#endif
#ifndef _MATHUTIL_FRUSTUM_H_
#include "math/util/frustum.h"
#endif
class PSSMLightShadowMap : public LightShadowMap
{
typedef LightShadowMap Parent;
public:
PSSMLightShadowMap( LightInfo *light );
// LightShadowMap
virtual ShadowType getShadowType() const { return ShadowType_PSSM; }
virtual void _render( RenderPassManager* renderPass, const SceneRenderState *diffuseState );
virtual void setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc);
/// Used to scale TSShapeInstance::smDetailAdjust to have
/// objects lod quicker when in the PSSM shadow.
/// @see TSShapeInstance::smDetailAdjust
static F32 smDetailAdjustScale;
/// Like TSShapeInstance::smSmallestVisiblePixelSize this is used
/// to define the smallest LOD to render.
/// @see TSShapeInstance::smSmallestVisiblePixelSize
static F32 smSmallestVisiblePixelSize;
protected:
void _setNumSplits( U32 numSplits, U32 texSize );
void _calcSplitPos(const Frustum& currFrustum);
Box3F _calcClipSpaceAABB(const Frustum& f, const MatrixF& transform, F32 farDist);
void _roundProjection(const MatrixF& lightMat, const MatrixF& cropMatrix, Point3F &offset, U32 splitNum);
static const int MAX_SPLITS = 4;
U32 mNumSplits;
F32 mSplitDist[MAX_SPLITS+1]; // +1 because we store a cap
RectI mViewports[MAX_SPLITS];
Point3F mScaleProj[MAX_SPLITS];
Point3F mOffsetProj[MAX_SPLITS];
Point4F mFarPlaneScalePSSM;
F32 mLogWeight;
};
#endif

View file

@ -0,0 +1,62 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _SHADOW_COMMON_H_
#define _SHADOW_COMMON_H_
#ifndef _DYNAMIC_CONSOLETYPES_H_
#include "console/dynamicTypes.h"
#endif
///
enum ShadowType
{
ShadowType_None = -1,
ShadowType_Spot,
ShadowType_PSSM,
ShadowType_Paraboloid,
ShadowType_DualParaboloidSinglePass,
ShadowType_DualParaboloid,
ShadowType_CubeMap,
ShadowType_Count,
};
DefineEnumType( ShadowType );
/// The different shadow filter modes used when rendering
/// shadowed lights.
/// @see setShadowFilterMode
enum ShadowFilterMode
{
ShadowFilterMode_None,
ShadowFilterMode_SoftShadow,
ShadowFilterMode_SoftShadowHighQuality
};
DefineEnumType( ShadowFilterMode );
#endif // _SHADOW_COMMON_H_

View file

@ -0,0 +1,192 @@
//-----------------------------------------------------------------------------
// 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/shadowMap/shadowMapManager.h"
#include "lighting/shadowMap/shadowMapPass.h"
#include "lighting/shadowMap/lightShadowMap.h"
#include "materials/materialManager.h"
#include "lighting/lightManager.h"
#include "core/util/safeDelete.h"
#include "scene/sceneRenderState.h"
#include "gfx/gfxTextureManager.h"
#include "core/module.h"
#include "console/consoleTypes.h"
GFX_ImplementTextureProfile(ShadowMapTexProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::PreserveSize | GFXTextureProfile::Dynamic ,
GFXTextureProfile::None);
MODULE_BEGIN( ShadowMapManager )
MODULE_INIT
{
ManagedSingleton< ShadowMapManager >::createSingleton();
}
MODULE_SHUTDOWN
{
ManagedSingleton< ShadowMapManager >::deleteSingleton();
}
MODULE_END;
AFTER_MODULE_INIT( Sim )
{
Con::addVariable( "$pref::Shadows::textureScalar",
TypeF32, &LightShadowMap::smShadowTexScalar,
"@brief Used to scale the shadow texture sizes.\n"
"This can reduce the shadow quality and texture memory overhead or increase them.\n"
"@ingroup AdvancedLighting\n" );
Con::NotifyDelegate callabck( &LightShadowMap::releaseAllTextures );
Con::addVariableNotify( "$pref::Shadows::textureScalar", callabck );
Con::addVariable( "$pref::Shadows::disable",
TypeBool, &ShadowMapPass::smDisableShadowsPref,
"Used to disable all shadow rendering.\n"
"@ingroup AdvancedLighting\n" );
Con::addVariable( "$Shadows::disable",
TypeBool, &ShadowMapPass::smDisableShadowsEditor,
"Used by the editor to disable all shadow rendering.\n"
"@ingroup AdvancedLighting\n" );
Con::NotifyDelegate shadowCallback( &ShadowMapManager::updateShadowDisable );
Con::addVariableNotify( "$pref::Shadows::disable", shadowCallback );
Con::addVariableNotify( "$Shadows::disable", shadowCallback );
}
Signal<void(void)> ShadowMapManager::smShadowDeactivateSignal;
ShadowMapManager::ShadowMapManager()
: mShadowMapPass(NULL),
mCurrentShadowMap(NULL),
mIsActive(false)
{
}
ShadowMapManager::~ShadowMapManager()
{
}
void ShadowMapManager::setLightShadowMapForLight( LightInfo *light )
{
ShadowMapParams *params = light->getExtended<ShadowMapParams>();
if ( params )
mCurrentShadowMap = params->getShadowMap();
else
mCurrentShadowMap = NULL;
}
void ShadowMapManager::activate()
{
ShadowManager::activate();
if (!getSceneManager())
{
Con::errorf("This world has no scene manager! Shadow manager not activating!");
return;
}
mShadowMapPass = new ShadowMapPass(LIGHTMGR, this);
getSceneManager()->getPreRenderSignal().notify( this, &ShadowMapManager::_onPreRender, 0.01f );
GFXTextureManager::addEventDelegate( this, &ShadowMapManager::_onTextureEvent );
mIsActive = true;
}
void ShadowMapManager::deactivate()
{
GFXTextureManager::removeEventDelegate( this, &ShadowMapManager::_onTextureEvent );
getSceneManager()->getPreRenderSignal().remove( this, &ShadowMapManager::_onPreRender );
SAFE_DELETE(mShadowMapPass);
mTapRotationTex = NULL;
// Clean up our shadow texture memory.
LightShadowMap::releaseAllTextures();
TEXMGR->cleanupPool();
mIsActive = false;
ShadowManager::deactivate();
}
void ShadowMapManager::_onPreRender( SceneManager *sg, const SceneRenderState *state )
{
if ( mShadowMapPass && state->isDiffusePass() )
mShadowMapPass->render( sg, state, (U32)-1 );
}
void ShadowMapManager::_onTextureEvent( GFXTexCallbackCode code )
{
if ( code == GFXZombify )
mTapRotationTex = NULL;
}
GFXTextureObject* ShadowMapManager::getTapRotationTex()
{
if ( mTapRotationTex.isValid() )
return mTapRotationTex;
mTapRotationTex.set( 64, 64, GFXFormatR8G8B8A8, &ShadowMapTexProfile,
"ShadowMapManager::getTapRotationTex" );
GFXLockedRect *rect = mTapRotationTex.lock();
U8 *f = rect->bits;
F32 angle;
for( U32 i = 0; i < 64*64; i++, f += 4 )
{
// We only pack the rotations into the red
// and green channels... the rest are empty.
angle = M_2PI_F * gRandGen.randF();
f[0] = U8_MAX * ( ( 1.0f + mSin( angle ) ) * 0.5f );
f[1] = U8_MAX * ( ( 1.0f + mCos( angle ) ) * 0.5f );
f[2] = 0;
f[3] = 0;
}
mTapRotationTex.unlock();
return mTapRotationTex;
}
void ShadowMapManager::updateShadowDisable()
{
bool disable = false;
if ( ShadowMapPass::smDisableShadowsEditor || ShadowMapPass::smDisableShadowsPref )
disable = true;
if ( disable != ShadowMapPass::smDisableShadows)
{
ShadowMapPass::smDisableShadows = disable;
smShadowDeactivateSignal.trigger();
}
}

View file

@ -0,0 +1,108 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _SHADOWMAPMANAGER_H_
#define _SHADOWMAPMANAGER_H_
#ifndef _TSINGLETON_H_
#include "core/util/tSingleton.h"
#endif
#ifndef _SHADOWMANAGER_H_
#include "lighting/shadowManager.h"
#endif
#ifndef _GFXENUMS_H_
#include "gfx/gfxEnums.h"
#endif
#ifndef _GFXTEXTUREHANDLE_H_
#include "gfx/gfxTextureHandle.h"
#endif
#ifndef _MPOINT4_H_
#include "math/mPoint4.h"
#endif
class LightShadowMap;
class ShadowMapPass;
class LightInfo;
class SceneManager;
class SceneRenderState;
class ShadowMapManager : public ShadowManager
{
typedef ShadowManager Parent;
friend class ShadowMapPass;
public:
ShadowMapManager();
virtual ~ShadowMapManager();
/// Sets the current shadowmap (used in setLightInfo/setTextureStage calls)
void setLightShadowMap( LightShadowMap *lm ) { mCurrentShadowMap = lm; }
/// Looks up the shadow map for the light then sets it.
void setLightShadowMapForLight( LightInfo *light );
/// Return the current shadow map
LightShadowMap* getCurrentShadowMap() const { return mCurrentShadowMap; }
ShadowMapPass* getShadowMapPass() const { return mShadowMapPass; }
// Shadow manager
virtual void activate();
virtual void deactivate();
GFXTextureObject* getTapRotationTex();
/// The shadow map deactivation signal.
static Signal<void(void)> smShadowDeactivateSignal;
static void updateShadowDisable();
protected:
void _onTextureEvent( GFXTexCallbackCode code );
void _onPreRender( SceneManager *sg, const SceneRenderState* state );
ShadowMapPass *mShadowMapPass;
LightShadowMap *mCurrentShadowMap;
///
GFXTexHandle mTapRotationTex;
bool mIsActive;
public:
// For ManagedSingleton.
static const char* getSingletonName() { return "ShadowMapManager"; }
};
/// Returns the ShadowMapManager singleton.
#define SHADOWMGR ManagedSingleton<ShadowMapManager>::instance()
GFX_DeclareTextureProfile( ShadowMapTexProfile );
#endif // _SHADOWMAPMANAGER_H_

View file

@ -0,0 +1,250 @@
//-----------------------------------------------------------------------------
// 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/shadowMap/shadowMapPass.h"
#include "lighting/shadowMap/lightShadowMap.h"
#include "lighting/shadowMap/shadowMapManager.h"
#include "lighting/lightManager.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
#include "renderInstance/renderPassManager.h"
#include "renderInstance/renderObjectMgr.h"
#include "renderInstance/renderMeshMgr.h"
#include "renderInstance/renderTerrainMgr.h"
#include "renderInstance/renderImposterMgr.h"
#include "core/util/safeDelete.h"
#include "console/consoleTypes.h"
#include "gfx/gfxTransformSaver.h"
#include "gfx/gfxDebugEvent.h"
#include "platform/platformTimer.h"
const String ShadowMapPass::PassTypeName("ShadowMap");
U32 ShadowMapPass::smActiveShadowMaps = 0;
U32 ShadowMapPass::smUpdatedShadowMaps = 0;
U32 ShadowMapPass::smNearShadowMaps = 0;
U32 ShadowMapPass::smShadowMapsDrawCalls = 0;
U32 ShadowMapPass::smShadowMapPolyCount = 0;
U32 ShadowMapPass::smRenderTargetChanges = 0;
U32 ShadowMapPass::smShadowPoolTexturesCount = 0.;
F32 ShadowMapPass::smShadowPoolMemory = 0.0f;
bool ShadowMapPass::smDisableShadows = false;
bool ShadowMapPass::smDisableShadowsEditor = false;
bool ShadowMapPass::smDisableShadowsPref = false;
/// We have a default 8ms render budget for shadow rendering.
U32 ShadowMapPass::smRenderBudgetMs = 8;
ShadowMapPass::ShadowMapPass(LightManager* lightManager, ShadowMapManager* shadowManager)
{
mLightManager = lightManager;
mShadowManager = shadowManager;
mShadowRPM = new ShadowRenderPassManager();
mShadowRPM->assignName( "ShadowRenderPassManager" );
mShadowRPM->registerObject();
Sim::getRootGroup()->addObject( mShadowRPM );
// Setup our render pass manager
mShadowRPM->addManager( new RenderMeshMgr(RenderPassManager::RIT_Mesh, 0.3f, 0.3f) );
mShadowRPM->addManager( new RenderMeshMgr( RenderPassManager::RIT_Interior, 0.4f, 0.4f ) );
//mShadowRPM->addManager( new RenderObjectMgr() );
mShadowRPM->addManager( new RenderTerrainMgr( 0.5f, 0.5f ) );
mShadowRPM->addManager( new RenderImposterMgr( 0.6f, 0.6f ) );
mActiveLights = 0;
mTimer = PlatformTimer::create();
Con::addVariable( "$ShadowStats::activeMaps", TypeS32, &smActiveShadowMaps,
"The shadow stats showing the active number of shadow maps.\n"
"@ingroup AdvancedLighting\n" );
Con::addVariable( "$ShadowStats::updatedMaps", TypeS32, &smUpdatedShadowMaps,
"The shadow stats showing the number of shadow maps updated this frame.\n"
"@ingroup AdvancedLighting\n" );
Con::addVariable( "$ShadowStats::nearMaps", TypeS32, &smNearShadowMaps,
"The shadow stats showing the number of shadow maps that are close enough to be updated very frame.\n"
"@ingroup AdvancedLighting\n" );
Con::addVariable( "$ShadowStats::drawCalls", TypeS32, &smShadowMapsDrawCalls,
"The shadow stats showing the number of draw calls in shadow map renders for this frame.\n"
"@ingroup AdvancedLighting\n" );
Con::addVariable( "$ShadowStats::polyCount", TypeS32, &smShadowMapPolyCount,
"The shadow stats showing the number of triangles in shadow map renders for this frame.\n"
"@ingroup AdvancedLighting\n" );
Con::addVariable( "$ShadowStats::rtChanges", TypeS32, &smRenderTargetChanges,
"The shadow stats showing the number of render target changes for shadow maps in this frame.\n"
"@ingroup AdvancedLighting\n" );
Con::addVariable( "$ShadowStats::poolTexCount", TypeS32, &smShadowPoolTexturesCount,
"The shadow stats showing the number of shadow textures in the shadow texture pool.\n"
"@ingroup AdvancedLighting\n" );
Con::addVariable( "$ShadowStats::poolTexMemory", TypeF32, &smShadowPoolMemory,
"The shadow stats showing the approximate texture memory usage of the shadow map texture pool.\n"
"@ingroup AdvancedLighting\n" );
}
ShadowMapPass::~ShadowMapPass()
{
SAFE_DELETE( mTimer );
if ( mShadowRPM )
mShadowRPM->deleteObject();
}
void ShadowMapPass::render( SceneManager *sceneManager,
const SceneRenderState *diffuseState,
U32 objectMask )
{
PROFILE_SCOPE( ShadowMapPass_Render );
// Prep some shadow rendering stats.
smActiveShadowMaps = 0;
smUpdatedShadowMaps = 0;
smNearShadowMaps = 0;
GFXDeviceStatistics stats;
stats.start( GFX->getDeviceStatistics() );
// NOTE: The lights were already registered by SceneManager.
// Update mLights
mLights.clear();
mLightManager->getAllUnsortedLights( &mLights );
mActiveLights = mLights.size();
// Use the per-frame incremented time for
// priority updates and to track when the
// shadow was last updated.
const U32 currTime = Sim::getCurrentTime();
// First do a loop thru the lights setting up the shadow
// info array for this pass.
Vector<LightShadowMap*> shadowMaps;
shadowMaps.reserve( mActiveLights );
for ( U32 i = 0; i < mActiveLights; i++ )
{
ShadowMapParams *params = mLights[i]->getExtended<ShadowMapParams>();
// Before we do anything... skip lights without shadows.
if ( !mLights[i]->getCastShadows() || smDisableShadows )
continue;
LightShadowMap *lsm = params->getOrCreateShadowMap();
// First check the visiblity query... if it wasn't
// visible skip it.
if ( lsm->wasOccluded() )
continue;
// Any shadow that is visible is counted as being
// active regardless if we update it or not.
++smActiveShadowMaps;
// Do a priority update for this shadow.
lsm->updatePriority( diffuseState, currTime );
shadowMaps.push_back( lsm );
}
// Now sort the shadow info by priority.
shadowMaps.sort( LightShadowMap::cmpPriority );
GFXDEBUGEVENT_SCOPE( ShadowMapPass_Render, ColorI::RED );
// Use a timer for tracking our shadow rendering
// budget to ensure a high precision results.
mTimer->getElapsedMs();
mTimer->reset();
for ( U32 i = 0; i < shadowMaps.size(); i++ )
{
LightShadowMap *lsm = shadowMaps[i];
{
GFXDEBUGEVENT_SCOPE( ShadowMapPass_Render_Shadow, ColorI::RED );
mShadowManager->setLightShadowMap( lsm );
lsm->render( mShadowRPM, diffuseState );
++smUpdatedShadowMaps;
}
// View dependent shadows or ones that are covering the entire
// screen are updated every frame no matter the time left in
// our shadow rendering budget.
if ( lsm->isViewDependent() || lsm->getLastScreenSize() >= 1.0f )
{
++smNearShadowMaps;
continue;
}
// See if we're over our frame budget for shadow
// updates... give up completely in that case.
if ( mTimer->getElapsedMs() > smRenderBudgetMs )
break;
}
// Cleanup old unused textures.
LightShadowMap::releaseUnusedTextures();
// Update the stats.
stats.end( GFX->getDeviceStatistics() );
smShadowMapsDrawCalls = stats.mDrawCalls;
smShadowMapPolyCount = stats.mPolyCount;
smRenderTargetChanges = stats.mRenderTargetChanges;
smShadowPoolTexturesCount = ShadowMapProfile.getStats().activeCount;
smShadowPoolMemory = ( ShadowMapProfile.getStats().activeBytes / 1024.0f ) / 1024.0f;
// The NULL here is importaint as having it around
// will cause extra work in AdvancedLightManager::setLightInfo().
mShadowManager->setLightShadowMap( NULL );
}
void ShadowRenderPassManager::addInst( RenderInst *inst )
{
PROFILE_SCOPE(ShadowRenderPassManager_addInst);
if ( inst->type == RIT_Mesh || inst->type == RIT_Interior )
{
MeshRenderInst *meshRI = static_cast<MeshRenderInst*>( inst );
if ( !meshRI->matInst )
return;
const BaseMaterialDefinition *mat = meshRI->matInst->getMaterial();
if ( !mat->castsShadows() || mat->isTranslucent() )
{
// Do not add this instance, return here and avoid the default behavior
// of calling up to Parent::addInst()
return;
}
}
Parent::addInst(inst);
}

View file

@ -0,0 +1,120 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _SHADOWMAPPASS_H_
#define _SHADOWMAPPASS_H_
#ifndef _RENDERPASSMANAGER_H_
#include "renderInstance/renderPassManager.h"
#endif
#ifndef _RENDERMESHMGR_H_
#include "renderInstance/renderMeshMgr.h"
#endif
#ifndef _LIGHTINFO_H_
#include "lighting/lightInfo.h"
#endif
#ifndef _SHADOW_COMMON_H_
#include "lighting/shadowMap/shadowCommon.h"
#endif
class RenderMeshMgr;
class LightShadowMap;
class LightManager;
class ShadowMapManager;
class BaseMatInstance;
class RenderObjectMgr;
class RenderTerrainMgr;
class PlatformTimer;
class ShadowRenderPassManager;
/// ShadowMapPass, this is plugged into the SceneManager to generate
/// ShadowMaps for the scene.
class ShadowMapPass
{
public:
ShadowMapPass() {} // Only called by ConsoleSystem
ShadowMapPass(LightManager* LightManager, ShadowMapManager* ShadowManager);
virtual ~ShadowMapPass();
//
// SceneRenderPass interface
//
/// Called to render a scene.
void render( SceneManager *sceneGraph,
const SceneRenderState *diffuseState,
U32 objectMask );
/// Return the type of pass this is
virtual const String& getPassType() const { return PassTypeName; };
/// Return our sort value. (Go first in order to have shadow maps available for RIT_Objects)
virtual F32 getSortValue() const { return 0.0f; }
virtual bool geometryOnly() const { return true; }
static const String PassTypeName;
/// Used to for debugging performance by disabling
/// shadow updates and rendering.
static bool smDisableShadows;
static bool smDisableShadowsEditor;
static bool smDisableShadowsPref;
private:
static U32 smActiveShadowMaps;
static U32 smUpdatedShadowMaps;
static U32 smNearShadowMaps;
static U32 smShadowMapsDrawCalls;
static U32 smShadowMapPolyCount;
static U32 smRenderTargetChanges;
static U32 smShadowPoolTexturesCount;
static F32 smShadowPoolMemory;
/// The milliseconds alotted for shadow map updates
/// on a per frame basis.
static U32 smRenderBudgetMs;
PlatformTimer *mTimer;
LightInfoList mLights;
U32 mActiveLights;
SimObjectPtr<ShadowRenderPassManager> mShadowRPM;
LightManager* mLightManager;
ShadowMapManager* mShadowManager;
};
class ShadowRenderPassManager : public RenderPassManager
{
typedef RenderPassManager Parent;
public:
ShadowRenderPassManager() : Parent() {}
/// Add a RenderInstance to the list
virtual void addInst( RenderInst *inst );
};
#endif // _SHADOWMAPPASS_H_

View file

@ -0,0 +1,224 @@
//-----------------------------------------------------------------------------
// 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/shadowMap/shadowMatHook.h"
#include "materials/materialManager.h"
#include "materials/customMaterialDefinition.h"
#include "materials/materialFeatureTypes.h"
#include "materials/materialFeatureData.h"
#include "shaderGen/featureType.h"
#include "shaderGen/featureMgr.h"
#include "scene/sceneRenderState.h"
#include "terrain/terrFeatureTypes.h"
const MatInstanceHookType ShadowMaterialHook::Type( "ShadowMap" );
ShadowMaterialHook::ShadowMaterialHook()
{
dMemset( mShadowMat, 0, sizeof( mShadowMat ) );
}
ShadowMaterialHook::~ShadowMaterialHook()
{
for ( U32 i = 0; i < ShadowType_Count; i++ )
SAFE_DELETE( mShadowMat[i] );
}
void ShadowMaterialHook::init( BaseMatInstance *inMat )
{
if( !inMat->isValid() )
return;
// Tweak the feature data to include just what we need.
FeatureSet features;
features.addFeature( MFT_VertTransform );
features.addFeature( MFT_DiffuseMap );
features.addFeature( MFT_TexAnim );
features.addFeature( MFT_AlphaTest );
features.addFeature( MFT_Visibility );
// Actually we want to include features from the inMat
// if they operate on the preTransform verts so things
// like wind/deformation effects will also affect the shadow.
const FeatureSet &inFeatures = inMat->getFeatures();
for ( U32 i = 0; i < inFeatures.getCount(); i++ )
{
const FeatureType& ft = inFeatures.getAt(i);
if ( ft.getGroup() == MFG_PreTransform )
features.addFeature( ft );
}
// Do instancing in shadows if we can.
if ( inFeatures.hasFeature( MFT_UseInstancing ) )
features.addFeature( MFT_UseInstancing );
Material *shadowMat = (Material*)inMat->getMaterial();
if ( dynamic_cast<CustomMaterial*>( shadowMat ) )
{
// This is a custom material... who knows what it really does, but
// if it wasn't already filtered out of the shadow render then just
// give it some default depth out material.
shadowMat = MATMGR->getMaterialDefinitionByName( "AL_DefaultShadowMaterial" );
}
// By default we want to disable some states
// that the material might enable for us.
GFXStateBlockDesc forced;
forced.setBlend( false );
forced.setAlphaTest( false );
// We should force on zwrite as the prepass
// will disable it by default.
forced.setZReadWrite( true, true );
// TODO: Should we render backfaces for
// shadows or does the ESM take care of
// all our acne issues?
//forced.setCullMode( GFXCullCW );
// Vector, and spotlights use the same shadow material.
BaseMatInstance *newMat = new ShadowMatInstance( shadowMat );
newMat->setUserObject( inMat->getUserObject() );
newMat->getFeaturesDelegate().bind( &ShadowMaterialHook::_overrideFeatures );
newMat->addStateBlockDesc( forced );
if( !newMat->init( features, inMat->getVertexFormat() ) )
{
SAFE_DELETE( newMat );
newMat = MATMGR->createWarningMatInstance();
}
mShadowMat[ShadowType_Spot] = newMat;
newMat = new ShadowMatInstance( shadowMat );
newMat->setUserObject( inMat->getUserObject() );
newMat->getFeaturesDelegate().bind( &ShadowMaterialHook::_overrideFeatures );
forced.setCullMode( GFXCullCW );
newMat->addStateBlockDesc( forced );
forced.cullDefined = false;
newMat->addShaderMacro( "CUBE_SHADOW_MAP", "" );
newMat->init( features, inMat->getVertexFormat() );
mShadowMat[ShadowType_CubeMap] = newMat;
// A dual paraboloid shadow rendered in a single draw call.
features.addFeature( MFT_ParaboloidVertTransform );
features.addFeature( MFT_IsSinglePassParaboloid );
features.removeFeature( MFT_VertTransform );
newMat = new ShadowMatInstance( shadowMat );
newMat->setUserObject( inMat->getUserObject() );
GFXStateBlockDesc noCull( forced );
noCull.setCullMode( GFXCullNone );
newMat->addStateBlockDesc( noCull );
newMat->getFeaturesDelegate().bind( &ShadowMaterialHook::_overrideFeatures );
newMat->init( features, inMat->getVertexFormat() );
mShadowMat[ShadowType_DualParaboloidSinglePass] = newMat;
// Regular dual paraboloid shadow.
features.addFeature( MFT_ParaboloidVertTransform );
features.removeFeature( MFT_IsSinglePassParaboloid );
features.removeFeature( MFT_VertTransform );
newMat = new ShadowMatInstance( shadowMat );
newMat->setUserObject( inMat->getUserObject() );
newMat->addStateBlockDesc( forced );
newMat->getFeaturesDelegate().bind( &ShadowMaterialHook::_overrideFeatures );
newMat->init( features, inMat->getVertexFormat() );
mShadowMat[ShadowType_DualParaboloid] = newMat;
/*
// A single paraboloid shadow.
newMat = new ShadowMatInstance( startMatInstance );
GFXStateBlockDesc noCull;
noCull.setCullMode( GFXCullNone );
newMat->addStateBlockDesc( noCull );
newMat->getFeaturesDelegate().bind( &ShadowMaterialHook::_overrideFeatures );
newMat->init( features, globalFeatures, inMat->getVertexFormat() );
mShadowMat[ShadowType_DualParaboloidSinglePass] = newMat;
*/
}
BaseMatInstance* ShadowMaterialHook::getShadowMat( ShadowType type ) const
{
AssertFatal( type < ShadowType_Count, "ShadowMaterialHook::getShadowMat() - Bad light type!" );
// The cubemap and pssm shadows use the same
// spotlight material for shadows.
if ( type == ShadowType_Spot ||
type == ShadowType_PSSM )
return mShadowMat[ShadowType_Spot];
// Get the specialized shadow material.
return mShadowMat[type];
}
void ShadowMaterialHook::_overrideFeatures( ProcessedMaterial *mat,
U32 stageNum,
MaterialFeatureData &fd,
const FeatureSet &features )
{
if ( stageNum != 0 )
{
fd.features.clear();
return;
}
// Disable the base texture if we don't
// have alpha test enabled.
if ( !fd.features[ MFT_AlphaTest ] )
{
fd.features.removeFeature( MFT_TexAnim );
fd.features.removeFeature( MFT_DiffuseMap );
}
// HACK: Need to figure out how to enable these
// suckers without this override call!
fd.features.setFeature( MFT_ParaboloidVertTransform,
features.hasFeature( MFT_ParaboloidVertTransform ) );
fd.features.setFeature( MFT_IsSinglePassParaboloid,
features.hasFeature( MFT_IsSinglePassParaboloid ) );
// The paraboloid transform outputs linear depth, so
// it needs to use the plain depth out feature.
if ( fd.features.hasFeature( MFT_ParaboloidVertTransform ) )
fd.features.addFeature( MFT_DepthOut );
else
fd.features.addFeature( MFT_EyeSpaceDepthOut );
}
ShadowMatInstance::ShadowMatInstance( Material *mat )
: MatInstance( *mat )
{
mLightmappedMaterial = mMaterial->isLightmapped();
}
bool ShadowMatInstance::setupPass( SceneRenderState *state, const SceneData &sgData )
{
// Respect SceneRenderState render flags
if( (mLightmappedMaterial && !state->renderLightmappedMeshes()) ||
(!mLightmappedMaterial && !state->renderNonLightmappedMeshes()) )
return false;
return Parent::setupPass(state, sgData);
}

View file

@ -0,0 +1,81 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _SHADOWMATHOOK_H_
#define _SHADOWMATHOOK_H_
#ifndef _MATINSTANCEHOOK_H_
#include "materials/matInstanceHook.h"
#endif
#ifndef _MATINSTANCE_H_
#include "materials/matInstance.h"
#endif
// TODO: Move ShadowType enum to somewhere
// with less dependancies.
#ifndef _SHADOWMAPPASS_H_
#include "lighting/shadowMap/shadowMapPass.h"
#endif
class ShadowMatInstance : public MatInstance
{
typedef MatInstance Parent;
bool mLightmappedMaterial;
public:
ShadowMatInstance( Material *mat );
virtual ~ShadowMatInstance() {}
virtual bool setupPass( SceneRenderState *state, const SceneData &sgData );
};
class ShadowMaterialHook : public MatInstanceHook
{
public:
ShadowMaterialHook();
// MatInstanceHook
virtual ~ShadowMaterialHook();
virtual const MatInstanceHookType& getType() const { return Type; }
/// The material hook type.
static const MatInstanceHookType Type;
BaseMatInstance* getShadowMat( ShadowType type ) const;
void init( BaseMatInstance *mat );
protected:
static void _overrideFeatures( ProcessedMaterial *mat,
U32 stageNum,
MaterialFeatureData &fd,
const FeatureSet &features );
///
BaseMatInstance* mShadowMat[ShadowType_Count];
};
#endif // _SHADOWMATHOOK_H_

View file

@ -0,0 +1,128 @@
//-----------------------------------------------------------------------------
// 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/shadowMap/singleLightShadowMap.h"
#include "lighting/shadowMap/shadowMapManager.h"
#include "lighting/common/lightMapParams.h"
#include "console/console.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
//#include "scene/sceneReflectPass.h"
#include "gfx/gfxDevice.h"
#include "gfx/util/gfxFrustumSaver.h"
#include "gfx/gfxTransformSaver.h"
#include "renderInstance/renderPassManager.h"
SingleLightShadowMap::SingleLightShadowMap( LightInfo *light )
: LightShadowMap( light )
{
}
SingleLightShadowMap::~SingleLightShadowMap()
{
releaseTextures();
}
void SingleLightShadowMap::_render( RenderPassManager* renderPass,
const SceneRenderState *diffuseState )
{
PROFILE_SCOPE(SingleLightShadowMap_render);
const LightMapParams *lmParams = mLight->getExtended<LightMapParams>();
const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true;
const U32 texSize = getBestTexSize();
if ( mShadowMapTex.isNull() ||
mTexSize != texSize )
{
mTexSize = texSize;
mShadowMapTex.set( mTexSize, mTexSize,
ShadowMapFormat, &ShadowMapProfile,
"SingleLightShadowMap" );
}
GFXFrustumSaver frustSaver;
GFXTransformSaver saver;
MatrixF lightMatrix;
calcLightMatrices( lightMatrix, diffuseState->getFrustum() );
lightMatrix.inverse();
GFX->setWorldMatrix(lightMatrix);
const MatrixF& lightProj = GFX->getProjectionMatrix();
mWorldToLightProj = lightProj * lightMatrix;
// Render the shadowmap!
GFX->pushActiveRenderTarget();
mTarget->attachTexture( GFXTextureTarget::Color0, mShadowMapTex );
mTarget->attachTexture( GFXTextureTarget::DepthStencil,
_getDepthTarget( mShadowMapTex->getWidth(), mShadowMapTex->getHeight() ) );
GFX->setActiveRenderTarget(mTarget);
GFX->clear(GFXClearStencil | GFXClearZBuffer | GFXClearTarget, ColorI(255,255,255), 1.0f, 0);
SceneManager* sceneManager = diffuseState->getSceneManager();
SceneRenderState shadowRenderState
(
sceneManager,
SPT_Shadow,
SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ),
renderPass
);
shadowRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial );
shadowRenderState.renderNonLightmappedMeshes( true );
shadowRenderState.renderLightmappedMeshes( bUseLightmappedGeometry );
shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
sceneManager->renderSceneNoLights( &shadowRenderState, SHADOW_TYPEMASK );
_debugRender( &shadowRenderState );
mTarget->resolve();
GFX->popActiveRenderTarget();
}
void SingleLightShadowMap::setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc)
{
if ( lsc->mTapRotationTexSC->isValid() )
GFX->setTexture( lsc->mTapRotationTexSC->getSamplerRegister(),
SHADOWMGR->getTapRotationTex() );
ShadowMapParams *p = mLight->getExtended<ShadowMapParams>();
if ( lsc->mLightParamsSC->isValid() )
{
Point4F lightParams( mLight->getRange().x,
p->overDarkFactor.x,
0.0f,
0.0f );
params->set(lsc->mLightParamsSC, lightParams);
}
// The softness is a factor of the texel size.
params->setSafe( lsc->mShadowSoftnessConst, p->shadowSoftness * ( 1.0f / mTexSize ) );
}

View file

@ -0,0 +1,46 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _SINGLELIGHTSHADOWMAP_H_
#define _SINGLELIGHTSHADOWMAP_H_
#ifndef _LIGHTSHADOWMAP_H_
#include "lighting/shadowMap/lightShadowMap.h"
#endif
//
// SingleLightShadowMap, holds the shadow map and various other things for a light.
//
// This represents everything we need to render the shadowmap for one light.
class SingleLightShadowMap : public LightShadowMap
{
public:
SingleLightShadowMap( LightInfo *light );
~SingleLightShadowMap();
// LightShadowMap
virtual ShadowType getShadowType() const { return ShadowType_Spot; }
virtual void _render( RenderPassManager* renderPass, const SceneRenderState *diffuseState );
virtual void setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc);
};
#endif // _SINGLELIGHTSHADOWMAP_H_