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,855 @@
//-----------------------------------------------------------------------------
// 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/advanced/advancedLightBinManager.h"
#include "lighting/advanced/advancedLightManager.h"
#include "lighting/advanced/advancedLightBufferConditioner.h"
#include "lighting/shadowMap/shadowMapManager.h"
#include "lighting/shadowMap/shadowMapPass.h"
#include "lighting/shadowMap/lightShadowMap.h"
#include "lighting/common/lightMapParams.h"
#include "renderInstance/renderPrePassMgr.h"
#include "gfx/gfxTransformSaver.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
#include "materials/materialManager.h"
#include "materials/sceneData.h"
#include "core/util/safeDelete.h"
#include "core/util/rgb2luv.h"
#include "gfx/gfxDebugEvent.h"
#include "math/util/matrixSet.h"
#include "console/consoleTypes.h"
const RenderInstType AdvancedLightBinManager::RIT_LightInfo( "LightInfo" );
const String AdvancedLightBinManager::smBufferName( "lightinfo" );
ShadowFilterMode AdvancedLightBinManager::smShadowFilterMode = ShadowFilterMode_SoftShadowHighQuality;
bool AdvancedLightBinManager::smPSSMDebugRender = false;
bool AdvancedLightBinManager::smUseSSAOMask = false;
ImplementEnumType( ShadowFilterMode,
"The shadow filtering modes for Advanced Lighting shadows.\n"
"@ingroup AdvancedLighting" )
{ ShadowFilterMode_None, "None",
"@brief Simple point sampled filtering.\n"
"This is the fastest and lowest quality mode." },
{ ShadowFilterMode_SoftShadow, "SoftShadow",
"@brief A variable tap rotated poisson disk soft shadow filter.\n"
"It performs 4 taps to classify the point as in shadow, out of shadow, or along a "
"shadow edge. Samples on the edge get an additional 8 taps to soften them." },
{ ShadowFilterMode_SoftShadowHighQuality, "SoftShadowHighQuality",
"@brief A 12 tap rotated poisson disk soft shadow filter.\n"
"It performs all the taps for every point without any early rejection." },
EndImplementEnumType;
// NOTE: The order here matches that of the LightInfo::Type enum.
const String AdvancedLightBinManager::smLightMatNames[] =
{
"AL_PointLightMaterial", // LightInfo::Point
"AL_SpotLightMaterial", // LightInfo::Spot
"AL_VectorLightMaterial", // LightInfo::Vector
"", // LightInfo::Ambient
};
// NOTE: The order here matches that of the LightInfo::Type enum.
const GFXVertexFormat* AdvancedLightBinManager::smLightMatVertex[] =
{
getGFXVertexFormat<AdvancedLightManager::LightVertex>(), // LightInfo::Point
getGFXVertexFormat<AdvancedLightManager::LightVertex>(), // LightInfo::Spot
getGFXVertexFormat<FarFrustumQuadVert>(), // LightInfo::Vector
NULL, // LightInfo::Ambient
};
// NOTE: The order here matches that of the ShadowType enum.
const String AdvancedLightBinManager::smShadowTypeMacro[] =
{
"", // ShadowType_Spot
"", // ShadowType_PSSM,
"SHADOW_PARABOLOID", // ShadowType_Paraboloid,
"SHADOW_DUALPARABOLOID_SINGLE_PASS", // ShadowType_DualParaboloidSinglePass,
"SHADOW_DUALPARABOLOID", // ShadowType_DualParaboloid,
"SHADOW_CUBE", // ShadowType_CubeMap,
};
AdvancedLightBinManager::RenderSignal &AdvancedLightBinManager::getRenderSignal()
{
static RenderSignal theSignal;
return theSignal;
}
IMPLEMENT_CONOBJECT(AdvancedLightBinManager);
ConsoleDocClass( AdvancedLightBinManager,
"@brief Rendering Manager responsible for lighting, shadows, and global variables affecing both.\n\n"
"Should not be exposed to TorqueScript as a game object, meant for internal use only\n\n"
"@ingroup Lighting"
);
AdvancedLightBinManager::AdvancedLightBinManager( AdvancedLightManager *lm /* = NULL */,
ShadowMapManager *sm /* = NULL */,
GFXFormat lightBufferFormat /* = GFXFormatR8G8B8A8 */ )
: RenderTexTargetBinManager( RIT_LightInfo, 1.0f, 1.0f, lightBufferFormat ),
mNumLightsCulled(0),
mLightManager(lm),
mShadowManager(sm),
mConditioner(NULL)
{
// Create an RGB conditioner
mConditioner = new AdvancedLightBufferConditioner( getTargetFormat(),
AdvancedLightBufferConditioner::RGB );
mNamedTarget.setConditioner( mConditioner );
mNamedTarget.registerWithName( smBufferName );
// We want a full-resolution buffer
mTargetSizeType = RenderTexTargetBinManager::WindowSize;
mMRTLightmapsDuringPrePass = false;
Con::NotifyDelegate callback( this, &AdvancedLightBinManager::_deleteLightMaterials );
Con::addVariableNotify( "$pref::Shadows::filterMode", callback );
Con::addVariableNotify( "$AL::PSSMDebugRender", callback );
Con::addVariableNotify( "$AL::UseSSAOMask", callback );
}
AdvancedLightBinManager::~AdvancedLightBinManager()
{
_deleteLightMaterials();
SAFE_DELETE(mConditioner);
Con::NotifyDelegate callback( this, &AdvancedLightBinManager::_deleteLightMaterials );
Con::removeVariableNotify( "$pref::shadows::filterMode", callback );
Con::removeVariableNotify( "$AL::PSSMDebugRender", callback );
Con::removeVariableNotify( "$AL::UseSSAOMask", callback );
}
void AdvancedLightBinManager::consoleInit()
{
Parent::consoleInit();
Con::addVariable( "$pref::shadows::filterMode",
TYPEID<ShadowFilterMode>(), &smShadowFilterMode,
"The filter mode to use for shadows.\n"
"@ingroup AdvancedLighting\n" );
Con::addVariable( "$AL::UseSSAOMask", TypeBool, &smUseSSAOMask,
"Used by the SSAO PostEffect to toggle the sampling of ssaomask "
"texture by the light shaders.\n"
"@ingroup AdvancedLighting\n" );
Con::addVariable( "$AL::PSSMDebugRender", TypeBool, &smPSSMDebugRender,
"Enables debug rendering of the PSSM shadows.\n"
"@ingroup AdvancedLighting\n" );
}
bool AdvancedLightBinManager::setTargetSize(const Point2I &newTargetSize)
{
bool ret = Parent::setTargetSize( newTargetSize );
// We require the viewport to match the default.
mNamedTarget.setViewport( GFX->getViewport() );
return ret;
}
void AdvancedLightBinManager::addLight( LightInfo *light )
{
// Get the light type.
const LightInfo::Type lightType = light->getType();
AssertFatal( lightType == LightInfo::Point ||
lightType == LightInfo::Spot, "Bogus light type." );
// Find a shadow map for this light, if it has one
ShadowMapParams *lsp = light->getExtended<ShadowMapParams>();
LightShadowMap *lsm = lsp->getShadowMap();
// Get the right shadow type.
ShadowType shadowType = ShadowType_None;
if ( light->getCastShadows() &&
lsm && lsm->hasShadowTex() &&
!ShadowMapPass::smDisableShadows )
shadowType = lsm->getShadowType();
// Add the entry
LightBinEntry lEntry;
lEntry.lightInfo = light;
lEntry.shadowMap = lsm;
lEntry.lightMaterial = _getLightMaterial( lightType, shadowType, lsp->hasCookieTex() );
if( lightType == LightInfo::Spot )
lEntry.vertBuffer = mLightManager->getConeMesh( lEntry.numPrims, lEntry.primBuffer );
else
lEntry.vertBuffer = mLightManager->getSphereMesh( lEntry.numPrims, lEntry.primBuffer );
// If it's a point light, push front, spot
// light, push back. This helps batches.
Vector<LightBinEntry> &curBin = mLightBin;
if ( light->getType() == LightInfo::Point )
curBin.push_front( lEntry );
else
curBin.push_back( lEntry );
}
void AdvancedLightBinManager::clear()
{
Con::setIntVariable("lightMetrics::activeLights", mLightBin.size());
Con::setIntVariable("lightMetrics::culledLights", mNumLightsCulled);
mLightBin.clear();
mNumLightsCulled = 0;
}
void AdvancedLightBinManager::render( SceneRenderState *state )
{
PROFILE_SCOPE( AdvancedLightManager_Render );
// Take a look at the SceneRenderState and see if we should skip drawing the pre-pass
if( state->disableAdvancedLightingBins() )
return;
// Automagically save & restore our viewport and transforms.
GFXTransformSaver saver;
if( !mLightManager )
return;
// Get the sunlight. If there's no sun, and no lights in the bins, no draw
LightInfo *sunLight = mLightManager->getSpecialLight( LightManager::slSunLightType );
if( !sunLight && mLightBin.empty() )
return;
GFXDEBUGEVENT_SCOPE( AdvancedLightBinManager_Render, ColorI::RED );
// Tell the superclass we're about to render
if ( !_onPreRender( state ) )
return;
// Clear as long as there isn't MRT population of light buffer with lightmap data
if ( !MRTLightmapsDuringPrePass() )
GFX->clear(GFXClearTarget, ColorI(0, 0, 0, 0), 1.0f, 0);
// Restore transforms
MatrixSet &matrixSet = getRenderPass()->getMatrixSet();
matrixSet.restoreSceneViewProjection();
const MatrixF &worldToCameraXfm = matrixSet.getWorldToCamera();
// Set up the SG Data
SceneData sgData;
sgData.init( state );
// There are cases where shadow rendering is disabled.
const bool disableShadows = state->isReflectPass() || ShadowMapPass::smDisableShadows;
// Pick the right material for rendering the sunlight... we only
// cast shadows when its enabled and we're not in a reflection.
LightMaterialInfo *vectorMatInfo;
if ( sunLight &&
sunLight->getCastShadows() &&
!disableShadows &&
sunLight->getExtended<ShadowMapParams>() )
vectorMatInfo = _getLightMaterial( LightInfo::Vector, ShadowType_PSSM, false );
else
vectorMatInfo = _getLightMaterial( LightInfo::Vector, ShadowType_None, false );
// Initialize and set the per-frame parameters after getting
// the vector light material as we use lazy creation.
_setupPerFrameParameters( state );
// Draw sunlight/ambient
if ( sunLight && vectorMatInfo )
{
GFXDEBUGEVENT_SCOPE( AdvancedLightBinManager_Render_Sunlight, ColorI::RED );
// Set up SG data
setupSGData( sgData, state, sunLight );
vectorMatInfo->setLightParameters( sunLight, state, worldToCameraXfm );
// Set light holds the active shadow map.
mShadowManager->setLightShadowMapForLight( sunLight );
// Set geometry
GFX->setVertexBuffer( mFarFrustumQuadVerts );
GFX->setPrimitiveBuffer( NULL );
// Render the material passes
while( vectorMatInfo->matInstance->setupPass( state, sgData ) )
{
vectorMatInfo->matInstance->setSceneInfo( state, sgData );
vectorMatInfo->matInstance->setTransforms( matrixSet, state );
GFX->drawPrimitive( GFXTriangleFan, 0, 2 );
}
}
// Blend the lights in the bin to the light buffer
for( LightBinIterator itr = mLightBin.begin(); itr != mLightBin.end(); itr++ )
{
LightBinEntry& curEntry = *itr;
LightInfo *curLightInfo = curEntry.lightInfo;
LightMaterialInfo *curLightMat = curEntry.lightMaterial;
const U32 numPrims = curEntry.numPrims;
const U32 numVerts = curEntry.vertBuffer->mNumVerts;
// Skip lights which won't affect the scene.
if ( !curLightMat || curLightInfo->getBrightness() <= 0.001f )
continue;
GFXDEBUGEVENT_SCOPE( AdvancedLightBinManager_Render_Light, ColorI::RED );
setupSGData( sgData, state, curLightInfo );
curLightMat->setLightParameters( curLightInfo, state, worldToCameraXfm );
mShadowManager->setLightShadowMap( curEntry.shadowMap );
// Let the shadow know we're about to render from it.
if ( curEntry.shadowMap )
curEntry.shadowMap->preLightRender();
// Set geometry
GFX->setVertexBuffer( curEntry.vertBuffer );
GFX->setPrimitiveBuffer( curEntry.primBuffer );
// Render the material passes
while( curLightMat->matInstance->setupPass( state, sgData ) )
{
// Set transforms
matrixSet.setWorld(*sgData.objTrans);
curLightMat->matInstance->setTransforms(matrixSet, state);
curLightMat->matInstance->setSceneInfo(state, sgData);
if(curEntry.primBuffer)
GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, numVerts, 0, numPrims);
else
GFX->drawPrimitive(GFXTriangleList, 0, numPrims);
}
// Tell it we're done rendering.
if ( curEntry.shadowMap )
curEntry.shadowMap->postLightRender();
}
// Set NULL for active shadow map (so nothing gets confused)
mShadowManager->setLightShadowMap(NULL);
GFX->setVertexBuffer( NULL );
GFX->setPrimitiveBuffer( NULL );
// Fire off a signal to let others know that light-bin rendering is ending now
getRenderSignal().trigger(state, this);
// Finish up the rendering
_onPostRender();
}
AdvancedLightBinManager::LightMaterialInfo* AdvancedLightBinManager::_getLightMaterial( LightInfo::Type lightType,
ShadowType shadowType,
bool useCookieTex )
{
PROFILE_SCOPE( AdvancedLightBinManager_GetLightMaterial );
// Build the key.
const LightMatKey key( lightType, shadowType, useCookieTex );
// See if we've already built this one.
LightMatTable::Iterator iter = mLightMaterials.find( key );
if ( iter != mLightMaterials.end() )
return iter->value;
// If we got here we need to build a material for
// this light+shadow combination.
LightMaterialInfo *info = NULL;
// First get the light material name and make sure
// this light has a material in the first place.
const String &lightMatName = smLightMatNames[ lightType ];
if ( lightMatName.isNotEmpty() )
{
Vector<GFXShaderMacro> shadowMacros;
// Setup the shadow type macros for this material.
if ( shadowType == ShadowType_None )
shadowMacros.push_back( GFXShaderMacro( "NO_SHADOW" ) );
else
{
shadowMacros.push_back( GFXShaderMacro( smShadowTypeMacro[ shadowType ] ) );
// Do we need to do shadow filtering?
if ( smShadowFilterMode != ShadowFilterMode_None )
{
shadowMacros.push_back( GFXShaderMacro( "SOFTSHADOW" ) );
const F32 SM = GFX->getPixelShaderVersion();
if ( SM >= 3.0f && smShadowFilterMode == ShadowFilterMode_SoftShadowHighQuality )
shadowMacros.push_back( GFXShaderMacro( "SOFTSHADOW_HIGH_QUALITY" ) );
}
}
if ( useCookieTex )
shadowMacros.push_back( GFXShaderMacro( "USE_COOKIE_TEX" ) );
// Its safe to add the PSSM debug macro to all the materials.
if ( smPSSMDebugRender )
shadowMacros.push_back( GFXShaderMacro( "PSSM_DEBUG_RENDER" ) );
// If its a vector light see if we can enable SSAO.
if ( lightType == LightInfo::Vector && smUseSSAOMask )
shadowMacros.push_back( GFXShaderMacro( "USE_SSAO_MASK" ) );
// Now create the material info object.
info = new LightMaterialInfo( lightMatName, smLightMatVertex[ lightType ], shadowMacros );
}
// Push this into the map and return it.
mLightMaterials.insertUnique( key, info );
return info;
}
void AdvancedLightBinManager::_deleteLightMaterials()
{
LightMatTable::Iterator iter = mLightMaterials.begin();
for ( ; iter != mLightMaterials.end(); iter++ )
delete iter->value;
mLightMaterials.clear();
}
void AdvancedLightBinManager::_setupPerFrameParameters( const SceneRenderState *state )
{
PROFILE_SCOPE( AdvancedLightBinManager_SetupPerFrameParameters );
const Frustum &frustum = state->getFrustum();
MatrixF invCam( frustum.getTransform() );
invCam.inverse();
const Point3F *wsFrustumPoints = frustum.getPoints();
const Point3F& cameraPos = frustum.getPosition();
// Now build the quad for drawing full-screen vector light
// passes.... this is a volatile VB and updates every frame.
FarFrustumQuadVert verts[4];
{
verts[0].point.set( wsFrustumPoints[Frustum::FarBottomLeft] - cameraPos );
invCam.mulP( wsFrustumPoints[Frustum::FarBottomLeft], &verts[0].normal );
verts[0].texCoord.set( -1.0, -1.0 );
verts[1].point.set( wsFrustumPoints[Frustum::FarTopLeft] - cameraPos );
invCam.mulP( wsFrustumPoints[Frustum::FarTopLeft], &verts[1].normal );
verts[1].texCoord.set( -1.0, 1.0 );
verts[2].point.set( wsFrustumPoints[Frustum::FarTopRight] - cameraPos );
invCam.mulP( wsFrustumPoints[Frustum::FarTopRight], &verts[2].normal );
verts[2].texCoord.set( 1.0, 1.0 );
verts[3].point.set( wsFrustumPoints[Frustum::FarBottomRight] - cameraPos );
invCam.mulP( wsFrustumPoints[Frustum::FarBottomRight], &verts[3].normal );
verts[3].texCoord.set( 1.0, -1.0 );
}
mFarFrustumQuadVerts.set( GFX, 4 );
dMemcpy( mFarFrustumQuadVerts.lock(), verts, sizeof( verts ) );
mFarFrustumQuadVerts.unlock();
PlaneF farPlane(wsFrustumPoints[Frustum::FarBottomLeft], wsFrustumPoints[Frustum::FarTopLeft], wsFrustumPoints[Frustum::FarTopRight]);
PlaneF vsFarPlane(verts[0].normal, verts[1].normal, verts[2].normal);
// Parameters calculated, assign them to the materials
LightMatTable::Iterator iter = mLightMaterials.begin();
for ( ; iter != mLightMaterials.end(); iter++ )
{
if ( iter->value )
iter->value->setViewParameters( frustum.getNearDist(),
frustum.getFarDist(),
frustum.getPosition(),
farPlane,
vsFarPlane);
}
}
void AdvancedLightBinManager::setupSGData( SceneData &data, const SceneRenderState* state, LightInfo *light )
{
PROFILE_SCOPE( AdvancedLightBinManager_setupSGData );
data.lights[0] = light;
data.ambientLightColor = state->getAmbientLightColor();
data.objTrans = &MatrixF::Identity;
if ( light )
{
if ( light->getType() == LightInfo::Point )
{
// The point light volume gets some flat spots along
// the perimiter mostly visible in the constant and
// quadradic falloff modes.
//
// To account for them slightly increase the scale
// instead of greatly increasing the polycount.
mLightMat = light->getTransform();
mLightMat.scale( light->getRange() * 1.01f );
data.objTrans = &mLightMat;
}
else if ( light->getType() == LightInfo::Spot )
{
mLightMat = light->getTransform();
// Rotate it to face down the -y axis.
MatrixF scaleRotateTranslate( EulerF( M_PI_F / -2.0f, 0.0f, 0.0f ) );
// Calculate the radius based on the range and angle.
F32 range = light->getRange().x;
F32 radius = range * mSin( mDegToRad( light->getOuterConeAngle() ) * 0.5f );
// NOTE: This fudge makes the cone a little bigger
// to remove the facet egde of the cone geometry.
radius *= 1.1f;
// Use the scale to distort the cone to
// match our radius and range.
scaleRotateTranslate.scale( Point3F( radius, radius, range ) );
// Apply the transform and set the position.
mLightMat *= scaleRotateTranslate;
mLightMat.setPosition( light->getPosition() );
data.objTrans = &mLightMat;
}
}
}
void AdvancedLightBinManager::MRTLightmapsDuringPrePass( bool val )
{
// Do not enable if the GFX device can't do MRT's
if ( GFX->getNumRenderTargets() < 2 )
val = false;
if ( mMRTLightmapsDuringPrePass != val )
{
mMRTLightmapsDuringPrePass = val;
// Reload materials to cause a feature recalculation on prepass materials
if(mLightManager->isActive())
MATMGR->flushAndReInitInstances();
RenderPrePassMgr *prepass;
if ( Sim::findObject( "AL_PrePassBin", prepass ) && prepass->getTargetTexture( 0 ) )
prepass->updateTargets();
}
}
AdvancedLightBinManager::LightMaterialInfo::LightMaterialInfo( const String &matName,
const GFXVertexFormat *vertexFormat,
const Vector<GFXShaderMacro> &macros )
: matInstance(NULL),
zNearFarInvNearFar(NULL),
farPlane(NULL),
vsFarPlane(NULL),
negFarPlaneDotEye(NULL),
lightPosition(NULL),
lightDirection(NULL),
lightColor(NULL),
lightAttenuation(NULL),
lightRange(NULL),
lightAmbient(NULL),
lightTrilight(NULL),
lightSpotParams(NULL)
{
Material *mat = MATMGR->getMaterialDefinitionByName( matName );
if ( !mat )
return;
matInstance = new LightMatInstance( *mat );
for ( U32 i=0; i < macros.size(); i++ )
matInstance->addShaderMacro( macros[i].name, macros[i].value );
matInstance->init( MATMGR->getDefaultFeatures(), vertexFormat );
lightDirection = matInstance->getMaterialParameterHandle("$lightDirection");
lightAmbient = matInstance->getMaterialParameterHandle("$lightAmbient");
lightTrilight = matInstance->getMaterialParameterHandle("$lightTrilight");
lightSpotParams = matInstance->getMaterialParameterHandle("$lightSpotParams");
lightAttenuation = matInstance->getMaterialParameterHandle("$lightAttenuation");
lightRange = matInstance->getMaterialParameterHandle("$lightRange");
lightPosition = matInstance->getMaterialParameterHandle("$lightPosition");
farPlane = matInstance->getMaterialParameterHandle("$farPlane");
vsFarPlane = matInstance->getMaterialParameterHandle("$vsFarPlane");
negFarPlaneDotEye = matInstance->getMaterialParameterHandle("$negFarPlaneDotEye");
zNearFarInvNearFar = matInstance->getMaterialParameterHandle("$zNearFarInvNearFar");
lightColor = matInstance->getMaterialParameterHandle("$lightColor");
lightBrightness = matInstance->getMaterialParameterHandle("$lightBrightness");
}
AdvancedLightBinManager::LightMaterialInfo::~LightMaterialInfo()
{
SAFE_DELETE(matInstance);
}
void AdvancedLightBinManager::LightMaterialInfo::setViewParameters( const F32 _zNear,
const F32 _zFar,
const Point3F &_eyePos,
const PlaneF &_farPlane,
const PlaneF &_vsFarPlane)
{
MaterialParameters *matParams = matInstance->getMaterialParameters();
matParams->setSafe( farPlane, *((const Point4F *)&_farPlane) );
matParams->setSafe( vsFarPlane, *((const Point4F *)&_vsFarPlane) );
if ( negFarPlaneDotEye->isValid() )
{
// -dot( farPlane, eyePos )
const F32 negFarPlaneDotEyeVal = -( mDot( *((const Point3F *)&_farPlane), _eyePos ) + _farPlane.d );
matParams->set( negFarPlaneDotEye, negFarPlaneDotEyeVal );
}
matParams->setSafe( zNearFarInvNearFar, Point4F( _zNear, _zFar, 1.0f / _zNear, 1.0f / _zFar ) );
}
void AdvancedLightBinManager::LightMaterialInfo::setLightParameters( const LightInfo *lightInfo, const SceneRenderState* renderState, const MatrixF &worldViewOnly )
{
MaterialParameters *matParams = matInstance->getMaterialParameters();
// Set color in the right format, set alpha to the luminance value for the color.
ColorF col = lightInfo->getColor();
// TODO: The specularity control of the light
// is being scaled by the overall lumiance.
//
// Not sure if this may be the source of our
// bad specularity results maybe?
//
const Point3F colorToLumiance( 0.3576f, 0.7152f, 0.1192f );
F32 lumiance = mDot(*((const Point3F *)&lightInfo->getColor()), colorToLumiance );
col.alpha *= lumiance;
matParams->setSafe( lightColor, col );
matParams->setSafe( lightBrightness, lightInfo->getBrightness() );
switch( lightInfo->getType() )
{
case LightInfo::Vector:
{
VectorF lightDir = lightInfo->getDirection();
worldViewOnly.mulV(lightDir);
lightDir.normalize();
matParams->setSafe( lightDirection, lightDir );
// Set small number for alpha since it represents existing specular in
// the vector light. This prevents a divide by zero.
ColorF ambientColor = renderState->getAmbientLightColor();
ambientColor.alpha = 0.00001f;
matParams->setSafe( lightAmbient, ambientColor );
// If no alt color is specified, set it to the average of
// the ambient and main color to avoid artifacts.
//
// TODO: Trilight disabled until we properly implement it
// in the light info!
//
//ColorF lightAlt = lightInfo->getAltColor();
ColorF lightAlt( ColorF::BLACK ); // = lightInfo->getAltColor();
if ( lightAlt.red == 0.0f && lightAlt.green == 0.0f && lightAlt.blue == 0.0f )
lightAlt = (lightInfo->getColor() + renderState->getAmbientLightColor()) / 2.0f;
ColorF trilightColor = lightAlt;
matParams->setSafe(lightTrilight, trilightColor);
}
break;
case LightInfo::Spot:
{
const F32 outerCone = lightInfo->getOuterConeAngle();
const F32 innerCone = getMin( lightInfo->getInnerConeAngle(), outerCone );
const F32 outerCos = mCos( mDegToRad( outerCone / 2.0f ) );
const F32 innerCos = mCos( mDegToRad( innerCone / 2.0f ) );
Point4F spotParams( outerCos,
innerCos - outerCos,
mCos( mDegToRad( outerCone ) ),
0.0f );
matParams->setSafe( lightSpotParams, spotParams );
VectorF lightDir = lightInfo->getDirection();
worldViewOnly.mulV(lightDir);
lightDir.normalize();
matParams->setSafe( lightDirection, lightDir );
}
// Fall through
case LightInfo::Point:
{
const F32 radius = lightInfo->getRange().x;
matParams->setSafe( lightRange, radius );
Point3F lightPos;
worldViewOnly.mulP(lightInfo->getPosition(), &lightPos);
matParams->setSafe( lightPosition, lightPos );
// Get the attenuation falloff ratio and normalize it.
Point3F attenRatio = lightInfo->getExtended<ShadowMapParams>()->attenuationRatio;
F32 total = attenRatio.x + attenRatio.y + attenRatio.z;
if ( total > 0.0f )
attenRatio /= total;
Point2F attenParams( ( 1.0f / radius ) * attenRatio.y,
( 1.0f / ( radius * radius ) ) * attenRatio.z );
matParams->setSafe( lightAttenuation, attenParams );
break;
}
default:
AssertFatal( false, "Bad light type!" );
break;
}
}
bool LightMatInstance::setupPass( SceneRenderState *state, const SceneData &sgData )
{
// Go no further if the material failed to initialize properly.
if ( !mProcessedMaterial ||
mProcessedMaterial->getNumPasses() == 0 )
return false;
// Fetch the lightmap params
const LightMapParams *lmParams = sgData.lights[0]->getExtended<LightMapParams>();
// If no Lightmap params, let parent handle it
if(lmParams == NULL)
return Parent::setupPass(state, sgData);
// Defaults
bool bRetVal = true;
// What render pass is this...
if(mCurPass == -1)
{
// First pass, reset this flag
mInternalPass = false;
// Pass call to parent
bRetVal = Parent::setupPass(state, sgData);
}
else
{
// If this light is represented in a lightmap, it has already done it's
// job for non-lightmapped geometry. Now render the lightmapped geometry
// pass (specular + shadow-darkening)
if(!mInternalPass && lmParams->representedInLightmap)
mInternalPass = true;
else
return Parent::setupPass(state, sgData);
}
// Set up the shader constants we need to...
if(mLightMapParamsSC->isValid())
{
// If this is an internal pass, special case the parameters
if(mInternalPass)
{
AssertFatal( lmParams->shadowDarkenColor.alpha == -1.0f, "Assumption failed, check unpack code!" );
getMaterialParameters()->set( mLightMapParamsSC, lmParams->shadowDarkenColor );
}
else
getMaterialParameters()->set( mLightMapParamsSC, ColorF::WHITE );
}
// Now override stateblock with our own
if(!mInternalPass)
{
// If this is not an internal pass, and this light is represented in lightmaps
// than only effect non-lightmapped geometry for this pass
if(lmParams->representedInLightmap)
GFX->setStateBlock(mLitState[StaticLightNonLMGeometry]);
else // This is a normal, dynamic light.
GFX->setStateBlock(mLitState[DynamicLight]);
}
else // Internal pass, this is the add-specular/multiply-darken-color pass
GFX->setStateBlock(mLitState[StaticLightLMGeometry]);
return bRetVal;
}
bool LightMatInstance::init( const FeatureSet &features, const GFXVertexFormat *vertexFormat )
{
bool success = Parent::init(features, vertexFormat);
// If the initialization failed don't continue.
if ( !success || !mProcessedMaterial || mProcessedMaterial->getNumPasses() == 0 )
return false;
mLightMapParamsSC = getMaterialParameterHandle("$lightMapParams");
// Grab the state block for the first render pass (since this mat instance
// inserts a pass after the first pass)
AssertFatal(mProcessedMaterial->getNumPasses() > 0, "No passes created! Ohnoes");
const RenderPassData *rpd = mProcessedMaterial->getPass(0);
AssertFatal(rpd, "No render pass data!");
AssertFatal(rpd->mRenderStates[0], "No render state 0!");
// Get state block desc for normal (not wireframe, not translucent, not glow, etc)
// render state
GFXStateBlockDesc litState = rpd->mRenderStates[0]->getDesc();
// Create state blocks for each of the 3 possible combos in setupPass
//DynamicLight State: This will effect lightmapped and non-lightmapped geometry
// in the same way.
litState.separateAlphaBlendDefined = true;
litState.separateAlphaBlendEnable = false;
litState.stencilMask = RenderPrePassMgr::OpaqueDynamicLitMask | RenderPrePassMgr::OpaqueStaticLitMask;
mLitState[DynamicLight] = GFX->createStateBlock(litState);
// StaticLightNonLMGeometry State: This will treat non-lightmapped geometry
// in the usual way, but will not effect lightmapped geometry.
litState.separateAlphaBlendDefined = true;
litState.separateAlphaBlendEnable = false;
litState.stencilMask = RenderPrePassMgr::OpaqueDynamicLitMask;
mLitState[StaticLightNonLMGeometry] = GFX->createStateBlock(litState);
// StaticLightLMGeometry State: This will add specular information (alpha) but
// multiply-darken color information.
litState.blendDest = GFXBlendSrcColor;
litState.blendSrc = GFXBlendZero;
litState.stencilMask = RenderPrePassMgr::OpaqueStaticLitMask;
litState.separateAlphaBlendDefined = true;
litState.separateAlphaBlendEnable = true;
litState.separateAlphaBlendSrc = GFXBlendOne;
litState.separateAlphaBlendDest = GFXBlendOne;
litState.separateAlphaBlendOp = GFXBlendOpAdd;
mLitState[StaticLightLMGeometry] = GFX->createStateBlock(litState);
return true;
}

View file

@ -0,0 +1,234 @@
//-----------------------------------------------------------------------------
// 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 _ADVANCEDLIGHTBINMANAGER_H_
#define _ADVANCEDLIGHTBINMANAGER_H_
#ifndef _TEXTARGETBIN_MGR_H_
#include "renderInstance/renderTexTargetBinManager.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#ifndef _LIGHTINFO_H_
#include "lighting/lightInfo.h"
#endif
#ifndef _MATHUTIL_FRUSTUM_H_
#include "math/util/frustum.h"
#endif
#ifndef _MATINSTANCE_H_
#include "materials/matInstance.h"
#endif
#ifndef _SHADOW_COMMON_H_
#include "lighting/shadowMap/shadowCommon.h"
#endif
class AdvancedLightManager;
class ShadowMapManager;
class LightShadowMap;
class AdvancedLightBufferConditioner;
class LightMapParams;
class LightMatInstance : public MatInstance
{
typedef MatInstance Parent;
protected:
MaterialParameterHandle *mLightMapParamsSC;
bool mInternalPass;
enum
{
DynamicLight = 0,
StaticLightNonLMGeometry,
StaticLightLMGeometry,
NUM_LIT_STATES
};
GFXStateBlockRef mLitState[NUM_LIT_STATES];
public:
LightMatInstance(Material &mat) : Parent(mat), mLightMapParamsSC(NULL), mInternalPass(false) {}
virtual bool init( const FeatureSet &features, const GFXVertexFormat *vertexFormat );
virtual bool setupPass( SceneRenderState *state, const SceneData &sgData );
};
class AdvancedLightBinManager : public RenderTexTargetBinManager
{
typedef RenderTexTargetBinManager Parent;
public:
// Light info Render Inst Type
static const RenderInstType RIT_LightInfo;
// registered buffer name
static const String smBufferName;
/// The shadow filter mode to use on shadowed light materials.
static ShadowFilterMode smShadowFilterMode;
/// Used to toggle the PSSM debug rendering mode.
static bool smPSSMDebugRender;
/// Set by the SSAO post effect to tell the vector
/// light to compile in the SSAO mask.
static bool smUseSSAOMask;
// Used for console init
AdvancedLightBinManager( AdvancedLightManager *lm = NULL,
ShadowMapManager *sm = NULL,
GFXFormat lightBufferFormat = GFXFormatR8G8B8A8 );
virtual ~AdvancedLightBinManager();
// ConsoleObject
static void consoleInit();
// RenderBinManager
virtual void render(SceneRenderState *);
virtual void clear();
virtual void sort() {}
// Add a light to the bins
void addLight( LightInfo *light );
virtual bool setTargetSize(const Point2I &newTargetSize);
// ConsoleObject interface
DECLARE_CONOBJECT(AdvancedLightBinManager);
bool MRTLightmapsDuringPrePass() const { return mMRTLightmapsDuringPrePass; }
void MRTLightmapsDuringPrePass(bool val);
typedef Signal<void(SceneRenderState *, AdvancedLightBinManager *)> RenderSignal;
static RenderSignal &getRenderSignal();
AdvancedLightManager *getManager() { return mLightManager; }
protected:
/// Frees all the currently allocated light materials.
void _deleteLightMaterials();
// Track a light material and associated data
struct LightMaterialInfo
{
LightMatInstance *matInstance;
// { zNear, zFar, 1/zNear, 1/zFar }
MaterialParameterHandle *zNearFarInvNearFar;
// Far frustum plane (World Space)
MaterialParameterHandle *farPlane;
// Far frustum plane (View Space)
MaterialParameterHandle *vsFarPlane;
// -dot( farPlane, eyePos )
MaterialParameterHandle *negFarPlaneDotEye;
// Light Parameters
MaterialParameterHandle *lightPosition;
MaterialParameterHandle *lightDirection;
MaterialParameterHandle *lightColor;
MaterialParameterHandle *lightBrightness;
MaterialParameterHandle *lightAttenuation;
MaterialParameterHandle *lightRange;
MaterialParameterHandle *lightAmbient;
MaterialParameterHandle *lightTrilight;
MaterialParameterHandle *lightSpotParams;
LightMaterialInfo( const String &matName,
const GFXVertexFormat *vertexFormat,
const Vector<GFXShaderMacro> &macros = Vector<GFXShaderMacro>() );
virtual ~LightMaterialInfo();
void setViewParameters( const F32 zNear,
const F32 zFar,
const Point3F &eyePos,
const PlaneF &farPlane,
const PlaneF &_vsFarPlane );
void setLightParameters( const LightInfo *light, const SceneRenderState* renderState, const MatrixF &worldViewOnly );
};
protected:
struct LightBinEntry
{
LightInfo* lightInfo;
LightShadowMap* shadowMap;
LightMaterialInfo* lightMaterial;
GFXPrimitiveBuffer* primBuffer;
GFXVertexBuffer* vertBuffer;
U32 numPrims;
};
Vector<LightBinEntry> mLightBin;
typedef Vector<LightBinEntry>::iterator LightBinIterator;
bool mMRTLightmapsDuringPrePass;
/// Used in setupSGData to set the object transform.
MatrixF mLightMat;
U32 mNumLightsCulled;
AdvancedLightManager *mLightManager;
ShadowMapManager *mShadowManager;
static const String smLightMatNames[LightInfo::Count];
static const String smShadowTypeMacro[ShadowType_Count];
static const GFXVertexFormat* smLightMatVertex[LightInfo::Count];
typedef CompoundKey3<LightInfo::Type,ShadowType,bool> LightMatKey;
typedef HashTable<LightMatKey,LightMaterialInfo*> LightMatTable;
/// The fixed table of light material info.
LightMatTable mLightMaterials;
LightMaterialInfo* _getLightMaterial( LightInfo::Type lightType, ShadowType shadowType, bool useCookieTex );
///
void _onShadowFilterChanged();
AdvancedLightBufferConditioner *mConditioner;
typedef GFXVertexPNT FarFrustumQuadVert;
GFXVertexBufferHandle<FarFrustumQuadVert> mFarFrustumQuadVerts;
//void _createMaterials();
void _setupPerFrameParameters( const SceneRenderState *state );
void setupSGData( SceneData &data, const SceneRenderState* state, LightInfo *light );
};
#endif // _ADVANCEDLIGHTBINMANAGER_H_

