2012-11-26 18:58:24 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Doom 3 BFG Edition GPL Source Code
2012-11-28 15:47:07 +00:00
Copyright ( C ) 1993 - 2012 id Software LLC , a ZeniMax Media company .
2012-11-26 18:58:24 +00:00
2012-11-28 15:47:07 +00:00
This file is part of the Doom 3 BFG Edition GPL Source Code ( " Doom 3 BFG Edition Source Code " ) .
2012-11-26 18:58:24 +00:00
Doom 3 BFG Edition Source Code is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code . If not , see < http : //www.gnu.org/licenses/>.
In addition , the Doom 3 BFG Edition Source Code is also subject to certain additional terms . You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code . If not , please request a copy in writing from id Software at the address below .
If you have questions concerning this license or the applicable additional terms , you may contact in writing id Software LLC , c / o ZeniMax Media Inc . , Suite 120 , Rockville , Maryland 20850 USA .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# pragma hdrstop
2012-12-22 15:18:19 +00:00
# include "precompiled.h"
2012-11-26 18:58:24 +00:00
# include "tr_local.h"
extern idCVar r_useAreasConnectedForShadowCulling ;
extern idCVar r_useParallelAddShadows ;
extern idCVar r_forceShadowCaps ;
extern idCVar r_useShadowPreciseInsideTest ;
idCVar r_useAreasConnectedForShadowCulling ( " r_useAreasConnectedForShadowCulling " , " 2 " , CVAR_RENDERER | CVAR_INTEGER , " cull entities cut off by doors " ) ;
idCVar r_useParallelAddLights ( " r_useParallelAddLights " , " 1 " , CVAR_RENDERER | CVAR_BOOL , " aadd all lights in parallel with jobs " ) ;
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = =
R_ShadowBounds
Even though the extruded shadows are drawn projected to infinity , their effects are limited
to a fraction of the light ' s volume . An extruded box would require 12 faces to specify and
be a lot of trouble , but an axial bounding box is quick and easy to determine .
If the light is completely contained in the view , there is no value in trying to cull the
shadows , as they will all pass .
Pure function .
= = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void R_ShadowBounds ( const idBounds & modelBounds , const idBounds & lightBounds , const idVec3 & lightOrigin , idBounds & shadowBounds )
{
for ( int i = 0 ; i < 3 ; i + + )
{
2012-11-26 18:58:24 +00:00
shadowBounds [ 0 ] [ i ] = __fsels ( modelBounds [ 0 ] [ i ] - lightOrigin [ i ] , modelBounds [ 0 ] [ i ] , lightBounds [ 0 ] [ i ] ) ;
shadowBounds [ 1 ] [ i ] = __fsels ( lightOrigin [ i ] - modelBounds [ 1 ] [ i ] , modelBounds [ 1 ] [ i ] , lightBounds [ 1 ] [ i ] ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = =
idRenderEntityLocal : : IsDirectlyVisible ( )
= = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idRenderEntityLocal : : IsDirectlyVisible ( ) const
{
if ( viewCount ! = tr . viewCount )
{
2012-11-26 18:58:24 +00:00
return false ;
}
2012-11-28 15:47:07 +00:00
if ( viewEntity - > scissorRect . IsEmpty ( ) )
{
2012-11-26 18:58:24 +00:00
// a viewEntity was created for shadow generation, but the
// model global reference bounds isn't directly visible
return false ;
}
return true ;
}
/*
= = = = = = = = = = = = = = = = = = =
R_AddSingleLight
May be run in parallel .
Sets vLight - > removeFromList to true if the light should be removed from the list .
Builds a chain of entities that need to be added for shadows only off vLight - > shadowOnlyViewEntities .
Allocates and fills in vLight - > entityInteractionState .
Calc the light shader values , removing any light from the viewLight list
if it is determined to not have any visible effect due to being flashed off or turned off .
Add any precomputed shadow volumes .
= = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
static void R_AddSingleLight ( viewLight_t * vLight )
{
2012-11-26 18:58:24 +00:00
// until proven otherwise
vLight - > removeFromList = true ;
vLight - > shadowOnlyViewEntities = NULL ;
vLight - > preLightShadowVolumes = NULL ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// globals we really should pass in...
2012-11-28 15:47:07 +00:00
const viewDef_t * viewDef = tr . viewDef ;
const idRenderLightLocal * light = vLight - > lightDef ;
const idMaterial * lightShader = light - > lightShader ;
if ( lightShader = = NULL )
{
2012-11-26 18:58:24 +00:00
common - > Error ( " R_AddSingleLight: NULL lightShader " ) ;
return ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
SCOPED_PROFILE_EVENT ( lightShader - > GetName ( ) ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// see if we are suppressing the light in this view
2012-11-28 15:47:07 +00:00
if ( ! r_skipSuppress . GetBool ( ) )
{
if ( light - > parms . suppressLightInViewID & & light - > parms . suppressLightInViewID = = viewDef - > renderView . viewID )
{
2012-11-26 18:58:24 +00:00
return ;
}
2012-11-28 15:47:07 +00:00
if ( light - > parms . allowLightInViewID & & light - > parms . allowLightInViewID ! = viewDef - > renderView . viewID )
{
2012-11-26 18:58:24 +00:00
return ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// evaluate the light shader registers
2012-11-28 15:47:07 +00:00
float * lightRegs = ( float * ) R_FrameAlloc ( lightShader - > GetNumRegisters ( ) * sizeof ( float ) , FRAME_ALLOC_SHADER_REGISTER ) ;
lightShader - > EvaluateRegisters ( lightRegs , light - > parms . shaderParms , viewDef - > renderView . shaderParms ,
tr . viewDef - > renderView . time [ 0 ] * 0.001f , light - > parms . referenceSound ) ;
2012-11-26 18:58:24 +00:00
// if this is a purely additive light and no stage in the light shader evaluates
// to a positive light value, we can completely skip the light
2012-11-28 15:47:07 +00:00
if ( ! lightShader - > IsFogLight ( ) & & ! lightShader - > IsBlendLight ( ) )
{
2012-11-26 18:58:24 +00:00
int lightStageNum ;
2012-11-28 15:47:07 +00:00
for ( lightStageNum = 0 ; lightStageNum < lightShader - > GetNumStages ( ) ; lightStageNum + + )
{
const shaderStage_t * lightStage = lightShader - > GetStage ( lightStageNum ) ;
2012-11-26 18:58:24 +00:00
// ignore stages that fail the condition
2012-11-28 15:47:07 +00:00
if ( ! lightRegs [ lightStage - > conditionRegister ] )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2012-11-28 15:47:07 +00:00
const int * registers = lightStage - > color . registers ;
2012-11-26 18:58:24 +00:00
// snap tiny values to zero
2012-11-28 15:47:07 +00:00
if ( lightRegs [ registers [ 0 ] ] < 0.001f )
{
2012-11-26 18:58:24 +00:00
lightRegs [ registers [ 0 ] ] = 0.0f ;
}
2012-11-28 15:47:07 +00:00
if ( lightRegs [ registers [ 1 ] ] < 0.001f )
{
2012-11-26 18:58:24 +00:00
lightRegs [ registers [ 1 ] ] = 0.0f ;
}
2012-11-28 15:47:07 +00:00
if ( lightRegs [ registers [ 2 ] ] < 0.001f )
{
2012-11-26 18:58:24 +00:00
lightRegs [ registers [ 2 ] ] = 0.0f ;
}
2012-11-28 15:47:07 +00:00
if ( lightRegs [ registers [ 0 ] ] > 0.0f | |
2012-11-26 18:58:24 +00:00
lightRegs [ registers [ 1 ] ] > 0.0f | |
2012-11-28 15:47:07 +00:00
lightRegs [ registers [ 2 ] ] > 0.0f )
{
2012-11-26 18:58:24 +00:00
break ;
}
}
2012-11-28 15:47:07 +00:00
if ( lightStageNum = = lightShader - > GetNumStages ( ) )
{
2012-11-26 18:58:24 +00:00
// we went through all the stages and didn't find one that adds anything
// remove the light from the viewLights list, and change its frame marker
// so interaction generation doesn't think the light is visible and
// create a shadow for it
return ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
//--------------------------------------------
// copy data used by backend
//--------------------------------------------
vLight - > globalLightOrigin = light - > globalLightOrigin ;
vLight - > lightProject [ 0 ] = light - > lightProject [ 0 ] ;
vLight - > lightProject [ 1 ] = light - > lightProject [ 1 ] ;
vLight - > lightProject [ 2 ] = light - > lightProject [ 2 ] ;
vLight - > lightProject [ 3 ] = light - > lightProject [ 3 ] ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// the fog plane is the light far clip plane
idPlane fogPlane ( light - > baseLightProject [ 2 ] [ 0 ] - light - > baseLightProject [ 3 ] [ 0 ] ,
light - > baseLightProject [ 2 ] [ 1 ] - light - > baseLightProject [ 3 ] [ 1 ] ,
light - > baseLightProject [ 2 ] [ 2 ] - light - > baseLightProject [ 3 ] [ 2 ] ,
light - > baseLightProject [ 2 ] [ 3 ] - light - > baseLightProject [ 3 ] [ 3 ] ) ;
const float planeScale = idMath : : InvSqrt ( fogPlane . Normal ( ) . LengthSqr ( ) ) ;
vLight - > fogPlane [ 0 ] = fogPlane [ 0 ] * planeScale ;
vLight - > fogPlane [ 1 ] = fogPlane [ 1 ] * planeScale ;
vLight - > fogPlane [ 2 ] = fogPlane [ 2 ] * planeScale ;
vLight - > fogPlane [ 3 ] = fogPlane [ 3 ] * planeScale ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// copy the matrix for deforming the 'zeroOneCubeModel' to exactly cover the light volume in world space
vLight - > inverseBaseLightProject = light - > inverseBaseLightProject ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
vLight - > falloffImage = light - > falloffImage ;
vLight - > lightShader = light - > lightShader ;
vLight - > shaderRegisters = lightRegs ;
2012-11-28 15:47:07 +00:00
if ( r_useLightScissors . GetInteger ( ) ! = 0 )
{
2012-11-26 18:58:24 +00:00
// Calculate the matrix that projects the zero-to-one cube to exactly cover the
// light frustum in clip space.
idRenderMatrix invProjectMVPMatrix ;
idRenderMatrix : : Multiply ( viewDef - > worldSpace . mvp , light - > inverseBaseLightProject , invProjectMVPMatrix ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Calculate the projected bounds, either not clipped at all, near clipped, or fully clipped.
idBounds projected ;
2012-11-28 15:47:07 +00:00
if ( r_useLightScissors . GetInteger ( ) = = 1 )
{
2012-11-26 18:58:24 +00:00
idRenderMatrix : : ProjectedBounds ( projected , invProjectMVPMatrix , bounds_zeroOneCube ) ;
2012-11-28 15:47:07 +00:00
}
else if ( r_useLightScissors . GetInteger ( ) = = 2 )
{
2012-11-26 18:58:24 +00:00
idRenderMatrix : : ProjectedNearClippedBounds ( projected , invProjectMVPMatrix , bounds_zeroOneCube ) ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
idRenderMatrix : : ProjectedFullyClippedBounds ( projected , invProjectMVPMatrix , bounds_zeroOneCube ) ;
}
2012-11-28 15:47:07 +00:00
if ( projected [ 0 ] [ 2 ] > = projected [ 1 ] [ 2 ] )
{
2012-11-26 18:58:24 +00:00
// the light was culled to the view frustum
return ;
}
2012-11-28 15:47:07 +00:00
float screenWidth = ( float ) viewDef - > viewport . x2 - ( float ) viewDef - > viewport . x1 ;
float screenHeight = ( float ) viewDef - > viewport . y2 - ( float ) viewDef - > viewport . y1 ;
2012-11-26 18:58:24 +00:00
idScreenRect lightScissorRect ;
lightScissorRect . x1 = idMath : : Ftoi ( projected [ 0 ] [ 0 ] * screenWidth ) ;
lightScissorRect . x2 = idMath : : Ftoi ( projected [ 1 ] [ 0 ] * screenWidth ) ;
lightScissorRect . y1 = idMath : : Ftoi ( projected [ 0 ] [ 1 ] * screenHeight ) ;
lightScissorRect . y2 = idMath : : Ftoi ( projected [ 1 ] [ 1 ] * screenHeight ) ;
lightScissorRect . Expand ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
vLight - > scissorRect . Intersect ( lightScissorRect ) ;
vLight - > scissorRect . zmin = projected [ 0 ] [ 2 ] ;
vLight - > scissorRect . zmax = projected [ 1 ] [ 2 ] ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// this one stays on the list
vLight - > removeFromList = false ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
//--------------------------------------------
// create interactions with all entities the light may touch, and add viewEntities
// that may cast shadows, even if they aren't directly visible. Any real work
// will be deferred until we walk through the viewEntities
//--------------------------------------------
const int renderViewID = viewDef - > renderView . viewID ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// this bool array will be set true whenever the entity will visibly interact with the light
2012-11-28 15:47:07 +00:00
vLight - > entityInteractionState = ( byte * ) R_ClearedFrameAlloc ( light - > world - > entityDefs . Num ( ) * sizeof ( vLight - > entityInteractionState [ 0 ] ) , FRAME_ALLOC_INTERACTION_STATE ) ;
2012-11-26 18:58:24 +00:00
const bool lightCastsShadows = light - > LightCastsShadows ( ) ;
idInteraction * * const interactionTableRow = light - > world - > interactionTable + light - > index * light - > world - > interactionTableWidth ;
2012-11-28 15:47:07 +00:00
for ( areaReference_t * lref = light - > references ; lref ! = NULL ; lref = lref - > ownerNext )
{
portalArea_t * area = lref - > area ;
2012-11-26 18:58:24 +00:00
// some lights have their center of projection outside the world, but otherwise
// we want to ignore areas that are not connected to the light center due to a closed door
2012-11-28 15:47:07 +00:00
if ( light - > areaNum ! = - 1 & & r_useAreasConnectedForShadowCulling . GetInteger ( ) = = 2 )
{
if ( ! light - > world - > AreasAreConnected ( light - > areaNum , area - > areaNum , PS_BLOCK_VIEW ) )
{
2012-11-26 18:58:24 +00:00
// can't possibly be seen or shadowed
continue ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// check all the models in this area
2012-11-28 15:47:07 +00:00
for ( areaReference_t * eref = area - > entityRefs . areaNext ; eref ! = & area - > entityRefs ; eref = eref - > areaNext )
{
idRenderEntityLocal * edef = eref - > entity ;
if ( vLight - > entityInteractionState [ edef - > index ] ! = viewLight_t : : INTERACTION_UNCHECKED )
{
2012-11-26 18:58:24 +00:00
continue ;
}
// until proven otherwise
vLight - > entityInteractionState [ edef - > index ] = viewLight_t : : INTERACTION_NO ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// The table is updated at interaction::AllocAndLink() and interaction::UnlinkAndFree()
2012-11-28 15:47:07 +00:00
const idInteraction * inter = interactionTableRow [ edef - > index ] ;
const renderEntity_t & eParms = edef - > parms ;
const idRenderModel * eModel = eParms . hModel ;
2012-11-26 18:58:24 +00:00
// a large fraction of static entity / light pairs will still have no interactions even though
// they are both present in the same area(s)
2012-11-28 15:47:07 +00:00
if ( eModel ! = NULL & & ! eModel - > IsDynamicModel ( ) & & inter = = INTERACTION_EMPTY )
{
2012-11-26 18:58:24 +00:00
// the interaction was statically checked, and it didn't generate any surfaces,
// so there is no need to force the entity onto the view list if it isn't
// already there
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// We don't want the lights on weapons to illuminate anything else.
// There are two assumptions here -- that allowLightInViewID is only
// used for weapon lights, and that all weapons will have weaponDepthHack.
// A more general solution would be to have an allowLightOnEntityID field.
// HACK: the armor-mounted flashlight is a private spot light, which is probably
// wrong -- you would expect to see them in multiplayer.
2012-11-28 15:47:07 +00:00
if ( light - > parms . allowLightInViewID & & light - > parms . pointLight & & ! eParms . weaponDepthHack )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// non-shadow casting entities don't need to be added if they aren't
// directly visible
2012-11-28 15:47:07 +00:00
if ( ( eParms . noShadow | | ( eModel & & ! eModel - > ModelHasShadowCastingSurfaces ( ) ) ) & & ! edef - > IsDirectlyVisible ( ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// if the model doesn't accept lighting or cast shadows, it doesn't need to be added
2012-11-28 15:47:07 +00:00
if ( eModel & & ! eModel - > ModelHasInteractingSurfaces ( ) & & ! eModel - > ModelHasShadowCastingSurfaces ( ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// no interaction present, so either the light or entity has moved
// assert( lightHasMoved || edef->entityHasMoved );
2012-11-28 15:47:07 +00:00
if ( inter = = NULL )
{
2012-11-26 18:58:24 +00:00
// some big outdoor meshes are flagged to not create any dynamic interactions
// when the level designer knows that nearby moving lights shouldn't actually hit them
2012-11-28 15:47:07 +00:00
if ( eParms . noDynamicInteractions )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// do a check of the entity reference bounds against the light frustum to see if they can't
// possibly interact, despite sharing one or more world areas
2012-11-28 15:47:07 +00:00
if ( R_CullModelBoundsToLight ( light , edef - > localReferenceBounds , edef - > modelRenderMatrix ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// we now know that the entity and light do overlap
2012-11-28 15:47:07 +00:00
if ( edef - > IsDirectlyVisible ( ) )
{
2012-11-26 18:58:24 +00:00
// entity is directly visible, so the interaction is definitely needed
vLight - > entityInteractionState [ edef - > index ] = viewLight_t : : INTERACTION_YES ;
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// the entity is not directly visible, but if we can tell that it may cast
// shadows onto visible surfaces, we must make a viewEntity for it
2012-11-28 15:47:07 +00:00
if ( ! lightCastsShadows )
{
2012-11-26 18:58:24 +00:00
// surfaces are never shadowed in this light
continue ;
}
// if we are suppressing its shadow in this view (player shadows, etc), skip
2012-11-28 15:47:07 +00:00
if ( ! r_skipSuppress . GetBool ( ) )
{
if ( eParms . suppressShadowInViewID & & eParms . suppressShadowInViewID = = renderViewID )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2012-11-28 15:47:07 +00:00
if ( eParms . suppressShadowInLightID & & eParms . suppressShadowInLightID = = light - > parms . lightId )
{
2012-11-26 18:58:24 +00:00
continue ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// should we use the shadow bounds from pre-calculated interactions?
idBounds shadowBounds ;
R_ShadowBounds ( edef - > globalReferenceBounds , light - > globalLightBounds , light - > globalLightOrigin , shadowBounds ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// this test is pointless if we knew the light was completely contained
// in the view frustum, but the entity would also be directly visible in most
// of those cases.
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// this doesn't say that the shadow can't effect anything, only that it can't
// effect anything in the view, so we shouldn't set up a view entity
2012-11-28 15:47:07 +00:00
if ( idRenderMatrix : : CullBoundsToMVP ( viewDef - > worldSpace . mvp , shadowBounds ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// debug tool to allow viewing of only one entity at a time
2012-11-28 15:47:07 +00:00
if ( r_singleEntity . GetInteger ( ) > = 0 & & r_singleEntity . GetInteger ( ) ! = edef - > index )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// we do need it for shadows
vLight - > entityInteractionState [ edef - > index ] = viewLight_t : : INTERACTION_YES ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// we will need to create a viewEntity_t for it in the serial code section
2012-11-28 15:47:07 +00:00
shadowOnlyEntity_t * shadEnt = ( shadowOnlyEntity_t * ) R_FrameAlloc ( sizeof ( shadowOnlyEntity_t ) , FRAME_ALLOC_SHADOW_ONLY_ENTITY ) ;
2012-11-26 18:58:24 +00:00
shadEnt - > next = vLight - > shadowOnlyViewEntities ;
shadEnt - > edef = edef ;
vLight - > shadowOnlyViewEntities = shadEnt ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
//--------------------------------------------
// add the prelight shadows for the static world geometry
//--------------------------------------------
2012-11-28 15:47:07 +00:00
if ( light - > parms . prelightModel ! = NULL )
{
srfTriangles_t * tri = light - > parms . prelightModel - > Surface ( 0 ) - > geometry ;
2012-11-26 18:58:24 +00:00
// these shadows will have valid bounds, and can be culled normally,
// but they will typically cover most of the light's bounds
2012-11-28 15:47:07 +00:00
if ( idRenderMatrix : : CullBoundsToMVP ( viewDef - > worldSpace . mvp , tri - > bounds ) )
{
2012-11-26 18:58:24 +00:00
return ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// prelight models should always have static data that never gets purged
assert ( vertexCache . CacheIsCurrent ( tri - > shadowCache ) ) ;
assert ( vertexCache . CacheIsCurrent ( tri - > indexCache ) ) ;
2012-11-28 15:47:07 +00:00
drawSurf_t * shadowDrawSurf = ( drawSurf_t * ) R_FrameAlloc ( sizeof ( * shadowDrawSurf ) , FRAME_ALLOC_DRAW_SURFACE ) ;
2012-11-26 18:58:24 +00:00
shadowDrawSurf - > frontEndGeo = tri ;
shadowDrawSurf - > ambientCache = 0 ;
shadowDrawSurf - > indexCache = tri - > indexCache ;
shadowDrawSurf - > shadowCache = tri - > shadowCache ;
shadowDrawSurf - > jointCache = 0 ;
shadowDrawSurf - > numIndexes = 0 ;
shadowDrawSurf - > space = & viewDef - > worldSpace ;
shadowDrawSurf - > material = NULL ;
shadowDrawSurf - > extraGLState = 0 ;
shadowDrawSurf - > shaderRegisters = NULL ;
shadowDrawSurf - > scissorRect = vLight - > scissorRect ; // default to the light scissor and light depth bounds
shadowDrawSurf - > shadowVolumeState = SHADOWVOLUME_DONE ; // assume the shadow volume is done in case r_skipPrelightShadows is set
2012-11-28 15:47:07 +00:00
if ( ! r_skipPrelightShadows . GetBool ( ) )
{
preLightShadowVolumeParms_t * shadowParms = ( preLightShadowVolumeParms_t * ) R_FrameAlloc ( sizeof ( shadowParms [ 0 ] ) , FRAME_ALLOC_SHADOW_VOLUME_PARMS ) ;
2012-11-26 18:58:24 +00:00
shadowParms - > verts = tri - > preLightShadowVertexes ;
shadowParms - > numVerts = tri - > numVerts * 2 ;
shadowParms - > indexes = tri - > indexes ;
shadowParms - > numIndexes = tri - > numIndexes ;
shadowParms - > triangleBounds = tri - > bounds ;
shadowParms - > triangleMVP = viewDef - > worldSpace . mvp ;
shadowParms - > localLightOrigin = vLight - > globalLightOrigin ;
shadowParms - > localViewOrigin = viewDef - > renderView . vieworg ;
shadowParms - > zNear = r_znear . GetFloat ( ) ;
shadowParms - > lightZMin = vLight - > scissorRect . zmin ;
shadowParms - > lightZMax = vLight - > scissorRect . zmax ;
shadowParms - > forceShadowCaps = r_forceShadowCaps . GetBool ( ) ;
shadowParms - > useShadowPreciseInsideTest = r_useShadowPreciseInsideTest . GetBool ( ) ;
shadowParms - > useShadowDepthBounds = r_useShadowDepthBounds . GetBool ( ) ;
shadowParms - > numShadowIndices = & shadowDrawSurf - > numIndexes ;
shadowParms - > renderZFail = & shadowDrawSurf - > renderZFail ;
shadowParms - > shadowZMin = & shadowDrawSurf - > scissorRect . zmin ;
shadowParms - > shadowZMax = & shadowDrawSurf - > scissorRect . zmax ;
shadowParms - > shadowVolumeState = & shadowDrawSurf - > shadowVolumeState ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// the pre-light shadow volume "_prelight_light_3297" in "d3xpdm2" is malformed in that it contains the light origin so the precise inside test always fails
2012-11-28 15:47:07 +00:00
if ( tr . primaryWorld - > mapName . IcmpPath ( " maps/game/mp/d3xpdm2.map " ) = = 0 & & idStr : : Icmp ( light - > parms . prelightModel - > Name ( ) , " _prelight_light_3297 " ) = = 0 )
{
2012-11-26 18:58:24 +00:00
shadowParms - > useShadowPreciseInsideTest = false ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
shadowDrawSurf - > shadowVolumeState = SHADOWVOLUME_UNFINISHED ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
shadowParms - > next = vLight - > preLightShadowVolumes ;
vLight - > preLightShadowVolumes = shadowParms ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// actually link it in
shadowDrawSurf - > nextOnLight = vLight - > globalShadows ;
vLight - > globalShadows = shadowDrawSurf ;
}
}
REGISTER_PARALLEL_JOB ( R_AddSingleLight , " R_AddSingleLight " ) ;
/*
= = = = = = = = = = = = = = = = =
R_AddLights
= = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void R_AddLights ( )
{
2012-11-26 18:58:24 +00:00
SCOPED_PROFILE_EVENT ( " R_AddLights " ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
//-------------------------------------------------
// check each light individually, possibly in parallel
//-------------------------------------------------
2012-11-28 15:47:07 +00:00
if ( r_useParallelAddLights . GetBool ( ) )
{
for ( viewLight_t * vLight = tr . viewDef - > viewLights ; vLight ! = NULL ; vLight = vLight - > next )
{
tr . frontEndJobList - > AddJob ( ( jobRun_t ) R_AddSingleLight , vLight ) ;
2012-11-26 18:58:24 +00:00
}
tr . frontEndJobList - > Submit ( ) ;
tr . frontEndJobList - > Wait ( ) ;
2012-11-28 15:47:07 +00:00
}
else
{
for ( viewLight_t * vLight = tr . viewDef - > viewLights ; vLight ! = NULL ; vLight = vLight - > next )
{
2012-11-26 18:58:24 +00:00
R_AddSingleLight ( vLight ) ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
//-------------------------------------------------
// cull lights from the list if they turned out to not be needed
//-------------------------------------------------
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
tr . pc . c_viewLights = 0 ;
2012-11-28 15:47:07 +00:00
viewLight_t * * ptr = & tr . viewDef - > viewLights ;
while ( * ptr ! = NULL )
{
viewLight_t * vLight = * ptr ;
if ( vLight - > removeFromList )
{
2012-11-26 18:58:24 +00:00
vLight - > lightDef - > viewCount = - 1 ; // this probably doesn't matter with current code
* ptr = vLight - > next ;
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
ptr = & vLight - > next ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// serial work
tr . pc . c_viewLights + + ;
2012-11-28 15:47:07 +00:00
for ( shadowOnlyEntity_t * shadEnt = vLight - > shadowOnlyViewEntities ; shadEnt ! = NULL ; shadEnt = shadEnt - > next )
{
2012-11-26 18:58:24 +00:00
// this will add it to the viewEntities list, but with an empty scissor rect
R_SetEntityDefViewEntity ( shadEnt - > edef ) ;
}
2012-11-28 15:47:07 +00:00
if ( r_showLightScissors . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
R_ShowColoredScreenRect ( vLight - > scissorRect , vLight - > lightDef - > index ) ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
//-------------------------------------------------
// Add jobs to setup pre-light shadow volumes.
//-------------------------------------------------
2012-11-28 15:47:07 +00:00
if ( r_useParallelAddShadows . GetInteger ( ) = = 1 )
{
for ( viewLight_t * vLight = tr . viewDef - > viewLights ; vLight ! = NULL ; vLight = vLight - > next )
{
for ( preLightShadowVolumeParms_t * shadowParms = vLight - > preLightShadowVolumes ; shadowParms ! = NULL ; shadowParms = shadowParms - > next )
{
tr . frontEndJobList - > AddJob ( ( jobRun_t ) PreLightShadowVolumeJob , shadowParms ) ;
2012-11-26 18:58:24 +00:00
}
vLight - > preLightShadowVolumes = NULL ;
}
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
int start = Sys_Microseconds ( ) ;
2012-11-28 15:47:07 +00:00
for ( viewLight_t * vLight = tr . viewDef - > viewLights ; vLight ! = NULL ; vLight = vLight - > next )
{
for ( preLightShadowVolumeParms_t * shadowParms = vLight - > preLightShadowVolumes ; shadowParms ! = NULL ; shadowParms = shadowParms - > next )
{
2012-11-26 18:58:24 +00:00
PreLightShadowVolumeJob ( shadowParms ) ;
}
vLight - > preLightShadowVolumes = NULL ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int end = Sys_Microseconds ( ) ;
backEnd . pc . shadowMicroSec + = end - start ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
R_OptimizeViewLightsList
= = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void R_OptimizeViewLightsList ( )
{
2012-11-26 18:58:24 +00:00
// go through each visible light
int numViewLights = 0 ;
2012-11-28 15:47:07 +00:00
for ( viewLight_t * vLight = tr . viewDef - > viewLights ; vLight ! = NULL ; vLight = vLight - > next )
{
2012-11-26 18:58:24 +00:00
numViewLights + + ;
// If the light didn't have any lit surfaces visible, there is no need to
// draw any of the shadows. We still keep the vLight for debugging draws.
2012-11-28 15:47:07 +00:00
if ( ! vLight - > localInteractions & & ! vLight - > globalInteractions & & ! vLight - > translucentInteractions )
{
2012-11-26 18:58:24 +00:00
vLight - > localShadows = NULL ;
vLight - > globalShadows = NULL ;
}
}
2012-11-28 15:47:07 +00:00
if ( r_useShadowSurfaceScissor . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
// shrink the light scissor rect to only intersect the surfaces that will actually be drawn.
// This doesn't seem to actually help, perhaps because the surface scissor
// rects aren't actually the surface, but only the portal clippings.
2012-11-28 15:47:07 +00:00
for ( viewLight_t * vLight = tr . viewDef - > viewLights ; vLight ; vLight = vLight - > next )
{
drawSurf_t * surf ;
2012-11-26 18:58:24 +00:00
idScreenRect surfRect ;
2012-11-28 15:47:07 +00:00
if ( ! vLight - > lightShader - > LightCastsShadows ( ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
surfRect . Clear ( ) ;
2012-11-28 15:47:07 +00:00
for ( surf = vLight - > globalInteractions ; surf ! = NULL ; surf = surf - > nextOnLight )
{
2012-11-26 18:58:24 +00:00
surfRect . Union ( surf - > scissorRect ) ;
}
2012-11-28 15:47:07 +00:00
for ( surf = vLight - > localShadows ; surf ! = NULL ; surf = surf - > nextOnLight )
{
2012-11-26 18:58:24 +00:00
surf - > scissorRect . Intersect ( surfRect ) ;
}
2012-11-28 15:47:07 +00:00
for ( surf = vLight - > localInteractions ; surf ! = NULL ; surf = surf - > nextOnLight )
{
2012-11-26 18:58:24 +00:00
surfRect . Union ( surf - > scissorRect ) ;
}
2012-11-28 15:47:07 +00:00
for ( surf = vLight - > globalShadows ; surf ! = NULL ; surf = surf - > nextOnLight )
{
2012-11-26 18:58:24 +00:00
surf - > scissorRect . Intersect ( surfRect ) ;
}
2012-11-28 15:47:07 +00:00
for ( surf = vLight - > translucentInteractions ; surf ! = NULL ; surf = surf - > nextOnLight )
{
2012-11-26 18:58:24 +00:00
surfRect . Union ( surf - > scissorRect ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
vLight - > scissorRect . Intersect ( surfRect ) ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// sort the viewLights list so the largest lights come first, which will reduce
// the chance of GPU pipeline bubbles
2012-11-28 15:47:07 +00:00
struct sortLight_t
{
viewLight_t * vLight ;
2012-11-26 18:58:24 +00:00
int screenArea ;
2012-11-28 15:47:07 +00:00
static int sort ( const void * a , const void * b )
{
return ( ( sortLight_t * ) a ) - > screenArea - ( ( sortLight_t * ) b ) - > screenArea ;
2012-11-26 18:58:24 +00:00
}
} ;
2012-11-28 15:47:07 +00:00
sortLight_t * sortLights = ( sortLight_t * ) _alloca ( sizeof ( sortLight_t ) * numViewLights ) ;
2012-11-26 18:58:24 +00:00
int numSortLightsFilled = 0 ;
2012-11-28 15:47:07 +00:00
for ( viewLight_t * vLight = tr . viewDef - > viewLights ; vLight ! = NULL ; vLight = vLight - > next )
{
2012-11-26 18:58:24 +00:00
sortLights [ numSortLightsFilled ] . vLight = vLight ;
sortLights [ numSortLightsFilled ] . screenArea = vLight - > scissorRect . GetArea ( ) ;
numSortLightsFilled + + ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
qsort ( sortLights , numSortLightsFilled , sizeof ( sortLights [ 0 ] ) , sortLight_t : : sort ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// rebuild the linked list in order
tr . viewDef - > viewLights = NULL ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < numSortLightsFilled ; i + + )
{
2012-11-26 18:58:24 +00:00
sortLights [ i ] . vLight - > next = tr . viewDef - > viewLights ;
tr . viewDef - > viewLights = sortLights [ i ] . vLight ;
}
}