//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
# include "render_pch.h"
# include "shadowmgr.h"
# include "utllinkedlist.h"
# include "utlvector.h"
# include "interface.h"
# include "mathlib/vmatrix.h"
# include "bsptreedata.h"
# include "materialsystem/itexture.h"
# include "filesystem.h"
# include "utlbidirectionalset.h"
# include "l_studio.h"
# include "istudiorender.h"
# include "engine/ivmodelrender.h"
# include "collisionutils.h"
# include "debugoverlay.h"
# include "tier0/vprof.h"
# include "disp.h"
# include "gl_rmain.h"
# include "MaterialBuckets.h"
# include "r_decal.h"
# include "cmodel_engine.h"
# include "iclientrenderable.h"
# include "cdll_engine_int.h"
# include "sys_dll.h"
# include "render.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Shadow-related functionality exported by the engine
//
// We have two shadow-related caches in this system
// 1) A surface cache. We keep track of which surfaces the shadows can
// potentially hit. The computation of the surface cache should be
// as fast as possible
// 2) A surface vertex cache. Once we know what surfaces the shadow
// hits, we caompute the actual polygons using a clip. This is only
// useful for shadows that we know don't change too frequently, so
// we pass in a flag when making the shadow to indicate whether the
// vertex cache should be used or not. The assumption is that the client
// of this system should know whether the shadows are always changing or not
//
// The first cache is generated when the shadow is initially projected, and
// the second cache is generated when the surfaces are actually being rendered.
//
// For rendering, I assign a sort order ID to all materials used by shadow
// decals. The sort order serves the identical purpose to the material's EnumID
// but I remap those IDs so I can keep a small list of decals to render with
// that enum ID (the other option would be to allocate an array with a number
// of elements == to the number of material enumeration IDs, which is pretty large).
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// forward decarations
//-----------------------------------------------------------------------------
extern int r_surfacevisframe ;
extern IStudioRender * g_pStudioRender ;
# define BACKFACE_EPSILON 0.01f
// Max number of vertices per shadow decal
enum
{
SHADOW_VERTEX_SMALL_CACHE_COUNT = 8 ,
SHADOW_VERTEX_LARGE_CACHE_COUNT = 32 ,
SHADOW_VERTEX_TEMP_COUNT = 48 ,
MAX_CLIP_PLANE_COUNT = 4 ,
SURFACE_BOUNDS_CACHE_COUNT = 1024 ,
//=============================================================================
// HPE_BEGIN:
// [smessick] Cache size for the shadow decals. This used to be on the stack.
//=============================================================================
SHADOW_DECAL_CACHE_COUNT = 16 * 1024 ,
MAX_SHADOW_DECAL_CACHE_COUNT = 64 * 1024 ,
//=============================================================================
// HPE_END
//=============================================================================
} ;
//-----------------------------------------------------------------------------
// Used to clip the shadow decals
//-----------------------------------------------------------------------------
struct ShadowClipState_t
{
int m_CurrVert ;
int m_TempCount ;
int m_ClipCount ;
ShadowVertex_t m_pTempVertices [ SHADOW_VERTEX_TEMP_COUNT ] ;
ShadowVertex_t * RESTRICT m_ppClipVertices [ 2 ] [ SHADOW_VERTEX_TEMP_COUNT ] ;
} ;
//-----------------------------------------------------------------------------
// ConVars (must be defined before CShadowMgr is instanced!)
//-----------------------------------------------------------------------------
ConVar r_shadows ( " r_shadows " , " 1 " ) ;
ConVar r_shadows_gamecontrol ( " r_shadows_gamecontrol " , " -1 " , FCVAR_CHEAT ) ; // Shadow override controlled by game entities (shadow_controller)
static ConVar r_shadowwireframe ( " r_shadowwireframe " , " 0 " , FCVAR_CHEAT ) ;
static ConVar r_shadowids ( " r_shadowids " , " 0 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightdrawsweptbbox ( " r_flashlightdrawsweptbbox " , " 0 " ) ;
static ConVar r_flashlightdrawfrustumbbox ( " r_flashlightdrawfrustumbbox " , " 0 " ) ;
static ConVar r_flashlightnodraw ( " r_flashlightnodraw " , " 0 " ) ;
static ConVar r_flashlightupdatedepth ( " r_flashlightupdatedepth " , " 1 " ) ;
static ConVar r_flashlightdrawdepth ( " r_flashlightdrawdepth " , " 0 " ) ;
static ConVar r_flashlightrenderworld ( " r_flashlightrenderworld " , " 1 " ) ;
static ConVar r_flashlightrendermodels ( " r_flashlightrendermodels " , " 1 " ) ;
static ConVar r_flashlightrender ( " r_flashlightrender " , " 1 " ) ;
static ConVar r_flashlightculldepth ( " r_flashlightculldepth " , " 1 " ) ;
ConVar r_flashlight_version2 ( " r_flashlight_version2 " , " 0 " , FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ) ;
//-----------------------------------------------------------------------------
// Implementation of IShadowMgr
//-----------------------------------------------------------------------------
class CShadowMgr : public IShadowMgrInternal , ISpatialLeafEnumerator
{
public :
// constructor
CShadowMgr ( ) ;
// Methods inherited from IShadowMgr
virtual ShadowHandle_t CreateShadow ( IMaterial * pMaterial , IMaterial * pModelMaterial , void * pBindProxy , int creationFlags ) ;
virtual ShadowHandle_t CreateShadowEx ( IMaterial * pMaterial , IMaterial * pModelMaterial , void * pBindProxy , int creationFlags ) ;
virtual void DestroyShadow ( ShadowHandle_t handle ) ;
virtual void SetShadowMaterial ( ShadowHandle_t handle , IMaterial * pMaterial , IMaterial * pModelMaterial , void * pBindProxy ) ;
virtual void EnableShadow ( ShadowHandle_t handle , bool bEnable ) ;
virtual void ProjectFlashlight ( ShadowHandle_t handle , const VMatrix & worldToShadow , int nLeafCount , const int * pLeafList ) ;
virtual void ProjectShadow ( ShadowHandle_t handle , const Vector & origin ,
const Vector & projectionDir , const VMatrix & worldToShadow , const Vector2D & size ,
int nLeafCount , const int * pLeafList ,
float maxHeight , float falloffOffset , float falloffAmount , const Vector & vecCasterOrigin ) ;
virtual const Frustum_t & GetFlashlightFrustum ( ShadowHandle_t handle ) ;
virtual const FlashlightState_t & GetFlashlightState ( ShadowHandle_t handle ) ;
virtual int ProjectAndClipVertices ( ShadowHandle_t handle , int count ,
Vector * * ppPosition , ShadowVertex_t * * * ppOutVertex ) ;
virtual void AddShadowToBrushModel ( ShadowHandle_t handle , model_t * pModel ,
const Vector & origin , const QAngle & angles ) ;
virtual void RemoveAllShadowsFromBrushModel ( model_t * pModel ) ;
virtual void AddShadowToModel ( ShadowHandle_t shadow , ModelInstanceHandle_t handle ) ;
virtual void RemoveAllShadowsFromModel ( ModelInstanceHandle_t handle ) ;
virtual const ShadowInfo_t & GetInfo ( ShadowHandle_t handle ) ;
virtual void SetFlashlightRenderState ( ShadowHandle_t handle ) ;
// Methods inherited from IShadowMgrInternal
virtual void LevelInit ( int nSurfCount ) ;
virtual void LevelShutdown ( ) ;
virtual void AddShadowsOnSurfaceToRenderList ( ShadowDecalHandle_t decalHandle ) ;
virtual void ClearShadowRenderList ( ) ;
virtual void ComputeRenderInfo ( ShadowDecalRenderInfo_t * pInfo , ShadowHandle_t handle ) const ;
virtual void SetModelShadowState ( ModelInstanceHandle_t instance ) ;
virtual unsigned short InvalidShadowIndex ( ) ;
// Methods of ISpatialLeafEnumerator
virtual bool EnumerateLeaf ( int leaf , intp context ) ;
// Sets the texture coordinate range for a shadow...
virtual void SetShadowTexCoord ( ShadowHandle_t handle , float x , float y , float w , float h ) ;
// Set extra clip planes related to shadows...
// These are used to prevent pokethru and back-casting
virtual void ClearExtraClipPlanes ( ShadowHandle_t shadow ) ;
virtual void AddExtraClipPlane ( ShadowHandle_t shadow , const Vector & normal , float dist ) ;
// Gets the first model associated with a shadow
unsigned short & FirstModelInShadow ( ShadowHandle_t h ) { return m_Shadows [ h ] . m_FirstModel ; }
// Set the darkness falloff bias
virtual void SetFalloffBias ( ShadowHandle_t shadow , unsigned char ucBias ) ;
// Set the number of world material buckets. This should happen exactly once per level load.
virtual void SetNumWorldMaterialBuckets ( int numMaterialSortBins ) ;
// Update the state for a flashlight.
virtual void UpdateFlashlightState ( ShadowHandle_t shadowHandle , const FlashlightState_t & lightState ) ;
virtual void DrawFlashlightDecals ( int sortGroup , bool bDoMasking ) ;
virtual void DrawFlashlightDecalsOnSingleSurface ( SurfaceHandle_t surfID , bool bDoMasking ) ;
virtual void DrawFlashlightOverlays ( int sortGroup , bool bDoMasking ) ;
virtual void DrawFlashlightDepthTexture ( ) ;
virtual void SetFlashlightDepthTexture ( ShadowHandle_t shadowHandle , ITexture * pFlashlightDepthTexture , unsigned char ucShadowStencilBit ) ;
virtual void AddFlashlightRenderable ( ShadowHandle_t shadow , IClientRenderable * pRenderable ) ;
virtual void DrawFlashlightDecalsOnDisplacements ( int sortGroup , CDispInfo * * visibleDisps , int nVisibleDisps , bool bDoMasking ) ;
virtual bool ModelHasShadows ( ModelInstanceHandle_t instance ) ;
private :
enum
{
SHADOW_DISABLED = ( SHADOW_LAST_FLAG < < 1 ) ,
} ;
typedef CUtlFixedLinkedList < ShadowDecalHandle_t > : : IndexType_t ShadowSurfaceIndex_t ;
struct SurfaceBounds_t
{
fltx4 m_vecMins ;
fltx4 m_vecMaxs ;
Vector m_vecCenter ;
float m_flRadius ;
int m_nSurfaceIndex ;
} ;
struct ShadowVertexSmallList_t
{
ShadowVertex_t m_Verts [ SHADOW_VERTEX_SMALL_CACHE_COUNT ] ;
} ;
struct ShadowVertexLargeList_t
{
ShadowVertex_t m_Verts [ SHADOW_VERTEX_LARGE_CACHE_COUNT ] ;
} ;
// A cache entries' worth of vertices....
struct ShadowVertexCache_t
{
unsigned short m_Count ;
ShadowHandle_t m_Shadow ;
unsigned short m_CachedVerts ;
ShadowVertex_t * m_pVerts ;
} ;
typedef unsigned short FlashlightHandle_t ;
// Shadow state
struct Shadow_t : public ShadowInfo_t
{
Vector m_ProjectionDir ;
IMaterial * m_pMaterial ; // material for rendering surfaces
IMaterial * m_pModelMaterial ; // material for rendering models
void * m_pBindProxy ;
unsigned short m_Flags ;
unsigned short m_SortOrder ;
float m_flSphereRadius ; // Radius of sphere surrounding the shadow
Ray_t m_Ray ; // NOTE: Ray needs to be on 16-byte boundaries.
Vector m_vecSphereCenter ; // Sphere surrounding the shadow
FlashlightHandle_t m_FlashlightHandle ;
ITexture * m_pFlashlightDepthTexture ;
// Extra clip planes
unsigned short m_ClipPlaneCount ;
Vector m_ClipPlane [ MAX_CLIP_PLANE_COUNT ] ;
float m_ClipDist [ MAX_CLIP_PLANE_COUNT ] ;
// First shadow decal the shadow has
ShadowSurfaceIndex_t m_FirstDecal ;
// First model the shadow is projected onto
unsigned short m_FirstModel ;
// Stencil bit used to mask this shadow
unsigned char m_ucShadowStencilBit ;
} ;
// Each surface has one of these, they reference the main shadow
// projector and cached off shadow decals.
struct ShadowDecal_t
{
SurfaceHandle_t m_SurfID ;
ShadowSurfaceIndex_t m_ShadowListIndex ;
ShadowHandle_t m_Shadow ;
DispShadowHandle_t m_DispShadow ;
unsigned short m_ShadowVerts ;
// This is a handle of the next shadow decal to be rendered
ShadowDecalHandle_t m_NextRender ;
} ;
// This structure is used when building new shadow information
struct ShadowBuildInfo_t
{
ShadowHandle_t m_Shadow ;
Vector m_RayStart ;
Vector m_ProjectionDirection ;
Vector m_vecSphereCenter ; // Sphere surrounding the shadow
float m_flSphereRadius ; // Radius of sphere surrounding the shadow
const byte * m_pVis ; // Vis from the ray start
} ;
// This structure contains rendering information
struct ShadowRenderInfo_t
{
int m_VertexCount ;
int m_IndexCount ;
int m_nMaxVertices ;
int m_nMaxIndices ;
int m_Count ;
int * m_pCache ;
int m_DispCount ;
const VMatrix * m_pModelToWorld ;
VMatrix m_WorldToModel ;
DispShadowHandle_t * m_pDispCache ;
} ;
// Structures used to assign sort order handles
struct SortOrderInfo_t
{
int m_MaterialEnum ;
int m_RefCount ;
} ;
typedef void ( * ShadowDebugFunc_t ) ( ShadowHandle_t shadowHandle , const Vector & vecCentroid ) ;
// m_FlashlightWorldMaterialBuckets is where surfaces are stored per flashlight each frame.
typedef CUtlVector < FlashlightHandle_t > WorldMaterialBuckets_t ;
struct FlashlightInfo_t
{
FlashlightState_t m_FlashlightState ;
unsigned short m_Shadow ;
Frustum_t m_Frustum ;
CMaterialsBuckets < SurfaceHandle_t > m_MaterialBuckets ;
CMaterialsBuckets < SurfaceHandle_t > m_OccluderBuckets ;
CUtlVector < IClientRenderable * > m_Renderables ;
} ;
private :
// Applies a flashlight to all surfaces in the leaf
void ApplyFlashlightToLeaf ( const Shadow_t & shadow , mleaf_t * pLeaf , ShadowBuildInfo_t * pBuild ) ;
// Applies a shadow to all surfaces in the leaf
void ApplyShadowToLeaf ( const Shadow_t & shadow , mleaf_t * RESTRICT pLeaf , ShadowBuildInfo_t * RESTRICT pBuild ) ;
// These functions deal with creation of render sort ids
void SetMaterial ( Shadow_t & shadow , IMaterial * pMaterial , IMaterial * pModelMaterial , void * pBindProxy ) ;
void CleanupMaterial ( Shadow_t & shadow ) ;
// These functions add/remove shadow decals to surfaces
ShadowDecalHandle_t AddShadowDecalToSurface ( SurfaceHandle_t surfID , ShadowHandle_t handle ) ;
void RemoveShadowDecalFromSurface ( SurfaceHandle_t surfID , ShadowDecalHandle_t decalHandle ) ;
// Adds the surface to the list for this shadow
bool AddDecalToShadowList ( ShadowHandle_t handle , ShadowDecalHandle_t decalHandle ) ;
// Removes the shadow to the list of surfaces
void RemoveDecalFromShadowList ( ShadowHandle_t handle , ShadowDecalHandle_t decalHandle ) ;
// Actually projects + clips vertices
int ProjectAndClipVertices ( const Shadow_t & shadow , const VMatrix & worldToShadow ,
const VMatrix * pWorldToModel , int count , Vector * * ppPosition , ShadowVertex_t * * * ppOutVertex ) ;
// These functions hook/unhook shadows up to surfaces + vice versa
void AddSurfaceToShadow ( ShadowHandle_t handle , SurfaceHandle_t surfID ) ;
void RemoveSurfaceFromShadow ( ShadowHandle_t handle , SurfaceHandle_t surfID ) ;
void RemoveAllSurfacesFromShadow ( ShadowHandle_t handle ) ;
void RemoveAllShadowsFromSurface ( SurfaceHandle_t surfID ) ;
// Deals with model shadow management
void RemoveAllModelsFromShadow ( ShadowHandle_t handle ) ;
// Applies the shadow to a surface
void ApplyShadowToSurface ( ShadowBuildInfo_t & build , SurfaceHandle_t surfID ) ;
// Applies the shadow to a displacement
void ApplyShadowToDisplacement ( ShadowBuildInfo_t & build , IDispInfo * pDispInfo , bool bIsFlashlight ) ;
// Renders shadows that all share a material enumeration
void RenderShadowList ( IMatRenderContext * pRenderContext , ShadowDecalHandle_t decalHandle , const VMatrix * pModelToWorld ) ;
// Should we cache vertices?
bool ShouldCacheVertices ( const ShadowDecal_t & decal ) ;
// Generates a list displacement shadow vertices to render
bool GenerateDispShadowRenderInfo ( IMatRenderContext * pRenderContext , ShadowDecal_t & decal , ShadowRenderInfo_t & info ) ;
// Generates a list shadow vertices to render
bool GenerateNormalShadowRenderInfo ( IMatRenderContext * pRenderContext , ShadowDecal_t & decal , ShadowRenderInfo_t & info ) ;
// Adds normal shadows to the mesh builder
int AddNormalShadowsToMeshBuilder ( CMeshBuilder & meshBuilder , ShadowRenderInfo_t & info ) ;
// Adds displacement shadows to the mesh builder
int AddDisplacementShadowsToMeshBuilder ( CMeshBuilder & meshBuilder ,
ShadowRenderInfo_t & info , int baseIndex ) ;
// Does the actual work of computing shadow vertices
bool ComputeShadowVertices ( ShadowDecal_t & decal , const VMatrix * pModelToWorld , const VMatrix * pWorldToModel , ShadowVertexCache_t * pVertexCache ) ;
// Project vertices into shadow space
bool ProjectVerticesIntoShadowSpace ( const VMatrix & modelToShadow ,
float maxDist , int count , Vector * * RESTRICT ppPosition , ShadowClipState_t & clip ) ;
// Copies vertex info from the clipped vertices
void CopyClippedVertices ( int count , ShadowVertex_t * * ppSrcVert , ShadowVertex_t * pDstVert , const Vector & vToAdd ) ;
// Allocate, free vertices
ShadowVertex_t * AllocateVertices ( ShadowVertexCache_t & cache , int count ) ;
void FreeVertices ( ShadowVertexCache_t & cache ) ;
// Gets at cache entry...
ShadowVertex_t * GetCachedVerts ( const ShadowVertexCache_t & cache ) ;
// Clears out vertices in the temporary cache
void ClearTempCache ( ) ;
// Renders debugging information
void RenderDebuggingInfo ( const ShadowRenderInfo_t & info , ShadowDebugFunc_t func ) ;
// Methods for dealing with world material buckets for flashlights.
void ClearAllFlashlightMaterialBuckets ( void ) ;
void AddSurfaceToFlashlightMaterialBuckets ( ShadowHandle_t handle , SurfaceHandle_t surfID ) ;
void AllocFlashlightMaterialBuckets ( FlashlightHandle_t flashlightID ) ;
// Render all projected textures (including shadows and flashlights)
void RenderProjectedTextures ( const VMatrix * pModelToWorld ) ;
void RenderFlashlights ( bool bDoMasking , const VMatrix * pModelToWorld ) ;
void SetFlashlightStencilMasks ( bool bDoMasking ) ;
void SetStencilAndScissor ( IMatRenderContext * pRenderContext , FlashlightInfo_t & flashlightInfo , bool bUseStencil ) ;
void EnableStencilAndScissorMasking ( IMatRenderContext * pRenderContext , const FlashlightInfo_t & flashlightInfo , bool bDoMasking ) ;
void DisableStencilAndScissorMasking ( IMatRenderContext * pRenderContext ) ;
void RenderShadows ( const VMatrix * pModelToWorld ) ;
// Generates a list shadow vertices to render
void GenerateShadowRenderInfo ( IMatRenderContext * pRenderContext , ShadowDecalHandle_t decalHandle , ShadowRenderInfo_t & info ) ;
// Methods related to the surface bounds cache
void ComputeSurfaceBounds ( SurfaceBounds_t * pBounds , SurfaceHandle_t nSurfID ) ;
const SurfaceBounds_t * GetSurfaceBounds ( SurfaceHandle_t nSurfID ) ;
bool IsShadowNearSurface ( ShadowHandle_t h , SurfaceHandle_t nSurfID , const VMatrix * pModelToWorld , const VMatrix * pWorldToModel ) ;
private :
// List of all shadows (one per cast shadow)
// Align it so the Ray in the Shadow_t is aligned
CUtlLinkedList < Shadow_t , ShadowHandle_t , false , int , CUtlMemoryAligned < UtlLinkedListElem_t < Shadow_t , ShadowHandle_t > , 16 > > m_Shadows ;
// List of all shadow decals (one per surface hit by a shadow)
CUtlLinkedList < ShadowDecal_t , ShadowDecalHandle_t , true , int > m_ShadowDecals ;
// List of all shadow decals associated with a particular shadow
CUtlFixedLinkedList < ShadowDecalHandle_t > m_ShadowSurfaces ;
// List of queued decals waiting to be rendered....
CUtlVector < ShadowDecalHandle_t > m_RenderQueue ;
// Used to assign sort order handles
CUtlLinkedList < SortOrderInfo_t , unsigned short > m_SortOrderIds ;
// A cache of shadow vertex data...
CUtlLinkedList < ShadowVertexCache_t , unsigned short > m_VertexCache ;
// This is temporary, not saved off....
CUtlVector < ShadowVertexCache_t > m_TempVertexCache ;
// Vertex data
CUtlLinkedList < ShadowVertexSmallList_t , unsigned short > m_SmallVertexList ;
CUtlLinkedList < ShadowVertexLargeList_t , unsigned short > m_LargeVertexList ;
// Model-shadow association
CBidirectionalSet < ModelInstanceHandle_t , ShadowHandle_t , unsigned short > m_ShadowsOnModels ;
// Cache of information for surface bounds
typedef CUtlLinkedList < SurfaceBounds_t , unsigned short , false , int , CUtlMemoryFixed < UtlLinkedListElem_t < SurfaceBounds_t , unsigned short > , SURFACE_BOUNDS_CACHE_COUNT , 16 > > SurfaceBoundsCache_t ;
typedef SurfaceBoundsCache_t : : IndexType_t SurfaceBoundsCacheIndex_t ;
SurfaceBoundsCache_t m_SurfaceBoundsCache ;
SurfaceBoundsCacheIndex_t * m_pSurfaceBounds ;
// The number of decals we're gonna need to render
int m_DecalsToRender ;
CUtlLinkedList < FlashlightInfo_t > m_FlashlightStates ;
int m_NumWorldMaterialBuckets ;
bool m_bInitialized ;
//=============================================================================
// HPE_BEGIN:
// [smessick] These used to be dynamically allocated on the stack.
//=============================================================================
CUtlMemory < int > m_ShadowDecalCache ;
CUtlMemory < DispShadowHandle_t > m_DispShadowDecalCache ;
//=============================================================================
// HPE_END
//=============================================================================
} ;
//-----------------------------------------------------------------------------
// Singleton
//-----------------------------------------------------------------------------
static CShadowMgr s_ShadowMgr ;
IShadowMgrInternal * g_pShadowMgr = & s_ShadowMgr ;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR ( CShadowMgr , IShadowMgr ,
ENGINE_SHADOWMGR_INTERFACE_VERSION , s_ShadowMgr ) ;
//-----------------------------------------------------------------------------
// Shadows on model instances
//-----------------------------------------------------------------------------
unsigned short & FirstShadowOnModel ( ModelInstanceHandle_t h )
{
// See l_studio.cpp
return FirstShadowOnModelInstance ( h ) ;
}
unsigned short & FirstModelInShadow ( ShadowHandle_t h )
{
return s_ShadowMgr . FirstModelInShadow ( h ) ;
}
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CShadowMgr : : CShadowMgr ( )
{
m_ShadowSurfaces . SetGrowSize ( 4096 ) ;
m_ShadowDecals . SetGrowSize ( 4096 ) ;
m_ShadowsOnModels . Init ( : : FirstShadowOnModel , : : FirstModelInShadow ) ;
m_NumWorldMaterialBuckets = 0 ;
m_pSurfaceBounds = NULL ;
m_bInitialized = false ;
ClearShadowRenderList ( ) ;
//=============================================================================
// HPE_BEGIN:
// [smessick] Initialize the shadow decal caches. These used to be dynamically
// allocated on the stack, but we were getting stack overflows.
//=============================================================================
m_ShadowDecalCache . SetGrowSize ( 4096 ) ;
m_DispShadowDecalCache . SetGrowSize ( 4096 ) ;
m_ShadowDecalCache . Grow ( SHADOW_DECAL_CACHE_COUNT ) ;
m_DispShadowDecalCache . Grow ( SHADOW_DECAL_CACHE_COUNT ) ;
//=============================================================================
// HPE_END
//=============================================================================
}
//-----------------------------------------------------------------------------
// Level init, shutdown
//-----------------------------------------------------------------------------
void CShadowMgr : : LevelInit ( int nSurfCount )
{
if ( m_bInitialized )
return ;
m_bInitialized = true ;
m_pSurfaceBounds = new SurfaceBoundsCacheIndex_t [ nSurfCount ] ;
// NOTE: Need to memset to 0 if we switch to integer SurfaceBoundsCacheIndex_t here
COMPILE_TIME_ASSERT ( sizeof ( SurfaceBoundsCacheIndex_t ) = = 2 ) ;
memset ( m_pSurfaceBounds , 0xFF , nSurfCount * sizeof ( SurfaceBoundsCacheIndex_t ) ) ;
}
void CShadowMgr : : LevelShutdown ( )
{
if ( ! m_bInitialized )
return ;
if ( m_pSurfaceBounds )
{
delete [ ] m_pSurfaceBounds ;
m_pSurfaceBounds = NULL ;
}
m_SurfaceBoundsCache . RemoveAll ( ) ;
m_bInitialized = false ;
}
//-----------------------------------------------------------------------------
// Create, destroy material sort order ids...
//-----------------------------------------------------------------------------
void CShadowMgr : : SetMaterial ( Shadow_t & shadow , IMaterial * pMaterial , IMaterial * pModelMaterial , void * pBindProxy )
{
shadow . m_pMaterial = pMaterial ;
shadow . m_pModelMaterial = pModelMaterial ;
shadow . m_pBindProxy = pBindProxy ;
// We're holding onto this material
if ( pMaterial )
{
pMaterial - > IncrementReferenceCount ( ) ;
}
if ( pModelMaterial )
{
pModelMaterial - > IncrementReferenceCount ( ) ;
}
// Search the sort order handles for an enumeration id match
int materialEnum = ( intp ) pMaterial ;
for ( unsigned short i = m_SortOrderIds . Head ( ) ; i ! = m_SortOrderIds . InvalidIndex ( ) ;
i = m_SortOrderIds . Next ( i ) )
{
// Found a match, lets increment the refcount of this sort order id
if ( m_SortOrderIds [ i ] . m_MaterialEnum = = materialEnum )
{
+ + m_SortOrderIds [ i ] . m_RefCount ;
shadow . m_SortOrder = i ;
return ;
}
}
// Didn't find it, lets assign a new sort order ID, with a refcount of 1
shadow . m_SortOrder = m_SortOrderIds . AddToTail ( ) ;
m_SortOrderIds [ shadow . m_SortOrder ] . m_MaterialEnum = materialEnum ;
m_SortOrderIds [ shadow . m_SortOrder ] . m_RefCount = 1 ;
// Make sure the render queue has as many entries as the max sort order id.
int count = m_RenderQueue . Count ( ) ;
while ( count < m_SortOrderIds . MaxElementIndex ( ) )
{
MEM_ALLOC_CREDIT ( ) ;
m_RenderQueue . AddToTail ( SHADOW_DECAL_HANDLE_INVALID ) ;
+ + count ;
}
}
void CShadowMgr : : CleanupMaterial ( Shadow_t & shadow )
{
// Decrease the sort order reference count
if ( - - m_SortOrderIds [ shadow . m_SortOrder ] . m_RefCount < = 0 )
{
// No one referencing the sort order number?
// Then lets clean up the sort order id
m_SortOrderIds . Remove ( shadow . m_SortOrder ) ;
}
// We're done with this material
if ( shadow . m_pMaterial )
{
shadow . m_pMaterial - > DecrementReferenceCount ( ) ;
}
if ( shadow . m_pModelMaterial )
{
shadow . m_pModelMaterial - > DecrementReferenceCount ( ) ;
}
}
//-----------------------------------------------------------------------------
// For the model shadow list
//-----------------------------------------------------------------------------
unsigned short CShadowMgr : : InvalidShadowIndex ( )
{
return m_ShadowsOnModels . InvalidIndex ( ) ;
}
//-----------------------------------------------------------------------------
// Create, destroy shadows
//-----------------------------------------------------------------------------
ShadowHandle_t CShadowMgr : : CreateShadow ( IMaterial * pMaterial , IMaterial * pModelMaterial , void * pBindProxy , int creationFlags )
{
return CreateShadowEx ( pMaterial , pModelMaterial , pBindProxy , creationFlags ) ;
}
ShadowHandle_t CShadowMgr : : CreateShadowEx ( IMaterial * pMaterial , IMaterial * pModelMaterial , void * pBindProxy , int creationFlags )
{
# ifndef SWDS
ShadowHandle_t h = m_Shadows . AddToTail ( ) ;
//=============================================================================
// HPE_BEGIN:
// [smessick] Check for overflow.
//=============================================================================
if ( h = = m_Shadows . InvalidIndex ( ) )
{
ExecuteNTimes ( 10 , Warning ( " CShadowMgr::CreateShadowEx - overflowed m_Shadows linked list! \n " ) ) ;
return h ;
}
//=============================================================================
// HPE_END
//=============================================================================
Shadow_t & shadow = m_Shadows [ h ] ;
SetMaterial ( shadow , pMaterial , pModelMaterial , pBindProxy ) ;
shadow . m_Flags = creationFlags ;
shadow . m_FirstDecal = m_ShadowSurfaces . InvalidIndex ( ) ;
shadow . m_FirstModel = m_ShadowsOnModels . InvalidIndex ( ) ;
shadow . m_ProjectionDir . Init ( 0 , 0 , 1 ) ;
shadow . m_TexOrigin . Init ( 0 , 0 ) ;
shadow . m_TexSize . Init ( 1 , 1 ) ;
shadow . m_ClipPlaneCount = 0 ;
shadow . m_FalloffBias = 0 ;
shadow . m_pFlashlightDepthTexture = NULL ;
shadow . m_FlashlightHandle = m_FlashlightStates . InvalidIndex ( ) ;
if ( ( creationFlags & SHADOW_FLASHLIGHT ) ! = 0 )
{
shadow . m_FlashlightHandle = m_FlashlightStates . AddToTail ( ) ;
m_FlashlightStates [ shadow . m_FlashlightHandle ] . m_Shadow = h ;
if ( ! IsX360 ( ) & & ! r_flashlight_version2 . GetInt ( ) )
{
AllocFlashlightMaterialBuckets ( shadow . m_FlashlightHandle ) ;
}
}
MatrixSetIdentity ( shadow . m_WorldToShadow ) ;
return h ;
# endif
}
void CShadowMgr : : DestroyShadow ( ShadowHandle_t handle )
{
CleanupMaterial ( m_Shadows [ handle ] ) ;
RemoveAllSurfacesFromShadow ( handle ) ;
RemoveAllModelsFromShadow ( handle ) ;
if ( m_Shadows [ handle ] . m_FlashlightHandle ! = m_FlashlightStates . InvalidIndex ( ) )
{
m_FlashlightStates . Remove ( m_Shadows [ handle ] . m_FlashlightHandle ) ;
}
m_Shadows . Remove ( handle ) ;
}
//-----------------------------------------------------------------------------
// Resets the shadow material (useful for shadow LOD.. doing blobby at distance)
//-----------------------------------------------------------------------------
void CShadowMgr : : SetShadowMaterial ( ShadowHandle_t handle , IMaterial * pMaterial , IMaterial * pModelMaterial , void * pBindProxy )
{
Shadow_t & shadow = m_Shadows [ handle ] ;
if ( ( shadow . m_pMaterial ! = pMaterial ) | | ( shadow . m_pModelMaterial ! = pModelMaterial ) | | ( shadow . m_pBindProxy ! = pBindProxy ) )
{
CleanupMaterial ( shadow ) ;
SetMaterial ( shadow , pMaterial , pModelMaterial , pBindProxy ) ;
}
}
//-----------------------------------------------------------------------------
// Sets the texture coordinate range for a shadow...
//-----------------------------------------------------------------------------
void CShadowMgr : : SetShadowTexCoord ( ShadowHandle_t handle , float x , float y , float w , float h )
{
Shadow_t & shadow = m_Shadows [ handle ] ;
shadow . m_TexOrigin . Init ( x , y ) ;
shadow . m_TexSize . Init ( w , h ) ;
}
//-----------------------------------------------------------------------------
// Set extra clip planes related to shadows...
//-----------------------------------------------------------------------------
void CShadowMgr : : ClearExtraClipPlanes ( ShadowHandle_t h )
{
m_Shadows [ h ] . m_ClipPlaneCount = 0 ;
}
void CShadowMgr : : AddExtraClipPlane ( ShadowHandle_t h , const Vector & normal , float dist )
{
Shadow_t & shadow = m_Shadows [ h ] ;
Assert ( shadow . m_ClipPlaneCount < MAX_CLIP_PLANE_COUNT ) ;
VectorCopy ( normal , shadow . m_ClipPlane [ shadow . m_ClipPlaneCount ] ) ;
shadow . m_ClipDist [ shadow . m_ClipPlaneCount ] = dist ;
+ + shadow . m_ClipPlaneCount ;
}
//-----------------------------------------------------------------------------
// Gets at information about a particular shadow
//-----------------------------------------------------------------------------
const ShadowInfo_t & CShadowMgr : : GetInfo ( ShadowHandle_t handle )
{
return m_Shadows [ handle ] ;
}
//-----------------------------------------------------------------------------
// Gets at cache entry...
//-----------------------------------------------------------------------------
ShadowVertex_t * CShadowMgr : : GetCachedVerts ( const ShadowVertexCache_t & cache )
{
if ( cache . m_Count = = 0 )
return 0 ;
if ( cache . m_pVerts )
return cache . m_pVerts ;
if ( cache . m_Count < = SHADOW_VERTEX_SMALL_CACHE_COUNT )
return m_SmallVertexList [ cache . m_CachedVerts ] . m_Verts ;
return m_LargeVertexList [ cache . m_CachedVerts ] . m_Verts ;
}
//-----------------------------------------------------------------------------
// Allocates, cleans up vertex cache vertices
//-----------------------------------------------------------------------------
inline ShadowVertex_t * CShadowMgr : : AllocateVertices ( ShadowVertexCache_t & cache , int count )
{
cache . m_pVerts = 0 ;
if ( count < = SHADOW_VERTEX_SMALL_CACHE_COUNT )
{
cache . m_Count = count ;
cache . m_CachedVerts = m_SmallVertexList . AddToTail ( ) ;
return m_SmallVertexList [ cache . m_CachedVerts ] . m_Verts ;
}
else if ( count < = SHADOW_VERTEX_LARGE_CACHE_COUNT )
{
cache . m_Count = count ;
cache . m_CachedVerts = m_LargeVertexList . AddToTail ( ) ;
return m_LargeVertexList [ cache . m_CachedVerts ] . m_Verts ;
}
cache . m_Count = count ;
if ( count > 0 )
{
cache . m_pVerts = new ShadowVertex_t [ count ] ;
}
cache . m_CachedVerts = m_LargeVertexList . InvalidIndex ( ) ;
return cache . m_pVerts ;
}
inline void CShadowMgr : : FreeVertices ( ShadowVertexCache_t & cache )
{
if ( cache . m_Count = = 0 )
return ;
if ( cache . m_pVerts )
{
delete [ ] cache . m_pVerts ;
}
else if ( cache . m_Count < = SHADOW_VERTEX_SMALL_CACHE_COUNT )
{
m_SmallVertexList . Remove ( cache . m_CachedVerts ) ;
}
else
{
m_LargeVertexList . Remove ( cache . m_CachedVerts ) ;
}
}
//-----------------------------------------------------------------------------
// Clears out vertices in the temporary cache
//-----------------------------------------------------------------------------
void CShadowMgr : : ClearTempCache ( )
{
// Clear out the vertices
for ( int i = m_TempVertexCache . Count ( ) ; - - i > = 0 ; )
{
FreeVertices ( m_TempVertexCache [ i ] ) ;
}
m_TempVertexCache . RemoveAll ( ) ;
}
//-----------------------------------------------------------------------------
// Adds the surface to the list for this shadow
//-----------------------------------------------------------------------------
bool CShadowMgr : : AddDecalToShadowList ( ShadowHandle_t handle , ShadowDecalHandle_t decalHandle )
{
// Add the shadow to the list of surfaces affected by this shadow
ShadowSurfaceIndex_t idx = m_ShadowSurfaces . Alloc ( true ) ;
if ( idx = = m_ShadowSurfaces . InvalidIndex ( ) )
{
ExecuteNTimes ( 10 , Warning ( " CShadowMgr::AddDecalToShadowList - overflowed m_ShadowSurfaces linked list! \n " ) ) ;
return false ;
}
m_ShadowSurfaces [ idx ] = decalHandle ;
if ( m_Shadows [ handle ] . m_FirstDecal ! = m_ShadowSurfaces . InvalidIndex ( ) )
{
m_ShadowSurfaces . LinkBefore ( m_Shadows [ handle ] . m_FirstDecal , idx ) ;
}
m_Shadows [ handle ] . m_FirstDecal = idx ;
m_ShadowDecals [ decalHandle ] . m_ShadowListIndex = idx ;
return true ;
}
//-----------------------------------------------------------------------------
// Removes the shadow to the list of surfaces
//-----------------------------------------------------------------------------
void CShadowMgr : : RemoveDecalFromShadowList ( ShadowHandle_t handle , ShadowDecalHandle_t decalHandle )
{
ShadowSurfaceIndex_t idx = m_ShadowDecals [ decalHandle ] . m_ShadowListIndex ;
// Make sure the list of shadow decals for a single shadow is ok
if ( m_Shadows [ handle ] . m_FirstDecal = = idx )
{
m_Shadows [ handle ] . m_FirstDecal = m_ShadowSurfaces . Next ( idx ) ;
}
// Remove it from the shadow surfaces list
m_ShadowSurfaces . Free ( idx ) ;
// Blat out the decal index
m_ShadowDecals [ decalHandle ] . m_ShadowListIndex = m_ShadowSurfaces . InvalidIndex ( ) ;
}
//-----------------------------------------------------------------------------
// Computes spherical bounds for a surface
//-----------------------------------------------------------------------------
void CShadowMgr : : ComputeSurfaceBounds ( SurfaceBounds_t * pBounds , SurfaceHandle_t nSurfID )
{
pBounds - > m_vecCenter . Init ( ) ;
pBounds - > m_vecMins = ReplicateX4 ( FLT_MAX ) ;
pBounds - > m_vecMaxs = ReplicateX4 ( - FLT_MAX ) ;
int nCount = MSurf_VertCount ( nSurfID ) ;
for ( int i = 0 ; i < nCount ; + + i )
{
int nVertIndex = host_state . worldbrush - > vertindices [ MSurf_FirstVertIndex ( nSurfID ) + i ] ;
const Vector & position = host_state . worldbrush - > vertexes [ nVertIndex ] . position ;
pBounds - > m_vecCenter + = position ;
fltx4 pos4 = LoadUnaligned3SIMD ( position . Base ( ) ) ;
pBounds - > m_vecMins = MinSIMD ( pos4 , pBounds - > m_vecMins ) ;
pBounds - > m_vecMaxs = MaxSIMD ( pos4 , pBounds - > m_vecMaxs ) ;
}
fltx4 eps = ReplicateX4 ( 1e-3 ) ;
pBounds - > m_vecMins = SetWToZeroSIMD ( SubSIMD ( pBounds - > m_vecMins , eps ) ) ;
pBounds - > m_vecMaxs = SetWToZeroSIMD ( AddSIMD ( pBounds - > m_vecMaxs , eps ) ) ;
pBounds - > m_vecCenter / = nCount ;
pBounds - > m_flRadius = 0.0f ;
for ( int i = 0 ; i < nCount ; + + i )
{
int nVertIndex = host_state . worldbrush - > vertindices [ MSurf_FirstVertIndex ( nSurfID ) + i ] ;
const Vector & position = host_state . worldbrush - > vertexes [ nVertIndex ] . position ;
float flDistSq = position . DistToSqr ( pBounds - > m_vecCenter ) ;
if ( flDistSq > pBounds - > m_flRadius )
{
pBounds - > m_flRadius = flDistSq ;
}
}
pBounds - > m_flRadius = sqrt ( pBounds - > m_flRadius ) ;
}
//-----------------------------------------------------------------------------
// Get spherical bounds for a surface
//-----------------------------------------------------------------------------
const CShadowMgr : : SurfaceBounds_t * CShadowMgr : : GetSurfaceBounds ( SurfaceHandle_t surfID )
{
int nSurfaceIndex = MSurf_Index ( surfID ) ;
// NOTE: We're not bumping the surface index to the front of the LRU
// here, but I think if we did the cost doing that would exceed the cost
// of anything else in this path.
// If this turns out to not be true, then we should make this a true LRU
if ( m_pSurfaceBounds [ nSurfaceIndex ] ! = m_SurfaceBoundsCache . InvalidIndex ( ) )
return & m_SurfaceBoundsCache [ m_pSurfaceBounds [ nSurfaceIndex ] ] ;
SurfaceBoundsCacheIndex_t nIndex ;
if ( m_SurfaceBoundsCache . Count ( ) > = SURFACE_BOUNDS_CACHE_COUNT )
{
// Retire existing cache entry if we're out of space,
// move it to the head of the LRU cache
nIndex = m_SurfaceBoundsCache . Tail ( ) ;
m_SurfaceBoundsCache . Unlink ( nIndex ) ;
m_SurfaceBoundsCache . LinkToHead ( nIndex ) ;
m_pSurfaceBounds [ m_SurfaceBoundsCache [ nIndex ] . m_nSurfaceIndex ] = m_SurfaceBoundsCache . InvalidIndex ( ) ;
}
else
{
// Allocate new cache entry if we have more room
nIndex = m_SurfaceBoundsCache . AddToHead ( ) ;
}
m_pSurfaceBounds [ nSurfaceIndex ] = nIndex ;
// Computes the surface bounds
SurfaceBounds_t & bounds = m_SurfaceBoundsCache [ nIndex ] ;
bounds . m_nSurfaceIndex = nSurfaceIndex ;
ComputeSurfaceBounds ( & bounds , surfID ) ;
return & bounds ;
}
//-----------------------------------------------------------------------------
// Is the shadow near the surface?
//-----------------------------------------------------------------------------
bool CShadowMgr : : IsShadowNearSurface ( ShadowHandle_t h , SurfaceHandle_t nSurfID ,
const VMatrix * pModelToWorld , const VMatrix * pWorldToModel )
{
const Shadow_t & shadow = m_Shadows [ h ] ;
const SurfaceBounds_t * pBounds = GetSurfaceBounds ( nSurfID ) ;
Vector vecSurfCenter ;
if ( ! pModelToWorld )
{
vecSurfCenter = pBounds - > m_vecCenter ;
}
else
{
Vector3DMultiplyPosition ( * pModelToWorld , pBounds - > m_vecCenter , vecSurfCenter ) ;
}
// Sphere check
Vector vecDelta ;
VectorSubtract ( shadow . m_vecSphereCenter , vecSurfCenter , vecDelta ) ;
float flDistSqr = vecDelta . LengthSqr ( ) ;
float flMinDistSqr = pBounds - > m_flRadius + shadow . m_flSphereRadius ;
flMinDistSqr * = flMinDistSqr ;
if ( flDistSqr > = flMinDistSqr )
return false ;
if ( ! pModelToWorld )
return IsBoxIntersectingRay ( pBounds - > m_vecMins , pBounds - > m_vecMaxs , shadow . m_Ray ) ;
Ray_t transformedRay ;
Vector3DMultiplyPosition ( * pWorldToModel , shadow . m_Ray . m_Start , transformedRay . m_Start ) ;
Vector3DMultiply ( * pWorldToModel , shadow . m_Ray . m_Delta , transformedRay . m_Delta ) ;
transformedRay . m_StartOffset = shadow . m_Ray . m_StartOffset ;
transformedRay . m_Extents = shadow . m_Ray . m_Extents ;
transformedRay . m_IsRay = shadow . m_Ray . m_IsRay ;
transformedRay . m_IsSwept = shadow . m_Ray . m_IsSwept ;
return IsBoxIntersectingRay ( pBounds - > m_vecMins , pBounds - > m_vecMaxs , transformedRay ) ;
}
//-----------------------------------------------------------------------------
// Adds the shadow decal reference to the surface
//-----------------------------------------------------------------------------
inline ShadowDecalHandle_t CShadowMgr : : AddShadowDecalToSurface ( SurfaceHandle_t surfID , ShadowHandle_t handle )
{
ShadowDecalHandle_t decalHandle = m_ShadowDecals . Alloc ( true ) ;
if ( decalHandle = = m_ShadowDecals . InvalidIndex ( ) )
{
ExecuteNTimes ( 10 , Warning ( " CShadowMgr::AddShadowDecalToSurface - overflowed m_ShadowDecals linked list! \n " ) ) ;
return decalHandle ;
}
ShadowDecal_t & decal = m_ShadowDecals [ decalHandle ] ;
decal . m_SurfID = surfID ;
m_ShadowDecals . LinkBefore ( MSurf_ShadowDecals ( surfID ) , decalHandle ) ;
MSurf_ShadowDecals ( surfID ) = decalHandle ;
// Hook the shadow into the displacement system....
if ( ! SurfaceHasDispInfo ( surfID ) )
{
decal . m_DispShadow = DISP_SHADOW_HANDLE_INVALID ;
}
else
{
decal . m_DispShadow = MSurf_DispInfo ( surfID ) - > AddShadowDecal ( handle ) ;
}
decal . m_Shadow = handle ;
decal . m_ShadowVerts = m_VertexCache . InvalidIndex ( ) ;
decal . m_NextRender = SHADOW_DECAL_HANDLE_INVALID ;
decal . m_ShadowListIndex = m_ShadowSurfaces . InvalidIndex ( ) ;
//=============================================================================
// HPE_BEGIN:
// [smessick] Check the return value of AddDecalToShadowList and make sure
// to delete the newly created shadow decal if there is a failure.
//=============================================================================
if ( ! AddDecalToShadowList ( handle , decalHandle ) )
{
m_ShadowDecals . Free ( decalHandle ) ;
decalHandle = m_ShadowDecals . InvalidIndex ( ) ;
}
//=============================================================================
// HPE_END
//=============================================================================
return decalHandle ;
}
inline void CShadowMgr : : RemoveShadowDecalFromSurface ( SurfaceHandle_t surfID , ShadowDecalHandle_t decalHandle )
{
// Clean up its shadow verts if it has any
ShadowDecal_t & decal = m_ShadowDecals [ decalHandle ] ;
if ( decal . m_ShadowVerts ! = m_VertexCache . InvalidIndex ( ) )
{
FreeVertices ( m_VertexCache [ decal . m_ShadowVerts ] ) ;
m_VertexCache . Remove ( decal . m_ShadowVerts ) ;
decal . m_ShadowVerts = m_VertexCache . InvalidIndex ( ) ;
}
// Clean up displacement...
if ( decal . m_DispShadow ! = DISP_SHADOW_HANDLE_INVALID )
{
MSurf_DispInfo ( decal . m_SurfID ) - > RemoveShadowDecal ( decal . m_DispShadow ) ;
}
// Make sure the list of shadow decals on a surface is set up correctly
if ( MSurf_ShadowDecals ( surfID ) = = decalHandle )
{
MSurf_ShadowDecals ( surfID ) = m_ShadowDecals . Next ( decalHandle ) ;
}
RemoveDecalFromShadowList ( decal . m_Shadow , decalHandle ) ;
// Kill the shadow decal
m_ShadowDecals . Free ( decalHandle ) ;
}
void CShadowMgr : : AddSurfaceToFlashlightMaterialBuckets ( ShadowHandle_t handle , SurfaceHandle_t surfID )
{
// Make sure that this is a flashlight.
Assert ( m_Shadows [ handle ] . m_Flags & SHADOW_FLASHLIGHT ) ;
// Get the flashlight id for this particular shadow handle and make sure that it's valid.
FlashlightHandle_t flashlightID = m_Shadows [ handle ] . m_FlashlightHandle ;
Assert ( flashlightID ! = m_FlashlightStates . InvalidIndex ( ) ) ;
m_FlashlightStates [ flashlightID ] . m_MaterialBuckets . AddElement ( MSurf_MaterialSortID ( surfID ) , surfID ) ;
}
//-----------------------------------------------------------------------------
// Adds the shadow decal reference to the surface
// This causes a shadow decal to be made
//-----------------------------------------------------------------------------
void CShadowMgr : : AddSurfaceToShadow ( ShadowHandle_t handle , SurfaceHandle_t surfID )
{
// FIXME: We could make this work, but there's a perf cost...
// Basically, we'd need to have a separate rendering batch for
// each translucent material the shadow is projected onto. The
// material alpha would have to be taken into account, so that
// no multiplication occurs where the alpha == 0
// FLASHLIGHTFIXME: get rid of some of these checks for the ones that will work just fine with the flashlight.
bool bIsFlashlight = ( ( m_Shadows [ handle ] . m_Flags & SHADOW_FLASHLIGHT ) ! = 0 ) ;
if ( ! bIsFlashlight & & MSurf_Flags ( surfID ) & ( SURFDRAW_TRANS | SURFDRAW_ALPHATEST | SURFDRAW_NOSHADOWS ) )
return ;
# ifdef _XBOX
// Don't let the flashlight get on water on XBox
if ( bIsFlashlight & & ( MSurf_Flags ( surfID ) & SURFDRAW_WATERSURFACE ) )
return ;
# endif
#if 0
// Make sure the surface has the shadow on it exactly once...
ShadowDecalHandle_t dh = MSurf_ShadowDecals ( surfID ) ;
while ( dh ! = m_ShadowDecals . InvalidIndex ( ) )
{
Assert ( m_ShadowDecals [ dh ] . m_Shadow ! = handle ) ;
dh = m_ShadowDecals . Next ( dh ) ;
}
# endif
// Create a shadow decal for this surface and add it to the surface
AddShadowDecalToSurface ( surfID , handle ) ;
}
void CShadowMgr : : RemoveSurfaceFromShadow ( ShadowHandle_t handle , SurfaceHandle_t surfID )
{
// Find the decal associated with the handle that lies on the surface
// FIXME: Linear search; bleah.
// Luckily the search is probably over only a couple items at most
// Linear searching over the shadow surfaces so we can remove the entry
// in the shadow surface list if we find a match
ASSERT_SURF_VALID ( surfID ) ;
ShadowSurfaceIndex_t i = m_Shadows [ handle ] . m_FirstDecal ;
while ( i ! = m_ShadowSurfaces . InvalidIndex ( ) )
{
ShadowDecalHandle_t decalHandle = m_ShadowSurfaces [ i ] ;
if ( m_ShadowDecals [ decalHandle ] . m_SurfID = = surfID )
{
// Found a match! There should be at most one shadow decal
// associated with a particular shadow per surface
RemoveShadowDecalFromSurface ( surfID , decalHandle ) ;
// FIXME: Could check the shadow doesn't appear again in the list
return ;
}
i = m_ShadowSurfaces . Next ( i ) ;
}
# ifdef _DEBUG
// Here, the shadow didn't have the surface in its list
// let's make sure the surface doesn't think it's got the shadow in its list
ShadowDecalHandle_t dh = MSurf_ShadowDecals ( surfID ) ;
while ( dh ! = m_ShadowDecals . InvalidIndex ( ) )
{
Assert ( m_ShadowDecals [ dh ] . m_Shadow ! = handle ) ;
dh = m_ShadowDecals . Next ( dh ) ;
}
# endif
}
void CShadowMgr : : RemoveAllSurfacesFromShadow ( ShadowHandle_t handle )
{
// Iterate over all the decals associated with a particular shadow
// Remove the decals from the surfaces they are associated with
ShadowSurfaceIndex_t i = m_Shadows [ handle ] . m_FirstDecal ;
ShadowSurfaceIndex_t next ;
while ( i ! = m_ShadowSurfaces . InvalidIndex ( ) )
{
ShadowDecalHandle_t decalHandle = m_ShadowSurfaces [ i ] ;
next = m_ShadowSurfaces . Next ( i ) ;
RemoveShadowDecalFromSurface ( m_ShadowDecals [ decalHandle ] . m_SurfID , decalHandle ) ;
i = next ;
}
m_Shadows [ handle ] . m_FirstDecal = m_ShadowSurfaces . InvalidIndex ( ) ;
}
void CShadowMgr : : RemoveAllShadowsFromSurface ( SurfaceHandle_t surfID )
{
// Iterate over all the decals associated with a particular shadow
// Remove the decals from the surfaces they are associated with
ShadowDecalHandle_t dh = MSurf_ShadowDecals ( surfID ) ;
while ( dh ! = m_ShadowDecals . InvalidIndex ( ) )
{
// Remove this shadow from the surface
ShadowDecalHandle_t next = m_ShadowDecals . Next ( dh ) ;
// Remove the surface from the shadow
RemoveShadowDecalFromSurface ( m_ShadowDecals [ dh ] . m_SurfID , dh ) ;
dh = next ;
}
MSurf_ShadowDecals ( surfID ) = m_ShadowDecals . InvalidIndex ( ) ;
}
//-----------------------------------------------------------------------------
// Shadow/model association
//-----------------------------------------------------------------------------
void CShadowMgr : : AddShadowToModel ( ShadowHandle_t handle , ModelInstanceHandle_t model )
{
// FIXME: Add culling here based on the model bbox
// and the shadow bbox
// FIXME:
/*
// Trivial bbox reject.
Vector bbMin , bbMax ;
pDisp - > GetBoundingBox ( bbMin , bbMax ) ;
if ( decalinfo - > m_Position . x - decalinfo - > m_Size < bbMax . x & & decalinfo - > m_Position . x + decalinfo - > m_Size > bbMin . x & &
decalinfo - > m_Position . y - decalinfo - > m_Size < bbMax . y & & decalinfo - > m_Position . y + decalinfo - > m_Size > bbMin . y & &
decalinfo - > m_Position . z - decalinfo - > m_Size < bbMax . z & & decalinfo - > m_Position . z + decalinfo - > m_Size > bbMin . z )
*/
if ( model = = MODEL_INSTANCE_INVALID )
{
// async data not loaded yet
return ;
}
if ( r_flashlightrender . GetBool ( ) = = false )
return ;
m_ShadowsOnModels . AddElementToBucket ( model , handle ) ;
}
void CShadowMgr : : RemoveAllShadowsFromModel ( ModelInstanceHandle_t model )
{
if ( model ! = MODEL_INSTANCE_INVALID )
{
m_ShadowsOnModels . RemoveBucket ( model ) ;
FOR_EACH_LL ( m_FlashlightStates , i )
{
FlashlightInfo_t & info = m_FlashlightStates [ i ] ;
for ( int j = 0 ; j < info . m_Renderables . Count ( ) ; j + + )
{
if ( info . m_Renderables [ j ] - > GetModelInstance ( ) = = model )
{
info . m_Renderables . Remove ( j ) ;
break ;
}
}
}
}
}
void CShadowMgr : : RemoveAllModelsFromShadow ( ShadowHandle_t handle )
{
m_ShadowsOnModels . RemoveElement ( handle ) ;
FOR_EACH_LL ( m_FlashlightStates , i )
{
FlashlightInfo_t & info = m_FlashlightStates [ i ] ;
if ( info . m_Shadow = = handle )
{
info . m_Renderables . RemoveAll ( ) ;
}
}
}
//-----------------------------------------------------------------------------
// Shadow state...
//-----------------------------------------------------------------------------
void CShadowMgr : : SetModelShadowState ( ModelInstanceHandle_t instance )
{
# ifndef SWDS
g_pStudioRender - > ClearAllShadows ( ) ;
if ( instance ! = MODEL_INSTANCE_INVALID & & r_shadows . GetInt ( ) )
{
bool bWireframe = r_shadowwireframe . GetBool ( ) ;
unsigned short i = m_ShadowsOnModels . FirstElement ( instance ) ;
while ( i ! = m_ShadowsOnModels . InvalidIndex ( ) )
{
Shadow_t & shadow = m_Shadows [ m_ShadowsOnModels . Element ( i ) ] ;
if ( ! bWireframe )
{
if ( shadow . m_Flags & SHADOW_FLASHLIGHT )
{
// NULL means that the models material should be used.
// This is what we want in the case of the flashlight
// since we need to render the models material again with different lighting.
// Need to add something here to specify which flashlight.
g_pStudioRender - > AddShadow ( NULL , NULL , & m_FlashlightStates [ shadow . m_FlashlightHandle ] . m_FlashlightState , & shadow . m_WorldToShadow , shadow . m_pFlashlightDepthTexture ) ;
}
else if ( r_shadows_gamecontrol . GetInt ( ) ! = 0 )
{
g_pStudioRender - > AddShadow ( shadow . m_pModelMaterial , shadow . m_pBindProxy ) ;
}
}
else if ( ( shadow . m_Flags & SHADOW_FLASHLIGHT ) | | r_shadows_gamecontrol . GetInt ( ) ! = 0 )
{
g_pStudioRender - > AddShadow ( g_pMaterialMRMWireframe , NULL ) ;
}
i = m_ShadowsOnModels . NextElement ( i ) ;
}
}
# endif
}
bool CShadowMgr : : ModelHasShadows ( ModelInstanceHandle_t instance )
{
if ( instance ! = MODEL_INSTANCE_INVALID )
{
if ( m_ShadowsOnModels . FirstElement ( instance ) ! = m_ShadowsOnModels . InvalidIndex ( ) )
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Applies the shadow to a surface
//-----------------------------------------------------------------------------
void CShadowMgr : : ApplyShadowToSurface ( ShadowBuildInfo_t & build , SurfaceHandle_t surfID )
{
// We've found a potential surface to add to the shadow
// At this point, we want to do fast culling to see whether we actually
// should apply the shadow or not before actually adding it to any lists
// FIXME: implement
// Put the texture extents into shadow space; see if there's an intersection
// If not, we can early out
// To do this, we're gonna want to project the surface into the space of the decal
// Therefore, we want to produce a surface->world transformation, and a
// world->shadow/light space transformation
// Then we transform the surface points into shadow space and apply the projection
// in shadow space.
/*
// Get the texture associated with this surface
mtexinfo_t * tex = pSurface - > texinfo ;
Vector4D & textureU = tex - > textureVecsTexelsPerWorldUnits [ 0 ] ;
Vector4D & textureV = tex - > textureVecsTexelsPerWorldUnits [ 1 ] ;
// project decal center into the texture space of the surface
float s = DotProduct ( decalinfo - > m_Position , textureU . AsVector3D ( ) ) +
textureU . w - surf - > textureMins [ 0 ] ;
float t = DotProduct ( decalinfo - > m_Position , textureV . AsVector3D ( ) ) +
textureV . w - surf - > textureMins [ 1 ] ;
*/
// Don't do any more computation at the moment, only do it if
// we end up rendering the surface later on
AddSurfaceToShadow ( build . m_Shadow , surfID ) ;
}
//-----------------------------------------------------------------------------
// Applies the shadow to a displacement
//-----------------------------------------------------------------------------
void CShadowMgr : : ApplyShadowToDisplacement ( ShadowBuildInfo_t & build , IDispInfo * pDispInfo , bool bIsFlashlight )
{
// Avoid noshadow displacements
if ( ! bIsFlashlight & & ( MSurf_Flags ( pDispInfo - > GetParent ( ) ) & SURFDRAW_NOSHADOWS ) )
return ;
// Trivial bbox reject.
Vector bbMin , bbMax ;
pDispInfo - > GetBoundingBox ( bbMin , bbMax ) ;
if ( ! bIsFlashlight )
{
if ( ! IsBoxIntersectingSphere ( bbMin , bbMax , build . m_vecSphereCenter , build . m_flSphereRadius ) )
return ;
}
else
{
if ( R_CullBox ( bbMin , bbMax , GetFlashlightFrustum ( build . m_Shadow ) ) )
return ;
}
SurfaceHandle_t surfID = pDispInfo - > GetParent ( ) ;
if ( surfID - > m_bDynamicShadowsEnabled = = false & & ! bIsFlashlight )
return ;
AddSurfaceToShadow ( build . m_Shadow , surfID ) ;
}
//-----------------------------------------------------------------------------
// Allows us to disable particular shadows
//-----------------------------------------------------------------------------
void CShadowMgr : : EnableShadow ( ShadowHandle_t handle , bool bEnable )
{
if ( ! bEnable )
{
// We need to remove the shadow from all surfaces it may currently be in
RemoveAllSurfacesFromShadow ( handle ) ;
RemoveAllModelsFromShadow ( handle ) ;
m_Shadows [ handle ] . m_Flags | = SHADOW_DISABLED ;
}
else
{
// FIXME: Could make this recompute the cache...
m_Shadows [ handle ] . m_Flags & = ~ SHADOW_DISABLED ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Set the darkness falloff bias
// Input : shadow -
// ucBias -
//-----------------------------------------------------------------------------
void CShadowMgr : : SetFalloffBias ( ShadowHandle_t shadow , unsigned char ucBias )
{
m_Shadows [ shadow ] . m_FalloffBias = ucBias ;
}
//-----------------------------------------------------------------------------
// Recursive routine to find surface to apply a decal to. World coordinates of
// the decal are passed in r_recalpos like the rest of the engine. This should
// be called through R_DecalShoot()
//-----------------------------------------------------------------------------
void CShadowMgr : : ProjectShadow ( ShadowHandle_t handle , const Vector & origin ,
const Vector & projectionDir , const VMatrix & worldToShadow , const Vector2D & size ,
int nLeafCount , const int * pLeafList ,
float maxHeight , float falloffOffset , float falloffAmount , const Vector & vecCasterOrigin )
{
VPROF_BUDGET ( " CShadowMgr::ProjectShadow " , VPROF_BUDGETGROUP_SHADOW_RENDERING ) ;
// First, we need to remove the shadow from all surfaces it may
// currently be in; in other words we're invalidating the shadow surface cache
RemoveAllSurfacesFromShadow ( handle ) ;
RemoveAllModelsFromShadow ( handle ) ;
// Don't bother with this shadow if it's disabled
Shadow_t & shadow = m_Shadows [ handle ] ;
if ( shadow . m_Flags & SHADOW_DISABLED )
return ;
// Don't compute the surface cache if shadows are off..
if ( ! r_shadows . GetInt ( ) )
return ;
// Set the falloff coefficient
shadow . m_FalloffOffset = falloffOffset ;
VectorCopy ( projectionDir , shadow . m_ProjectionDir ) ;
// We need to know about surfaces in leaves hit by the ray...
// We'd like to stop iterating as soon as the entire swept volume
// enters a solid leaf; that may be hard to determine. Instead,
// we should stop iterating when the ray center enters a solid leaf?
AssertFloatEquals ( projectionDir . LengthSqr ( ) , 1.0f , 1e-3 ) ;
// The maximum ray distance is equal to the distance it takes the
// falloff to get to 15%.
shadow . m_MaxDist = maxHeight ; //sqrt( coeff / 0.10f ) + falloffOffset;
shadow . m_FalloffAmount = falloffAmount ;
MatrixCopy ( worldToShadow , shadow . m_WorldToShadow ) ;
// Compute a rough bounding sphere for the ray
float flRadius = sqrt ( size . x * size . x + size . y * size . y ) * 0.5f ;
VectorMA ( origin , 0.5f * maxHeight , projectionDir , shadow . m_vecSphereCenter ) ;
shadow . m_flSphereRadius = 0.5f * maxHeight + flRadius ;
Vector vecEndPoint ;
Vector vecMins ( - flRadius , - flRadius , - flRadius ) ;
Vector vecMaxs ( flRadius , flRadius , flRadius ) ;
VectorMA ( origin , maxHeight , projectionDir , vecEndPoint ) ;
shadow . m_Ray . Init ( origin , vecEndPoint , vecMins , vecMaxs ) ;
// No more work necessary if it hits no leaves
if ( nLeafCount = = 0 )
return ;
// We're hijacking the surface vis frame to make sure we enumerate
// surfaces only once;
+ + r_surfacevisframe ;
// Clear out the displacement tags also
DispInfo_ClearAllTags ( host_state . worldbrush - > hDispInfos ) ;
ShadowBuildInfo_t build ;
build . m_Shadow = handle ;
build . m_RayStart = origin ;
build . m_pVis = NULL ;
build . m_vecSphereCenter = shadow . m_vecSphereCenter ;
build . m_flSphereRadius = shadow . m_flSphereRadius ;
VectorCopy ( projectionDir , build . m_ProjectionDirection ) ;
// Enumerate leaves
for ( int i = 0 ; i < nLeafCount ; + + i )
{
// NOTE: Scope specifier eliminates virtual function call
CShadowMgr : : EnumerateLeaf ( pLeafList [ i ] , ( intp ) & build ) ;
}
}
void DrawFrustum ( Frustum_t & frustum )
{
const int maxPoints = 8 ;
int i ;
for ( i = 0 ; i < FRUSTUM_NUMPLANES ; i + + )
{
Vector points [ maxPoints ] ;
Vector points2 [ maxPoints ] ;
int numPoints = PolyFromPlane ( points , frustum . GetPlane ( i ) - > normal , frustum . GetPlane ( i ) - > dist ) ;
Assert ( numPoints < = maxPoints ) ;
Vector * in , * out ;
in = points ;
out = points2 ;
int j ;
for ( j = 0 ; j < FRUSTUM_NUMPLANES ; j + + )
{
if ( i = = j )
{
continue ;
}
numPoints = ClipPolyToPlane ( in , numPoints , out , frustum . GetPlane ( j ) - > normal , frustum . GetPlane ( j ) - > dist ) ;
Assert ( numPoints < = maxPoints ) ;
V_swap ( in , out ) ;
}
int c ;
for ( c = 0 ; c < numPoints ; c + + )
{
CDebugOverlay : : AddLineOverlay ( in [ c ] , in [ ( c + 1 ) % numPoints ] , 0 , 255 , 0 , 255 , true , 0.0f ) ;
}
}
}
//static void LineDrawHelper( const Vector &startShadowSpace, const Vector &endShadowSpace,
// const VMatrix &shadowToWorld, unsigned char r, unsigned char g,
// unsigned char b, bool ignoreZ )
//{
// Vector startWorldSpace, endWorldSpace;
// Vector3DMultiplyPositionProjective( shadowToWorld, startShadowSpace, startWorldSpace );
// Vector3DMultiplyPositionProjective( shadowToWorld, endShadowSpace, endWorldSpace );
//
// CDebugOverlay::AddLineOverlay( startWorldSpace,
// endWorldSpace,
// r, g, b, ignoreZ
// , 0.0 );
//}
void CShadowMgr : : ProjectFlashlight ( ShadowHandle_t handle , const VMatrix & worldToShadow , int nLeafCount , const int * pLeafList )
{
VPROF_BUDGET ( " CShadowMgr::ProjectFlashlight " , VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING ) ;
Shadow_t & shadow = m_Shadows [ handle ] ;
if ( ! IsX360 ( ) & & ! r_flashlight_version2 . GetInt ( ) )
{
// First, we need to remove the shadow from all surfaces it may
// currently be in; in other words we're invalidating the shadow surface cache
RemoveAllSurfacesFromShadow ( handle ) ;
RemoveAllModelsFromShadow ( handle ) ;
m_FlashlightStates [ shadow . m_FlashlightHandle ] . m_OccluderBuckets . Flush ( ) ;
}
// Don't bother with this shadow if it's disabled
if ( m_Shadows [ handle ] . m_Flags & SHADOW_DISABLED )
return ;
// Don't compute the surface cache if shadows are off..
if ( ! r_shadows . GetInt ( ) )
return ;
MatrixCopy ( worldToShadow , shadow . m_WorldToShadow ) ;
// We need this for our various bounding computations
VMatrix shadowToWorld ;
MatrixInverseGeneral ( shadow . m_WorldToShadow , shadowToWorld ) ;
// Set up the frustum for the flashlight so that we can cull each leaf against it.
Assert ( shadow . m_Flags & SHADOW_FLASHLIGHT ) ;
Frustum_t & frustum = m_FlashlightStates [ shadow . m_FlashlightHandle ] . m_Frustum ;
FrustumPlanesFromMatrix ( shadowToWorld , frustum ) ;
CalculateSphereFromProjectionMatrixInverse ( shadowToWorld , & shadow . m_vecSphereCenter , & shadow . m_flSphereRadius ) ;
if ( nLeafCount = = 0 )
return ;
// We're hijacking the surface vis frame to make sure we enumerate
// surfaces only once;
+ + r_surfacevisframe ;
// Clear out the displacement tags also
DispInfo_ClearAllTags ( host_state . worldbrush - > hDispInfos ) ;
ShadowBuildInfo_t build ;
build . m_Shadow = handle ;
build . m_RayStart = m_FlashlightStates [ shadow . m_FlashlightHandle ] . m_FlashlightState . m_vecLightOrigin ;
build . m_pVis = NULL ;
build . m_vecSphereCenter = shadow . m_vecSphereCenter ;
build . m_flSphereRadius = shadow . m_flSphereRadius ;
if ( r_flashlightdrawfrustumbbox . GetBool ( ) )
{
Vector mins , maxs ;
CalculateAABBFromProjectionMatrixInverse ( shadowToWorld , & mins , & maxs ) ;
CDebugOverlay : : AddBoxOverlay ( Vector ( 0.0f , 0.0f , 0.0f ) , mins , maxs , QAngle ( 0 , 0 , 0 ) ,
0 , 0 , 255 , 100 , 0.0f ) ;
}
for ( int i = 0 ; i < nLeafCount ; + + i )
{
// NOTE: Scope specifier eliminates virtual function call
CShadowMgr : : EnumerateLeaf ( pLeafList [ i ] , ( intp ) & build ) ;
}
}
//-----------------------------------------------------------------------------
// Applies the flashlight to all surfaces in the leaf
//-----------------------------------------------------------------------------
void CShadowMgr : : ApplyFlashlightToLeaf ( const Shadow_t & shadow , mleaf_t * pLeaf , ShadowBuildInfo_t * pBuild )
{
// Get the bounds of the leaf so that we can test it against the flashlight frustum.
Vector leafMins , leafMaxs ;
VectorAdd ( pLeaf - > m_vecCenter , pLeaf - > m_vecHalfDiagonal , leafMaxs ) ;
VectorSubtract ( pLeaf - > m_vecCenter , pLeaf - > m_vecHalfDiagonal , leafMins ) ;
// The flashlight frustum didn't intersect the bounding box for this leaf! Get outta here!
if ( R_CullBox ( leafMins , leafMaxs , GetFlashlightFrustum ( pBuild - > m_Shadow ) ) )
return ;
// Iterate over all surfaces in the leaf, check for backfacing
// and apply the shadow to the surface if it's not backfaced.
// Note that this really only indicates that the shadow may potentially
// sit on the surface; when we render, we'll actually do the clipping
// computation and at that point we'll remove surfaces that don't
// actually hit the surface
bool bCullDepth = r_flashlightculldepth . GetBool ( ) ;
SurfaceHandle_t * pHandle = & host_state . worldbrush - > marksurfaces [ pLeaf - > firstmarksurface ] ;
for ( int i = 0 ; i < pLeaf - > nummarksurfaces ; i + + )
{
SurfaceHandle_t surfID = pHandle [ i ] ;
// only process each surface once;
if ( MSurf_VisFrame ( surfID ) = = r_surfacevisframe )
continue ;
MSurf_VisFrame ( surfID ) = r_surfacevisframe ;
Assert ( ! MSurf_DispInfo ( surfID ) ) ;
// perspective projection
// world-space vertex
int vertIndex = host_state . worldbrush - > vertindices [ MSurf_FirstVertIndex ( surfID ) ] ;
Vector & worldPos = host_state . worldbrush - > vertexes [ vertIndex ] . position ;
// Get the lookdir
Vector lookdir ;
VectorSubtract ( worldPos , pBuild - > m_RayStart , lookdir ) ;
VectorNormalize ( lookdir ) ;
const cplane_t & surfPlane = MSurf_Plane ( surfID ) ;
// Now apply the spherical cull
float flDist = DotProduct ( surfPlane . normal , pBuild - > m_vecSphereCenter ) - surfPlane . dist ;
if ( fabs ( flDist ) > = pBuild - > m_flSphereRadius )
continue ;
ApplyShadowToSurface ( * pBuild , surfID ) ;
// Backface cull
if ( bCullDepth )
{
if ( ( MSurf_Flags ( surfID ) & SURFDRAW_NOCULL ) = = 0 )
{
if ( DotProduct ( surfPlane . normal , lookdir ) < BACKFACE_EPSILON )
continue ;
}
else
{
// Avoid edge-on shadows regardless.
float dot = DotProduct ( surfPlane . normal , lookdir ) ;
if ( fabs ( dot ) < BACKFACE_EPSILON )
continue ;
}
}
FlashlightInfo_t & flashlightInfo = m_FlashlightStates [ shadow . m_FlashlightHandle ] ;
flashlightInfo . m_OccluderBuckets . AddElement ( MSurf_MaterialSortID ( surfID ) , surfID ) ;
}
}
//-----------------------------------------------------------------------------
// Applies a shadow to all surfaces in the leaf
//-----------------------------------------------------------------------------
void CShadowMgr : : ApplyShadowToLeaf ( const Shadow_t & shadow , mleaf_t * RESTRICT pLeaf , ShadowBuildInfo_t * RESTRICT pBuild )
{
// Iterate over all surfaces in the leaf, check for backfacing
// and apply the shadow to the surface if it's not backfaced.
// Note that this really only indicates that the shadow may potentially
// sit on the surface; when we render, we'll actually do the clipping
// computation and at that point we'll remove surfaces that don't
// actually hit the surface
SurfaceHandle_t * pHandle = & host_state . worldbrush - > marksurfaces [ pLeaf - > firstmarksurface ] ;
for ( int i = 0 ; i < pLeaf - > nummarksurfaces ; i + + )
{
SurfaceHandleRestrict_t surfID = pHandle [ i ] ;
// only process each surface once;
if ( MSurf_VisFrame ( surfID ) = = r_surfacevisframe )
continue ;
MSurf_VisFrame ( surfID ) = r_surfacevisframe ;
Assert ( ! MSurf_DispInfo ( surfID ) ) ;
// If this surface has specifically had dynamic shadows disabled on it, then get out!
if ( ! MSurf_AreDynamicShadowsEnabled ( surfID ) )
continue ;
// Backface cull
const cplane_t * RESTRICT pSurfPlane = & MSurf_Plane ( surfID ) ;
bool bInFront ;
if ( ( MSurf_Flags ( surfID ) & SURFDRAW_NOCULL ) = = 0 )
{
if ( DotProduct ( pSurfPlane - > normal , pBuild - > m_ProjectionDirection ) > - BACKFACE_EPSILON )
continue ;
bInFront = true ;
}
else
{
// Avoid edge-on shadows regardless.
float dot = DotProduct ( pSurfPlane - > normal , pBuild - > m_ProjectionDirection ) ;
if ( fabs ( dot ) < BACKFACE_EPSILON )
continue ;
bInFront = ( dot < 0 ) ;
}
// Here, it's front facing...
// Discard stuff on the wrong side of the ray start
if ( bInFront )
{
if ( DotProduct ( pSurfPlane - > normal , pBuild - > m_RayStart ) < pSurfPlane - > dist )
continue ;
}
else
{
if ( DotProduct ( pSurfPlane - > normal , pBuild - > m_RayStart ) > pSurfPlane - > dist )
continue ;
}
// Now apply the spherical cull
float flDist = DotProduct ( pSurfPlane - > normal , pBuild - > m_vecSphereCenter ) - pSurfPlane - > dist ;
if ( fabs ( flDist ) > = pBuild - > m_flSphereRadius )
continue ;
ApplyShadowToSurface ( * pBuild , surfID ) ;
}
}
# define BIT_SET( a, b ) ((a)[(b)>>3] & (1<<((b)&7)))
//-----------------------------------------------------------------------------
// Applies a projected texture to all surfaces in the leaf
//-----------------------------------------------------------------------------
bool CShadowMgr : : EnumerateLeaf ( int leaf , intp context )
{
VPROF ( " CShadowMgr::EnumerateLeaf " ) ;
ShadowBuildInfo_t * pBuild = ( ShadowBuildInfo_t * ) context ;
// Skip this leaf if it's not visible from the shadow caster
if ( pBuild - > m_pVis )
{
int cluster = CM_LeafCluster ( leaf ) ;
if ( ! BIT_SET ( pBuild - > m_pVis , cluster ) )
return true ;
}
const Shadow_t & shadow = m_Shadows [ pBuild - > m_Shadow ] ;
mleaf_t * pLeaf = & host_state . worldbrush - > leafs [ leaf ] ;
bool bIsFlashlight ;
if ( shadow . m_Flags & SHADOW_FLASHLIGHT )
{
bIsFlashlight = true ;
ApplyFlashlightToLeaf ( shadow , pLeaf , pBuild ) ;
}
else
{
bIsFlashlight = false ;
ApplyShadowToLeaf ( shadow , pLeaf , pBuild ) ;
}
// Add the decal to each displacement in the leaf it touches.
for ( int i = 0 ; i < pLeaf - > dispCount ; i + + )
{
IDispInfo * pDispInfo = MLeaf_Disaplcement ( pLeaf , i ) ;
// Make sure the decal hasn't already been added to it.
if ( pDispInfo - > GetTag ( ) )
continue ;
pDispInfo - > SetTag ( ) ;
ApplyShadowToDisplacement ( * pBuild , pDispInfo , bIsFlashlight ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Adds a shadow to a brush model
//-----------------------------------------------------------------------------
void CShadowMgr : : AddShadowToBrushModel ( ShadowHandle_t handle , model_t * pModel ,
const Vector & origin , const QAngle & angles )
{
// Don't compute the surface cache if shadows are off..
if ( ! r_shadows . GetInt ( ) )
return ;
const Shadow_t * RESTRICT pShadow = & m_Shadows [ handle ] ;
// Transform the shadow ray direction into model space
Vector shadowDirInModelSpace ;
bool bIsFlashlight = ( pShadow - > m_Flags & SHADOW_FLASHLIGHT ) ! = 0 ;
if ( ! bIsFlashlight )
{
// FLASHLIGHTFIXME: should do backface culling for projective light sources.
matrix3x4_t worldToModel ;
AngleIMatrix ( angles , worldToModel ) ;
VectorRotate ( pShadow - > m_ProjectionDir , worldToModel , shadowDirInModelSpace ) ;
}
// Just add all non-backfacing brush surfaces to the list of potential
// surfaces that we may be casting a shadow onto.
SurfaceHandleRestrict_t surfID = SurfaceHandleFromIndex ( pModel - > brush . firstmodelsurface , pModel - > brush . pShared ) ;
for ( int i = 0 ; i < pModel - > brush . nummodelsurfaces ; + + i , + + surfID )
{
// Don't bother with nodraw surfaces
int nFlags = MSurf_Flags ( surfID ) ;
if ( nFlags & SURFDRAW_NODRAW )
continue ;
if ( ! bIsFlashlight )
{
// FLASHLIGHTFIXME: should do backface culling for projective light sources.
// Don't bother with backfacing surfaces
if ( ( nFlags & SURFDRAW_NOCULL ) = = 0 )
{
const cplane_t * RESTRICT pSurfPlane = & MSurf_Plane ( surfID ) ;
float dot = DotProduct ( shadowDirInModelSpace , pSurfPlane - > normal ) ;
if ( dot > 0 )
continue ;
}
}
// FIXME: We may want to do some more high-level per-surface culling
// If so, it'll be added to ApplyShadowToSurface. Call it instead.
AddSurfaceToShadow ( handle , surfID ) ;
}
}
//-----------------------------------------------------------------------------
// Removes all shadows from a brush model
//-----------------------------------------------------------------------------
void CShadowMgr : : RemoveAllShadowsFromBrushModel ( model_t * pModel )
{
SurfaceHandle_t surfID = SurfaceHandleFromIndex ( pModel - > brush . firstmodelsurface , pModel - > brush . pShared ) ;
for ( int i = 0 ; i < pModel - > brush . nummodelsurfaces ; + + i , + + surfID )
{
RemoveAllShadowsFromSurface ( surfID ) ;
}
}
//-----------------------------------------------------------------------------
// Adds the shadow decals on the surface to a queue of things to render
//-----------------------------------------------------------------------------
void CShadowMgr : : AddShadowsOnSurfaceToRenderList ( ShadowDecalHandle_t decalHandle )
{
// Don't compute the surface cache if shadows are off..
if ( ! r_shadows . GetInt ( ) )
return ;
// Add all surface decals into the appropriate render lists
while ( decalHandle ! = m_ShadowDecals . InvalidIndex ( ) )
{
ShadowDecal_t & shadowDecal = m_ShadowDecals [ decalHandle ] ;
if ( m_Shadows [ shadowDecal . m_Shadow ] . m_Flags & SHADOW_FLASHLIGHT )
{
AddSurfaceToFlashlightMaterialBuckets ( shadowDecal . m_Shadow , shadowDecal . m_SurfID ) ;
// We've got one more decal to render
+ + m_DecalsToRender ;
}
else if ( r_shadows_gamecontrol . GetInt ( ) ! = 0 )
{
// For shadow rendering, hook the decal into the render list based on the shadow material, not the surface material.
int sortOrder = m_Shadows [ shadowDecal . m_Shadow ] . m_SortOrder ;
m_ShadowDecals [ decalHandle ] . m_NextRender = m_RenderQueue [ sortOrder ] ;
m_RenderQueue [ sortOrder ] = decalHandle ;
// We've got one more decal to render
+ + m_DecalsToRender ;
}
decalHandle = m_ShadowDecals . Next ( decalHandle ) ;
}
}
void CShadowMgr : : ClearShadowRenderList ( )
{
COMPILE_TIME_ASSERT ( sizeof ( ShadowDecalHandle_t ) = = 2 ) ;
// Clear out the render list
if ( m_RenderQueue . Count ( ) > 0 )
{
memset ( m_RenderQueue . Base ( ) , 0xFF , m_RenderQueue . Count ( ) * sizeof ( ShadowDecalHandle_t ) ) ;
}
m_DecalsToRender = 0 ;
// Clear all lists pertaining to flashlight decals that need to be rendered.
ClearAllFlashlightMaterialBuckets ( ) ;
}
void CShadowMgr : : RenderShadows ( const VMatrix * pModelToWorld )
{
VPROF_BUDGET ( " CShadowMgr::RenderShadows " , VPROF_BUDGETGROUP_SHADOW_RENDERING ) ;
// Iterate through all sort ids and render for regular shadows, which get their materials from the shadow material.
CMatRenderContextPtr pRenderContext ( materials ) ;
int i ;
for ( i = 0 ; i < m_RenderQueue . Count ( ) ; + + i )
{
if ( m_RenderQueue [ i ] ! = m_ShadowDecals . InvalidIndex ( ) )
{
RenderShadowList ( pRenderContext , m_RenderQueue [ i ] , pModelToWorld ) ;
}
}
}
void CShadowMgr : : RenderProjectedTextures ( const VMatrix * pModelToWorld )
{
VPROF_BUDGET ( " CShadowMgr::RenderProjectedTextures " , VPROF_BUDGETGROUP_SHADOW_RENDERING ) ;
RenderFlashlights ( true , pModelToWorld ) ;
RenderShadows ( pModelToWorld ) ;
// Clear out the render list, we've rendered it now
ClearShadowRenderList ( ) ;
}
//-----------------------------------------------------------------------------
// A 2D sutherland-hodgman clipper
//-----------------------------------------------------------------------------
class CClipTop
{
public :
static inline bool Inside ( ShadowVertex_t const & vert ) { return vert . m_ShadowSpaceTexCoord . y < 1 ; }
static inline float Clip ( const Vector & one , const Vector & two ) { return ( 1 - one . y ) / ( two . y - one . y ) ; }
static inline bool IsPlane ( ) { return false ; }
static inline bool IsAbove ( ) { return false ; }
} ;
class CClipLeft
{
public :
static inline bool Inside ( ShadowVertex_t const & vert ) { return vert . m_ShadowSpaceTexCoord . x > 0 ; }
static inline float Clip ( const Vector & one , const Vector & two ) { return one . x / ( one . x - two . x ) ; }
static inline bool IsPlane ( ) { return false ; }
static inline bool IsAbove ( ) { return false ; }
} ;
class CClipRight
{
public :
static inline bool Inside ( ShadowVertex_t const & vert ) { return vert . m_ShadowSpaceTexCoord . x < 1 ; }
static inline float Clip ( const Vector & one , const Vector & two ) { return ( 1 - one . x ) / ( two . x - one . x ) ; }
static inline bool IsPlane ( ) { return false ; }
static inline bool IsAbove ( ) { return false ; }
} ;
class CClipBottom
{
public :
static inline bool Inside ( ShadowVertex_t const & vert ) { return vert . m_ShadowSpaceTexCoord . y > 0 ; }
static inline float Clip ( const Vector & one , const Vector & two ) { return one . y / ( one . y - two . y ) ; }
static inline bool IsPlane ( ) { return false ; }
static inline bool IsAbove ( ) { return false ; }
} ;
class CClipAbove
{
public :
static inline bool Inside ( ShadowVertex_t const & vert ) { return vert . m_ShadowSpaceTexCoord . z > 0 ; }
static inline float Clip ( const Vector & one , const Vector & two ) { return one . z / ( one . z - two . z ) ; }
static inline bool IsPlane ( ) { return false ; }
static inline bool IsAbove ( ) { return true ; }
} ;
class CClipPlane
{
public :
static inline bool Inside ( ShadowVertex_t const & vert )
{
return DotProduct ( vert . m_Position , m_pNormal ) < m_Dist ;
}
static inline float Clip ( const Vector & one , const Vector & two )
{
Vector dir ;
VectorSubtract ( two , one , dir ) ;
return IntersectRayWithPlane ( one , dir , m_pNormal , m_Dist ) ;
}
static inline bool IsAbove ( ) { return false ; }
static inline bool IsPlane ( ) { return true ; }
static void SetPlane ( const Vector normal , float dist )
{
m_pNormal = normal ;
m_Dist = dist ;
}
private :
static Vector m_pNormal ;
static float m_Dist ;
} ;
Vector CClipPlane : : m_pNormal ;
float CClipPlane : : m_Dist ;
static inline void ClampTexCoord ( ShadowVertex_t * pInVertex , ShadowVertex_t * pOutVertex )
{
if ( fabs ( pInVertex - > m_ShadowSpaceTexCoord [ 0 ] ) < 1e-3 )
pOutVertex - > m_ShadowSpaceTexCoord [ 0 ] = 0.0f ;
else if ( fabs ( pInVertex - > m_ShadowSpaceTexCoord [ 0 ] - 1.0f ) < 1e-3 )
pOutVertex - > m_ShadowSpaceTexCoord [ 0 ] = 1.0f ;
if ( fabs ( pInVertex - > m_ShadowSpaceTexCoord [ 1 ] ) < 1e-3 )
pOutVertex - > m_ShadowSpaceTexCoord [ 1 ] = 0.0f ;
else if ( fabs ( pInVertex - > m_ShadowSpaceTexCoord [ 1 ] - 1.0f ) < 1e-3 )
pOutVertex - > m_ShadowSpaceTexCoord [ 1 ] = 1.0f ;
}
template < class Clipper >
static inline void Intersect ( ShadowVertex_t * pStart , ShadowVertex_t * pEnd , ShadowVertex_t * pOut , bool startInside , Clipper & clipper )
{
// Clip the edge to the clip plane
float t ;
if ( ! Clipper : : IsPlane ( ) )
{
if ( ! Clipper : : IsAbove ( ) )
{
// This is the path the we always take for perspective light volumes.
t = Clipper : : Clip ( pStart - > m_ShadowSpaceTexCoord , pEnd - > m_ShadowSpaceTexCoord ) ;
VectorLerp ( pStart - > m_ShadowSpaceTexCoord , pEnd - > m_ShadowSpaceTexCoord , t , pOut - > m_ShadowSpaceTexCoord ) ;
}
else
{
t = Clipper : : Clip ( pStart - > m_ShadowSpaceTexCoord , pEnd - > m_ShadowSpaceTexCoord ) ;
VectorLerp ( pStart - > m_ShadowSpaceTexCoord , pEnd - > m_ShadowSpaceTexCoord , t , pOut - > m_ShadowSpaceTexCoord ) ;
// This is a special thing we do here to avoid hard-edged shadows
if ( startInside )
ClampTexCoord ( pEnd , pOut ) ;
else
ClampTexCoord ( pStart , pOut ) ;
}
}
else
{
t = Clipper : : Clip ( pStart - > m_Position , pEnd - > m_Position ) ;
VectorLerp ( pStart - > m_ShadowSpaceTexCoord , pEnd - > m_ShadowSpaceTexCoord , t , pOut - > m_ShadowSpaceTexCoord ) ;
}
VectorLerp ( pStart - > m_Position , pEnd - > m_Position , t , pOut - > m_Position ) ;
}
template < class Clipper >
static void ShadowClip ( ShadowClipState_t & clip , Clipper & clipper )
{
if ( clip . m_ClipCount = = 0 )
return ;
// Ye Olde Sutherland-Hodgman clipping algorithm
int numOutVerts = 0 ;
ShadowVertex_t * * pSrcVert = clip . m_ppClipVertices [ clip . m_CurrVert ] ;
ShadowVertex_t * * pDestVert = clip . m_ppClipVertices [ ! clip . m_CurrVert ] ;
int numVerts = clip . m_ClipCount ;
ShadowVertex_t * pStart = pSrcVert [ numVerts - 1 ] ;
bool startInside = Clipper : : Inside ( * pStart ) ;
for ( int i = 0 ; i < numVerts ; + + i )
{
ShadowVertex_t * pEnd = pSrcVert [ i ] ;
bool endInside = Clipper : : Inside ( * pEnd ) ;
if ( endInside )
{
if ( ! startInside )
{
// Started outside, ended inside, need to clip the edge
if ( clip . m_TempCount > = SHADOW_VERTEX_TEMP_COUNT )
return ;
// Allocate a new clipped vertex
pDestVert [ numOutVerts ] = & clip . m_pTempVertices [ clip . m_TempCount + + ] ;
// Clip the edge to the clip plane
Intersect ( pStart , pEnd , pDestVert [ numOutVerts ] , startInside , clipper ) ;
+ + numOutVerts ;
}
pDestVert [ numOutVerts + + ] = pEnd ;
}
else
{
if ( startInside )
{
// Started inside, ended outside, need to clip the edge
if ( clip . m_TempCount > = SHADOW_VERTEX_TEMP_COUNT )
return ;
// Allocate a new clipped vertex
pDestVert [ numOutVerts ] = & clip . m_pTempVertices [ clip . m_TempCount + + ] ;
// Clip the edge to the clip plane
Intersect ( pStart , pEnd , pDestVert [ numOutVerts ] , startInside , clipper ) ;
+ + numOutVerts ;
}
}
pStart = pEnd ;
startInside = endInside ;
}
// Switch source lists
clip . m_CurrVert = 1 - clip . m_CurrVert ;
clip . m_ClipCount = numOutVerts ;
Assert ( clip . m_ClipCount < = SHADOW_VERTEX_TEMP_COUNT ) ;
}
//-----------------------------------------------------------------------------
// Project vertices into shadow space
//-----------------------------------------------------------------------------
bool CShadowMgr : : ProjectVerticesIntoShadowSpace ( const VMatrix & modelToShadow ,
float maxDist , int count , Vector * * RESTRICT ppPosition , ShadowClipState_t & clip )
{
bool insideVolume = false ;
// Create vertices to clip to...
for ( int i = 0 ; i < count ; + + i )
{
Assert ( ppPosition [ i ] ) ;
VectorCopy ( * ppPosition [ i ] , clip . m_pTempVertices [ i ] . m_Position ) ;
// Project the points into shadow texture space
Vector3DMultiplyPosition ( modelToShadow , * ppPosition [ i ] , clip . m_pTempVertices [ i ] . m_ShadowSpaceTexCoord ) ;
// Set up clipping coords...
clip . m_ppClipVertices [ 0 ] [ i ] = & clip . m_pTempVertices [ i ] ;
if ( clip . m_pTempVertices [ i ] . m_ShadowSpaceTexCoord [ 2 ] < maxDist )
{
insideVolume = true ;
}
}
clip . m_TempCount = clip . m_ClipCount = count ;
clip . m_CurrVert = 0 ;
return insideVolume ;
}
//-----------------------------------------------------------------------------
// Projects + clips shadows
//-----------------------------------------------------------------------------
int CShadowMgr : : ProjectAndClipVertices ( const Shadow_t & shadow , const VMatrix & worldToShadow ,
const VMatrix * pWorldToModel , int count , Vector * * ppPosition , ShadowVertex_t * * * ppOutVertex )
{
VPROF ( " ProjectAndClipVertices " ) ;
static ShadowClipState_t clip ;
if ( ! ProjectVerticesIntoShadowSpace ( worldToShadow , shadow . m_MaxDist , count , ppPosition , clip ) )
return 0 ;
// Clippers...
CClipTop top ;
CClipBottom bottom ;
CClipLeft left ;
CClipRight right ;
CClipAbove above ;
CClipPlane plane ;
// Sutherland-hodgman clip
ShadowClip ( clip , top ) ;
ShadowClip ( clip , bottom ) ;
ShadowClip ( clip , left ) ;
ShadowClip ( clip , right ) ;
ShadowClip ( clip , above ) ;
// Planes to suppress back-casting
for ( int i = 0 ; i < shadow . m_ClipPlaneCount ; + + i )
{
if ( pWorldToModel )
{
cplane_t worldPlane , modelPlane ;
worldPlane . normal = shadow . m_ClipPlane [ i ] ;
worldPlane . dist = shadow . m_ClipDist [ i ] ;
MatrixTransformPlane ( * pWorldToModel , worldPlane , modelPlane ) ;
plane . SetPlane ( modelPlane . normal , modelPlane . dist ) ;
}
else
{
plane . SetPlane ( shadow . m_ClipPlane [ i ] , shadow . m_ClipDist [ i ] ) ;
}
ShadowClip ( clip , plane ) ;
}
if ( clip . m_ClipCount < 3 )
return 0 ;
// Return a pointer to the array of clipped vertices...
Assert ( ppOutVertex ) ;
* ppOutVertex = clip . m_ppClipVertices [ clip . m_CurrVert ] ;
return clip . m_ClipCount ;
}
//-----------------------------------------------------------------------------
// Accessor for use by the displacements
//-----------------------------------------------------------------------------
int CShadowMgr : : ProjectAndClipVertices ( ShadowHandle_t handle , int count ,
Vector * * ppPosition , ShadowVertex_t * * * ppOutVertex )
{
return ProjectAndClipVertices ( m_Shadows [ handle ] ,
m_Shadows [ handle ] . m_WorldToShadow , NULL , count , ppPosition , ppOutVertex ) ;
}
//-----------------------------------------------------------------------------
// Copies vertex info from the clipped vertices
//-----------------------------------------------------------------------------
// This version treats texcoords as Vector
inline void CShadowMgr : : CopyClippedVertices ( int count , ShadowVertex_t * * ppSrcVert , ShadowVertex_t * pDstVert , const Vector & vToAdd )
{
for ( int i = 0 ; i < count ; + + i )
{
pDstVert [ i ] . m_Position = ppSrcVert [ i ] - > m_Position + vToAdd ;
pDstVert [ i ] . m_ShadowSpaceTexCoord = ppSrcVert [ i ] - > m_ShadowSpaceTexCoord ;
// Make sure it's been clipped
Assert ( ppSrcVert [ i ] - > m_ShadowSpaceTexCoord [ 0 ] > = - 1e-3 f ) ;
Assert ( ppSrcVert [ i ] - > m_ShadowSpaceTexCoord [ 0 ] - 1.0f < = 1e-3 f ) ;
Assert ( ppSrcVert [ i ] - > m_ShadowSpaceTexCoord [ 1 ] > = - 1e-3 f ) ;
Assert ( ppSrcVert [ i ] - > m_ShadowSpaceTexCoord [ 1 ] - 1.0f < = 1e-3 f ) ;
}
}
//-----------------------------------------------------------------------------
// Does the actual work of computing shadow vertices
//-----------------------------------------------------------------------------
bool CShadowMgr : : ComputeShadowVertices ( ShadowDecal_t & decal ,
const VMatrix * pModelToWorld , const VMatrix * pWorldToModel , ShadowVertexCache_t * pVertexCache )
{
VPROF ( " CShadowMgr::ComputeShadowVertices " ) ;
// Prepare for the clipping
Vector * * ppVec = ( Vector * * ) stackalloc ( MSurf_VertCount ( decal . m_SurfID ) * sizeof ( Vector * ) ) ;
for ( int i = 0 ; i < MSurf_VertCount ( decal . m_SurfID ) ; + + i )
{
int vertIndex = host_state . worldbrush - > vertindices [ MSurf_FirstVertIndex ( decal . m_SurfID ) + i ] ;
ppVec [ i ] = & host_state . worldbrush - > vertexes [ vertIndex ] . position ;
}
// Compute the modelToShadow transform.
// In the case of the world, just use worldToShadow...
VMatrix * pModelToShadow = & m_Shadows [ decal . m_Shadow ] . m_WorldToShadow ;
VMatrix temp ;
if ( pModelToWorld )
{
MatrixMultiply ( * pModelToShadow , * pModelToWorld , temp ) ;
pModelToShadow = & temp ;
}
else
{
pWorldToModel = NULL ;
}
// Create vertices to clip to...
ShadowVertex_t * * ppSrcVert ;
int clipCount = ProjectAndClipVertices ( m_Shadows [ decal . m_Shadow ] , * pModelToShadow , pWorldToModel ,
MSurf_VertCount ( decal . m_SurfID ) , ppVec , & ppSrcVert ) ;
if ( clipCount = = 0 )
{
pVertexCache - > m_Count = 0 ;
return false ;
}
// Allocate the vertices we're going to use for the decal
ShadowVertex_t * pDstVert = AllocateVertices ( * pVertexCache , clipCount ) ;
Assert ( pDstVert ) ;
// Copy the clipped vertices into the cache
const Vector & vNormal = MSurf_Plane ( decal . m_SurfID ) . normal ;
CopyClippedVertices ( clipCount , ppSrcVert , pDstVert , vNormal * OVERLAY_AVOID_FLICKER_NORMAL_OFFSET ) ;
// Indicate which shadow this is related to
pVertexCache - > m_Shadow = decal . m_Shadow ;
return true ;
}
//-----------------------------------------------------------------------------
// Should we cache vertices?
//-----------------------------------------------------------------------------
inline bool CShadowMgr : : ShouldCacheVertices ( const ShadowDecal_t & decal )
{
return ( m_Shadows [ decal . m_Shadow ] . m_Flags & SHADOW_CACHE_VERTS ) ! = 0 ;
}
//-----------------------------------------------------------------------------
// Generates a list displacement shadow vertices to render
//-----------------------------------------------------------------------------
inline bool CShadowMgr : : GenerateDispShadowRenderInfo ( IMatRenderContext * pRenderContext , ShadowDecal_t & decal , ShadowRenderInfo_t & info )
{
//=============================================================================
// HPE_BEGIN:
// [smessick] Added an overflow condition for the max disp decal cache.
//=============================================================================
if ( info . m_DispCount > = MAX_SHADOW_DECAL_CACHE_COUNT )
{
info . m_DispCount = MAX_SHADOW_DECAL_CACHE_COUNT ;
return true ;
}
//=============================================================================
// HPE_END
//=============================================================================
int v , i ;
if ( ! MSurf_DispInfo ( decal . m_SurfID ) - > ComputeShadowFragments ( decal . m_DispShadow , v , i ) )
return false ;
// Catch overflows....
if ( ( info . m_VertexCount + v > = info . m_nMaxVertices ) | | ( info . m_IndexCount + i > = info . m_nMaxIndices ) )
return true ;
info . m_VertexCount + = v ;
info . m_IndexCount + = i ;
info . m_pDispCache [ info . m_DispCount + + ] = decal . m_DispShadow ;
return true ;
}
//-----------------------------------------------------------------------------
// Generates a list shadow vertices to render
//-----------------------------------------------------------------------------
inline bool CShadowMgr : : GenerateNormalShadowRenderInfo ( IMatRenderContext * pRenderContext , ShadowDecal_t & decal , ShadowRenderInfo_t & info )
{
//=============================================================================
// HPE_BEGIN:
// [smessick] Check for cache overflow.
//=============================================================================
if ( info . m_Count > = MAX_SHADOW_DECAL_CACHE_COUNT )
{
info . m_Count = MAX_SHADOW_DECAL_CACHE_COUNT ;
return true ;
}
//=============================================================================
// HPE_END
//=============================================================================
// Look for a cache hit
ShadowVertexCache_t * pVertexCache ;
if ( decal . m_ShadowVerts ! = m_VertexCache . InvalidIndex ( ) )
{
// Ok, we've already computed the data, lets use it
info . m_pCache [ info . m_Count ] = decal . m_ShadowVerts ;
pVertexCache = & m_VertexCache [ decal . m_ShadowVerts ] ;
}
else
{
// Attempt to cull the surface
bool bIsNear = IsShadowNearSurface ( decal . m_Shadow , decal . m_SurfID , info . m_pModelToWorld , & info . m_WorldToModel ) ;
if ( ! bIsNear )
return false ;
// In this case, we gotta recompute the shadow decal vertices
// and maybe even store it into the cache....
bool shouldCacheVerts = ShouldCacheVertices ( decal ) ;
if ( shouldCacheVerts )
{
decal . m_ShadowVerts = m_VertexCache . AddToTail ( ) ;
info . m_pCache [ info . m_Count ] = decal . m_ShadowVerts ;
pVertexCache = & m_VertexCache [ decal . m_ShadowVerts ] ;
}
else
{
int i = m_TempVertexCache . AddToTail ( ) ;
info . m_pCache [ info . m_Count ] = - i - 1 ;
pVertexCache = & m_TempVertexCache [ i ] ;
Assert ( info . m_pCache [ info . m_Count ] < 0 ) ;
}
// Compute the shadow vertices
// If no vertices were created, indicate this surface should be removed from the cache
if ( ! ComputeShadowVertices ( decal , info . m_pModelToWorld , & info . m_WorldToModel , pVertexCache ) )
return false ;
}
// Catch overflows....
int nAdditionalIndices = 3 * ( pVertexCache - > m_Count - 2 ) ;
if ( ( info . m_VertexCount + pVertexCache - > m_Count > = info . m_nMaxVertices ) | |
( info . m_IndexCount + nAdditionalIndices > = info . m_nMaxIndices ) )
{
return true ;
}
// Update vertex, index, and decal counts
info . m_VertexCount + = pVertexCache - > m_Count ;
info . m_IndexCount + = nAdditionalIndices ;
+ + info . m_Count ;
return true ;
}
//-----------------------------------------------------------------------------
// Generates a list shadow vertices to render
//-----------------------------------------------------------------------------
void CShadowMgr : : GenerateShadowRenderInfo ( IMatRenderContext * pRenderContext , ShadowDecalHandle_t decalHandle , ShadowRenderInfo_t & info )
{
info . m_VertexCount = 0 ;
info . m_IndexCount = 0 ;
info . m_Count = 0 ;
info . m_DispCount = 0 ;
// Keep the lists only full of valid decals; that way we can preserve
// the render lists in the case that we discover a shadow isn't needed.
ShadowDecalHandle_t next ;
for ( ; decalHandle ! = m_ShadowDecals . InvalidIndex ( ) ; decalHandle = next )
{
ShadowDecal_t & decal = m_ShadowDecals [ decalHandle ] ;
next = m_ShadowDecals [ decalHandle ] . m_NextRender ;
// Skip translucent shadows [ don't add their verts + indices to the render lists ]
Shadow_t & shadow = m_Shadows [ decal . m_Shadow ] ;
if ( shadow . m_FalloffBias = = 255 )
continue ;
bool keepShadow ;
if ( decal . m_DispShadow ! = DISP_SHADOW_HANDLE_INVALID )
{
// Handle shadows on displacements...
keepShadow = GenerateDispShadowRenderInfo ( pRenderContext , decal , info ) ;
}
else
{
// Handle shadows on normal surfaces
keepShadow = GenerateNormalShadowRenderInfo ( pRenderContext , decal , info ) ;
}
// Retire the surface if the shadow didn't actually hit it
if ( ! keepShadow & & ShouldCacheVertices ( decal ) )
{
// If no triangles were generated
// (the decal was completely clipped off)
// In this case, remove the decal from the surface cache
// so next time it'll be faster (for cached decals)
RemoveShadowDecalFromSurface ( decal . m_SurfID , decalHandle ) ;
}
}
}
//-----------------------------------------------------------------------------
// Computes information for rendering
//-----------------------------------------------------------------------------
void CShadowMgr : : ComputeRenderInfo ( ShadowDecalRenderInfo_t * pInfo , ShadowHandle_t handle ) const
{
const ShadowInfo_t & i = m_Shadows [ handle ] ;
pInfo - > m_vTexOrigin = i . m_TexOrigin ;
pInfo - > m_vTexSize = i . m_TexSize ;
pInfo - > m_flFalloffOffset = i . m_FalloffOffset ;
pInfo - > m_flFalloffAmount = i . m_FalloffAmount ;
pInfo - > m_flFalloffBias = i . m_FalloffBias ;
float flFalloffDist = i . m_MaxDist - i . m_FalloffOffset ;
pInfo - > m_flOOZFalloffDist = ( flFalloffDist > 0.0f ) ? 1.0f / flFalloffDist : 1.0f ;
}
//-----------------------------------------------------------------------------
// Adds normal shadows to the mesh builder
//-----------------------------------------------------------------------------
int CShadowMgr : : AddNormalShadowsToMeshBuilder ( CMeshBuilder & meshBuilder , ShadowRenderInfo_t & info )
{
// Step through the cache and add all shadows on normal surfaces
ShadowDecalRenderInfo_t shadow ;
int baseIndex = 0 ;
for ( int i = 0 ; i < info . m_Count ; + + i )
{
// Two loops here, basically to minimize the # of if statements we need
ShadowVertexCache_t * pVertexCache ;
if ( info . m_pCache [ i ] < 0 )
{
pVertexCache = & m_TempVertexCache [ - info . m_pCache [ i ] - 1 ] ;
}
else
{
pVertexCache = & m_VertexCache [ info . m_pCache [ i ] ] ;
}
ShadowVertex_t * pVerts = GetCachedVerts ( * pVertexCache ) ;
g_pShadowMgr - > ComputeRenderInfo ( & shadow , pVertexCache - > m_Shadow ) ;
int j ;
unsigned char c ;
Vector2D texCoord ;
int vCount = pVertexCache - > m_Count - 2 ;
if ( vCount < = 0 )
continue ;
for ( j = 0 ; j < vCount ; + + j , + + pVerts )
{
// Transform + offset the texture coords
Vector2DMultiply ( pVerts - > m_ShadowSpaceTexCoord . AsVector2D ( ) , shadow . m_vTexSize , texCoord ) ;
texCoord + = shadow . m_vTexOrigin ;
c = ComputeDarkness ( pVerts - > m_ShadowSpaceTexCoord . z , shadow ) ;
meshBuilder . Position3fv ( pVerts - > m_Position . Base ( ) ) ;
meshBuilder . Color4ub ( c , c , c , c ) ;
meshBuilder . TexCoord2fv ( 0 , texCoord . Base ( ) ) ;
meshBuilder . AdvanceVertex ( ) ;
meshBuilder . FastIndex ( baseIndex ) ;
meshBuilder . FastIndex ( j + baseIndex + 1 ) ;
meshBuilder . FastIndex ( j + baseIndex + 2 ) ;
}
Vector2DMultiply ( pVerts - > m_ShadowSpaceTexCoord . AsVector2D ( ) , shadow . m_vTexSize , texCoord ) ;
texCoord + = shadow . m_vTexOrigin ;
c = ComputeDarkness ( pVerts - > m_ShadowSpaceTexCoord . z , shadow ) ;
meshBuilder . Position3fv ( pVerts - > m_Position . Base ( ) ) ;
meshBuilder . Color4ub ( c , c , c , c ) ;
meshBuilder . TexCoord2fv ( 0 , texCoord . Base ( ) ) ;
meshBuilder . AdvanceVertex ( ) ;
+ + pVerts ;
Vector2DMultiply ( pVerts - > m_ShadowSpaceTexCoord . AsVector2D ( ) , shadow . m_vTexSize , texCoord ) ;
texCoord + = shadow . m_vTexOrigin ;
c = ComputeDarkness ( pVerts - > m_ShadowSpaceTexCoord . z , shadow ) ;
meshBuilder . Position3fv ( pVerts - > m_Position . Base ( ) ) ;
meshBuilder . Color4ub ( c , c , c , c ) ;
meshBuilder . TexCoord2fv ( 0 , texCoord . Base ( ) ) ;
meshBuilder . AdvanceVertex ( ) ;
// Update the base index
baseIndex + = vCount + 2 ;
}
return baseIndex ;
}
//-----------------------------------------------------------------------------
// Adds displacement shadows to the mesh builder
//-----------------------------------------------------------------------------
int CShadowMgr : : AddDisplacementShadowsToMeshBuilder ( CMeshBuilder & meshBuilder ,
ShadowRenderInfo_t & info , int baseIndex )
{
if ( ! r_DrawDisp . GetBool ( ) )
return baseIndex ;
// Step through the cache and add all shadows on displacement surfaces
for ( int i = 0 ; i < info . m_DispCount ; + + i )
{
baseIndex = DispInfo_AddShadowsToMeshBuilder ( meshBuilder , info . m_pDispCache [ i ] , baseIndex ) ;
}
return baseIndex ;
}
//-----------------------------------------------------------------------------
// The following methods will display debugging info in the middle of each shadow decal
//-----------------------------------------------------------------------------
static void DrawShadowID ( ShadowHandle_t shadowHandle , const Vector & vecCentroid )
{
# ifndef SWDS
char buf [ 32 ] ;
Q_snprintf ( buf , sizeof ( buf ) , " %d " , shadowHandle ) ;
CDebugOverlay : : AddTextOverlay ( vecCentroid , 0 , buf ) ;
# endif
}
void CShadowMgr : : RenderDebuggingInfo ( const ShadowRenderInfo_t & info , ShadowDebugFunc_t func )
{
// Step through the cache and add all shadows on normal surfaces
for ( int i = 0 ; i < info . m_Count ; + + i )
{
ShadowVertexCache_t * pVertexCache ;
if ( info . m_pCache [ i ] < 0 )
{
pVertexCache = & m_TempVertexCache [ - info . m_pCache [ i ] - 1 ] ;
}
else
{
pVertexCache = & m_VertexCache [ info . m_pCache [ i ] ] ;
}
ShadowVertex_t * pVerts = GetCachedVerts ( * pVertexCache ) ;
Vector vecNormal ;
float flTotalArea = 0.0f ;
Vector vecCentroid ( 0 , 0 , 0 ) ;
Vector vecApex = pVerts [ 0 ] . m_Position ;
int vCount = pVertexCache - > m_Count ;
for ( int j = 0 ; j < vCount - 2 ; + + j )
{
Vector v1 = pVerts [ j + 1 ] . m_Position ;
Vector v2 = pVerts [ j + 2 ] . m_Position ;
CrossProduct ( v2 - v1 , v1 - vecApex , vecNormal ) ;
float flArea = vecNormal . Length ( ) ;
flTotalArea + = flArea ;
vecCentroid + = ( vecApex + v1 + v2 ) * flArea / 3.0f ;
}
if ( flTotalArea )
{
vecCentroid / = flTotalArea ;
}
func ( pVertexCache - > m_Shadow , vecCentroid ) ;
}
}
//-----------------------------------------------------------------------------
// Renders shadows that all share a material enumeration
//-----------------------------------------------------------------------------
void CShadowMgr : : RenderShadowList ( IMatRenderContext * pRenderContext , ShadowDecalHandle_t decalHandle , const VMatrix * pModelToWorld )
{
//=============================================================================
// HPE_BEGIN:
// [smessick] Make sure we don't overflow our caches.
//=============================================================================
if ( m_DecalsToRender > m_ShadowDecalCache . Count ( ) )
{
// Don't grow past the MAX_SHADOW_DECAL_CACHE_COUNT cap.
int diff = min ( m_DecalsToRender , ( int ) MAX_SHADOW_DECAL_CACHE_COUNT ) - m_ShadowDecalCache . Count ( ) ;
if ( diff > 0 )
{
// Grow the cache.
m_ShadowDecalCache . Grow ( diff ) ;
DevMsg ( " [CShadowMgr::RenderShadowList] growing shadow decal cache (decals: %d, cache: %d, diff: %d). \n " , m_DecalsToRender , m_ShadowDecalCache . Count ( ) , diff ) ;
}
}
if ( m_DecalsToRender > m_DispShadowDecalCache . Count ( ) )
{
// Don't grow past the MAX_SHADOW_DECAL_CACHE_COUNT cap.
int diff = min ( m_DecalsToRender , ( int ) MAX_SHADOW_DECAL_CACHE_COUNT ) - m_DispShadowDecalCache . Count ( ) ;
if ( diff > 0 )
{
// Grow the cache.
m_DispShadowDecalCache . Grow ( diff ) ;
DevMsg ( " [CShadowMgr::RenderShadowList] growing disp shadow decal cache (decals: %d, cache: %d, diff: %d). \n " , m_DecalsToRender , m_DispShadowDecalCache . Count ( ) , diff ) ;
}
}
//=============================================================================
// HPE_END
//=============================================================================
// Set the render state...
Shadow_t & shadow = m_Shadows [ m_ShadowDecals [ decalHandle ] . m_Shadow ] ;
if ( r_shadowwireframe . GetInt ( ) = = 0 )
{
pRenderContext - > Bind ( shadow . m_pMaterial , shadow . m_pBindProxy ) ;
}
else
{
pRenderContext - > Bind ( g_materialWorldWireframe ) ;
}
// Blow away the temporary vertex cache (for normal surfaces)
ClearTempCache ( ) ;
// Set up rendering info structure
ShadowRenderInfo_t info ;
//=============================================================================
// HPE_BEGIN:
// [smessick] This code used to create the cache dynamically on the stack.
//=============================================================================
info . m_pCache = m_ShadowDecalCache . Base ( ) ;
info . m_pDispCache = m_DispShadowDecalCache . Base ( ) ;
//=============================================================================
// HPE_END
//=============================================================================
info . m_pModelToWorld = pModelToWorld ;
if ( pModelToWorld )
{
MatrixInverseTR ( * pModelToWorld , info . m_WorldToModel ) ;
}
info . m_nMaxIndices = pRenderContext - > GetMaxIndicesToRender ( ) ;
info . m_nMaxVertices = pRenderContext - > GetMaxVerticesToRender ( shadow . m_pMaterial ) ;
// Iterate over all decals in the decal list and generate polygon lists
// Creating them from scratch if their shadow poly cache is invalid
GenerateShadowRenderInfo ( pRenderContext , decalHandle , info ) ;
Assert ( info . m_Count < = m_DecalsToRender ) ;
Assert ( info . m_DispCount < = m_DecalsToRender ) ;
//=============================================================================
// HPE_BEGIN:
// [smessick] Also check against the max.
//=============================================================================
Assert ( info . m_Count < = m_ShadowDecalCache . Count ( ) & &
info . m_Count < = MAX_SHADOW_DECAL_CACHE_COUNT ) ;
Assert ( info . m_DispCount < = m_DispShadowDecalCache . Count ( ) & &
info . m_DispCount < = MAX_SHADOW_DECAL_CACHE_COUNT ) ;
//=============================================================================
// HPE_END
//=============================================================================
// Now that the vertex lists are created, render them
IMesh * pMesh = pRenderContext - > GetDynamicMesh ( ) ;
CMeshBuilder meshBuilder ;
meshBuilder . Begin ( pMesh , MATERIAL_TRIANGLES , info . m_VertexCount , info . m_IndexCount ) ;
// Add in shadows from both normal surfaces + displacement surfaces
int baseIndex = AddNormalShadowsToMeshBuilder ( meshBuilder , info ) ;
AddDisplacementShadowsToMeshBuilder ( meshBuilder , info , baseIndex ) ;
meshBuilder . End ( ) ;
pMesh - > Draw ( ) ;
if ( r_shadowids . GetInt ( ) ! = 0 )
{
RenderDebuggingInfo ( info , DrawShadowID ) ;
}
}
//-----------------------------------------------------------------------------
// Set the number of world material buckets. This should get called on level load.
//-----------------------------------------------------------------------------
void CShadowMgr : : SetNumWorldMaterialBuckets ( int numMaterialSortBins )
{
m_NumWorldMaterialBuckets = numMaterialSortBins ;
FlashlightHandle_t flashlightID ;
for ( flashlightID = m_FlashlightStates . Head ( ) ;
flashlightID ! = m_FlashlightStates . InvalidIndex ( ) ;
flashlightID = m_FlashlightStates . Next ( flashlightID ) )
{
m_FlashlightStates [ flashlightID ] . m_MaterialBuckets . SetNumMaterialSortIDs ( numMaterialSortBins ) ;
m_FlashlightStates [ flashlightID ] . m_OccluderBuckets . SetNumMaterialSortIDs ( numMaterialSortBins ) ;
}
ClearAllFlashlightMaterialBuckets ( ) ;
}
//-----------------------------------------------------------------------------
// Per frame call to clear all of the flashlight world material buckets.
//-----------------------------------------------------------------------------
void CShadowMgr : : ClearAllFlashlightMaterialBuckets ( void )
{
if ( IsX360 ( ) | | r_flashlight_version2 . GetInt ( ) )
return ;
FlashlightHandle_t flashlightID ;
for ( flashlightID = m_FlashlightStates . Head ( ) ;
flashlightID ! = m_FlashlightStates . InvalidIndex ( ) ;
flashlightID = m_FlashlightStates . Next ( flashlightID ) )
{
m_FlashlightStates [ flashlightID ] . m_MaterialBuckets . Flush ( ) ;
}
}
//-----------------------------------------------------------------------------
// Allocate world material buckets for a particular flashlight. This should get called on flashlight creation.
//-----------------------------------------------------------------------------
void CShadowMgr : : AllocFlashlightMaterialBuckets ( FlashlightHandle_t flashlightID )
{
Assert ( m_FlashlightStates . MaxElementIndex ( ) > = flashlightID ) ;
m_FlashlightStates [ flashlightID ] . m_MaterialBuckets . SetNumMaterialSortIDs ( m_NumWorldMaterialBuckets ) ;
m_FlashlightStates [ flashlightID ] . m_OccluderBuckets . SetNumMaterialSortIDs ( m_NumWorldMaterialBuckets ) ;
}
//-----------------------------------------------------------------------------
// Update a particular flashlight's state.
//-----------------------------------------------------------------------------
void CShadowMgr : : UpdateFlashlightState ( ShadowHandle_t shadowHandle , const FlashlightState_t & lightState )
{
m_FlashlightStates [ m_Shadows [ shadowHandle ] . m_FlashlightHandle ] . m_FlashlightState = lightState ;
}
void CShadowMgr : : SetFlashlightDepthTexture ( ShadowHandle_t shadowHandle , ITexture * pFlashlightDepthTexture , unsigned char ucShadowStencilBit )
{
m_Shadows [ shadowHandle ] . m_pFlashlightDepthTexture = pFlashlightDepthTexture ;
m_Shadows [ shadowHandle ] . m_ucShadowStencilBit = ucShadowStencilBit ;
}
bool ScreenSpaceRectFromPoints ( IMatRenderContext * pRenderContext , Vector vClippedPolygons [ 8 ] [ 10 ] , int * pNumPoints , int nNumPolygons , int * nLeft , int * nTop , int * nRight , int * nBottom )
{
if ( nNumPolygons = = 0 )
return false ;
VMatrix matView , matProj , matViewProj ;
pRenderContext - > GetMatrix ( MATERIAL_VIEW , & matView ) ;
pRenderContext - > GetMatrix ( MATERIAL_PROJECTION , & matProj ) ;
MatrixMultiply ( matProj , matView , matViewProj ) ;
float fMinX , fMaxX , fMinY , fMaxY ; // Init bounding rect
fMinX = fMinY = FLT_MAX ;
fMaxX = fMaxY = - FLT_MAX ;
for ( int i = 0 ; i < nNumPolygons ; i + + )
{
for ( int j = 0 ; j < pNumPoints [ i ] ; j + + )
{
Vector vScreenSpacePoint ;
matViewProj . V3Mul ( vClippedPolygons [ i ] [ j ] , vScreenSpacePoint ) ; // Transform from World to screen space
fMinX = fpmin ( fMinX , vScreenSpacePoint . x ) ; // Update mins/maxes
fMaxX = fpmax ( fMaxX , vScreenSpacePoint . x ) ; //
fMinY = fpmin ( fMinY , - vScreenSpacePoint . y ) ; // These are in -1 to +1 range
fMaxY = fpmax ( fMaxY , - vScreenSpacePoint . y ) ; //
}
}
int nWidth , nHeight ;
g_pMaterialSystem - > GetBackBufferDimensions ( nWidth , nHeight ) ; // Get render target dimensions
* nLeft = ( ( fMinX * 0.5f + 0.5f ) * ( float ) nWidth ) - 1 ; // Convert to render target pixel units
* nTop = ( ( fMinY * 0.5f + 0.5f ) * ( float ) nHeight ) - 1 ;
* nRight = ( ( fMaxX * 0.5f + 0.5f ) * ( float ) nWidth ) + 1 ;
* nBottom = ( ( fMaxY * 0.5f + 0.5f ) * ( float ) nHeight ) + 1 ;
* nLeft = clamp ( * nLeft , 0 , nWidth ) ; // Clamp to render target dimensions
* nTop = clamp ( * nTop , 0 , nHeight ) ;
* nRight = clamp ( * nRight , 0 , nWidth ) ;
* nBottom = clamp ( * nBottom , 0 , nHeight ) ;
Assert ( ( * nLeft < = * nRight ) & & ( * nTop < = * nBottom ) ) ;
// Do we have an actual subrect of the whole screen?
bool bWithinBounds = ( ( * nLeft > 0 ) | | ( * nTop > 0 ) | | ( * nRight < nWidth ) | | ( * nBottom < nHeight ) ) ;
// Compute valid area
nWidth = ( * nRight - * nLeft ) ;
nHeight = ( * nBottom - * nTop ) ;
int nArea = ( nWidth > 0 ) & & ( nHeight > 0 ) ? nWidth * nHeight : 0 ;
// Valid rect?
return bWithinBounds & & ( nArea > 0 ) ;
}
// Turn this optimization off by default
static ConVar r_flashlightclip ( " r_flashlightclip " , " 0 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightdrawclip ( " r_flashlightdrawclip " , " 0 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightscissor ( " r_flashlightscissor " , " 1 " , 0 ) ;
void ExtractFrustumPlanes ( Frustum frustumPlanes , float flPlaneEpsilon )
{
const CViewSetup & view = g_EngineRenderer - > ViewGetCurrent ( ) ;
float flFOVy = CalcFovY ( view . fov , view . m_flAspectRatio ) ;
Frustum_t frustum ;
Vector vForward , vRight , vUp ;
AngleVectors ( view . angles , & vForward , & vRight , & vUp ) ;
GeneratePerspectiveFrustum ( view . origin , vForward , vRight , vUp ,
view . zNear + flPlaneEpsilon , view . zFar - flPlaneEpsilon , // Apply epsilon to near and far
view . fov , flFOVy , frustum ) ;
// Copy out to the planes that the engine renderer uses.
for ( int i = 0 ; i < FRUSTUM_NUMPLANES ; i + + )
{
frustumPlanes [ i ] . m_Normal = frustum . GetPlane ( i ) - > normal ;
frustumPlanes [ i ] . m_Dist = frustum . GetPlane ( i ) - > dist ;
}
}
void ConstructNearAndFarPolygons ( Vector * pVecNearPlane , Vector * pVecFarPlane , float flPlaneEpsilon )
{
const CViewSetup & view = g_EngineRenderer - > ViewGetCurrent ( ) ;
float fovY = CalcFovY ( view . fov , view . m_flAspectRatio ) ;
// Compute near and far plane half-width and half-height
float flTanHalfAngleRadians = tan ( view . fov * ( 0.5f * M_PI / 180.0f ) ) ;
float flHalfNearWidth = flTanHalfAngleRadians * ( view . zNear + flPlaneEpsilon ) ;
float flHalfFarWidth = flTanHalfAngleRadians * ( view . zFar - flPlaneEpsilon ) ;
flTanHalfAngleRadians = tan ( fovY * ( 0.5f * M_PI / 180.0f ) ) ;
float flHalfNearHeight = flTanHalfAngleRadians * ( view . zNear + flPlaneEpsilon ) ;
float flHalfFarHeight = flTanHalfAngleRadians * ( view . zFar - flPlaneEpsilon ) ;
// World-space orientation of viewer
Vector vForward , vRight , vUp ;
AngleVectors ( view . angles , & vForward , & vRight , & vUp ) ;
vForward . NormalizeInPlace ( ) ;
vRight . NormalizeInPlace ( ) ;
vUp . NormalizeInPlace ( ) ;
// Center of near and far planes in world space
Vector vCenterNear = view . origin + vForward * ( view . zNear + flPlaneEpsilon ) ;
Vector vCenterFar = view . origin + vForward * ( view . zFar - flPlaneEpsilon ) ;
pVecNearPlane [ 0 ] = vCenterNear - ( vRight * flHalfNearWidth ) - ( vUp * flHalfNearHeight ) ;
pVecNearPlane [ 1 ] = vCenterNear - ( vRight * flHalfNearWidth ) + ( vUp * flHalfNearHeight ) ;
pVecNearPlane [ 2 ] = vCenterNear + ( vRight * flHalfNearWidth ) + ( vUp * flHalfNearHeight ) ;
pVecNearPlane [ 3 ] = vCenterNear + ( vRight * flHalfNearWidth ) - ( vUp * flHalfNearHeight ) ;
pVecFarPlane [ 0 ] = vCenterNear - ( vRight * flHalfFarWidth ) - ( vUp * flHalfFarHeight ) ;
pVecFarPlane [ 1 ] = vCenterNear + ( vRight * flHalfFarWidth ) - ( vUp * flHalfFarHeight ) ;
pVecFarPlane [ 2 ] = vCenterNear + ( vRight * flHalfFarWidth ) + ( vUp * flHalfFarHeight ) ;
pVecFarPlane [ 3 ] = vCenterNear - ( vRight * flHalfFarWidth ) + ( vUp * flHalfFarHeight ) ;
}
void DrawDebugPolygon ( int nNumVerts , Vector * pVecPoints , bool bFrontFacing , bool bNearPlane )
{
int r = 0 , g = 0 , b = 0 ;
if ( bFrontFacing )
b = 255 ;
else
r = 255 ;
if ( bNearPlane ) // Draw near plane green for visualization
{
r = b = 0 ;
g = 255 ;
}
// Draw triangles fanned out from vertex zero
for ( int i = 1 ; i < ( nNumVerts - 1 ) ; i + + )
{
Vector v0 = pVecPoints [ 0 ] ;
Vector v1 = pVecPoints [ bFrontFacing ? i : i + 1 ] ;
Vector v2 = pVecPoints [ bFrontFacing ? i + 1 : i ] ;
CDebugOverlay : : AddTriangleOverlay ( v0 , v1 , v2 , r , g , b , 20 , true , 0 ) ;
}
// Draw solid lines around the polygon
for ( int i = 0 ; i < nNumVerts ; i + + )
{
Vector v0 = pVecPoints [ i ] ;
Vector v1 = pVecPoints [ ( i + 1 ) % nNumVerts ] ;
CDebugOverlay : : AddLineOverlay ( v0 , v1 , 255 , 255 , 255 , 255 , false , 0 ) ;
}
}
void DrawPolygonToStencil ( IMatRenderContext * pRenderContext , int nNumVerts , Vector * pVecPoints , bool bFrontFacing , bool bNearPlane )
{
IMaterial * pMaterial = materials - > FindMaterial ( " engine/writestencil " , TEXTURE_GROUP_OTHER , true ) ;
pRenderContext - > Bind ( pMaterial ) ;
IMesh * pMesh = pRenderContext - > GetDynamicMesh ( true ) ;
pRenderContext - > MatrixMode ( MATERIAL_MODEL ) ;
pRenderContext - > PushMatrix ( ) ;
pRenderContext - > LoadIdentity ( ) ;
CMeshBuilder meshBuilder ;
meshBuilder . Begin ( pMesh , MATERIAL_TRIANGLES , nNumVerts - 2 ) ;
// Fan out from vertex zero
for ( int i = 1 ; i < ( nNumVerts - 1 ) ; i + + )
{
meshBuilder . Position3f ( pVecPoints [ 0 ] . x , pVecPoints [ 0 ] . y , pVecPoints [ 0 ] . z ) ;
meshBuilder . AdvanceVertex ( ) ;
int index = bFrontFacing ? i : i + 1 ;
meshBuilder . Position3f ( pVecPoints [ index ] . x , pVecPoints [ index ] . y , pVecPoints [ index ] . z ) ;
meshBuilder . AdvanceVertex ( ) ;
index = bFrontFacing ? i + 1 : i ;
meshBuilder . Position3f ( pVecPoints [ index ] . x , pVecPoints [ index ] . y , pVecPoints [ index ] . z ) ;
meshBuilder . AdvanceVertex ( ) ;
}
meshBuilder . End ( false , true ) ;
pRenderContext - > MatrixMode ( MATERIAL_MODEL ) ;
pRenderContext - > PopMatrix ( ) ;
}
// Determine if two Vectors are sufficiently close (Manhattan-ish distance, not Euclidean)
bool SufficientlyClose ( Vector v1 , Vector v2 , float flEpsilon )
{
if ( fabs ( v1 . x - v2 . x ) > flEpsilon ) // Bail if x components are sufficiently different
return false ;
if ( fabs ( v1 . y - v2 . y ) > flEpsilon ) // Bail if y components are sufficiently different
return false ;
if ( fabs ( v1 . z - v2 . z ) > flEpsilon ) // Bail if z components are sufficiently different
return false ;
return true ;
}
int ClipPlaneToFrustum ( Vector * pInPoints , Vector * pOutPoints , Vector * pVecWorldFrustumPoints )
{
Vector vClipPing [ 10 ] ; // Vector lists to ping-pong between while clipping
Vector vClipPong [ 10 ] ; //
bool bPing = true ; // Ping holds the latest polygon
vClipPing [ 0 ] = pInPoints [ 0 ] ; // Copy into Ping
vClipPing [ 1 ] = pInPoints [ 1 ] ;
vClipPing [ 2 ] = pInPoints [ 2 ] ;
vClipPing [ 3 ] = pInPoints [ 3 ] ;
int nNumPoints = 4 ;
for ( int i = 0 ; i < 6 ; i + + )
{
Vector vNormal ;
float flDist ;
if ( nNumPoints < 3 ) // If we're already clipped away, bail out entirely
break ;
Vector * pClipPolygon = pVecWorldFrustumPoints + ( 4 * i ) ; // Polygon defining clip plane
ComputeTrianglePlane ( pClipPolygon [ 0 ] , pClipPolygon [ 1 ] , pClipPolygon [ 2 ] , vNormal , flDist ) ; // Compute plane normal and dist
if ( bPing )
nNumPoints = ClipPolyToPlane ( vClipPing , nNumPoints , vClipPong , vNormal , flDist ) ; // Clip Ping into Pong
else
nNumPoints = ClipPolyToPlane ( vClipPong , nNumPoints , vClipPing , vNormal , flDist ) ; // Clip Pong into Ping
bPing = ! bPing ; // Flip buffers
}
if ( nNumPoints < 3 )
return 0 ;
if ( bPing )
memcpy ( pOutPoints , vClipPing , nNumPoints * sizeof ( Vector ) ) ;
else
memcpy ( pOutPoints , vClipPong , nNumPoints * sizeof ( Vector ) ) ;
return nNumPoints ;
}
void CShadowMgr : : SetStencilAndScissor ( IMatRenderContext * pRenderContext , FlashlightInfo_t & flashlightInfo , bool bUseStencil )
{
VMatrix matFlashlightToWorld ;
MatrixInverseGeneral ( m_Shadows [ flashlightInfo . m_Shadow ] . m_WorldToShadow , matFlashlightToWorld ) ;
// Eight points defining the frustum in Flashlight space
Vector vFrustumPoints [ 24 ] = { Vector ( 0.0f , 0.0f , 0.0f ) , Vector ( 1.0f , 0.0f , 0.0f ) , Vector ( 1.0f , 1.0f , 0.0f ) , Vector ( 0.0f , 1.0f , 0.0f ) , // Near
Vector ( 0.0f , 0.0f , 1.0f ) , Vector ( 0.0f , 1.0f , 1.0f ) , Vector ( 1.0f , 1.0f , 1.0f ) , Vector ( 1.0f , 0.0f , 1.0f ) , // Far
Vector ( 1.0f , 0.0f , 0.0f ) , Vector ( 1.0f , 0.0f , 1.0f ) , Vector ( 1.0f , 1.0f , 1.0f ) , Vector ( 1.0f , 1.0f , 0.0f ) , // Right
Vector ( 0.0f , 0.0f , 0.0f ) , Vector ( 0.0f , 1.0f , 0.0f ) , Vector ( 0.0f , 1.0f , 1.0f ) , Vector ( 0.0f , 0.0f , 1.0f ) , // Left
Vector ( 0.0f , 1.0f , 0.0f ) , Vector ( 1.0f , 1.0f , 0.0f ) , Vector ( 1.0f , 1.0f , 1.0f ) , Vector ( 0.0f , 1.0f , 1.0f ) , // Bottom
Vector ( 0.0f , 0.0f , 0.0f ) , Vector ( 0.0f , 0.0f , 1.0f ) , Vector ( 1.0f , 0.0f , 1.0f ) , Vector ( 1.0f , 0.0f , 0.0f ) } ; // Top
// Transform points to world space
Vector vWorldFrustumPoints [ 24 ] ;
for ( int i = 0 ; i < 24 ; i + + )
{
matFlashlightToWorld . V3Mul ( vFrustumPoints [ i ] , vWorldFrustumPoints [ i ] ) ;
}
// Express near and far planes of View frustum in world space
Frustum frustumPlanes ;
const float flPlaneEpsilon = 0.4f ;
ExtractFrustumPlanes ( frustumPlanes , flPlaneEpsilon ) ;
Vector vNearNormal = frustumPlanes [ FRUSTUM_NEARZ ] . m_Normal ;
Vector vFarNormal = frustumPlanes [ FRUSTUM_FARZ ] . m_Normal ;
float flNearDist = frustumPlanes [ FRUSTUM_NEARZ ] . m_Dist ;
float flFarDist = frustumPlanes [ FRUSTUM_FARZ ] . m_Dist ;
Vector vTempFace [ 5 ] ;
Vector vClippedFace [ 6 ] ;
Vector vClippedPolygons [ 8 ] [ 10 ] ; // Array of up to eight polygons (10 verts is more than enough for each)
int nNumVertices [ 8 ] ; // Number vertices on each of the of clipped polygons
int nNumPolygons = 0 ; // How many polygons have survived the clip
// Clip each face individually to near and far planes
for ( int i = 0 ; i < 6 ; i + + )
{
Vector * inVerts = vWorldFrustumPoints + ( 4 * i ) ; // Series of quadrilateral inputs
Vector * tempVerts = vTempFace ;
Vector * outVerts = vClippedFace ;
int nClipCount = ClipPolyToPlane ( inVerts , 4 , tempVerts , vNearNormal , flNearDist ) ; // need to set fOnPlaneEpsilon?
if ( nClipCount > 2 ) // If the polygon survived the near clip, try the far as well
{
nClipCount = ClipPolyToPlane ( tempVerts , nClipCount , outVerts , vFarNormal , flFarDist ) ; // need to set fOnPlaneEpsilon?
if ( nClipCount > 2 ) // If we still have a poly after clipping to both planes, add it to the list
{
memcpy ( vClippedPolygons [ nNumPolygons ] , outVerts , nClipCount * sizeof ( Vector ) ) ;
nNumVertices [ nNumPolygons ] = nClipCount ;
nNumPolygons + + ;
}
}
}
// Construct polygons for near and far planes
Vector vNearPlane [ 4 ] , vFarPlane [ 4 ] ;
ConstructNearAndFarPolygons ( vNearPlane , vFarPlane , flPlaneEpsilon ) ;
bool bNearPlane = false ;
// Clip near plane to flashlight frustum and tack on to list
int nClipCount = ClipPlaneToFrustum ( vNearPlane , vClippedPolygons [ nNumPolygons ] , vWorldFrustumPoints ) ;
if ( nClipCount > 2 ) // If the near plane clipped and resulted in a polygon, take note in the polygon list
{
nNumVertices [ nNumPolygons ] = nClipCount ;
nNumPolygons + + ;
bNearPlane = true ;
}
/*
TODO : do we even need to do the far plane ?
// Clip near plane to flashlight frustum and tack on to list
nClipCount = ClipPlaneToFrustum ( vFarPlane , vClippedPolygons [ nNumPolygons ] , vWorldFrustumPoints ) ;
if ( nClipCount > 2 ) // If the near plane clipped and resulted in a polygon, take note in the polygon list
{
nNumVertices [ nNumPolygons ] = nClipCount ;
nNumPolygons + + ;
}
*/
// Fuse positions of any verts which are within epsilon
for ( int i = 0 ; i < nNumPolygons ; i + + ) // For each polygon
{
for ( int j = 0 ; j < nNumVertices [ i ] ; j + + ) // For each vertex
{
for ( int k = i + 1 ; k < nNumPolygons ; k + + ) // For each later polygon
{
for ( int m = 0 ; m < nNumVertices [ k ] ; m + + ) // For each vertex
{
if ( SufficientlyClose ( vClippedPolygons [ i ] [ j ] , vClippedPolygons [ k ] [ m ] , 0.1f ) )
{
vClippedPolygons [ k ] [ m ] = vClippedPolygons [ i ] [ j ] ;
}
}
}
}
}
// Calculate scissoring rect
flashlightInfo . m_FlashlightState . m_bScissor = false ;
if ( r_flashlightscissor . GetBool ( ) & & ( nNumPolygons > 0 ) )
{
int nLeft , nTop , nRight , nBottom ;
flashlightInfo . m_FlashlightState . m_bScissor = ScreenSpaceRectFromPoints ( pRenderContext , vClippedPolygons , nNumVertices , nNumPolygons , & nLeft , & nTop , & nRight , & nBottom ) ;
if ( flashlightInfo . m_FlashlightState . m_bScissor )
{
flashlightInfo . m_FlashlightState . m_nLeft = nLeft ;
flashlightInfo . m_FlashlightState . m_nTop = nTop ;
flashlightInfo . m_FlashlightState . m_nRight = nRight ;
flashlightInfo . m_FlashlightState . m_nBottom = nBottom ;
}
}
if ( r_flashlightdrawclip . GetBool ( ) & & r_flashlightclip . GetBool ( ) & & bUseStencil )
{
// Draw back facing debug polygons
for ( int i = 0 ; i < nNumPolygons ; i + + )
{
DrawDebugPolygon ( nNumVertices [ i ] , vClippedPolygons [ i ] , false , false ) ;
}
/*
// Draw front facing debug polygons
for ( int i = 0 ; i < nNumPolygons ; i + + )
{
DrawDebugPolygon ( nNumVertices [ i ] , vClippedPolygons [ i ] , true , bNearPlane & & ( i = = nNumPolygons - 1 ) ) ;
}
*/
}
if ( r_flashlightclip . GetBool ( ) & & bUseStencil )
{
/*
// The traditional settings...
// Set up to set stencil bit on front facing polygons
pRenderContext - > SetStencilEnable ( true ) ;
pRenderContext - > SetStencilFailOperation ( STENCILOPERATION_KEEP ) ; // Stencil fails
pRenderContext - > SetStencilZFailOperation ( STENCILOPERATION_KEEP ) ; // Stencil passes but depth fails
pRenderContext - > SetStencilPassOperation ( STENCILOPERATION_REPLACE ) ; // Z and stencil both pass
pRenderContext - > SetStencilCompareFunction ( STENCILCOMPARISONFUNCTION_ALWAYS ) ; // Stencil always pass
pRenderContext - > SetStencilReferenceValue ( m_Shadows [ flashlightInfo . m_Shadow ] . m_ucShadowStencilBit ) ;
pRenderContext - > SetStencilTestMask ( m_Shadows [ flashlightInfo . m_Shadow ] . m_ucShadowStencilBit ) ;
pRenderContext - > SetStencilWriteMask ( m_Shadows [ flashlightInfo . m_Shadow ] . m_ucShadowStencilBit ) ; // Bit mask which is specific to this shadow
*/
// Just blast front faces into the stencil buffer no matter what...
pRenderContext - > SetStencilEnable ( true ) ;
pRenderContext - > SetStencilFailOperation ( STENCILOPERATION_REPLACE ) ; // Stencil fails
pRenderContext - > SetStencilZFailOperation ( STENCILOPERATION_REPLACE ) ; // Stencil passes but depth fails
pRenderContext - > SetStencilPassOperation ( STENCILOPERATION_REPLACE ) ; // Z and stencil both pass
pRenderContext - > SetStencilCompareFunction ( STENCILCOMPARISONFUNCTION_ALWAYS ) ; // Stencil always pass
pRenderContext - > SetStencilReferenceValue ( m_Shadows [ flashlightInfo . m_Shadow ] . m_ucShadowStencilBit ) ;
pRenderContext - > SetStencilTestMask ( m_Shadows [ flashlightInfo . m_Shadow ] . m_ucShadowStencilBit ) ;
pRenderContext - > SetStencilWriteMask ( m_Shadows [ flashlightInfo . m_Shadow ] . m_ucShadowStencilBit ) ; // Bit mask which is specific to this shadow
for ( int i = 0 ; i < nNumPolygons ; i + + ) // Set the stencil bit on front facing
{
DrawPolygonToStencil ( pRenderContext , nNumVertices [ i ] , vClippedPolygons [ i ] , true , false ) ;
}
/*
pRenderContext - > SetStencilReferenceValue ( 0x00000000 ) ; // All bits cleared
for ( int i = 0 ; i < nNumPolygons ; i + + ) // Clear the stencil bit on back facing
{
DrawPolygonToStencil ( nNumVertices [ i ] , vClippedPolygons [ i ] , false , false ) ;
}
*/
pRenderContext - > SetStencilEnable ( false ) ;
}
}
//---------------------------------------------------------------------------------------
// Set masking stencil bits for all flashlights
//---------------------------------------------------------------------------------------
void CShadowMgr : : SetFlashlightStencilMasks ( bool bDoMasking )
{
VPROF_BUDGET ( " CShadowMgr::RenderFlashlights " , VPROF_BUDGETGROUP_SHADOW_RENDERING ) ;
if ( IsX360 ( ) | | r_flashlight_version2 . GetInt ( ) )
return ;
// Bail out if we're not doing any of these optimizations
if ( ! ( r_flashlightclip . GetBool ( ) | | r_flashlightscissor . GetBool ( ) ) )
return ;
FlashlightHandle_t flashlightID = m_FlashlightStates . Head ( ) ;
if ( flashlightID = = m_FlashlightStates . InvalidIndex ( ) )
return ;
CMatRenderContextPtr pRenderContext ( materials ) ;
for ( ;
flashlightID ! = m_FlashlightStates . InvalidIndex ( ) ;
flashlightID = m_FlashlightStates . Next ( flashlightID ) )
{
FlashlightInfo_t & flashlightInfo = m_FlashlightStates [ flashlightID ] ;
SetStencilAndScissor ( pRenderContext , flashlightInfo , m_Shadows [ flashlightInfo . m_Shadow ] . m_pFlashlightDepthTexture ! = NULL ) ;
}
}
void CShadowMgr : : DisableStencilAndScissorMasking ( IMatRenderContext * pRenderContext )
{
if ( r_flashlightclip . GetBool ( ) )
{
pRenderContext - > SetStencilEnable ( false ) ;
}
// Scissor even if we're not shadow depth mapping
if ( r_flashlightscissor . GetBool ( ) )
{
pRenderContext - > SetScissorRect ( - 1 , - 1 , - 1 , - 1 , false ) ;
}
}
//---------------------------------------------------------------------------------------
// Enable/Disable masking based on stencil bit
//---------------------------------------------------------------------------------------
void CShadowMgr : : EnableStencilAndScissorMasking ( IMatRenderContext * pRenderContext , const FlashlightInfo_t & flashlightInfo , bool bDoMasking )
{
// Bail out if we're not doing any of these optimizations
if ( ! ( r_flashlightclip . GetBool ( ) | | r_flashlightscissor . GetBool ( ) ) | | ! bDoMasking )
return ;
// Only turn on scissor when rendering to the back buffer
if ( pRenderContext - > GetRenderTarget ( ) = = NULL )
{
// Only do the stencil optimization when shadow depth mapping
if ( r_flashlightclip . GetBool ( ) & & m_Shadows [ flashlightInfo . m_Shadow ] . m_pFlashlightDepthTexture ! = NULL )
{
unsigned char ucShadowStencilBit = m_Shadows [ flashlightInfo . m_Shadow ] . m_ucShadowStencilBit ;
pRenderContext - > SetStencilEnable ( true ) ;
pRenderContext - > SetStencilFailOperation ( STENCILOPERATION_KEEP ) ; // Stencil fails
pRenderContext - > SetStencilZFailOperation ( STENCILOPERATION_KEEP ) ; // Stencil passes but depth fails
pRenderContext - > SetStencilPassOperation ( STENCILOPERATION_KEEP ) ; // Z and stencil both pass
pRenderContext - > SetStencilCompareFunction ( STENCILCOMPARISONFUNCTION_EQUAL ) ; // Bit must be set
pRenderContext - > SetStencilReferenceValue ( ucShadowStencilBit ) ; // Specific bit
pRenderContext - > SetStencilTestMask ( ucShadowStencilBit ) ; // Specific bit
pRenderContext - > SetStencilWriteMask ( 0x00000000 ) ;
}
// Scissor even if we're not shadow depth mapping
if ( r_flashlightscissor . GetBool ( ) & & flashlightInfo . m_FlashlightState . m_bScissor )
{
pRenderContext - > SetScissorRect ( flashlightInfo . m_FlashlightState . m_nLeft , flashlightInfo . m_FlashlightState . m_nTop ,
flashlightInfo . m_FlashlightState . m_nRight , flashlightInfo . m_FlashlightState . m_nBottom , true ) ;
}
}
else // disable
{
DisableStencilAndScissorMasking ( pRenderContext ) ;
}
}
//---------------------------------------------------------------------------------------
// Sets the render states necessary to render a flashlight
//---------------------------------------------------------------------------------------
void CShadowMgr : : SetFlashlightRenderState ( ShadowHandle_t handle )
{
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
if ( handle = = SHADOW_HANDLE_INVALID )
{
pRenderContext - > SetFlashlightMode ( false ) ;
return ;
}
const Shadow_t & shadow = m_Shadows [ handle ] ;
pRenderContext - > SetFlashlightMode ( true ) ;
const FlashlightInfo_t & flashlightInfo = m_FlashlightStates [ shadow . m_FlashlightHandle ] ;
pRenderContext - > SetFlashlightStateEx ( flashlightInfo . m_FlashlightState , shadow . m_WorldToShadow , shadow . m_pFlashlightDepthTexture ) ;
}
//---------------------------------------------------------------------------------------
// Render all of the world and displacement surfaces that need to be drawn for flashlights
//---------------------------------------------------------------------------------------
void CShadowMgr : : RenderFlashlights ( bool bDoMasking , const VMatrix * pModelToWorld )
{
# ifndef SWDS
VPROF_BUDGET ( " CShadowMgr::RenderFlashlights " , VPROF_BUDGETGROUP_SHADOW_RENDERING ) ;
if ( IsX360 ( ) | | r_flashlight_version2 . GetInt ( ) )
return ;
if ( r_flashlightrender . GetBool ( ) = = false )
return ;
// Draw the projective light sources, which get their material
// from the surface and not from the shadow.
// Tell the materialsystem that we are drawing additive flashlight lighting.
FlashlightHandle_t flashlightID = m_FlashlightStates . Head ( ) ;
if ( flashlightID = = m_FlashlightStates . InvalidIndex ( ) )
return ;
bool bWireframe = r_shadowwireframe . GetBool ( ) ;
CMatRenderContextPtr pRenderContext ( materials ) ;
PIXEVENT ( pRenderContext , " CShadowMgr::RenderFlashlights " ) ;
pRenderContext - > SetFlashlightMode ( true ) ;
for ( ;
flashlightID ! = m_FlashlightStates . InvalidIndex ( ) ;
flashlightID = m_FlashlightStates . Next ( flashlightID ) )
{
FlashlightInfo_t & flashlightInfo = m_FlashlightStates [ flashlightID ] ;
CMaterialsBuckets < SurfaceHandle_t > & materialBuckets = flashlightInfo . m_MaterialBuckets ;
CMaterialsBuckets < SurfaceHandle_t > : : SortIDHandle_t sortIDHandle = materialBuckets . GetFirstUsedSortID ( ) ;
if ( sortIDHandle = = materialBuckets . InvalidSortIDHandle ( ) )
continue ;
pRenderContext - > SetFlashlightStateEx ( flashlightInfo . m_FlashlightState , m_Shadows [ flashlightInfo . m_Shadow ] . m_WorldToShadow , m_Shadows [ flashlightInfo . m_Shadow ] . m_pFlashlightDepthTexture ) ;
EnableStencilAndScissorMasking ( pRenderContext , flashlightInfo , bDoMasking ) ;
for ( ; sortIDHandle ! = materialBuckets . InvalidSortIDHandle ( ) ;
sortIDHandle = materialBuckets . GetNextUsedSortID ( sortIDHandle ) )
{
int sortID = materialBuckets . GetSortID ( sortIDHandle ) ;
if ( bWireframe )
{
pRenderContext - > Bind ( g_materialWorldWireframe ) ;
}
else
{
pRenderContext - > Bind ( materialSortInfoArray [ sortID ] . material ) ;
pRenderContext - > BindLightmapPage ( materialSortInfoArray [ sortID ] . lightmapPageID ) ;
}
CMaterialsBuckets < SurfaceHandle_t > : : ElementHandle_t elemHandle ;
// Figure out how many indices we have.
int numIndices = 0 ;
for ( elemHandle = materialBuckets . GetElementListHead ( sortID ) ;
elemHandle ! = materialBuckets . InvalidElementHandle ( ) ;
elemHandle = materialBuckets . GetElementListNext ( elemHandle ) )
{
SurfaceHandle_t surfID = materialBuckets . GetElement ( elemHandle ) ;
if ( ! SurfaceHasDispInfo ( surfID ) )
{
numIndices + = 3 * ( MSurf_VertCount ( surfID ) - 2 ) ;
}
}
if ( numIndices > 0 )
{
// NOTE: If we ever need to make this faster, we could get larger
// batches here.
// Draw this batch.
# if NEWMESH
IIndexBuffer * pIndexBuffer = pRenderContext - > GetDynamicIndexBuffer ( MATERIAL_INDEX_FORMAT_16BIT ) ;
CIndexBufferBuilder indexBufferBuilder ;
indexBufferBuilder . Begin ( pIndexBuffer , numIndices ) ;
# else
IMesh * pMesh = pRenderContext - > GetDynamicMesh ( false , g_WorldStaticMeshes [ sortID ] , 0 ) ;
CMeshBuilder meshBuilder ;
meshBuilder . Begin ( pMesh , MATERIAL_TRIANGLES , 0 , numIndices ) ;
# endif
for ( elemHandle = materialBuckets . GetElementListHead ( sortID ) ;
elemHandle ! = materialBuckets . InvalidElementHandle ( ) ;
elemHandle = materialBuckets . GetElementListNext ( elemHandle ) )
{
SurfaceHandle_t surfID = materialBuckets . GetElement ( elemHandle ) ;
if ( ! SurfaceHasDispInfo ( surfID ) )
{
# if NEWMESH
BuildIndicesForWorldSurface ( indexBufferBuilder , surfID , host_state . worldbrush ) ;
# else
BuildIndicesForWorldSurface ( meshBuilder , surfID , host_state . worldbrush ) ;
# endif
}
}
// close out the index buffer
# if NEWMESH
indexBufferBuilder . End ( false ) ; // haven't tested this one yet (flashlights)
// FIXME: IMaterial::GetVertexFormat() should do this stripping (add a separate 'SupportsCompression' accessor)
VertexFormat_t vertexFormat = materialSortInfoArray [ sortID ] . material - > GetVertexFormat ( ) & ~ VERTEX_FORMAT_COMPRESSED ;
pRenderContext - > BindVertexBuffer ( 0 , g_WorldStaticMeshes [ sortID ] , 0 , materialSortInfoArray [ sortID ] . material - > GetVertexFormat ( ) ) ; // hack fixme. . . use currently bound material format instead of passing in?
pRenderContext - > BindIndexBuffer ( pIndexBuffer , 0 ) ;
pRenderContext - > Draw ( MATERIAL_TRIANGLES , 0 , numIndices ) ;
# else
meshBuilder . End ( false , true ) ;
# endif
}
// NOTE: If we ever need to make this faster, we could get larger batches here.
// Draw displacements
for ( elemHandle = materialBuckets . GetElementListHead ( sortID ) ;
elemHandle ! = materialBuckets . InvalidElementHandle ( ) ;
elemHandle = materialBuckets . GetElementListNext ( elemHandle ) )
{
SurfaceHandle_t surfID = materialBuckets . GetElement ( elemHandle ) ;
if ( SurfaceHasDispInfo ( surfID ) )
{
CDispInfo * pDisp = ( CDispInfo * ) MSurf_DispInfo ( surfID ) ;
Assert ( pDisp ) ;
if ( bWireframe )
{
pDisp - > SpecifyDynamicMesh ( ) ;
}
else
{
Assert ( pDisp & & pDisp - > m_pMesh & & pDisp - > m_pMesh - > m_pMesh ) ;
pDisp - > m_pMesh - > m_pMesh - > Draw ( pDisp - > m_iIndexOffset , pDisp - > m_nIndices ) ;
}
}
}
}
}
// Tell the materialsystem that we are finished drawing additive flashlight lighting.
pRenderContext - > SetFlashlightMode ( false ) ;
// Turn off stencil masking
DisableStencilAndScissorMasking ( pRenderContext ) ;
# endif
}
const Frustum_t & CShadowMgr : : GetFlashlightFrustum ( ShadowHandle_t handle )
{
Assert ( m_Shadows [ handle ] . m_Flags & SHADOW_FLASHLIGHT ) ;
Assert ( m_Shadows [ handle ] . m_FlashlightHandle ! = m_Shadows . InvalidIndex ( ) ) ;
return m_FlashlightStates [ m_Shadows [ handle ] . m_FlashlightHandle ] . m_Frustum ;
}
const FlashlightState_t & CShadowMgr : : GetFlashlightState ( ShadowHandle_t handle )
{
Assert ( m_Shadows [ handle ] . m_Flags & SHADOW_FLASHLIGHT ) ;
Assert ( m_Shadows [ handle ] . m_FlashlightHandle ! = m_Shadows . InvalidIndex ( ) ) ;
return m_FlashlightStates [ m_Shadows [ handle ] . m_FlashlightHandle ] . m_FlashlightState ;
}
void CShadowMgr : : DrawFlashlightDecals ( int sortGroup , bool bDoMasking )
{
VPROF_BUDGET ( " CShadowMgr::DrawFlashlightDecals " , VPROF_BUDGETGROUP_SHADOW_RENDERING ) ;
if ( IsX360 ( ) | | r_flashlight_version2 . GetInt ( ) )
return ;
FlashlightHandle_t flashlightID = m_FlashlightStates . Head ( ) ;
if ( flashlightID = = m_FlashlightStates . InvalidIndex ( ) )
return ;
CMatRenderContextPtr pRenderContext ( materials ) ;
pRenderContext - > SetFlashlightMode ( true ) ;
for ( ;
flashlightID ! = m_FlashlightStates . InvalidIndex ( ) ;
flashlightID = m_FlashlightStates . Next ( flashlightID ) )
{
FlashlightInfo_t & flashlightInfo = m_FlashlightStates [ flashlightID ] ;
pRenderContext - > SetFlashlightState ( flashlightInfo . m_FlashlightState , m_Shadows [ flashlightInfo . m_Shadow ] . m_WorldToShadow ) ;
EnableStencilAndScissorMasking ( pRenderContext , flashlightInfo , bDoMasking ) ;
DecalSurfaceDraw ( pRenderContext , sortGroup ) ;
}
// Tell the materialsystem that we are finished drawing additive flashlight lighting.
pRenderContext - > SetFlashlightMode ( false ) ;
// Turn off stencil masking
DisableStencilAndScissorMasking ( pRenderContext ) ;
}
void CShadowMgr : : DrawFlashlightDecalsOnDisplacements ( int sortGroup , CDispInfo * visibleDisps [ MAX_MAP_DISPINFO ] , int nVisibleDisps , bool bDoMasking )
{
VPROF_BUDGET ( " CShadowMgr::DrawFlashlightDecalsOnDisplacements " , VPROF_BUDGETGROUP_SHADOW_RENDERING ) ;
if ( IsX360 ( ) | | r_flashlight_version2 . GetInt ( ) )
return ;
FlashlightHandle_t flashlightID = m_FlashlightStates . Head ( ) ;
if ( flashlightID = = m_FlashlightStates . InvalidIndex ( ) )
return ;
CMatRenderContextPtr pRenderContext ( materials ) ;
pRenderContext - > SetFlashlightMode ( true ) ;
DispInfo_BatchDecals ( visibleDisps , nVisibleDisps ) ;
for ( ;
flashlightID ! = m_FlashlightStates . InvalidIndex ( ) ;
flashlightID = m_FlashlightStates . Next ( flashlightID ) )
{
FlashlightInfo_t & flashlightInfo = m_FlashlightStates [ flashlightID ] ;
pRenderContext - > SetFlashlightState ( flashlightInfo . m_FlashlightState , m_Shadows [ flashlightInfo . m_Shadow ] . m_WorldToShadow ) ;
EnableStencilAndScissorMasking ( pRenderContext , flashlightInfo , bDoMasking ) ;
DispInfo_DrawDecals ( visibleDisps , nVisibleDisps ) ;
}
// Tell the materialsystem that we are finished drawing additive flashlight lighting.
pRenderContext - > SetFlashlightMode ( false ) ;
// Turn off stencil masking
DisableStencilAndScissorMasking ( pRenderContext ) ;
}
void CShadowMgr : : DrawFlashlightDecalsOnSingleSurface ( SurfaceHandle_t surfID , bool bDoMasking )
{
VPROF_BUDGET ( " CShadowMgr::DrawFlashlightDecalsOnSingleSurface " , VPROF_BUDGETGROUP_SHADOW_RENDERING ) ;
if ( IsX360 ( ) | | r_flashlight_version2 . GetInt ( ) )
return ;
FlashlightHandle_t flashlightID = m_FlashlightStates . Head ( ) ;
if ( flashlightID = = m_FlashlightStates . InvalidIndex ( ) )
return ;
CMatRenderContextPtr pRenderContext ( materials ) ;
pRenderContext - > SetFlashlightMode ( true ) ;
for ( ;
flashlightID ! = m_FlashlightStates . InvalidIndex ( ) ;
flashlightID = m_FlashlightStates . Next ( flashlightID ) )
{
FlashlightInfo_t & flashlightInfo = m_FlashlightStates [ flashlightID ] ;
pRenderContext - > SetFlashlightState ( flashlightInfo . m_FlashlightState , m_Shadows [ flashlightInfo . m_Shadow ] . m_WorldToShadow ) ;
EnableStencilAndScissorMasking ( pRenderContext , flashlightInfo , bDoMasking ) ;
DrawDecalsOnSingleSurface ( pRenderContext , surfID ) ;
}
// Tell the materialsystem that we are finished drawing additive flashlight lighting.
pRenderContext - > SetFlashlightMode ( false ) ;
// Turn off stencil masking
DisableStencilAndScissorMasking ( pRenderContext ) ;
}
void CShadowMgr : : DrawFlashlightOverlays ( int nSortGroup , bool bDoMasking )
{
VPROF_BUDGET ( " CShadowMgr::DrawFlashlightOverlays " , VPROF_BUDGETGROUP_SHADOW_RENDERING ) ;
if ( IsX360 ( ) | | r_flashlight_version2 . GetInt ( ) )
return ;
FlashlightHandle_t flashlightID = m_FlashlightStates . Head ( ) ;
if ( flashlightID = = m_FlashlightStates . InvalidIndex ( ) )
return ;
if ( r_flashlightrender . GetBool ( ) = = false )
return ;
CMatRenderContextPtr pRenderContext ( materials ) ;
pRenderContext - > SetFlashlightMode ( true ) ;
for ( ;
flashlightID ! = m_FlashlightStates . InvalidIndex ( ) ;
flashlightID = m_FlashlightStates . Next ( flashlightID ) )
{
FlashlightInfo_t & flashlightInfo = m_FlashlightStates [ flashlightID ] ;
pRenderContext - > SetFlashlightState ( flashlightInfo . m_FlashlightState , m_Shadows [ flashlightInfo . m_Shadow ] . m_WorldToShadow ) ;
EnableStencilAndScissorMasking ( pRenderContext , flashlightInfo , bDoMasking ) ;
OverlayMgr ( ) - > RenderOverlays ( nSortGroup ) ;
}
// Tell the materialsystem that we are finished drawing additive flashlight lighting.
pRenderContext - > SetFlashlightMode ( false ) ;
// Turn off stencil masking
DisableStencilAndScissorMasking ( pRenderContext ) ;
}
void CShadowMgr : : DrawFlashlightDepthTexture ( )
{
int i = 0 ;
FlashlightHandle_t flashlightID = m_FlashlightStates . Head ( ) ;
while ( flashlightID ! = m_FlashlightStates . InvalidIndex ( ) ) // Count up the shadows
{
FlashlightInfo_t & flashlightInfo = m_FlashlightStates [ flashlightID ] ;
if ( m_Shadows [ flashlightInfo . m_Shadow ] . m_pFlashlightDepthTexture )
{
bool foundVar ;
IMaterial * pMaterial = materials - > FindMaterial ( " debug/showz " , TEXTURE_GROUP_OTHER , true ) ;
IMaterialVar * BaseTextureVar = pMaterial - > FindVar ( " $basetexture " , & foundVar , false ) ;
if ( ! foundVar )
return ;
IMaterialVar * FrameVar = pMaterial - > FindVar ( " $frame " , & foundVar , false ) ;
if ( ! foundVar )
return ;
float w = 256.0f , h = 256.0f ;
float wOffset = ( i % 2 ) * 256.0f ; // Even|Odd go left|right
float hOffset = ( i / 2 ) * 256.0f ; // Rows of two
BaseTextureVar - > SetTextureValue ( m_Shadows [ flashlightInfo . m_Shadow ] . m_pFlashlightDepthTexture ) ;
FrameVar - > SetIntValue ( 0 ) ;
CMatRenderContextPtr pRenderContext ( materials ) ;
pRenderContext - > Bind ( pMaterial ) ;
IMesh * pMesh = pRenderContext - > GetDynamicMesh ( true ) ;
CMeshBuilder meshBuilder ;
meshBuilder . Begin ( pMesh , MATERIAL_QUADS , 1 ) ;
meshBuilder . Position3f ( wOffset , hOffset , 0.0f ) ;
# ifdef DX_TO_GL_ABSTRACTION
meshBuilder . TexCoord2f ( 0 , 0.0f , 1.0f ) ; // Posix is rotated due to render target origin differences
# else
meshBuilder . TexCoord2f ( 0 , 0.0f , 0.0f ) ;
# endif
meshBuilder . AdvanceVertex ( ) ;
meshBuilder . Position3f ( wOffset + w , hOffset , 0.0f ) ;
# ifdef DX_TO_GL_ABSTRACTION
meshBuilder . TexCoord2f ( 0 , 0.0f , 0.0f ) ;
# else
meshBuilder . TexCoord2f ( 0 , 1.0f , 0.0f ) ;
# endif
meshBuilder . AdvanceVertex ( ) ;
meshBuilder . Position3f ( wOffset + w , hOffset + h , 0.0f ) ;
# ifdef DX_TO_GL_ABSTRACTION
meshBuilder . TexCoord2f ( 0 , 1.0f , 0.0f ) ;
# else
meshBuilder . TexCoord2f ( 0 , 1.0f , 1.0f ) ;
# endif
meshBuilder . AdvanceVertex ( ) ;
meshBuilder . Position3f ( wOffset , hOffset + h , 0.0f ) ;
# ifdef DX_TO_GL_ABSTRACTION
meshBuilder . TexCoord2f ( 0 , 1.0f , 1.0f ) ;
# else
meshBuilder . TexCoord2f ( 0 , 0.0f , 1.0f ) ;
# endif
meshBuilder . AdvanceVertex ( ) ;
meshBuilder . End ( ) ;
pMesh - > Draw ( ) ;
i + + ;
}
flashlightID = m_FlashlightStates . Next ( flashlightID ) ;
}
}
void CShadowMgr : : AddFlashlightRenderable ( ShadowHandle_t shadowHandle , IClientRenderable * pRenderable )
{
Shadow_t & shadow = m_Shadows [ shadowHandle ] ;
FlashlightInfo_t & flashlightInfo = m_FlashlightStates [ shadow . m_FlashlightHandle ] ;
if ( pRenderable - > GetModelInstance ( ) ! = MODEL_INSTANCE_INVALID )
{
flashlightInfo . m_Renderables . AddToTail ( pRenderable ) ;
}
}