View file

@ -0,0 +1,183 @@
//-----------------------------------------------------------------------------
// 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/advanced/advancedLightBufferConditioner.h"
#include "shaderGen/shaderOp.h"
#include "gfx/gfxDevice.h"
#include "core/util/safeDelete.h"
AdvancedLightBufferConditioner::~AdvancedLightBufferConditioner()
{
}
Var *AdvancedLightBufferConditioner::_conditionOutput( Var *unconditionedOutput, MultiLine *meta )
{
Var *conditionedOutput = new Var;
if(GFX->getAdapterType() == OpenGL)
conditionedOutput->setType("vec4");
else
conditionedOutput->setType("float4");
DecOp *outputDecl = new DecOp(conditionedOutput);
if(mColorFormat == RGB)
{
conditionedOutput->setName("rgbLightInfoOut");
// If this is a 16 bit integer format, scale up/down the values. All other
// formats just write out the full 0..1
if(getBufferFormat() == GFXFormatR16G16B16A16)
meta->addStatement( new GenOp( " @ = max(4.0, (float4(lightColor, specular) * NL_att + float4(bufferSample.rgb, 0.0)) / 4.0);\r\n", outputDecl ) );
else
meta->addStatement( new GenOp( " @ = float4(lightColor, specular) * NL_att + float4(bufferSample.rgb, 0.0);\r\n", outputDecl ) );
}
else
{
// Input u'v' assumed to be scaled
conditionedOutput->setName("luvLightInfoOut");
meta->addStatement( new GenOp( " @ = float4( lerp(bufferSample.xy, lightColor.xy, saturate(NL_att / bufferSample.z) * 0.5),\r\n", outputDecl ) );
meta->addStatement( new GenOp( " bufferSample.z + NL_att, bufferSample.w + saturate(specular * NL_att) );\r\n" ) );
}
return conditionedOutput;
}
Var *AdvancedLightBufferConditioner::_unconditionInput( Var *conditionedInput, MultiLine *meta )
{
if(mColorFormat == RGB)
{
if(getBufferFormat() == GFXFormatR16G16B16A16)
meta->addStatement( new GenOp( " lightColor = @.rgb * 4.0;\r\n", conditionedInput ) );
else
meta->addStatement( new GenOp( " lightColor = @.rgb;\r\n", conditionedInput ) );
meta->addStatement( new GenOp( " NL_att = dot(@.rgb, float3(0.3576, 0.7152, 0.1192));\r\n", conditionedInput ) );
}
else
{
meta->addStatement( new GenOp( " // TODO: This clamps HDR values.\r\n" ) );
meta->addStatement( new GenOp( " NL_att = @.b;\r\n", conditionedInput ) );
meta->addStatement( new GenOp( " lightColor = DecodeLuv(float3(saturate(NL_att), @.rg * 0.62));\r\n", conditionedInput ) );
}
meta->addStatement( new GenOp( " specular = max(@.a / NL_att, 0.00001f);\r\n", conditionedInput ) );
return NULL;
}
Var *AdvancedLightBufferConditioner::printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta )
{
Var *methodVar = new Var;
methodVar->setName(methodName);
DecOp *methodDecl = new DecOp(methodVar);
Var *lightColor = new Var;
lightColor->setName("lightColor");
DecOp *lightColorDecl = new DecOp(lightColor);
Var *NLAtt = new Var;
NLAtt->setName("NL_att");
DecOp *NLAttDecl = new DecOp(NLAtt);
Var *specular = new Var;
specular->setName("specular");
DecOp *specularDecl = new DecOp(specular);
Var *bufferSample = new Var;
bufferSample->setName("bufferSample");
DecOp *bufferSampleDecl = new DecOp(bufferSample);
const bool isCondition = ( methodType == ConditionerFeature::ConditionMethod );
if(GFX->getAdapterType() == OpenGL)
{
methodVar->setType(avar("%s", isCondition ? "vec4" : "void"));
lightColor->setType(avar("%s vec3", isCondition ? "in" : "out"));
NLAtt->setType(avar("%s float", isCondition ? "in" : "out"));
specular->setType(avar("%s float", isCondition ? "in" : "out"));
bufferSample->setType("in vec4");
}
else
{
methodVar->setType(avar("inline %s", isCondition ? "float4" : "void"));
lightColor->setType(avar("%s float3", isCondition ? "in" : "out"));
NLAtt->setType(avar("%s float", isCondition ? "in" : "out"));
specular->setType(avar("%s float", isCondition ? "in" : "out"));
bufferSample->setType("in float4");
}
// If this is LUV, print methods to convert RGB<->LUV as needed
if(mColorFormat == LUV)
{
if(!isCondition)
{
meta->addStatement( new GenOp( "float3 DecodeLuv(float3 Luv)\r\n{\r\n" ) );
meta->addStatement( new GenOp( " float2 xy = float2(9.0f, 4.0f) * Luv.yz / (dot(Luv.yz, float2(6.0f, -16.0f)) + 12.0f);\r\n" ) );
meta->addStatement( new GenOp( " float Ld = Luv.x;\r\n" ) );
meta->addStatement( new GenOp( " float3 XYZ = float3(xy.x, Ld, 1.0f - xy.x - xy.y);\r\n" ) );
meta->addStatement( new GenOp( " XYZ.xz = XYZ.xz * Ld / xy.y;\r\n" ) );
meta->addStatement( new GenOp( " const float3x3 XYZ2RGB =\r\n" ) );
meta->addStatement( new GenOp( " {\r\n" ) );
meta->addStatement( new GenOp( " 2.5651f, -1.1665f, -0.3986f,\r\n" ) );
meta->addStatement( new GenOp( " -1.0217f, 1.9777f, 0.0439f,\r\n" ) );
meta->addStatement( new GenOp( " 0.0753f, -0.2543f, 1.1892f\r\n" ) );
meta->addStatement( new GenOp( " };\r\n" ) );
meta->addStatement( new GenOp( " return mul(XYZ2RGB, XYZ);\r\n" ) );
meta->addStatement( new GenOp( "}\r\n\r\n" ) );
}
else
{
// Shouldn't need this
}
}
// Method header and opening bracket
if(isCondition)
{
// All parameters are input parameters, and the return value is float4.
// If this is an LUV buffer format, than the previous pixel value is needed
// for interpolation.
meta->addStatement( new GenOp( "@(@, @, @, @)\r\n", methodDecl, lightColorDecl, NLAttDecl, specularDecl, bufferSampleDecl ) );
}
else
{
// Sample as input, parameters as output. Void return.
meta->addStatement( new GenOp( "@(@, @, @, @)\r\n", methodDecl, bufferSampleDecl, lightColorDecl, NLAttDecl, specularDecl ) );
}
meta->addStatement( new GenOp( "{\r\n" ) );
// We don't use this way of passing var's around, so this should cause a crash
// if something uses this improperly
return ( isCondition ? NULL : bufferSample );
}
void AdvancedLightBufferConditioner::printMethodFooter( ConditionerFeature::MethodType methodType, Var *retVar, Stream &stream, MultiLine *meta )
{
// Return and closing bracket
if(methodType == ConditionerFeature::ConditionMethod)
meta->addStatement( new GenOp( "\r\n return @;\r\n", retVar ) );
// Uncondition will assign output parameters
meta->addStatement( new GenOp( "}\r\n" ) );
}

View file

@ -0,0 +1,65 @@
//-----------------------------------------------------------------------------
// 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 _ADVANCED_LIGHTBUFFER_CONDITIONER_H_
#define _ADVANCED_LIGHTBUFFER_CONDITIONER_H_
#ifndef _CONDITIONER_BASE_H_
#include "shaderGen/conditionerFeature.h"
#endif
class AdvancedLightBufferConditioner : public ConditionerFeature
{
typedef ConditionerFeature Parent;
public:
enum ColorFormat
{
RGB,
LUV
};
public:
AdvancedLightBufferConditioner(const GFXFormat bufferFormat, const ColorFormat colorFormat)
: Parent(bufferFormat), mColorFormat(colorFormat)
{
}
virtual ~AdvancedLightBufferConditioner();
virtual String getName()
{
return String("Light Buffer Conditioner ") + String( mColorFormat == RGB ? "[RGB]" : "[LUV]" );
}
protected:
ColorFormat mColorFormat;
virtual Var *_conditionOutput( Var *unconditionedOutput, MultiLine *meta );
virtual Var *_unconditionInput( Var *conditionedInput, MultiLine *meta );
virtual Var *printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta );
virtual void printMethodFooter( MethodType methodType, Var *retVar, Stream &stream, MultiLine *meta );
};
#endif // _ADVANCED_LIGHTBUFFER_CONDITIONER_H_

View file

@ -0,0 +1,671 @@
//-----------------------------------------------------------------------------
// 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/advanced/advancedLightManager.h"
#include "lighting/advanced/advancedLightBinManager.h"
#include "lighting/advanced/advancedLightingFeatures.h"
#include "lighting/shadowMap/shadowMapManager.h"
#include "lighting/shadowMap/lightShadowMap.h"
#include "lighting/common/sceneLighting.h"
#include "lighting/common/lightMapParams.h"
#include "core/util/safeDelete.h"
#include "renderInstance/renderPrePassMgr.h"
#include "materials/materialManager.h"
#include "math/util/sphereMesh.h"
#include "console/consoleTypes.h"
#include "scene/sceneRenderState.h"
ImplementEnumType( ShadowType,
"\n\n"
"@ingroup AdvancedLighting" )
{ ShadowType_Spot, "Spot" },
{ ShadowType_PSSM, "PSSM" },
{ ShadowType_Paraboloid, "Paraboloid" },
{ ShadowType_DualParaboloidSinglePass, "DualParaboloidSinglePass" },
{ ShadowType_DualParaboloid, "DualParaboloid" },
{ ShadowType_CubeMap, "CubeMap" },
EndImplementEnumType;
AdvancedLightManager AdvancedLightManager::smSingleton;
AdvancedLightManager::AdvancedLightManager()
: LightManager( "Advanced Lighting", "ADVLM" )
{
mLightBinManager = NULL;
mLastShader = NULL;
mAvailableSLInterfaces = NULL;
}
AdvancedLightManager::~AdvancedLightManager()
{
mLastShader = NULL;
mLastConstants = NULL;
for (LightConstantMap::Iterator i = mConstantLookup.begin(); i != mConstantLookup.end(); i++)
{
if (i->value)
SAFE_DELETE(i->value);
}
mConstantLookup.clear();
}
bool AdvancedLightManager::isCompatible() const
{
// TODO: We need at least 3.0 shaders at the moment
// but this should be relaxed to 2.0 soon.
if ( GFX->getPixelShaderVersion() < 3.0 )
return false;
// TODO: Test for the necessary texture formats!
return true;
}
void AdvancedLightManager::activate( SceneManager *sceneManager )
{
Parent::activate( sceneManager );
GFXShader::addGlobalMacro( "TORQUE_ADVANCED_LIGHTING" );
sceneManager->setPostEffectFog( true );
SHADOWMGR->activate();
// Find a target format that supports blending...
// we prefer the floating point format if it works.
Vector<GFXFormat> formats;
formats.push_back( GFXFormatR16G16B16A16F );
formats.push_back( GFXFormatR16G16B16A16 );
GFXFormat blendTargetFormat = GFX->selectSupportedFormat( &GFXDefaultRenderTargetProfile,
formats,
true,
true,
false );
mLightBinManager = new AdvancedLightBinManager( this, SHADOWMGR, blendTargetFormat );
mLightBinManager->assignName( "AL_LightBinMgr" );
// First look for the prepass bin...
RenderPrePassMgr *prePassBin = _findPrePassRenderBin();
// If we didn't find the prepass bin then add one.
if ( !prePassBin )
{
prePassBin = new RenderPrePassMgr( true, blendTargetFormat );
prePassBin->assignName( "AL_PrePassBin" );
prePassBin->registerObject();
getSceneManager()->getDefaultRenderPass()->addManager( prePassBin );
mPrePassRenderBin = prePassBin;
}
// Tell the material manager that prepass is enabled.
MATMGR->setPrePassEnabled( true );
// Insert our light bin manager.
mLightBinManager->setRenderOrder( prePassBin->getRenderOrder() + 0.01f );
getSceneManager()->getDefaultRenderPass()->addManager( mLightBinManager );
AdvancedLightingFeatures::registerFeatures(mPrePassRenderBin->getTargetFormat(), mLightBinManager->getTargetFormat());
// Last thing... let everyone know we're active.
smActivateSignal.trigger( getId(), true );
}
void AdvancedLightManager::deactivate()
{
Parent::deactivate();
GFXShader::removeGlobalMacro( "TORQUE_ADVANCED_LIGHTING" );
// Release our bin manager... it will take care of
// removing itself from the render passes.
if( mLightBinManager )
{
mLightBinManager->MRTLightmapsDuringPrePass(false);
mLightBinManager->deleteObject();
}
mLightBinManager = NULL;
if ( mPrePassRenderBin )
mPrePassRenderBin->deleteObject();
mPrePassRenderBin = NULL;
SHADOWMGR->deactivate();
mLastShader = NULL;
mLastConstants = NULL;
for (LightConstantMap::Iterator i = mConstantLookup.begin(); i != mConstantLookup.end(); i++)
{
if (i->value)
SAFE_DELETE(i->value);
}
mConstantLookup.clear();
mSphereGeometry = NULL;
mSphereIndices = NULL;
mConeGeometry = NULL;
mConeIndices = NULL;
AdvancedLightingFeatures::unregisterFeatures();
// Now let everyone know we've deactivated.
smActivateSignal.trigger( getId(), false );
}
void AdvancedLightManager::_addLightInfoEx( LightInfo *lightInfo )
{
lightInfo->addExtended( new ShadowMapParams( lightInfo ) );
lightInfo->addExtended( new LightMapParams( lightInfo ) );
}
void AdvancedLightManager::_initLightFields()
{
#define DEFINE_LIGHT_FIELD( var, type, enum_ ) \
static inline const char* _get##var##Field( void *obj, const char *data ) \
{ \
ShadowMapParams *p = _getShadowMapParams( obj ); \
if ( p ) \
return Con::getData( type, &p->var, 0, enum_ ); \
else \
return ""; \
} \
\
static inline bool _set##var##Field( void *object, const char *index, const char *data ) \
{ \
ShadowMapParams *p = _getShadowMapParams( object ); \
if ( p ) \
{ \
Con::setData( type, &p->var, 0, 1, &data, enum_ ); \
p->_validate(); \
} \
return false; \
}
#define DEFINE_LIGHTMAP_FIELD( var, type, enum_ ) \
static inline const char* _get##var##Field( void *obj, const char *data ) \
{ \
LightMapParams *p = _getLightMapParams( obj ); \
if ( p ) \
return Con::getData( type, &p->var, 0, enum_ ); \
else \
return ""; \
} \
\
static inline bool _set##var##Field( void *object, const char *index, const char *data ) \
{ \
LightMapParams *p = _getLightMapParams( object ); \
if ( p ) \
{ \
Con::setData( type, &p->var, 0, 1, &data, enum_ ); \
} \
return false; \
}
#define ADD_LIGHT_FIELD( field, type, var, desc ) \
ConsoleObject::addProtectedField( field, type, 0, \
&Dummy::_set##var##Field, &Dummy::_get##var##Field, desc )
// Our dummy adaptor class which we hide in here
// to keep from poluting the global namespace.
class Dummy
{
protected:
static inline ShadowMapParams* _getShadowMapParams( void *obj )
{
ISceneLight *sceneLight = dynamic_cast<ISceneLight*>( (SimObject*)obj );
if ( sceneLight )
{
LightInfo *lightInfo = sceneLight->getLight();
if ( lightInfo )
return lightInfo->getExtended<ShadowMapParams>();
}
return NULL;
}
static inline LightMapParams* _getLightMapParams( void *obj )
{
ISceneLight *sceneLight = dynamic_cast<ISceneLight*>( (SimObject*)obj );
if ( sceneLight )
{
LightInfo *lightInfo = sceneLight->getLight();
if ( lightInfo )
return lightInfo->getExtended<LightMapParams>();
}
return NULL;
}
public:
DEFINE_LIGHT_FIELD( attenuationRatio, TypePoint3F, NULL );
DEFINE_LIGHT_FIELD( shadowType, TYPEID< ShadowType >(), ConsoleBaseType::getType( TYPEID< ShadowType >() )->getEnumTable() );
DEFINE_LIGHT_FIELD( texSize, TypeS32, NULL );
DEFINE_LIGHT_FIELD( cookie, TypeStringFilename, NULL );
DEFINE_LIGHT_FIELD( numSplits, TypeS32, NULL );
DEFINE_LIGHT_FIELD( logWeight, TypeF32, NULL );
DEFINE_LIGHT_FIELD( overDarkFactor, TypePoint4F, NULL);
DEFINE_LIGHT_FIELD( shadowDistance, TypeF32, NULL );
DEFINE_LIGHT_FIELD( shadowSoftness, TypeF32, NULL );
DEFINE_LIGHT_FIELD( fadeStartDist, TypeF32, NULL );
DEFINE_LIGHT_FIELD( lastSplitTerrainOnly, TypeBool, NULL );
DEFINE_LIGHTMAP_FIELD( representedInLightmap, TypeBool, NULL );
DEFINE_LIGHTMAP_FIELD( shadowDarkenColor, TypeColorF, NULL );
DEFINE_LIGHTMAP_FIELD( includeLightmappedGeometryInShadow, TypeBool, NULL );
};
ConsoleObject::addGroup( "Advanced Lighting" );
ADD_LIGHT_FIELD( "attenuationRatio", TypePoint3F, attenuationRatio,
"The proportions of constant, linear, and quadratic attenuation to use for "
"the falloff for point and spot lights." );
ADD_LIGHT_FIELD( "shadowType", TYPEID< ShadowType >(), shadowType,
"The type of shadow to use on this light." );
ADD_LIGHT_FIELD( "cookie", TypeStringFilename, cookie,
"A custom pattern texture which is projected from the light." );
ADD_LIGHT_FIELD( "texSize", TypeS32, texSize,
"The texture size of the shadow map." );
ADD_LIGHT_FIELD( "overDarkFactor", TypePoint4F, overDarkFactor,
"The ESM shadow darkening factor");
ADD_LIGHT_FIELD( "shadowDistance", TypeF32, shadowDistance,
"The distance from the camera to extend the PSSM shadow." );
ADD_LIGHT_FIELD( "shadowSoftness", TypeF32, shadowSoftness,
"" );
ADD_LIGHT_FIELD( "numSplits", TypeS32, numSplits,
"The logrithmic PSSM split distance factor." );
ADD_LIGHT_FIELD( "logWeight", TypeF32, logWeight,
"The logrithmic PSSM split distance factor." );
ADD_LIGHT_FIELD( "fadeStartDistance", TypeF32, fadeStartDist,
"Start fading shadows out at this distance. 0 = auto calculate this distance.");
ADD_LIGHT_FIELD( "lastSplitTerrainOnly", TypeBool, lastSplitTerrainOnly,
"This toggles only terrain being rendered to the last split of a PSSM shadow map.");
ConsoleObject::endGroup( "Advanced Lighting" );
ConsoleObject::addGroup( "Advanced Lighting Lightmap" );
ADD_LIGHT_FIELD( "representedInLightmap", TypeBool, representedInLightmap,
"This light is represented in lightmaps (static light, default: false)");
ADD_LIGHT_FIELD( "shadowDarkenColor", TypeColorF, shadowDarkenColor,
"The color that should be used to multiply-blend dynamic shadows onto lightmapped geometry (ignored if 'representedInLightmap' is false)");
ADD_LIGHT_FIELD( "includeLightmappedGeometryInShadow", TypeBool, includeLightmappedGeometryInShadow,
"This light should render lightmapped geometry during its shadow-map update (ignored if 'representedInLightmap' is false)");
ConsoleObject::endGroup( "Advanced Lighting Lightmap" );
#undef DEFINE_LIGHT_FIELD
#undef ADD_LIGHT_FIELD
}
void AdvancedLightManager::setLightInfo( ProcessedMaterial *pmat,
const Material *mat,
const SceneData &sgData,
const SceneRenderState *state,
U32 pass,
GFXShaderConstBuffer *shaderConsts)
{
// Skip this if we're rendering from the prepass bin.
if ( sgData.binType == SceneData::PrePassBin )
return;
PROFILE_SCOPE(AdvancedLightManager_setLightInfo);
LightingShaderConstants *lsc = getLightingShaderConstants(shaderConsts);
LightShadowMap *lsm = SHADOWMGR->getCurrentShadowMap();
LightInfo *light;
if ( lsm )
light = lsm->getLightInfo();
else
{
light = sgData.lights[0];
if ( !light )
light = getDefaultLight();
}
// NOTE: If you encounter a crash from this point forward
// while setting a shader constant its probably because the
// mConstantLookup has bad shaders/constants in it.
//
// This is a known crash bug that can occur if materials/shaders
// are reloaded and the light manager is not reset.
//
// We should look to fix this by clearing the table.
// Update the forward shading light constants.
_update4LightConsts( sgData,
lsc->mLightPositionSC,
lsc->mLightDiffuseSC,
lsc->mLightAmbientSC,
lsc->mLightInvRadiusSqSC,
lsc->mLightSpotDirSC,
lsc->mLightSpotAngleSC,
lsc->mLightSpotFalloffSC,
shaderConsts );
if ( lsm && light->getCastShadows() )
{
if ( lsc->mWorldToLightProjSC->isValid() )
shaderConsts->set( lsc->mWorldToLightProjSC,
lsm->getWorldToLightProj(),
lsc->mWorldToLightProjSC->getType() );
if ( lsc->mViewToLightProjSC->isValid() )
{
// TODO: Should probably cache these results and
// not do this mul here on every material that needs
// this transform.
shaderConsts->set( lsc->mViewToLightProjSC,
lsm->getWorldToLightProj() * state->getCameraTransform(),
lsc->mViewToLightProjSC->getType() );
}
shaderConsts->setSafe( lsc->mShadowMapSizeSC, 1.0f / (F32)lsm->getTexSize() );
// Do this last so that overrides can properly override parameters previously set
lsm->setShaderParameters(shaderConsts, lsc);
}
else
{
if ( lsc->mViewToLightProjSC->isValid() )
{
// TODO: Should probably cache these results and
// not do this mul here on every material that needs
// this transform.
MatrixF proj;
light->getWorldToLightProj( &proj );
shaderConsts->set( lsc->mViewToLightProjSC,
proj * state->getCameraTransform(),
lsc->mViewToLightProjSC->getType() );
}
}
}
void AdvancedLightManager::registerGlobalLight(LightInfo *light, SimObject *obj)
{
Parent::registerGlobalLight( light, obj );
// Pass the volume lights to the bin manager.
if ( mLightBinManager &&
( light->getType() == LightInfo::Point ||
light->getType() == LightInfo::Spot ) )
mLightBinManager->addLight( light );
}
void AdvancedLightManager::unregisterAllLights()
{
Parent::unregisterAllLights();
if ( mLightBinManager )
mLightBinManager->clear();
}
bool AdvancedLightManager::setTextureStage( const SceneData &sgData,
const U32 currTexFlag,
const U32 textureSlot,
GFXShaderConstBuffer *shaderConsts,
ShaderConstHandles *handles )
{
LightShadowMap* lsm = SHADOWMGR->getCurrentShadowMap();
// Assign Shadowmap, if it exists
LightingShaderConstants* lsc = getLightingShaderConstants(shaderConsts);
if ( !lsc )
return false;
if ( lsm && lsm->getLightInfo()->getCastShadows() )
return lsm->setTextureStage( currTexFlag, lsc );
if ( currTexFlag == Material::DynamicLight )
{
S32 reg = lsc->mShadowMapSC->getSamplerRegister();
if ( reg != -1 )
GFX->setTexture( reg, GFXTexHandle::ONE );
return true;
}
else if ( currTexFlag == Material::DynamicLightMask )
{
S32 reg = lsc->mCookieMapSC->getSamplerRegister();
if ( reg != -1 && sgData.lights[0] )
{
ShadowMapParams *p = sgData.lights[0]->getExtended<ShadowMapParams>();
if ( lsc->mCookieMapSC->getType() == GFXSCT_SamplerCube )
GFX->setCubeTexture( reg, p->getCookieCubeTex() );
else
GFX->setTexture( reg, p->getCookieTex() );
}
return true;
}
return false;
}
LightingShaderConstants* AdvancedLightManager::getLightingShaderConstants(GFXShaderConstBuffer* buffer)
{
if ( !buffer )
return NULL;
PROFILE_SCOPE( AdvancedLightManager_GetLightingShaderConstants );
GFXShader* shader = buffer->getShader();
// Check to see if this is the same shader, we'll get hit repeatedly by
// the same one due to the render bin loops.
if ( mLastShader.getPointer() != shader )
{
LightConstantMap::Iterator iter = mConstantLookup.find(shader);
if ( iter != mConstantLookup.end() )
{
mLastConstants = iter->value;
}
else
{
LightingShaderConstants* lsc = new LightingShaderConstants();
mConstantLookup[shader] = lsc;
mLastConstants = lsc;
}
// Set our new shader
mLastShader = shader;
}
// Make sure that our current lighting constants are initialized
if (!mLastConstants->mInit)
mLastConstants->init(shader);
return mLastConstants;
}
GFXVertexBufferHandle<AdvancedLightManager::LightVertex> AdvancedLightManager::getSphereMesh(U32 &outNumPrimitives, GFXPrimitiveBuffer *&outPrimitives)
{
static SphereMesh sSphereMesh;
if( mSphereGeometry.isNull() )
{
const SphereMesh::TriangleMesh * sphereMesh = sSphereMesh.getMesh(3);
S32 numPoly = sphereMesh->numPoly;
mSpherePrimitiveCount = 0;
mSphereGeometry.set(GFX, numPoly*3, GFXBufferTypeStatic);
mSphereGeometry.lock();
S32 vertexIndex = 0;
for (S32 i=0; i<numPoly; i++)
{
mSpherePrimitiveCount++;
mSphereGeometry[vertexIndex].point = sphereMesh->poly[i].pnt[0];
mSphereGeometry[vertexIndex].color = ColorI::WHITE;
vertexIndex++;
mSphereGeometry[vertexIndex].point = sphereMesh->poly[i].pnt[1];
mSphereGeometry[vertexIndex].color = ColorI::WHITE;
vertexIndex++;
mSphereGeometry[vertexIndex].point = sphereMesh->poly[i].pnt[2];
mSphereGeometry[vertexIndex].color = ColorI::WHITE;
vertexIndex++;
}
mSphereGeometry.unlock();
}
outNumPrimitives = mSpherePrimitiveCount;
outPrimitives = NULL; // For now
return mSphereGeometry;
}
GFXVertexBufferHandle<AdvancedLightManager::LightVertex> AdvancedLightManager::getConeMesh(U32 &outNumPrimitives, GFXPrimitiveBuffer *&outPrimitives )
{
static const Point2F circlePoints[] =
{
Point2F(0.707107f, 0.707107f),
Point2F(0.923880f, 0.382683f),
Point2F(1.000000f, 0.000000f),
Point2F(0.923880f, -0.382684f),
Point2F(0.707107f, -0.707107f),
Point2F(0.382683f, -0.923880f),
Point2F(0.000000f, -1.000000f),
Point2F(-0.382683f, -0.923880f),
Point2F(-0.707107f, -0.707107f),
Point2F(-0.923880f, -0.382684f),
Point2F(-1.000000f, 0.000000f),
Point2F(-0.923879f, 0.382684f),
Point2F(-0.707107f, 0.707107f),
Point2F(-0.382683f, 0.923880f),
Point2F(0.000000f, 1.000000f),
Point2F(0.382684f, 0.923879f)
};
const S32 numPoints = sizeof(circlePoints)/sizeof(Point2F);
if ( mConeGeometry.isNull() )
{
mConeGeometry.set(GFX, numPoints + 1, GFXBufferTypeStatic);
mConeGeometry.lock();
mConeGeometry[0].point = Point3F(0.0f,0.0f,0.0f);
for (S32 i=1; i<numPoints + 1; i++)
{
S32 imod = (i - 1) % numPoints;
mConeGeometry[i].point = Point3F(circlePoints[imod].x,circlePoints[imod].y, -1.0f);
mConeGeometry[i].color = ColorI::WHITE;
}
mConeGeometry.unlock();
mConePrimitiveCount = numPoints * 2 - 1;
// Now build the index buffer
mConeIndices.set(GFX, mConePrimitiveCount * 3, mConePrimitiveCount, GFXBufferTypeStatic);
U16 *idx = NULL;
mConeIndices.lock( &idx );
// Build the cone
U32 idxIdx = 0;
for( int i = 1; i < numPoints + 1; i++ )
{
idx[idxIdx++] = 0; // Triangles on cone start at top point
idx[idxIdx++] = i;
idx[idxIdx++] = ( i + 1 > numPoints ) ? 1 : i + 1;
}
// Build the bottom of the cone (reverse winding order)
for( int i = 1; i < numPoints - 1; i++ )
{
idx[idxIdx++] = 1;
idx[idxIdx++] = i + 2;
idx[idxIdx++] = i + 1;
}
mConeIndices.unlock();
}
outNumPrimitives = mConePrimitiveCount;
outPrimitives = mConeIndices.getPointer();
return mConeGeometry;
}
LightShadowMap* AdvancedLightManager::findShadowMapForObject( SimObject *object )
{
if ( !object )
return NULL;
ISceneLight *sceneLight = dynamic_cast<ISceneLight*>( object );
if ( !sceneLight || !sceneLight->getLight() )
return NULL;
return sceneLight->getLight()->getExtended<ShadowMapParams>()->getShadowMap();
}
ConsoleFunction( setShadowVizLight, const char*, 2, 2, "" )
{
static const String DebugTargetName( "AL_ShadowVizTexture" );
NamedTexTarget *target = NamedTexTarget::find( DebugTargetName );
if ( target )
target->unregister();
AdvancedLightManager *lm = dynamic_cast<AdvancedLightManager*>( LIGHTMGR );
if ( !lm )
return 0;
SimObject *object;
Sim::findObject( argv[1], object );
LightShadowMap *lightShadowMap = lm->findShadowMapForObject( object );
if ( !lightShadowMap || !lightShadowMap->getTexture() )
return 0;
lightShadowMap->setDebugTarget( DebugTargetName );
GFXTextureObject *texObject = lightShadowMap->getTexture();
const Point3I &size = texObject->getSize();
F32 aspect = (F32)size.x / (F32)size.y;
char *result = Con::getReturnBuffer( 64 );
dSprintf( result, 64, "%d %d %g", size.x, size.y, aspect );
return result;
}

View file

@ -0,0 +1,143 @@
//-----------------------------------------------------------------------------
// 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 _ADVANCEDLIGHTMANAGER_H_
#define _ADVANCEDLIGHTMANAGER_H_
#ifndef _SIMOBJECT_H_
#include "console/simObject.h"
#endif
#ifndef _LIGHTMANAGER_H_
#include "lighting/lightManager.h"
#endif
#ifndef _LIGHTINFO_H_
#include "lighting/lightInfo.h"
#endif
#ifndef _GFXTEXTUREHANDLE_H_
#include "gfx/gfxTextureHandle.h"
#endif
#ifndef _GFXTARGET_H_
#include "gfx/gfxTarget.h"
#endif
#ifndef _TDICTIONARY_H_
#include "core/util/tDictionary.h"
#endif
#ifndef _LIGHTSHADOWMAP_H_
#include "lighting/shadowMap/lightShadowMap.h"
#endif
#ifndef _GFXPRIMITIVEBUFFER_H_
#include "gfx/gfxPrimitiveBuffer.h"
#endif
#ifndef _GFXVERTEXBUFFER_H_
#include "gfx/gfxVertexBuffer.h"
#endif
class AvailableSLInterfaces;
class AdvancedLightBinManager;
class RenderPrePassMgr;
class BaseMatInstance;
class MaterialParameters;
class MaterialParameterHandle;
class GFXShader;
class GFXShaderConstHandle;
class ShadowMapManager;
class AdvancedLightManager : public LightManager
{
typedef LightManager Parent;
public:
/// Return the lightBinManager for this light manager.
AdvancedLightBinManager* getLightBinManager() { return mLightBinManager; }
// LightManager
virtual bool isCompatible() const;
virtual void activate( SceneManager *sceneManager );
virtual void deactivate();
virtual void registerGlobalLight(LightInfo *light, SimObject *obj);
virtual void unregisterAllLights();
virtual void setLightInfo( ProcessedMaterial *pmat,
const Material *mat,
const SceneData &sgData,
const SceneRenderState *state,
U32 pass,
GFXShaderConstBuffer *shaderConsts );
virtual bool setTextureStage( const SceneData &sgData,
const U32 currTexFlag,
const U32 textureSlot,
GFXShaderConstBuffer *shaderConsts,
ShaderConstHandles *handles );
typedef GFXVertexPC LightVertex;
GFXVertexBufferHandle<LightVertex> getSphereMesh(U32 &outNumPrimitives, GFXPrimitiveBuffer *&outPrimitives );
GFXVertexBufferHandle<LightVertex> getConeMesh(U32 &outNumPrimitives, GFXPrimitiveBuffer *&outPrimitives );
LightShadowMap* findShadowMapForObject( SimObject *object );
protected:
// LightManager
virtual void _addLightInfoEx( LightInfo *lightInfo );
virtual void _initLightFields();
/// A simple protected singleton. Use LightManager::findByName()
/// to access this light manager.
/// @see LightManager::findByName()
static AdvancedLightManager smSingleton;
// These are protected because we're a singleton and
// no one else should be creating us!
AdvancedLightManager();
virtual ~AdvancedLightManager();
SimObjectPtr<AdvancedLightBinManager> mLightBinManager;
SimObjectPtr<RenderPrePassMgr> mPrePassRenderBin;
LightConstantMap mConstantLookup;
GFXShaderRef mLastShader;
LightingShaderConstants* mLastConstants;
// Convex geometry for lights
GFXVertexBufferHandle<LightVertex> mSphereGeometry;
GFXPrimitiveBufferHandle mSphereIndices;
U32 mSpherePrimitiveCount;
GFXVertexBufferHandle<LightVertex> mConeGeometry;
GFXPrimitiveBufferHandle mConeIndices;
U32 mConePrimitiveCount;
LightingShaderConstants* getLightingShaderConstants(GFXShaderConstBuffer* shader);
};
#endif // _ADVANCEDLIGHTMANAGER_H_

View file

@ -0,0 +1,101 @@
//-----------------------------------------------------------------------------
// 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/advanced/advancedLightingFeatures.h"
#include "shaderGen/featureMgr.h"
#include "gfx/gfxStringEnumTranslate.h"
#include "materials/materialParameters.h"
#include "materials/materialFeatureTypes.h"
#include "materials/matTextureTarget.h"
#include "gfx/gfxDevice.h"
#include "core/util/safeDelete.h"
#ifndef TORQUE_OS_MAC
# include "lighting/advanced/hlsl/gBufferConditionerHLSL.h"
# include "lighting/advanced/hlsl/advancedLightingFeaturesHLSL.h"
#else
# include "lighting/advanced/glsl/gBufferConditionerGLSL.h"
# include "lighting/advanced/glsl/advancedLightingFeaturesGLSL.h"
#endif
bool AdvancedLightingFeatures::smFeaturesRegistered = false;
void AdvancedLightingFeatures::registerFeatures( const GFXFormat &prepassTargetFormat, const GFXFormat &lightInfoTargetFormat )
{
AssertFatal( !smFeaturesRegistered, "AdvancedLightingFeatures::registerFeatures() - Features already registered. Bad!" );
// If we ever need this...
TORQUE_UNUSED(lightInfoTargetFormat);
ConditionerFeature *cond = NULL;
if(GFX->getAdapterType() == OpenGL)
{
#ifdef TORQUE_OS_MAC
cond = new GBufferConditionerGLSL( prepassTargetFormat );
FEATUREMGR->registerFeature(MFT_PrePassConditioner, cond);
FEATUREMGR->registerFeature(MFT_RTLighting, new DeferredRTLightingFeatGLSL());
FEATUREMGR->registerFeature(MFT_NormalMap, new DeferredBumpFeatGLSL());
FEATUREMGR->registerFeature(MFT_PixSpecular, new DeferredPixelSpecularGLSL());
FEATUREMGR->registerFeature(MFT_MinnaertShading, new DeferredMinnaertGLSL());
FEATUREMGR->registerFeature(MFT_SubSurface, new DeferredSubSurfaceGLSL());
#endif
}
else
{
#ifndef TORQUE_OS_MAC
cond = new GBufferConditionerHLSL( prepassTargetFormat, GBufferConditionerHLSL::ViewSpace );
FEATUREMGR->registerFeature(MFT_PrePassConditioner, cond);
FEATUREMGR->registerFeature(MFT_RTLighting, new DeferredRTLightingFeatHLSL());
FEATUREMGR->registerFeature(MFT_NormalMap, new DeferredBumpFeatHLSL());
FEATUREMGR->registerFeature(MFT_PixSpecular, new DeferredPixelSpecularHLSL());
FEATUREMGR->registerFeature(MFT_MinnaertShading, new DeferredMinnaertHLSL());
FEATUREMGR->registerFeature(MFT_SubSurface, new DeferredSubSurfaceHLSL());
#endif
}
NamedTexTarget *target = NamedTexTarget::find( "prepass" );
if ( target )
target->setConditioner( cond );
smFeaturesRegistered = true;
}
void AdvancedLightingFeatures::unregisterFeatures()
{
NamedTexTarget *target = NamedTexTarget::find( "prepass" );
if ( target )
target->setConditioner( NULL );
FEATUREMGR->unregisterFeature(MFT_PrePassConditioner);
FEATUREMGR->unregisterFeature(MFT_RTLighting);
FEATUREMGR->unregisterFeature(MFT_NormalMap);
FEATUREMGR->unregisterFeature(MFT_PixSpecular);
FEATUREMGR->unregisterFeature(MFT_MinnaertShading);
FEATUREMGR->unregisterFeature(MFT_SubSurface);
smFeaturesRegistered = false;
}

View file

@ -0,0 +1,42 @@
//-----------------------------------------------------------------------------
// 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 _ADVANCEDLIGHTINGFEATURES_H_
#define _ADVANCEDLIGHTINGFEATURES_H_
#ifndef _GFXENUMS_H_
#include "gfx/gfxEnums.h"
#endif
class AdvancedLightingFeatures
{
public:
static void registerFeatures( const GFXFormat &prepassTargetFormat, const GFXFormat &lightInfoTargetFormat );
static void unregisterFeatures();
private:
static bool smFeaturesRegistered;
};
#endif // _ADVANCEDLIGHTINGFEATURES_H_

View file

@ -0,0 +1,725 @@
//-----------------------------------------------------------------------------
// 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/advanced/glsl/advancedLightingFeaturesGLSL.h"
#include "lighting/advanced/advancedLightBinManager.h"
#include "shaderGen/langElement.h"
#include "shaderGen/shaderOp.h"
#include "shaderGen/conditionerFeature.h"
#include "renderInstance/renderPrePassMgr.h"
#include "materials/processedMaterial.h"
#include "materials/materialFeatureTypes.h"
void DeferredRTLightingFeatGLSL::processPixMacros( Vector<GFXShaderMacro> &macros,
const MaterialFeatureData &fd )
{
/// TODO: This needs to be done via some sort of material
/// feature and not just allow all translucent elements to
/// read from the light prepass.
/*
if ( fd.features[MFT_IsTranslucent] )
{
Parent::processPixMacros( macros, fd );
return;
}
*/
// Pull in the uncondition method for the light info buffer
NamedTexTarget *texTarget = NamedTexTarget::find( AdvancedLightBinManager::smBufferName );
if ( texTarget && texTarget->getConditioner() )
{
ConditionerMethodDependency *unconditionMethod = texTarget->getConditioner()->getConditionerMethodDependency(ConditionerFeature::UnconditionMethod);
unconditionMethod->createMethodMacro( String::ToLower( AdvancedLightBinManager::smBufferName ) + "Uncondition", macros );
addDependency(unconditionMethod);
}
}
void DeferredRTLightingFeatGLSL::processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
/// TODO: This needs to be done via some sort of material
/// feature and not just allow all translucent elements to
/// read from the light prepass.
/*
if ( fd.features[MFT_IsTranslucent] )
{
Parent::processVert( componentList, fd );
return;
}
*/
// Pass screen space position to pixel shader to compute a full screen buffer uv
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *ssPos = connectComp->getElement( RT_TEXCOORD );
ssPos->setName( "screenspacePos" );
ssPos->setType( "vec4" );
// Var *outPosition = (Var*) LangElement::find( "hpos" );
// AssertFatal( outPosition, "No hpos, ohnoes." );
output = new GenOp( " @ = gl_Position;\r\n", ssPos );
}
void DeferredRTLightingFeatGLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
/// TODO: This needs to be done via some sort of material
/// feature and not just allow all translucent elements to
/// read from the light prepass.
/*
if ( fd.features[MFT_IsTranslucent] )
{
Parent::processPix( componentList, fd );
return;
}
*/
MultiLine *meta = new MultiLine;
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *ssPos = connectComp->getElement( RT_TEXCOORD );
ssPos->setName( "screenspacePos" );
ssPos->setType( "vec4" );
Var *uvScene = new Var;
uvScene->setType( "vec2" );
uvScene->setName( "uvScene" );
LangElement *uvSceneDecl = new DecOp( uvScene );
Var *rtParams = (Var*) LangElement::find( "renderTargetParams" );
if( !rtParams )
{
rtParams = new Var;
rtParams->setType( "vec4" );
rtParams->setName( "renderTargetParams" );
rtParams->uniform = true;
rtParams->constSortPos = cspPass;
}
meta->addStatement( new GenOp( " @ = @.xy / @.w;\r\n", uvSceneDecl, ssPos, ssPos ) ); // get the screen coord... its -1 to +1
meta->addStatement( new GenOp( " @ = ( @ + 1.0 ) / 2.0;\r\n", uvScene, uvScene ) ); // get the screen coord to 0 to 1
meta->addStatement( new GenOp( " @ = ( @ * @.zw ) + @.xy;\r\n", uvScene, uvScene, rtParams, rtParams) ); // scale it down and offset it to the rt size
Var *lightInfoSamp = new Var;
lightInfoSamp->setType( "vec4" );
lightInfoSamp->setName( "lightInfoSample" );
// create texture var
Var *lightInfoBuffer = new Var;
lightInfoBuffer->setType( "sampler2D" );
lightInfoBuffer->setName( "lightInfoBuffer" );
lightInfoBuffer->uniform = true;
lightInfoBuffer->sampler = true;
lightInfoBuffer->constNum = Var::getTexUnitNum(); // used as texture unit num here
String unconditionLightInfo = String::ToLower( AdvancedLightBinManager::smBufferName ) + "Uncondition";
meta->addStatement( new GenOp( " vec3 d_lightcolor;\r\n" ) );
meta->addStatement( new GenOp( " float d_NL_Att;\r\n" ) );
meta->addStatement( new GenOp( " float d_specular;\r\n" ) );
meta->addStatement( new GenOp( avar( " %s(texture2D(@, @), d_lightcolor, d_NL_Att, d_specular);\r\n", unconditionLightInfo.c_str() ),
lightInfoBuffer, uvScene ) );
Var *rtShading = new Var;
rtShading->setType( "vec4" );
rtShading->setName( "rtShading" );
LangElement *rtShadingDecl = new DecOp( rtShading );
meta->addStatement( new GenOp( " @ = vec4( d_lightcolor, 1.0 );\r\n", rtShadingDecl ) );
// This is kind of weak sauce
if( !fd.features[MFT_SubSurface] && !fd.features[MFT_ToneMap] && !fd.features[MFT_LightMap] )
meta->addStatement( new GenOp( " @;\r\n", assignColor( rtShading, Material::Mul ) ) );
output = meta;
}
ShaderFeature::Resources DeferredRTLightingFeatGLSL::getResources( const MaterialFeatureData &fd )
{
/// TODO: This needs to be done via some sort of material
/// feature and not just allow all translucent elements to
/// read from the light prepass.
/*
if( fd.features[MFT_IsTranslucent] )
return Parent::getResources( fd );
*/
Resources res;
res.numTex = 1;
res.numTexReg = 1;
return res;
}
void DeferredRTLightingFeatGLSL::setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex )
{
/// TODO: This needs to be done via some sort of material
/// feature and not just allow all translucent elements to
/// read from the light prepass.
/*
if( fd.features[MFT_IsTranslucent] )
{
Parent::setTexData( stageDat, fd, passData, texIndex );
return;
}
*/
NamedTexTarget *texTarget = NamedTexTarget::find( AdvancedLightBinManager::smBufferName );
if( texTarget )
{
passData.mTexType[ texIndex ] = Material::TexTarget;
passData.mTexSlot[ texIndex++ ].texTarget = texTarget;
}
}
void DeferredBumpFeatGLSL::processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
if( fd.features[MFT_PrePassConditioner] )
{
// There is an output conditioner active, so we need to supply a transform
// to the pixel shader.
MultiLine *meta = new MultiLine;
// setup texture space matrix
Var *texSpaceMat = (Var*) LangElement::find( "objToTangentSpace" );
if( !texSpaceMat )
{
LangElement * texSpaceSetup = setupTexSpaceMat( componentList, &texSpaceMat );
meta->addStatement( texSpaceSetup );
texSpaceMat = (Var*) LangElement::find( "objToTangentSpace" );
}
// turn obj->tangent into world->tangent
Var *worldToTangent = new Var;
worldToTangent->setType( "mat3" );
worldToTangent->setName( "worldToTangent" );
LangElement *worldToTangentDecl = new DecOp( worldToTangent );
// Get the world->obj transform
Var *worldToObj = new Var;
worldToObj->setType( "mat4" );
worldToObj->setName( "worldToObj" );
worldToObj->uniform = true;
worldToObj->constSortPos = cspPrimitive;
Var *mat3Conversion = new Var;
mat3Conversion->setType( "mat3" );
mat3Conversion->setName( "worldToObjMat3" );
LangElement* mat3Lang = new DecOp(mat3Conversion);
meta->addStatement( new GenOp( " @ = mat3(@[0].xyz, @[1].xyz, @[2].xyz);\r\n ", mat3Lang, worldToObj, worldToObj, worldToObj) );
// assign world->tangent transform
meta->addStatement( new GenOp( " @ = @ * @;\r\n", worldToTangentDecl, texSpaceMat, mat3Conversion ) );
// send transform to pixel shader
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *worldToTangentR1 = connectComp->getElement( RT_TEXCOORD );
worldToTangentR1->setName( "worldToTangentR1" );
worldToTangentR1->setType( "vec3" );
meta->addStatement( new GenOp( " @ = @[0];\r\n", worldToTangentR1, worldToTangent ) );
Var *worldToTangentR2 = connectComp->getElement( RT_TEXCOORD );
worldToTangentR2->setName( "worldToTangentR2" );
worldToTangentR2->setType( "vec3" );
meta->addStatement( new GenOp( " @ = @[1];\r\n", worldToTangentR2, worldToTangent ) );
Var *worldToTangentR3 = connectComp->getElement( RT_TEXCOORD );
worldToTangentR3->setName( "worldToTangentR3" );
worldToTangentR3->setType( "vec3" );
meta->addStatement( new GenOp( " @ = @[2];\r\n", worldToTangentR3, worldToTangent ) );
// Make sure there are texcoords
if( !fd.features[MFT_DiffuseMap] )
{
// find incoming texture var
Var *inTex = getVertTexCoord( "texCoord" );
// grab connector texcoord register
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *outTex = connectComp->getElement( RT_TEXCOORD );
outTex->setName( "outTexCoord" );
outTex->setType( "vec2" );
outTex->mapsToSampler = true;
if( fd.features[MFT_TexAnim] )
{
inTex->setType( "vec4" );
// create texture mat var
Var *texMat = new Var;
texMat->setType( "mat4" );
texMat->setName( "texMat" );
texMat->uniform = true;
texMat->constSortPos = cspPotentialPrimitive;
meta->addStatement( new GenOp( " @ = @ * @;\r\n", outTex, texMat, inTex ) );
}
else
{
// setup language elements to output incoming tex coords to output
meta->addStatement( new GenOp( " @ = @;\r\n", outTex, inTex ) );
}
}
output = meta;
}
else if ( fd.materialFeatures[MFT_NormalsOut] ||
fd.features[MFT_IsTranslucent] ||
!fd.features[MFT_RTLighting] )
{
Parent::processVert( componentList, fd );
return;
}
else
{
output = NULL;
}
}
void DeferredBumpFeatGLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// NULL output in case nothing gets handled
output = NULL;
if( fd.features[MFT_PrePassConditioner] )
{
MultiLine *meta = new MultiLine;
// Pull the world->tangent transform from the vertex shader
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *worldToTangentR1 = connectComp->getElement( RT_TEXCOORD );
worldToTangentR1->setName( "worldToTangentR1" );
worldToTangentR1->setType( "vec3" );
Var *worldToTangentR2 = connectComp->getElement( RT_TEXCOORD );
worldToTangentR2->setName( "worldToTangentR2" );
worldToTangentR2->setType( "vec3" );
Var *worldToTangentR3 = connectComp->getElement( RT_TEXCOORD );
worldToTangentR3->setName( "worldToTangentR3" );
worldToTangentR3->setType( "vec3" );
Var *worldToTangent = new Var;
worldToTangent->setType( "mat3" );
worldToTangent->setName( "worldToTangent" );
LangElement *worldToTangentDecl = new DecOp( worldToTangent );
// Build world->tangent matrix
meta->addStatement( new GenOp( " @;\r\n", worldToTangentDecl ) );
meta->addStatement( new GenOp( " @[0] = @;\r\n", worldToTangent, worldToTangentR1 ) );
meta->addStatement( new GenOp( " @[1] = @;\r\n", worldToTangent, worldToTangentR2 ) );
meta->addStatement( new GenOp( " @[2] = @;\r\n", worldToTangent, worldToTangentR3 ) );
// create texture var
Var *bumpMap = new Var;
bumpMap->setType( "sampler2D" );
bumpMap->setName( "bumpMap" );
bumpMap->uniform = true;
bumpMap->sampler = true;
bumpMap->constNum = Var::getTexUnitNum(); // used as texture unit num here
Var *texCoord = (Var*) LangElement::find( "outTexCoord" );
if( !texCoord )
{
// grab connector texcoord register
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
texCoord = connectComp->getElement( RT_TEXCOORD );
texCoord->setName( "outTexCoord" );
texCoord->setType( "vec2" );
texCoord->mapsToSampler = true;
}
LangElement * texOp = new GenOp( "texture2D(@, @)", bumpMap, texCoord );
// create bump normal
Var *bumpNorm = new Var;
bumpNorm->setName( "bumpNormal" );
bumpNorm->setType( "vec4" );
LangElement *bumpNormDecl = new DecOp( bumpNorm );
meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) );
// This var is read from GBufferConditionerHLSL and
// used in the prepass output.
Var *gbNormal = new Var;
gbNormal->setName( "gbNormal" );
gbNormal->setType( "vec3" );
LangElement *gbNormalDecl = new DecOp( gbNormal );
// Normalize is done later...
// Note: The reverse mul order is intentional. Affine matrix.
meta->addStatement( new GenOp( " @ = @.xyz * @;\r\n", gbNormalDecl, bumpNorm, worldToTangent ) );
output = meta;
return;
}
else if ( fd.materialFeatures[MFT_NormalsOut] ||
fd.features[MFT_IsTranslucent] ||
!fd.features[MFT_RTLighting] )
{
Parent::processPix( componentList, fd );
return;
}
else if ( fd.features[MFT_PixSpecular] )
{
Var *bumpSample = (Var *)LangElement::find( "bumpSample" );
if( bumpSample == NULL )
{
Var *texCoord = (Var*) LangElement::find( "outTexCoord" );
if( !texCoord )
{
// grab connector texcoord register
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
texCoord = connectComp->getElement( RT_TEXCOORD );
texCoord->setName( "outTexCoord" );
texCoord->setType( "vec2" );
texCoord->mapsToSampler = true;
}
Var *bumpMap = new Var;
bumpMap->setType( "sampler2D" );
bumpMap->setName( "bumpMap" );
bumpMap->uniform = true;
bumpMap->sampler = true;
bumpMap->constNum = Var::getTexUnitNum(); // used as texture unit num here
bumpSample = new Var;
bumpSample->setType( "vec4" );
bumpSample->setName( "bumpSample" );
LangElement *bumpSampleDecl = new DecOp( bumpSample );
output = new GenOp( " @ = texture2D(@, @);\r\n", bumpSampleDecl, bumpMap, texCoord );
return;
}
}
output = NULL;
}
ShaderFeature::Resources DeferredBumpFeatGLSL::getResources( const MaterialFeatureData &fd )
{
if ( fd.materialFeatures[MFT_NormalsOut] ||
fd.features[MFT_IsTranslucent] ||
fd.features[MFT_Parallax] ||
!fd.features[MFT_RTLighting] )
return Parent::getResources( fd );
Resources res;
if(!fd.features[MFT_SpecularMap])
{
res.numTex = 1;
res.numTexReg = 1;
}
return res;
}
void DeferredBumpFeatGLSL::setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex )
{
if ( fd.materialFeatures[MFT_NormalsOut] ||
fd.features[MFT_IsTranslucent] ||
!fd.features[MFT_RTLighting] )
{
Parent::setTexData( stageDat, fd, passData, texIndex );
return;
}
GFXTextureObject *normalMap = stageDat.getTex( MFT_NormalMap );
if ( !fd.features[MFT_Parallax] && !fd.features[MFT_SpecularMap] &&
( fd.features[MFT_PrePassConditioner] ||
fd.features[MFT_PixSpecular] ) &&
normalMap )
{
passData.mTexType[ texIndex ] = Material::Bump;
passData.mTexSlot[ texIndex++ ].texObject = normalMap;
}
}
void DeferredPixelSpecularGLSL::processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] )
{
Parent::processVert( componentList, fd );
return;
}
output = NULL;
}
void DeferredPixelSpecularGLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] )
{
Parent::processPix( componentList, fd );
return;
}
MultiLine *meta = new MultiLine;
Var *specular = new Var;
specular->setType( "float" );
specular->setName( "specular" );
LangElement * specDecl = new DecOp( specular );
Var *specCol = (Var*)LangElement::find( "specularColor" );
if(specCol == NULL)
{
specCol = new Var;
specCol->setType( "vec4" );
specCol->setName( "specularColor" );
specCol->uniform = true;
specCol->constSortPos = cspPotentialPrimitive;
}
Var *specPow = new Var;
specPow->setType( "float" );
specPow->setName( "specularPower" );
// If the gloss map flag is set, than the specular power is in the alpha
// channel of the specular map
if( fd.features[ MFT_GlossMap ] )
meta->addStatement( new GenOp( " @ = @.a * 255;\r\n", new DecOp( specPow ), specCol ) );
else
{
specPow->uniform = true;
specPow->constSortPos = cspPotentialPrimitive;
}
Var *constSpecPow = new Var;
constSpecPow->setType( "float" );
constSpecPow->setName( "constantSpecularPower" );
constSpecPow->uniform = true;
constSpecPow->constSortPos = cspPass;
Var *lightInfoSamp = (Var *)LangElement::find( "lightInfoSample" );
AssertFatal( lightInfoSamp, "Something hosed the deferred features! Can't find lightInfoSample" );
// (a^m)^n = a^(m*n)
meta->addStatement( new GenOp( " @ = pow(d_specular, ceil(@ / @)) * d_NL_Att;\r\n", specDecl, specPow, constSpecPow ) );
LangElement *specMul = new GenOp( "@ * @", specCol, specular );
LangElement *final = specMul;
// We we have a normal map then mask the specular
if( !fd.features[MFT_SpecularMap] && fd.features[MFT_NormalMap] )
{
Var *bumpSample = (Var*)LangElement::find( "bumpSample" );
final = new GenOp( "@ * @.a", final, bumpSample );
}
// add to color
meta->addStatement( new GenOp( " @;\r\n", assignColor( final, Material::Add ) ) );
output = meta;
}
ShaderFeature::Resources DeferredPixelSpecularGLSL::getResources( const MaterialFeatureData &fd )
{
if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] )
return Parent::getResources( fd );
Resources res;
return res;
}
ShaderFeature::Resources DeferredMinnaertGLSL::getResources( const MaterialFeatureData &fd )
{
Resources res;
if( !fd.features[MFT_IsTranslucent] && fd.features[MFT_RTLighting] )
{
res.numTex = 1;
res.numTexReg = 1;
}
return res;
}
void DeferredMinnaertGLSL::setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex )
{
if( !fd.features[MFT_IsTranslucent] && fd.features[MFT_RTLighting] )
{
NamedTexTarget *texTarget = NamedTexTarget::find(RenderPrePassMgr::BufferName);
if ( texTarget )
{
passData.mTexType[ texIndex ] = Material::TexTarget;
passData.mTexSlot[ texIndex++ ].texTarget = texTarget;
}
}
}
void DeferredMinnaertGLSL::processPixMacros( Vector<GFXShaderMacro> &macros,
const MaterialFeatureData &fd )
{
if( !fd.features[MFT_IsTranslucent] && fd.features[MFT_RTLighting] )
{
// Pull in the uncondition method for the g buffer
NamedTexTarget *texTarget = NamedTexTarget::find( RenderPrePassMgr::BufferName );
if ( texTarget && texTarget->getConditioner() )
{
ConditionerMethodDependency *unconditionMethod = texTarget->getConditioner()->getConditionerMethodDependency(ConditionerFeature::UnconditionMethod);
unconditionMethod->createMethodMacro( String::ToLower(RenderPrePassMgr::BufferName) + "Uncondition", macros );
addDependency(unconditionMethod);
}
}
}
void DeferredMinnaertGLSL::processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// If there is no deferred information, bail on this feature
if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] )
{
output = NULL;
return;
}
// grab incoming vert position
Var *inVertPos = (Var*) LangElement::find( "position" );
AssertFatal( inVertPos, "Something went bad with ShaderGen. The vertex position should be already defined." );
// grab output for gbuffer normal
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *outWSEyeVec= connectComp->getElement( RT_TEXCOORD );
outWSEyeVec->setName( "outWSViewVec" );
outWSEyeVec->setType( "vec4" );
// create objToWorld variable
Var *objToWorld = (Var*) LangElement::find( "objTrans" );
if( !objToWorld )
{
objToWorld = new Var;
objToWorld->setType( "mat4x4" );
objToWorld->setName( "objTrans" );
objToWorld->uniform = true;
objToWorld->constSortPos = cspPrimitive;
}
// Eye Pos world
Var *eyePosWorld = (Var*) LangElement::find( "eyePosWorld" );
if( !eyePosWorld )
{
eyePosWorld = new Var;
eyePosWorld->setType( "vec3" );
eyePosWorld->setName( "eyePosWorld" );
eyePosWorld->uniform = true;
eyePosWorld->constSortPos = cspPass;
}
// Kick out the world-space normal
LangElement *statement = new GenOp( " @ = vec4(@, @) - vec4(@, 0.0);\r\n",
outWSEyeVec, objToWorld, inVertPos, eyePosWorld );
output = statement;
}
void DeferredMinnaertGLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// If there is no deferred information, bail on this feature
if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] )
{
output = NULL;
return;
}
Var *minnaertConstant = new Var;
minnaertConstant->setType( "float" );
minnaertConstant->setName( "minnaertConstant" );
minnaertConstant->uniform = true;
minnaertConstant->constSortPos = cspPotentialPrimitive;
// create texture var
Var *prepassBuffer = new Var;
prepassBuffer->setType( "sampler2D" );
prepassBuffer->setName( "prepassBuffer" );
prepassBuffer->uniform = true;
prepassBuffer->sampler = true;
prepassBuffer->constNum = Var::getTexUnitNum(); // used as texture unit num here
// Texture coord
Var *uvScene = (Var*) LangElement::find( "uvScene" );
AssertFatal(uvScene != NULL, "Unable to find UVScene, no RTLighting feature?");
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *wsViewVec = (Var*) LangElement::find( "wsPos" );
if( !wsViewVec )
{
wsViewVec = connectComp->getElement( RT_TEXCOORD );
wsViewVec->setName( "outWSViewVec" );
wsViewVec->setType( "vec4" );
wsViewVec->mapsToSampler = false;
wsViewVec->uniform = false;
}
String unconditionPrePassMethod = String::ToLower(RenderPrePassMgr::BufferName) + "Uncondition";
MultiLine *meta = new MultiLine;
meta->addStatement( new GenOp( avar( " vec4 normalDepth = %s(texture2D(@, @));\r\n", unconditionPrePassMethod.c_str() ), prepassBuffer, uvScene ) );
meta->addStatement( new GenOp( " vec3 worldViewVec = normalize(@.xyz / @.w);\r\n", wsViewVec, wsViewVec ) );
meta->addStatement( new GenOp( " float vDotN = dot(normalDepth.xyz, worldViewVec);\r\n" ) );
meta->addStatement( new GenOp( " float Minnaert = pow(d_NL_Att, @) * pow(vDotN, 1.0 - @);\r\n", minnaertConstant, minnaertConstant ) );
meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "vec4(Minnaert, Minnaert, Minnaert, 1.0)" ), Material::Mul ) ) );
output = meta;
}
void DeferredSubSurfaceGLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// If there is no deferred information, bail on this feature
if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] )
{
output = NULL;
return;
}
Var *subSurfaceParams = new Var;
subSurfaceParams->setType( "vec4" );
subSurfaceParams->setName( "subSurfaceParams" );
subSurfaceParams->uniform = true;
subSurfaceParams->constSortPos = cspPotentialPrimitive;
Var *inColor = (Var*) LangElement::find( "rtShading" );
MultiLine *meta = new MultiLine;
meta->addStatement( new GenOp( " float subLamb = smoothstep(-@.a, 1.0, d_NL_Att) - smoothstep(0.0, 1.0, d_NL_Att);\r\n", subSurfaceParams ) );
meta->addStatement( new GenOp( " subLamb = max(0.0, subLamb);\r\n" ) );
meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "vec4(@.rgb + (subLamb * @.rgb), 1.0)", inColor, subSurfaceParams ), Material::Mul ) ) );
output = meta;
}

