From 3e1d21ef34b5d7ce7345a485d7aa8e44fe79264f Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Sat, 2 Jul 2016 11:22:04 +0200 Subject: [PATCH] Merged some SS2 shadow mapping performance improvements --- neo/renderer/Material.cpp | 37 +- neo/renderer/Material.h | 33 +- neo/renderer/tr_frontend_addmodels.cpp | 502 +++++++++++++++---------- 3 files changed, 367 insertions(+), 205 deletions(-) diff --git a/neo/renderer/Material.cpp b/neo/renderer/Material.cpp index de6a0022..721db3be 100644 --- a/neo/renderer/Material.cpp +++ b/neo/renderer/Material.cpp @@ -3,6 +3,8 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2014-2016 Robert Beckebans +Copyright (C) 2014-2016 Kot in Action Creative Artel This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -1205,6 +1207,11 @@ void idMaterial::ParseFragmentMap( idLexer& src, newShaderStage_t* newStage ) { src.ReadTokenOnLine( &token ); + if( !token.Icmp( "normalMap" ) ) + { + td = TD_BUMP; + continue; + } if( !token.Icmp( "cubeMap" ) ) { cubeMap = CF_NATIVE; @@ -2337,7 +2344,8 @@ void idMaterial::ParseMaterial( idLexer& src ) // volume would be coplanar with the surface, giving depth fighting // we could make this no-self-shadows, but it may be more important // to receive shadows from no-self-shadow monsters - SetMaterialFlag( MF_NOSHADOWS ); + if( !r_useShadowMapping.GetBool() ) // motorsep 11-08-2014; when shadow mapping is on, we allow two-sided surfaces to cast shadows + SetMaterialFlag( MF_NOSHADOWS ); } // backSided else if( !token.Icmp( "backSided" ) ) @@ -2507,6 +2515,33 @@ void idMaterial::ParseMaterial( idLexer& src ) SetMaterialFlag( MF_NOSHADOWS ); continue; } + + // motorsep 11-23-2014; material LOD keys that define what LOD iteration the surface falls into + else if( !token.Icmp( "lod1" ) ) + { + SetMaterialFlag( MF_LOD1 ); + continue; + } + else if( !token.Icmp( "lod2" ) ) + { + SetMaterialFlag( MF_LOD2 ); + continue; + } + else if( !token.Icmp( "lod3" ) ) + { + SetMaterialFlag( MF_LOD3 ); + continue; + } + else if( !token.Icmp( "lod4" ) ) + { + SetMaterialFlag( MF_LOD4 ); + continue; + } + else if( !token.Icmp( "persistentLOD" ) ) + { + SetMaterialFlag( MF_LOD_PERSISTENT ); + continue; + } else if( token == "{" ) { // create the new stage diff --git a/neo/renderer/Material.h b/neo/renderer/Material.h index 904e5498..34bdafa3 100644 --- a/neo/renderer/Material.h +++ b/neo/renderer/Material.h @@ -3,6 +3,8 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2014-2016 Robert Beckebans +Copyright (C) 2014-2016 Kot in Action Creative Artel This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -280,8 +282,15 @@ typedef enum MF_NOSHADOWS = BIT( 2 ), MF_FORCESHADOWS = BIT( 3 ), MF_NOSELFSHADOW = BIT( 4 ), - MF_NOPORTALFOG = BIT( 5 ), // this fog volume won't ever consider a portal fogged out - MF_EDITOR_VISIBLE = BIT( 6 ) // in use (visible) per editor + MF_NOPORTALFOG = BIT( 5 ), // this fog volume won't ever consider a portal fogged out + MF_EDITOR_VISIBLE = BIT( 6 ), // in use (visible) per editor + // motorsep 11-23-2014; material LOD keys that define what LOD iteration the surface falls into + MF_LOD1_SHIFT = 7, + MF_LOD1 = BIT( 7 ), // motorsep 11-24-2014; material flag for LOD1 iteration + MF_LOD2 = BIT( 8 ), // motorsep 11-24-2014; material flag for LOD2 iteration + MF_LOD3 = BIT( 9 ), // motorsep 11-24-2014; material flag for LOD3 iteration + MF_LOD4 = BIT( 10 ), // motorsep 11-24-2014; material flag for LOD4 iteration + MF_LOD_PERSISTENT = BIT( 11 ) // motorsep 11-24-2014; material flag for persistent LOD iteration } materialFlags_t; // contents flags, NOTE: make sure to keep the defines in doom_defs.script up to date with these! @@ -789,6 +798,24 @@ public: }; void AddReference(); + // motorsep 11-23-2014; material LOD keys that define what LOD iteration the surface falls into + // lod1 - lod4 defines several levels of LOD + // persistentLOD specifies the LOD iteration that still being rendered, even after the camera is beyond the distance at which LOD iteration should not be rendered + + bool IsLOD() const + { + return ( materialFlags & ( MF_LOD1 | MF_LOD2 | MF_LOD3 | MF_LOD4 ) ) != 0; + } + // foresthale 2014-11-24: added IsLODVisibleForDistance method + bool IsLODVisibleForDistance( float distance, float lodBase ) const + { + int bit = ( materialFlags & ( MF_LOD1 | MF_LOD2 | MF_LOD3 | MF_LOD4 ) ) >> MF_LOD1_SHIFT; + float m1 = lodBase * ( bit >> 1 ); + float m2 = lodBase * bit; + return distance >= m1 && ( distance < m2 || ( materialFlags & ( MF_LOD_PERSISTENT ) ) ); + } + + private: // parse the entire material void CommonInit(); @@ -825,7 +852,7 @@ private: idStr desc; // description idStr renderBump; // renderbump command options, without the "renderbump" at the start - idImage* lightFalloffImage; // only for light shaders + idImage* lightFalloffImage; // only for light shaders idImage* fastPathBumpImage; // if any of these are set, they all will be idImage* fastPathDiffuseImage; diff --git a/neo/renderer/tr_frontend_addmodels.cpp b/neo/renderer/tr_frontend_addmodels.cpp index 55c45a7d..4cbd9274 100644 --- a/neo/renderer/tr_frontend_addmodels.cpp +++ b/neo/renderer/tr_frontend_addmodels.cpp @@ -3,7 +3,8 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. -Copyright (C) 2013-2014 Robert Beckebans +Copyright (C) 2014-2016 Robert Beckebans +Copyright (C) 2014-2016 Kot in Action Creative Artel This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -26,7 +27,6 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ - #pragma hdrstop #include "precompiled.h" @@ -44,6 +44,8 @@ idCVar r_forceShadowCaps( "r_forceShadowCaps", "0", CVAR_RENDERER | CVAR_BOOL, " // 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 +// 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)" ); static const float CHECK_BOUNDS_EPSILON = 1.0f; @@ -315,7 +317,7 @@ void R_SetupDrawSurfShader( drawSurf_t* drawSurf, const idMaterial* shader, cons shaderParms = generatedShaderParms; } - // allocte frame memory for the shader register values + // allocate frame memory for the shader register values float* regs = ( float* )R_FrameAlloc( shader->GetNumRegisters() * sizeof( float ), FRAME_ALLOC_SHADER_REGISTER ); drawSurf->shaderRegisters = regs; @@ -602,7 +604,36 @@ void R_AddSingleModel( viewEntity_t* vEntity ) { continue; } - if( !shader->IsDrawn() ) + + // 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(); + + if( !shader->IsLODVisibleForDistance( distance, r_lodMaterialDistance.GetFloat() ) ) + { + continue; + } + } + + // foresthale 2014-09-01: don't skip surfaces that use the "forceShadows" flag + if( !shader->IsDrawn() && !shader->SurfaceCastsShadow() ) { continue; // collision hulls, etc } @@ -625,7 +656,8 @@ void R_AddSingleModel( viewEntity_t* vEntity ) { continue; } - if( !shader->IsDrawn() ) + // foresthale 2014-09-01: don't skip surfaces that use the "forceShadows" flag + if( !shader->IsDrawn() && !shader->SurfaceCastsShadow() ) { continue; } @@ -680,84 +712,90 @@ void R_AddSingleModel( viewEntity_t* vEntity ) //-------------------------- // base drawing surface //-------------------------- - drawSurf_t* baseDrawSurf = NULL; - if( surfaceDirectlyVisible ) + const float* shaderRegisters = NULL; + if( shader->IsDrawn() ) { - // make sure we have an ambient cache and all necessary normals / tangents - if( !vertexCache.CacheIsCurrent( tri->indexCache ) ) + drawSurf_t* baseDrawSurf = NULL; + if( surfaceDirectlyVisible ) { - tri->indexCache = vertexCache.AllocIndex( tri->indexes, ALIGN( tri->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); - } - - 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 ) - { - assert( tri->staticModelWithJoints == NULL ); - R_DeriveTangents( tri ); - - // RB: this was hit by parametric particle models .. - //assert( false ); // this should no longer be hit - // RB end - } - tri->ambientCache = vertexCache.AllocVertex( tri->verts, ALIGN( tri->numVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) ); - } - - // 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; - baseDrawSurf->renderZFail = 0; - - R_SetupDrawSurfShader( baseDrawSurf, shader, renderEntity ); - - // 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 ) - { - // any deforms may have created multiple draw surfaces - for( drawSurf_t* surf = deformDrawSurf, * next = NULL; surf != NULL; surf = next ) - { - next = surf->nextOnLight; - - surf->linkChain = NULL; - surf->nextOnLight = vEntity->drawSurfs; - vEntity->drawSurfs = surf; - } - } - } - - // 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 ) ) - { - tri->ambientCache = vertexCache.AllocVertex( tri->verts, ALIGN( tri->numVerts * sizeof( tri->verts[0] ), VERTEX_CACHE_ALIGN ) ); - } + // make sure we have an ambient cache and all necessary normals / tangents if( !vertexCache.CacheIsCurrent( tri->indexCache ) ) { - tri->indexCache = vertexCache.AllocIndex( tri->indexes, ALIGN( tri->numIndexes * sizeof( tri->indexes[0] ), INDEX_CACHE_ALIGN ) ); + tri->indexCache = vertexCache.AllocIndex( tri->indexes, ALIGN( tri->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); } - R_SetupDrawSurfJoints( baseDrawSurf, tri, shader ); + 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 ) + { + assert( tri->staticModelWithJoints == NULL ); + R_DeriveTangents( tri ); + + // RB: this was hit by parametric particle models .. + //assert( false ); // this should no longer be hit + // RB end + } + tri->ambientCache = vertexCache.AllocVertex( tri->verts, ALIGN( tri->numVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) ); + } - baseDrawSurf->numIndexes = tri->numIndexes; - baseDrawSurf->ambientCache = tri->ambientCache; - baseDrawSurf->indexCache = tri->indexCache; - baseDrawSurf->shadowCache = 0; + // 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; + baseDrawSurf->renderZFail = 0; - baseDrawSurf->linkChain = NULL; // link to the view - baseDrawSurf->nextOnLight = vEntity->drawSurfs; - vEntity->drawSurfs = baseDrawSurf; + R_SetupDrawSurfShader( baseDrawSurf, shader, renderEntity ); + + shaderRegisters = baseDrawSurf->shaderRegisters; + + // 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 ) + { + // any deforms may have created multiple draw surfaces + for( drawSurf_t* surf = deformDrawSurf, * next = NULL; surf != NULL; surf = next ) + { + next = surf->nextOnLight; + + surf->linkChain = NULL; + surf->nextOnLight = vEntity->drawSurfs; + vEntity->drawSurfs = surf; + } + } + } + + // 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 ) ) + { + tri->ambientCache = vertexCache.AllocVertex( tri->verts, ALIGN( tri->numVerts * sizeof( tri->verts[0] ), VERTEX_CACHE_ALIGN ) ); + } + if( !vertexCache.CacheIsCurrent( tri->indexCache ) ) + { + tri->indexCache = vertexCache.AllocIndex( tri->indexes, ALIGN( tri->numIndexes * sizeof( tri->indexes[0] ), INDEX_CACHE_ALIGN ) ); + } + + R_SetupDrawSurfJoints( baseDrawSurf, tri, shader ); + + baseDrawSurf->numIndexes = tri->numIndexes; + baseDrawSurf->ambientCache = tri->ambientCache; + baseDrawSurf->indexCache = tri->indexCache; + baseDrawSurf->shadowCache = 0; + + baseDrawSurf->linkChain = NULL; // link to the view + baseDrawSurf->nextOnLight = vEntity->drawSurfs; + vEntity->drawSurfs = baseDrawSurf; + } } } @@ -810,104 +848,117 @@ void R_AddSingleModel( viewEntity_t* vEntity ) // contact the light, even when the total model does if( surfInter == NULL || surfInter->lightTrisIndexCache > 0 ) { - // create a drawSurf for this interaction - drawSurf_t* lightDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *lightDrawSurf ), FRAME_ALLOC_DRAW_SURFACE ); - - if( surfInter != NULL ) + // make sure we have a valid shader register even if we didn't generate a drawn mesh above + if( shaderRegisters == NULL ) { - // optimized static interaction - lightDrawSurf->numIndexes = surfInter->numLightTrisIndexes; - lightDrawSurf->indexCache = surfInter->lightTrisIndexCache; + drawSurf_t scratchSurf; + R_SetupDrawSurfShader( &scratchSurf, shader, renderEntity ); + shaderRegisters = scratchSurf.shaderRegisters; } - else + + if( shaderRegisters != NULL ) { - // throw the entire source surface at it without any per-triangle culling - lightDrawSurf->numIndexes = tri->numIndexes; - lightDrawSurf->indexCache = tri->indexCache; + // create a drawSurf for this interaction + drawSurf_t* lightDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *lightDrawSurf ), FRAME_ALLOC_DRAW_SURFACE ); - // optionally cull the triangles to the light volume - if( r_cullDynamicLightTriangles.GetBool() ) + if( surfInter != NULL ) { - - vertCacheHandle_t lightIndexCache = vertexCache.AllocIndex( NULL, ALIGN( lightDrawSurf->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); - if( vertexCache.CacheIsCurrent( lightIndexCache ) ) + // 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; + + // optionally cull the triangles to the light volume + // motorsep 11-09-2014; added && shader->SurfaceCastsShadow() per Lordhavoc's recommendation; should skip shadows calculation for surfaces with noShadows material flag + // when using shadow volumes + if( r_cullDynamicLightTriangles.GetBool() && !r_skipDynamicShadows.GetBool() && !r_useShadowMapping.GetBool() && shader->SurfaceCastsShadow() ) { - lightDrawSurf->indexCache = lightIndexCache; - - dynamicShadowParms = ( dynamicShadowVolumeParms_t* )R_FrameAlloc( sizeof( dynamicShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS ); - - dynamicShadowParms->verts = tri->verts; - dynamicShadowParms->numVerts = tri->numVerts; - dynamicShadowParms->indexes = tri->indexes; - dynamicShadowParms->numIndexes = tri->numIndexes; - dynamicShadowParms->silEdges = tri->silEdges; - dynamicShadowParms->numSilEdges = tri->numSilEdges; - dynamicShadowParms->joints = gpuSkinned ? tri->staticModelWithJoints->jointsInverted : NULL; - dynamicShadowParms->numJoints = gpuSkinned ? tri->staticModelWithJoints->numInvertedJoints : 0; - dynamicShadowParms->triangleBounds = tri->bounds; - dynamicShadowParms->triangleMVP = vEntity->mvp; - dynamicShadowParms->localLightOrigin = localLightOrigin; - dynamicShadowParms->localViewOrigin = localViewOrigin; - idRenderMatrix::Multiply( vLight->lightDef->baseLightProject, entityDef->modelRenderMatrix, dynamicShadowParms->localLightProject ); - dynamicShadowParms->zNear = znear; - dynamicShadowParms->lightZMin = vLight->scissorRect.zmin; - dynamicShadowParms->lightZMax = vLight->scissorRect.zmax; - dynamicShadowParms->cullShadowTrianglesToLight = false; - dynamicShadowParms->forceShadowCaps = false; - dynamicShadowParms->useShadowPreciseInsideTest = false; - dynamicShadowParms->useShadowDepthBounds = false; - dynamicShadowParms->tempFacing = NULL; - dynamicShadowParms->tempCulled = NULL; - dynamicShadowParms->tempVerts = NULL; - dynamicShadowParms->indexBuffer = NULL; - dynamicShadowParms->shadowIndices = NULL; - dynamicShadowParms->maxShadowIndices = 0; - dynamicShadowParms->numShadowIndices = NULL; - dynamicShadowParms->lightIndices = ( triIndex_t* )vertexCache.MappedIndexBuffer( lightIndexCache ); - dynamicShadowParms->maxLightIndices = lightDrawSurf->numIndexes; - dynamicShadowParms->numLightIndices = &lightDrawSurf->numIndexes; - dynamicShadowParms->renderZFail = NULL; - dynamicShadowParms->shadowZMin = NULL; - dynamicShadowParms->shadowZMax = NULL; - dynamicShadowParms->shadowVolumeState = & lightDrawSurf->shadowVolumeState; - - lightDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED; - - dynamicShadowParms->next = vEntity->dynamicShadowVolumes; - vEntity->dynamicShadowVolumes = dynamicShadowParms; + vertCacheHandle_t lightIndexCache = vertexCache.AllocIndex( NULL, ALIGN( lightDrawSurf->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); + if( vertexCache.CacheIsCurrent( lightIndexCache ) ) + { + lightDrawSurf->indexCache = lightIndexCache; + + dynamicShadowParms = ( dynamicShadowVolumeParms_t* )R_FrameAlloc( sizeof( dynamicShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS ); + + dynamicShadowParms->verts = tri->verts; + dynamicShadowParms->numVerts = tri->numVerts; + dynamicShadowParms->indexes = tri->indexes; + dynamicShadowParms->numIndexes = tri->numIndexes; + dynamicShadowParms->silEdges = tri->silEdges; + dynamicShadowParms->numSilEdges = tri->numSilEdges; + dynamicShadowParms->joints = gpuSkinned ? tri->staticModelWithJoints->jointsInverted : NULL; + dynamicShadowParms->numJoints = gpuSkinned ? tri->staticModelWithJoints->numInvertedJoints : 0; + dynamicShadowParms->triangleBounds = tri->bounds; + dynamicShadowParms->triangleMVP = vEntity->mvp; + dynamicShadowParms->localLightOrigin = localLightOrigin; + dynamicShadowParms->localViewOrigin = localViewOrigin; + idRenderMatrix::Multiply( vLight->lightDef->baseLightProject, entityDef->modelRenderMatrix, dynamicShadowParms->localLightProject ); + dynamicShadowParms->zNear = znear; + dynamicShadowParms->lightZMin = vLight->scissorRect.zmin; + dynamicShadowParms->lightZMax = vLight->scissorRect.zmax; + dynamicShadowParms->cullShadowTrianglesToLight = false; + dynamicShadowParms->forceShadowCaps = false; + dynamicShadowParms->useShadowPreciseInsideTest = false; + dynamicShadowParms->useShadowDepthBounds = false; + dynamicShadowParms->tempFacing = NULL; + dynamicShadowParms->tempCulled = NULL; + dynamicShadowParms->tempVerts = NULL; + dynamicShadowParms->indexBuffer = NULL; + dynamicShadowParms->shadowIndices = NULL; + dynamicShadowParms->maxShadowIndices = 0; + dynamicShadowParms->numShadowIndices = NULL; + dynamicShadowParms->lightIndices = ( triIndex_t* )vertexCache.MappedIndexBuffer( lightIndexCache ); + dynamicShadowParms->maxLightIndices = lightDrawSurf->numIndexes; + dynamicShadowParms->numLightIndices = &lightDrawSurf->numIndexes; + dynamicShadowParms->renderZFail = NULL; + dynamicShadowParms->shadowZMin = NULL; + dynamicShadowParms->shadowZMax = NULL; + dynamicShadowParms->shadowVolumeState = & lightDrawSurf->shadowVolumeState; + + lightDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED; + + dynamicShadowParms->next = vEntity->dynamicShadowVolumes; + vEntity->dynamicShadowVolumes = dynamicShadowParms; + } } } + + lightDrawSurf->ambientCache = tri->ambientCache; + lightDrawSurf->shadowCache = 0; + lightDrawSurf->frontEndGeo = tri; + lightDrawSurf->space = vEntity; + lightDrawSurf->material = shader; + lightDrawSurf->extraGLState = 0; + lightDrawSurf->scissorRect = vLight->scissorRect; // interactionScissor; + lightDrawSurf->sort = 0.0f; + lightDrawSurf->renderZFail = 0; + lightDrawSurf->shaderRegisters = shaderRegisters; + + R_SetupDrawSurfJoints( lightDrawSurf, tri, shader ); + + // 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; } - lightDrawSurf->ambientCache = tri->ambientCache; - lightDrawSurf->shadowCache = 0; - lightDrawSurf->frontEndGeo = tri; - lightDrawSurf->space = vEntity; - lightDrawSurf->material = shader; - lightDrawSurf->extraGLState = 0; - lightDrawSurf->scissorRect = vLight->scissorRect; // interactionScissor; - lightDrawSurf->sort = 0.0f; - lightDrawSurf->renderZFail = 0; - lightDrawSurf->shaderRegisters = baseDrawSurf->shaderRegisters; - - R_SetupDrawSurfJoints( lightDrawSurf, tri, shader ); - - // 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; } } @@ -915,10 +966,51 @@ void R_AddSingleModel( viewEntity_t* vEntity ) // surface shadows //-------------------------- +#if 1 if( !shader->SurfaceCastsShadow() && !( r_useShadowMapping.GetBool() && r_forceShadowMapsOnAlphaTestedSurfaces.GetBool() && shader->Coverage() == MC_PERFORATED ) ) { continue; } +#else + // Steel Storm 2 behaviour - this destroys many alpha tested shadows in vanilla BFG + + // 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() ) + continue; + } + } + + // 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 + if( !lightDef->LightCastsShadows() ) { continue; @@ -929,9 +1021,10 @@ void R_AddSingleModel( viewEntity_t* vEntity ) } // if the static shadow does not have any shadows - if( surfInter != NULL && surfInter->numShadowIndexes == 0 && !r_useShadowMapping.GetBool() ) + if( surfInter != NULL && surfInter->numShadowIndexes == 0 ) { - continue; + if( !r_useShadowMapping.GetBool() ) + continue; } // some entities, like view weapons, don't cast any shadows @@ -946,6 +1039,7 @@ void R_AddSingleModel( viewEntity_t* vEntity ) continue; } + // RB begin if( r_useShadowMapping.GetBool() ) { @@ -1050,7 +1144,7 @@ void R_AddSingleModel( viewEntity_t* vEntity ) shadowDrawSurf->scissorRect = vLight->scissorRect; // default to the light scissor and light depth bounds shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE; // assume the shadow volume is done in case r_skipStaticShadows is set - if( !r_skipStaticShadows.GetBool() ) + if( !r_skipStaticShadows.GetBool() && !r_useShadowMapping.GetBool() ) { staticShadowVolumeParms_t* staticShadowParms = ( staticShadowVolumeParms_t* )R_FrameAlloc( sizeof( staticShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS ); @@ -1107,9 +1201,8 @@ void R_AddSingleModel( viewEntity_t* vEntity ) shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE; // assume the shadow volume is done in case the index cache allocation failed // if the index cache was successfully allocated then setup the parms to create a shadow volume in parallel - if( vertexCache.CacheIsCurrent( shadowDrawSurf->indexCache ) && !r_skipDynamicShadows.GetBool() ) + if( vertexCache.CacheIsCurrent( shadowDrawSurf->indexCache ) && !r_skipDynamicShadows.GetBool() && !r_useShadowMapping.GetBool() ) { - // if the parms were not already allocated for culling interaction triangles to the light frustum if( dynamicShadowParms == NULL ) { @@ -1265,48 +1358,55 @@ void R_AddModels() //------------------------------------------------- // Kick off jobs to setup static and dynamic shadow volumes. //------------------------------------------------- - - if( r_useParallelAddShadows.GetInteger() == 1 ) + if( ( r_skipStaticShadows.GetBool() && r_skipDynamicShadows.GetBool() ) || r_useShadowMapping.GetBool() ) { - for( viewEntity_t* vEntity = tr.viewDef->viewEntitys; vEntity != NULL; vEntity = vEntity->next ) - { - for( staticShadowVolumeParms_t* shadowParms = vEntity->staticShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) - { - tr.frontEndJobList->AddJob( ( jobRun_t )StaticShadowVolumeJob, shadowParms ); - } - for( dynamicShadowVolumeParms_t* shadowParms = vEntity->dynamicShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) - { - tr.frontEndJobList->AddJob( ( jobRun_t )DynamicShadowVolumeJob, shadowParms ); - } - vEntity->staticShadowVolumes = NULL; - vEntity->dynamicShadowVolumes = NULL; - } - tr.frontEndJobList->Submit(); - // wait here otherwise the shadow volume index buffer may be unmapped before all shadow volumes have been constructed - tr.frontEndJobList->Wait(); + // no shadow volumes were chained to any entity, all are in DONE state, we don't need to Submit() or Wait() } else { - int start = Sys_Microseconds(); - - for( viewEntity_t* vEntity = tr.viewDef->viewEntitys; vEntity != NULL; vEntity = vEntity->next ) + if( r_useParallelAddShadows.GetInteger() == 1 ) { - for( staticShadowVolumeParms_t* shadowParms = vEntity->staticShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) + for( viewEntity_t* vEntity = tr.viewDef->viewEntitys; vEntity != NULL; vEntity = vEntity->next ) { - StaticShadowVolumeJob( shadowParms ); + for( staticShadowVolumeParms_t* shadowParms = vEntity->staticShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) + { + tr.frontEndJobList->AddJob( ( jobRun_t )StaticShadowVolumeJob, shadowParms ); + } + for( dynamicShadowVolumeParms_t* shadowParms = vEntity->dynamicShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) + { + tr.frontEndJobList->AddJob( ( jobRun_t )DynamicShadowVolumeJob, shadowParms ); + } + vEntity->staticShadowVolumes = NULL; + vEntity->dynamicShadowVolumes = NULL; } - for( dynamicShadowVolumeParms_t* shadowParms = vEntity->dynamicShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) - { - DynamicShadowVolumeJob( shadowParms ); - } - vEntity->staticShadowVolumes = NULL; - vEntity->dynamicShadowVolumes = NULL; + tr.frontEndJobList->Submit(); + // wait here otherwise the shadow volume index buffer may be unmapped before all shadow volumes have been constructed + tr.frontEndJobList->Wait(); + } + else + { + int start = Sys_Microseconds(); + + for( viewEntity_t* vEntity = tr.viewDef->viewEntitys; vEntity != NULL; vEntity = vEntity->next ) + { + for( staticShadowVolumeParms_t* shadowParms = vEntity->staticShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) + { + StaticShadowVolumeJob( shadowParms ); + } + for( dynamicShadowVolumeParms_t* shadowParms = vEntity->dynamicShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) + { + DynamicShadowVolumeJob( shadowParms ); + } + vEntity->staticShadowVolumes = NULL; + vEntity->dynamicShadowVolumes = NULL; + } + + int end = Sys_Microseconds(); + backEnd.pc.shadowMicroSec += end - start; } - - int end = Sys_Microseconds(); - backEnd.pc.shadowMicroSec += end - start; } + //------------------------------------------------- // Move the draw surfs to the view. //-------------------------------------------------