2011-11-22 21:28:15 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Doom 3 GPL Source Code
2011-12-06 18:20:15 +00:00
Copyright ( C ) 1999 - 2011 id Software LLC , a ZeniMax Media company .
2011-11-22 21:28:15 +00:00
2011-12-06 16:14:59 +00:00
This file is part of the Doom 3 GPL Source Code ( " Doom 3 Source Code " ) .
2011-11-22 21:28:15 +00:00
Doom 3 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 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 Source Code . If not , see < http : //www.gnu.org/licenses/>.
In addition , the Doom 3 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 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 .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2011-12-16 22:28:29 +00:00
# include "sys/platform.h"
# include "idlib/math/Interpolate.h"
# include "framework/Game.h"
# include "renderer/VertexCache.h"
# include "renderer/RenderWorld_local.h"
# include "ui/Window.h"
2011-11-22 21:28:15 +00:00
2011-12-16 22:28:29 +00:00
# include "renderer/tr_local.h"
2011-11-22 21:28:15 +00:00
2024-06-13 22:12:00 +00:00
# include "Model_local.h"
2011-11-22 21:28:15 +00:00
static const float CHECK_BOUNDS_EPSILON = 1.0f ;
2024-06-13 22:12:00 +00:00
static idCVar r_useSoftParticles ( " r_useSoftParticles " , " 1 " , CVAR_RENDERER | CVAR_BOOL , " soften particle transitions when player walks through them or they cross solid geometry " ) ;
2011-11-22 21:28:15 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
VERTEX CACHE GENERATORS
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = = = = = = =
R_CreateAmbientCache
Create it if needed
= = = = = = = = = = = = = = = = = =
*/
bool R_CreateAmbientCache ( srfTriangles_t * tri , bool needsLighting ) {
if ( tri - > ambientCache ) {
return true ;
}
// we are going to use it for drawing, so make sure we have the tangents and normals
if ( needsLighting & & ! tri - > tangentsCalculated ) {
R_DeriveTangents ( tri ) ;
}
vertexCache . Alloc ( tri - > verts , tri - > numVerts * sizeof ( tri - > verts [ 0 ] ) , & tri - > ambientCache ) ;
if ( ! tri - > ambientCache ) {
return false ;
}
return true ;
}
/*
= = = = = = = = = = = = = = = = = =
R_CreateLightingCache
Returns false if the cache couldn ' t be allocated , in which case the surface should be skipped .
= = = = = = = = = = = = = = = = = =
*/
bool R_CreateLightingCache ( const idRenderEntityLocal * ent , const idRenderLightLocal * light , srfTriangles_t * tri ) {
idVec3 localLightOrigin ;
// fogs and blends don't need light vectors
if ( light - > lightShader - > IsFogLight ( ) | | light - > lightShader - > IsBlendLight ( ) ) {
return true ;
}
// not needed if we have vertex programs
if ( tr . backEndRendererHasVertexPrograms ) {
return true ;
}
R_GlobalPointToLocal ( ent - > modelMatrix , light - > globalLightOrigin , localLightOrigin ) ;
int size = tri - > ambientSurface - > numVerts * sizeof ( lightingCache_t ) ;
lightingCache_t * cache = ( lightingCache_t * ) _alloca16 ( size ) ;
# if 1
SIMDProcessor - > CreateTextureSpaceLightVectors ( & cache [ 0 ] . localLightVector , localLightOrigin ,
tri - > ambientSurface - > verts , tri - > ambientSurface - > numVerts , tri - > indexes , tri - > numIndexes ) ;
# else
bool * used = ( bool * ) _alloca16 ( tri - > ambientSurface - > numVerts * sizeof ( used [ 0 ] ) ) ;
memset ( used , 0 , tri - > ambientSurface - > numVerts * sizeof ( used [ 0 ] ) ) ;
// because the interaction may be a very small subset of the full surface,
// it makes sense to only deal with the verts used
for ( int j = 0 ; j < tri - > numIndexes ; j + + ) {
int i = tri - > indexes [ j ] ;
if ( used [ i ] ) {
continue ;
}
used [ i ] = true ;
idVec3 lightDir ;
const idDrawVert * v ;
v = & tri - > ambientSurface - > verts [ i ] ;
lightDir = localLightOrigin - v - > xyz ;
cache [ i ] . localLightVector [ 0 ] = lightDir * v - > tangents [ 0 ] ;
cache [ i ] . localLightVector [ 1 ] = lightDir * v - > tangents [ 1 ] ;
cache [ i ] . localLightVector [ 2 ] = lightDir * v - > normal ;
}
# endif
vertexCache . Alloc ( cache , size , & tri - > lightingCache ) ;
if ( ! tri - > lightingCache ) {
return false ;
}
return true ;
}
/*
= = = = = = = = = = = = = = = = = =
R_CreatePrivateShadowCache
This is used only for a specific light
= = = = = = = = = = = = = = = = = =
*/
void R_CreatePrivateShadowCache ( srfTriangles_t * tri ) {
if ( ! tri - > shadowVertexes ) {
return ;
}
vertexCache . Alloc ( tri - > shadowVertexes , tri - > numVerts * sizeof ( * tri - > shadowVertexes ) , & tri - > shadowCache ) ;
}
/*
= = = = = = = = = = = = = = = = = =
R_CreateVertexProgramShadowCache
This is constant for any number of lights , the vertex program
takes care of projecting the verts to infinity .
= = = = = = = = = = = = = = = = = =
*/
void R_CreateVertexProgramShadowCache ( srfTriangles_t * tri ) {
if ( tri - > verts = = NULL ) {
return ;
}
shadowCache_t * temp = ( shadowCache_t * ) _alloca16 ( tri - > numVerts * 2 * sizeof ( shadowCache_t ) ) ;
# if 1
SIMDProcessor - > CreateVertexProgramShadowCache ( & temp - > xyz , tri - > verts , tri - > numVerts ) ;
# else
int numVerts = tri - > numVerts ;
const idDrawVert * verts = tri - > verts ;
for ( int i = 0 ; i < numVerts ; i + + ) {
const float * v = verts [ i ] . xyz . ToFloatPtr ( ) ;
temp [ i * 2 + 0 ] . xyz [ 0 ] = v [ 0 ] ;
temp [ i * 2 + 1 ] . xyz [ 0 ] = v [ 0 ] ;
temp [ i * 2 + 0 ] . xyz [ 1 ] = v [ 1 ] ;
temp [ i * 2 + 1 ] . xyz [ 1 ] = v [ 1 ] ;
temp [ i * 2 + 0 ] . xyz [ 2 ] = v [ 2 ] ;
temp [ i * 2 + 1 ] . xyz [ 2 ] = v [ 2 ] ;
temp [ i * 2 + 0 ] . xyz [ 3 ] = 1.0f ; // on the model surface
temp [ i * 2 + 1 ] . xyz [ 3 ] = 0.0f ; // will be projected to infinity
}
# endif
vertexCache . Alloc ( temp , tri - > numVerts * 2 * sizeof ( shadowCache_t ) , & tri - > shadowCache ) ;
}
/*
= = = = = = = = = = = = = = = = = =
R_SkyboxTexGen
= = = = = = = = = = = = = = = = = =
*/
void R_SkyboxTexGen ( drawSurf_t * surf , const idVec3 & viewOrg ) {
int i ;
idVec3 localViewOrigin ;
R_GlobalPointToLocal ( surf - > space - > modelMatrix , viewOrg , localViewOrigin ) ;
int numVerts = surf - > geo - > numVerts ;
int size = numVerts * sizeof ( idVec3 ) ;
idVec3 * texCoords = ( idVec3 * ) _alloca16 ( size ) ;
const idDrawVert * verts = surf - > geo - > verts ;
for ( i = 0 ; i < numVerts ; i + + ) {
texCoords [ i ] [ 0 ] = verts [ i ] . xyz [ 0 ] - localViewOrigin [ 0 ] ;
texCoords [ i ] [ 1 ] = verts [ i ] . xyz [ 1 ] - localViewOrigin [ 1 ] ;
texCoords [ i ] [ 2 ] = verts [ i ] . xyz [ 2 ] - localViewOrigin [ 2 ] ;
}
surf - > dynamicTexCoords = vertexCache . AllocFrameTemp ( texCoords , size ) ;
}
/*
= = = = = = = = = = = = = = = = = =
R_WobbleskyTexGen
= = = = = = = = = = = = = = = = = =
*/
void R_WobbleskyTexGen ( drawSurf_t * surf , const idVec3 & viewOrg ) {
int i ;
idVec3 localViewOrigin ;
const int * parms = surf - > material - > GetTexGenRegisters ( ) ;
float wobbleDegrees = surf - > shaderRegisters [ parms [ 0 ] ] ;
float wobbleSpeed = surf - > shaderRegisters [ parms [ 1 ] ] ;
float rotateSpeed = surf - > shaderRegisters [ parms [ 2 ] ] ;
wobbleDegrees = wobbleDegrees * idMath : : PI / 180 ;
wobbleSpeed = wobbleSpeed * 2 * idMath : : PI / 60 ;
rotateSpeed = rotateSpeed * 2 * idMath : : PI / 60 ;
// very ad-hoc "wobble" transform
float transform [ 16 ] ;
float a = tr . viewDef - > floatTime * wobbleSpeed ;
float s = sin ( a ) * sin ( wobbleDegrees ) ;
float c = cos ( a ) * sin ( wobbleDegrees ) ;
float z = cos ( wobbleDegrees ) ;
idVec3 axis [ 3 ] ;
axis [ 2 ] [ 0 ] = c ;
axis [ 2 ] [ 1 ] = s ;
axis [ 2 ] [ 2 ] = z ;
axis [ 1 ] [ 0 ] = - sin ( a * 2 ) * sin ( wobbleDegrees ) ;
axis [ 1 ] [ 2 ] = - s * sin ( wobbleDegrees ) ;
axis [ 1 ] [ 1 ] = sqrt ( 1.0f - ( axis [ 1 ] [ 0 ] * axis [ 1 ] [ 0 ] + axis [ 1 ] [ 2 ] * axis [ 1 ] [ 2 ] ) ) ;
// make the second vector exactly perpendicular to the first
axis [ 1 ] - = ( axis [ 2 ] * axis [ 1 ] ) * axis [ 2 ] ;
axis [ 1 ] . Normalize ( ) ;
// construct the third with a cross
axis [ 0 ] . Cross ( axis [ 1 ] , axis [ 2 ] ) ;
// add the rotate
s = sin ( rotateSpeed * tr . viewDef - > floatTime ) ;
c = cos ( rotateSpeed * tr . viewDef - > floatTime ) ;
transform [ 0 ] = axis [ 0 ] [ 0 ] * c + axis [ 1 ] [ 0 ] * s ;
transform [ 4 ] = axis [ 0 ] [ 1 ] * c + axis [ 1 ] [ 1 ] * s ;
transform [ 8 ] = axis [ 0 ] [ 2 ] * c + axis [ 1 ] [ 2 ] * s ;
transform [ 1 ] = axis [ 1 ] [ 0 ] * c - axis [ 0 ] [ 0 ] * s ;
transform [ 5 ] = axis [ 1 ] [ 1 ] * c - axis [ 0 ] [ 1 ] * s ;
transform [ 9 ] = axis [ 1 ] [ 2 ] * c - axis [ 0 ] [ 2 ] * s ;
transform [ 2 ] = axis [ 2 ] [ 0 ] ;
transform [ 6 ] = axis [ 2 ] [ 1 ] ;
transform [ 10 ] = axis [ 2 ] [ 2 ] ;
transform [ 3 ] = transform [ 7 ] = transform [ 11 ] = 0.0f ;
transform [ 12 ] = transform [ 13 ] = transform [ 14 ] = 0.0f ;
R_GlobalPointToLocal ( surf - > space - > modelMatrix , viewOrg , localViewOrigin ) ;
int numVerts = surf - > geo - > numVerts ;
int size = numVerts * sizeof ( idVec3 ) ;
idVec3 * texCoords = ( idVec3 * ) _alloca16 ( size ) ;
const idDrawVert * verts = surf - > geo - > verts ;
for ( i = 0 ; i < numVerts ; i + + ) {
idVec3 v ;
v [ 0 ] = verts [ i ] . xyz [ 0 ] - localViewOrigin [ 0 ] ;
v [ 1 ] = verts [ i ] . xyz [ 1 ] - localViewOrigin [ 1 ] ;
v [ 2 ] = verts [ i ] . xyz [ 2 ] - localViewOrigin [ 2 ] ;
R_LocalPointToGlobal ( transform , v , texCoords [ i ] ) ;
}
surf - > dynamicTexCoords = vertexCache . AllocFrameTemp ( texCoords , size ) ;
}
/*
= = = = = = = = = = = = = = = = =
R_SpecularTexGen
Calculates the specular coordinates for cards without vertex programs .
= = = = = = = = = = = = = = = = =
*/
static void R_SpecularTexGen ( drawSurf_t * surf , const idVec3 & globalLightOrigin , const idVec3 & viewOrg ) {
const srfTriangles_t * tri ;
idVec3 localLightOrigin ;
idVec3 localViewOrigin ;
R_GlobalPointToLocal ( surf - > space - > modelMatrix , globalLightOrigin , localLightOrigin ) ;
R_GlobalPointToLocal ( surf - > space - > modelMatrix , viewOrg , localViewOrigin ) ;
tri = surf - > geo ;
// FIXME: change to 3 component?
int size = tri - > numVerts * sizeof ( idVec4 ) ;
idVec4 * texCoords = ( idVec4 * ) _alloca16 ( size ) ;
# if 1
SIMDProcessor - > CreateSpecularTextureCoords ( texCoords , localLightOrigin , localViewOrigin ,
tri - > verts , tri - > numVerts , tri - > indexes , tri - > numIndexes ) ;
# else
bool * used = ( bool * ) _alloca16 ( tri - > numVerts * sizeof ( used [ 0 ] ) ) ;
memset ( used , 0 , tri - > numVerts * sizeof ( used [ 0 ] ) ) ;
// because the interaction may be a very small subset of the full surface,
// it makes sense to only deal with the verts used
for ( int j = 0 ; j < tri - > numIndexes ; j + + ) {
int i = tri - > indexes [ j ] ;
if ( used [ i ] ) {
continue ;
}
used [ i ] = true ;
float ilength ;
const idDrawVert * v = & tri - > verts [ i ] ;
idVec3 lightDir = localLightOrigin - v - > xyz ;
idVec3 viewDir = localViewOrigin - v - > xyz ;
ilength = idMath : : RSqrt ( lightDir * lightDir ) ;
lightDir [ 0 ] * = ilength ;
lightDir [ 1 ] * = ilength ;
lightDir [ 2 ] * = ilength ;
ilength = idMath : : RSqrt ( viewDir * viewDir ) ;
viewDir [ 0 ] * = ilength ;
viewDir [ 1 ] * = ilength ;
viewDir [ 2 ] * = ilength ;
lightDir + = viewDir ;
texCoords [ i ] [ 0 ] = lightDir * v - > tangents [ 0 ] ;
texCoords [ i ] [ 1 ] = lightDir * v - > tangents [ 1 ] ;
texCoords [ i ] [ 2 ] = lightDir * v - > normal ;
texCoords [ i ] [ 3 ] = 1 ;
}
# endif
surf - > dynamicTexCoords = vertexCache . AllocFrameTemp ( texCoords , size ) ;
}
//=======================================================================================================
/*
= = = = = = = = = = = = =
R_SetEntityDefViewEntity
If the entityDef isn ' t already on the viewEntity list , create
a viewEntity and add it to the list with an empty scissor rect .
This does not instantiate dynamic models for the entity yet .
= = = = = = = = = = = = =
*/
viewEntity_t * R_SetEntityDefViewEntity ( idRenderEntityLocal * def ) {
viewEntity_t * vModel ;
if ( def - > viewCount = = tr . viewCount ) {
return def - > viewEntity ;
}
def - > viewCount = tr . viewCount ;
// set the model and modelview matricies
vModel = ( viewEntity_t * ) R_ClearedFrameAlloc ( sizeof ( * vModel ) ) ;
vModel - > entityDef = def ;
// the scissorRect will be expanded as the model bounds is accepted into visible portal chains
vModel - > scissorRect . Clear ( ) ;
// copy the model and weapon depth hack for back-end use
vModel - > modelDepthHack = def - > parms . modelDepthHack ;
vModel - > weaponDepthHack = def - > parms . weaponDepthHack ;
R_AxisToModelMatrix ( def - > parms . axis , def - > parms . origin , vModel - > modelMatrix ) ;
// we may not have a viewDef if we are just creating shadows at entity creation time
if ( tr . viewDef ) {
myGlMultMatrix ( vModel - > modelMatrix , tr . viewDef - > worldSpace . modelViewMatrix , vModel - > modelViewMatrix ) ;
vModel - > next = tr . viewDef - > viewEntitys ;
tr . viewDef - > viewEntitys = vModel ;
}
def - > viewEntity = vModel ;
return vModel ;
}
/*
= = = = = = = = = = = = = = = = = = = =
R_TestPointInViewLight
= = = = = = = = = = = = = = = = = = = =
*/
static const float INSIDE_LIGHT_FRUSTUM_SLOP = 32 ;
// this needs to be greater than the dist from origin to corner of near clip plane
static bool R_TestPointInViewLight ( const idVec3 & org , const idRenderLightLocal * light ) {
int i ;
idVec3 local ;
for ( i = 0 ; i < 6 ; i + + ) {
float d = light - > frustum [ i ] . Distance ( org ) ;
if ( d > INSIDE_LIGHT_FRUSTUM_SLOP ) {
return false ;
}
}
return true ;
}
/*
= = = = = = = = = = = = = = = = = = =
R_PointInFrustum
Assumes positive sides face outward
= = = = = = = = = = = = = = = = = = =
*/
static bool R_PointInFrustum ( idVec3 & p , idPlane * planes , int numPlanes ) {
for ( int i = 0 ; i < numPlanes ; i + + ) {
float d = planes [ i ] . Distance ( p ) ;
if ( d > 0 ) {
return false ;
}
}
return true ;
}
/*
= = = = = = = = = = = = =
R_SetLightDefViewLight
If the lightDef isn ' t already on the viewLight list , create
a viewLight and add it to the list with an empty scissor rect .
= = = = = = = = = = = = =
*/
viewLight_t * R_SetLightDefViewLight ( idRenderLightLocal * light ) {
viewLight_t * vLight ;
if ( light - > viewCount = = tr . viewCount ) {
return light - > viewLight ;
}
light - > viewCount = tr . viewCount ;
// add to the view light chain
vLight = ( viewLight_t * ) R_ClearedFrameAlloc ( sizeof ( * vLight ) ) ;
vLight - > lightDef = light ;
// the scissorRect will be expanded as the light bounds is accepted into visible portal chains
vLight - > scissorRect . Clear ( ) ;
// calculate the shadow cap optimization states
vLight - > viewInsideLight = R_TestPointInViewLight ( tr . viewDef - > renderView . vieworg , light ) ;
if ( ! vLight - > viewInsideLight ) {
vLight - > viewSeesShadowPlaneBits = 0 ;
for ( int i = 0 ; i < light - > numShadowFrustums ; i + + ) {
float d = light - > shadowFrustums [ i ] . planes [ 5 ] . Distance ( tr . viewDef - > renderView . vieworg ) ;
if ( d < INSIDE_LIGHT_FRUSTUM_SLOP ) {
vLight - > viewSeesShadowPlaneBits | = 1 < < i ;
}
}
} else {
// this should not be referenced in this case
vLight - > viewSeesShadowPlaneBits = 63 ;
}
// see if the light center is in view, which will allow us to cull invisible shadows
vLight - > viewSeesGlobalLightOrigin = R_PointInFrustum ( light - > globalLightOrigin , tr . viewDef - > frustum , 4 ) ;
// 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 ] ;
vLight - > fogPlane = light - > frustum [ 5 ] ;
vLight - > frustumTris = light - > frustumTris ;
vLight - > falloffImage = light - > falloffImage ;
vLight - > lightShader = light - > lightShader ;
vLight - > shaderRegisters = NULL ; // allocated and evaluated in R_AddLightSurfaces
// link the view light
vLight - > next = tr . viewDef - > viewLights ;
tr . viewDef - > viewLights = vLight ;
light - > viewLight = vLight ;
return vLight ;
}
/*
= = = = = = = = = = = = = = = = =
idRenderWorldLocal : : CreateLightDefInteractions
When a lightDef is determined to effect the view ( contact the frustum and non - 0 light ) , it will check to
make sure that it has interactions for all the entityDefs that it might possibly contact .
This does not guarantee that all possible interactions for this light are generated , only that
the ones that may effect the current view are generated . so it does need to be called every view .
This does not cause entityDefs to create dynamic models , all work is done on the referenceBounds .
All entities that have non - empty interactions with viewLights will
have viewEntities made for them and be put on the viewEntity list ,
even if their surfaces aren ' t visible , because they may need to cast shadows .
Interactions are usually removed when a entityDef or lightDef is modified , unless the change
is known to not effect them , so there is no danger of getting a stale interaction , we just need to
check that needed ones are created .
An interaction can be at several levels :
Don ' t interact ( but share an area ) ( numSurfaces = 0 )
Entity reference bounds touches light frustum , but surfaces haven ' t been generated ( numSurfaces = - 1 )
Shadow surfaces have been generated , but light surfaces have not . The shadow surface may still be empty due to bounds being conservative .
Both shadow and light surfaces have been generated . Either or both surfaces may still be empty due to conservative bounds .
= = = = = = = = = = = = = = = = =
*/
void idRenderWorldLocal : : CreateLightDefInteractions ( idRenderLightLocal * ldef ) {
areaReference_t * eref ;
areaReference_t * lref ;
idRenderEntityLocal * edef ;
portalArea_t * area ;
idInteraction * inter ;
for ( lref = ldef - > references ; lref ; lref = lref - > ownerNext ) {
area = lref - > area ;
// check all the models in this area
for ( eref = area - > entityRefs . areaNext ; eref ! = & area - > entityRefs ; eref = eref - > areaNext ) {
edef = eref - > entity ;
// if the entity doesn't have any light-interacting surfaces, we could skip this,
// but we don't want to instantiate dynamic models yet, so we can't check that on
// most things
// if the entity isn't viewed
if ( tr . viewDef & & edef - > viewCount ! = tr . viewCount ) {
// if the light doesn't cast shadows, skip
if ( ! ldef - > lightShader - > LightCastsShadows ( ) ) {
continue ;
}
// if we are suppressing its shadow in this view, skip
if ( ! r_skipSuppress . GetBool ( ) ) {
if ( edef - > parms . suppressShadowInViewID & & edef - > parms . suppressShadowInViewID = = tr . viewDef - > renderView . viewID ) {
continue ;
}
if ( edef - > parms . suppressShadowInLightID & & edef - > parms . suppressShadowInLightID = = ldef - > parms . lightId ) {
continue ;
}
}
}
// 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
if ( edef - > parms . noDynamicInteractions & & edef - > world - > generateAllInteractionsCalled ) {
continue ;
}
// if any of the edef's interaction match this light, we don't
2011-12-06 18:20:15 +00:00
// need to consider it.
2011-11-22 21:28:15 +00:00
if ( r_useInteractionTable . GetBool ( ) & & this - > interactionTable ) {
// allocating these tables may take several megs on big maps, but it saves 3% to 5% of
// the CPU time. The table is updated at interaction::AllocAndLink() and interaction::UnlinkAndFree()
int index = ldef - > index * this - > interactionTableWidth + edef - > index ;
inter = this - > interactionTable [ index ] ;
if ( inter ) {
// if this entity wasn't in view already, the scissor rect will be empty,
// so it will only be used for shadow casting
if ( ! inter - > IsEmpty ( ) ) {
R_SetEntityDefViewEntity ( edef ) ;
}
continue ;
}
} else {
// scan the doubly linked lists, which may have several dozen entries
// we could check either model refs or light refs for matches, but it is
// assumed that there will be less lights in an area than models
// so the entity chains should be somewhat shorter (they tend to be fairly close).
for ( inter = edef - > firstInteraction ; inter ! = NULL ; inter = inter - > entityNext ) {
if ( inter - > lightDef = = ldef ) {
break ;
}
}
// if we already have an interaction, we don't need to do anything
if ( inter ! = NULL ) {
// if this entity wasn't in view already, the scissor rect will be empty,
// so it will only be used for shadow casting
if ( ! inter - > IsEmpty ( ) ) {
R_SetEntityDefViewEntity ( edef ) ;
}
continue ;
}
}
//
// create a new interaction, but don't do any work other than bbox to frustum culling
//
idInteraction * inter = idInteraction : : AllocAndLink ( edef , ldef ) ;
// do a check of the entity reference bounds against the light frustum,
// trying to avoid creating a viewEntity if it hasn't been already
float modelMatrix [ 16 ] ;
float * m ;
if ( edef - > viewCount = = tr . viewCount ) {
m = edef - > viewEntity - > modelMatrix ;
} else {
R_AxisToModelMatrix ( edef - > parms . axis , edef - > parms . origin , modelMatrix ) ;
m = modelMatrix ;
}
if ( R_CullLocalBox ( edef - > referenceBounds , m , 6 , ldef - > frustum ) ) {
inter - > MakeEmpty ( ) ;
continue ;
}
// we will do a more precise per-surface check when we are checking the entity
// if this entity wasn't in view already, the scissor rect will be empty,
// so it will only be used for shadow casting
R_SetEntityDefViewEntity ( edef ) ;
}
}
}
//===============================================================================================================
/*
= = = = = = = = = = = = = = = = =
R_LinkLightSurf
= = = = = = = = = = = = = = = = =
*/
2011-12-06 18:20:15 +00:00
void R_LinkLightSurf ( const drawSurf_t * * link , const srfTriangles_t * tri , const viewEntity_t * space ,
2011-11-22 21:28:15 +00:00
const idRenderLightLocal * light , const idMaterial * shader , const idScreenRect & scissor , bool viewInsideShadow ) {
drawSurf_t * drawSurf ;
if ( ! space ) {
space = & tr . viewDef - > worldSpace ;
}
drawSurf = ( drawSurf_t * ) R_FrameAlloc ( sizeof ( * drawSurf ) ) ;
drawSurf - > geo = tri ;
drawSurf - > space = space ;
drawSurf - > material = shader ;
drawSurf - > scissorRect = scissor ;
drawSurf - > dsFlags = 0 ;
2024-06-13 22:12:00 +00:00
drawSurf - > particle_radius = 0.0f ; // #3878
2011-11-22 21:28:15 +00:00
if ( viewInsideShadow ) {
drawSurf - > dsFlags | = DSF_VIEW_INSIDE_SHADOW ;
}
if ( ! shader ) {
// shadows won't have a shader
drawSurf - > shaderRegisters = NULL ;
} else {
// process the shader expressions for conditionals / color / texcoords
const float * constRegs = shader - > ConstantRegisters ( ) ;
if ( constRegs ) {
// this shader has only constants for parameters
drawSurf - > shaderRegisters = constRegs ;
} else {
// FIXME: share with the ambient surface?
float * regs = ( float * ) R_FrameAlloc ( shader - > GetNumRegisters ( ) * sizeof ( float ) ) ;
drawSurf - > shaderRegisters = regs ;
shader - > EvaluateRegisters ( regs , space - > entityDef - > parms . shaderParms , tr . viewDef , space - > entityDef - > parms . referenceSound ) ;
}
// calculate the specular coordinates if we aren't using vertex programs
2012-07-18 20:01:10 +00:00
if ( ! tr . backEndRendererHasVertexPrograms & & ! r_skipSpecular . GetBool ( ) ) {
2011-11-22 21:28:15 +00:00
R_SpecularTexGen ( drawSurf , light - > globalLightOrigin , tr . viewDef - > renderView . vieworg ) ;
// if we failed to allocate space for the specular calculations, drop the surface
if ( ! drawSurf - > dynamicTexCoords ) {
return ;
}
}
}
// actually link it in
drawSurf - > nextOnLight = * link ;
* link = drawSurf ;
}
/*
= = = = = = = = = = = = = = = = = = = = = =
R_ClippedLightScissorRectangle
= = = = = = = = = = = = = = = = = = = = = =
*/
idScreenRect R_ClippedLightScissorRectangle ( viewLight_t * vLight ) {
int i , j ;
const idRenderLightLocal * light = vLight - > lightDef ;
idScreenRect r ;
idFixedWinding w ;
r . Clear ( ) ;
for ( i = 0 ; i < 6 ; i + + ) {
const idWinding * ow = light - > frustumWindings [ i ] ;
// projected lights may have one of the frustums degenerated
if ( ! ow ) {
continue ;
}
// the light frustum planes face out from the light,
// so the planes that have the view origin on the negative
// side will be the "back" faces of the light, which must have
// some fragment inside the portalStack to be visible
if ( light - > frustum [ i ] . Distance ( tr . viewDef - > renderView . vieworg ) > = 0 ) {
continue ;
}
w = * ow ;
// now check the winding against each of the frustum planes
for ( j = 0 ; j < 5 ; j + + ) {
if ( ! w . ClipInPlace ( - tr . viewDef - > frustum [ j ] ) ) {
break ;
}
}
// project these points to the screen and add to bounds
for ( j = 0 ; j < w . GetNumPoints ( ) ; j + + ) {
idPlane eye , clip ;
idVec3 ndc ;
R_TransformModelToClip ( w [ j ] . ToVec3 ( ) , tr . viewDef - > worldSpace . modelViewMatrix , tr . viewDef - > projectionMatrix , eye , clip ) ;
if ( clip [ 3 ] < = 0.01f ) {
clip [ 3 ] = 0.01f ;
}
R_TransformClipToDevice ( clip , tr . viewDef , ndc ) ;
float windowX = 0.5f * ( 1.0f + ndc [ 0 ] ) * ( tr . viewDef - > viewport . x2 - tr . viewDef - > viewport . x1 ) ;
float windowY = 0.5f * ( 1.0f + ndc [ 1 ] ) * ( tr . viewDef - > viewport . y2 - tr . viewDef - > viewport . y1 ) ;
if ( windowX > tr . viewDef - > scissor . x2 ) {
windowX = tr . viewDef - > scissor . x2 ;
} else if ( windowX < tr . viewDef - > scissor . x1 ) {
windowX = tr . viewDef - > scissor . x1 ;
}
if ( windowY > tr . viewDef - > scissor . y2 ) {
windowY = tr . viewDef - > scissor . y2 ;
} else if ( windowY < tr . viewDef - > scissor . y1 ) {
windowY = tr . viewDef - > scissor . y1 ;
}
r . AddPoint ( windowX , windowY ) ;
}
}
// add the fudge boundary
r . Expand ( ) ;
return r ;
}
/*
= = = = = = = = = = = = = = = = = =
R_CalcLightScissorRectangle
The light screen bounds will be used to crop the scissor rect during
stencil clears and interaction drawing
= = = = = = = = = = = = = = = = = =
*/
int c_clippedLight , c_unclippedLight ;
idScreenRect R_CalcLightScissorRectangle ( viewLight_t * vLight ) {
idScreenRect r ;
srfTriangles_t * tri ;
idPlane eye , clip ;
idVec3 ndc ;
if ( vLight - > lightDef - > parms . pointLight ) {
idBounds bounds ;
idRenderLightLocal * lightDef = vLight - > lightDef ;
tr . viewDef - > viewFrustum . ProjectionBounds ( idBox ( lightDef - > parms . origin , lightDef - > parms . lightRadius , lightDef - > parms . axis ) , bounds ) ;
return R_ScreenRectFromViewFrustumBounds ( bounds ) ;
}
if ( r_useClippedLightScissors . GetInteger ( ) = = 2 ) {
return R_ClippedLightScissorRectangle ( vLight ) ;
}
r . Clear ( ) ;
tri = vLight - > lightDef - > frustumTris ;
for ( int i = 0 ; i < tri - > numVerts ; i + + ) {
R_TransformModelToClip ( tri - > verts [ i ] . xyz , tr . viewDef - > worldSpace . modelViewMatrix ,
tr . viewDef - > projectionMatrix , eye , clip ) ;
// if it is near clipped, clip the winding polygons to the view frustum
if ( clip [ 3 ] < = 1 ) {
c_clippedLight + + ;
if ( r_useClippedLightScissors . GetInteger ( ) ) {
return R_ClippedLightScissorRectangle ( vLight ) ;
} else {
r . x1 = r . y1 = 0 ;
r . x2 = ( tr . viewDef - > viewport . x2 - tr . viewDef - > viewport . x1 ) - 1 ;
r . y2 = ( tr . viewDef - > viewport . y2 - tr . viewDef - > viewport . y1 ) - 1 ;
return r ;
}
}
R_TransformClipToDevice ( clip , tr . viewDef , ndc ) ;
float windowX = 0.5f * ( 1.0f + ndc [ 0 ] ) * ( tr . viewDef - > viewport . x2 - tr . viewDef - > viewport . x1 ) ;
float windowY = 0.5f * ( 1.0f + ndc [ 1 ] ) * ( tr . viewDef - > viewport . y2 - tr . viewDef - > viewport . y1 ) ;
if ( windowX > tr . viewDef - > scissor . x2 ) {
windowX = tr . viewDef - > scissor . x2 ;
} else if ( windowX < tr . viewDef - > scissor . x1 ) {
windowX = tr . viewDef - > scissor . x1 ;
}
if ( windowY > tr . viewDef - > scissor . y2 ) {
windowY = tr . viewDef - > scissor . y2 ;
} else if ( windowY < tr . viewDef - > scissor . y1 ) {
windowY = tr . viewDef - > scissor . y1 ;
}
r . AddPoint ( windowX , windowY ) ;
}
// add the fudge boundary
r . Expand ( ) ;
c_unclippedLight + + ;
return r ;
}
/*
= = = = = = = = = = = = = = = = =
R_AddLightSurfaces
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 .
Adds entities to the viewEntity list if they are needed for shadow casting .
Add any precomputed shadow volumes .
Removes lights from the viewLights list if they are completely
turned off , or completely off screen .
Create any new interactions needed between the viewLights
and the viewEntitys due to game movement
= = = = = = = = = = = = = = = = =
*/
void R_AddLightSurfaces ( void ) {
viewLight_t * vLight ;
idRenderLightLocal * light ;
viewLight_t * * ptr ;
// go through each visible light, possibly removing some from the list
ptr = & tr . viewDef - > viewLights ;
while ( * ptr ) {
vLight = * ptr ;
light = vLight - > lightDef ;
const idMaterial * lightShader = light - > lightShader ;
if ( ! lightShader ) {
common - > Error ( " R_AddLightSurfaces: NULL lightShader " ) ;
}
// see if we are suppressing the light in this view
if ( ! r_skipSuppress . GetBool ( ) ) {
if ( light - > parms . suppressLightInViewID
& & light - > parms . suppressLightInViewID = = tr . viewDef - > renderView . viewID ) {
* ptr = vLight - > next ;
light - > viewCount = - 1 ;
continue ;
}
2011-12-06 18:20:15 +00:00
if ( light - > parms . allowLightInViewID
2011-11-22 21:28:15 +00:00
& & light - > parms . allowLightInViewID ! = tr . viewDef - > renderView . viewID ) {
* ptr = vLight - > next ;
light - > viewCount = - 1 ;
continue ;
}
}
// evaluate the light shader registers
float * lightRegs = ( float * ) R_FrameAlloc ( lightShader - > GetNumRegisters ( ) * sizeof ( float ) ) ;
vLight - > shaderRegisters = lightRegs ;
lightShader - > EvaluateRegisters ( lightRegs , light - > parms . shaderParms , tr . viewDef , light - > parms . referenceSound ) ;
// 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
if ( ! lightShader - > IsFogLight ( ) & & ! lightShader - > IsBlendLight ( ) ) {
int lightStageNum ;
for ( lightStageNum = 0 ; lightStageNum < lightShader - > GetNumStages ( ) ; lightStageNum + + ) {
const shaderStage_t * lightStage = lightShader - > GetStage ( lightStageNum ) ;
// ignore stages that fail the condition
if ( ! lightRegs [ lightStage - > conditionRegister ] ) {
continue ;
}
const int * registers = lightStage - > color . registers ;
// snap tiny values to zero to avoid lights showing up with the wrong color
if ( lightRegs [ registers [ 0 ] ] < 0.001f ) {
lightRegs [ registers [ 0 ] ] = 0.0f ;
}
if ( lightRegs [ registers [ 1 ] ] < 0.001f ) {
lightRegs [ registers [ 1 ] ] = 0.0f ;
}
if ( lightRegs [ registers [ 2 ] ] < 0.001f ) {
lightRegs [ registers [ 2 ] ] = 0.0f ;
}
// FIXME: when using the following values the light shows up bright red when using nvidia drivers/hardware
// this seems to have been fixed ?
//lightRegs[ registers[0] ] = 1.5143074e-005f;
//lightRegs[ registers[1] ] = 1.5483369e-005f;
//lightRegs[ registers[2] ] = 1.7014690e-005f;
if ( lightRegs [ registers [ 0 ] ] > 0.0f | |
lightRegs [ registers [ 1 ] ] > 0.0f | |
lightRegs [ registers [ 2 ] ] > 0.0f ) {
break ;
}
}
if ( lightStageNum = = lightShader - > GetNumStages ( ) ) {
// 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
* ptr = vLight - > next ;
light - > viewCount = - 1 ;
continue ;
}
}
if ( r_useLightScissors . GetBool ( ) ) {
// calculate the screen area covered by the light frustum
// which will be used to crop the stencil cull
idScreenRect scissorRect = R_CalcLightScissorRectangle ( vLight ) ;
// intersect with the portal crossing scissor rectangle
vLight - > scissorRect . Intersect ( scissorRect ) ;
if ( r_showLightScissors . GetBool ( ) ) {
R_ShowColoredScreenRect ( vLight - > scissorRect , light - > index ) ;
}
}
#if 0
// this never happens, because CullLightByPortals() does a more precise job
if ( vLight - > scissorRect . IsEmpty ( ) ) {
// this light doesn't touch anything on screen, so remove it from the list
* ptr = vLight - > next ;
continue ;
}
# endif
// this one stays on the list
ptr = & vLight - > next ;
// if we are doing a soft-shadow novelty test, regenerate the light with
// a random offset every time
if ( r_lightSourceRadius . GetFloat ( ) ! = 0.0f ) {
for ( int i = 0 ; i < 3 ; i + + ) {
light - > globalLightOrigin [ i ] + = r_lightSourceRadius . GetFloat ( ) * ( - 1 + 2 * ( rand ( ) & 0xfff ) / ( float ) 0xfff ) ;
}
}
// 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
tr . viewDef - > renderWorld - > CreateLightDefInteractions ( light ) ;
tr . pc . c_viewLights + + ;
// fog lights will need to draw the light frustum triangles, so make sure they
// are in the vertex cache
if ( lightShader - > IsFogLight ( ) ) {
if ( ! light - > frustumTris - > ambientCache ) {
if ( ! R_CreateAmbientCache ( light - > frustumTris , false ) ) {
// skip if we are out of vertex memory
continue ;
}
}
// touch the surface so it won't get purged
vertexCache . Touch ( light - > frustumTris - > ambientCache ) ;
}
// add the prelight shadows for the static world geometry
if ( light - > parms . prelightModel & & r_useOptimizedShadows . GetBool ( ) ) {
if ( ! light - > parms . prelightModel - > NumSurfaces ( ) ) {
common - > Error ( " no surfs in prelight model '%s' " , light - > parms . prelightModel - > Name ( ) ) ;
}
srfTriangles_t * tri = light - > parms . prelightModel - > Surface ( 0 ) - > geometry ;
if ( ! tri - > shadowVertexes ) {
common - > Error ( " R_AddLightSurfaces: prelight model '%s' without shadowVertexes " , light - > parms . prelightModel - > Name ( ) ) ;
}
// these shadows will all have valid bounds, and can be culled normally
if ( r_useShadowCulling . GetBool ( ) ) {
if ( R_CullLocalBox ( tri - > bounds , tr . viewDef - > worldSpace . modelMatrix , 5 , tr . viewDef - > frustum ) ) {
continue ;
}
}
// if we have been purged, re-upload the shadowVertexes
if ( ! tri - > shadowCache ) {
R_CreatePrivateShadowCache ( tri ) ;
if ( ! tri - > shadowCache ) {
continue ;
}
}
// touch the shadow surface so it won't get purged
vertexCache . Touch ( tri - > shadowCache ) ;
if ( ! tri - > indexCache & & r_useIndexBuffers . GetBool ( ) ) {
vertexCache . Alloc ( tri - > indexes , tri - > numIndexes * sizeof ( tri - > indexes [ 0 ] ) , & tri - > indexCache , true ) ;
}
if ( tri - > indexCache ) {
vertexCache . Touch ( tri - > indexCache ) ;
}
R_LinkLightSurf ( & vLight - > globalShadows , tri , NULL , light , NULL , vLight - > scissorRect , true /* FIXME? */ ) ;
}
}
}
//===============================================================================================================
/*
= = = = = = = = = = = = = = = = = =
R_IssueEntityDefCallback
= = = = = = = = = = = = = = = = = =
*/
bool R_IssueEntityDefCallback ( idRenderEntityLocal * def ) {
bool update ;
2023-03-28 17:18:55 +00:00
idBounds oldBounds ;
const bool checkBounds = r_checkBounds . GetBool ( ) ;
if ( checkBounds ) {
oldBounds = def - > referenceBounds ;
}
2011-11-22 21:28:15 +00:00
def - > archived = false ; // will need to be written to the demo file
tr . pc . c_entityDefCallbacks + + ;
if ( tr . viewDef ) {
update = def - > parms . callback ( & def - > parms , & tr . viewDef - > renderView ) ;
} else {
update = def - > parms . callback ( & def - > parms , NULL ) ;
}
if ( ! def - > parms . hModel ) {
common - > Error ( " R_IssueEntityDefCallback: dynamic entity callback didn't set model " ) ;
2011-12-16 13:36:30 +00:00
return false ;
2011-11-22 21:28:15 +00:00
}
2023-03-28 17:18:55 +00:00
if ( checkBounds ) {
2011-11-22 21:28:15 +00:00
if ( oldBounds [ 0 ] [ 0 ] > def - > referenceBounds [ 0 ] [ 0 ] + CHECK_BOUNDS_EPSILON | |
oldBounds [ 0 ] [ 1 ] > def - > referenceBounds [ 0 ] [ 1 ] + CHECK_BOUNDS_EPSILON | |
oldBounds [ 0 ] [ 2 ] > def - > referenceBounds [ 0 ] [ 2 ] + CHECK_BOUNDS_EPSILON | |
oldBounds [ 1 ] [ 0 ] < def - > referenceBounds [ 1 ] [ 0 ] - CHECK_BOUNDS_EPSILON | |
oldBounds [ 1 ] [ 1 ] < def - > referenceBounds [ 1 ] [ 1 ] - CHECK_BOUNDS_EPSILON | |
oldBounds [ 1 ] [ 2 ] < def - > referenceBounds [ 1 ] [ 2 ] - CHECK_BOUNDS_EPSILON ) {
common - > Printf ( " entity %i callback extended reference bounds \n " , def - > index ) ;
}
}
return update ;
}
/*
= = = = = = = = = = = = = = = = = = =
R_EntityDefDynamicModel
Issues a deferred entity callback if necessary .
If the model isn ' t dynamic , it returns the original .
Returns the cached dynamic model if present , otherwise creates
it and any necessary overlays
= = = = = = = = = = = = = = = = = = =
*/
idRenderModel * R_EntityDefDynamicModel ( idRenderEntityLocal * def ) {
bool callbackUpdate ;
// allow deferred entities to construct themselves
if ( def - > parms . callback ) {
callbackUpdate = R_IssueEntityDefCallback ( def ) ;
} else {
callbackUpdate = false ;
}
idRenderModel * model = def - > parms . hModel ;
if ( ! model ) {
common - > Error ( " R_EntityDefDynamicModel: NULL model " ) ;
}
if ( model - > IsDynamicModel ( ) = = DM_STATIC ) {
def - > dynamicModel = NULL ;
def - > dynamicModelFrameCount = 0 ;
return model ;
}
// continously animating models (particle systems, etc) will have their snapshot updated every single view
if ( callbackUpdate | | ( model - > IsDynamicModel ( ) = = DM_CONTINUOUS & & def - > dynamicModelFrameCount ! = tr . frameCount ) ) {
R_ClearEntityDefDynamicModel ( def ) ;
}
// if we don't have a snapshot of the dynamic model, generate it now
if ( ! def - > dynamicModel ) {
// instantiate the snapshot of the dynamic model, possibly reusing memory from the cached snapshot
def - > cachedDynamicModel = model - > InstantiateDynamicModel ( & def - > parms , tr . viewDef , def - > cachedDynamicModel ) ;
if ( def - > cachedDynamicModel ) {
// add any overlays to the snapshot of the dynamic model
if ( def - > overlay & & ! r_skipOverlays . GetBool ( ) ) {
def - > overlay - > AddOverlaySurfacesToModel ( def - > cachedDynamicModel ) ;
} else {
idRenderModelOverlay : : RemoveOverlaySurfacesFromModel ( def - > cachedDynamicModel ) ;
}
if ( r_checkBounds . GetBool ( ) ) {
idBounds b = def - > cachedDynamicModel - > Bounds ( ) ;
if ( b [ 0 ] [ 0 ] < def - > referenceBounds [ 0 ] [ 0 ] - CHECK_BOUNDS_EPSILON | |
b [ 0 ] [ 1 ] < def - > referenceBounds [ 0 ] [ 1 ] - CHECK_BOUNDS_EPSILON | |
b [ 0 ] [ 2 ] < def - > referenceBounds [ 0 ] [ 2 ] - CHECK_BOUNDS_EPSILON | |
b [ 1 ] [ 0 ] > def - > referenceBounds [ 1 ] [ 0 ] + CHECK_BOUNDS_EPSILON | |
b [ 1 ] [ 1 ] > def - > referenceBounds [ 1 ] [ 1 ] + CHECK_BOUNDS_EPSILON | |
b [ 1 ] [ 2 ] > def - > referenceBounds [ 1 ] [ 2 ] + CHECK_BOUNDS_EPSILON ) {
common - > Printf ( " entity %i dynamic model exceeded reference bounds \n " , def - > index ) ;
}
}
}
def - > dynamicModel = def - > cachedDynamicModel ;
def - > dynamicModelFrameCount = tr . frameCount ;
}
// set model depth hack value
if ( def - > dynamicModel & & model - > DepthHack ( ) ! = 0.0f & & tr . viewDef ) {
idPlane eye , clip ;
idVec3 ndc ;
R_TransformModelToClip ( def - > parms . origin , tr . viewDef - > worldSpace . modelViewMatrix , tr . viewDef - > projectionMatrix , eye , clip ) ;
R_TransformClipToDevice ( clip , tr . viewDef , ndc ) ;
def - > parms . modelDepthHack = model - > DepthHack ( ) * ( 1.0f - ndc . z ) ;
}
// FIXME: if any of the surfaces have deforms, create a frame-temporary model with references to the
// undeformed surfaces. This would allow deforms to be light interacting.
return def - > dynamicModel ;
}
/*
= = = = = = = = = = = = = = = = =
R_AddDrawSurf
= = = = = = = = = = = = = = = = =
*/
void R_AddDrawSurf ( const srfTriangles_t * tri , const viewEntity_t * space , const renderEntity_t * renderEntity ,
2024-06-13 22:12:00 +00:00
const idMaterial * shader , const idScreenRect & scissor , const float soft_particle_radius )
{
2011-11-22 21:28:15 +00:00
drawSurf_t * drawSurf ;
const float * shaderParms ;
static float refRegs [ MAX_EXPRESSION_REGISTERS ] ; // don't put on stack, or VC++ will do a page touch
float generatedShaderParms [ MAX_ENTITY_SHADER_PARMS ] ;
drawSurf = ( drawSurf_t * ) R_FrameAlloc ( sizeof ( * drawSurf ) ) ;
drawSurf - > geo = tri ;
drawSurf - > space = space ;
drawSurf - > material = shader ;
drawSurf - > scissorRect = scissor ;
drawSurf - > sort = shader - > GetSort ( ) + tr . sortOffset ;
2024-06-13 22:12:00 +00:00
if ( soft_particle_radius ! = - 1.0f ) // #3878
{
drawSurf - > dsFlags = DSF_SOFT_PARTICLE ;
drawSurf - > particle_radius = soft_particle_radius ;
}
else
{
drawSurf - > dsFlags = 0 ;
drawSurf - > particle_radius = 0.0f ;
}
2011-11-22 21:28:15 +00:00
// bumping this offset each time causes surfaces with equal sort orders to still
// deterministically draw in the order they are added
tr . sortOffset + = 0.000001f ;
// if it doesn't fit, resize the list
if ( tr . viewDef - > numDrawSurfs = = tr . viewDef - > maxDrawSurfs ) {
drawSurf_t * * old = tr . viewDef - > drawSurfs ;
int count ;
if ( tr . viewDef - > maxDrawSurfs = = 0 ) {
tr . viewDef - > maxDrawSurfs = INITIAL_DRAWSURFS ;
count = 0 ;
} else {
count = tr . viewDef - > maxDrawSurfs * sizeof ( tr . viewDef - > drawSurfs [ 0 ] ) ;
tr . viewDef - > maxDrawSurfs * = 2 ;
}
tr . viewDef - > drawSurfs = ( drawSurf_t * * ) R_FrameAlloc ( tr . viewDef - > maxDrawSurfs * sizeof ( tr . viewDef - > drawSurfs [ 0 ] ) ) ;
2021-06-22 20:39:02 +00:00
if ( count > 0 )
2024-06-13 22:12:00 +00:00
memcpy ( tr . viewDef - > drawSurfs , old , count ) ;
2011-11-22 21:28:15 +00:00
}
tr . viewDef - > drawSurfs [ tr . viewDef - > numDrawSurfs ] = drawSurf ;
tr . viewDef - > numDrawSurfs + + ;
// process the shader expressions for conditionals / color / texcoords
const float * constRegs = shader - > ConstantRegisters ( ) ;
if ( constRegs ) {
// shader only uses constant values
drawSurf - > shaderRegisters = constRegs ;
} else {
float * regs = ( float * ) R_FrameAlloc ( shader - > GetNumRegisters ( ) * sizeof ( float ) ) ;
drawSurf - > shaderRegisters = regs ;
// a reference shader will take the calculated stage color value from another shader
// and use that for the parm0-parm3 of the current shader, which allows a stage of
// a light model and light flares to pick up different flashing tables from
// different light shaders
if ( renderEntity - > referenceShader ) {
// evaluate the reference shader to find our shader parms
const shaderStage_t * pStage ;
renderEntity - > referenceShader - > EvaluateRegisters ( refRegs , renderEntity - > shaderParms , tr . viewDef , renderEntity - > referenceSound ) ;
pStage = renderEntity - > referenceShader - > GetStage ( 0 ) ;
memcpy ( generatedShaderParms , renderEntity - > shaderParms , sizeof ( generatedShaderParms ) ) ;
generatedShaderParms [ 0 ] = refRegs [ pStage - > color . registers [ 0 ] ] ;
generatedShaderParms [ 1 ] = refRegs [ pStage - > color . registers [ 1 ] ] ;
generatedShaderParms [ 2 ] = refRegs [ pStage - > color . registers [ 2 ] ] ;
shaderParms = generatedShaderParms ;
} else {
// evaluate with the entityDef's shader parms
shaderParms = renderEntity - > shaderParms ;
}
2011-12-02 22:39:30 +00:00
float oldFloatTime = 0.0f ;
int oldTime = 0 ;
2011-11-22 21:28:15 +00:00
if ( space - > entityDef & & space - > entityDef - > parms . timeGroup ) {
oldFloatTime = tr . viewDef - > floatTime ;
oldTime = tr . viewDef - > renderView . time ;
tr . viewDef - > floatTime = game - > GetTimeGroupTime ( space - > entityDef - > parms . timeGroup ) * 0.001 ;
tr . viewDef - > renderView . time = game - > GetTimeGroupTime ( space - > entityDef - > parms . timeGroup ) ;
}
shader - > EvaluateRegisters ( regs , shaderParms , tr . viewDef , renderEntity - > referenceSound ) ;
if ( space - > entityDef & & space - > entityDef - > parms . timeGroup ) {
tr . viewDef - > floatTime = oldFloatTime ;
tr . viewDef - > renderView . time = oldTime ;
}
}
// check for deformations
R_DeformDrawSurf ( drawSurf ) ;
// skybox surfaces need a dynamic texgen
switch ( shader - > Texgen ( ) ) {
case TG_SKYBOX_CUBE :
R_SkyboxTexGen ( drawSurf , tr . viewDef - > renderView . vieworg ) ;
break ;
case TG_WOBBLESKY_CUBE :
R_WobbleskyTexGen ( drawSurf , tr . viewDef - > renderView . vieworg ) ;
break ;
}
// check for gui surfaces
idUserInterface * gui = NULL ;
if ( ! space - > entityDef ) {
gui = shader - > GlobalGui ( ) ;
} else {
int guiNum = shader - > GetEntityGui ( ) - 1 ;
if ( guiNum > = 0 & & guiNum < MAX_RENDERENTITY_GUI ) {
gui = renderEntity - > gui [ guiNum ] ;
}
if ( gui = = NULL ) {
gui = shader - > GlobalGui ( ) ;
}
}
if ( gui ) {
// force guis on the fast time
float oldFloatTime ;
int oldTime ;
oldFloatTime = tr . viewDef - > floatTime ;
oldTime = tr . viewDef - > renderView . time ;
tr . viewDef - > floatTime = game - > GetTimeGroupTime ( 1 ) * 0.001 ;
tr . viewDef - > renderView . time = game - > GetTimeGroupTime ( 1 ) ;
idBounds ndcBounds ;
if ( ! R_PreciseCullSurface ( drawSurf , ndcBounds ) ) {
// did we ever use this to forward an entity color to a gui that didn't set color?
// memcpy( tr.guiShaderParms, shaderParms, sizeof( tr.guiShaderParms ) );
R_RenderGuiSurf ( gui , drawSurf ) ;
}
tr . viewDef - > floatTime = oldFloatTime ;
tr . viewDef - > renderView . time = oldTime ;
}
// we can't add subviews at this point, because that would
// increment tr.viewCount, messing up the rest of the surface
// adds for this view
}
/*
= = = = = = = = = = = = = = =
R_AddAmbientDrawsurfs
Adds surfaces for the given viewEntity
Walks through the viewEntitys list and creates drawSurf_t for each surface of
each viewEntity that has a non - empty scissorRect
= = = = = = = = = = = = = = =
*/
static void R_AddAmbientDrawsurfs ( viewEntity_t * vEntity ) {
int i , total ;
idRenderEntityLocal * def ;
srfTriangles_t * tri ;
idRenderModel * model ;
const idMaterial * shader ;
def = vEntity - > entityDef ;
if ( def - > dynamicModel ) {
model = def - > dynamicModel ;
} else {
model = def - > parms . hModel ;
}
// add all the surfaces
total = model - > NumSurfaces ( ) ;
for ( i = 0 ; i < total ; i + + ) {
const modelSurface_t * surf = model - > Surface ( i ) ;
// for debugging, only show a single surface at a time
if ( r_singleSurface . GetInteger ( ) > = 0 & & i ! = r_singleSurface . GetInteger ( ) ) {
continue ;
}
tri = surf - > geometry ;
if ( ! tri ) {
continue ;
}
if ( ! tri - > numIndexes ) {
continue ;
}
shader = surf - > shader ;
shader = R_RemapShaderBySkin ( shader , def - > parms . customSkin , def - > parms . customShader ) ;
R_GlobalShaderOverride ( & shader ) ;
2011-12-06 18:20:15 +00:00
if ( ! shader ) {
2011-11-22 21:28:15 +00:00
continue ;
}
if ( ! shader - > IsDrawn ( ) ) {
continue ;
}
// debugging tool to make sure we are have the correct pre-calculated bounds
if ( r_checkBounds . GetBool ( ) ) {
int j , k ;
for ( j = 0 ; j < tri - > numVerts ; j + + ) {
for ( k = 0 ; k < 3 ; k + + ) {
if ( tri - > verts [ j ] . xyz [ k ] > tri - > bounds [ 1 ] [ k ] + CHECK_BOUNDS_EPSILON
| | tri - > verts [ j ] . xyz [ k ] < tri - > bounds [ 0 ] [ k ] - CHECK_BOUNDS_EPSILON ) {
common - > Printf ( " bad tri->bounds on %s:%s \n " , def - > parms . hModel - > Name ( ) , shader - > GetName ( ) ) ;
break ;
}
if ( tri - > verts [ j ] . xyz [ k ] > def - > referenceBounds [ 1 ] [ k ] + CHECK_BOUNDS_EPSILON
| | tri - > verts [ j ] . xyz [ k ] < def - > referenceBounds [ 0 ] [ k ] - CHECK_BOUNDS_EPSILON ) {
common - > Printf ( " bad referenceBounds on %s:%s \n " , def - > parms . hModel - > Name ( ) , shader - > GetName ( ) ) ;
break ;
}
}
if ( k ! = 3 ) {
break ;
}
}
}
if ( ! R_CullLocalBox ( tri - > bounds , vEntity - > modelMatrix , 5 , tr . viewDef - > frustum ) ) {
def - > visibleCount = tr . viewCount ;
// make sure we have an ambient cache
if ( ! R_CreateAmbientCache ( tri , shader - > ReceivesLighting ( ) ) ) {
// don't add anything if the vertex cache was too full to give us an ambient cache
return ;
}
// touch it so it won't get purged
vertexCache . Touch ( tri - > ambientCache ) ;
if ( r_useIndexBuffers . GetBool ( ) & & ! tri - > indexCache ) {
vertexCache . Alloc ( tri - > indexes , tri - > numIndexes * sizeof ( tri - > indexes [ 0 ] ) , & tri - > indexCache , true ) ;
}
if ( tri - > indexCache ) {
vertexCache . Touch ( tri - > indexCache ) ;
}
2024-06-13 22:12:00 +00:00
// Soft Particles -- SteveL #3878
float particle_radius = - 1.0f ; // Default = disallow softening, but allow modelDepthHack if specified in the decl.
if ( r_useSoftParticles . GetBool ( )
& & ! shader - > ReceivesLighting ( ) // don't soften surfaces that are meant to be solid
& & tr . viewDef - > renderView . viewID > = 0 ) // Skip during "invisible" rendering passes (e.g. lightgem)
{
const idRenderModelPrt * prt = dynamic_cast < const idRenderModelPrt * > ( def - > parms . hModel ) ; // yuck.
if ( prt )
{
particle_radius = prt - > SofteningRadius ( surf - > id ) ;
}
}
2011-11-22 21:28:15 +00:00
// add the surface for drawing
2024-06-13 22:12:00 +00:00
R_AddDrawSurf ( tri , vEntity , & vEntity - > entityDef - > parms , shader , vEntity - > scissorRect , particle_radius ) ;
2011-11-22 21:28:15 +00:00
// ambientViewCount is used to allow light interactions to be rejected
// if the ambient surface isn't visible at all
tri - > ambientViewCount = tr . viewCount ;
}
}
// add the lightweight decal surfaces
for ( idRenderModelDecal * decal = def - > decals ; decal ; decal = decal - > Next ( ) ) {
decal - > AddDecalDrawSurf ( vEntity ) ;
}
}
/*
= = = = = = = = = = = = = = = = = =
R_CalcEntityScissorRectangle
= = = = = = = = = = = = = = = = = =
*/
idScreenRect R_CalcEntityScissorRectangle ( viewEntity_t * vEntity ) {
idBounds bounds ;
idRenderEntityLocal * def = vEntity - > entityDef ;
tr . viewDef - > viewFrustum . ProjectionBounds ( idBox ( def - > referenceBounds , def - > parms . origin , def - > parms . axis ) , bounds ) ;
return R_ScreenRectFromViewFrustumBounds ( bounds ) ;
}
/*
= = = = = = = = = = = = = = = = = = =
R_AddModelSurfaces
Here is where dynamic models actually get instantiated , and necessary
interactions get created . This is all done on a sort - by - model basis
to keep source data in cache ( most likely L2 ) as any interactions and
shadows are generated , since dynamic models will typically be lit by
two or more lights .
= = = = = = = = = = = = = = = = = = =
*/
void R_AddModelSurfaces ( void ) {
viewEntity_t * vEntity ;
idInteraction * inter , * next ;
idRenderModel * model ;
// clear the ambient surface list
tr . viewDef - > numDrawSurfs = 0 ;
tr . viewDef - > maxDrawSurfs = 0 ; // will be set to INITIAL_DRAWSURFS on R_AddDrawSurf
// go through each entity that is either visible to the view, or to
// any light that intersects the view (for shadows)
for ( vEntity = tr . viewDef - > viewEntitys ; vEntity ; vEntity = vEntity - > next ) {
if ( r_useEntityScissors . GetBool ( ) ) {
// calculate the screen area covered by the entity
idScreenRect scissorRect = R_CalcEntityScissorRectangle ( vEntity ) ;
// intersect with the portal crossing scissor rectangle
vEntity - > scissorRect . Intersect ( scissorRect ) ;
if ( r_showEntityScissors . GetBool ( ) ) {
R_ShowColoredScreenRect ( vEntity - > scissorRect , vEntity - > entityDef - > index ) ;
}
}
2011-12-02 22:39:30 +00:00
float oldFloatTime = 0.0f ;
int oldTime = 0 ;
2011-11-22 21:28:15 +00:00
game - > SelectTimeGroup ( vEntity - > entityDef - > parms . timeGroup ) ;
if ( vEntity - > entityDef - > parms . timeGroup ) {
oldFloatTime = tr . viewDef - > floatTime ;
oldTime = tr . viewDef - > renderView . time ;
tr . viewDef - > floatTime = game - > GetTimeGroupTime ( vEntity - > entityDef - > parms . timeGroup ) * 0.001 ;
tr . viewDef - > renderView . time = game - > GetTimeGroupTime ( vEntity - > entityDef - > parms . timeGroup ) ;
}
if ( tr . viewDef - > isXraySubview & & vEntity - > entityDef - > parms . xrayIndex = = 1 ) {
if ( vEntity - > entityDef - > parms . timeGroup ) {
tr . viewDef - > floatTime = oldFloatTime ;
tr . viewDef - > renderView . time = oldTime ;
}
continue ;
} else if ( ! tr . viewDef - > isXraySubview & & vEntity - > entityDef - > parms . xrayIndex = = 2 ) {
if ( vEntity - > entityDef - > parms . timeGroup ) {
tr . viewDef - > floatTime = oldFloatTime ;
tr . viewDef - > renderView . time = oldTime ;
}
continue ;
}
2024-06-13 22:12:00 +00:00
// Don't let particle entities re-instantiate their dynamic model during non-visible
// views (in TDM, the light gem render) -- SteveL #3970
if ( tr . viewDef - > renderView . viewID < 0
& & dynamic_cast < const idRenderModelPrt * > ( vEntity - > entityDef - > parms . hModel ) ! = NULL ) // yuck.
{
continue ;
}
2011-11-22 21:28:15 +00:00
// add the ambient surface if it has a visible rectangle
if ( ! vEntity - > scissorRect . IsEmpty ( ) ) {
model = R_EntityDefDynamicModel ( vEntity - > entityDef ) ;
if ( model = = NULL | | model - > NumSurfaces ( ) < = 0 ) {
if ( vEntity - > entityDef - > parms . timeGroup ) {
tr . viewDef - > floatTime = oldFloatTime ;
tr . viewDef - > renderView . time = oldTime ;
}
continue ;
}
R_AddAmbientDrawsurfs ( vEntity ) ;
tr . pc . c_visibleViewEntities + + ;
} else {
tr . pc . c_shadowViewEntities + + ;
}
//
// for all the entity / light interactions on this entity, add them to the view
//
if ( tr . viewDef - > isXraySubview ) {
if ( vEntity - > entityDef - > parms . xrayIndex = = 2 ) {
for ( inter = vEntity - > entityDef - > firstInteraction ; inter ! = NULL & & ! inter - > IsEmpty ( ) ; inter = next ) {
next = inter - > entityNext ;
if ( inter - > lightDef - > viewCount ! = tr . viewCount ) {
continue ;
}
inter - > AddActiveInteraction ( ) ;
}
}
} else {
// all empty interactions are at the end of the list so once the
// first is encountered all the remaining interactions are empty
for ( inter = vEntity - > entityDef - > firstInteraction ; inter ! = NULL & & ! inter - > IsEmpty ( ) ; inter = next ) {
next = inter - > entityNext ;
// skip any lights that aren't currently visible
// this is run after any lights that are turned off have already
// been removed from the viewLights list, and had their viewCount cleared
if ( inter - > lightDef - > viewCount ! = tr . viewCount ) {
continue ;
}
inter - > AddActiveInteraction ( ) ;
}
}
if ( vEntity - > entityDef - > parms . timeGroup ) {
tr . viewDef - > floatTime = oldFloatTime ;
tr . viewDef - > renderView . time = oldTime ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
R_RemoveUnecessaryViewLights
= = = = = = = = = = = = = = = = = = = = =
*/
void R_RemoveUnecessaryViewLights ( void ) {
viewLight_t * vLight ;
// go through each visible light
for ( vLight = tr . viewDef - > viewLights ; vLight ; vLight = vLight - > next ) {
// 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
if ( ! vLight - > localInteractions & & ! vLight - > globalInteractions & & ! vLight - > translucentInteractions ) {
vLight - > localShadows = NULL ;
vLight - > globalShadows = NULL ;
}
}
if ( r_useShadowSurfaceScissor . GetBool ( ) ) {
// 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.
for ( vLight = tr . viewDef - > viewLights ; vLight ; vLight = vLight - > next ) {
const drawSurf_t * surf ;
idScreenRect surfRect ;
if ( ! vLight - > lightShader - > LightCastsShadows ( ) ) {
continue ;
}
surfRect . Clear ( ) ;
for ( surf = vLight - > globalInteractions ; surf ; surf = surf - > nextOnLight ) {
surfRect . Union ( surf - > scissorRect ) ;
}
for ( surf = vLight - > localShadows ; surf ; surf = surf - > nextOnLight ) {
const_cast < drawSurf_t * > ( surf ) - > scissorRect . Intersect ( surfRect ) ;
}
for ( surf = vLight - > localInteractions ; surf ; surf = surf - > nextOnLight ) {
surfRect . Union ( surf - > scissorRect ) ;
}
for ( surf = vLight - > globalShadows ; surf ; surf = surf - > nextOnLight ) {
const_cast < drawSurf_t * > ( surf ) - > scissorRect . Intersect ( surfRect ) ;
}
for ( surf = vLight - > translucentInteractions ; surf ; surf = surf - > nextOnLight ) {
surfRect . Union ( surf - > scissorRect ) ;
}
vLight - > scissorRect . Intersect ( surfRect ) ;
}
}
}