View file

@ -0,0 +1,158 @@
//-----------------------------------------------------------------------------
// 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 _DEFERREDFEATURESGLSL_H_
#define _DEFERREDFEATURESGLSL_H_
#include "shaderGen/GLSL/shaderFeatureGLSL.h"
#include "shaderGen/GLSL/bumpGLSL.h"
#include "shaderGen/GLSL/pixSpecularGLSL.h"
class ConditionerMethodDependency;
/// Lights the pixel by sampling from the light prepass buffer. It will
/// fall back to default vertex lighting functionality if
class DeferredRTLightingFeatGLSL : public RTLightingFeatGLSL
{
typedef RTLightingFeatGLSL Parent;
public:
virtual void processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processPixMacros( Vector<GFXShaderMacro> &macros,
const MaterialFeatureData &fd );
virtual Material::BlendOp getBlendOp(){ return Material::None; }
virtual Resources getResources( const MaterialFeatureData &fd );
virtual void setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex );
virtual String getName()
{
return "Deferred RT Lighting Feature";
}
};
/// Used to write the normals during the depth/normal prepass.
class DeferredBumpFeatGLSL : public BumpFeatGLSL
{
typedef BumpFeatGLSL Parent;
public:
virtual void processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual Material::BlendOp getBlendOp() { return Material::LerpAlpha; }
virtual Resources getResources( const MaterialFeatureData &fd );
virtual void setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex );
virtual String getName()
{
return "Bumpmap [Deferred]";
}
};
/// Generates specular highlights in the forward pass
/// from the light prepass buffer.
class DeferredPixelSpecularGLSL : public PixelSpecularGLSL
{
typedef PixelSpecularGLSL Parent;
public:
virtual void processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual Resources getResources( const MaterialFeatureData &fd );
virtual String getName()
{
return "Pixel Specular [Deferred]";
}
};
///
class DeferredMinnaertGLSL : public ShaderFeatureGLSL
{
typedef ShaderFeatureGLSL Parent;
public:
virtual void processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processPixMacros( Vector<GFXShaderMacro> &macros,
const MaterialFeatureData &fd );
virtual Resources getResources( const MaterialFeatureData &fd );
virtual void setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex );
virtual String getName()
{
return "Minnaert Shading [Deferred]";
}
};
///
class DeferredSubSurfaceGLSL : public ShaderFeatureGLSL
{
typedef ShaderFeatureGLSL Parent;
public:
virtual void processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual String getName()
{
return "Sub-Surface Approximation [Deferred]";
}
};
#endif // _DEFERREDFEATURESGLSL_H_

View file

