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 .
2024-08-21 16:39:52 +00:00
Copyright ( C ) 2014 - 2024 Robert Beckebans
2016-07-02 09:22:04 +00:00
Copyright ( C ) 2014 - 2016 Kot in Action Creative Artel
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 .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-12-22 15:18:19 +00:00
# include "precompiled.h"
2021-07-29 13:42:02 +00:00
# pragma hdrstop
2012-11-26 18:58:24 +00:00
2024-08-27 09:41:39 +00:00
# if defined(USE_INTRINSICS_SSE)
2024-08-30 16:41:03 +00:00
# if MOC_MULTITHREADED
2024-09-02 19:00:01 +00:00
# include "CullingThreadPool.h"
2024-08-30 16:41:03 +00:00
# else
# include "../libs/moc/MaskedOcclusionCulling.h"
# endif
2024-08-27 09:41:39 +00:00
# endif
2024-08-21 22:05:04 +00:00
2017-09-03 08:22:36 +00:00
# include "RenderCommon.h"
2012-11-26 18:58:24 +00:00
# include "Model_local.h"
idCVar r_skipStaticShadows ( " r_skipStaticShadows " , " 0 " , CVAR_RENDERER | CVAR_BOOL , " skip static shadows " ) ;
idCVar r_skipDynamicShadows ( " r_skipDynamicShadows " , " 0 " , CVAR_RENDERER | CVAR_BOOL , " skip dynamic shadows " ) ;
2024-01-17 05:01:28 +00:00
idCVar r_useParallelAddModels ( " r_useParallelAddModels " , " 1 " , CVAR_RENDERER | CVAR_BOOL | CVAR_NOCHEAT , " add all models in parallel with jobs " ) ;
idCVar r_useParallelAddShadows ( " r_useParallelAddShadows " , " 1 " , CVAR_RENDERER | CVAR_INTEGER | CVAR_NOCHEAT , " 0 = off, 1 = threaded " , 0 , 1 ) ;
2012-11-26 18:58:24 +00:00
idCVar r_forceShadowCaps ( " r_forceShadowCaps " , " 0 " , CVAR_RENDERER | CVAR_BOOL , " 0 = skip rendering shadow caps if view is outside shadow volume, 1 = always render shadow caps " ) ;
2014-05-11 12:36:51 +00:00
// RB begin
idCVar r_forceShadowMapsOnAlphaTestedSurfaces ( " r_forceShadowMapsOnAlphaTestedSurfaces " , " 1 " , CVAR_RENDERER | CVAR_BOOL , " 0 = same shadowing as with stencil shadows, 1 = ignore noshadows for alpha tested materials " ) ;
// RB end
2016-07-02 09:22:04 +00:00
// foresthale 2014-11-24: cvar to control the material lod flags - this is the distance at which a mesh switches from lod1 to lod2, where lod3 will appear at this distance *2, lod4 at *4, and persistentLOD keyword will disable the max distance check (thus extending this LOD to all further distances, rather than disappearing)
idCVar r_lodMaterialDistance ( " r_lodMaterialDistance " , " 500 " , CVAR_RENDERER | CVAR_FLOAT , " surfaces further than this distance will use lower quality versions (if their material uses the lod1-4 keywords, persistentLOD disables the max distance checks) " ) ;
2012-11-26 18:58:24 +00:00
static const float CHECK_BOUNDS_EPSILON = 1.0f ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
/*
= = = = = = = = = = = = = = = = = =
R_ClearEntityDefDynamicModel
If we know the reference bounds stays the same , we
only need to do this on entity update , not the full
R_FreeEntityDefDerivedData
= = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void R_ClearEntityDefDynamicModel ( idRenderEntityLocal * def )
{
2012-11-26 18:58:24 +00:00
// free all the interaction surfaces
2012-11-28 15:47:07 +00:00
for ( idInteraction * inter = def - > firstInteraction ; inter ! = NULL & & ! inter - > IsEmpty ( ) ; inter = inter - > entityNext )
{
2012-11-26 18:58:24 +00:00
inter - > FreeSurfaces ( ) ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// clear the dynamic model if present
2012-11-28 15:47:07 +00:00
if ( def - > dynamicModel )
{
2012-11-26 18:58:24 +00:00
// this is copied from cachedDynamicModel, so it doesn't need to be freed
def - > dynamicModel = NULL ;
}
def - > dynamicModelFrameCount = 0 ;
}
/*
= = = = = = = = = = = = = = = = = =
R_IssueEntityDefCallback
= = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool R_IssueEntityDefCallback ( idRenderEntityLocal * def )
{
2012-11-26 18:58:24 +00:00
idBounds oldBounds = def - > localReferenceBounds ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
bool update ;
2012-11-28 15:47:07 +00:00
if ( tr . viewDef ! = NULL )
{
2012-11-26 18:58:24 +00:00
update = def - > parms . callback ( & def - > parms , & tr . viewDef - > renderView ) ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
update = def - > parms . callback ( & def - > parms , NULL ) ;
}
tr . pc . c_entityDefCallbacks + + ;
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( def - > parms . hModel = = NULL )
{
2012-11-26 18:58:24 +00:00
common - > Error ( " R_IssueEntityDefCallback: dynamic entity callback didn't set model " ) ;
}
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( r_checkBounds . GetBool ( ) )
{
if ( oldBounds [ 0 ] [ 0 ] > def - > localReferenceBounds [ 0 ] [ 0 ] + CHECK_BOUNDS_EPSILON | |
2012-11-26 18:58:24 +00:00
oldBounds [ 0 ] [ 1 ] > def - > localReferenceBounds [ 0 ] [ 1 ] + CHECK_BOUNDS_EPSILON | |
oldBounds [ 0 ] [ 2 ] > def - > localReferenceBounds [ 0 ] [ 2 ] + CHECK_BOUNDS_EPSILON | |
oldBounds [ 1 ] [ 0 ] < def - > localReferenceBounds [ 1 ] [ 0 ] - CHECK_BOUNDS_EPSILON | |
oldBounds [ 1 ] [ 1 ] < def - > localReferenceBounds [ 1 ] [ 1 ] - CHECK_BOUNDS_EPSILON | |
2012-11-28 15:47:07 +00:00
oldBounds [ 1 ] [ 2 ] < def - > localReferenceBounds [ 1 ] [ 2 ] - CHECK_BOUNDS_EPSILON )
{
2012-11-26 18:58:24 +00:00
common - > Printf ( " entity %i callback extended reference bounds \n " , def - > index ) ;
}
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
return update ;
}
/*
= = = = = = = = = = = = = = = = = = =
R_EntityDefDynamicModel
This is also called by the game code for idRenderWorldLocal : : ModelTrace ( ) , and idRenderWorldLocal : : Trace ( ) which is bad for performance . . .
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 .
= = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idRenderModel * R_EntityDefDynamicModel ( idRenderEntityLocal * def )
{
if ( def - > dynamicModelFrameCount = = tr . frameCount )
{
2012-11-26 18:58:24 +00:00
return def - > dynamicModel ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// allow deferred entities to construct themselves
bool callbackUpdate ;
2012-11-28 15:47:07 +00:00
if ( def - > parms . callback ! = NULL )
{
2012-11-26 18:58:24 +00:00
SCOPED_PROFILE_EVENT ( " R_IssueEntityDefCallback " ) ;
callbackUpdate = R_IssueEntityDefCallback ( def ) ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
callbackUpdate = false ;
}
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
idRenderModel * model = def - > parms . hModel ;
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( model = = NULL )
{
2012-11-26 18:58:24 +00:00
common - > Error ( " R_EntityDefDynamicModel: NULL model " ) ;
return NULL ;
}
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( model - > IsDynamicModel ( ) = = DM_STATIC )
{
2012-11-26 18:58:24 +00:00
def - > dynamicModel = NULL ;
def - > dynamicModelFrameCount = 0 ;
return model ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// continously animating models (particle systems, etc) will have their snapshot updated every single view
2012-11-28 15:47:07 +00:00
if ( callbackUpdate | | ( model - > IsDynamicModel ( ) = = DM_CONTINUOUS & & def - > dynamicModelFrameCount ! = tr . frameCount ) )
{
2012-11-26 18:58:24 +00:00
R_ClearEntityDefDynamicModel ( def ) ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// if we don't have a snapshot of the dynamic model, generate it now
2012-11-28 15:47:07 +00:00
if ( def - > dynamicModel = = NULL )
{
2012-11-26 18:58:24 +00:00
SCOPED_PROFILE_EVENT ( " InstantiateDynamicModel " ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// instantiate the snapshot of the dynamic model, possibly reusing memory from the cached snapshot
def - > cachedDynamicModel = model - > InstantiateDynamicModel ( & def - > parms , tr . viewDef , def - > cachedDynamicModel ) ;
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( def - > cachedDynamicModel ! = NULL & & r_checkBounds . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
idBounds b = def - > cachedDynamicModel - > Bounds ( ) ;
2012-11-28 15:47:07 +00:00
if ( b [ 0 ] [ 0 ] < def - > localReferenceBounds [ 0 ] [ 0 ] - CHECK_BOUNDS_EPSILON | |
2012-11-26 18:58:24 +00:00
b [ 0 ] [ 1 ] < def - > localReferenceBounds [ 0 ] [ 1 ] - CHECK_BOUNDS_EPSILON | |
b [ 0 ] [ 2 ] < def - > localReferenceBounds [ 0 ] [ 2 ] - CHECK_BOUNDS_EPSILON | |
b [ 1 ] [ 0 ] > def - > localReferenceBounds [ 1 ] [ 0 ] + CHECK_BOUNDS_EPSILON | |
b [ 1 ] [ 1 ] > def - > localReferenceBounds [ 1 ] [ 1 ] + CHECK_BOUNDS_EPSILON | |
2012-11-28 15:47:07 +00:00
b [ 1 ] [ 2 ] > def - > localReferenceBounds [ 1 ] [ 2 ] + CHECK_BOUNDS_EPSILON )
{
2012-11-26 18:58:24 +00:00
common - > Printf ( " entity %i dynamic model exceeded reference bounds \n " , def - > index ) ;
}
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
def - > dynamicModel = def - > cachedDynamicModel ;
def - > dynamicModelFrameCount = tr . frameCount ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// set model depth hack value
2012-11-28 15:47:07 +00:00
if ( def - > dynamicModel ! = NULL & & model - > DepthHack ( ) ! = 0.0f & & tr . viewDef ! = NULL )
{
2012-11-26 18:58:24 +00:00
idPlane eye , clip ;
idVec3 ndc ;
R_TransformModelToClip ( def - > parms . origin , tr . viewDef - > worldSpace . modelViewMatrix , tr . viewDef - > projectionMatrix , eye , clip ) ;
R_TransformClipToDevice ( clip , ndc ) ;
def - > parms . modelDepthHack = model - > DepthHack ( ) * ( 1.0f - ndc . z ) ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
def - > parms . modelDepthHack = 0.0f ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
return def - > dynamicModel ;
}
/*
= = = = = = = = = = = = = = = = = = =
R_SetupDrawSurfShader
= = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void R_SetupDrawSurfShader ( drawSurf_t * drawSurf , const idMaterial * shader , const renderEntity_t * renderEntity )
{
2012-11-26 18:58:24 +00:00
drawSurf - > material = shader ;
drawSurf - > sort = shader - > GetSort ( ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// process the shader expressions for conditionals / color / texcoords
2012-11-28 15:47:07 +00:00
const float * constRegs = shader - > ConstantRegisters ( ) ;
if ( likely ( constRegs ! = NULL ) )
{
2012-11-26 18:58:24 +00:00
// shader only uses constant values
drawSurf - > shaderRegisters = constRegs ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
// by default evaluate with the entityDef's shader parms
2012-11-28 15:47:07 +00:00
const float * shaderParms = renderEntity - > shaderParms ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// 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
float generatedShaderParms [ MAX_ENTITY_SHADER_PARMS ] ;
2012-11-28 15:47:07 +00:00
if ( unlikely ( renderEntity - > referenceShader ! = NULL ) )
{
2012-11-26 18:58:24 +00:00
// evaluate the reference shader to find our shader parms
float refRegs [ MAX_EXPRESSION_REGISTERS ] ;
renderEntity - > referenceShader - > EvaluateRegisters ( refRegs , renderEntity - > shaderParms ,
2012-11-28 15:47:07 +00:00
tr . viewDef - > renderView . shaderParms ,
tr . viewDef - > renderView . time [ renderEntity - > timeGroup ] * 0.001f , renderEntity - > referenceSound ) ;
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
const shaderStage_t * pStage = renderEntity - > referenceShader - > GetStage ( 0 ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
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 ] ] ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
shaderParms = generatedShaderParms ;
}
2019-11-11 19:27:44 +00:00
2016-07-02 09:22:04 +00:00
// allocate frame memory for the shader register values
2012-11-28 15:47:07 +00:00
float * regs = ( float * ) R_FrameAlloc ( shader - > GetNumRegisters ( ) * sizeof ( float ) , FRAME_ALLOC_SHADER_REGISTER ) ;
2012-11-26 18:58:24 +00:00
drawSurf - > shaderRegisters = regs ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// process the shader expressions for conditionals / color / texcoords
shader - > EvaluateRegisters ( regs , shaderParms , tr . viewDef - > renderView . shaderParms ,
2012-11-28 15:47:07 +00:00
tr . viewDef - > renderView . time [ renderEntity - > timeGroup ] * 0.001f , renderEntity - > referenceSound ) ;
2012-11-26 18:58:24 +00:00
}
}
/*
= = = = = = = = = = = = = = = = = = =
R_SetupDrawSurfJoints
= = = = = = = = = = = = = = = = = = =
*/
2022-02-21 16:59:13 +00:00
void R_SetupDrawSurfJoints ( drawSurf_t * drawSurf , const srfTriangles_t * tri , const idMaterial * shader , nvrhi : : ICommandList * commandList )
2012-11-28 15:47:07 +00:00
{
2023-03-16 19:49:30 +00:00
// RB: added check wether GPU skinning is available at all
2023-03-10 17:39:53 +00:00
if ( tri - > staticModelWithJoints = = NULL | | ! r_useGPUSkinning . GetBool ( ) )
2012-11-28 15:47:07 +00:00
{
2012-11-26 18:58:24 +00:00
drawSurf - > jointCache = 0 ;
return ;
}
2014-04-20 14:29:58 +00:00
// RB end
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
idRenderModelStatic * model = tri - > staticModelWithJoints ;
2012-11-26 18:58:24 +00:00
assert ( model - > jointsInverted ! = NULL ) ;
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( ! vertexCache . CacheIsCurrent ( model - > jointsInvertedBuffer ) )
{
2022-02-21 16:59:13 +00:00
model - > jointsInvertedBuffer = vertexCache . AllocJoint ( model - > jointsInverted , model - > numInvertedJoints , sizeof ( idJointMat ) , commandList ) ;
2012-11-26 18:58:24 +00:00
}
drawSurf - > jointCache = model - > jointsInvertedBuffer ;
}
/*
= = = = = = = = = = = = = = = = = = =
R_AddSingleModel
May be run in parallel .
Here is where dynamic models actually get instantiated , and necessary
interaction surfaces 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 .
= = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void R_AddSingleModel ( viewEntity_t * vEntity )
{
2012-11-26 18:58:24 +00:00
// we will add all interaction surfs here, to be chained to the lights in later serial code
vEntity - > drawSurfs = NULL ;
2019-11-11 19:27:44 +00:00
2021-04-15 19:28:50 +00:00
// RB
vEntity - > useLightGrid = false ;
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 ;
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
idRenderEntityLocal * entityDef = vEntity - > entityDef ;
const renderEntity_t * renderEntity = & entityDef - > parms ;
const idRenderWorldLocal * world = entityDef - > world ;
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( viewDef - > isXraySubview & & entityDef - > parms . xrayIndex = = 1 )
{
2012-11-26 18:58:24 +00:00
return ;
2012-11-28 15:47:07 +00:00
}
else if ( ! viewDef - > isXraySubview & & entityDef - > parms . xrayIndex = = 2 )
{
2012-11-26 18:58:24 +00:00
return ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
SCOPED_PROFILE_EVENT ( renderEntity - > hModel = = NULL ? " Unknown Model " : renderEntity - > hModel - > Name ( ) ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// calculate the znear for testing whether or not the view is inside a shadow projection
const float znear = ( viewDef - > renderView . cramZNear ) ? ( r_znear . GetFloat ( ) * 0.25f ) : r_znear . GetFloat ( ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// if the entity wasn't seen through a portal chain, it was added just for light shadows
const bool modelIsVisible = ! vEntity - > scissorRect . IsEmpty ( ) ;
const bool addInteractions = modelIsVisible & & ( ! viewDef - > isXraySubview | | entityDef - > parms . xrayIndex = = 2 ) ;
const int entityIndex = entityDef - > index ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
//---------------------------
// Find which of the visible lights contact this entity
//
// If the entity doesn't accept light or cast shadows from any surface,
// this can be skipped.
//
// OPTIMIZE: world areas can assume all referenced lights are used
//---------------------------
int numContactedLights = 0 ;
static const int MAX_CONTACTED_LIGHTS = 128 ;
2012-11-28 15:47:07 +00:00
viewLight_t * contactedLights [ MAX_CONTACTED_LIGHTS ] ;
idInteraction * staticInteractions [ MAX_CONTACTED_LIGHTS ] ;
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( renderEntity - > hModel = = NULL | |
2012-11-26 18:58:24 +00:00
renderEntity - > hModel - > ModelHasInteractingSurfaces ( ) | |
2012-11-28 15:47:07 +00:00
renderEntity - > hModel - > ModelHasShadowCastingSurfaces ( ) )
{
2012-11-26 18:58:24 +00:00
SCOPED_PROFILE_EVENT ( " Find lights " ) ;
2012-11-28 15:47:07 +00:00
for ( viewLight_t * vLight = viewDef - > viewLights ; vLight ! = NULL ; vLight = vLight - > next )
{
if ( vLight - > scissorRect . IsEmpty ( ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2012-11-28 15:47:07 +00:00
if ( vLight - > entityInteractionState ! = NULL )
{
2012-11-26 18:58:24 +00:00
// new code path, everything was done in AddLight
2012-11-28 15:47:07 +00:00
if ( vLight - > entityInteractionState [ entityIndex ] = = viewLight_t : : INTERACTION_YES )
{
2012-11-26 18:58:24 +00:00
contactedLights [ numContactedLights ] = vLight ;
staticInteractions [ numContactedLights ] = world - > interactionTable [ vLight - > lightDef - > index * world - > interactionTableWidth + entityIndex ] ;
2012-11-28 15:47:07 +00:00
if ( + + numContactedLights = = MAX_CONTACTED_LIGHTS )
{
2012-11-26 18:58:24 +00:00
break ;
}
}
continue ;
}
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
const idRenderLightLocal * lightDef = vLight - > lightDef ;
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( ! lightDef - > globalLightBounds . IntersectsBounds ( entityDef - > globalReferenceBounds ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( R_CullModelBoundsToLight ( lightDef , entityDef - > localReferenceBounds , entityDef - > modelRenderMatrix ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( ! modelIsVisible )
{
2012-11-26 18:58:24 +00:00
// some lights have their center of projection outside the world
2012-11-28 15:47:07 +00:00
if ( lightDef - > areaNum ! = - 1 )
{
2012-11-26 18:58:24 +00:00
// if no part of the model is in an area that is connected to
// the light center (it is behind a solid, closed door), we can ignore it
bool areasConnected = false ;
2012-11-28 15:47:07 +00:00
for ( areaReference_t * ref = entityDef - > entityRefs ; ref ! = NULL ; ref = ref - > ownerNext )
{
if ( world - > AreasAreConnected ( lightDef - > areaNum , ref - > area - > areaNum , PS_BLOCK_VIEW ) )
{
2012-11-26 18:58:24 +00:00
areasConnected = true ;
break ;
}
}
2012-11-28 15:47:07 +00:00
if ( areasConnected = = false )
{
2012-11-26 18:58:24 +00:00
// can't possibly be seen or shadowed
continue ;
}
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// check more precisely for shadow visibility
idBounds shadowBounds ;
R_ShadowBounds ( entityDef - > globalReferenceBounds , lightDef - > globalLightBounds , lightDef - > globalLightOrigin , shadowBounds ) ;
2019-11-11 19:27:44 +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
2012-11-28 15:47:07 +00:00
if ( idRenderMatrix : : CullBoundsToMVP ( viewDef - > worldSpace . mvp , shadowBounds ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
}
contactedLights [ numContactedLights ] = vLight ;
staticInteractions [ numContactedLights ] = world - > interactionTable [ vLight - > lightDef - > index * world - > interactionTableWidth + entityIndex ] ;
2012-11-28 15:47:07 +00:00
if ( + + numContactedLights = = MAX_CONTACTED_LIGHTS )
{
2012-11-26 18:58:24 +00:00
break ;
}
}
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// if we aren't visible and none of the shadows stretch into the view,
// we don't need to do anything else
2012-11-28 15:47:07 +00:00
if ( ! modelIsVisible & & numContactedLights = = 0 )
{
2012-11-26 18:58:24 +00:00
return ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
//---------------------------
// create a dynamic model if the geometry isn't static
//---------------------------
2012-11-28 15:47:07 +00:00
idRenderModel * model = R_EntityDefDynamicModel ( entityDef ) ;
if ( model = = NULL | | model - > NumSurfaces ( ) < = 0 )
{
2012-11-26 18:58:24 +00:00
return ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// add the lightweight blood decal surfaces if the model is directly visible
2012-11-28 15:47:07 +00:00
if ( modelIsVisible )
{
2012-11-26 18:58:24 +00:00
assert ( ! vEntity - > scissorRect . IsEmpty ( ) ) ;
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( entityDef - > decals ! = NULL & & ! r_skipDecals . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
entityDef - > decals - > CreateDeferredDecals ( model ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
unsigned int numDrawSurfs = entityDef - > decals - > GetNumDecalDrawSurfs ( ) ;
2012-11-28 15:47:07 +00:00
for ( unsigned int i = 0 ; i < numDrawSurfs ; i + + )
{
drawSurf_t * decalDrawSurf = entityDef - > decals - > CreateDecalDrawSurf ( vEntity , i ) ;
if ( decalDrawSurf ! = NULL )
{
2012-11-26 18:58:24 +00:00
decalDrawSurf - > linkChain = NULL ;
decalDrawSurf - > nextOnLight = vEntity - > drawSurfs ;
vEntity - > drawSurfs = decalDrawSurf ;
}
}
}
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( entityDef - > overlays ! = NULL & & ! r_skipOverlays . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
entityDef - > overlays - > CreateDeferredOverlays ( model ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
unsigned int numDrawSurfs = entityDef - > overlays - > GetNumOverlayDrawSurfs ( ) ;
2012-11-28 15:47:07 +00:00
for ( unsigned int i = 0 ; i < numDrawSurfs ; i + + )
{
drawSurf_t * overlayDrawSurf = entityDef - > overlays - > CreateOverlayDrawSurf ( vEntity , model , i ) ;
if ( overlayDrawSurf ! = NULL )
{
2012-11-26 18:58:24 +00:00
overlayDrawSurf - > linkChain = NULL ;
overlayDrawSurf - > nextOnLight = vEntity - > drawSurfs ;
vEntity - > drawSurfs = overlayDrawSurf ;
}
}
}
}
2019-11-11 19:27:44 +00:00
2021-04-15 19:28:50 +00:00
// RB: use first valid lightgrid
for ( areaReference_t * ref = entityDef - > entityRefs ; ref ! = NULL ; ref = ref - > ownerNext )
{
idImage * lightGridImage = ref - > area - > lightGrid . GetIrradianceImage ( ) ;
if ( ref - > area - > lightGrid . lightGridPoints . Num ( ) & & lightGridImage & & ! lightGridImage - > IsDefaulted ( ) )
{
vEntity - > useLightGrid = true ;
2021-04-26 14:31:04 +00:00
vEntity - > lightGridAtlasImage = lightGridImage ;
vEntity - > lightGridAtlasSingleProbeSize = ref - > area - > lightGrid . imageSingleProbeSize ;
vEntity - > lightGridAtlasBorderSize = ref - > area - > lightGrid . imageBorderSize ;
2021-04-15 19:28:50 +00:00
for ( int i = 0 ; i < 3 ; i + + )
{
vEntity - > lightGridOrigin [ i ] = ref - > area - > lightGrid . lightGridOrigin [ i ] ;
vEntity - > lightGridSize [ i ] = ref - > area - > lightGrid . lightGridSize [ i ] ;
vEntity - > lightGridBounds [ i ] = ref - > area - > lightGrid . lightGridBounds [ i ] ;
}
break ;
}
}
// RB end
2012-11-26 18:58:24 +00:00
//---------------------------
// copy matrix related stuff for back-end use
// and setup a render matrix for faster culling
//---------------------------
vEntity - > modelDepthHack = renderEntity - > modelDepthHack ;
vEntity - > weaponDepthHack = renderEntity - > weaponDepthHack ;
vEntity - > skipMotionBlur = renderEntity - > skipMotionBlur ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
memcpy ( vEntity - > modelMatrix , entityDef - > modelMatrix , sizeof ( vEntity - > modelMatrix ) ) ;
R_MatrixMultiply ( entityDef - > modelMatrix , viewDef - > worldSpace . modelViewMatrix , vEntity - > modelViewMatrix ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
idRenderMatrix viewMat ;
2012-11-28 15:47:07 +00:00
idRenderMatrix : : Transpose ( * ( idRenderMatrix * ) vEntity - > modelViewMatrix , viewMat ) ;
2012-11-26 18:58:24 +00:00
idRenderMatrix : : Multiply ( viewDef - > projectionRenderMatrix , viewMat , vEntity - > mvp ) ;
2024-08-21 22:24:17 +00:00
idRenderMatrix : : Multiply ( viewDef - > unjitteredProjectionRenderMatrix , viewMat , vEntity - > unjitteredMVP ) ;
2012-11-28 15:47:07 +00:00
if ( renderEntity - > weaponDepthHack )
{
2012-11-26 18:58:24 +00:00
idRenderMatrix : : ApplyDepthHack ( vEntity - > mvp ) ;
}
2012-11-28 15:47:07 +00:00
if ( renderEntity - > modelDepthHack ! = 0.0f )
{
2012-11-26 18:58:24 +00:00
idRenderMatrix : : ApplyModelDepthHack ( vEntity - > mvp , renderEntity - > modelDepthHack ) ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// local light and view origins are used to determine if the view is definitely outside
// an extruded shadow volume, which means we can skip drawing the end caps
idVec3 localViewOrigin ;
R_GlobalPointToLocal ( vEntity - > modelMatrix , viewDef - > renderView . vieworg , localViewOrigin ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
//---------------------------
// add all the model surfaces
//---------------------------
2012-11-28 15:47:07 +00:00
for ( int surfaceNum = 0 ; surfaceNum < model - > NumSurfaces ( ) ; surfaceNum + + )
{
const modelSurface_t * surf = model - > Surface ( surfaceNum ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// for debugging, only show a single surface at a time
2012-11-28 15:47:07 +00:00
if ( r_singleSurface . GetInteger ( ) > = 0 & & surfaceNum ! = r_singleSurface . GetInteger ( ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
srfTriangles_t * tri = surf - > geometry ;
if ( tri = = NULL )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2012-12-11 22:48:55 +00:00
if ( tri - > numIndexes = = 0 )
2012-11-28 15:47:07 +00:00
{
2012-11-26 18:58:24 +00:00
continue ; // happens for particles
}
2012-11-28 15:47:07 +00:00
const idMaterial * shader = surf - > shader ;
if ( shader = = NULL )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2019-11-11 19:27:44 +00:00
2016-07-02 09:22:04 +00:00
// motorsep 11-24-2014; checking for LOD surface for LOD1 iteration
if ( shader - > IsLOD ( ) )
{
// foresthale 2014-11-24: calculate the bounds and get the distance from camera to bounds
idBounds & localBounds = tri - > bounds ;
if ( tri - > staticModelWithJoints )
{
// skeletal models have difficult to compute bounds for surfaces, so use the whole entity
localBounds = vEntity - > entityDef - > localReferenceBounds ;
}
const float * bounds = localBounds . ToFloatPtr ( ) ;
idVec3 nearestPointOnBounds = localViewOrigin ;
nearestPointOnBounds . x = Max ( nearestPointOnBounds . x , bounds [ 0 ] ) ;
nearestPointOnBounds . x = Min ( nearestPointOnBounds . x , bounds [ 3 ] ) ;
nearestPointOnBounds . y = Max ( nearestPointOnBounds . y , bounds [ 1 ] ) ;
nearestPointOnBounds . y = Min ( nearestPointOnBounds . y , bounds [ 4 ] ) ;
nearestPointOnBounds . z = Max ( nearestPointOnBounds . z , bounds [ 2 ] ) ;
nearestPointOnBounds . z = Min ( nearestPointOnBounds . z , bounds [ 5 ] ) ;
idVec3 delta = nearestPointOnBounds - localViewOrigin ;
float distance = delta . LengthFast ( ) ;
2019-11-11 19:27:44 +00:00
2016-07-02 09:22:04 +00:00
if ( ! shader - > IsLODVisibleForDistance ( distance , r_lodMaterialDistance . GetFloat ( ) ) )
{
continue ;
}
}
2019-11-11 19:27:44 +00:00
2016-07-02 09:22:04 +00:00
// foresthale 2014-09-01: don't skip surfaces that use the "forceShadows" flag
if ( ! shader - > IsDrawn ( ) & & ! shader - > SurfaceCastsShadow ( ) )
2012-11-28 15:47:07 +00:00
{
2012-11-26 18:58:24 +00:00
continue ; // collision hulls, etc
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// RemapShaderBySkin
2012-11-28 15:47:07 +00:00
if ( entityDef - > parms . customShader ! = NULL )
{
2012-11-26 18:58:24 +00:00
// this is sort of a hack, but causes deformed surfaces to map to empty surfaces,
// so the item highlight overlay doesn't highlight the autosprite surface
2012-11-28 15:47:07 +00:00
if ( shader - > Deform ( ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
shader = entityDef - > parms . customShader ;
2012-11-28 15:47:07 +00:00
}
else if ( entityDef - > parms . customSkin )
{
2012-11-26 18:58:24 +00:00
shader = entityDef - > parms . customSkin - > RemapShaderBySkin ( shader ) ;
2012-11-28 15:47:07 +00:00
if ( shader = = NULL )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2016-07-02 09:22:04 +00:00
// foresthale 2014-09-01: don't skip surfaces that use the "forceShadows" flag
if ( ! shader - > IsDrawn ( ) & & ! shader - > SurfaceCastsShadow ( ) )
2012-11-28 15:47:07 +00:00
{
2012-11-26 18:58:24 +00:00
continue ;
}
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// optionally override with the renderView->globalMaterial
2012-11-28 15:47:07 +00:00
if ( tr . primaryRenderView . globalMaterial ! = NULL )
{
2012-11-26 18:58:24 +00:00
shader = tr . primaryRenderView . globalMaterial ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
SCOPED_PROFILE_EVENT ( shader - > GetName ( ) ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// debugging tool to make sure we have the correct pre-calculated bounds
2012-11-28 15:47:07 +00:00
if ( r_checkBounds . GetBool ( ) )
{
for ( int j = 0 ; j < tri - > numVerts ; j + + )
{
2012-11-26 18:58:24 +00:00
int k ;
2012-11-28 15:47:07 +00:00
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 )
{
2012-11-26 18:58:24 +00:00
common - > Printf ( " bad tri->bounds on %s:%s \n " , entityDef - > parms . hModel - > Name ( ) , shader - > GetName ( ) ) ;
break ;
}
2012-11-28 15:47:07 +00:00
if ( tri - > verts [ j ] . xyz [ k ] > entityDef - > localReferenceBounds [ 1 ] [ k ] + CHECK_BOUNDS_EPSILON
| | tri - > verts [ j ] . xyz [ k ] < entityDef - > localReferenceBounds [ 0 ] [ k ] - CHECK_BOUNDS_EPSILON )
{
2012-11-26 18:58:24 +00:00
common - > Printf ( " bad referenceBounds on %s:%s \n " , entityDef - > parms . hModel - > Name ( ) , shader - > GetName ( ) ) ;
break ;
}
}
2012-11-28 15:47:07 +00:00
if ( k ! = 3 )
{
2012-11-26 18:58:24 +00:00
break ;
}
}
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// view frustum culling for the precise surface bounds, which is tighter
// than the entire entity reference bounds
// If the entire model wasn't visible, there is no need to check the
// individual surfaces.
2024-08-21 22:05:04 +00:00
bool surfaceDirectlyVisible = modelIsVisible & & ! idRenderMatrix : : CullBoundsToMVP ( vEntity - > mvp , tri - > bounds ) ;
2024-08-22 20:05:47 +00:00
// RB: added check wether GPU skinning is available at all
const bool gpuSkinned = ( tri - > staticModelWithJoints ! = NULL & & r_useGPUSkinning . GetBool ( ) ) ;
2024-08-27 21:01:08 +00:00
//const char* shaderName = shader->GetName();
//if( idStr::Cmp( shaderName, "textures/rock/sharprock_dark") == 0 )
//{
// tr.pc.c_mocTests += 0;
//}
2024-08-27 09:41:39 +00:00
# if defined(USE_INTRINSICS_SSE)
2024-08-26 08:26:40 +00:00
2024-08-27 21:01:08 +00:00
const bool viewInsideSurface = tri - > bounds . ContainsPoint ( localViewOrigin ) ;
//if( viewInsideSurface && idStr::Cmp( shaderName, "models/weapons/berserk/fist") != 0 )
//{
// tr.pc.c_mocTests += 1;
//
// tr.viewDef->renderWorld->DebugBounds( colorCyan, tri->bounds, renderEntity->origin );
//}
2024-08-26 08:26:40 +00:00
2024-08-21 22:05:04 +00:00
// RB: test surface visibility by drawing the triangles of the bounds
2024-08-26 20:58:59 +00:00
if ( r_useMaskedOcclusionCulling . GetBool ( ) & & ! viewInsideSurface & & ! viewDef - > isMirror & & ! viewDef - > isSubview )
2024-08-21 22:05:04 +00:00
{
2024-09-02 21:14:08 +00:00
if ( //!model->IsStaticWorldModel() &&
! renderEntity - > weaponDepthHack & & renderEntity - > modelDepthHack = = 0.0f )
2024-08-22 20:05:47 +00:00
{
2024-08-26 20:36:58 +00:00
idVec4 triVerts [ 8 ] ;
2024-08-21 22:05:04 +00:00
2024-08-22 20:05:47 +00:00
tr . pc . c_mocIndexes + = 36 ;
tr . pc . c_mocVerts + = 8 ;
2024-08-21 22:05:04 +00:00
2024-12-18 20:32:13 +00:00
idBounds surfaceBounds ;
# if 1
2024-08-22 21:19:59 +00:00
if ( gpuSkinned )
{
2024-12-18 20:32:13 +00:00
surfaceBounds = vEntity - > entityDef - > localReferenceBounds ;
2024-08-22 21:19:59 +00:00
}
else
# endif
{
2024-12-18 20:32:13 +00:00
surfaceBounds = tri - > bounds ;
2024-08-22 21:19:59 +00:00
}
2024-08-21 22:05:04 +00:00
2024-08-26 08:26:40 +00:00
idRenderMatrix modelRenderMatrix ;
idRenderMatrix : : CreateFromOriginAxis ( renderEntity - > origin , renderEntity - > axis , modelRenderMatrix ) ;
2024-08-22 20:05:47 +00:00
idRenderMatrix inverseBaseModelProject ;
2024-12-18 20:32:13 +00:00
idRenderMatrix : : OffsetScaleForBounds ( modelRenderMatrix , surfaceBounds , inverseBaseModelProject ) ;
2024-08-21 22:05:04 +00:00
2024-08-22 20:05:47 +00:00
idRenderMatrix invProjectMVPMatrix ;
idRenderMatrix : : Multiply ( viewDef - > worldSpace . unjitteredMVP , inverseBaseModelProject , invProjectMVPMatrix ) ;
2024-08-21 22:05:04 +00:00
2024-08-22 20:05:47 +00:00
tr . pc . c_mocTests + = 1 ;
2024-08-21 22:05:04 +00:00
2024-08-26 08:26:40 +00:00
// NOTE: unit cube instead of zeroToOne cube
idVec4 * verts = tr . maskedUnitCubeVerts ;
2024-08-26 20:36:58 +00:00
for ( int i = 0 ; i < 8 ; i + + )
2024-08-22 20:05:47 +00:00
{
// transform to clip space
2024-08-26 20:36:58 +00:00
invProjectMVPMatrix . TransformPoint ( verts [ i ] , triVerts [ i ] ) ;
}
2024-08-21 22:05:04 +00:00
2024-09-02 21:14:08 +00:00
// backface none so objects are still visible where we run into
2024-08-30 16:41:03 +00:00
# if MOC_MULTITHREADED
2024-09-02 21:14:08 +00:00
tr . maskedOcclusionThreaded - > SetMatrix ( NULL ) ;
MaskedOcclusionCulling : : CullingResult result = tr . maskedOcclusionThreaded - > TestTriangles ( ( float * ) triVerts , tr . maskedZeroOneCubeIndexes , 12 , MaskedOcclusionCulling : : BACKFACE_NONE ) ;
2024-08-30 16:41:03 +00:00
# else
2024-09-02 21:14:08 +00:00
MaskedOcclusionCulling : : CullingResult result = tr . maskedOcclusionCulling - > TestTriangles ( ( float * ) triVerts , tr . maskedZeroOneCubeIndexes , 12 , NULL , MaskedOcclusionCulling : : BACKFACE_NONE ) ;
2024-08-30 16:41:03 +00:00
# endif
2024-09-02 21:14:08 +00:00
if ( result ! = MaskedOcclusionCulling : : VISIBLE )
2024-08-22 20:05:47 +00:00
{
2024-08-26 08:26:40 +00:00
tr . pc . c_mocCulledSurfaces + = 1 ;
2024-08-22 20:05:47 +00:00
surfaceDirectlyVisible = false ;
}
2024-08-21 22:05:04 +00:00
}
2024-08-22 20:05:47 +00:00
}
2024-08-27 09:41:39 +00:00
# endif // #if defined(USE_INTRINSICS_SSE)
2024-08-21 22:05:04 +00:00
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
//--------------------------
// base drawing surface
//--------------------------
2016-07-02 09:22:04 +00:00
const float * shaderRegisters = NULL ;
2023-03-16 19:49:30 +00:00
drawSurf_t * baseDrawSurf = NULL ;
if ( surfaceDirectlyVisible & & shader - > IsDrawn ( ) )
2012-11-28 15:47:07 +00:00
{
2023-03-16 19:49:30 +00:00
// make sure we have an ambient cache and all necessary normals / tangents
if ( ! vertexCache . CacheIsCurrent ( tri - > indexCache ) )
2012-11-28 15:47:07 +00:00
{
2023-03-16 19:49:30 +00:00
tri - > indexCache = vertexCache . AllocIndex ( tri - > indexes , tri - > numIndexes ) ;
}
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
if ( ! vertexCache . CacheIsCurrent ( tri - > ambientCache ) )
{
// we are going to use it for drawing, so make sure we have the tangents and normals
if ( shader - > ReceivesLighting ( ) & & ! tri - > tangentsCalculated )
2012-11-28 15:47:07 +00:00
{
2023-03-16 19:49:30 +00:00
assert ( tri - > staticModelWithJoints = = NULL ) ;
R_DeriveTangents ( tri ) ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
// RB: this was hit by parametric particle models ..
//assert( false ); // this should no longer be hit
// RB end
2012-11-26 18:58:24 +00:00
}
2023-03-16 19:49:30 +00:00
tri - > ambientCache = vertexCache . AllocVertex ( tri - > verts , tri - > numVerts ) ;
}
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
// add the surface for drawing
// we can re-use some of the values for light interaction surfaces
baseDrawSurf = ( drawSurf_t * ) R_FrameAlloc ( sizeof ( * baseDrawSurf ) , FRAME_ALLOC_DRAW_SURFACE ) ;
baseDrawSurf - > frontEndGeo = tri ;
baseDrawSurf - > space = vEntity ;
baseDrawSurf - > scissorRect = vEntity - > scissorRect ;
baseDrawSurf - > extraGLState = 0 ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
R_SetupDrawSurfShader ( baseDrawSurf , shader , renderEntity ) ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
shaderRegisters = baseDrawSurf - > shaderRegisters ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
// Check for deformations (eyeballs, flares, etc)
const deform_t shaderDeform = shader - > Deform ( ) ;
if ( shaderDeform ! = DFRM_NONE )
{
drawSurf_t * deformDrawSurf = R_DeformDrawSurf ( baseDrawSurf ) ;
if ( deformDrawSurf ! = NULL )
2012-11-28 15:47:07 +00:00
{
2023-03-16 19:49:30 +00:00
// any deforms may have created multiple draw surfaces
for ( drawSurf_t * surf = deformDrawSurf , * next = NULL ; surf ! = NULL ; surf = next )
2016-07-02 09:22:04 +00:00
{
2023-03-16 19:49:30 +00:00
next = surf - > nextOnLight ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
surf - > linkChain = NULL ;
surf - > nextOnLight = vEntity - > drawSurfs ;
vEntity - > drawSurfs = surf ;
2016-07-02 09:22:04 +00:00
}
2012-11-26 18:58:24 +00:00
}
2023-03-16 19:49:30 +00:00
}
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
// Most deform source surfaces do not need to be rendered.
// However, particles are rendered in conjunction with the source surface.
if ( shaderDeform = = DFRM_NONE | | shaderDeform = = DFRM_PARTICLE | | shaderDeform = = DFRM_PARTICLE2 )
{
// copy verts and indexes to this frame's hardware memory if they aren't already there
if ( ! vertexCache . CacheIsCurrent ( tri - > ambientCache ) )
2012-11-28 15:47:07 +00:00
{
2023-03-16 19:49:30 +00:00
tri - > ambientCache = vertexCache . AllocVertex ( tri - > verts , tri - > numVerts ) ;
}
if ( ! vertexCache . CacheIsCurrent ( tri - > indexCache ) )
{
tri - > indexCache = vertexCache . AllocIndex ( tri - > indexes , tri - > numIndexes ) ;
}
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
R_SetupDrawSurfJoints ( baseDrawSurf , tri , shader ) ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
baseDrawSurf - > numIndexes = tri - > numIndexes ;
baseDrawSurf - > ambientCache = tri - > ambientCache ;
baseDrawSurf - > indexCache = tri - > indexCache ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
baseDrawSurf - > linkChain = NULL ; // link to the view
baseDrawSurf - > nextOnLight = vEntity - > drawSurfs ;
vEntity - > drawSurfs = baseDrawSurf ;
2012-11-26 18:58:24 +00:00
}
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
//----------------------------------------
// add all light interactions
//----------------------------------------
2012-11-28 15:47:07 +00:00
for ( int contactedLight = 0 ; contactedLight < numContactedLights ; contactedLight + + )
{
viewLight_t * vLight = contactedLights [ contactedLight ] ;
const idRenderLightLocal * lightDef = vLight - > lightDef ;
const idInteraction * interaction = staticInteractions [ contactedLight ] ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// check for a static interaction
2012-11-28 15:47:07 +00:00
surfaceInteraction_t * surfInter = NULL ;
if ( interaction > INTERACTION_EMPTY & & interaction - > staticInteraction )
{
2012-11-26 18:58:24 +00:00
// we have a static interaction that was calculated accurately
assert ( model - > NumSurfaces ( ) = = interaction - > numSurfaces ) ;
surfInter = & interaction - > surfaces [ surfaceNum ] ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
// try to do a more precise cull of this model surface to the light
2012-11-28 15:47:07 +00:00
if ( R_CullModelBoundsToLight ( lightDef , tri - > bounds , entityDef - > modelRenderMatrix ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// "invisible ink" lights and shaders (imp spawn drawing on walls, etc)
2012-11-28 15:47:07 +00:00
if ( shader - > Spectrum ( ) ! = lightDef - > lightShader - > Spectrum ( ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// Calculate the local light origin to determine if the view is inside the shadow
// projection and to calculate the triangle facing for dynamic shadow volumes.
idVec3 localLightOrigin ;
R_GlobalPointToLocal ( vEntity - > modelMatrix , lightDef - > globalLightOrigin , localLightOrigin ) ;
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
//--------------------------
// surface light interactions
//--------------------------
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( addInteractions & & surfaceDirectlyVisible & & shader - > ReceivesLighting ( ) )
{
2012-11-26 18:58:24 +00:00
// static interactions can commonly find that no triangles from a surface
// contact the light, even when the total model does
2012-11-28 15:47:07 +00:00
if ( surfInter = = NULL | | surfInter - > lightTrisIndexCache > 0 )
{
2016-07-02 09:22:04 +00:00
// make sure we have a valid shader register even if we didn't generate a drawn mesh above
if ( shaderRegisters = = NULL )
2012-11-28 15:47:07 +00:00
{
2016-07-02 09:22:04 +00:00
drawSurf_t scratchSurf ;
R_SetupDrawSurfShader ( & scratchSurf , shader , renderEntity ) ;
shaderRegisters = scratchSurf . shaderRegisters ;
2012-11-28 15:47:07 +00:00
}
2019-11-11 19:27:44 +00:00
2016-07-02 09:22:04 +00:00
if ( shaderRegisters ! = NULL )
2012-11-28 15:47:07 +00:00
{
2016-07-02 09:22:04 +00:00
// create a drawSurf for this interaction
drawSurf_t * lightDrawSurf = ( drawSurf_t * ) R_FrameAlloc ( sizeof ( * lightDrawSurf ) , FRAME_ALLOC_DRAW_SURFACE ) ;
2019-11-11 19:27:44 +00:00
2016-07-02 09:22:04 +00:00
if ( surfInter ! = NULL )
2012-11-28 15:47:07 +00:00
{
2016-07-02 09:22:04 +00:00
// optimized static interaction
lightDrawSurf - > numIndexes = surfInter - > numLightTrisIndexes ;
lightDrawSurf - > indexCache = surfInter - > lightTrisIndexCache ;
}
else
{
// throw the entire source surface at it without any per-triangle culling
lightDrawSurf - > numIndexes = tri - > numIndexes ;
lightDrawSurf - > indexCache = tri - > indexCache ;
2012-11-26 18:58:24 +00:00
}
2019-11-11 19:27:44 +00:00
2016-07-02 09:22:04 +00:00
lightDrawSurf - > ambientCache = tri - > ambientCache ;
lightDrawSurf - > frontEndGeo = tri ;
lightDrawSurf - > space = vEntity ;
lightDrawSurf - > material = shader ;
lightDrawSurf - > extraGLState = 0 ;
lightDrawSurf - > scissorRect = vLight - > scissorRect ; // interactionScissor;
lightDrawSurf - > sort = 0.0f ;
lightDrawSurf - > shaderRegisters = shaderRegisters ;
2019-11-11 19:27:44 +00:00
2022-03-16 14:24:08 +00:00
R_SetupDrawSurfJoints ( lightDrawSurf , tri , shader ) ;
2019-11-11 19:27:44 +00:00
2016-07-02 09:22:04 +00:00
// Determine which linked list to add the light surface to.
// There will only be localSurfaces if the light casts shadows and
// there are surfaces with NOSELFSHADOW.
if ( shader - > Coverage ( ) = = MC_TRANSLUCENT )
{
lightDrawSurf - > linkChain = & vLight - > translucentInteractions ;
}
else if ( ! lightDef - > parms . noShadows & & shader - > TestMaterialFlag ( MF_NOSELFSHADOW ) )
{
lightDrawSurf - > linkChain = & vLight - > localInteractions ;
}
else
{
lightDrawSurf - > linkChain = & vLight - > globalInteractions ;
}
lightDrawSurf - > nextOnLight = vEntity - > drawSurfs ;
vEntity - > drawSurfs = lightDrawSurf ;
2012-11-26 18:58:24 +00:00
}
}
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
//--------------------------
// surface shadows
//--------------------------
2019-11-11 19:27:44 +00:00
2016-07-02 09:22:04 +00:00
# if 1
2023-03-16 19:49:30 +00:00
if ( ! shader - > SurfaceCastsShadow ( ) & & ! ( r_forceShadowMapsOnAlphaTestedSurfaces . GetBool ( ) & & shader - > Coverage ( ) = = MC_PERFORATED ) )
2012-11-28 15:47:07 +00:00
{
2012-11-26 18:58:24 +00:00
continue ;
}
2016-07-02 09:22:04 +00:00
# else
// Steel Storm 2 behaviour - this destroys many alpha tested shadows in vanilla BFG
2019-11-11 19:27:44 +00:00
2016-07-02 09:22:04 +00:00
// motorsep 11-08-2014; if r_forceShadowMapsOnAlphaTestedSurfaces is 0 when shadow mapping is on,
// don't render shadows from all alphaTest surfaces.
// Useful as global performance booster for old GPUs to disable shadows from grass/foliage/etc.
if ( r_useShadowMapping . GetBool ( ) )
{
if ( shader - > Coverage ( ) = = MC_PERFORATED )
{
if ( ! r_forceShadowMapsOnAlphaTestedSurfaces . GetBool ( ) )
2019-11-11 19:27:44 +00:00
{
2016-07-02 09:22:04 +00:00
continue ;
2019-11-11 19:27:44 +00:00
}
2016-07-02 09:22:04 +00:00
}
}
2019-11-11 19:27:44 +00:00
2016-07-02 09:22:04 +00:00
// if material has "noShadows" global key
if ( ! shader - > SurfaceCastsShadow ( ) )
{
// motorsep 11-08-2014; if r_forceShadowMapsOnAlphaTestedSurfaces is 1 when shadow mapping is on,
// check if a surface IS NOT alphaTested and has "noShadows" global key;
// or if a surface IS alphaTested and has "noShadows" global key;
// if either is true, don't make surfaces cast shadow maps.
if ( r_useShadowMapping . GetBool ( ) )
{
if ( shader - > Coverage ( ) ! = MC_PERFORATED & & shader - > TestMaterialFlag ( MF_NOSHADOWS ) )
{
continue ;
}
else if ( shader - > Coverage ( ) = = MC_PERFORATED & & shader - > TestMaterialFlag ( MF_NOSHADOWS ) )
{
continue ;
}
}
else
{
continue ;
}
}
# endif
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( ! lightDef - > LightCastsShadows ( ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// some entities, like view weapons, don't cast any shadows
2012-11-28 15:47:07 +00:00
if ( entityDef - > parms . noShadow )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
// No shadow if it's suppressed for this light.
2012-11-28 15:47:07 +00:00
if ( entityDef - > parms . suppressShadowInLightID & & entityDef - > parms . suppressShadowInLightID = = lightDef - > parms . lightId )
{
2012-11-26 18:58:24 +00:00
continue ;
}
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
// RB: draw shadow occluder using shadow mapping
// OPTIMIZE: check if projected occluder box intersects the view
//
//if( addInteractions && surfaceDirectlyVisible && shader->ReceivesLighting() )
2014-05-10 12:40:01 +00:00
{
2023-03-16 19:49:30 +00:00
// static interactions can commonly find that no triangles from a surface
// contact the light, even when the total model does
if ( surfInter = = NULL | | surfInter - > lightTrisIndexCache > 0 )
2014-05-10 12:40:01 +00:00
{
2023-03-16 19:49:30 +00:00
// create a drawSurf for this interaction
drawSurf_t * shadowDrawSurf = ( drawSurf_t * ) R_FrameAlloc ( sizeof ( * shadowDrawSurf ) , FRAME_ALLOC_DRAW_SURFACE ) ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
if ( surfInter ! = NULL )
{
// optimized static interaction
shadowDrawSurf - > numIndexes = surfInter - > numLightTrisIndexes ;
shadowDrawSurf - > indexCache = surfInter - > lightTrisIndexCache ;
}
else
{
// make sure we have an ambient cache and all necessary normals / tangents
if ( ! vertexCache . CacheIsCurrent ( tri - > indexCache ) )
2014-05-10 12:40:01 +00:00
{
2023-03-16 19:49:30 +00:00
tri - > indexCache = vertexCache . AllocIndex ( tri - > indexes , tri - > numIndexes ) ;
2014-05-10 12:40:01 +00:00
}
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
// throw the entire source surface at it without any per-triangle culling
shadowDrawSurf - > numIndexes = tri - > numIndexes ;
shadowDrawSurf - > indexCache = tri - > indexCache ;
}
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
if ( ! vertexCache . CacheIsCurrent ( tri - > ambientCache ) )
{
// we are going to use it for drawing, so make sure we have the tangents and normals
if ( shader - > ReceivesLighting ( ) & & ! tri - > tangentsCalculated )
2014-05-10 12:40:01 +00:00
{
2023-03-16 19:49:30 +00:00
assert ( tri - > staticModelWithJoints = = NULL ) ;
R_DeriveTangents ( tri ) ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
// RB: this was hit by parametric particle models ..
//assert( false ); // this should no longer be hit
// RB end
2014-05-11 12:36:51 +00:00
}
2023-03-16 19:49:30 +00:00
tri - > ambientCache = vertexCache . AllocVertex ( tri - > verts , tri - > numVerts ) ;
2014-05-10 12:40:01 +00:00
}
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
shadowDrawSurf - > ambientCache = tri - > ambientCache ;
shadowDrawSurf - > frontEndGeo = tri ;
shadowDrawSurf - > space = vEntity ;
shadowDrawSurf - > material = shader ;
shadowDrawSurf - > extraGLState = 0 ;
shadowDrawSurf - > scissorRect = vLight - > scissorRect ; // interactionScissor;
shadowDrawSurf - > sort = 0.0f ;
//shadowDrawSurf->shaderRegisters = baseDrawSurf->shaderRegisters; // TODO FIXME
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
if ( shader - > Coverage ( ) = = MC_PERFORATED )
2012-11-28 15:47:07 +00:00
{
2023-03-16 19:49:30 +00:00
R_SetupDrawSurfShader ( shadowDrawSurf , shader , renderEntity ) ;
2012-11-26 18:58:24 +00:00
}
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
R_SetupDrawSurfJoints ( shadowDrawSurf , tri , shader ) ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
// determine which linked list to add the shadow surface to
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
//shadowDrawSurf->linkChain = shader->TestMaterialFlag( MF_NOSELFSHADOW ) ? &vLight->localShadows : &vLight->globalShadows;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
shadowDrawSurf - > linkChain = & vLight - > globalShadows ;
shadowDrawSurf - > nextOnLight = vEntity - > drawSurfs ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
vEntity - > drawSurfs = shadowDrawSurf ;
2019-11-11 19:27:44 +00:00
2023-03-16 19:49:30 +00:00
}
}
// RB end
2012-11-26 18:58:24 +00:00
}
}
}
REGISTER_PARALLEL_JOB ( R_AddSingleModel , " R_AddSingleModel " ) ;
/*
= = = = = = = = = = = = = = = = =
R_LinkDrawSurfToView
Als called directly by GuiModel
= = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void R_LinkDrawSurfToView ( drawSurf_t * drawSurf , viewDef_t * viewDef )
{
2012-11-26 18:58:24 +00:00
// if it doesn't fit, resize the list
2012-11-28 15:47:07 +00:00
if ( viewDef - > numDrawSurfs = = viewDef - > maxDrawSurfs )
{
drawSurf_t * * old = viewDef - > drawSurfs ;
2012-11-26 18:58:24 +00:00
int count ;
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( viewDef - > maxDrawSurfs = = 0 )
{
2012-11-26 18:58:24 +00:00
viewDef - > maxDrawSurfs = INITIAL_DRAWSURFS ;
count = 0 ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
count = viewDef - > maxDrawSurfs * sizeof ( viewDef - > drawSurfs [ 0 ] ) ;
viewDef - > maxDrawSurfs * = 2 ;
}
2012-11-28 15:47:07 +00:00
viewDef - > drawSurfs = ( drawSurf_t * * ) R_FrameAlloc ( viewDef - > maxDrawSurfs * sizeof ( viewDef - > drawSurfs [ 0 ] ) , FRAME_ALLOC_DRAW_SURFACE_POINTER ) ;
2012-11-26 18:58:24 +00:00
memcpy ( viewDef - > drawSurfs , old , count ) ;
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
viewDef - > drawSurfs [ viewDef - > numDrawSurfs ] = drawSurf ;
viewDef - > numDrawSurfs + + ;
}
/*
= = = = = = = = = = = = = = = = = = =
R_AddModels
The end result of running this is the addition of drawSurf_t to the
tr . viewDef - > drawSurfs [ ] array and light link chains , along with
frameData and vertexCache allocations to support the drawSurfs .
= = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void R_AddModels ( )
{
2012-11-26 18:58:24 +00:00
SCOPED_PROFILE_EVENT ( " R_AddModels " ) ;
2019-11-11 19:27:44 +00:00
2024-08-21 16:39:52 +00:00
// RB: already done in R_FillMaskedOcclusionBufferWithModels
// tr.viewDef->viewEntitys = R_SortViewEntities( tr.viewDef->viewEntitys );
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
//-------------------------------------------------
// Go through each view entity that is either visible to the view, or to
// any light that intersects the view (for shadows).
//-------------------------------------------------
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
if ( r_useParallelAddModels . GetBool ( ) )
{
for ( viewEntity_t * vEntity = tr . viewDef - > viewEntitys ; vEntity ! = NULL ; vEntity = vEntity - > next )
{
tr . frontEndJobList - > AddJob ( ( jobRun_t ) R_AddSingleModel , vEntity ) ;
2012-11-26 18:58:24 +00:00
}
tr . frontEndJobList - > Submit ( ) ;
tr . frontEndJobList - > Wait ( ) ;
2012-11-28 15:47:07 +00:00
}
else
{
for ( viewEntity_t * vEntity = tr . viewDef - > viewEntitys ; vEntity ! = NULL ; vEntity = vEntity - > next )
{
2012-11-26 18:58:24 +00:00
R_AddSingleModel ( vEntity ) ;
}
}
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
//-------------------------------------------------
// Move the draw surfs to the view.
//-------------------------------------------------
2019-11-11 19:27:44 +00:00
2012-11-26 18:58:24 +00:00
tr . viewDef - > numDrawSurfs = 0 ; // clear the ambient surface list
tr . viewDef - > maxDrawSurfs = 0 ; // will be set to INITIAL_DRAWSURFS on R_LinkDrawSurfToView
2019-11-11 19:27:44 +00:00
2012-11-28 15:47:07 +00:00
for ( viewEntity_t * vEntity = tr . viewDef - > viewEntitys ; vEntity ! = NULL ; vEntity = vEntity - > next )
{
2020-03-29 15:12:11 +00:00
// RB
if ( vEntity - > drawSurfs ! = NULL )
{
tr . pc . c_visibleViewEntities + + ;
}
2012-11-28 15:47:07 +00:00
for ( drawSurf_t * ds = vEntity - > drawSurfs ; ds ! = NULL ; )
{
drawSurf_t * next = ds - > nextOnLight ;
if ( ds - > linkChain = = NULL )
{
2012-11-26 18:58:24 +00:00
R_LinkDrawSurfToView ( ds , tr . viewDef ) ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
ds - > nextOnLight = * ds - > linkChain ;
* ds - > linkChain = ds ;
}
ds = next ;
}
2020-03-29 15:12:11 +00:00
2012-11-26 18:58:24 +00:00
vEntity - > drawSurfs = NULL ;
}
}