@ -0,0 +1,320 @@
//-----------------------------------------------------------------------------
// 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/advanced/glsl/gBufferConditionerGLSL.h"
#include "shaderGen/featureMgr.h"
#include "gfx/gfxStringEnumTranslate.h"
#include "materials/materialFeatureTypes.h"
#include "materials/materialFeatureData.h"
GBufferConditionerGLSL::GBufferConditionerGLSL( const GFXFormat bufferFormat ) :
Parent( bufferFormat )
{
// Figure out how we should store the normal data. These are the defaults.
mCanWriteNegativeValues = false;
mNormalStorageType = CartesianXYZ;
// Note: We clear to a depth 1 (the w component) so
// that the unrendered parts of the scene end up
// farthest to the camera.
switch(bufferFormat)
{
case GFXFormatR8G8B8A8:
// TODO: Some kind of logic here. Spherical is better, but is more
// expensive.
mNormalStorageType = Spherical;
mBitsPerChannel = 8;
break;
case GFXFormatR16G16B16A16F:
// Floating point buffers don't need to encode negative values
mCanWriteNegativeValues = true;
mNormalStorageType = Spherical;
mBitsPerChannel = 16;
break;
// Store a 32bit depth with a sperical normal in the
// integer 16 format. This gives us perfect depth
// precision and high quality normals within a 64bit
// buffer format.
case GFXFormatR16G16B16A16:
mNormalStorageType = Spherical;
mBitsPerChannel = 16;
break;
case GFXFormatR32G32B32A32F:
mCanWriteNegativeValues = true;
mNormalStorageType = CartesianXYZ;
mBitsPerChannel = 32;
break;
default:
AssertFatal(false, "Unsupported G-Buffer format");
}
}
GBufferConditionerGLSL::~GBufferConditionerGLSL()
{
}
void GBufferConditionerGLSL::processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
output = NULL;
if( !fd.features[MFT_NormalMap] )
{
// grab incoming vert normal
Var *inNormal = (Var*) LangElement::find( "normal" );
AssertFatal( inNormal, "Something went bad with ShaderGen. The normal should be already defined." );
// grab output for gbuffer normal
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *outNormal = connectComp->getElement( RT_TEXCOORD );
outNormal->setName( "gbNormal" );
outNormal->setType( "vec3" );
// create objToWorld variable
Var *objToWorld = (Var*) LangElement::find( "objTrans" );
if( !objToWorld )
{
objToWorld = new Var;
objToWorld->setType( "mat4" );
objToWorld->setName( "objTrans" );
objToWorld->uniform = true;
objToWorld->constSortPos = cspPrimitive;
}
// Kick out the world-space normal
LangElement *statement = new GenOp( " @ = vec3(@ * vec4(normalize(@), 0.0));\r\n", outNormal, objToWorld, inNormal );
output = statement;
}
}
void GBufferConditionerGLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// sanity
AssertFatal( fd.features[MFT_EyeSpaceDepthOut], "No depth-out feature enabled! Bad news!" );
MultiLine *meta = new MultiLine;
// grab connector normal
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *gbNormal = (Var*) LangElement::find( "gbNormal" );
if( !gbNormal )
{
gbNormal = connectComp->getElement( RT_TEXCOORD );
gbNormal->setName( "gbNormal" );
gbNormal->setType( "vec3" );
gbNormal->mapsToSampler = false;
gbNormal->uniform = false;
}
// find depth
ShaderFeature *depthFeat = FEATUREMGR->getByType( MFT_EyeSpaceDepthOut );
AssertFatal( depthFeat != NULL, "No eye space depth feature found!" );
Var *depth = (Var*) LangElement::find(depthFeat->getOutputVarName());
AssertFatal( depth, "Something went bad with ShaderGen. The depth should be already generated by the EyeSpaceDepthOut feature." );
Var *unconditionedOut = new Var;
unconditionedOut->setType("vec4");
unconditionedOut->setName("normal_depth");
LangElement *outputDecl = new DecOp( unconditionedOut );
// NOTE: We renormalize the normal here as they
// will not stay normalized during interpolation.
meta->addStatement( new GenOp(" @ = @;", outputDecl, new GenOp( "vec4(normalize(@), @)", gbNormal, depth ) ) );
meta->addStatement( assignOutput( unconditionedOut ) );
output = meta;
}
ShaderFeature::Resources GBufferConditionerGLSL::getResources( const MaterialFeatureData &fd )
{
Resources res;
// Passing from VS->PS:
// - world space normal (gbNormal)
res.numTexReg = 1;
return res;
}
Var* GBufferConditionerGLSL::printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta )
{
const bool isCondition = ( methodType == ConditionerFeature::ConditionMethod );
Var *retVal = NULL;
// The uncondition method inputs are changed
if( isCondition )
retVal = Parent::printMethodHeader( methodType, methodName, stream, meta );
else
{
Var *methodVar = new Var;
methodVar->setName(methodName);
methodVar->setType("vec4");
DecOp *methodDecl = new DecOp(methodVar);
Var *prepassSampler = new Var;
prepassSampler->setName("prepassSamplerVar");
prepassSampler->setType("sampler2D");
DecOp *prepassSamplerDecl = new DecOp(prepassSampler);
Var *screenUV = new Var;
screenUV->setName("screenUVVar");
screenUV->setType("vec2");
DecOp *screenUVDecl = new DecOp(screenUV);
Var *bufferSample = new Var;
bufferSample->setName("bufferSample");
bufferSample->setType("vec4");
DecOp *bufferSampleDecl = new DecOp(bufferSample);
meta->addStatement( new GenOp( "@(@, @)\r\n", methodDecl, prepassSamplerDecl, screenUVDecl ) );
meta->addStatement( new GenOp( "{\r\n" ) );
meta->addStatement( new GenOp( " // Sampler g-buffer\r\n" ) );
// The gbuffer has no mipmaps, so use tex2dlod when
// so that the shader compiler can optimize.
meta->addStatement( new GenOp( " @ = texture2DLod(@, @, 0.0);\r\n", bufferSampleDecl, prepassSampler, screenUV ) );
// We don't use this way of passing var's around, so this should cause a crash
// if something uses this improperly
retVal = bufferSample;
}
return retVal;
}
GenOp* GBufferConditionerGLSL::_posnegEncode( GenOp *val )
{
return mCanWriteNegativeValues ? val : new GenOp("0.5 * (@ + 1.0)", val);
}
GenOp* GBufferConditionerGLSL::_posnegDecode( GenOp *val )
{
return mCanWriteNegativeValues ? val : new GenOp("@ * 2.0 - 1.0", val);
}
Var* GBufferConditionerGLSL::_conditionOutput( Var *unconditionedOutput, MultiLine *meta )
{
Var *retVar = new Var;
retVar->setType("vec4");
retVar->setName("_gbConditionedOutput");
LangElement *outputDecl = new DecOp( retVar );
switch(mNormalStorageType)
{
case CartesianXYZ:
meta->addStatement( new GenOp( " // g-buffer conditioner: vec4(normal.xyz, depth)\r\n" ) );
meta->addStatement( new GenOp( " @ = vec4(@, @.a);\r\n", outputDecl,
_posnegEncode(new GenOp("@.xyz", unconditionedOutput)), unconditionedOutput ) );
break;
case CartesianXY:
meta->addStatement( new GenOp( " // g-buffer conditioner: vec4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) );
meta->addStatement( new GenOp( " @ = vec4(@, @.a);", outputDecl,
_posnegEncode(new GenOp("vec3(@.xy, sign(@.z))", unconditionedOutput, unconditionedOutput)), unconditionedOutput ) );
break;
case Spherical:
meta->addStatement( new GenOp( " // g-buffer conditioner: vec4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) );
meta->addStatement( new GenOp( " @ = vec4(@, 0.0, @.a);\r\n", outputDecl,
_posnegEncode(new GenOp("vec2(atan2(@.y, @.x) / 3.14159265358979323846f, @.z)", unconditionedOutput, unconditionedOutput, unconditionedOutput ) ),
unconditionedOutput ) );
break;
}
// Encode depth into two channels
if(mNormalStorageType != CartesianXYZ)
{
const U64 maxValPerChannel = 1 << mBitsPerChannel;
const U64 extraVal = (maxValPerChannel * maxValPerChannel - 1) - (maxValPerChannel - 1) * 2;
meta->addStatement( new GenOp( " \r\n // Encode depth into hi/lo\r\n" ) );
meta->addStatement( new GenOp( avar( " vec3 _tempDepth = fract(@.a * vec3(1.0, %llu.0, %llu.0));\r\n", maxValPerChannel - 1, extraVal ),
unconditionedOutput ) );
meta->addStatement( new GenOp( avar( " @.zw = _tempDepth.xy - _tempDepth.yz * vec2(1.0/%llu.0, 1.0/%llu.0);\r\n\r\n", maxValPerChannel - 1, maxValPerChannel - 1 ),
retVar ) );
}
AssertFatal( retVar != NULL, avar( "Cannot condition output to buffer format: %s", GFXStringTextureFormat[getBufferFormat()] ) );
return retVar;
}
Var* GBufferConditionerGLSL::_unconditionInput( Var *conditionedInput, MultiLine *meta )
{
Var *retVar = new Var;
retVar->setType("vec4");
retVar->setName("_gbUnconditionedInput");
LangElement *outputDecl = new DecOp( retVar );
switch(mNormalStorageType)
{
case CartesianXYZ:
meta->addStatement( new GenOp( " // g-buffer unconditioner: vec4(normal.xyz, depth)\r\n" ) );
meta->addStatement( new GenOp( " @ = vec4(@, @.a);\r\n", outputDecl,
_posnegDecode(new GenOp("@.xyz", conditionedInput)), conditionedInput ) );
break;
case CartesianXY:
meta->addStatement( new GenOp( " // g-buffer unconditioner: vec4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) );
meta->addStatement( new GenOp( " @ = vec4(@, @.a);\r\n", outputDecl,
_posnegDecode(new GenOp("@.xyz", conditionedInput)), conditionedInput ) );
meta->addStatement( new GenOp( " @.z *= sqrt(1.0 - dot(@.xy, @.xy));\r\n", retVar, retVar, retVar ) );
break;
case Spherical:
meta->addStatement( new GenOp( " // g-buffer unconditioner: vec4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) );
meta->addStatement( new GenOp( " vec2 spGPUAngles = @;\r\n", _posnegDecode(new GenOp("@.xy", conditionedInput)) ) );
meta->addStatement( new GenOp( " vec2 sincosTheta;\r\n" ) );
meta->addStatement( new GenOp( " sincosTheta.x = sin(spGPUAngles.x * 3.14159265358979323846);\r\n" ) );
meta->addStatement( new GenOp( " sincosTheta.y = cos(spGPUAngles.x * 3.14159265358979323846);\r\n" ) );
meta->addStatement( new GenOp( " vec2 sincosPhi = vec2(sqrt(1.0 - spGPUAngles.y * spGPUAngles.y), spGPUAngles.y);\r\n" ) );
meta->addStatement( new GenOp( " @ = vec4(sincosTheta.y * sincosPhi.x, sincosTheta.x * sincosPhi.x, sincosPhi.y, @.a);\r\n", outputDecl, conditionedInput ) );
break;
}
// Recover depth from encoding
if(mNormalStorageType != CartesianXYZ)
{
const U64 maxValPerChannel = 1 << mBitsPerChannel;
meta->addStatement( new GenOp( " \r\n // Decode depth\r\n" ) );
meta->addStatement( new GenOp( avar( " @.w = dot( @.zw, vec2(1.0, 1.0/%llu.0));\r\n", maxValPerChannel - 1 ),
retVar, conditionedInput ) );
}
AssertFatal( retVar != NULL, avar( "Cannot uncondition input from buffer format: %s", GFXStringTextureFormat[getBufferFormat()] ) );
return retVar;
}

View file

@ -0,0 +1,74 @@
//-----------------------------------------------------------------------------
// 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 _GBUFFER_CONDITIONER_GLSL_H_
#define _GBUFFER_CONDITIONER_GLSL_H_
#ifndef _CONDITIONER_BASE_H_
#include "shaderGen/conditionerFeature.h"
#endif
#ifndef _SHADEROP_H_
#include "shaderGen/shaderOp.h"
#endif
///
class GBufferConditionerGLSL : public ConditionerFeature
{
typedef ConditionerFeature Parent;
public:
enum NormalStorage
{
CartesianXYZ,
CartesianXY,
Spherical,
};
protected:
NormalStorage mNormalStorageType;
bool mCanWriteNegativeValues;
U32 mBitsPerChannel;
public:
GBufferConditionerGLSL( const GFXFormat bufferFormat );
virtual ~GBufferConditionerGLSL();
virtual void processVert( Vector<ShaderComponent*> &componentList, const MaterialFeatureData &fd );
virtual void processPix( Vector<ShaderComponent*> &componentList, const MaterialFeatureData &fd );
virtual Resources getResources( const MaterialFeatureData &fd );
virtual String getName() { return "GBuffer Conditioner"; }
protected:
virtual Var *printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta );
virtual GenOp* _posnegEncode( GenOp *val );
virtual GenOp* _posnegDecode( GenOp *val );
virtual Var* _conditionOutput( Var *unconditionedOutput, MultiLine *meta );
virtual Var* _unconditionInput( Var *conditionedInput, MultiLine *meta );
};
#endif // _GBUFFER_CONDITIONER_GLSL_H_

View file

@ -0,0 +1,636 @@
//-----------------------------------------------------------------------------
// 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/advanced/hlsl/advancedLightingFeaturesHLSL.h"
#include "lighting/advanced/advancedLightBinManager.h"
#include "shaderGen/langElement.h"
#include "shaderGen/shaderOp.h"
#include "shaderGen/conditionerFeature.h"
#include "renderInstance/renderPrePassMgr.h"
#include "materials/processedMaterial.h"
#include "materials/materialFeatureTypes.h"
void DeferredRTLightingFeatHLSL::processPixMacros( Vector<GFXShaderMacro> &macros,
const MaterialFeatureData &fd )
{
// Skip deferred features, and use forward shading instead
if ( fd.features[MFT_ForwardShading] )
{
Parent::processPixMacros( macros, fd );
return;
}
// Pull in the uncondition method for the light info buffer
NamedTexTarget *texTarget = NamedTexTarget::find( AdvancedLightBinManager::smBufferName );
if ( texTarget && texTarget->getConditioner() )
{
ConditionerMethodDependency *unconditionMethod = texTarget->getConditioner()->getConditionerMethodDependency(ConditionerFeature::UnconditionMethod);
unconditionMethod->createMethodMacro( String::ToLower( AdvancedLightBinManager::smBufferName ) + "Uncondition", macros );
addDependency(unconditionMethod);
}
}
void DeferredRTLightingFeatHLSL::processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// Skip deferred features, and use forward shading instead
if ( fd.features[MFT_ForwardShading] )
{
Parent::processVert( componentList, fd );
return;
}
// Pass screen space position to pixel shader to compute a full screen buffer uv
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *ssPos = connectComp->getElement( RT_TEXCOORD );
ssPos->setName( "screenspacePos" );
ssPos->setStructName( "OUT" );
ssPos->setType( "float4" );
Var *outPosition = (Var*) LangElement::find( "hpos" );
AssertFatal( outPosition, "No hpos, ohnoes." );
output = new GenOp( " @ = @;\r\n", ssPos, outPosition );
}
void DeferredRTLightingFeatHLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// Skip deferred features, and use forward shading instead
if ( fd.features[MFT_ForwardShading] )
{
Parent::processPix( componentList, fd );
return;
}
MultiLine *meta = new MultiLine;
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *ssPos = connectComp->getElement( RT_TEXCOORD );
ssPos->setName( "screenspacePos" );
ssPos->setStructName( "IN" );
ssPos->setType( "float4" );
Var *uvScene = new Var;
uvScene->setType( "float2" );
uvScene->setName( "uvScene" );
LangElement *uvSceneDecl = new DecOp( uvScene );
String rtParamName = String::ToString( "rtParams%d", mLastTexIndex );
Var *rtParams = (Var*) LangElement::find( rtParamName );
if( !rtParams )
{
rtParams = new Var;
rtParams->setType( "float4" );
rtParams->setName( rtParamName );
rtParams->uniform = true;
rtParams->constSortPos = cspPass;
}
meta->addStatement( new GenOp( " @ = @.xy / @.w;\r\n", uvSceneDecl, ssPos, ssPos ) ); // get the screen coord... its -1 to +1
meta->addStatement( new GenOp( " @ = ( @ + 1.0 ) / 2.0;\r\n", uvScene, uvScene ) ); // get the screen coord to 0 to 1
meta->addStatement( new GenOp( " @.y = 1.0 - @.y;\r\n", uvScene, uvScene ) ); // flip the y axis
meta->addStatement( new GenOp( " @ = ( @ * @.zw ) + @.xy;\r\n", uvScene, uvScene, rtParams, rtParams) ); // scale it down and offset it to the rt size
Var *lightInfoSamp = new Var;
lightInfoSamp->setType( "float4" );
lightInfoSamp->setName( "lightInfoSample" );
// create texture var
Var *lightInfoBuffer = new Var;
lightInfoBuffer->setType( "sampler2D" );
lightInfoBuffer->setName( "lightInfoBuffer" );
lightInfoBuffer->uniform = true;
lightInfoBuffer->sampler = true;
lightInfoBuffer->constNum = Var::getTexUnitNum(); // used as texture unit num here
// Declare the RTLighting variables in this feature, they will either be assigned
// in this feature, or in the tonemap/lightmap feature
Var *d_lightcolor = new Var( "d_lightcolor", "float3" );
meta->addStatement( new GenOp( " @;\r\n", new DecOp( d_lightcolor ) ) );
Var *d_NL_Att = new Var( "d_NL_Att", "float" );
meta->addStatement( new GenOp( " @;\r\n", new DecOp( d_NL_Att ) ) );
Var *d_specular = new Var( "d_specular", "float" );
meta->addStatement( new GenOp( " @;\r\n", new DecOp( d_specular ) ) );
// Perform the uncondition here.
String unconditionLightInfo = String::ToLower( AdvancedLightBinManager::smBufferName ) + "Uncondition";
meta->addStatement( new GenOp( avar( " %s(tex2D(@, @), @, @, @);\r\n",
unconditionLightInfo.c_str() ), lightInfoBuffer, uvScene, d_lightcolor, d_NL_Att, d_specular ) );
// If this has an interlaced pre-pass, do averaging here
if( fd.features[MFT_InterlacedPrePass] )
{
Var *oneOverTargetSize = (Var*) LangElement::find( "oneOverTargetSize" );
if( !oneOverTargetSize )
{
oneOverTargetSize = new Var;
oneOverTargetSize->setType( "float2" );
oneOverTargetSize->setName( "oneOverTargetSize" );
oneOverTargetSize->uniform = true;
oneOverTargetSize->constSortPos = cspPass;
}
meta->addStatement( new GenOp( " float id_NL_Att, id_specular;\r\n float3 id_lightcolor;\r\n" ) );
meta->addStatement( new GenOp( avar( " %s(tex2D(@, @ + float2(0.0, @.y)), id_lightcolor, id_NL_Att, id_specular);\r\n",
unconditionLightInfo.c_str() ), lightInfoBuffer, uvScene, oneOverTargetSize ) );
meta->addStatement( new GenOp(" @ = lerp(@, id_lightcolor, 0.5);\r\n", d_lightcolor, d_lightcolor ) );
meta->addStatement( new GenOp(" @ = lerp(@, id_NL_Att, 0.5);\r\n", d_NL_Att, d_NL_Att ) );
meta->addStatement( new GenOp(" @ = lerp(@, id_specular, 0.5);\r\n", d_specular, d_specular ) );
}
// This is kind of weak sauce
if( !fd.features[MFT_VertLit] && !fd.features[MFT_ToneMap] && !fd.features[MFT_LightMap] && !fd.features[MFT_SubSurface] )
meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "float4(@, 1.0)", d_lightcolor ), Material::Mul ) ) );
output = meta;
}
ShaderFeature::Resources DeferredRTLightingFeatHLSL::getResources( const MaterialFeatureData &fd )
{
// Skip deferred features, and use forward shading instead
if ( fd.features[MFT_ForwardShading] )
return Parent::getResources( fd );
// HACK: See DeferredRTLightingFeatHLSL::setTexData.
mLastTexIndex = 0;
Resources res;
res.numTex = 1;
res.numTexReg = 1;
return res;
}
void DeferredRTLightingFeatHLSL::setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex )
{
// Skip deferred features, and use forward shading instead
if ( fd.features[MFT_ForwardShading] )
{
Parent::setTexData( stageDat, fd, passData, texIndex );
return;
}
NamedTexTarget *texTarget = NamedTexTarget::find( AdvancedLightBinManager::smBufferName );
if( texTarget )
{
// HACK: We store this for use in DeferredRTLightingFeatHLSL::processPix()
// which cannot deduce the texture unit itself.
mLastTexIndex = texIndex;
passData.mTexType[ texIndex ] = Material::TexTarget;
passData.mTexSlot[ texIndex++ ].texTarget = texTarget;
}
}
void DeferredBumpFeatHLSL::processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
if( fd.features[MFT_PrePassConditioner] )
{
// There is an output conditioner active, so we need to supply a transform
// to the pixel shader.
MultiLine *meta = new MultiLine;
// We need the view to tangent space transform in the pixel shader.
getOutViewToTangent( componentList, meta, fd );
// Make sure there are texcoords
if( !fd.features[MFT_Parallax] && !fd.features[MFT_DiffuseMap] )
{
const bool useTexAnim = fd.features[MFT_TexAnim];
getOutTexCoord( "texCoord",
"float2",
true,
useTexAnim,
meta,
componentList );
if ( fd.features.hasFeature( MFT_DetailNormalMap ) )
addOutDetailTexCoord( componentList,
meta,
useTexAnim );
}
output = meta;
}
else if ( fd.materialFeatures[MFT_NormalsOut] ||
fd.features[MFT_ForwardShading] ||
!fd.features[MFT_RTLighting] )
{
Parent::processVert( componentList, fd );
return;
}
else
{
output = NULL;
}
}
void DeferredBumpFeatHLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// NULL output in case nothing gets handled
output = NULL;
if( fd.features[MFT_PrePassConditioner] )
{
MultiLine *meta = new MultiLine;
Var *viewToTangent = getInViewToTangent( componentList );
// create texture var
Var *bumpMap = getNormalMapTex();
Var *texCoord = getInTexCoord( "texCoord", "float2", true, componentList );
LangElement *texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord );
// create bump normal
Var *bumpNorm = new Var;
bumpNorm->setName( "bumpNormal" );
bumpNorm->setType( "float4" );
LangElement *bumpNormDecl = new DecOp( bumpNorm );
meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) );
// If we have a detail normal map we add the xy coords of
// it to the base normal map. This gives us the effect we
// want with few instructions and minial artifacts.
if ( fd.features.hasFeature( MFT_DetailNormalMap ) )
{
bumpMap = new Var;
bumpMap->setType( "sampler2D" );
bumpMap->setName( "detailBumpMap" );
bumpMap->uniform = true;
bumpMap->sampler = true;
bumpMap->constNum = Var::getTexUnitNum();
texCoord = getInTexCoord( "detCoord", "float2", true, componentList );
texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord );
Var *detailBump = new Var;
detailBump->setName( "detailBump" );
detailBump->setType( "float4" );
meta->addStatement( expandNormalMap( texOp, new DecOp( detailBump ), detailBump, fd ) );
Var *detailBumpScale = new Var;
detailBumpScale->setType( "float" );
detailBumpScale->setName( "detailBumpStrength" );
detailBumpScale->uniform = true;
detailBumpScale->constSortPos = cspPass;
meta->addStatement( new GenOp( " @.xy += @.xy * @;\r\n", bumpNorm, detailBump, detailBumpScale ) );
}
// This var is read from GBufferConditionerHLSL and
// used in the prepass output.
//
// By using the 'half' type here we get a bunch of partial
// precision optimized code on further operations on the normal
// which helps alot on older Geforce cards.
//
Var *gbNormal = new Var;
gbNormal->setName( "gbNormal" );
gbNormal->setType( "half3" );
LangElement *gbNormalDecl = new DecOp( gbNormal );
// Normalize is done later...
// Note: The reverse mul order is intentional. Affine matrix.
meta->addStatement( new GenOp( " @ = (half3)mul( @.xyz, @ );\r\n", gbNormalDecl, bumpNorm, viewToTangent ) );
output = meta;
return;
}
else if ( fd.materialFeatures[MFT_NormalsOut] ||
fd.features[MFT_ForwardShading] ||
!fd.features[MFT_RTLighting] )
{
Parent::processPix( componentList, fd );
return;
}
else if ( fd.features[MFT_PixSpecular] && !fd.features[MFT_SpecularMap] )
{
Var *bumpSample = (Var *)LangElement::find( "bumpSample" );
if( bumpSample == NULL )
{
Var *texCoord = getInTexCoord( "texCoord", "float2", true, componentList );
Var *bumpMap = getNormalMapTex();
bumpSample = new Var;
bumpSample->setType( "float4" );
bumpSample->setName( "bumpSample" );
LangElement *bumpSampleDecl = new DecOp( bumpSample );
output = new GenOp( " @ = tex2D(@, @);\r\n", bumpSampleDecl, bumpMap, texCoord );
return;
}
}
output = NULL;
}
ShaderFeature::Resources DeferredBumpFeatHLSL::getResources( const MaterialFeatureData &fd )
{
if ( fd.materialFeatures[MFT_NormalsOut] ||
fd.features[MFT_ForwardShading] ||
fd.features[MFT_Parallax] ||
!fd.features[MFT_RTLighting] )
return Parent::getResources( fd );
Resources res;
if(!fd.features[MFT_SpecularMap])
{
res.numTex = 1;
res.numTexReg = 1;
if ( fd.features[MFT_PrePassConditioner] &&
fd.features.hasFeature( MFT_DetailNormalMap ) )
{
res.numTex += 1;
if ( !fd.features.hasFeature( MFT_DetailMap ) )
res.numTexReg += 1;
}
}
return res;
}
void DeferredBumpFeatHLSL::setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex )
{
if ( fd.materialFeatures[MFT_NormalsOut] ||
fd.features[MFT_ForwardShading] ||
!fd.features[MFT_RTLighting] )
{
Parent::setTexData( stageDat, fd, passData, texIndex );
return;
}
if ( !fd.features[MFT_Parallax] && !fd.features[MFT_SpecularMap] &&
( fd.features[MFT_PrePassConditioner] ||
fd.features[MFT_PixSpecular] ) )
{
passData.mTexType[ texIndex ] = Material::Bump;
passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_NormalMap );
if ( fd.features[MFT_PrePassConditioner] &&
fd.features.hasFeature( MFT_DetailNormalMap ) )
{
passData.mTexType[ texIndex ] = Material::DetailBump;
passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_DetailNormalMap );
}
}
}
void DeferredPixelSpecularHLSL::processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] )
{
Parent::processVert( componentList, fd );
return;
}
output = NULL;
}
void DeferredPixelSpecularHLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] )
{
Parent::processPix( componentList, fd );
return;
}
MultiLine *meta = new MultiLine;
Var *specular = new Var;
specular->setType( "float" );
specular->setName( "specular" );
LangElement * specDecl = new DecOp( specular );
Var *specCol = (Var*)LangElement::find( "specularColor" );
if(specCol == NULL)
{
specCol = new Var;
specCol->setType( "float4" );
specCol->setName( "specularColor" );
specCol->uniform = true;
specCol->constSortPos = cspPotentialPrimitive;
}
Var *specPow = new Var;
specPow->setType( "float" );
specPow->setName( "specularPower" );
// If the gloss map flag is set, than the specular power is in the alpha
// channel of the specular map
if( fd.features[ MFT_GlossMap ] )
meta->addStatement( new GenOp( " @ = @.a * 255;\r\n", new DecOp( specPow ), specCol ) );
else
{
specPow->uniform = true;
specPow->constSortPos = cspPotentialPrimitive;
}
Var *lightInfoSamp = (Var *)LangElement::find( "lightInfoSample" );
Var *d_specular = (Var*)LangElement::find( "d_specular" );
Var *d_NL_Att = (Var*)LangElement::find( "d_NL_Att" );
AssertFatal( lightInfoSamp && d_specular && d_NL_Att,
"DeferredPixelSpecularHLSL::processPix - Something hosed the deferred features!" );
// (a^m)^n = a^(m*n)
meta->addStatement( new GenOp( " @ = pow( @, ceil(@ / AL_ConstantSpecularPower)) * @;\r\n",
specDecl, d_specular, specPow, d_NL_Att ) );
LangElement *specMul = new GenOp( "float4( @.rgb, 0 ) * @", specCol, specular );
LangElement *final = specMul;
// We we have a normal map then mask the specular
if( !fd.features[MFT_SpecularMap] && fd.features[MFT_NormalMap] )
{
Var *bumpSample = (Var*)LangElement::find( "bumpSample" );
final = new GenOp( "@ * @.a", final, bumpSample );
}
// add to color
meta->addStatement( new GenOp( " @;\r\n", assignColor( final, Material::Add ) ) );
output = meta;
}
ShaderFeature::Resources DeferredPixelSpecularHLSL::getResources( const MaterialFeatureData &fd )
{
if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] )
return Parent::getResources( fd );
Resources res;
return res;
}
ShaderFeature::Resources DeferredMinnaertHLSL::getResources( const MaterialFeatureData &fd )
{
Resources res;
if( !fd.features[MFT_ForwardShading] && fd.features[MFT_RTLighting] )
{
res.numTex = 1;
res.numTexReg = 1;
}
return res;
}
void DeferredMinnaertHLSL::setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex )
{
if( !fd.features[MFT_ForwardShading] && fd.features[MFT_RTLighting] )
{
NamedTexTarget *texTarget = NamedTexTarget::find(RenderPrePassMgr::BufferName);
if ( texTarget )
{
passData.mTexType[ texIndex ] = Material::TexTarget;
passData.mTexSlot[ texIndex++ ].texTarget = texTarget;
}
}
}
void DeferredMinnaertHLSL::processPixMacros( Vector<GFXShaderMacro> &macros,
const MaterialFeatureData &fd )
{
if( !fd.features[MFT_ForwardShading] && fd.features[MFT_RTLighting] )
{
// Pull in the uncondition method for the g buffer
NamedTexTarget *texTarget = NamedTexTarget::find( RenderPrePassMgr::BufferName );
if ( texTarget && texTarget->getConditioner() )
{
ConditionerMethodDependency *unconditionMethod = texTarget->getConditioner()->getConditionerMethodDependency(ConditionerFeature::UnconditionMethod);
unconditionMethod->createMethodMacro( String::ToLower(RenderPrePassMgr::BufferName) + "Uncondition", macros );
addDependency(unconditionMethod);
}
}
}
void DeferredMinnaertHLSL::processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// If there is no deferred information, bail on this feature
if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] )
{
output = NULL;
return;
}
// Make sure we pass the world space position to the
// pixel shader so we can calculate a view vector.
MultiLine *meta = new MultiLine;
addOutWsPosition( componentList, fd.features[MFT_UseInstancing], meta );
output = meta;
}
void DeferredMinnaertHLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// If there is no deferred information, bail on this feature
if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] )
{
output = NULL;
return;
}
Var *minnaertConstant = new Var;
minnaertConstant->setType( "float" );
minnaertConstant->setName( "minnaertConstant" );
minnaertConstant->uniform = true;
minnaertConstant->constSortPos = cspPotentialPrimitive;
// create texture var
Var *prepassBuffer = new Var;
prepassBuffer->setType( "sampler2D" );
prepassBuffer->setName( "prepassBuffer" );
prepassBuffer->uniform = true;
prepassBuffer->sampler = true;
prepassBuffer->constNum = Var::getTexUnitNum(); // used as texture unit num here
// Texture coord
Var *uvScene = (Var*) LangElement::find( "uvScene" );
AssertFatal(uvScene != NULL, "Unable to find UVScene, no RTLighting feature?");
MultiLine *meta = new MultiLine;
// Get the world space view vector.
Var *wsViewVec = getWsView( getInWsPosition( componentList ), meta );
String unconditionPrePassMethod = String::ToLower(RenderPrePassMgr::BufferName) + "Uncondition";
Var *d_NL_Att = (Var*)LangElement::find( "d_NL_Att" );
meta->addStatement( new GenOp( avar( " float4 normalDepth = %s(@, @);\r\n", unconditionPrePassMethod.c_str() ), prepassBuffer, uvScene ) );
meta->addStatement( new GenOp( " float vDotN = dot(normalDepth.xyz, @);\r\n", wsViewVec ) );
meta->addStatement( new GenOp( " float Minnaert = pow( @, @) * pow(vDotN, 1.0 - @);\r\n", d_NL_Att, minnaertConstant, minnaertConstant ) );
meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "float4(Minnaert, Minnaert, Minnaert, 1.0)" ), Material::Mul ) ) );
output = meta;
}
void DeferredSubSurfaceHLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// If there is no deferred information, bail on this feature
if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] )
{
output = NULL;
return;
}
Var *subSurfaceParams = new Var;
subSurfaceParams->setType( "float4" );
subSurfaceParams->setName( "subSurfaceParams" );
subSurfaceParams->uniform = true;
subSurfaceParams->constSortPos = cspPotentialPrimitive;
Var *d_lightcolor = (Var*)LangElement::find( "d_lightcolor" );
Var *d_NL_Att = (Var*)LangElement::find( "d_NL_Att" );
MultiLine *meta = new MultiLine;
meta->addStatement( new GenOp( " float subLamb = smoothstep(-@.a, 1.0, @) - smoothstep(0.0, 1.0, @);\r\n", subSurfaceParams, d_NL_Att, d_NL_Att ) );
meta->addStatement( new GenOp( " subLamb = max(0.0, subLamb);\r\n" ) );
meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "float4(@ + (subLamb * @.rgb), 1.0)", d_lightcolor, subSurfaceParams ), Material::Mul ) ) );
output = meta;
}

View file

@ -0,0 +1,170 @@
//-----------------------------------------------------------------------------
// 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 _DEFERREDFEATURESHLSL_H_
#define _DEFERREDFEATURESHLSL_H_
#include "shaderGen/HLSL/shaderFeatureHLSL.h"
#include "shaderGen/HLSL/bumpHLSL.h"
#include "shaderGen/HLSL/pixSpecularHLSL.h"
class ConditionerMethodDependency;
/// Lights the pixel by sampling from the light prepass
/// buffer. It will fall back to forward lighting
/// functionality for non-deferred rendered surfaces.
///
/// Also note that this feature is only used in the
/// forward rendering pass. It is not used during the
/// prepass step.
///
class DeferredRTLightingFeatHLSL : public RTLightingFeatHLSL
{
typedef RTLightingFeatHLSL Parent;
protected:
/// @see DeferredRTLightingFeatHLSL::processPix()
U32 mLastTexIndex;
public:
virtual void processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processPixMacros( Vector<GFXShaderMacro> &macros,
const MaterialFeatureData &fd );
virtual Material::BlendOp getBlendOp(){ return Material::None; }
virtual Resources getResources( const MaterialFeatureData &fd );
virtual void setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex );
virtual String getName()
{
return "Deferred RT Lighting";
}
};
/// This is used during the
class DeferredBumpFeatHLSL : public BumpFeatHLSL
{
typedef BumpFeatHLSL Parent;
public:
virtual void processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual Material::BlendOp getBlendOp() { return Material::LerpAlpha; }
virtual Resources getResources( const MaterialFeatureData &fd );
virtual void setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex );
virtual String getName()
{
return "Bumpmap [Deferred]";
}
};
/// Generates specular highlights in the forward pass
/// from the light prepass buffer.
class DeferredPixelSpecularHLSL : public PixelSpecularHLSL
{
typedef PixelSpecularHLSL Parent;
public:
virtual void processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual Resources getResources( const MaterialFeatureData &fd );
virtual String getName()
{
return "Pixel Specular [Deferred]";
}
};
///
class DeferredMinnaertHLSL : public ShaderFeatureHLSL
{
typedef ShaderFeatureHLSL Parent;
public:
virtual void processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual void processPixMacros( Vector<GFXShaderMacro> &macros,
const MaterialFeatureData &fd );
virtual Resources getResources( const MaterialFeatureData &fd );
virtual void setTexData( Material::StageData &stageDat,
const MaterialFeatureData &fd,
RenderPassData &passData,
U32 &texIndex );
virtual String getName()
{
return "Minnaert Shading [Deferred]";
}
};
///
class DeferredSubSurfaceHLSL : public ShaderFeatureHLSL
{
typedef ShaderFeatureHLSL Parent;
public:
virtual void processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd );
virtual String getName()
{
return "Sub-Surface Approximation [Deferred]";
}
};
#endif // _DEFERREDFEATURESHLSL_H_

View file

@ -0,0 +1,404 @@
//-----------------------------------------------------------------------------
// 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/advanced/hlsl/gBufferConditionerHLSL.h"
#include "shaderGen/featureMgr.h"
#include "gfx/gfxStringEnumTranslate.h"
#include "materials/materialFeatureTypes.h"
#include "materials/materialFeatureData.h"
#include "shaderGen/hlsl/shaderFeatureHLSL.h"
GBufferConditionerHLSL::GBufferConditionerHLSL( const GFXFormat bufferFormat, const NormalSpace nrmSpace ) :
Parent( bufferFormat )
{
// Figure out how we should store the normal data. These are the defaults.
mCanWriteNegativeValues = false;
mNormalStorageType = CartesianXYZ;
// Note: We clear to a depth 1 (the w component) so
// that the unrendered parts of the scene end up
// farthest to the camera.
const NormalStorage &twoCmpNrmStorageType = ( nrmSpace == WorldSpace ? Spherical : LambertAzimuthal );
switch(bufferFormat)
{
case GFXFormatR8G8B8A8:
mNormalStorageType = twoCmpNrmStorageType;
mBitsPerChannel = 8;
break;
case GFXFormatR16G16B16A16F:
// Floating point buffers don't need to encode negative values
mCanWriteNegativeValues = true;
mNormalStorageType = twoCmpNrmStorageType;
mBitsPerChannel = 16;
break;
// Store a 32bit depth with a sperical normal in the
// integer 16 format. This gives us perfect depth
// precision and high quality normals within a 64bit
// buffer format.
case GFXFormatR16G16B16A16:
mNormalStorageType = twoCmpNrmStorageType;
mBitsPerChannel = 16;
break;
case GFXFormatR32G32B32A32F:
mCanWriteNegativeValues = true;
mNormalStorageType = CartesianXYZ;
mBitsPerChannel = 32;
break;
default:
AssertFatal(false, "Unsupported G-Buffer format");
}
}
GBufferConditionerHLSL::~GBufferConditionerHLSL()
{
}
void GBufferConditionerHLSL::processVert( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// If we have a normal map then that feature will
// take care of passing gbNormal to the pixel shader.
if ( fd.features[MFT_NormalMap] )
return;
MultiLine *meta = new MultiLine;
output = meta;
// grab incoming vert normal
Var *inNormal = (Var*) LangElement::find( "normal" );
AssertFatal( inNormal, "Something went bad with ShaderGen. The normal should be already defined." );
// grab output for gbuffer normal
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *outNormal = connectComp->getElement( RT_TEXCOORD );
outNormal->setName( "gbNormal" );
outNormal->setStructName( "OUT" );
outNormal->setType( "float3" );
if( !fd.features[MFT_ParticleNormal] )
{
// Kick out the view-space normal
// TODO: Total hack because Conditioner is directly derived
// from ShaderFeature and not from ShaderFeatureHLSL.
NamedFeatureHLSL dummy( String::EmptyString );
dummy.mInstancingFormat = mInstancingFormat;
Var *worldViewOnly = dummy.getWorldView( componentList, fd.features[MFT_UseInstancing], meta );
meta->addStatement( new GenOp(" @ = mul(@, float4( normalize(@), 0.0 ) ).xyz;\r\n",
outNormal, worldViewOnly, inNormal ) );
}
else
{
// Assume the particle normal generator has already put this in view space
// and normalized it
meta->addStatement( new GenOp( " @ = @;\r\n", outNormal, inNormal ) );
}
}
void GBufferConditionerHLSL::processPix( Vector<ShaderComponent*> &componentList,
const MaterialFeatureData &fd )
{
// sanity
AssertFatal( fd.features[MFT_EyeSpaceDepthOut], "No depth-out feature enabled! Bad news!" );
MultiLine *meta = new MultiLine;
// grab connector normal
ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
Var *gbNormal = (Var*) LangElement::find( "gbNormal" );
if( !gbNormal )
{
gbNormal = connectComp->getElement( RT_TEXCOORD );
gbNormal->setName( "gbNormal" );
gbNormal->setStructName( "IN" );
gbNormal->setType( "float3" );
gbNormal->mapsToSampler = false;
gbNormal->uniform = false;
}
// find depth
ShaderFeature *depthFeat = FEATUREMGR->getByType( MFT_EyeSpaceDepthOut );
AssertFatal( depthFeat != NULL, "No eye space depth feature found!" );
Var *depth = (Var*) LangElement::find(depthFeat->getOutputVarName());
AssertFatal( depth, "Something went bad with ShaderGen. The depth should be already generated by the EyeSpaceDepthOut feature." );
Var *unconditionedOut = new Var;
unconditionedOut->setType("float4");
unconditionedOut->setName("normal_depth");
LangElement *outputDecl = new DecOp( unconditionedOut );
// If we're doing prepass blending then we need
// to steal away the alpha channel before the
// conditioner stomps on it.
Var *alphaVal = NULL;
if ( fd.features[ MFT_IsTranslucentZWrite ] )
{
alphaVal = new Var( "outAlpha", "float" );
meta->addStatement( new GenOp( " @ = OUT.col.a; // MFT_IsTranslucentZWrite\r\n", new DecOp( alphaVal ) ) );
}
// If using interlaced normals, invert the normal
if(fd.features[MFT_InterlacedPrePass])
{
// NOTE: Its safe to not call ShaderFeatureHLSL::addOutVpos() in the vertex
// shader as for SM 3.0 nothing is needed there.
Var *Vpos = ShaderFeatureHLSL::getInVpos( meta, componentList );
Var *iGBNormal = new Var( "interlacedGBNormal", "float3" );
meta->addStatement(new GenOp(" @ = (frac(@.y * 0.5) < 0.1 ? reflect(@, float3(0.0, -1.0, 0.0)) : @);\r\n", new DecOp(iGBNormal), Vpos, gbNormal, gbNormal));
gbNormal = iGBNormal;
}
// NOTE: We renormalize the normal here as they
// will not stay normalized during interpolation.
meta->addStatement( new GenOp(" @ = @;", outputDecl, new GenOp( "float4(normalize(@), @)", gbNormal, depth ) ) );
meta->addStatement( assignOutput( unconditionedOut ) );
// If we have an alpha var then we're doing prepass lerp blending.
if ( alphaVal )
{
Var *outColor = (Var*)LangElement::find( getOutputTargetVarName( DefaultTarget ) );
meta->addStatement( new GenOp( " @.ba = float2( 0, @ ); // MFT_IsTranslucentZWrite\r\n", outColor, alphaVal ) );
}
output = meta;
}
ShaderFeature::Resources GBufferConditionerHLSL::getResources( const MaterialFeatureData &fd )
{
Resources res;
// Passing from VS->PS:
// - world space normal (gbNormal)
res.numTexReg = 1;
return res;
}
Var* GBufferConditionerHLSL::printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta )
{
const bool isCondition = ( methodType == ConditionerFeature::ConditionMethod );
Var *retVal = NULL;
// The uncondition method inputs are changed
if( isCondition )
retVal = Parent::printMethodHeader( methodType, methodName, stream, meta );
else
{
Var *methodVar = new Var;
methodVar->setName(methodName);
methodVar->setType("inline float4");
DecOp *methodDecl = new DecOp(methodVar);
Var *prepassSampler = new Var;
prepassSampler->setName("prepassSamplerVar");
prepassSampler->setType("sampler2D");
DecOp *prepassSamplerDecl = new DecOp(prepassSampler);
Var *screenUV = new Var;
screenUV->setName("screenUVVar");
screenUV->setType("float2");
DecOp *screenUVDecl = new DecOp(screenUV);
Var *bufferSample = new Var;
bufferSample->setName("bufferSample");
bufferSample->setType("float4");
DecOp *bufferSampleDecl = new DecOp(bufferSample);
meta->addStatement( new GenOp( "@(@, @)\r\n", methodDecl, prepassSamplerDecl, screenUVDecl ) );
meta->addStatement( new GenOp( "{\r\n" ) );
meta->addStatement( new GenOp( " // Sampler g-buffer\r\n" ) );
#ifdef TORQUE_OS_XENON
meta->addStatement( new GenOp( " @;\r\n", bufferSampleDecl ) );
meta->addStatement( new GenOp( " asm { tfetch2D @, @, @, MagFilter = point, MinFilter = point, MipFilter = point };\r\n", bufferSample, screenUV, prepassSampler ) );
#else
// The gbuffer has no mipmaps, so use tex2dlod when
// possible so that the shader compiler can optimize.
meta->addStatement( new GenOp( " #if TORQUE_SM >= 30\r\n" ) );
meta->addStatement( new GenOp( " @ = tex2Dlod(@, float4(@,0,0));\r\n", bufferSampleDecl, prepassSampler, screenUV ) );
meta->addStatement( new GenOp( " #else\r\n" ) );
meta->addStatement( new GenOp( " @ = tex2D(@, @);\r\n", bufferSampleDecl, prepassSampler, screenUV ) );
meta->addStatement( new GenOp( " #endif\r\n\r\n" ) );
#endif
// We don't use this way of passing var's around, so this should cause a crash
// if something uses this improperly
retVal = bufferSample;
}
return retVal;
}
GenOp* GBufferConditionerHLSL::_posnegEncode( GenOp *val )
{
if(mNormalStorageType == LambertAzimuthal)
return mCanWriteNegativeValues ? val : new GenOp(avar("(%f * (@ + %f))", 1.0f/(M_SQRT2_F * 2.0f), M_SQRT2_F), val);
else
return mCanWriteNegativeValues ? val : new GenOp("(0.5 * (@ + 1.0))", val);
}
GenOp* GBufferConditionerHLSL::_posnegDecode( GenOp *val )
{
if(mNormalStorageType == LambertAzimuthal)
return mCanWriteNegativeValues ? val : new GenOp(avar("(@ * %f - %f)", M_SQRT2_F * 2.0f, M_SQRT2_F), val);
else
return mCanWriteNegativeValues ? val : new GenOp("(@ * 2.0 - 1.0)", val);
}
Var* GBufferConditionerHLSL::_conditionOutput( Var *unconditionedOutput, MultiLine *meta )
{
Var *retVar = new Var;
retVar->setType("float4");
retVar->setName("_gbConditionedOutput");
LangElement *outputDecl = new DecOp( retVar );
switch(mNormalStorageType)
{
case CartesianXYZ:
meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.xyz, depth)\r\n" ) );
meta->addStatement( new GenOp( " @ = float4(@, @.a);\r\n", outputDecl,
_posnegEncode(new GenOp("@.xyz", unconditionedOutput)), unconditionedOutput ) );
break;
case CartesianXY:
meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) );
meta->addStatement( new GenOp( " @ = float4(@, @.a);", outputDecl,
_posnegEncode(new GenOp("float3(@.xy, sign(@.z))", unconditionedOutput, unconditionedOutput)), unconditionedOutput ) );
break;
case Spherical:
meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) );
meta->addStatement( new GenOp( " @ = float4(@, 0.0, @.a);\r\n", outputDecl,
_posnegEncode(new GenOp("float2(atan2(@.y, @.x) / 3.14159265358979323846f, @.z)", unconditionedOutput, unconditionedOutput, unconditionedOutput ) ),
unconditionedOutput ) );
// HACK: This fixes the noise present when using a floating point
// gbuffer on Geforce cards and the "flat areas unlit" issues.
//
// We need work around atan2() above to fix this issue correctly
// without the extra overhead of this test.
//
meta->addStatement( new GenOp( " if ( abs( dot( @.xyz, float3( 0.0, 0.0, 1.0 ) ) ) > 0.999f ) @ = float4( 0, 1 * sign( @.z ), 0, @.a );\r\n",
unconditionedOutput, retVar, unconditionedOutput, unconditionedOutput ) );
break;
case LambertAzimuthal:
//http://en.wikipedia.org/wiki/Lambert_azimuthal_equal-area_projection
//
// Note we're casting to half to use partial precision
// sqrt which is much faster on older Geforces while
// still being acceptable for normals.
//
meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.X, normal.Y, depth Hi, depth Lo)\r\n" ) );
meta->addStatement( new GenOp( " @ = float4(@, 0.0, @.a);\r\n", outputDecl,
_posnegEncode(new GenOp("sqrt(half(2.0/(1.0 - @.y))) * half2(@.xz)", unconditionedOutput, unconditionedOutput)),
unconditionedOutput ) );
break;
}
// Encode depth into two channels
if(mNormalStorageType != CartesianXYZ)
{
const U64 maxValPerChannel = 1 << mBitsPerChannel;
meta->addStatement( new GenOp( " \r\n // Encode depth into hi/lo\r\n" ) );
meta->addStatement( new GenOp( avar( " float2 _tempDepth = frac(@.a * float2(1.0, %llu.0));\r\n", maxValPerChannel - 1 ),
unconditionedOutput ) );
meta->addStatement( new GenOp( avar( " @.zw = _tempDepth.xy - _tempDepth.yy * float2(1.0/%llu.0, 0.0);\r\n\r\n", maxValPerChannel - 1 ),
retVar ) );
}
AssertFatal( retVar != NULL, avar( "Cannot condition output to buffer format: %s", GFXStringTextureFormat[getBufferFormat()] ) );
return retVar;
}
Var* GBufferConditionerHLSL::_unconditionInput( Var *conditionedInput, MultiLine *meta )
{
Var *retVar = new Var;
retVar->setType("float4");
retVar->setName("_gbUnconditionedInput");
LangElement *outputDecl = new DecOp( retVar );
switch(mNormalStorageType)
{
case CartesianXYZ:
meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.xyz, depth)\r\n" ) );
meta->addStatement( new GenOp( " @ = float4(@, @.a);\r\n", outputDecl,
_posnegDecode(new GenOp("@.xyz", conditionedInput)), conditionedInput ) );
break;
case CartesianXY:
meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) );
meta->addStatement( new GenOp( " @ = float4(@, @.a);\r\n", outputDecl,
_posnegDecode(new GenOp("@.xyz", conditionedInput)), conditionedInput ) );
meta->addStatement( new GenOp( " @.z *= sqrt(1.0 - dot(@.xy, @.xy));\r\n", retVar, retVar, retVar ) );
break;
case Spherical:
meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) );
meta->addStatement( new GenOp( " float2 spGPUAngles = @;\r\n", _posnegDecode(new GenOp("@.xy", conditionedInput)) ) );
meta->addStatement( new GenOp( " float2 sincosTheta;\r\n" ) );
meta->addStatement( new GenOp( " sincos(spGPUAngles.x * 3.14159265358979323846f, sincosTheta.x, sincosTheta.y);\r\n" ) );
meta->addStatement( new GenOp( " float2 sincosPhi = float2(sqrt(1.0 - spGPUAngles.y * spGPUAngles.y), spGPUAngles.y);\r\n" ) );
meta->addStatement( new GenOp( " @ = float4(sincosTheta.y * sincosPhi.x, sincosTheta.x * sincosPhi.x, sincosPhi.y, @.a);\r\n", outputDecl, conditionedInput ) );
break;
case LambertAzimuthal:
// Note we're casting to half to use partial precision
// sqrt which is much faster on older Geforces while
// still being acceptable for normals.
//
meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.X, normal.Y, depth Hi, depth Lo)\r\n" ) );
meta->addStatement( new GenOp( " float2 _inpXY = @;\r\n", _posnegDecode(new GenOp("@.xy", conditionedInput)) ) );
meta->addStatement( new GenOp( " float _xySQ = dot(_inpXY, _inpXY);\r\n" ) );
meta->addStatement( new GenOp( " @ = float4( sqrt(half(1.0 - (_xySQ / 4.0))) * _inpXY, -1.0 + (_xySQ / 2.0), @.a).xzyw;\r\n", outputDecl, conditionedInput ) );
break;
}
// Recover depth from encoding
if(mNormalStorageType != CartesianXYZ)
{
const U64 maxValPerChannel = 1 << mBitsPerChannel;
meta->addStatement( new GenOp( " \r\n // Decode depth\r\n" ) );
meta->addStatement( new GenOp( avar( " @.w = dot( @.zw, float2(1.0, 1.0/%llu.0));\r\n", maxValPerChannel - 1 ),
retVar, conditionedInput ) );
}
AssertFatal( retVar != NULL, avar( "Cannot uncondition input from buffer format: %s", GFXStringTextureFormat[getBufferFormat()] ) );
return retVar;
}

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 _GBUFFER_CONDITIONER_HLSL_H_
#define _GBUFFER_CONDITIONER_HLSL_H_
#ifndef _CONDITIONER_BASE_H_
#include "shaderGen/conditionerFeature.h"
#endif
#ifndef _SHADEROP_H_
#include "shaderGen/shaderOp.h"
#endif
///
class GBufferConditionerHLSL : public ConditionerFeature
{
typedef ConditionerFeature Parent;
public:
enum NormalStorage
{
CartesianXYZ,
CartesianXY,
Spherical,
LambertAzimuthal,
};
enum NormalSpace
{
WorldSpace,
ViewSpace,
};
protected:
NormalStorage mNormalStorageType;
bool mCanWriteNegativeValues;
U32 mBitsPerChannel;
public:
GBufferConditionerHLSL( const GFXFormat bufferFormat, const NormalSpace nrmSpace );
virtual ~GBufferConditionerHLSL();
virtual void processVert( Vector<ShaderComponent*> &componentList, const MaterialFeatureData &fd );
virtual void processPix( Vector<ShaderComponent*> &componentList, const MaterialFeatureData &fd );
virtual Resources getResources( const MaterialFeatureData &fd );
virtual String getName() { return "GBuffer Conditioner"; }
protected:
virtual Var *printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta );
virtual GenOp* _posnegEncode( GenOp *val );
virtual GenOp* _posnegDecode( GenOp *val );
virtual Var* _conditionOutput( Var *unconditionedOutput, MultiLine *meta );
virtual Var* _unconditionInput( Var *conditionedInput, MultiLine *meta );
};
#endif // _GBUFFER_CONDITIONER_HLSL_H_

View file

@ -0,0 +1,412 @@
//-----------------------------------------------------------------------------
// 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/basic/basicLightManager.h"
#include "platform/platformTimer.h"
#include "console/simSet.h"
#include "console/consoleTypes.h"
#include "core/module.h"
#include "core/util/safeDelete.h"
#include "materials/processedMaterial.h"
#include "shaderGen/shaderFeature.h"
#include "lighting/basic/basicSceneObjectLightingPlugin.h"
#include "shaderGen/shaderGenVars.h"
#include "gfx/gfxShader.h"
#include "materials/sceneData.h"
#include "materials/materialParameters.h"
#include "materials/materialManager.h"
#include "materials/materialFeatureTypes.h"
#include "math/util/frustum.h"
#include "scene/sceneObject.h"
#include "renderInstance/renderPrePassMgr.h"
#include "shaderGen/featureMgr.h"
#include "shaderGen/HLSL/shaderFeatureHLSL.h"
#include "shaderGen/HLSL/bumpHLSL.h"
#include "shaderGen/HLSL/pixSpecularHLSL.h"
#include "lighting/basic/blInteriorSystem.h"
#include "lighting/basic/blTerrainSystem.h"
#include "lighting/common/projectedShadow.h"
#ifdef TORQUE_OS_MAC
#include "shaderGen/GLSL/shaderFeatureGLSL.h"
#include "shaderGen/GLSL/bumpGLSL.h"
#include "shaderGen/GLSL/pixSpecularGLSL.h"
#endif
MODULE_BEGIN( BasicLightManager )
MODULE_SHUTDOWN_AFTER( Scene )
MODULE_INIT
{
ManagedSingleton< BasicLightManager >::createSingleton();
}
MODULE_SHUTDOWN
{
ManagedSingleton< BasicLightManager >::deleteSingleton();
}
MODULE_END;
U32 BasicLightManager::smActiveShadowPlugins = 0;
U32 BasicLightManager::smShadowsUpdated = 0;
U32 BasicLightManager::smElapsedUpdateMs = 0;
F32 BasicLightManager::smProjectedShadowFilterDistance = 40.0f;
static S32 QSORT_CALLBACK comparePluginScores( const void *a, const void *b )
{
const BasicSceneObjectLightingPlugin *A = *((BasicSceneObjectLightingPlugin**)a);
const BasicSceneObjectLightingPlugin *B = *((BasicSceneObjectLightingPlugin**)b);
F32 dif = B->getScore() - A->getScore();
return (S32)mFloor( dif );
}
BasicLightManager::BasicLightManager()
: LightManager( "Basic Lighting", "BLM" ),
mLastShader(NULL),
mLastConstants(NULL)
{
mTimer = PlatformTimer::create();
mInteriorSystem = new blInteriorSystem;
mTerrainSystem = new blTerrainSystem;
getSceneLightingInterface()->registerSystem( mInteriorSystem );
getSceneLightingInterface()->registerSystem( mTerrainSystem );
Con::addVariable( "$BasicLightManagerStats::activePlugins",
TypeS32, &smActiveShadowPlugins,
"The number of active Basic Lighting SceneObjectLightingPlugin objects this frame.\n"
"@ingroup BasicLighting\n" );
Con::addVariable( "$BasicLightManagerStats::shadowsUpdated",
TypeS32, &smShadowsUpdated,
"The number of Basic Lighting shadows updated this frame.\n"
"@ingroup BasicLighting\n" );
Con::addVariable( "$BasicLightManagerStats::elapsedUpdateMs",
TypeS32, &smElapsedUpdateMs,
"The number of milliseconds spent this frame updating Basic Lighting shadows.\n"
"@ingroup BasicLighting\n" );
Con::addVariable( "$BasicLightManager::shadowFilterDistance",
TypeF32, &smProjectedShadowFilterDistance,
"The maximum distance in meters that projected shadows will get soft filtering.\n"
"@ingroup BasicLighting\n" );
Con::addVariable( "$pref::ProjectedShadow::fadeStartPixelSize",
TypeF32, &ProjectedShadow::smFadeStartPixelSize,
"A size in pixels at which BL shadows begin to fade out. "
"This should be a larger value than fadeEndPixelSize.\n"
"@see DecalData\n"
"@ingroup BasicLighting\n" );
Con::addVariable( "$pref::ProjectedShadow::fadeEndPixelSize",
TypeF32, &ProjectedShadow::smFadeEndPixelSize,
"A size in pixels at which BL shadows are fully faded out. "
"This should be a smaller value than fadeStartPixelSize.\n"
"@see DecalData\n"
"@ingroup BasicLighting\n" );
}
BasicLightManager::~BasicLightManager()
{
mLastShader = NULL;
mLastConstants = NULL;
for (LightConstantMap::Iterator i = mConstantLookup.begin(); i != mConstantLookup.end(); i++)
{
if (i->value)
SAFE_DELETE(i->value);
}
mConstantLookup.clear();
if (mTimer)
SAFE_DELETE( mTimer );
SAFE_DELETE( mTerrainSystem );
SAFE_DELETE( mInteriorSystem );
}
bool BasicLightManager::isCompatible() const
{
// As long as we have some shaders this works.
return GFX->getPixelShaderVersion() > 1.0;
}
void BasicLightManager::activate( SceneManager *sceneManager )
{
Parent::activate( sceneManager );
if( GFX->getAdapterType() == OpenGL )
{
#ifdef TORQUE_OS_MAC
FEATUREMGR->registerFeature( MFT_LightMap, new LightmapFeatGLSL );
FEATUREMGR->registerFeature( MFT_ToneMap, new TonemapFeatGLSL );
FEATUREMGR->registerFeature( MFT_NormalMap, new BumpFeatGLSL );
FEATUREMGR->registerFeature( MFT_RTLighting, new RTLightingFeatGLSL );
FEATUREMGR->registerFeature( MFT_PixSpecular, new PixelSpecularGLSL );
#endif
}
else
{
#ifndef TORQUE_OS_MAC
FEATUREMGR->registerFeature( MFT_LightMap, new LightmapFeatHLSL );
FEATUREMGR->registerFeature( MFT_ToneMap, new TonemapFeatHLSL );
FEATUREMGR->registerFeature( MFT_NormalMap, new BumpFeatHLSL );
FEATUREMGR->registerFeature( MFT_RTLighting, new RTLightingFeatHLSL );
FEATUREMGR->registerFeature( MFT_PixSpecular, new PixelSpecularHLSL );
#endif
}
FEATUREMGR->unregisterFeature( MFT_MinnaertShading );
FEATUREMGR->unregisterFeature( MFT_SubSurface );
// First look for the prepass bin...
RenderPrePassMgr *prePassBin = _findPrePassRenderBin();
/*
// If you would like to use forward shading, and have a linear depth pre-pass
// than un-comment this code block.
if ( !prePassBin )
{
Vector<GFXFormat> formats;
formats.push_back( GFXFormatR32F );
formats.push_back( GFXFormatR16F );
formats.push_back( GFXFormatR8G8B8A8 );
GFXFormat linearDepthFormat = GFX->selectSupportedFormat( &GFXDefaultRenderTargetProfile,
formats,
true,
false );
// Uncomment this for a no-color-write z-fill pass.
//linearDepthFormat = GFXFormat_COUNT;
prePassBin = new RenderPrePassMgr( linearDepthFormat != GFXFormat_COUNT, linearDepthFormat );
prePassBin->registerObject();
rpm->addManager( prePassBin );
}
*/
mPrePassRenderBin = prePassBin;
// If there is a prepass bin
MATMGR->setPrePassEnabled( mPrePassRenderBin.isValid() );
sceneManager->setPostEffectFog( mPrePassRenderBin.isValid() && mPrePassRenderBin->getTargetChainLength() > 0 );
// Tell the material manager that we don't use prepass.
MATMGR->setPrePassEnabled( false );
GFXShader::addGlobalMacro( "TORQUE_BASIC_LIGHTING" );
// Hook into the SceneManager prerender signal.
sceneManager->getPreRenderSignal().notify( this, &BasicLightManager::_onPreRender );
// Last thing... let everyone know we're active.
smActivateSignal.trigger( getId(), true );
}
void BasicLightManager::deactivate()
{
Parent::deactivate();
mLastShader = NULL;
mLastConstants = NULL;
for (LightConstantMap::Iterator i = mConstantLookup.begin(); i != mConstantLookup.end(); i++)
{
if (i->value)
SAFE_DELETE(i->value);
}
mConstantLookup.clear();
if ( mPrePassRenderBin )
mPrePassRenderBin->deleteObject();
mPrePassRenderBin = NULL;
GFXShader::removeGlobalMacro( "TORQUE_BASIC_LIGHTING" );
// Remove us from the prerender signal.
getSceneManager()->getPreRenderSignal().remove( this, &BasicLightManager::_onPreRender );
// Now let everyone know we've deactivated.
smActivateSignal.trigger( getId(), false );
}
void BasicLightManager::_onPreRender( SceneManager *sceneManger, const SceneRenderState *state )
{
// Update all our shadow plugins here!
Vector<BasicSceneObjectLightingPlugin*> *pluginInsts = BasicSceneObjectLightingPlugin::getPluginInstances();
Vector<BasicSceneObjectLightingPlugin*>::const_iterator pluginIter = (*pluginInsts).begin();
for ( ; pluginIter != (*pluginInsts).end(); pluginIter++ )
{
BasicSceneObjectLightingPlugin *plugin = *pluginIter;
plugin->updateShadow( (SceneRenderState*)state );
}
U32 pluginCount = (*pluginInsts).size();
// Sort them by the score.
dQsort( (*pluginInsts).address(), pluginCount, sizeof(BasicSceneObjectLightingPlugin*), comparePluginScores );
mTimer->getElapsedMs();
mTimer->reset();
U32 numUpdated = 0;
U32 targetMs = 5;
S32 updateMs = 0;
pluginIter = (*pluginInsts).begin();
for ( ; pluginIter != (*pluginInsts).end(); pluginIter++ )
{
BasicSceneObjectLightingPlugin *plugin = *pluginIter;
// If we run out of update time then stop.
updateMs = mTimer->getElapsedMs();
if ( updateMs >= targetMs )
break;
// NOTE! Fix this all up to past const SceneRenderState!
plugin->renderShadow( (SceneRenderState*)state );
numUpdated++;
}
smShadowsUpdated = numUpdated;
smActiveShadowPlugins = pluginCount;
smElapsedUpdateMs = updateMs;
}
BasicLightManager::LightingShaderConstants::LightingShaderConstants()
: mInit( false ),
mShader( NULL ),
mLightPosition( NULL ),
mLightDiffuse( NULL ),
mLightAmbient( NULL ),
mLightInvRadiusSq( NULL ),
mLightSpotDir( NULL ),
mLightSpotAngle( NULL ),
mLightSpotFalloff( NULL )
{
}
BasicLightManager::LightingShaderConstants::~LightingShaderConstants()
{
if (mShader.isValid())
{
mShader->getReloadSignal().remove( this, &LightingShaderConstants::_onShaderReload );
mShader = NULL;
}
}
void BasicLightManager::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 );
}
mLightPosition = shader->getShaderConstHandle( ShaderGenVars::lightPosition );
mLightDiffuse = shader->getShaderConstHandle( ShaderGenVars::lightDiffuse);
mLightInvRadiusSq = shader->getShaderConstHandle( ShaderGenVars::lightInvRadiusSq );
mLightAmbient = shader->getShaderConstHandle( ShaderGenVars::lightAmbient );
mLightSpotDir = shader->getShaderConstHandle( ShaderGenVars::lightSpotDir );
mLightSpotAngle = shader->getShaderConstHandle( ShaderGenVars::lightSpotAngle );
mLightSpotFalloff = shader->getShaderConstHandle( ShaderGenVars::lightSpotFalloff );
mInit = true;
}
void BasicLightManager::LightingShaderConstants::_onShaderReload()
{
if (mShader.isValid())
init( mShader );
}
void BasicLightManager::setLightInfo( ProcessedMaterial* pmat,
const Material* mat,
const SceneData& sgData,
const SceneRenderState *state,
U32 pass,
GFXShaderConstBuffer* shaderConsts )
{
PROFILE_SCOPE( BasicLightManager_SetLightInfo );
GFXShader *shader = shaderConsts->getShader();
// Check to see if this is the same shader. Since we
// sort by material we should get hit repeatedly by the
// same one. This optimization should save us many
// hash table lookups.
if ( mLastShader.getPointer() != shader )
{
LightConstantMap::Iterator iter = mConstantLookup.find(shader);
if ( iter != mConstantLookup.end() )
{
mLastConstants = iter->value;
}
else
{
LightingShaderConstants* lsc = new LightingShaderConstants();
mConstantLookup[shader] = lsc;
mLastConstants = lsc;
}
// Set our new shader
mLastShader = shader;
}
// Make sure that our current lighting constants are initialized
if (!mLastConstants->mInit)
mLastConstants->init(shader);
// NOTE: If you encounter a crash from this point forward
// while setting a shader constant its probably because the
// mConstantLookup has bad shaders/constants in it.
//
// This is a known crash bug that can occur if materials/shaders
// are reloaded and the light manager is not reset.
//
// We should look to fix this by clearing the table.
_update4LightConsts( sgData,
mLastConstants->mLightPosition,
mLastConstants->mLightDiffuse,
mLastConstants->mLightAmbient,
mLastConstants->mLightInvRadiusSq,
mLastConstants->mLightSpotDir,
mLastConstants->mLightSpotAngle,
mLastConstants->mLightSpotFalloff,
shaderConsts );
}

View file

@ -0,0 +1,134 @@
//-----------------------------------------------------------------------------
// 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 _BASICLIGHTMANAGER_H_
#define _BASICLIGHTMANAGER_H_
#ifndef _LIGHTMANAGER_H_
#include "lighting/lightManager.h"
#endif
#ifndef _TDICTIONARY_H_
#include "core/util/tDictionary.h"
#endif
#ifndef _GFXSHADER_H_
#include "gfx/gfxShader.h"
#endif
#ifndef _SIMOBJECT_H_
#include "console/simObject.h"
#endif
#ifndef _TSINGLETON_H_
#include "core/util/tSingleton.h"
#endif
class AvailableSLInterfaces;
class GFXShaderConstHandle;
class RenderPrePassMgr;
class PlatformTimer;
class blInteriorSystem;
class blTerrainSystem;
class BasicLightManager : public LightManager
{
typedef LightManager Parent;
// For access to protected constructor.
friend class ManagedSingleton<BasicLightManager>;
public:
// LightManager
virtual bool isCompatible() const;
virtual void activate( SceneManager *sceneManager );
virtual void deactivate();
virtual void setLightInfo(ProcessedMaterial* pmat, const Material* mat, const SceneData& sgData, const SceneRenderState *state, U32 pass, GFXShaderConstBuffer* shaderConsts);
virtual bool setTextureStage(const SceneData& sgData, const U32 currTexFlag, const U32 textureSlot, GFXShaderConstBuffer* shaderConsts, ShaderConstHandles* handles) { return false; }
static F32 getShadowFilterDistance() { return smProjectedShadowFilterDistance; }
protected:
// LightManager
virtual void _addLightInfoEx( LightInfo *lightInfo ) { }
virtual void _initLightFields() { }
void _onPreRender( SceneManager *sceneManger, const SceneRenderState *state );
// These are protected because we're a singleton and
// no one else should be creating us!
BasicLightManager();
virtual ~BasicLightManager();
SimObjectPtr<RenderPrePassMgr> mPrePassRenderBin;
struct LightingShaderConstants
{
bool mInit;
GFXShaderRef mShader;
GFXShaderConstHandle *mLightPosition;
GFXShaderConstHandle *mLightDiffuse;
GFXShaderConstHandle *mLightAmbient;
GFXShaderConstHandle *mLightInvRadiusSq;
GFXShaderConstHandle *mLightSpotDir;
GFXShaderConstHandle *mLightSpotAngle;
GFXShaderConstHandle *mLightSpotFalloff;
LightingShaderConstants();
~LightingShaderConstants();
void init( GFXShader *shader );
void _onShaderReload();
};
typedef Map<GFXShader*, LightingShaderConstants*> LightConstantMap;
LightConstantMap mConstantLookup;
GFXShaderRef mLastShader;
LightingShaderConstants* mLastConstants;
/// Statics used for light manager/projected shadow metrics.
static U32 smActiveShadowPlugins;
static U32 smShadowsUpdated;
static U32 smElapsedUpdateMs;
/// This is used to determine the distance
/// at which the shadow filtering PostEffect
/// will be enabled for ProjectedShadow.
static F32 smProjectedShadowFilterDistance;
/// A timer used for tracking update time.
PlatformTimer *mTimer;
blInteriorSystem* mInteriorSystem;
blTerrainSystem* mTerrainSystem;
public:
// For ManagedSingleton.
static const char* getSingletonName() { return "BasicLightManager"; }
};
#define BLM ManagedSingleton<BasicLightManager>::instance()
#endif // _BASICLIGHTMANAGER_H_

View file

@ -0,0 +1,230 @@
//-----------------------------------------------------------------------------
// 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/basic/basicSceneObjectLightingPlugin.h"
#include "lighting/lightManager.h"
#include "lighting/shadowMap/shadowMapPass.h"
#include "lighting/shadowMap/shadowMapManager.h"
#include "lighting/common/projectedShadow.h"
#include "T3D/shapeBase.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxTransformSaver.h"
#include "ts/tsRenderState.h"
#include "gfx/sim/cubemapData.h"
#include "scene/reflector.h"
#include "T3D/decal/decalManager.h"
#include "core/module.h"
MODULE_BEGIN( BasicSceneObjectLightingPlugin )
MODULE_INIT
{
BasicSceneObjectPluginFactory::createSingleton();
}
MODULE_SHUTDOWN
{
BasicSceneObjectPluginFactory::deleteSingleton();
}
MODULE_END;
static const U32 shadowObjectTypeMask = PlayerObjectType | CorpseObjectType | ItemObjectType | VehicleObjectType;
Vector<BasicSceneObjectLightingPlugin*> BasicSceneObjectLightingPlugin::smPluginInstances( __FILE__, __LINE__ );
BasicSceneObjectLightingPlugin::BasicSceneObjectLightingPlugin(SceneObject* parent)
: mParentObject( parent )
{
mShadow = NULL;
// Stick us on the list.
smPluginInstances.push_back( this );
}
BasicSceneObjectLightingPlugin::~BasicSceneObjectLightingPlugin()
{
SAFE_DELETE( mShadow );
// Delete us from the list.
smPluginInstances.remove( this );
}
void BasicSceneObjectLightingPlugin::reset()
{
SAFE_DELETE( mShadow );
}
void BasicSceneObjectLightingPlugin::cleanupPluginInstances()
{
for (U32 i = 0; i < smPluginInstances.size(); i++)
{
BasicSceneObjectLightingPlugin *plug = smPluginInstances[i];
smPluginInstances.remove( plug );
delete plug;
i--;
}
smPluginInstances.clear();
}
void BasicSceneObjectLightingPlugin::resetAll()
{
for( U32 i = 0, num = smPluginInstances.size(); i < num; ++ i )
smPluginInstances[ i ]->reset();
}
const F32 BasicSceneObjectLightingPlugin::getScore() const
{
return mShadow ? mShadow->getScore() : 0.0f;
}
void BasicSceneObjectLightingPlugin::updateShadow( SceneRenderState *state )
{
if ( !mShadow )
mShadow = new ProjectedShadow( mParentObject );
mShadow->update( state );
}
void BasicSceneObjectLightingPlugin::renderShadow( SceneRenderState *state )
{
// hack until new scenegraph in place
GFXTransformSaver ts;
TSRenderState rstate;
rstate.setSceneState(state);
F32 camDist = (state->getCameraPosition() - mParentObject->getRenderPosition()).len();
// Make sure the shadow wants to be rendered
if( mShadow->shouldRender( state ) )
{
// Render! (and note the time)
mShadow->render( camDist, rstate );
}
}
BasicSceneObjectPluginFactory::BasicSceneObjectPluginFactory()
: mEnabled( false )
{
LightManager::smActivateSignal.notify( this, &BasicSceneObjectPluginFactory::_onLMActivate );
ShadowMapManager::smShadowDeactivateSignal.notify( this, &BasicSceneObjectPluginFactory::_setEnabled );
}
BasicSceneObjectPluginFactory::~BasicSceneObjectPluginFactory()
{
LightManager::smActivateSignal.remove( this, &BasicSceneObjectPluginFactory::_onLMActivate );
ShadowMapManager::smShadowDeactivateSignal.remove( this, &BasicSceneObjectPluginFactory::_setEnabled );
}
void BasicSceneObjectPluginFactory::_onLMActivate( const char *lm, bool enable )
{
_setEnabled();
}
void BasicSceneObjectPluginFactory::_setEnabled()
{
bool enable = false;
// Enabled if using basic lighting.
LightManager *lm = LightManager::getActiveLM();
if ( lm && dStricmp( lm->getName(), "Basic Lighting" ) == 0 )
enable = true;
// Disabled if all shadows are explictly disabled.
if ( ShadowMapPass::smDisableShadows )
enable = false;
// Already at the desired state.
if ( enable == mEnabled )
return;
if ( enable )
{
SceneObject::smSceneObjectAdd.notify(this, &BasicSceneObjectPluginFactory::addLightPlugin);
SceneObject::smSceneObjectRemove.notify(this, &BasicSceneObjectPluginFactory::removeLightPlugin);
if( gDecalManager )
gDecalManager->getClearDataSignal().notify( this, &BasicSceneObjectPluginFactory::_onDecalManagerClear );
addToExistingObjects();
}
else
{
SceneObject::smSceneObjectAdd.remove(this, &BasicSceneObjectPluginFactory::addLightPlugin);
SceneObject::smSceneObjectRemove.remove(this, &BasicSceneObjectPluginFactory::removeLightPlugin);
if( gDecalManager )
gDecalManager->getClearDataSignal().remove( this, &BasicSceneObjectPluginFactory::_onDecalManagerClear );
BasicSceneObjectLightingPlugin::cleanupPluginInstances();
}
mEnabled = enable;
}
void BasicSceneObjectPluginFactory::_onDecalManagerClear()
{
BasicSceneObjectLightingPlugin::resetAll();
}
void BasicSceneObjectPluginFactory::removeLightPlugin( SceneObject *obj )
{
// Grab the plugin instance.
SceneObjectLightingPlugin *lightPlugin = obj->getLightingPlugin();
// Delete it, which will also remove it
// from the static list of plugin instances.
if ( lightPlugin )
{
delete lightPlugin;
obj->setLightingPlugin( NULL );
}
}
void BasicSceneObjectPluginFactory::addLightPlugin(SceneObject* obj)
{
bool serverObj = obj->isServerObject();
bool isShadowType = (obj->getTypeMask() & shadowObjectTypeMask);
if ( !isShadowType || serverObj )
return;
obj->setLightingPlugin(new BasicSceneObjectLightingPlugin(obj));
}
// Some objects may not get cleaned up during mission load/free, so add our
// plugin to existing scene objects
void BasicSceneObjectPluginFactory::addToExistingObjects()
{
SimpleQueryList sql;
gClientContainer.findObjects( shadowObjectTypeMask, SimpleQueryList::insertionCallback, &sql);
for (SceneObject** i = sql.mList.begin(); i != sql.mList.end(); i++)
addLightPlugin(*i);
}

View file

@ -0,0 +1,99 @@
//-----------------------------------------------------------------------------
// 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 _BASICSCENEOBJECTLIGHTINGPLUGIN_H_
#define _BASICSCENEOBJECTLIGHTINGPLUGIN_H_
#ifndef _SCENEOBJECTLIGHTINGPLUGIN_H_
#include "scene/sceneObjectLightingPlugin.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
#ifndef _TSINGLETON_H_
#include "core/util/tSingleton.h"
#endif
class ShadowBase;
class BasicSceneObjectLightingPlugin : public SceneObjectLightingPlugin
{
private:
ShadowBase* mShadow;
SceneObject* mParentObject;
static Vector<BasicSceneObjectLightingPlugin*> smPluginInstances;
public:
BasicSceneObjectLightingPlugin(SceneObject* parent);
~BasicSceneObjectLightingPlugin();
static Vector<BasicSceneObjectLightingPlugin*>* getPluginInstances() { return &smPluginInstances; }
static void cleanupPluginInstances();
static void resetAll();
const F32 getScore() const;
// Called from BasicLightManager
virtual void updateShadow( SceneRenderState *state );
virtual void renderShadow( SceneRenderState *state );
// Called by statics
virtual U32 packUpdate(SceneObject* obj, U32 checkMask, NetConnection *conn, U32 mask, BitStream *stream) { return 0; }
virtual void unpackUpdate(SceneObject* obj, NetConnection *conn, BitStream *stream) { }
virtual void reset();
};
class BasicSceneObjectPluginFactory : public ManagedSingleton< BasicSceneObjectPluginFactory >
{
protected:
/// Called from the light manager on activation.
/// @see LightManager::addActivateCallback
void _onLMActivate( const char *lm, bool enable );
void _onDecalManagerClear();
void removeLightPlugin(SceneObject* obj);
void addLightPlugin(SceneObject* obj);
void addToExistingObjects();
bool mEnabled;
public:
BasicSceneObjectPluginFactory();
~BasicSceneObjectPluginFactory();
// For ManagedSingleton.
static const char* getSingletonName() { return "BasicSceneObjectPluginFactory"; }
void _setEnabled();
};
#endif // !_BASICSCENEOBJECTLIGHTINGPLUGIN_H_

File diff suppressed because it is too large Load diff

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 _BLINTERIOSYSTEM_H_
#define _BLINTERIOSYSTEM_H_
#ifndef _SCENELIGHTING_H_
#include "lighting/common/sceneLighting.h"
#endif
#ifndef _SG_SYSTEM_INTERFACE_H
#include "lighting/lightingInterfaces.h"
#endif
//
// Lighting system interface
//
class blInteriorSystem : public SceneLightingInterface
{
public:
static bool smUseVertexLighting;
virtual SceneLighting::ObjectProxy* createObjectProxy(SceneObject* obj, SceneLighting::ObjectProxyList* sceneObjects);
virtual PersistInfo::PersistChunk* createPersistChunk(const U32 chunkType);
virtual bool createPersistChunkFromProxy(SceneLighting::ObjectProxy* objproxy, PersistInfo::PersistChunk **ret);
virtual void init();
virtual U32 addObjectType();
virtual U32 addToClippingMask();
virtual void processLightingBegin();
virtual void processLightingCompleted(bool success);
// Given a ray, this will return the color from the lightmap of this object, return true if handled
virtual bool getColorFromRayInfo(RayInfo collision, ColorF& result);
};
#endif // !_BLINTERIOSYSTEM_H_

View file

@ -0,0 +1,715 @@
//-----------------------------------------------------------------------------
// 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/basic/blTerrainSystem.h"
#include "core/bitVector.h"
#include "lighting/common/shadowVolumeBSP.h"
#include "lighting/lightingInterfaces.h"
#include "terrain/terrData.h"
#include "lighting/basic/basicLightManager.h"
#include "lighting/common/sceneLighting.h"
#include "gfx/bitmap/gBitmap.h"
#include "collision/collision.h"
extern SceneLighting* gLighting;
struct blTerrainChunk : public PersistInfo::PersistChunk
{
typedef PersistInfo::PersistChunk Parent;
blTerrainChunk();
~blTerrainChunk();
GBitmap *mLightmap;
bool read(Stream &);
bool write(Stream &);
};
//------------------------------------------------------------------------------
// Class SceneLighting::TerrainChunk
//------------------------------------------------------------------------------
blTerrainChunk::blTerrainChunk()
{
mChunkType = PersistChunk::TerrainChunkType;
mLightmap = NULL;
}
blTerrainChunk::~blTerrainChunk()
{
if(mLightmap)
delete mLightmap;
}
//------------------------------------------------------------------------------
bool blTerrainChunk::read(Stream & stream)
{
if(!Parent::read(stream))
return(false);
mLightmap = new GBitmap();
return mLightmap->readBitmap("png",stream);
}
bool blTerrainChunk::write(Stream & stream)
{
if(!Parent::write(stream))
return(false);
if(!mLightmap)
return(false);
if(!mLightmap->writeBitmap("png",stream))
return(false);
return(true);
}
class blTerrainProxy : public SceneLighting::ObjectProxy
{
protected:
typedef ObjectProxy Parent;
BitVector mShadowMask;
ShadowVolumeBSP * mShadowVolume;
ColorF * mLightmap;
/// The dimension of the lightmap in pixels.
const U32 mLightMapSize;
/// The dimension of the terrain height map sample array.
const U32 mTerrainBlockSize;
ColorF *sgBakedLightmap;
Vector<LightInfo *> sgLights;
bool sgMarkStaticShadow(void *terrainproxy, SceneObject *sceneobject, LightInfo *light);
//void postLight(bool lastLight);
void lightVector(LightInfo *);
struct SquareStackNode
{
U8 mLevel;
U16 mClipFlags;
Point2I mPos;
};
S32 testSquare(const Point3F &, const Point3F &, S32, F32, const Vector<PlaneF> &);
bool markObjectShadow(ObjectProxy *);
bool sgIsCorrectStaticObjectType(SceneObject *obj);
inline ColorF _getValue( S32 row, S32 column );
public:
blTerrainProxy(SceneObject * obj);
~blTerrainProxy();
TerrainBlock * operator->() {return(static_cast<TerrainBlock*>(static_cast<SceneObject*>(mObj)));}
TerrainBlock * getObject() {return(static_cast<TerrainBlock*>(static_cast<SceneObject*>(mObj)));}
bool getShadowedSquares(const Vector<PlaneF> &, Vector<U16> &);
// lighting
void init();
bool preLight(LightInfo *);
void light(LightInfo *);
// persist
U32 getResourceCRC();
bool setPersistInfo(PersistInfo::PersistChunk *);
bool getPersistInfo(PersistInfo::PersistChunk *);
virtual bool supportsShadowVolume();
virtual void getClipPlanes(Vector<PlaneF>& planes);
virtual void addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level);
// events
//virtual void processTGELightProcessEvent(U32 curr, U32 max, LightInfo* currlight);
//virtual void processSGObjectProcessEvent(LightInfo* currLight);
};
//-------------------------------------------------------------------------------
// Class SceneLighting::TerrainProxy:
//-------------------------------------------------------------------------------
blTerrainProxy::blTerrainProxy( SceneObject *obj ) :
Parent( obj ),
mLightMapSize( getObject()->getLightMapSize() ),
mTerrainBlockSize( getObject()->getBlockSize() ),
mLightmap( NULL )
{
}
blTerrainProxy::~blTerrainProxy()
{
delete [] mLightmap;
}
//-------------------------------------------------------------------------------
void blTerrainProxy::init()
{
mLightmap = new ColorF[ mLightMapSize * mLightMapSize ];
dMemset(mLightmap, 0, mLightMapSize * mLightMapSize * sizeof(ColorF));
mShadowMask.setSize( mTerrainBlockSize * mTerrainBlockSize );
}
bool blTerrainProxy::preLight(LightInfo * light)
{
if(!bool(mObj))
return(false);
if(light->getType() != LightInfo::Vector)
return(false);
mShadowMask.clear();
return(true);
}
inline ColorF blTerrainProxy::_getValue( S32 row, S32 column )
{
while( row < 0 )
row += mLightMapSize;
row = row % mLightMapSize;
while( column < 0 )
column += mLightMapSize;
column = column % mLightMapSize;
U32 offset = row * mLightMapSize + column;
return mLightmap[offset];
}
bool blTerrainProxy::markObjectShadow(ObjectProxy * proxy)
{
if (!proxy->supportsShadowVolume())
return false;
// setup the clip planes
Vector<PlaneF> clipPlanes;
proxy->getClipPlanes(clipPlanes);
Vector<U16> shadowList;
if(!getShadowedSquares(clipPlanes, shadowList))
return(false);
// set the correct bit
for(U32 i = 0; i < shadowList.size(); i++)
mShadowMask.set(shadowList[i]);
return(true);
}
void blTerrainProxy::light(LightInfo * light)
{
// If we don't have terrain or its not a directional
// light then skip processing.
TerrainBlock * terrain = getObject();
if ( !terrain || light->getType() != LightInfo::Vector )
return;
S32 time = Platform::getRealMilliseconds();
// reset
mShadowVolume = new ShadowVolumeBSP;
// build interior shadow volume
for(ObjectProxy ** itr = gLighting->mLitObjects.begin(); itr != gLighting->mLitObjects.end(); itr++)
{
ObjectProxy* objproxy = *itr;
if (markObjectShadow(objproxy))
objproxy->addToShadowVolume(mShadowVolume, light, SceneLighting::SHADOW_DETAIL);
}
lightVector(light);
// set the lightmap...
terrain->clearLightMap();
// Blur...
F32 kernel[3][3] = { {1, 2, 1},
{2, 3, 2},
{1, 2, 1} };
F32 modifier = 1;
F32 divisor = 0;
for( U32 i=0; i<3; i++ )
{
for( U32 j=0; j<3; j++ )
{
if( i==1 && j==1 )
{
kernel[i][j] = 1 + kernel[i][j] * modifier;
}
else
{
kernel[i][j] = kernel[i][j] * modifier;
}
divisor += kernel[i][j];
}
}
for( U32 i=0; i < mLightMapSize; i++ )
{
for( U32 j=0; j < mLightMapSize; j++ )
{
ColorF val;
val = _getValue( i-1, j-1 ) * kernel[0][0];
val += _getValue( i-1, j ) * kernel[0][1];
val += _getValue( i-1, j+1 ) * kernel[0][2];
val += _getValue( i, j-1 ) * kernel[1][0];
val += _getValue( i, j ) * kernel[1][1];
val += _getValue( i, j+1 ) * kernel[1][2];
val += _getValue( i+1, j-1 ) * kernel[2][0];
val += _getValue( i+1, j ) * kernel[2][1];
val += _getValue( i+1, j+1 ) * kernel[2][2];
U32 edge = 0;
if( j == 0 || j == mLightMapSize - 1 )
edge++;
if( i == 0 || i == mLightMapSize - 1 )
edge++;
if( !edge )
val = val / divisor;
else
val = mLightmap[ i * mLightMapSize + j ];
// clamp values
mLightmap[ i * mLightMapSize + j ]= val;
}
}
// And stuff it into the texture...
GBitmap *terrLightMap = terrain->getLightMap();
for(U32 y = 0; y < mLightMapSize; y++)
{
for(U32 x = 0; x < mLightMapSize; x++)
{
ColorI color(255, 255, 255, 255);
color.red = mLightmap[x + y * mLightMapSize].red * 255;
color.green = mLightmap[x + y * mLightMapSize].green * 255;
color.blue = mLightmap[x + y * mLightMapSize].blue * 255;
terrLightMap->setColor(x, y, color);
}
}
/*
// This handles matching up the outer edges of the terrain
// lightmap when it has neighbors
if (!terrain->isTiling())
{
for (S32 y = 0; y < terrLightMap->getHeight(); y++)
{
ColorI c;
if (terrain->getFile()->mEdgeTerrainFiles[0])
{
terrLightMap->getColor(terrLightMap->getWidth()-1,y,c);
terrLightMap->setColor(0,y,c);
terrLightMap->setColor(1,y,c);
}
else
{
terrLightMap->getColor(0,y,c);
terrLightMap->setColor(terrLightMap->getWidth()-1,y,c);
terrLightMap->setColor(terrLightMap->getWidth()-2,y,c);
}
}
for (S32 x = 0; x < terrLightMap->getHeight(); x++)
{
ColorI c;
if (terrain->getFile()->mEdgeTerrainFiles[1])
{
terrLightMap->getColor(x,terrLightMap->getHeight()-1,c);
terrLightMap->setColor(x,0,c);
terrLightMap->setColor(x,1,c);
}
else
{
terrLightMap->getColor(x,0,c);
terrLightMap->setColor(x,terrLightMap->getHeight()-1,c);
terrLightMap->setColor(x,terrLightMap->getHeight()-2,c);
}
}
}
*/
delete mShadowVolume;
Con::printf(" = terrain lit in %3.3f seconds", (Platform::getRealMilliseconds()-time)/1000.f);
}
//------------------------------------------------------------------------------
S32 blTerrainProxy::testSquare(const Point3F & min, const Point3F & max, S32 mask, F32 expand, const Vector<PlaneF> & clipPlanes)
{
expand = 0;
S32 retMask = 0;
Point3F minPoint, maxPoint;
for(S32 i = 0; i < clipPlanes.size(); i++)
{
if(mask & (1 << i))
{
if(clipPlanes[i].x > 0)
{
maxPoint.x = max.x;
minPoint.x = min.x;
}
else
{
maxPoint.x = min.x;
minPoint.x = max.x;
}
if(clipPlanes[i].y > 0)
{
maxPoint.y = max.y;
minPoint.y = min.y;
}
else
{
maxPoint.y = min.y;
minPoint.y = max.y;
}
if(clipPlanes[i].z > 0)
{
maxPoint.z = max.z;
minPoint.z = min.z;
}
else
{
maxPoint.z = min.z;
minPoint.z = max.z;
}
F32 maxDot = mDot(maxPoint, clipPlanes[i]);
F32 minDot = mDot(minPoint, clipPlanes[i]);
F32 planeD = clipPlanes[i].d;
if(maxDot <= -(planeD + expand))
return(U16(-1));
if(minDot <= -planeD)
retMask |= (1 << i);
}
}
return(retMask);
}
bool blTerrainProxy::getShadowedSquares(const Vector<PlaneF> & clipPlanes, Vector<U16> & shadowList)
{
TerrainBlock *terrain = getObject();
if ( !terrain )
return false;
// TODO: Fix me for variable terrain sizes!
return true;
/*
SquareStackNode stack[TerrainBlock::BlockShift * 4];
stack[0].mLevel = TerrainBlock::BlockShift;
stack[0].mClipFlags = 0xff;
stack[0].mPos.set(0,0);
U32 stackSize = 1;
Point3F blockPos;
terrain->getTransform().getColumn(3, &blockPos);
S32 squareSize = terrain->getSquareSize();
F32 floatSquareSize = (F32)squareSize;
bool marked = false;
// push through all the levels of the quadtree
while(stackSize)
{
SquareStackNode * node = &stack[stackSize - 1];
S32 clipFlags = node->mClipFlags;
Point2I pos = node->mPos;
GridSquare * sq = terrain->findSquare(node->mLevel, pos);
Point3F minPoint, maxPoint;
minPoint.set(squareSize * pos.x + blockPos.x,
squareSize * pos.y + blockPos.y,
fixedToFloat(sq->minHeight));
maxPoint.set(minPoint.x + (squareSize << node->mLevel),
minPoint.y + (squareSize << node->mLevel),
fixedToFloat(sq->maxHeight));
// test the square against the current level
if(clipFlags)
{
clipFlags = testSquare(minPoint, maxPoint, clipFlags, floatSquareSize, clipPlanes);
if(clipFlags == U16(-1))
{
stackSize--;
continue;
}
}
// shadowed?
if(node->mLevel == 0)
{
marked = true;
shadowList.push_back(pos.x + (pos.y << TerrainBlock::BlockShift));
stackSize--;
continue;
}
// setup the next level of squares
U8 nextLevel = node->mLevel - 1;
S32 squareHalfSize = 1 << nextLevel;
for(U32 i = 0; i < 4; i++)
{
node[i].mLevel = nextLevel;
node[i].mClipFlags = clipFlags;
}
node[3].mPos = pos;
node[2].mPos.set(pos.x + squareHalfSize, pos.y);
node[1].mPos.set(pos.x, pos.y + squareHalfSize);
node[0].mPos.set(pos.x + squareHalfSize, pos.y + squareHalfSize);
stackSize += 3;
}
return marked;
*/
}
void blTerrainProxy::lightVector(LightInfo * light)
{
// Grab our terrain object
TerrainBlock* terrain = getObject();
if (!terrain)
return;
// Get the direction to the light (the inverse of the direction
// the light is pointing)
Point3F lightDir = -light->getDirection();
lightDir.normalize();
// Get the ratio between the light map pixel and world space (used below)
F32 lmTerrRatio = (F32)mTerrainBlockSize / (F32) mLightMapSize;
lmTerrRatio *= terrain->getSquareSize();
// Get the terrain position
Point3F terrPos( terrain->getTransform().getPosition() );
U32 i = 0;
for (U32 y = 0; y < mLightMapSize; y++)
{
for (U32 x = 0; x < mLightMapSize; x++)
{
// Get the relative pixel position and scale it
// by the ratio between lightmap and world space
Point2F pixelPos(x, y);
pixelPos *= lmTerrRatio;
// Start with a default normal of straight up
Point3F normal(0.0f, 0.0f, 1.0f);
// Try to get the actual normal from the terrain.
// Note: this won't change the default normal if
// it can't find a normal.
terrain->getNormal(pixelPos, &normal);
// The terrain lightmap only contains shadows.
F32 shadowed = 0.0f;
// Get the height at the lightmap pixel's position
F32 height = 0.0f;
terrain->getHeight(pixelPos, &height);
// Calculate the 3D position of the pixel
Point3F pixelPos3F(pixelPos.x, pixelPos.y, height);
// Translate that position by the terrain's transform
terrain->getTransform().mulP(pixelPos3F);
// Offset slighting along the normal so that we don't
// raycast into ourself
pixelPos3F += (normal * 0.1f);
// Calculate the light's position.
// If it is a vector light like the sun (no position
// just direction) then translate along that direction
// a reasonable distance to get a point sufficiently
// far away
Point3F lightPos = light->getPosition();
if(light->getType() == LightInfo::Vector)
{
lightPos = 1000.f * lightDir;
lightPos = pixelPos3F + lightPos;
}
// Cast a ray from the world space position of the lightmap pixel to the light source.
// If we hit something then we are in shadow. This allows us to be shadowed by anything
// that supports a castRay operation.
RayInfo info;
if(terrain->getContainer()->castRay(pixelPos3F, lightPos, STATIC_COLLISION_TYPEMASK, &info))
{
// Shadow the pixel.
shadowed = 1.0f;
}
// Set the final lightmap color.
mLightmap[i++] += ColorF::WHITE * mClampF( 1.0f - shadowed, 0.0f, 1.0f );
}
}
}
//--------------------------------------------------------------------------
U32 blTerrainProxy::getResourceCRC()
{
TerrainBlock * terrain = getObject();
if(!terrain)
return(0);
return(terrain->getCRC());
}
//--------------------------------------------------------------------------
bool blTerrainProxy::setPersistInfo(PersistInfo::PersistChunk * info)
{
if(!Parent::setPersistInfo(info))
return(false);
blTerrainChunk * chunk = dynamic_cast<blTerrainChunk*>(info);
AssertFatal(chunk, "blTerrainProxy::setPersistInfo: invalid info chunk!");
TerrainBlock * terrain = getObject();
if(!terrain || !terrain->getLightMap())
return(false);
terrain->setLightMap( new GBitmap( *chunk->mLightmap) );
return(true);
}
bool blTerrainProxy::getPersistInfo(PersistInfo::PersistChunk * info)
{
if(!Parent::getPersistInfo(info))
return(false);
blTerrainChunk * chunk = dynamic_cast<blTerrainChunk*>(info);
AssertFatal(chunk, "blTerrainProxy::getPersistInfo: invalid info chunk!");
TerrainBlock * terrain = getObject();
if(!terrain || !terrain->getLightMap())
return(false);
if(chunk->mLightmap) delete chunk->mLightmap;
chunk->mLightmap = new GBitmap(*terrain->getLightMap());
return(true);
}
bool blTerrainProxy::supportsShadowVolume()
{
return false;
}
void blTerrainProxy::getClipPlanes(Vector<PlaneF>& planes)
{
}
void blTerrainProxy::addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level)
{
}
void blTerrainSystem::init()
{
}
U32 blTerrainSystem::addObjectType()
{
return TerrainObjectType;
}
SceneLighting::ObjectProxy* blTerrainSystem::createObjectProxy(SceneObject* obj, SceneLighting::ObjectProxyList* sceneObjects)
{
if ((obj->getTypeMask() & TerrainObjectType) != 0)
return new blTerrainProxy(obj);
else
return NULL;
}
PersistInfo::PersistChunk* blTerrainSystem::createPersistChunk(const U32 chunkType)
{
if (chunkType == PersistInfo::PersistChunk::TerrainChunkType)
return new blTerrainChunk();
else
return NULL;
}
bool blTerrainSystem::createPersistChunkFromProxy(SceneLighting::ObjectProxy* objproxy, PersistInfo::PersistChunk **ret)
{
if (dynamic_cast<blTerrainProxy*>(objproxy) != NULL)
{
*ret = new blTerrainChunk();
return true;
} else {
return NULL;
}
}
// Given a ray, this will return the color from the lightmap of this object, return true if handled
bool blTerrainSystem::getColorFromRayInfo(const RayInfo & collision, ColorF& result) const
{
TerrainBlock *terrain = dynamic_cast<TerrainBlock *>(collision.object);
if (!terrain)
return false;
Point2F uv;
F32 terrainlength = (F32)terrain->getBlockSize();
Point3F pos = terrain->getPosition();
uv.x = (collision.point.x - pos.x) / terrainlength;
uv.y = (collision.point.y - pos.y) / terrainlength;
// similar to x = x & width...
uv.x = uv.x - F32(U32(uv.x));
uv.y = uv.y - F32(U32(uv.y));
const GBitmap* lightmap = terrain->getLightMap();
if (!lightmap)
return false;
result = lightmap->sampleTexel(uv.x, uv.y);
// terrain lighting is dim - look into this (same thing done in shaders)...
result *= 2.0f;
return true;
}

View file

@ -0,0 +1,49 @@
//-----------------------------------------------------------------------------
// 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 _BLTERRAINSYSTEM_H_
#define _BLTERRAINSYSTEM_H_
#ifndef _SCENELIGHTING_H_
#include "lighting/common/sceneLighting.h"
#endif
#ifndef _SG_SYSTEM_INTERFACE_H
#include "lighting/lightingInterfaces.h"
#endif
//
// Lighting system interface
//
class blTerrainSystem : public SceneLightingInterface
{
public:
virtual void init();
virtual U32 addObjectType();
virtual SceneLighting::ObjectProxy* createObjectProxy(SceneObject* obj, SceneLighting::ObjectProxyList* sceneObjects);
virtual PersistInfo::PersistChunk* createPersistChunk(const U32 chunkType);
virtual bool createPersistChunkFromProxy(SceneLighting::ObjectProxy* objproxy, PersistInfo::PersistChunk **ret);
// Given a ray, this will return the color from the lightmap of this object, return true if handled
virtual bool getColorFromRayInfo(const RayInfo & collision, ColorF& result) const;
};
#endif // !_BLTERRAINSYSTEM_H_

View file

@ -0,0 +1,352 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "lighting/common/blobShadow.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxTextureManager.h"
#include "gfx/bitmap/gBitmap.h"
#include "math/mathUtils.h"
#include "lighting/lightInfo.h"
#include "lighting/lightingInterfaces.h"
#include "T3D/shapeBase.h"
#include "scene/sceneManager.h"
#include "lighting/lightManager.h"
#include "ts/tsMesh.h"
DepthSortList BlobShadow::smDepthSortList;
GFXTexHandle BlobShadow::smGenericShadowTexture = NULL;
S32 BlobShadow::smGenericShadowDim = 32;
U32 BlobShadow::smShadowMask = TerrainObjectType | InteriorObjectType;
F32 BlobShadow::smGenericRadiusSkew = 0.4f; // shrink radius of shape when it always uses generic shadow...
Box3F gBlobShadowBox;
SphereF gBlobShadowSphere;
Point3F gBlobShadowPoly[4];
//--------------------------------------------------------------
BlobShadow::BlobShadow(SceneObject* parentObject, LightInfo* light, TSShapeInstance* shapeInstance)
{
mParentObject = parentObject;
mShapeBase = dynamic_cast<ShapeBase*>(parentObject);
mParentLight = light;
mShapeInstance = shapeInstance;
mRadius = 0.0f;
mLastRenderTime = 0;
mDepthBias = -0.0002f;
generateGenericShadowBitmap(smGenericShadowDim);
setupStateBlocks();
}
void BlobShadow::setupStateBlocks()
{
GFXStateBlockDesc sh;
sh.cullDefined = true;
sh.cullMode = GFXCullNone;
sh.zDefined = true;
sh.zEnable = true;
sh.zWriteEnable = false;
sh.zBias = mDepthBias;
sh.blendDefined = true;
sh.blendEnable = true;
sh.blendSrc = GFXBlendSrcAlpha;
sh.blendDest = GFXBlendInvSrcAlpha;
sh.alphaDefined = true;
sh.alphaTestEnable = true;
sh.alphaTestFunc = GFXCmpGreater;
sh.alphaTestRef = 0;
sh.samplersDefined = true;
sh.samplers[0] = GFXSamplerStateDesc::getClampLinear();
mShadowSB = GFX->createStateBlock(sh);
}
BlobShadow::~BlobShadow()
{
mShadowBuffer = NULL;
}
bool BlobShadow::shouldRender(F32 camDist)
{
Point3F lightDir;
if (mShapeBase && mShapeBase->getFadeVal() < TSMesh::VISIBILITY_EPSILON)
return false;
F32 shadowLen = 10.0f * mShapeInstance->getShape()->radius;
Point3F pos = mShapeInstance->getShape()->center;
// this is a bit of a hack...move generic shadows towards feet/base of shape
pos *= 0.5f;
pos.convolve(mParentObject->getScale());
mParentObject->getRenderTransform().mulP(pos);
if(mParentLight->getType() == LightInfo::Vector)
{
lightDir = mParentLight->getDirection();
}
else
{
lightDir = pos - mParentLight->getPosition();
lightDir.normalize();
}
// pos is where shadow will be centered (in world space)
setRadius(mShapeInstance, mParentObject->getScale());
bool render = prepare(pos, lightDir, shadowLen);
return render;
}
void BlobShadow::generateGenericShadowBitmap(S32 dim)
{
if(smGenericShadowTexture)
return;
GBitmap * bitmap = new GBitmap(dim,dim,false,GFXFormatR8G8B8A8);
U8 * bits = bitmap->getWritableBits();
dMemset(bits, 0, dim*dim*4);
S32 center = dim >> 1;
F32 invRadiusSq = 1.0f / (F32)(center*center);
F32 tmpF;
for (S32 i=0; i<dim; i++)
{
for (S32 j=0; j<dim; j++)
{
tmpF = (F32)((i-center)*(i-center)+(j-center)*(j-center)) * invRadiusSq;
U8 val = tmpF>0.99f ? 0 : (U8)(180.0f*(1.0f-tmpF)); // 180 out of 255 max
bits[(i*dim*4)+(j*4)+3] = val;
}
}
smGenericShadowTexture.set( bitmap, &GFXDefaultStaticDiffuseProfile, true, "BlobShadow" );
}
//--------------------------------------------------------------
void BlobShadow::setLightMatrices(const Point3F & lightDir, const Point3F & pos)
{
AssertFatal(mDot(lightDir,lightDir)>0.0001f,"BlobShadow::setLightDir: light direction must be a non-zero vector.");
// construct light matrix
Point3F x,z;
if (mFabs(lightDir.z)>0.001f)
{
// mCross(Point3F(1,0,0),lightDir,&z);
z.x = 0.0f;
z.y = lightDir.z;
z.z = -lightDir.y;
z.normalize();
mCross(lightDir,z,&x);
}
else
{
mCross(lightDir,Point3F(0,0,1),&x);
x.normalize();
mCross(x,lightDir,&z);
}
mLightToWorld.identity();
mLightToWorld.setColumn(0,x);
mLightToWorld.setColumn(1,lightDir);
mLightToWorld.setColumn(2,z);
mLightToWorld.setColumn(3,pos);
mWorldToLight = mLightToWorld;
mWorldToLight.inverse();
}
void BlobShadow::setRadius(F32 radius)
{
mRadius = radius;
}
void BlobShadow::setRadius(TSShapeInstance * shapeInstance, const Point3F & scale)
{
const Box3F & bounds = shapeInstance->getShape()->bounds;
F32 dx = 0.5f * (bounds.maxExtents.x-bounds.minExtents.x) * scale.x;
F32 dy = 0.5f * (bounds.maxExtents.y-bounds.minExtents.y) * scale.y;
F32 dz = 0.5f * (bounds.maxExtents.z-bounds.minExtents.z) * scale.z;
mRadius = mSqrt(dx*dx+dy*dy+dz*dz);
}
//--------------------------------------------------------------
bool BlobShadow::prepare(const Point3F & pos, Point3F lightDir, F32 shadowLen)
{
if (mPartition.empty())
{
// --------------------------------------
// 1.
F32 dirMult = (1.0f) * (1.0f);
if (dirMult < 0.99f)
{
lightDir.z *= dirMult;
lightDir.z -= 1.0f - dirMult;
}
lightDir.normalize();
shadowLen *= (1.0f) * (1.0f);
// --------------------------------------
// 2. get polys
F32 radius = mRadius;
radius *= smGenericRadiusSkew;
buildPartition(pos,lightDir,radius,shadowLen);
}
if (mPartition.empty())
// no need to draw shadow if nothing to cast it onto
return false;
return true;
}
//--------------------------------------------------------------
void BlobShadow::buildPartition(const Point3F & p, const Point3F & lightDir, F32 radius, F32 shadowLen)
{
setLightMatrices(lightDir,p);
Point3F extent(2.0f*radius,shadowLen,2.0f*radius);
smDepthSortList.clear();
smDepthSortList.set(mWorldToLight,extent);
smDepthSortList.setInterestNormal(lightDir);
if (shadowLen<1.0f)
// no point in even this short of a shadow...
shadowLen = 1.0f;
mInvShadowDistance = 1.0f / shadowLen;
// build world space box and sphere around shadow
Point3F x,y,z;
mLightToWorld.getColumn(0,&x);
mLightToWorld.getColumn(1,&y);
mLightToWorld.getColumn(2,&z);
x *= radius;
y *= shadowLen;
z *= radius;
gBlobShadowBox.maxExtents.set(mFabs(x.x)+mFabs(y.x)+mFabs(z.x),
mFabs(x.y)+mFabs(y.y)+mFabs(z.y),
mFabs(x.z)+mFabs(y.z)+mFabs(z.z));
y *= 0.5f;
gBlobShadowSphere.radius = gBlobShadowBox.maxExtents.len();
gBlobShadowSphere.center = p + y;
gBlobShadowBox.minExtents = y + p - gBlobShadowBox.maxExtents;
gBlobShadowBox.maxExtents += y + p;
// get polys
gClientContainer.findObjects(STATIC_COLLISION_TYPEMASK, BlobShadow::collisionCallback, this);
// setup partition list
gBlobShadowPoly[0].set(-radius,0,-radius);
gBlobShadowPoly[1].set(-radius,0, radius);
gBlobShadowPoly[2].set( radius,0, radius);
gBlobShadowPoly[3].set( radius,0,-radius);
mPartition.clear();
mPartitionVerts.clear();
smDepthSortList.depthPartition(gBlobShadowPoly,4,mPartition,mPartitionVerts);
if(mPartitionVerts.empty())
return;
// Find the rough distance of the shadow verts
// from the object position and use that to scale
// the visibleAlpha so that the shadow fades out
// the further away from you it gets
F32 dist = 0.0f;
// Calculate the center of the partition verts
Point3F shadowCenter(0.0f, 0.0f, 0.0f);
for (U32 i = 0; i < mPartitionVerts.size(); i++)
shadowCenter += mPartitionVerts[i];
shadowCenter /= mPartitionVerts.size();
mLightToWorld.mulP(shadowCenter);
dist = (p - shadowCenter).len();
// now set up tverts & colors
mShadowBuffer.set(GFX, mPartitionVerts.size(), GFXBufferTypeVolatile);
mShadowBuffer.lock();
F32 visibleAlpha = 255.0f;
if (mShapeBase && mShapeBase->getFadeVal())
visibleAlpha = mClampF(255.0f * mShapeBase->getFadeVal(), 0, 255);
visibleAlpha *= 1.0f - (dist / gBlobShadowSphere.radius);
F32 invRadius = 1.0f / radius;
for (S32 i=0; i<mPartitionVerts.size(); i++)
{
Point3F vert = mPartitionVerts[i];
mShadowBuffer[i].point.set(vert);
mShadowBuffer[i].color.set(255, 255, 255, visibleAlpha);
mShadowBuffer[i].texCoord.set(0.5f + 0.5f * mPartitionVerts[i].x * invRadius, 0.5f + 0.5f * mPartitionVerts[i].z * invRadius);
};
mShadowBuffer.unlock();
}
//--------------------------------------------------------------
void BlobShadow::collisionCallback(SceneObject * obj, void* thisPtr)
{
if (obj->getWorldBox().isOverlapped(gBlobShadowBox))
{
// only interiors clip...
ClippedPolyList::allowClipping = (obj->getTypeMask() & LIGHTMGR->getSceneLightingInterface()->mClippingMask) != 0;
obj->buildPolyList(PLC_Collision,&smDepthSortList,gBlobShadowBox,gBlobShadowSphere);
ClippedPolyList::allowClipping = true;
}
}
//--------------------------------------------------------------
void BlobShadow::render( F32 camDist, const TSRenderState &rdata )
{
mLastRenderTime = Platform::getRealMilliseconds();
GFX->pushWorldMatrix();
MatrixF world = GFX->getWorldMatrix();
world.mul(mLightToWorld);
GFX->setWorldMatrix(world);
GFX->disableShaders();
GFX->setStateBlock(mShadowSB);
GFX->setTexture(0, smGenericShadowTexture);
GFX->setVertexBuffer(mShadowBuffer);
for(U32 p=0; p<mPartition.size(); p++)
GFX->drawPrimitive(GFXTriangleFan, mPartition[p].vertexStart, (mPartition[p].vertexCount - 2));
// This is a bad nasty hack which forces the shadow to reconstruct itself every frame.
mPartition.clear();
GFX->popWorldMatrix();
}
void BlobShadow::deleteGenericShadowBitmap()
{
smGenericShadowTexture = NULL;
}

View file

@ -0,0 +1,87 @@
//-----------------------------------------------------------------------------
// 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 _BLOBSHADOW_H_
#define _BLOBSHADOW_H_
#include "collision/depthSortList.h"
#include "scene/sceneObject.h"
#include "ts/tsShapeInstance.h"
#include "lighting/common/shadowBase.h"
class ShapeBase;
class LightInfo;
class BlobShadow : public ShadowBase
{
F32 mRadius;
F32 mInvShadowDistance;
MatrixF mLightToWorld;
MatrixF mWorldToLight;
Vector<DepthSortList::Poly> mPartition;
Vector<Point3F> mPartitionVerts;
GFXVertexBufferHandle<GFXVertexPCT> mShadowBuffer;
static U32 smShadowMask;
static DepthSortList smDepthSortList;
static GFXTexHandle smGenericShadowTexture;
static F32 smGenericRadiusSkew;
static S32 smGenericShadowDim;
U32 mLastRenderTime;
static void collisionCallback(SceneObject*,void *);
private:
SceneObject* mParentObject;
ShapeBase* mShapeBase;
LightInfo* mParentLight;
TSShapeInstance* mShapeInstance;
GFXStateBlockRef mShadowSB;
F32 mDepthBias;
void setupStateBlocks();
void setLightMatrices(const Point3F & lightDir, const Point3F & pos);
void buildPartition(const Point3F & p, const Point3F & lightDir, F32 radius, F32 shadowLen);
public:
BlobShadow(SceneObject* parentobject, LightInfo* light, TSShapeInstance* shapeinstance);
~BlobShadow();
void setRadius(F32 radius);
void setRadius(TSShapeInstance *, const Point3F & scale);
bool prepare(const Point3F & pos, Point3F lightDir, F32 shadowLen);
bool shouldRender(F32 camDist);
void update( const SceneRenderState *state ) {}
void render( F32 camDist, const TSRenderState &rdata );
U32 getLastRenderTime() const { return mLastRenderTime; }
static void generateGenericShadowBitmap(S32 dim);
static void deleteGenericShadowBitmap();
};
#endif

View file

@ -0,0 +1,61 @@
//-----------------------------------------------------------------------------
// 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 "lighting/common/lightMapParams.h"
#include "core/stream/bitStream.h"
const LightInfoExType LightMapParams::Type( "LightMapParams" );
LightMapParams::LightMapParams( LightInfo *light ) :
representedInLightmap(false),
includeLightmappedGeometryInShadow(false),
shadowDarkenColor(0.0f, 0.0f, 0.0f, -1.0f)
{
}
LightMapParams::~LightMapParams()
{
}
void LightMapParams::set( const LightInfoEx *ex )
{
// TODO: Do we even need this?
}
void LightMapParams::packUpdate( BitStream *stream ) const
{
stream->writeFlag(representedInLightmap);
stream->writeFlag(includeLightmappedGeometryInShadow);
stream->write(shadowDarkenColor);
}
void LightMapParams::unpackUpdate( BitStream *stream )
{
representedInLightmap = stream->readFlag();
includeLightmappedGeometryInShadow = stream->readFlag();
stream->read(&shadowDarkenColor);
// Always make sure that the alpha value of the shadowDarkenColor is -1.0
shadowDarkenColor.alpha = -1.0f;
}

View file

@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// 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 _LIGHTMAPPARAMS_H_
#define _LIGHTMAPPARAMS_H_
#ifndef _LIGHTINFO_H_
#include "lighting/lightInfo.h"
#endif
class LightMapParams : public LightInfoEx
{
public:
LightMapParams( LightInfo *light );
virtual ~LightMapParams();
/// 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 );
public:
// We're leaving these public for easy access
// for console protected fields.
bool representedInLightmap; ///< This light is represented in lightmaps (static light, default: false)
ColorF shadowDarkenColor; ///< The color that should be used to multiply-blend dynamic shadows onto lightmapped geometry (ignored if 'representedInLightmap' is false)
bool includeLightmappedGeometryInShadow; ///< This light should render lightmapped geometry during its shadow-map update (ignored if 'representedInLightmap' is false)
};
#endif

View file

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

View file

@ -0,0 +1,127 @@
//-----------------------------------------------------------------------------
// 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 _PROJECTEDSHADOW_H_
#define _PROJECTEDSHADOW_H_
#ifndef _DEPTHSORTLIST_H_
#include "collision/depthSortList.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
#ifndef _TSSHAPEINSTANCE_H_
#include "ts/tsShapeInstance.h"
#endif
#ifndef _LIGHTINGSYSTEM_SHADOWBASE_H_
#include "lighting/common/shadowBase.h"
#endif
class ShapeBase;
class LightInfo;
class DecalData;
class DecalInstance;
class RenderPassManager;
class PostEffect;
class RenderMeshMgr;
class CustomMaterial;
class BaseMatInstance;
class MaterialParameterHandle;
GFX_DeclareTextureProfile( BLProjectedShadowProfile );
GFX_DeclareTextureProfile( BLProjectedShadowZProfile );
class ProjectedShadow : public ShadowBase
{
protected:
/// This parameter is used to
/// adjust the far plane out for our
/// orthographic render in order to
/// force our object towards one end of the
/// the eye space depth range.
static F32 smDepthAdjust;
F32 mRadius;
MatrixF mWorldToLight;
U32 mLastRenderTime;
F32 mShadowLength;
F32 mScore;
bool mUpdateTexture;
Point3F mLastObjectScale;
Point3F mLastObjectPosition;
VectorF mLastLightDir;
DecalData *mDecalData;
DecalInstance *mDecalInstance;
SceneObject *mParentObject;
ShapeBase *mShapeBase;
MaterialParameterHandle *mCasterPositionSC;
MaterialParameterHandle *mShadowLengthSC;
static SimObjectPtr<RenderPassManager> smRenderPass;
static SimObjectPtr<PostEffect> smShadowFilter;
static RenderPassManager* _getRenderPass();
GFXTexHandle mShadowTexture;
GFXTextureTargetRef mRenderTarget;
GFXTextureObject* _getDepthTarget( U32 width, U32 height );
void _renderToTexture( F32 camDist, const TSRenderState &rdata );
bool _updateDecal( const SceneRenderState *sceneState );
void _calcScore( const SceneRenderState *state );
/// Returns a spotlight shadow material for use when
/// rendering meshes into the projected shadow.
static BaseMatInstance* _getShadowMaterial( BaseMatInstance *inMat );
public:
/// @see DecalData
static float smFadeStartPixelSize;
static float smFadeEndPixelSize;
ProjectedShadow( SceneObject *object );
virtual ~ProjectedShadow();
bool shouldRender( const SceneRenderState *state );
void update( const SceneRenderState *state );
void render( F32 camDist, const TSRenderState &rdata );
U32 getLastRenderTime() const { return mLastRenderTime; }
const F32 getScore() const { return mScore; }
};
#endif // _PROJECTEDSHADOW_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,254 @@
//-----------------------------------------------------------------------------
// 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 _SCENELIGHTING_H_
#define _SCENELIGHTING_H_
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
#ifndef _SGSCENEPERSIST_H_
#include "lighting/common/scenePersist.h"
#endif
#ifndef _LIGHTINFO_H_
#include "lighting/lightInfo.h"
#endif
class ShadowVolumeBSP;
class LightInfo;
class AvailableSLInterfaces;
class SceneLighting : public SimObject
{
typedef SimObject Parent;
protected:
AvailableSLInterfaces* mLightingInterfaces;
virtual void getMLName(const char* misName, const U32 missionCRC, const U32 buffSize, char* filenameBuffer);
public:
S32 sgTimeTemp;
S32 sgTimeTemp2;
virtual void sgNewEvent(U32 light, S32 object, U32 event);
virtual void sgLightingStartEvent();
virtual void sgLightingCompleteEvent();
virtual void sgTGEPassSetupEvent();
virtual void sgTGELightStartEvent(U32 light);
virtual void sgTGELightProcessEvent(U32 light, S32 object);
virtual void sgTGELightCompleteEvent(U32 light);
virtual void sgTGESetProgress(U32 light, S32 object);
virtual void sgSGPassSetupEvent();
virtual void sgSGObjectStartEvent(S32 object);
virtual void sgSGObjectProcessEvent(U32 light, S32 object);
virtual void sgSGObjectCompleteEvent(S32 object);
virtual void sgSGSetProgress(U32 light, S32 object);
// 'sg' prefix omitted to conform with existing 'addInterior' method...
void addStatic(ShadowVolumeBSP *shadowVolume, SceneObject *sceneobject, LightInfo *light, S32 level);
// persist objects moved to 'sgScenePersist.h' for clarity...
// everything below this line should be original code...
U32 calcMissionCRC();
bool verifyMissionInfo(PersistInfo::PersistChunk *);
bool getMissionInfo(PersistInfo::PersistChunk *);
bool loadPersistInfo(const char *);
bool savePersistInfo(const char *);
class ObjectProxy;
enum {
SHADOW_DETAIL = -1
};
//------------------------------------------------------------------------------
/// Create a proxy for each object to store data.
class ObjectProxy
{
public:
SimObjectPtr<SceneObject> mObj;
U32 mChunkCRC;
ObjectProxy(SceneObject * obj) : mObj(obj){mChunkCRC = 0;}
virtual ~ObjectProxy(){}
SceneObject * operator->() {return(mObj);}
SceneObject * getObject() {return(mObj);}
/// @name Lighting Interface
/// @{
virtual bool loadResources() {return(true);}
virtual void init() {}
virtual bool tgePreLight(LightInfo* light) { return preLight(light); }
virtual bool preLight(LightInfo *) {return(false);}
virtual void light(LightInfo *) {}
virtual void postLight(bool lastLight) {}
/// @}
/// @name Lighting events
/// @{
// Called when the lighting process begins
virtual void processLightingStart() {}
// Called when a TGELight event is started, return true if status has been reported to console
virtual void processTGELightProcessEvent(U32 curr, U32 max, LightInfo*) { Con::printf(" Lighting object %d of %d...", (curr+1), max); }
// Called for lighting kit lights
virtual bool processStartObjectLightingEvent(U32 current, U32 max) { Con::printf(" Lighting object %d of %d... %s: %s", (current+1), max, mObj->getClassName(), mObj->getName()); return true; }
// Called once per object and SG light - used for calling light on an object
virtual void processSGObjectProcessEvent(LightInfo* currLight) { light(currLight); };
/// @}
/// @name Persistence
///
/// We cache lighting information to cut down on load times.
///
/// There are flags such as ForceAlways and LoadOnly which allow you
/// to control this behaviour.
/// @{
bool calcValidation();
bool isValidChunk(PersistInfo::PersistChunk *);
virtual U32 getResourceCRC() = 0;
virtual bool setPersistInfo(PersistInfo::PersistChunk *);
virtual bool getPersistInfo(PersistInfo::PersistChunk *);
/// @}
// Called to figure out if this object should be added to the shadow volume
virtual bool supportsShadowVolume() { return false; }
// Called to retrieve the clip planes of the object. Currently used for terrain lighting, but could be used to speed up other
// lighting calculations.
virtual void getClipPlanes(Vector<PlaneF>& planes) { }
// Called to add the object to the shadow volume
virtual void addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level) { } ;
};
typedef Vector<ObjectProxy*> ObjectProxyList;
ObjectProxyList mSceneObjects;
ObjectProxyList mLitObjects;
LightInfoList mLights;
SceneLighting(AvailableSLInterfaces* lightingInterfaces);
~SceneLighting();
enum Flags {
ForceAlways = BIT(0), ///< Regenerate the scene lighting no matter what.
ForceWritable = BIT(1), ///< Regenerate the scene lighting only if we can write to the lighting cache files.
LoadOnly = BIT(2), ///< Just load cached lighting data.
};
bool lightScene(const char *, BitSet32 flags = 0);
bool isLighting();
S32 mStartTime;
char mFileName[1024];
SceneManager * mSceneManager;
bool light(BitSet32);
void completed(bool success);
void processEvent(U32 light, S32 object);
void processCache();
};
class sgSceneLightingProcessEvent : public SimEvent
{
private:
U32 sgLightIndex;
S32 sgObjectIndex;
U32 sgEvent;
public:
enum sgEventTypes
{
sgLightingStartEventType,
sgLightingCompleteEventType,
sgSGPassSetupEventType,
sgSGObjectStartEventType,
sgSGObjectCompleteEventType,
sgSGObjectProcessEventType,
sgTGEPassSetupEventType,
sgTGELightStartEventType,
sgTGELightCompleteEventType,
sgTGELightProcessEventType
};
sgSceneLightingProcessEvent(U32 lightIndex, S32 objectIndex, U32 event)
{
sgLightIndex = lightIndex;
sgObjectIndex = objectIndex;
sgEvent = event;
}
void process(SimObject * object)
{
AssertFatal(object, "SceneLightingProcessEvent:: null event object!");
if(!object)
return;
SceneLighting *sl = static_cast<SceneLighting*>(object);
switch(sgEvent)
{
case sgLightingStartEventType:
sl->sgLightingStartEvent();
break;
case sgLightingCompleteEventType:
sl->sgLightingCompleteEvent();
break;
case sgTGEPassSetupEventType:
sl->sgTGEPassSetupEvent();
break;
case sgTGELightStartEventType:
sl->sgTGELightStartEvent(sgLightIndex);
break;
case sgTGELightProcessEventType:
sl->sgTGELightProcessEvent(sgLightIndex, sgObjectIndex);
break;
case sgTGELightCompleteEventType:
sl->sgTGELightCompleteEvent(sgLightIndex);
break;
case sgSGPassSetupEventType:
sl->sgSGPassSetupEvent();
break;
case sgSGObjectStartEventType:
sl->sgSGObjectStartEvent(sgObjectIndex);
break;
case sgSGObjectProcessEventType:
sl->sgSGObjectProcessEvent(sgLightIndex, sgObjectIndex);
break;
case sgSGObjectCompleteEventType:
sl->sgSGObjectCompleteEvent(sgObjectIndex);
break;
default:
return;
};
};
};
extern SceneLighting *gLighting;
#endif//_SGSCENELIGHTING_H_

View file

@ -0,0 +1,66 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
static const Point3F BoxNormals[] =
{
Point3F( 1, 0, 0),
Point3F(-1, 0, 0),
Point3F( 0, 1, 0),
Point3F( 0,-1, 0),
Point3F( 0, 0, 1),
Point3F( 0, 0,-1)
};
static U32 BoxVerts[][4] = {
{7,6,4,5}, // +x
{0,2,3,1}, // -x
{7,3,2,6}, // +y
{0,1,5,4}, // -y
{7,5,1,3}, // +z
{0,4,6,2} // -z
};
static U32 BoxSharedEdgeMask[][6] = {
{0, 0, 1, 4, 8, 2},
{0, 0, 2, 8, 4, 1},
{8, 2, 0, 0, 1, 4},
{4, 1, 0, 0, 2, 8},
{1, 4, 8, 2, 0, 0},
{2, 8, 4, 1, 0, 0}
};
static Point3F BoxPnts[] = {
Point3F(0,0,0),
Point3F(0,0,1),
Point3F(0,1,0),
Point3F(0,1,1),
Point3F(1,0,0),
Point3F(1,0,1),
Point3F(1,1,0),
Point3F(1,1,1)
};
extern SceneLighting *gLighting;
extern F32 gParellelVectorThresh;
extern F32 gPlaneNormThresh;
extern F32 gPlaneDistThresh;

View file

@ -0,0 +1,144 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "lighting/common/scenePersist.h"
#include "lighting/lightingInterfaces.h"
#include "scene/sceneManager.h"
#include "lighting/lightManager.h"
U32 PersistInfo::smFileVersion = 0x11;
PersistInfo::~PersistInfo()
{
for(U32 i = 0; i < mChunks.size(); i++)
delete mChunks[i];
}
//------------------------------------------------------------------------------
bool PersistInfo::read(Stream & stream)
{
U32 version;
if(!stream.read(&version) || version != smFileVersion)
return(false);
U32 numChunks;
if(!stream.read(&numChunks))
return(false);
if(numChunks == 0)
return(false);
// read in all the chunks
for(U32 i = 0; i < numChunks; i++)
{
U32 chunkType;
if(!stream.read(&chunkType))
return(false);
// MissionChunk must be first chunk
if(i == 0 && chunkType != PersistChunk::MissionChunkType)
return(false);
if(i != 0 && chunkType == PersistChunk::MissionChunkType)
return(false);
// Create the right chunk for the system
bool bChunkFound = false;
SceneLightingInterfaces sli = LIGHTMGR->getSceneLightingInterface()->mAvailableSystemInterfaces;
for(SceneLightingInterface** itr = sli.begin(); itr != sli.end() && !bChunkFound; itr++)
{
PersistInfo::PersistChunk* pc = (*itr)->createPersistChunk(chunkType);
if (pc != NULL)
{
mChunks.push_back(pc);
bChunkFound = true;
}
}
if (!bChunkFound)
{
// create the chunk
switch(chunkType)
{
case PersistChunk::MissionChunkType:
mChunks.push_back(new PersistInfo::MissionChunk);
break;
default:
return(false);
break;
}
}
// load the chunk info
if(!mChunks[i]->read(stream))
return(false);
}
return(true);
}
bool PersistInfo::write(Stream & stream)
{
if(!stream.write(smFileVersion))
return(false);
if(!stream.write((U32)mChunks.size()))
return(false);
for(U32 i = 0; i < mChunks.size(); i++)
{
if(!stream.write(mChunks[i]->mChunkType))
return(false);
if(!mChunks[i]->write(stream))
return(false);
}
return(true);
}
//------------------------------------------------------------------------------
// Class SceneLighting::PersistInfo::PersistChunk
//------------------------------------------------------------------------------
bool PersistInfo::PersistChunk::read(Stream & stream)
{
if(!stream.read(&mChunkCRC))
return(false);
return(true);
}
bool PersistInfo::PersistChunk::write(Stream & stream)
{
if(!stream.write(mChunkCRC))
return(false);
return(true);
}
//------------------------------------------------------------------------------
// Class SceneLighting::PersistInfo::MissionChunk
//------------------------------------------------------------------------------
PersistInfo::MissionChunk::MissionChunk()
{
mChunkType = PersistChunk::MissionChunkType;
}

View file

@ -0,0 +1,68 @@
//-----------------------------------------------------------------------------
// 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 _SGSCENEPERSIST_H_
#define _SGSCENEPERSIST_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class Stream;
struct PersistInfo
{
struct PersistChunk
{
enum {
MissionChunkType = 0,
InteriorChunkType,
TerrainChunkType,
AtlasLightMapChunkType
};
U32 mChunkType;
U32 mChunkCRC;
virtual ~PersistChunk() {}
virtual bool read(Stream &);
virtual bool write(Stream &);
};
struct MissionChunk : public PersistChunk
{
typedef PersistChunk Parent;
MissionChunk();
};
~PersistInfo();
Vector<PersistChunk*> mChunks;
static U32 smFileVersion;
bool read(Stream &);
bool write(Stream &);
};
#endif//_SGSCENEPERSIST_H_

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 _LIGHTINGSYSTEM_SHADOWBASE_H_
#define _LIGHTINGSYSTEM_SHADOWBASE_H_
class TSRenderState;
class ShadowBase
{
public:
virtual ~ShadowBase() {}
virtual bool shouldRender( const SceneRenderState *state ) = 0;
virtual void update( const SceneRenderState *state ) = 0;
virtual void render(F32 camDist, const TSRenderState &rdata ) = 0;
virtual U32 getLastRenderTime() const = 0;
virtual const F32 getScore() const = 0;
};
#endif

View file

@ -0,0 +1,731 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "lighting/common/shadowVolumeBSP.h"
#include "lighting/lightInfo.h"
#include "math/mPlane.h"
ShadowVolumeBSP::ShadowVolumeBSP() :
mSVRoot(0),
mNodeStore(0),
mPolyStore(0),
mFirstInteriorNode(0)
{
}
ShadowVolumeBSP::~ShadowVolumeBSP()
{
for(U32 i = 0; i < mSurfaces.size(); i++)
delete mSurfaces[i];
}
void ShadowVolumeBSP::insertShadowVolume(SVNode ** root, U32 volume)
{
SVNode * traverse = mShadowVolumes[volume];
// insert 'em
while(traverse)
{
// copy it
*root = createNode();
(*root)->mPlaneIndex = traverse->mPlaneIndex;
(*root)->mSurfaceInfo = traverse->mSurfaceInfo;
(*root)->mShadowVolume = traverse->mShadowVolume;
// do the next
root = &(*root)->mFront;
traverse = traverse->mFront;
}
}
ShadowVolumeBSP::SVNode::Side ShadowVolumeBSP::whichSide(SVPoly * poly, const PlaneF & plane) const
{
bool front = false;
bool back = false;
for(U32 i = 0; i < poly->mWindingCount; i++)
{
switch(plane.whichSide(poly->mWinding[i]))
{
case PlaneF::Front:
if(back)
return(SVNode::Split);
front = true;
break;
case PlaneF::Back:
if(front)
return(SVNode::Split);
back = true;
break;
default:
break;
}
}
AssertFatal(!(front && back), "ShadowVolumeBSP::whichSide - failed to classify poly");
if(!front && !back)
return(SVNode::On);
return(front ? SVNode::Front : SVNode::Back);
}
void ShadowVolumeBSP::splitPoly(SVPoly * poly, const PlaneF & plane, SVPoly ** front, SVPoly ** back)
{
PlaneF::Side sides[SVPoly::MaxWinding];
U32 i;
for(i = 0; i < poly->mWindingCount; i++)
sides[i] = plane.whichSide(poly->mWinding[i]);
// create the polys
(*front) = createPoly();
(*back) = createPoly();
// copy the info
(*front)->mWindingCount = (*back)->mWindingCount = 0;
(*front)->mPlane = (*back)->mPlane = poly->mPlane;
(*front)->mTarget = (*back)->mTarget = poly->mTarget;
(*front)->mSurfaceInfo = (*back)->mSurfaceInfo = poly->mSurfaceInfo;
(*front)->mShadowVolume = (*back)->mShadowVolume = poly->mShadowVolume;
//
for(i = 0; i < poly->mWindingCount; i++)
{
U32 j = (i+1) % poly->mWindingCount;
if(sides[i] == PlaneF::On)
{
(*front)->mWinding[(*front)->mWindingCount++] = poly->mWinding[i];
(*back)->mWinding[(*back)->mWindingCount++] = poly->mWinding[i];
}
else if(sides[i] == PlaneF::Front)
{
(*front)->mWinding[(*front)->mWindingCount++] = poly->mWinding[i];
if(sides[j] == PlaneF::Back)
{
const Point3F & a = poly->mWinding[i];
const Point3F & b = poly->mWinding[j];
F32 t = plane.intersect(a, b);
AssertFatal(t >=0 && t <= 1, "ShadowVolumeBSP::splitPoly - bad plane intersection");
Point3F pos;
pos.interpolate(a, b, t);
//
(*front)->mWinding[(*front)->mWindingCount++] =
(*back)->mWinding[(*back)->mWindingCount++] = pos;
}
}
else if(sides[i] == PlaneF::Back)
{
(*back)->mWinding[(*back)->mWindingCount++] = poly->mWinding[i];
if(sides[j] == PlaneF::Front)
{
const Point3F & a = poly->mWinding[i];
const Point3F & b = poly->mWinding[j];
F32 t = plane.intersect(a, b);
AssertFatal(t >=0 && t <= 1, "ShadowVolumeBSP::splitPoly - bad plane intersection");
Point3F pos;
pos.interpolate(a, b, t);
(*front)->mWinding[(*front)->mWindingCount++] =
(*back)->mWinding[(*back)->mWindingCount++] = pos;
}
}
}
AssertFatal((*front)->mWindingCount && (*back)->mWindingCount, "ShadowVolume::split - invalid split");
}
void ShadowVolumeBSP::addUniqueVolume(SurfaceInfo * surfaceInfo, U32 volume)
{
if(!surfaceInfo)
return;
for(U32 i = 0; i < surfaceInfo->mShadowed.size(); i++)
if(surfaceInfo->mShadowed[i] == volume)
return;
// add it
surfaceInfo->mShadowed.push_back(volume);
}
void ShadowVolumeBSP::insertPoly(SVNode ** root, SVPoly * poly)
{
if(!(*root))
{
insertShadowVolume(root, poly->mShadowVolume);
if(poly->mSurfaceInfo && !mFirstInteriorNode)
mFirstInteriorNode = mShadowVolumes[poly->mShadowVolume];
if(poly->mTarget)
addUniqueVolume(poly->mTarget->mSurfaceInfo, poly->mShadowVolume);
recyclePoly(poly);
return;
}
const PlaneF & plane = getPlane((*root)->mPlaneIndex);
//
switch(whichSide(poly, plane))
{
case SVNode::On:
case SVNode::Front:
insertPolyFront(root, poly);
break;
case SVNode::Back:
insertPolyBack(root, poly);
break;
case SVNode::Split:
{
SVPoly * front;
SVPoly * back;
splitPoly(poly, plane, &front, &back);
recyclePoly(poly);
insertPolyFront(root, front);
insertPolyBack(root, back);
break;
}
}
}
void ShadowVolumeBSP::insertPolyFront(SVNode ** root, SVPoly * poly)
{
// POLY type node?
if(!(*root)->mFront)
{
if(poly->mSurfaceInfo && !mFirstInteriorNode)
mFirstInteriorNode = mShadowVolumes[poly->mShadowVolume];
addUniqueVolume(poly->mSurfaceInfo, (*root)->mShadowVolume);
recyclePoly(poly);
}
else
insertPoly(&(*root)->mFront, poly);
}
void ShadowVolumeBSP::insertPolyBack(SVNode ** root, SVPoly * poly)
{
// list of nodes where an interior has been added
if(poly->mSurfaceInfo && !(*root)->mSurfaceInfo && !(*root)->mBack)
{
if(!mFirstInteriorNode)
mFirstInteriorNode = mShadowVolumes[poly->mShadowVolume];
mParentNodes.push_back(*root);
}
// POLY type node?
if(!(*root)->mFront)
{
poly->mTarget = (*root);
insertPoly(&(*root)->mBack, poly);
}
else
insertPoly(&(*root)->mBack, poly);
}
//------------------------------------------------------------------------------
ShadowVolumeBSP::SVNode * ShadowVolumeBSP::createNode()
{
SVNode * node;
if(mNodeStore)
{
node = mNodeStore;
mNodeStore = mNodeStore->mFront;
}
else
node = mNodeChunker.alloc();
//
node->mFront = node->mBack = 0;
node->mShadowVolume = 0;
node->mSurfaceInfo = 0;
return(node);
}
void ShadowVolumeBSP::recycleNode(SVNode * node)
{
if(!node)
return;
recycleNode(node->mFront);
recycleNode(node->mBack);
//
node->mFront = mNodeStore;
node->mBack = 0;
mNodeStore = node;
}
ShadowVolumeBSP::SVPoly * ShadowVolumeBSP::createPoly()
{
SVPoly * poly;
if(mPolyStore)
{
poly = mPolyStore;
mPolyStore = mPolyStore->mNext;
}
else
poly = mPolyChunker.alloc();
//
poly->mNext = 0;
poly->mTarget = 0;
poly->mSurfaceInfo = 0;
poly->mShadowVolume = 0;
poly->mWindingCount = 0;
for (U32 i = 0; i < SVPoly::MaxWinding; i++)
poly->mWinding[i] = Point3F(0.0f, 0.0f, 0.0f);
return(poly);
}
void ShadowVolumeBSP::recyclePoly(SVPoly * poly)
{
if(!poly)
return;
recyclePoly(poly->mNext);
//
poly->mNext = mPolyStore;
mPolyStore = poly;
}
U32 ShadowVolumeBSP::insertPlane(const PlaneF & plane)
{
mPlanes.push_back(plane);
return(mPlanes.size() - 1);
}
const PlaneF & ShadowVolumeBSP::getPlane(U32 index) const
{
AssertFatal(index < mPlanes.size(), "ShadowVolumeBSP::getPlane - index out of range");
return(mPlanes[index]);
}
ShadowVolumeBSP::SVNode * ShadowVolumeBSP::getShadowVolume(U32 index)
{
AssertFatal(index < mShadowVolumes.size(), "ShadowVolumeBSP::getShadowVolume - index out of range");
return(mShadowVolumes[index]);
}
bool ShadowVolumeBSP::testPoint(SVNode * root, const Point3F & pnt)
{
const PlaneF & plane = getPlane(root->mPlaneIndex);
switch(plane.whichSide(pnt))
{
case PlaneF::On:
if(!root->mFront)
return(true);
else
{
if(testPoint(root->mFront, pnt))
return(true);
else
{
if(!root->mBack)
return(false);
else
return(testPoint(root->mBack, pnt));
}
}
break;
//
case PlaneF::Front:
if(root->mFront)
return(testPoint(root->mFront, pnt));
else
return(true);
break;
//
case PlaneF::Back:
if(root->mBack)
return(testPoint(root->mBack, pnt));
else
return(false);
break;
}
return(false);
}
//------------------------------------------------------------------------------
bool ShadowVolumeBSP::testPoly(SVNode * root, SVPoly * poly)
{
const PlaneF & plane = getPlane(root->mPlaneIndex);
switch(whichSide(poly, plane))
{
case SVNode::On:
case SVNode::Front:
if(root->mFront)
return(testPoly(root->mFront, poly));
recyclePoly(poly);
return(true);
case SVNode::Back:
if(root->mBack)
return(testPoly(root->mBack, poly));
recyclePoly(poly);
break;
case SVNode::Split:
{
if(!root->mFront)
{
recyclePoly(poly);
return(true);
}
SVPoly * front;
SVPoly * back;
splitPoly(poly, plane, &front, &back);
recyclePoly(poly);
if(testPoly(root->mFront, front))
{
recyclePoly(back);
return(true);
}
if(root->mBack)
return(testPoly(root->mBack, back));
recyclePoly(back);
break;
}
}
return(false);
}
//------------------------------------------------------------------------------
void ShadowVolumeBSP::buildPolyVolume(SVPoly * poly, LightInfo * light)
{
if(light->getType() != LightInfo::Vector)
return;
// build the poly
Point3F pointOffset = light->getDirection() * 10.f;
// create the shadow volume
mShadowVolumes.increment();
SVNode ** traverse = &mShadowVolumes.last();
U32 shadowVolumeIndex = mShadowVolumes.size() - 1;
for(U32 i = 0; i < poly->mWindingCount; i++)
{
U32 j = (i + 1) % poly->mWindingCount;
if(poly->mWinding[i] == poly->mWinding[j])
continue;
(*traverse) = createNode();
Point3F & a = poly->mWinding[i];
Point3F & b = poly->mWinding[j];
Point3F c = b + pointOffset;
(*traverse)->mPlaneIndex = insertPlane(PlaneF(a,b,c));
(*traverse)->mShadowVolume = shadowVolumeIndex;
traverse = &(*traverse)->mFront;
}
// do the poly node
(*traverse) = createNode();
(*traverse)->mPlaneIndex = insertPlane(poly->mPlane);
(*traverse)->mShadowVolume = poly->mShadowVolume = shadowVolumeIndex;
}
ShadowVolumeBSP::SVPoly * ShadowVolumeBSP::copyPoly(SVPoly * src)
{
SVPoly * poly = createPoly();
dMemcpy(poly, src, sizeof(SVPoly));
poly->mTarget = 0;
poly->mNext = 0;
return(poly);
}
//------------------------------------------------------------------------------
void ShadowVolumeBSP::addToPolyList(SVPoly ** store, SVPoly * poly) const
{
poly->mNext = *store;
*store = poly;
}
//------------------------------------------------------------------------------
void ShadowVolumeBSP::clipPoly(SVNode * root, SVPoly ** store, SVPoly * poly)
{
if(!root)
{
recyclePoly(poly);
return;
}
const PlaneF & plane = getPlane(root->mPlaneIndex);
switch(whichSide(poly, plane))
{
case SVNode::On:
case SVNode::Back:
if(root->mBack)
clipPoly(root->mBack, store, poly);
else
addToPolyList(store, poly);
break;
case SVNode::Front:
// encountered POLY node?
if(!root->mFront)
{
recyclePoly(poly);
return;
}
else
clipPoly(root->mFront, store, poly);
break;
case SVNode::Split:
{
SVPoly * front;
SVPoly * back;
splitPoly(poly, plane, &front, &back);
AssertFatal(front && back, "ShadowVolumeBSP::clipPoly: invalid split");
recyclePoly(poly);
// front
if(!root->mFront)
{
recyclePoly(front);
return;
}
else
clipPoly(root->mFront, store, front);
// back
if(root->mBack)
clipPoly(root->mBack, store, back);
else
addToPolyList(store, back);
break;
}
}
}
// clip a poly to it's own shadow volume
void ShadowVolumeBSP::clipToSelf(SVNode * root, SVPoly ** store, SVPoly * poly)
{
if(!root)
{
addToPolyList(store, poly);
return;
}
const PlaneF & plane = getPlane(root->mPlaneIndex);
switch(whichSide(poly, plane))
{
case SVNode::Front:
clipToSelf(root->mFront, store, poly);
break;
case SVNode::On:
addToPolyList(store, poly);
break;
case SVNode::Back:
recyclePoly(poly);
break;
case SVNode::Split:
{
SVPoly * front = 0;
SVPoly * back = 0;
splitPoly(poly, plane, &front, &back);
AssertFatal(front && back, "ShadowVolumeBSP::clipToSelf: invalid split");
recyclePoly(poly);
recyclePoly(back);
clipToSelf(root->mFront, store, front);
break;
}
}
}
//------------------------------------------------------------------------------
F32 ShadowVolumeBSP::getPolySurfaceArea(SVPoly * poly) const
{
if(!poly)
return(0.f);
Point3F areaNorm(0,0,0);
for(U32 i = 0; i < poly->mWindingCount; i++)
{
U32 j = (i + 1) % poly->mWindingCount;
Point3F tmp;
mCross(poly->mWinding[i], poly->mWinding[j], &tmp);
areaNorm += tmp;
}
F32 area = mDot(poly->mPlane, areaNorm);
if(area < 0.f)
area *= -0.5f;
else
area *= 0.5f;
if(poly->mNext)
area += getPolySurfaceArea(poly->mNext);
return(area);
}
//------------------------------------------------------------------------------
F32 ShadowVolumeBSP::getClippedSurfaceArea(SVNode * root, SVPoly * poly)
{
SVPoly * store = 0;
clipPoly(root, &store, poly);
F32 area = getPolySurfaceArea(store);
recyclePoly(store);
return(area);
}
//-------------------------------------------------------------------------------
// Class SceneLighting::ShadowVolumeBSP
//-------------------------------------------------------------------------------
void ShadowVolumeBSP::movePolyList(SVPoly ** dest, SVPoly * list) const
{
while(list)
{
SVPoly * next = list->mNext;
addToPolyList(dest, list);
list = next;
}
}
F32 ShadowVolumeBSP::getLitSurfaceArea(SVPoly * poly, SurfaceInfo * surfaceInfo)
{
// clip the poly to the shadow volumes
SVPoly * polyStore = poly;
for(U32 i = 0; polyStore && (i < surfaceInfo->mShadowed.size()); i++)
{
SVPoly * polyList = 0;
SVPoly * traverse = polyStore;
while(traverse)
{
SVPoly * next = traverse->mNext;
traverse->mNext = 0;
SVPoly * currentStore = 0;
clipPoly(mShadowVolumes[surfaceInfo->mShadowed[i]], &currentStore, traverse);
if(currentStore)
movePolyList(&polyList, currentStore);
traverse = next;
}
polyStore = polyList;
}
// get the lit area
F32 area = getPolySurfaceArea(polyStore);
recyclePoly(polyStore);
return(area);
}
//------------------------------------------------------------------------------
void ShadowVolumeBSP::removeLastInterior()
{
if(!mSVRoot || !mFirstInteriorNode)
return;
AssertFatal(mFirstInteriorNode->mSurfaceInfo, "No surface info for first interior node!");
// reset the planes
mPlanes.setSize(mFirstInteriorNode->mPlaneIndex);
U32 i;
// flush the shadow volumes
for(i = mFirstInteriorNode->mShadowVolume; i < mShadowVolumes.size(); i++)
recycleNode(mShadowVolumes[i]);
mShadowVolumes.setSize(mFirstInteriorNode->mShadowVolume);
// flush the interior nodes
if(!mParentNodes.size() && (mFirstInteriorNode->mShadowVolume == mSVRoot->mShadowVolume))
{
recycleNode(mSVRoot);
mSVRoot = 0;
}
else
{
for(i = 0; i < mParentNodes.size(); i++)
{
recycleNode(mParentNodes[i]->mBack);
mParentNodes[i]->mBack = 0;
}
}
// flush the surfaces
for(i = 0; i < mSurfaces.size(); i++)
delete mSurfaces[i];
mSurfaces.clear();
mFirstInteriorNode = 0;
}

View file

@ -0,0 +1,154 @@
//-----------------------------------------------------------------------------
// 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 _SHADOWVOLUMEBSP_H_
#define _SHADOWVOLUMEBSP_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _DATACHUNKER_H_
#include "core/dataChunker.h"
#endif
#ifndef _LIGHTMANAGER_H_
#include "lighting/lightManager.h"
#endif
/// Used to calculate shadows.
class ShadowVolumeBSP
{
public:
ShadowVolumeBSP();
~ShadowVolumeBSP();
struct SVNode;
struct SurfaceInfo
{
U32 mSurfaceIndex;
U32 mPlaneIndex;
Vector<U32> mShadowed;
SVNode * mShadowVolume;
};
struct SVNode
{
enum Side
{
Front = 0,
Back = 1,
On = 2,
Split = 3
};
SVNode * mFront;
SVNode * mBack;
U32 mPlaneIndex;
U32 mShadowVolume;
/// Used with shadowed interiors.
SurfaceInfo * mSurfaceInfo;
};
struct SVPoly
{
enum {
MaxWinding = 32
};
U32 mWindingCount;
Point3F mWinding[MaxWinding];
PlaneF mPlane;
SVNode * mTarget;
U32 mShadowVolume;
SVPoly * mNext;
SurfaceInfo * mSurfaceInfo;
};
void insertPoly(SVNode **, SVPoly *);
void insertPolyFront(SVNode **, SVPoly *);
void insertPolyBack(SVNode **, SVPoly *);
void splitPoly(SVPoly *, const PlaneF &, SVPoly **, SVPoly **);
void insertShadowVolume(SVNode **, U32);
void addUniqueVolume(SurfaceInfo *, U32);
SVNode::Side whichSide(SVPoly *, const PlaneF &) const;
//
bool testPoint(SVNode *, const Point3F &);
bool testPoly(SVNode *, SVPoly *);
void addToPolyList(SVPoly **, SVPoly *) const;
void clipPoly(SVNode *, SVPoly **, SVPoly *);
void clipToSelf(SVNode *, SVPoly **, SVPoly *);
F32 getPolySurfaceArea(SVPoly *) const;
F32 getClippedSurfaceArea(SVNode *, SVPoly *);
void movePolyList(SVPoly **, SVPoly *) const;
F32 getLitSurfaceArea(SVPoly *, SurfaceInfo *);
Vector<SurfaceInfo *> mSurfaces;
Chunker<SVNode> mNodeChunker;
Chunker<SVPoly> mPolyChunker;
SVNode * createNode();
void recycleNode(SVNode *);
SVPoly * createPoly();
void recyclePoly(SVPoly *);
U32 insertPlane(const PlaneF &);
const PlaneF & getPlane(U32) const;
//
SVNode * mSVRoot;
Vector<SVNode*> mShadowVolumes;
SVNode * getShadowVolume(U32);
Vector<PlaneF> mPlanes;
SVNode * mNodeStore;
SVPoly * mPolyStore;
// used to remove the last inserted interior from the tree
Vector<SVNode*> mParentNodes;
SVNode * mFirstInteriorNode;
void removeLastInterior();
/// @name Access functions
/// @{
void insertPoly(SVPoly * poly) {insertPoly(&mSVRoot, poly);}
bool testPoint(Point3F & pnt) {return(testPoint(mSVRoot, pnt));}
bool testPoly(SVPoly * poly) {return(testPoly(mSVRoot, poly));}
F32 getClippedSurfaceArea(SVPoly * poly) {return(getClippedSurfaceArea(mSVRoot, poly));}
/// @}
/// @name Helpers
/// @{
void buildPolyVolume(SVPoly *, LightInfo *);
SVPoly * copyPoly(SVPoly *);
/// @}
};
#endif

View file

@ -0,0 +1,221 @@
//-----------------------------------------------------------------------------
// 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/lightInfo.h"
#include "math/mMath.h"
#include "core/color.h"
#include "gfx/gfxCubemap.h"
#include "console/simObject.h"
#include "math/mathUtils.h"
LightInfoExType::LightInfoExType( const char *type )
{
TypeMap::Iterator iter = getTypeMap().find( type );
if ( iter == getTypeMap().end() )
iter = getTypeMap().insertUnique( type, getTypeMap().size() );
mTypeIndex = iter->value;
}
LightInfo::LightInfo()
: mTransform( true ),
mColor( 0.0f, 0.0f, 0.0f, 1.0f ),
mBrightness( 1.0f ),
mAmbient( 0.0f, 0.0f, 0.0f, 1.0f ),
mRange( 1.0f, 1.0f, 1.0f ),
mInnerConeAngle( 90.0f ),
mOuterConeAngle( 90.0f ),
mType( Vector ),
mCastShadows( false ),
mPriority( 1.0f ),
mScore( 0.0f ),
mDebugRender( false )
{
}
LightInfo::~LightInfo()
{
deleteAllLightInfoEx();
}
void LightInfo::set( const LightInfo *light )
{
mTransform = light->mTransform;
mColor = light->mColor;
mBrightness = light->mBrightness;
mAmbient = light->mAmbient;
mRange = light->mRange;
mInnerConeAngle = light->mInnerConeAngle;
mOuterConeAngle = light->mOuterConeAngle;
mType = light->mType;
mCastShadows = light->mCastShadows;
for ( U32 i=0; i < mExtended.size(); i++ )
{
LightInfoEx *ex = light->mExtended[ i ];
if ( ex )
mExtended[i]->set( ex );
else
{
delete mExtended[i];
mExtended[i] = NULL;
}
}
}
void LightInfo::setGFXLight( GFXLightInfo *outLight )
{
switch( getType() )
{
case LightInfo::Point :
outLight->mType = GFXLightInfo::Point;
break;
case LightInfo::Spot :
outLight->mType = GFXLightInfo::Spot;
break;
case LightInfo::Vector:
outLight->mType = GFXLightInfo::Vector;
break;
case LightInfo::Ambient:
outLight->mType = GFXLightInfo::Ambient;
break;
default:
break;
}
outLight->mPos = getPosition();
outLight->mDirection = getDirection();
outLight->mColor = mColor * mBrightness;
outLight->mAmbient = mAmbient;
outLight->mRadius = mRange.x;
outLight->mInnerConeAngle = mInnerConeAngle;
outLight->mOuterConeAngle = mOuterConeAngle;
}
void LightInfo::setDirection( const VectorF &dir )
{
MathUtils::getMatrixFromForwardVector( mNormalize( dir ), &mTransform );
}
void LightInfo::deleteExtended( const LightInfoExType& type )
{
if ( type >= mExtended.size() )
return;
SAFE_DELETE( mExtended[ type ] );
}
void LightInfo::deleteAllLightInfoEx()
{
for ( U32 i = 0; i < mExtended.size(); i++ )
delete mExtended[ i ];
mExtended.clear();
}
LightInfoEx* LightInfo::getExtended( const LightInfoExType &type ) const
{
if ( type >= mExtended.size() )
return NULL;
return mExtended[ type ];
}
void LightInfo::addExtended( LightInfoEx *lightInfoEx )
{
AssertFatal( lightInfoEx, "LightInfo::addExtended() - Got null extended light info!" );
const LightInfoExType &type = lightInfoEx->getType();
while ( mExtended.size() <= type )
mExtended.push_back( NULL );
delete mExtended[type];
mExtended[type] = lightInfoEx;
}
void LightInfo::packExtended( BitStream *stream ) const
{
for ( U32 i = 0; i < mExtended.size(); i++ )
if ( mExtended[ i ] )
mExtended[ i ]->packUpdate( stream );
}
void LightInfo::unpackExtended( BitStream *stream )
{
for ( U32 i = 0; i < mExtended.size(); i++ )
if ( mExtended[ i ] )
mExtended[ i ]->unpackUpdate( stream );
}
void LightInfo::getWorldToLightProj( MatrixF *outMatrix ) const
{
if ( mType == Spot )
{
// For spots we need to include the cone projection.
F32 fov = mDegToRad( getOuterConeAngle() );
F32 range = getRange().x;
MatrixF proj;
MathUtils::makeProjection( &proj, fov, 1.0f, range * 0.01f, range, true );
MatrixF light = getTransform();
light.inverse();
*outMatrix = proj * light;
return;
}
else
{
// The other lights just use the light transform.
*outMatrix = getTransform();
outMatrix->inverse();
}
}
void LightInfoList::registerLight( LightInfo *light )
{
if(!light)
return;
// just add the light, we'll try to scan for dupes later...
push_back(light);
}
void LightInfoList::unregisterLight( LightInfo *light )
{
// remove all of them...
LightInfoList &list = *this;
for(U32 i=0; i<list.size(); i++)
{
if(list[i] != light)
continue;
// this moves last to i, which allows
// the search to continue forward...
list.erase_fast(i);
// want to check this location again...
i--;
}
}

View file

@ -0,0 +1,258 @@
//-----------------------------------------------------------------------------
// 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 _LIGHTINFO_H_
#define _LIGHTINFO_H_
#ifndef _GFXSTRUCTS_H_
#include "gfx/gfxStructs.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#ifndef _TDICTIONARY_H_
#include "core/util/tDictionary.h"
#endif
struct SceneData;
class LightManager;
class SimObject;
class BitStream;
/// The extended light info type wrapper object.
class LightInfoExType
{
protected:
typedef HashTable<String,U32> TypeMap;
/// Returns the map of all the info types. We create
/// it as a method static so that its available to other
/// statics regardless of initialization order.
static inline TypeMap& getTypeMap()
{
static TypeMap smTypeMap;
return smTypeMap;
}
/// The info type index for this type.
U32 mTypeIndex;
public:
LightInfoExType( const char *type );
inline LightInfoExType( const LightInfoExType &type )
: mTypeIndex( type.mTypeIndex )
{
}
inline operator U32 () const { return mTypeIndex; }
};
/// This is the base class for extended lighting info
/// that lies outside of the normal info stored in LightInfo.
class LightInfoEx
{
public:
/// Basic destructor so we can delete the extended info
/// without knowing the concrete type.
virtual ~LightInfoEx() { }
///
virtual const LightInfoExType& getType() const = 0;
/// Copy the values from the other LightInfoEx.
virtual void set( const LightInfoEx *ex ) {}
///
virtual void packUpdate( BitStream *stream ) const {}
///
virtual void unpackUpdate( BitStream *stream ) {}
};
/// This is the base light information class that will be tracked by the
/// engine. Should basically contain a bounding volume and methods to interact
/// with the rest of the system (for example, setting GFX fixed function lights).
class LightInfo
{
public:
enum Type
{
Point = 0,
Spot = 1,
Vector = 2,
Ambient = 3,
Count = 4,
};
protected:
Type mType;
/// The primary light color.
ColorF mColor;
F32 mBrightness;
ColorF mAmbient;
MatrixF mTransform;
Point3F mRange;
F32 mInnerConeAngle;
F32 mOuterConeAngle;
bool mCastShadows;
::Vector<LightInfoEx*> mExtended;
/// The priority of this light used for
/// light and shadow scoring.
F32 mPriority;
/// A temporary which holds the score used
/// when prioritizing lights for rendering.
F32 mScore;
/// Whether to render debugging visualizations
/// for this light.
bool mDebugRender;
public:
LightInfo();
~LightInfo();
// Copies data passed in from light
void set( const LightInfo *light );
// Sets a fixed function GFXLight with our properties
void setGFXLight( GFXLightInfo *light );
// Accessors
Type getType() const { return mType; }
void setType( Type val ) { mType = val; }
const MatrixF& getTransform() const { return mTransform; }
void setTransform( const MatrixF &xfm ) { mTransform = xfm; }
Point3F getPosition() const { return mTransform.getPosition(); }
void setPosition( const Point3F &pos ) { mTransform.setPosition( pos ); }
VectorF getDirection() const { return mTransform.getForwardVector(); }
void setDirection( const VectorF &val );
const ColorF& getColor() const { return mColor; }
void setColor( const ColorF &val ) { mColor = val; }
F32 getBrightness() const { return mBrightness; }
void setBrightness( F32 val ) { mBrightness = val; }
const ColorF& getAmbient() const { return mAmbient; }
void setAmbient( const ColorF &val ) { mAmbient = val; }
const Point3F& getRange() const { return mRange; }
void setRange( const Point3F &range ) { mRange = range; }
void setRange( F32 range ) { mRange.set( range, range, range ); }
F32 getInnerConeAngle() const { return mInnerConeAngle; }
void setInnerConeAngle( F32 val ) { mInnerConeAngle = val; }
F32 getOuterConeAngle() const { return mOuterConeAngle; }
void setOuterConeAngle( F32 val ) { mOuterConeAngle = val; }
bool getCastShadows() const { return mCastShadows; }
void setCastShadows( bool castShadows ) { mCastShadows = castShadows; }
void setPriority( F32 priority ) { mPriority = priority; }
F32 getPriority() const { return mPriority; }
void setScore( F32 score ) { mScore = score; }
F32 getScore() const { return mScore; }
bool isDebugRenderingEnabled() const { return mDebugRender; }
void enableDebugRendering( bool value ) { mDebugRender = value; }
/// Helper function for getting the extended light info.
/// @see getExtended
template <class ExClass>
inline ExClass* getExtended() const { return (ExClass*)getExtended( ExClass::Type ); }
/// Returns the extended light info for the selected type.
LightInfoEx* getExtended( const LightInfoExType &type ) const;
/// Adds the extended info to the light deleting the
/// existing extended info if it has one.
void addExtended( LightInfoEx *lightInfoEx );
/// Delete all registered LightInfoEx instances of the given
/// type.
void deleteExtended( const LightInfoExType& type );
///
void deleteAllLightInfoEx();
// Builds the world to light view projection used for
// shadow texture and cookie lookups.
void getWorldToLightProj( MatrixF *outMatrix ) const;
///
void packExtended( BitStream *stream ) const;
///
void unpackExtended( BitStream *stream );
};
///
class LightInfoList : public Vector<LightInfo*>
{
public:
void registerLight( LightInfo *light );
void unregisterLight( LightInfo *light );
};
/// When the scene is queried for lights, the light manager will get
/// this interface to trigger a register light call.
class ISceneLight
{
public:
virtual ~ISceneLight() {}
/// Submit lights to the light manager passed in.
virtual void submitLights( LightManager *lm, bool staticLighting ) = 0;
///
virtual LightInfo* getLight() = 0;
};
#endif // _LIGHTINFO_H_

View file

@ -0,0 +1,499 @@
//-----------------------------------------------------------------------------
// 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/lightManager.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "core/util/safeDelete.h"
#include "console/sim.h"
#include "console/simSet.h"
#include "scene/sceneManager.h"
#include "materials/materialManager.h"
#include "materials/sceneData.h"
#include "lighting/lightInfo.h"
#include "lighting/lightingInterfaces.h"
#include "T3D/gameBase/gameConnection.h"
#include "gfx/gfxStringEnumTranslate.h"
#include "console/engineAPI.h"
#include "renderInstance/renderPrePassMgr.h"
Signal<void(const char*,bool)> LightManager::smActivateSignal;
LightManager *LightManager::smActiveLM = NULL;
LightManager::LightManager( const char *name, const char *id )
: mName( name ),
mId( id ),
mIsActive( false ),
mSceneManager( NULL ),
mDefaultLight( NULL ),
mAvailableSLInterfaces( NULL ),
mCullPos( Point3F::Zero )
{
_getLightManagers().insert( mName, this );
dMemset( &mSpecialLights, 0, sizeof( mSpecialLights ) );
}
LightManager::~LightManager()
{
_getLightManagers().erase( mName );
SAFE_DELETE( mAvailableSLInterfaces );
SAFE_DELETE( mDefaultLight );
}
LightManagerMap& LightManager::_getLightManagers()
{
static LightManagerMap lightManagerMap;
return lightManagerMap;
}
LightManager* LightManager::findByName( const char *name )
{
LightManagerMap &lightManagers = _getLightManagers();
LightManagerMap::Iterator iter = lightManagers.find( name );
if ( iter != lightManagers.end() )
return iter->value;
return NULL;
}
void LightManager::getLightManagerNames( String *outString )
{
LightManagerMap &lightManagers = _getLightManagers();
LightManagerMap::Iterator iter = lightManagers.begin();
for ( ; iter != lightManagers.end(); iter++ )
*outString += iter->key + "\t";
// TODO!
//outString->rtrim();
}
LightInfo* LightManager::createLightInfo(LightInfo* light /* = NULL */)
{
LightInfo *outLight = (light != NULL) ? light : new LightInfo;
LightManagerMap &lightManagers = _getLightManagers();
LightManagerMap::Iterator iter = lightManagers.begin();
for ( ; iter != lightManagers.end(); iter++ )
{
LightManager *lm = iter->value;
lm->_addLightInfoEx( outLight );
}
return outLight;
}
void LightManager::initLightFields()
{
LightManagerMap &lightManagers = _getLightManagers();
LightManagerMap::Iterator iter = lightManagers.begin();
for ( ; iter != lightManagers.end(); iter++ )
{
LightManager *lm = iter->value;
lm->_initLightFields();
}
}
IMPLEMENT_GLOBAL_CALLBACK( onLightManagerActivate, void, ( const char *name ), ( name ),
"A callback called by the engine when a light manager is activated.\n"
"@param name The name of the light manager being activated.\n"
"@ingroup Lighting\n" );
void LightManager::activate( SceneManager *sceneManager )
{
AssertFatal( sceneManager, "LightManager::activate() - Got null scene manager!" );
AssertFatal( mIsActive == false, "LightManager::activate() - Already activated!" );
AssertFatal( smActiveLM == NULL, "LightManager::activate() - A previous LM is still active!" );
mIsActive = true;
mSceneManager = sceneManager;
smActiveLM = this;
onLightManagerActivate_callback( getName() );
}
IMPLEMENT_GLOBAL_CALLBACK( onLightManagerDeactivate, void, ( const char *name ), ( name ),
"A callback called by the engine when a light manager is deactivated.\n"
"@param name The name of the light manager being deactivated.\n"
"@ingroup Lighting\n" );
void LightManager::deactivate()
{
AssertFatal( mIsActive == true, "LightManager::deactivate() - Already deactivated!" );
AssertFatal( smActiveLM == this, "LightManager::activate() - This isn't the active light manager!" );
if( Sim::getRootGroup() ) // To protect against shutdown.
onLightManagerDeactivate_callback( getName() );
mIsActive = false;
mSceneManager = NULL;
smActiveLM = NULL;
// Just in case... make sure we're all clear.
unregisterAllLights();
}
LightInfo* LightManager::getDefaultLight()
{
// The sun is always our default light when
// when its registered.
if ( mSpecialLights[ LightManager::slSunLightType ] )
return mSpecialLights[ LightManager::slSunLightType ];
// Else return a dummy special light.
if ( !mDefaultLight )
mDefaultLight = createLightInfo();
return mDefaultLight;
}
LightInfo* LightManager::getSpecialLight( LightManager::SpecialLightTypesEnum type, bool useDefault )
{
if ( mSpecialLights[type] )
return mSpecialLights[type];
if ( useDefault )
return getDefaultLight();
return NULL;
}
void LightManager::setSpecialLight( LightManager::SpecialLightTypesEnum type, LightInfo *light )
{
if ( light && type == slSunLightType )
{
// The sun must be specially positioned and ranged
// so that it can be processed like a point light
// in the stock light shader used by Basic Lighting.
light->setPosition( mCullPos - ( light->getDirection() * 10000.0f ) );
light->setRange( 2000000.0f );
}
mSpecialLights[type] = light;
registerGlobalLight( light, NULL );
}
void LightManager::registerGlobalLights( const Frustum *frustum, bool staticLighting )
{
PROFILE_SCOPE( LightManager_RegisterGlobalLights );
// TODO: We need to work this out...
//
// 1. Why do we register and unregister lights on every
// render when they don't often change... shouldn't we
// just register once and keep them?
//
// 2. If we do culling of lights should this happen as part
// of registration or somewhere else?
//
// Grab the lights to process.
Vector<SceneObject*> activeLights;
const U32 lightMask = LightObjectType;
if ( staticLighting || !frustum )
{
// We're processing static lighting or want all the lights
// in the container registerd... so no culling.
getSceneManager()->getContainer()->findObjectList( lightMask, &activeLights );
}
else
{
// Cull the lights using the frustum.
getSceneManager()->getContainer()->findObjectList( *frustum, lightMask, &activeLights );
// Store the culling position for sun placement
// later... see setSpecialLight.
mCullPos = frustum->getPosition();
// HACK: Make sure the control object always gets
// processed as lights mounted to it don't change
// the shape bounds and can often get culled.
GameConnection *conn = GameConnection::getConnectionToServer();
if ( conn->getControlObject() )
{
GameBase *conObject = conn->getControlObject();
activeLights.push_back_unique( conObject );
}
}
// Let the lights register themselves.
for ( U32 i = 0; i < activeLights.size(); i++ )
{
ISceneLight *lightInterface = dynamic_cast<ISceneLight*>( activeLights[i] );
if ( lightInterface )
lightInterface->submitLights( this, staticLighting );
}
}
void LightManager::registerGlobalLight( LightInfo *light, SimObject *obj )
{
AssertFatal( !mRegisteredLights.contains( light ),
"LightManager::registerGlobalLight - This light is already registered!" );
mRegisteredLights.push_back( light );
}
void LightManager::unregisterGlobalLight( LightInfo *light )
{
mRegisteredLights.unregisterLight( light );
// If this is the sun... clear the special light too.
if ( light == mSpecialLights[slSunLightType] )
dMemset( mSpecialLights, 0, sizeof( mSpecialLights ) );
}
void LightManager::registerLocalLight( LightInfo *light )
{
// TODO: What should we do here?
}
void LightManager::unregisterLocalLight( LightInfo *light )
{
// TODO: What should we do here?
}
void LightManager::unregisterAllLights()
{
dMemset( mSpecialLights, 0, sizeof( mSpecialLights ) );
mRegisteredLights.clear();
}
void LightManager::getAllUnsortedLights( Vector<LightInfo*> *list ) const
{
list->merge( mRegisteredLights );
}
void LightManager::_update4LightConsts( const SceneData &sgData,
GFXShaderConstHandle *lightPositionSC,
GFXShaderConstHandle *lightDiffuseSC,
GFXShaderConstHandle *lightAmbientSC,
GFXShaderConstHandle *lightInvRadiusSqSC,
GFXShaderConstHandle *lightSpotDirSC,
GFXShaderConstHandle *lightSpotAngleSC,
GFXShaderConstHandle *lightSpotFalloffSC,
GFXShaderConstBuffer *shaderConsts )
{
PROFILE_SCOPE( LightManager_Update4LightConsts );
// Skip over gathering lights if we don't have to!
if ( lightPositionSC->isValid() ||
lightDiffuseSC->isValid() ||
lightInvRadiusSqSC->isValid() ||
lightSpotDirSC->isValid() ||
lightSpotAngleSC->isValid() ||
lightSpotFalloffSC->isValid() )
{
PROFILE_SCOPE( LightManager_Update4LightConsts_setLights );
// NOTE: We haven't ported the lighting shaders on OSX
// to the optimized HLSL versions.
#ifdef TORQUE_OS_MAC
static AlignedArray<Point3F> lightPositions( 4, sizeof( Point4F ) );
#else
static AlignedArray<Point4F> lightPositions( 3, sizeof( Point4F ) );
static AlignedArray<Point4F> lightSpotDirs( 3, sizeof( Point4F ) );
#endif
static AlignedArray<Point4F> lightColors( 4, sizeof( Point4F ) );
static Point4F lightInvRadiusSq;
static Point4F lightSpotAngle;
static Point4F lightSpotFalloff;
F32 range;
// Need to clear the buffers so that we don't leak
// lights from previous passes or have NaNs.
dMemset( lightPositions.getBuffer(), 0, lightPositions.getBufferSize() );
dMemset( lightColors.getBuffer(), 0, lightColors.getBufferSize() );
lightInvRadiusSq = Point4F::Zero;
lightSpotAngle.set( -1.0f, -1.0f, -1.0f, -1.0f );
lightSpotFalloff.set( F32_MAX, F32_MAX, F32_MAX, F32_MAX );
// Gather the data for the first 4 lights.
const LightInfo *light;
for ( U32 i=0; i < 4; i++ )
{
light = sgData.lights[i];
if ( !light )
break;
#ifdef TORQUE_OS_MAC
lightPositions[i] = light->getPosition();
#else
// The light positions and spot directions are
// in SoA order to make optimal use of the GPU.
const Point3F &lightPos = light->getPosition();
lightPositions[0][i] = lightPos.x;
lightPositions[1][i] = lightPos.y;
lightPositions[2][i] = lightPos.z;
const VectorF &lightDir = light->getDirection();
lightSpotDirs[0][i] = lightDir.x;
lightSpotDirs[1][i] = lightDir.y;
lightSpotDirs[2][i] = lightDir.z;
if ( light->getType() == LightInfo::Spot )
{
lightSpotAngle[i] = mCos( mDegToRad( light->getOuterConeAngle() / 2.0f ) );
lightSpotFalloff[i] = 1.0f / getMax( F32_MIN, mCos( mDegToRad( light->getInnerConeAngle() / 2.0f ) ) - lightSpotAngle[i] );
}
#endif
// Prescale the light color by the brightness to
// avoid doing this in the shader.
lightColors[i] = Point4F(light->getColor()) * light->getBrightness();
// We need 1 over range^2 here.
range = light->getRange().x;
lightInvRadiusSq[i] = 1.0f / ( range * range );
}
shaderConsts->setSafe( lightPositionSC, lightPositions );
shaderConsts->setSafe( lightDiffuseSC, lightColors );
shaderConsts->setSafe( lightInvRadiusSqSC, lightInvRadiusSq );
#ifndef TORQUE_OS_MAC
shaderConsts->setSafe( lightSpotDirSC, lightSpotDirs );
shaderConsts->setSafe( lightSpotAngleSC, lightSpotAngle );
shaderConsts->setSafe( lightSpotFalloffSC, lightSpotFalloff );
#endif
}
// Setup the ambient lighting from the first
// light which is the directional light if
// one exists at all in the scene.
if ( lightAmbientSC->isValid() )
shaderConsts->set( lightAmbientSC, sgData.ambientLightColor );
}
AvailableSLInterfaces* LightManager::getSceneLightingInterface()
{
if ( !mAvailableSLInterfaces )
mAvailableSLInterfaces = new AvailableSLInterfaces();
return mAvailableSLInterfaces;
}
bool LightManager::lightScene( const char* callback, const char* param )
{
BitSet32 flags = 0;
if ( param )
{
if ( !dStricmp( param, "forceAlways" ) )
flags.set( SceneLighting::ForceAlways );
else if ( !dStricmp(param, "forceWritable" ) )
flags.set( SceneLighting::ForceWritable );
else if ( !dStricmp(param, "loadOnly" ) )
flags.set( SceneLighting::LoadOnly );
}
// The SceneLighting object will delete itself
// once the lighting process is complete.
SceneLighting* sl = new SceneLighting( getSceneLightingInterface() );
return sl->lightScene( callback, flags );
}
RenderPrePassMgr* LightManager::_findPrePassRenderBin()
{
RenderPassManager* rpm = getSceneManager()->getDefaultRenderPass();
for( U32 i = 0; i < rpm->getManagerCount(); i++ )
{
RenderBinManager *bin = rpm->getManager( i );
if( bin->getRenderInstType() == RenderPrePassMgr::RIT_PrePass )
{
return ( RenderPrePassMgr* ) bin;
}
}
return NULL;
}
DefineEngineFunction( setLightManager, bool, ( const char *name ),,
"Finds and activates the named light manager.\n"
"@return Returns true if the light manager is found and activated.\n"
"@ingroup Lighting\n" )
{
return gClientSceneGraph->setLightManager( name );
}
DefineEngineFunction( lightScene, bool, ( const char *completeCallbackFn, const char *mode ), ( NULL, NULL ),
"Will generate static lighting for the scene if supported by the active light manager.\n\n"
"If mode is \"forceAlways\", the lightmaps will be regenerated regardless of whether "
"lighting cache files can be written to. If mode is \"forceWritable\", then the lightmaps "
"will be regenerated only if the lighting cache files can be written.\n"
"@param completeCallbackFn The name of the function to execute when the lighting is complete.\n"
"@param mode One of \"forceAlways\", \"forceWritable\" or \"loadOnly\".\n"
"@return Returns true if the scene lighting process was started.\n"
"@ingroup Lighting\n" )
{
if ( !LIGHTMGR )
return false;
return LIGHTMGR->lightScene( completeCallbackFn, mode );
}
DefineEngineFunction( getLightManagerNames, String, (),,
"Returns a tab seperated list of light manager names.\n"
"@ingroup Lighting\n" )
{
String names;
LightManager::getLightManagerNames( &names );
return names;
}
DefineEngineFunction( getActiveLightManager, const char*, (),,
"Returns the active light manager name.\n"
"@ingroup Lighting\n" )
{
if ( !LIGHTMGR )
return NULL;
return LIGHTMGR->getName();
}
DefineEngineFunction( resetLightManager, void, (),,
"@brief Deactivates and then activates the currently active light manager."
"This causes most shaders to be regenerated and is often used when global "
"rendering changes have occured.\n"
"@ingroup Lighting\n" )
{
LightManager *lm = LIGHTMGR;
if ( !lm )
return;
SceneManager *sm = lm->getSceneManager();
lm->deactivate();
lm->activate( sm );
}

View file

@ -0,0 +1,232 @@
//-----------------------------------------------------------------------------
// 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 _LIGHTMANAGER_H_
#define _LIGHTMANAGER_H_
#ifndef _TORQUE_STRING_H_
#include "core/util/str.h"
#endif
#ifndef _TSIGNAL_H_
#include "core/util/tSignal.h"
#endif
#ifndef _LIGHTINFO_H_
#include "lighting/lightInfo.h"
#endif
#ifndef _LIGHTQUERY_H_
#include "lighting/lightQuery.h"
#endif
class SimObject;
class LightManager;
class Material;
class ProcessedMaterial;
class SceneManager;
struct SceneData;
class Point3F;
class AvailableSLInterfaces;
class SceneObject;
class GFXShaderConstBuffer;
class GFXShaderConstHandle;
class ShaderConstHandles;
class SceneRenderState;
class RenderPrePassMgr;
class Frustum;
///
typedef Map<String,LightManager*> LightManagerMap;
class LightManager
{
public:
enum SpecialLightTypesEnum
{
slSunLightType,
slSpecialLightTypesCount
};
LightManager( const char *name, const char *id );
virtual ~LightManager();
///
static void initLightFields();
///
static LightInfo* createLightInfo(LightInfo* light = NULL);
///
static LightManager* findByName( const char *name );
/// Returns a tab seperated list of available light managers.
static void getLightManagerNames( String *outString );
/// The light manager activation signal.
static Signal<void(const char*,bool)> smActivateSignal;
/// Returns the active LM.
static inline LightManager* getActiveLM() { return smActiveLM; }
/// Return an id string used to load different versions of light manager
/// specific assets. It shoud be short, contain no spaces, and be safe
/// for filename use.
const char* getName() const { return mName.c_str(); }
/// Return an id string used to load different versions of light manager
/// specific assets. It shoud be short, contain no spaces, and be safe
/// for filename use.
const char* getId() const { return mId.c_str(); }
// Returns the scene manager passed at activation.
SceneManager* getSceneManager() { return mSceneManager; }
// Should return true if this light manager is compatible
// on the current platform and GFX device.
virtual bool isCompatible() const = 0;
// Called when the lighting manager should become active
virtual void activate( SceneManager *sceneManager );
// Called when we don't want the light manager active (should clean up)
virtual void deactivate();
// Returns the active scene lighting interface for this light manager.
virtual AvailableSLInterfaces* getSceneLightingInterface();
// Returns a "default" light info that callers should not free. Used for instances where we don't actually care about
// the light (for example, setting default data for SceneData)
virtual LightInfo* getDefaultLight();
/// Returns the special light or the default light if useDefault is true.
/// @see getDefaultLight
virtual LightInfo* getSpecialLight( SpecialLightTypesEnum type,
bool useDefault = true );
/// Set a special light type.
virtual void setSpecialLight( SpecialLightTypesEnum type, LightInfo *light );
// registered before scene traversal...
virtual void registerGlobalLight( LightInfo *light, SimObject *obj );
virtual void unregisterGlobalLight( LightInfo *light );
// registered per object...
virtual void registerLocalLight( LightInfo *light );
virtual void unregisterLocalLight( LightInfo *light );
virtual void registerGlobalLights( const Frustum *frustum, bool staticlighting );
virtual void unregisterAllLights();
/// Returns all unsorted and un-scored lights (both global and local).
void getAllUnsortedLights( Vector<LightInfo*> *list ) const;
/// Sets shader constants / textures for light infos
virtual void setLightInfo( ProcessedMaterial *pmat,
const Material *mat,
const SceneData &sgData,
const SceneRenderState *state,
U32 pass,
GFXShaderConstBuffer *shaderConsts ) = 0;
/// Allows us to set textures during the Material::setTextureStage call, return true if we've done work.
virtual bool setTextureStage( const SceneData &sgData,
const U32 currTexFlag,
const U32 textureSlot,
GFXShaderConstBuffer *shaderConsts,
ShaderConstHandles *handles ) = 0;
/// Called when the static scene lighting (aka lightmaps) should be computed.
virtual bool lightScene( const char* callback, const char* param );
/// Returns true if this light manager is active
virtual bool isActive() const { return mIsActive; }
protected:
/// The current active light manager.
static LightManager *smActiveLM;
/// Find the pre-pass render bin on the scene's default render pass.
RenderPrePassMgr* _findPrePassRenderBin();
/// This helper function sets the shader constansts
/// for the stock 4 light forward lighting code.
static void _update4LightConsts( const SceneData &sgData,
GFXShaderConstHandle *lightPositionSC,
GFXShaderConstHandle *lightDiffuseSC,
GFXShaderConstHandle *lightAmbientSC,
GFXShaderConstHandle *lightInvRadiusSqSC,
GFXShaderConstHandle *lightSpotDirSC,
GFXShaderConstHandle *lightSpotAngleSC,
GFXShaderConstHandle *lightSpotFalloffSC,
GFXShaderConstBuffer *shaderConsts );
/// A dummy default light used when no lights
/// happen to be registered with the manager.
LightInfo *mDefaultLight;
/// The list of global registered lights which is
/// initialized before the scene is rendered.
LightInfoList mRegisteredLights;
/// The registered special light list.
LightInfo *mSpecialLights[slSpecialLightTypesCount];
/// The root culling position used for
/// special sun light placement.
/// @see setSpecialLight
Point3F mCullPos;
/// The scene lighting interfaces for
/// lightmap generation.
AvailableSLInterfaces *mAvailableSLInterfaces;
/// Attaches any LightInfoEx data for this manager
/// to the light info object.
virtual void _addLightInfoEx( LightInfo *lightInfo ) = 0;
///
virtual void _initLightFields() = 0;
/// Returns the static light manager map.
static LightManagerMap& _getLightManagers();
/// The constant light manager name initialized
/// in the constructor.
const String mName;
/// The constant light manager identifier initialized
/// in the constructor.
const String mId;
/// Is true if this light manager has been activated.
bool mIsActive;
/// The scene graph the light manager is associated with.
SceneManager *mSceneManager;
};
/// Returns the current active light manager.
#define LIGHTMGR LightManager::getActiveLM()
#endif // _LIGHTMANAGER_H_

View file

@ -0,0 +1,171 @@
//-----------------------------------------------------------------------------
// 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/lightQuery.h"
#include "lighting/lightManager.h"
#include "platform/profiler.h"
LightQuery::LightQuery( U32 maxLights )
: mMaxLights( maxLights )
{
}
LightQuery::~LightQuery()
{
}
void LightQuery::init( const Point3F &cameraPos,
const Point3F &cameraDir,
F32 viewDist )
{
mVolume.center = cameraPos;
mVolume.radius = viewDist;
mLights.clear();
}
void LightQuery::init( const SphereF &bounds )
{
mVolume = bounds;
mLights.clear();
}
void LightQuery::init( const Box3F &bounds )
{
bounds.getCenter( &mVolume.center );
mVolume.radius = ( bounds.maxExtents - mVolume.center ).len();
mLights.clear();
}
void LightQuery::getLights( LightInfo** outLights, U32 maxLights )
{
PROFILE_SCOPE( LightQuery_getLights );
// Gather lights if we haven't already.
if ( mLights.empty() )
_scoreLights();
U32 lightCount = getMin( (U32)mLights.size(), getMin( mMaxLights, maxLights ) );
// Copy them over.
for ( U32 i = 0; i < lightCount; i++ )
{
LightInfo *light = mLights[i];
// If the score reaches zero then we got to
// the end of the valid lights for this object.
if ( light->getScore() <= 0.0f )
break;
outLights[i] = light;
}
}
void LightQuery::_scoreLights()
{
PROFILE_SCOPE( LightQuery_scoreLights );
if ( !LIGHTMGR )
return;
// Get all the lights.
LIGHTMGR->getAllUnsortedLights( &mLights );
LightInfo *sun = LIGHTMGR->getSpecialLight( LightManager::slSunLightType );
const Point3F lumDot( 0.2125f, 0.7154f, 0.0721f );
Vector<LightInfo*>::iterator iter = mLights.begin();
for ( ; iter != mLights.end(); iter++ )
{
// Get the light.
LightInfo *light = (*iter);
F32 luminace = 0.0f;
F32 dist = 0.0f;
F32 weight = 0.0f;
const bool isSpot = light->getType() == LightInfo::Spot;
const bool isPoint = light->getType() == LightInfo::Point;
if ( isPoint || isSpot )
{
// Get the luminocity.
luminace = mDot( light->getColor(), lumDot ) * light->getBrightness();
// Get the distance to the light... score it 1 to 0 near to far.
F32 lenSq = ( mVolume.center - light->getPosition() ).lenSquared();
F32 radiusSq = mSquared( light->getRange().x + mVolume.radius );
F32 distSq = radiusSq - lenSq;
if ( distSq > 0.0f )
dist = mClampF( distSq / ( 1000.0f * 1000.0f ), 0.0f, 1.0f );
// TODO: This culling is broken... it culls spotlights
// that are actually visible.
if ( false && isSpot && dist > 0.0f )
{
// TODO: I cannot test to see if we're within
// the cone without a more detailed test... so
// just reject if we're behind the spot direction.
Point3F toCenter = mVolume.center - light->getPosition();
F32 angDot = mDot( toCenter, light->getDirection() );
if ( angDot < 0.0f )
dist = 0.0f;
}
weight = light->getPriority();
}
else
{
// The sun always goes first
// regardless of the settings.
if ( light == sun )
{
weight = F32_MAX;
dist = 1.0f;
luminace = 1.0f;
}
else
{
// TODO: When we have multiple directional
// lights we should score them here.
}
}
// TODO: Manager ambient lights here too!
light->setScore( luminace * weight * dist );
}
// Sort them!
mLights.sort( _lightScoreCmp );
}
S32 LightQuery::_lightScoreCmp( LightInfo* const *a, LightInfo* const *b )
{
F32 diff = (*a)->getScore() - (*b)->getScore();
return diff < 0 ? 1 : diff > 0 ? -1 : 0;
}

View file

@ -0,0 +1,82 @@
//-----------------------------------------------------------------------------
// 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 _LIGHTQUERY_H_
#define _LIGHTQUERY_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#ifndef _MSPHERE_H_
#include "math/mSphere.h"
#endif
#ifndef _MBOX_H_
#include "math/mBox.h"
#endif
class LightManager;
class LightInfo;
/// Used to gather an score lights for rendering.
class LightQuery
{
public:
LightQuery( U32 maxLights = 4 );
~LightQuery();
/// Set the query volume from a camera position and direction.
void init( const Point3F &cameraPos,
const Point3F &cameraDir,
F32 viewDist );
/// Set the query volume from a sphere.
void init( const SphereF &bounds );
/// Set the query volume from a box.
void init( const Box3F &bounds );
/// This returns the best lights based on the query volume.
void getLights( LightInfo** outLights, U32 maxLights );
protected:
void _scoreLights();
static S32 _lightScoreCmp( LightInfo* const *a, LightInfo* const *b );
/// The maximum lights to return from the query.
const U32 mMaxLights;
/// The sorted list of best lights.
Vector<LightInfo*> mLights;
/// The sphere used to query for lights.
SphereF mVolume;
};
#endif // _LIGHTQUERY_H_

View file

@ -0,0 +1,51 @@
//-----------------------------------------------------------------------------
// 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/lightingInterfaces.h"
void AvailableSLInterfaces::registerSystem(SceneLightingInterface* si)
{
mAvailableSystemInterfaces.push_back(si);
mDirty = true;
}
void AvailableSLInterfaces::initInterfaces()
{
if ( !mDirty )
return;
mAvailableObjectTypes = mClippingMask = mZoneLightSkipMask = 0;
SceneLightingInterface** sitr = mAvailableSystemInterfaces.begin();
for ( ; sitr != mAvailableSystemInterfaces.end(); sitr++ )
{
SceneLightingInterface* si = (*sitr);
si->init();
mAvailableObjectTypes |= si->addObjectType();
mClippingMask |= si->addToClippingMask();
mZoneLightSkipMask |= si->addToZoneLightSkipMask();
}
mDirty = false;
}

View file

@ -0,0 +1,123 @@
//-----------------------------------------------------------------------------
// 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 _SG_SYSTEM_INTERFACE_H
#define _SG_SYSTEM_INTERFACE_H
#ifndef _SGSCENEPERSIST_H_
#include "lighting/common/scenePersist.h"
#endif
#ifndef _SCENELIGHTING_H_
#include "lighting/common/sceneLighting.h"
#endif
class ObjectProxy;
class ObjectProxyList;
class SceneLightingInterface;
template <class T> class Vector;
typedef Vector<SceneLightingInterface*> SceneLightingInterfaces;
// List of available "systems" that the lighting kit can use
class AvailableSLInterfaces
{
protected:
bool mDirty;
public:
AvailableSLInterfaces()
: mAvailableObjectTypes( 0 ),
mClippingMask( 0 ),
mZoneLightSkipMask( 0 ),
mDirty( true )
{
VECTOR_SET_ASSOCIATION( mAvailableSystemInterfaces );
}
// Register a system
void registerSystem(SceneLightingInterface* si);
// Init the interfaces
void initInterfaces();
// The actual list of SceneLightingInterfaces
SceneLightingInterfaces mAvailableSystemInterfaces;
// Object types that are registered with the system
U32 mAvailableObjectTypes;
// Clipping typemask
U32 mClippingMask;
// Object types that we should skip zone lighting for
U32 mZoneLightSkipMask;
};
// This object is responsible for returning PersistChunk and ObjectProxy classes for the lighting system to use
// We may want to eventually split this into scene lighting vs. dynamic lighting. getColorFromRayInfo is a dynamic
// lighting thing.
class SceneLightingInterface
{
public:
SceneLightingInterface()
{
}
virtual ~SceneLightingInterface() { }
virtual void init() { }
//
// Scene lighting methods
//
// Creates an object proxy for obj
virtual SceneLighting::ObjectProxy* createObjectProxy(SceneObject* obj, SceneLighting::ObjectProxyList* sceneObjects) = 0;
// Creates a PersistChunk based on the chunkType flag
virtual PersistInfo::PersistChunk* createPersistChunk(const U32 chunkType) = 0;
// Creates a PersistChunk if needed for a proxy, returns true if it's "handled" by the system and ret contains the PersistChunk if needed.
virtual bool createPersistChunkFromProxy(SceneLighting::ObjectProxy* proxy, PersistInfo::PersistChunk** ret) = 0;
// Returns which object type flag this system supports (used to query scene graph for objects to light)
virtual U32 addObjectType() = 0;
// Add an object type flag to the "allow clipping mask" (used for blob shadows)
virtual U32 addToClippingMask() { return 0; }
// Add an object type flag to skip zone lighting
virtual U32 addToZoneLightSkipMask() { return 0; }
// Allows for processing/validating of the scene list after loading cached persistant info, return false if a relight is required or true if the data looks good.
virtual bool postProcessLoad(PersistInfo* pi, SceneLighting::ObjectProxyList* sceneObjects) { return true; }
virtual void processLightingBegin() { }
virtual void processLightingCompleted(bool success) { }
//
// Runtime / dynamic methods
//
// Given a ray, this will return the color from the lightmap of this object, return true if handled
virtual bool getColorFromRayInfo(const RayInfo & collision, ColorF& result) const { return false; }
};
#endif

View file

@ -0,0 +1,79 @@
//-----------------------------------------------------------------------------
// 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/shadowManager.h"
#include "scene/sceneManager.h"
#include "materials/materialManager.h"
const String ShadowManager::ManagerTypeName("ShadowManager");
//------------------------------------------------------------------------------
bool ShadowManager::canActivate()
{
return true;
}
//------------------------------------------------------------------------------
void ShadowManager::activate()
{
mSceneManager = gClientSceneGraph; //;getWorld()->findWorldManager<SceneManager>();
}
//------------------------------------------------------------------------------
SceneManager* ShadowManager::getSceneManager()
{
return mSceneManager;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Runtime switching of shadow systems. Requires correct world to be pushed at console.
ConsoleFunction( setShadowManager, bool, 1, 3, "string sShadowSystemName" )
{
/*
// Make sure this new one exists
ShadowManager * newSM = dynamic_cast<ShadowManager*>(ConsoleObject::create(argv[1]));
if (!newSM)
return false;
// Cleanup current
ShadowManager * currentSM = world->findWorldManager<ShadowManager>();
if (currentSM)
{
currentSM->deactivate();
world->removeWorldManager(currentSM);
delete currentSM;
}
// Add to world and init.
world->addWorldManager(newSM);
newSM->activate();
MaterialManager::get()->reInitInstances();
*/
return true;
}

View file

@ -0,0 +1,66 @@
//-----------------------------------------------------------------------------
// 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 _SHADOWMANAGER_H_
#define _SHADOWMANAGER_H_
#ifndef _TSIGNAL_H_
#include "core/util/tSignal.h"
#endif
#ifndef _TORQUE_STRING_H_
#include "core/util/str.h"
#endif
class SceneManager;
class ShadowManager
{
public:
ShadowManager() : mSceneManager(NULL) {}
virtual ~ShadowManager() { }
// Called when the shadow manager should become active
virtual void activate();
// Called when we don't want the shadow manager active (should clean up)
virtual void deactivate() { }
// Return an "id" that other systems can use to load different versions of assets (custom shaders, etc.)
// Should be short and contain no spaces and safe for filename use.
//virtual const char* getId() const = 0;
// SceneManager manager
virtual SceneManager* getSceneManager();
// Called to find out if it is valid to activate this shadow system. If not, we should print out
// a console warning explaining why.
virtual bool canActivate();
// SimWorldManager
static const String ManagerTypeName;
const String & getManagerTypeName() const { return ManagerTypeName; }
private:
SceneManager* mSceneManager;
};
#endif // _SHADOWMANAGER_H_

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_