|
|
//===== Copyright � 1996-2007, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include "render_pch.h"
#include "brushbatchrender.h"
#include "icliententity.h"
#include "r_decal.h"
#include "gl_rsurf.h"
#include "tier0/vprof.h"
#include <algorithm>
CBrushBatchRender g_BrushBatchRenderer;
FORCEINLINE void ModulateMaterial( IMaterial *pMaterial, float *pOldColor ) { if ( g_bIsBlendingOrModulating ) { pOldColor[3] = pMaterial->GetAlphaModulation( ); pMaterial->GetColorModulation( &pOldColor[0], &pOldColor[1], &pOldColor[2] ); pMaterial->AlphaModulate( r_blend ); pMaterial->ColorModulate( r_colormod[0], r_colormod[1], r_colormod[2] ); }
}
FORCEINLINE void UnModulateMaterial( IMaterial *pMaterial, float *pOldColor ) { if ( g_bIsBlendingOrModulating ) { pMaterial->AlphaModulate( pOldColor[3] ); pMaterial->ColorModulate( pOldColor[0], pOldColor[1], pOldColor[2] ); } }
int __cdecl CBrushBatchRender::SurfaceCmp(const surfacelist_t *s0, const surfacelist_t *s1 ) { int sortID0 = MSurf_MaterialSortID( s0->surfID ); int sortID1 = MSurf_MaterialSortID( s1->surfID );
return sortID0 - sortID1; }
// Builds a transrender_t, then executes it's drawing commands
void CBrushBatchRender::DrawTranslucentBrushModel( IMatRenderContext *pRenderContext, model_t *model, IClientEntity *baseentity ) { transrender_t render; render.pLastBatch = NULL; render.pLastNode = NULL; render.nodeCount = 0; render.surfaceCount = 0; render.batchCount = 0; render.decalSurfaceCount = 0; BuildTransLists_r( render, model, model->brush.pShared->nodes + model->brush.firstnode ); void *pProxyData = baseentity ? baseentity->GetClientRenderable() : NULL; DrawTransLists( pRenderContext, render, pProxyData ); }
void CBrushBatchRender::AddSurfaceToBatch( transrender_t &render, transnode_t *pNode, transbatch_t *pBatch, SurfaceHandle_t surfID ) { pBatch->surfaceCount++; Assert(render.surfaceCount < MAX_TRANS_SURFACES);
pBatch->indexCount += (MSurf_VertCount( surfID )-2)*3; render.surfaces[render.surfaceCount] = surfID; render.surfaceCount++; if ( SurfaceHasDecals( surfID ) || MSurf_ShadowDecals( surfID ) != SHADOW_DECAL_HANDLE_INVALID ) { Assert(render.decalSurfaceCount < MAX_TRANS_DECALS); pNode->decalSurfaceCount++; render.decalSurfaces[render.decalSurfaceCount] = surfID; render.decalSurfaceCount++; } }
void CBrushBatchRender::AddTransNode( transrender_t &render ) { render.pLastNode = &render.nodes[render.nodeCount]; render.nodeCount++; Assert(render.nodeCount < MAX_TRANS_NODES); render.pLastBatch = NULL; render.pLastNode->firstBatch = render.batchCount; render.pLastNode->firstDecalSurface = render.decalSurfaceCount; render.pLastNode->batchCount = 0; render.pLastNode->decalSurfaceCount = 0; }
void CBrushBatchRender::AddTransBatch( transrender_t &render, SurfaceHandle_t surfID ) { transbatch_t &batch = render.batches[render.pLastNode->firstBatch + render.pLastNode->batchCount]; Assert(render.batchCount < MAX_TRANS_BATCHES); render.pLastNode->batchCount++; render.batchCount++; batch.firstSurface = render.surfaceCount; batch.surfaceCount = 0; batch.pMaterial = MSurf_TexInfo( surfID )->material; batch.sortID = MSurf_MaterialSortID( surfID ); batch.indexCount = 0; render.pLastBatch = &batch; AddSurfaceToBatch( render, render.pLastNode, &batch, surfID ); }
// build node lists
void CBrushBatchRender::BuildTransLists_r( transrender_t &render, model_t *model, mnode_t *node ) { float dot;
if (node->contents >= 0) return;
// node is just a decision point, so go down the apropriate sides
// find which side of the node we are on
cplane_t *plane = node->plane; if ( plane->type <= PLANE_Z ) { dot = modelorg[plane->type] - plane->dist; } else { dot = DotProduct (modelorg, plane->normal) - plane->dist; }
int side = (dot >= 0) ? 0 : 1;
// back side first - translucent surfaces need to render in back to front order
// to appear correctly.
BuildTransLists_r(render, model, node->children[!side]);
// emit all surfaces on node
CUtlVectorFixed<surfacelist_t, 256> sortList; SurfaceHandle_t surfID = SurfaceHandleFromIndex( node->firstsurface, model->brush.pShared ); for ( int i = 0; i < node->numsurfaces; i++, surfID++ ) { // skip opaque surfaces
if ( MSurf_Flags(surfID) & SURFDRAW_TRANS ) { if ( ((MSurf_Flags( surfID ) & SURFDRAW_NOCULL) == 0) ) { // Backface cull here, so they won't do any more work
if ( ( side ^ !!(MSurf_Flags( surfID ) & SURFDRAW_PLANEBACK)) ) continue; }
// If this can be appended to the previous batch, do so
int sortID = MSurf_MaterialSortID( surfID ); if ( render.pLastBatch && render.pLastBatch->sortID == sortID ) { AddSurfaceToBatch( render, render.pLastNode, render.pLastBatch, surfID ); } else { // save it off for sorting, then a later append
int sortIndex = sortList.AddToTail(); sortList[sortIndex].surfID = surfID; } } }
// We've got surfaces on this node that can't be added to the previous node
if ( sortList.Count() ) { // sort by material
sortList.Sort( SurfaceCmp );
// add a new sort group
AddTransNode( render ); int lastSortID = -1; // now add the optimal number of batches to that group
for ( int i = 0; i < sortList.Count(); i++ ) { surfID = sortList[i].surfID; int sortID = MSurf_MaterialSortID( surfID ); if ( lastSortID == sortID ) { // can be drawn in a single call with the current list of surfaces, append
AddSurfaceToBatch( render, render.pLastNode, render.pLastBatch, surfID ); } else { // requires a break (material/lightmap change).
AddTransBatch( render, surfID ); lastSortID = sortID; } }
// don't batch across decals or decals will sort incorrectly
if ( render.pLastNode->decalSurfaceCount ) { render.pLastNode = NULL; render.pLastBatch = NULL; } }
// front side
BuildTransLists_r(render, model, node->children[side]); }
void CBrushBatchRender::DrawTransLists( IMatRenderContext *pRenderContext, transrender_t &render, void *pProxyData ) { PIXEVENT( pRenderContext, "DrawTransLists()" );
bool skipLight = false; if ( g_pMaterialSystemConfig->nFullbright == 1 ) { pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP ); skipLight = true; }
float pOldColor[4];
for ( int i = 0; i < render.nodeCount; i++ ) { int j; const transnode_t &node = render.nodes[i]; for ( j = 0; j < node.batchCount; j++ ) { const transbatch_t &batch = render.batches[node.firstBatch+j];
CMeshBuilder meshBuilder; IMaterial *pMaterial = batch.pMaterial;
ModulateMaterial( pMaterial, pOldColor );
if ( !skipLight ) { pRenderContext->BindLightmapPage( materialSortInfoArray[batch.sortID].lightmapPageID ); } pRenderContext->Bind( pMaterial, pProxyData );
IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false, g_WorldStaticMeshes[batch.sortID], NULL, NULL ); meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, 0, batch.indexCount );
for ( int k = 0; k < batch.surfaceCount; k++ ) { SurfaceHandle_t surfID = render.surfaces[batch.firstSurface + k]; Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) ); BuildIndicesForSurface( meshBuilder, surfID );
} meshBuilder.End( false, true );
// Don't leave the material in a bogus state
UnModulateMaterial( pMaterial, pOldColor ); }
if ( node.decalSurfaceCount ) { for ( j = 0; j < node.decalSurfaceCount; j++ ) { SurfaceHandle_t surfID = render.decalSurfaces[node.firstDecalSurface + j]; Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) ); if( SurfaceHasDecals( surfID ) ) { DecalSurfaceAdd( surfID, BRUSHMODEL_DECAL_SORT_GROUP ); }
// Add shadows too....
ShadowDecalHandle_t decalHandle = MSurf_ShadowDecals( surfID ); if (decalHandle != SHADOW_DECAL_HANDLE_INVALID) { g_pShadowMgr->AddShadowsOnSurfaceToRenderList( decalHandle ); } }
// Now draw the decals + shadows for this node
// This order relative to the surfaces is important for translucency to work correctly.
DecalSurfaceDraw(pRenderContext, BRUSHMODEL_DECAL_SORT_GROUP);
// FIXME: Decals are not being rendered while illuminated by the flashlight
// FIXME: Need to draw decals in flashlight rendering mode
// Retire decals on opaque world surfaces
R_DecalFlushDestroyList();
DecalSurfacesInit( true );
// Draw all shadows on the brush
g_pShadowMgr->RenderProjectedTextures( pRenderContext ); } if ( g_ShaderDebug.anydebug ) { CUtlVector<msurface2_t *> brushList; for ( j = 0; j < node.batchCount; j++ ) { const transbatch_t &batch = render.batches[node.firstBatch+j]; for ( int k = 0; k < batch.surfaceCount; k++ ) { brushList.AddToTail( render.surfaces[batch.firstSurface + k] ); } } DrawDebugInformation( pRenderContext, brushList.Base(), brushList.Count() ); } } }
//-----------------------------------------------------------------------------
// Purpose: This is used when the mat_dxlevel is changed to reset the brush
// models.
//-----------------------------------------------------------------------------
void R_BrushBatchInit( void ) { g_BrushBatchRenderer.LevelInit(); }
void CBrushBatchRender::LevelInit() { AUTO_LOCK( m_Mutex );
unsigned short iNext; for( unsigned short i=m_renderList.Head(); i != m_renderList.InvalidIndex(); i=iNext ) { iNext = m_renderList.Next(i); delete m_renderList.Element(i); }
m_renderList.Purge(); ClearRenderHandles(); }
void CBrushBatchRender::ClearRenderHandles( void ) { for ( int iBrush = 1 ; iBrush < host_state.worldbrush->numsubmodels ; ++iBrush ) { char szBrushModel[5]; // inline model names "*1", "*2" etc
Q_snprintf( szBrushModel, sizeof( szBrushModel ), "*%i", iBrush ); model_t *pModel = modelloader->GetModelForName( szBrushModel, IModelLoader::FMODELLOADER_SERVER ); if ( pModel ) { pModel->brush.renderHandle = 0; } } }
// Create a compact, optimal list of rendering commands for the opaque parts of a brush model
// NOTE: This just skips translucent surfaces assuming a separate transrender_t pass!
CBrushBatchRender::brushrender_t *CBrushBatchRender::FindOrCreateRenderBatch( model_t *pModel ) { if ( !pModel->brush.nummodelsurfaces ) return NULL;
AUTO_LOCK( m_Mutex );
unsigned short index = pModel->brush.renderHandle - 1; if ( m_renderList.IsValidIndex( index ) ) return m_renderList.Element(index);
brushrender_t *pRender = new brushrender_t; index = m_renderList.AddToTail( pRender ); pModel->brush.renderHandle = index + 1; brushrender_t &render = *pRender;
render.pPlanes = NULL; render.pMeshes = NULL; render.planeCount = 0; render.meshCount = 0; render.totalIndexCount = 0; render.totalVertexCount = 0;
CUtlVector<cplane_t *> planeList; CUtlVector<surfacelist_t> surfaceList;
int i;
SurfaceHandle_t surfID = SurfaceHandleFromIndex( pModel->brush.firstmodelsurface, pModel->brush.pShared ); for (i=0 ; i<pModel->brush.nummodelsurfaces; i++, surfID++) { // UNDONE: For now, just draw these in a separate pass
if ( MSurf_Flags(surfID) & SURFDRAW_TRANS ) continue;
#ifndef _PS3
cplane_t *plane = surfID->plane; #else
cplane_t *plane = &surfID->m_plane; #endif
int planeIndex = planeList.Find(plane); if ( planeIndex == -1 ) { planeIndex = planeList.AddToTail( plane ); } surfacelist_t tmp; tmp.surfID = surfID; tmp.surfaceIndex = i; tmp.planeIndex = planeIndex; surfaceList.AddToTail( tmp ); } surfaceList.Sort( SurfaceCmp ); render.pPlanes = new cplane_t *[planeList.Count()]; render.planeCount = planeList.Count(); memcpy( render.pPlanes, planeList.Base(), sizeof(cplane_t *)*planeList.Count() ); render.pSurfaces = new brushrendersurface_t[surfaceList.Count()]; render.surfaceCount = surfaceList.Count();
int meshCount = 0; int batchCount = 0; int lastSortID = -1; IMesh *pLastMesh = NULL;
brushrendermesh_t *pMesh = NULL; brushrendermesh_t tmpMesh[MAX_VERTEX_FORMAT_CHANGES]; brushrenderbatch_t *pBatch = NULL; brushrenderbatch_t tmpBatch[128];
for ( i = 0; i < surfaceList.Count(); i++ ) { render.pSurfaces[i].surfaceIndex = surfaceList[i].surfaceIndex; render.pSurfaces[i].planeIndex = surfaceList[i].planeIndex;
surfID = surfaceList[i].surfID; int sortID = MSurf_MaterialSortID( surfID ); if ( g_WorldStaticMeshes[sortID] != pLastMesh ) { pMesh = tmpMesh + meshCount; pMesh->firstBatch = batchCount; pMesh->batchCount = 0; lastSortID = -1; // force a new batch
meshCount++; } if ( sortID != lastSortID ) { pBatch = tmpBatch + batchCount; pBatch->firstSurface = i; pBatch->surfaceCount = 0; pBatch->sortID = sortID; pBatch->pMaterial = MSurf_TexInfo( surfID )->material; pBatch->indexCount = 0; pMesh->batchCount++; batchCount++; } pLastMesh = g_WorldStaticMeshes[sortID]; lastSortID = sortID; pBatch->surfaceCount++; int vertCount = MSurf_VertCount( surfID ); int indexCount = (vertCount - 2) * 3; pBatch->indexCount += indexCount; render.totalIndexCount += indexCount; render.totalVertexCount += vertCount; }
render.pMeshes = new brushrendermesh_t[meshCount]; memcpy( render.pMeshes, tmpMesh, sizeof(brushrendermesh_t) * meshCount ); render.meshCount = meshCount; render.pBatches = new brushrenderbatch_t[batchCount]; memcpy( render.pBatches, tmpBatch, sizeof(brushrenderbatch_t) * batchCount ); render.batchCount = batchCount; return &render; }
//-----------------------------------------------------------------------------
// Draws an opaque (parts of a) brush model
//-----------------------------------------------------------------------------
void CBrushBatchRender::DrawOpaqueBrushModel( IMatRenderContext *pRenderContext, IClientEntity *baseentity, model_t *model, ERenderDepthMode_t DepthMode ) { VPROF( "R_DrawOpaqueBrushModel" ); SurfaceHandle_t firstSurfID = SurfaceHandleFromIndex( model->brush.firstmodelsurface, model->brush.pShared );
brushrender_t *pRender = FindOrCreateRenderBatch( model ); int i; if ( !pRender ) return;
bool skipLight = false;
PIXEVENT( pRenderContext, "DrawOpaqueBrushModel()" );
if ( (g_pMaterialSystemConfig->nFullbright == 1) || ( DepthMode == DEPTH_MODE_SHADOW || DepthMode == DEPTH_MODE_SSA0 ) ) { pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP ); skipLight = true; }
void *pProxyData = baseentity ? baseentity->GetClientRenderable() : NULL; int backface[1024]; Assert( pRender->planeCount < 1024 );
// NOTE: Backface culling is almost no perf gain. Can be removed from brush model rendering.
// Check the shared planes once
if ( DepthMode == DEPTH_MODE_SHADOW || DepthMode == DEPTH_MODE_SSA0 ) { for ( i = 0; i < pRender->planeCount; i++ ) { backface[i] = 0; } } else { for ( i = 0; i < pRender->planeCount; i++ ) { float dot = DotProduct( modelorg, pRender->pPlanes[i]->normal) - pRender->pPlanes[i]->dist; backface[i] = ( dot < BACKFACE_EPSILON ) ? true : false; // don't backface cull when rendering to shadow map
} }
float pOldColor[4];
// PORTAL 2 PAINT RENDERING
CUtlVectorFixedGrowable< SurfaceHandle_t, 64 > paintableSurfaces; CUtlVectorFixedGrowable< int, 16 > batchPaintableSurfaceCount; CUtlVectorFixedGrowable< int, 16 > batchPaintableSurfaceIndexCount;
for ( i = 0; i < pRender->meshCount; i++ ) { brushrendermesh_t &mesh = pRender->pMeshes[i]; for ( int j = 0; j < mesh.batchCount; j++ ) { int nBatchIndex = batchPaintableSurfaceCount.Count(); batchPaintableSurfaceCount.AddToTail( 0 ); batchPaintableSurfaceIndexCount.AddToTail( 0 );
brushrenderbatch_t &batch = pRender->pBatches[mesh.firstBatch + j];
int k; for ( k = 0; k < batch.surfaceCount; k++ ) { brushrendersurface_t &surface = pRender->pSurfaces[batch.firstSurface + k]; if ( !backface[surface.planeIndex] ) break; }
if ( k == batch.surfaceCount ) continue;
CMeshBuilder meshBuilder; IMaterial *pMaterial = NULL;
if ( DepthMode != DEPTH_MODE_NORMAL ) { // Select proper override material
int nAlphaTest = (int) batch.pMaterial->IsAlphaTested(); int nNoCull = (int) batch.pMaterial->IsTwoSided(); IMaterial *pDepthWriteMaterial;
if ( DepthMode == DEPTH_MODE_SHADOW ) { pDepthWriteMaterial = g_pMaterialDepthWrite[ nAlphaTest ][ nNoCull ]; } else { pDepthWriteMaterial = g_pMaterialSSAODepthWrite[ nAlphaTest ][ nNoCull ]; }
if ( nAlphaTest == 1 ) { static unsigned int originalTextureVarCache = 0; IMaterialVar *pOriginalTextureVar = batch.pMaterial->FindVarFast( "$basetexture", &originalTextureVarCache ); static unsigned int originalTextureFrameVarCache = 0; IMaterialVar *pOriginalTextureFrameVar = batch.pMaterial->FindVarFast( "$frame", &originalTextureFrameVarCache ); static unsigned int originalAlphaRefCache = 0; IMaterialVar *pOriginalAlphaRefVar = batch.pMaterial->FindVarFast( "$AlphaTestReference", &originalAlphaRefCache );
static unsigned int textureVarCache = 0; IMaterialVar *pTextureVar = pDepthWriteMaterial->FindVarFast( "$basetexture", &textureVarCache ); static unsigned int textureFrameVarCache = 0; IMaterialVar *pTextureFrameVar = pDepthWriteMaterial->FindVarFast( "$frame", &textureFrameVarCache ); static unsigned int alphaRefCache = 0; IMaterialVar *pAlphaRefVar = pDepthWriteMaterial->FindVarFast( "$AlphaTestReference", &alphaRefCache );
if( pTextureVar && pOriginalTextureVar ) { pTextureVar->SetTextureValue( pOriginalTextureVar->GetTextureValue() ); }
if( pTextureFrameVar && pOriginalTextureFrameVar ) { pTextureFrameVar->SetIntValue( pOriginalTextureFrameVar->GetIntValue() ); }
if( pAlphaRefVar && pOriginalAlphaRefVar ) { pAlphaRefVar->SetFloatValue( pOriginalAlphaRefVar->GetFloatValue() ); } }
pMaterial = pDepthWriteMaterial; } else { pMaterial = batch.pMaterial;
// Store off the old color + alpha
ModulateMaterial( pMaterial, pOldColor ); if ( !skipLight ) { pRenderContext->BindLightmapPage( materialSortInfoArray[batch.sortID].lightmapPageID ); } }
pRenderContext->Bind( pMaterial, pProxyData ); IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false, g_WorldStaticMeshes[batch.sortID], NULL, NULL ); meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, 0, batch.indexCount );
for ( ; k < batch.surfaceCount; k++ ) { brushrendersurface_t &surface = pRender->pSurfaces[batch.firstSurface + k]; if ( backface[surface.planeIndex] ) continue; SurfaceHandle_t surfID = firstSurfID + surface.surfaceIndex;
Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
// PORTAL 2 PAINT RENDERING
if ( (DepthMode == DEPTH_MODE_NORMAL) && ( MSurf_Flags( surfID ) & SURFDRAW_PAINTED ) ) { paintableSurfaces.AddToTail( surfID ); ++ batchPaintableSurfaceCount[ nBatchIndex ];
int nVertCount, nIndexCount;
Shader_GetSurfVertexAndIndexCount( surfID, &nVertCount, &nIndexCount ); batchPaintableSurfaceIndexCount[ nBatchIndex ] += nIndexCount; } BuildIndicesForSurface( meshBuilder, surfID );
if( SurfaceHasDecals( surfID ) && (DepthMode == DEPTH_MODE_NORMAL)) { DecalSurfaceAdd( surfID, BRUSHMODEL_DECAL_SORT_GROUP ); }
// Add overlay fragments to list.
// FIXME: A little code support is necessary to get overlays working on brush models
// OverlayMgr()->AddFragmentListToRenderList( MSurf_OverlayFragmentList( surfID ), false );
if ( DepthMode == DEPTH_MODE_NORMAL ) { // Add render-to-texture shadows too....
ShadowDecalHandle_t decalHandle = MSurf_ShadowDecals( surfID ); if (decalHandle != SHADOW_DECAL_HANDLE_INVALID) { g_pShadowMgr->AddShadowsOnSurfaceToRenderList( decalHandle ); } } }
meshBuilder.End( false, true );
if ( DepthMode == DEPTH_MODE_NORMAL ) { // Don't leave the material in a bogus state
UnModulateMaterial( pMaterial, pOldColor ); } } }
if ( DepthMode != DEPTH_MODE_NORMAL ) return;
// PORTAL 2 PAINT RENDERING
if ( paintableSurfaces.Count() ) { pRenderContext->SetRenderingPaint( true ); PIXEVENT( pRenderContext, "Paint" );
int nBatchIndex = 0; int nSurfaceIndex = 0; for ( i = 0; i < pRender->meshCount; i++ ) { brushrendermesh_t &mesh = pRender->pMeshes[i]; for ( int j = 0; j < mesh.batchCount; j++ ) { int nSurfaceCount = batchPaintableSurfaceCount[ nBatchIndex ]; if ( nSurfaceCount > 0 ) { brushrenderbatch_t &batch = pRender->pBatches[mesh.firstBatch + j];
IMaterial *pMaterial = batch.pMaterial;
if ( !skipLight ) { pRenderContext->BindLightmapPage( materialSortInfoArray[batch.sortID].lightmapPageID ); }
pRenderContext->Bind( pMaterial, pProxyData );
CMeshBuilder meshBuilder; IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false, g_WorldStaticMeshes[batch.sortID], NULL, NULL ); meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, 0, batchPaintableSurfaceIndexCount[ nBatchIndex ] );
for ( int i = 0; i < nSurfaceCount; ++ i, ++ nSurfaceIndex ) { BuildIndicesForSurface( meshBuilder, paintableSurfaces[ nSurfaceIndex ] ); }
meshBuilder.End( false, true ); }
++ nBatchIndex; } } pRenderContext->SetRenderingPaint( false ); }
if ( g_ShaderDebug.anydebug ) { for ( i = 0; i < pRender->meshCount; i++ ) { brushrendermesh_t &mesh = pRender->pMeshes[i]; CUtlVector<msurface2_t *> brushList; for ( int j = 0; j < mesh.batchCount; j++ ) { brushrenderbatch_t &batch = pRender->pBatches[mesh.firstBatch + j]; for ( int k = 0; k < batch.surfaceCount; k++ ) { brushrendersurface_t &surface = pRender->pSurfaces[batch.firstSurface + k]; if ( backface[surface.planeIndex] ) continue; SurfaceHandle_t surfID = firstSurfID + surface.surfaceIndex; brushList.AddToTail(surfID); } } // now draw debug for each drawn surface
DrawDebugInformation( pRenderContext, brushList.Base(), brushList.Count() ); } } }
//-----------------------------------------------------------------------------
// Draws an translucent (sorted) brush model
//-----------------------------------------------------------------------------
void CBrushBatchRender::DrawTranslucentBrushModel( IMatRenderContext *pRenderContext, IClientEntity *baseentity, model_t *model, ERenderDepthMode_t DepthMode, bool bDrawOpaque, bool bDrawTranslucent ) { if ( bDrawOpaque ) { DrawOpaqueBrushModel( pRenderContext, baseentity, model, DepthMode ); }
if ( ( DepthMode == DEPTH_MODE_NORMAL ) && bDrawTranslucent ) { DrawTranslucentBrushModel( pRenderContext, model, baseentity ); } }
//-----------------------------------------------------------------------------
// Purpose: Draws a brush model shadow for render-to-texture shadows
//-----------------------------------------------------------------------------
// UNDONE: This is reasonable, but it could be much faster as follows:
// Build a vertex buffer cache. A block-allocated static mesh with 1024 verts
// per block or something.
// When a new brush is encountered, fill it in to the current block or the
// next one (first fit allocator). Then this routine could simply draw
// a static mesh with a single index buffer build, draw call (no dynamic vb).
void CBrushBatchRender::DrawBrushModelShadow( IMatRenderContext *pRenderContext, model_t *model, IClientRenderable *pRenderable ) { brushrender_t *pRender = FindOrCreateRenderBatch( (model_t *)model ); if ( !pRender ) return;
pRenderContext->Bind( g_pMaterialShadowBuild, pRenderable );
// Draws all surfaces in the brush model in arbitrary order
SurfaceHandle_t surfID = SurfaceHandleFromIndex( model->brush.firstmodelsurface, model->brush.pShared ); IMesh *pMesh = pRenderContext->GetDynamicMesh(); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, pRender->totalVertexCount, pRender->totalIndexCount );
for ( int i=0 ; i<model->brush.nummodelsurfaces ; i++, surfID++) { Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
if ( MSurf_Flags(surfID) & SURFDRAW_TRANS ) continue;
int startVert = MSurf_FirstVertIndex( surfID ); int vertCount = MSurf_VertCount( surfID ); int startIndex = meshBuilder.GetCurrentVertex(); int j; for ( j = 0; j < vertCount; j++ ) { int vertIndex = model->brush.pShared->vertindices[startVert + j];
// world-space vertex
meshBuilder.Position3fv( model->brush.pShared->vertexes[vertIndex].position.Base() ); meshBuilder.TexCoord2f( 0, 0.0f, 0.0f ); meshBuilder.AdvanceVertex(); }
for ( j = 0; j < vertCount-2; j++ ) { meshBuilder.FastIndex( startIndex ); meshBuilder.FastIndex( startIndex + j + 1 ); meshBuilder.FastIndex( startIndex + j + 2 ); } } meshBuilder.End(); pMesh->Draw(); }
inline bool __cdecl CBrushBatchRender::BatchSortLessFunc( const BrushBatchRenderData_t &left, const BrushBatchRenderData_t &right ) { brushrenderbatch_t &leftBatch = left.m_pBrushRender->pBatches[ left.m_nBatchIndex ]; brushrenderbatch_t &rightBatch = right.m_pBrushRender->pBatches[ right.m_nBatchIndex ];
if ( left.m_pMaterial != right.m_pMaterial ) return left.m_pMaterial < right.m_pMaterial; if ( leftBatch.sortID != rightBatch.sortID ) return leftBatch.sortID < rightBatch.sortID; if ( left.m_pInstanceData->m_pStencilState != right.m_pInstanceData->m_pStencilState ) return left.m_pInstanceData->m_pStencilState < right.m_pInstanceData->m_pStencilState; if ( left.m_pInstanceData->m_pBrushToWorld != right.m_pInstanceData->m_pBrushToWorld ) return left.m_pInstanceData->m_pBrushToWorld < right.m_pInstanceData->m_pBrushToWorld; return false; }
//-----------------------------------------------------------------------------
// Builds the lists of stuff to draw
//-----------------------------------------------------------------------------
void CBrushBatchRender::BuildBatchListToDraw( int nCount, const BrushArrayInstanceData_t *pInstanceData, CUtlVectorFixedGrowable< BrushBatchRenderData_t, 1024 > &batchesToRender, brushrender_t **ppBrushRender ) { for ( int i = 0; i < nCount; ++i ) { const BrushArrayInstanceData_t &instance = pInstanceData[i]; brushrender_t *pRender = g_BrushBatchRenderer.FindOrCreateRenderBatch( const_cast< model_t* >( instance.m_pBrushModel ) ); ppBrushRender[i] = pRender; if ( !pRender ) continue;
for ( int m = 0; m < pRender->meshCount; ++m ) { brushrendermesh_t &mesh = pRender->pMeshes[m]; int nBatchIndex = mesh.firstBatch; for ( int j = 0; j < mesh.batchCount; ++j, ++nBatchIndex ) { int nIndex = batchesToRender.AddToTail(); BrushBatchRenderData_t &batch = batchesToRender[nIndex]; batch.m_pMaterial = pRender->pBatches[nBatchIndex].pMaterial; batch.m_pInstanceData = &instance; batch.m_pBrushRender = pRender; batch.m_nBatchIndex = nBatchIndex; Assert( nBatchIndex < ( 1 << 15 ) ); } } } }
//-----------------------------------------------------------------------------
// Computes lightmap pages
//-----------------------------------------------------------------------------
void CBrushBatchRender::ComputeLightmapPages( int nCount, BrushBatchRenderData_t *pRenderData ) { if ( g_pMaterialSystemConfig->nFullbright != 1 ) { for ( int i = 0; i < nCount; ++i ) { brushrenderbatch_t &batch = pRenderData[i].m_pBrushRender->pBatches[ pRenderData[i].m_nBatchIndex ]; int nSortID = batch.sortID; Assert( nSortID >= 0 && nSortID < g_WorldStaticMeshes.Count() ); pRenderData[i].m_nLightmapPage = materialSortInfoArray[ nSortID ].lightmapPageID; } return; }
// Fullbright case. Bump works for unbumped surfaces too
for ( int i = 0; i < nCount; ++i ) { pRenderData[i].m_nLightmapPage = MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP; } }
//-----------------------------------------------------------------------------
// Computes the # of indices in an instance group
//-----------------------------------------------------------------------------
int CBrushBatchRender::ComputeInstanceGroups( IMatRenderContext *pRenderContext, int nCount, BrushBatchRenderData_t *pRenderData, CUtlVectorFixedGrowable< BrushInstanceGroup_t, 512 > &instanceGroups ) { int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
int nMaxInstanceCount = 0; IMaterial *pLastMaterial = NULL; IMaterial *pLastActualMaterial = NULL; BrushBatchRenderData_t *pFirstInstance = NULL; int nInstanceCount = 0; int nIndexCount = 0; for ( int i = 0; i < nCount; i++ ) { BrushBatchRenderData_t &renderData = pRenderData[i]; brushrenderbatch_t &batch = renderData.m_pBrushRender->pBatches[ renderData.m_nBatchIndex ];
int nNextIndexCount = nIndexCount + batch.indexCount;
// Fire it away if the material changes or we overflow index count
if ( ( pLastMaterial != batch.pMaterial ) || ( pLastMaterial != pLastActualMaterial ) || ( nNextIndexCount > nMaxIndices ) ) { if ( nInstanceCount > 0 ) { int nIndex = instanceGroups.AddToTail(); instanceGroups[nIndex].m_pRenderData = pFirstInstance; instanceGroups[nIndex].m_nCount = nInstanceCount; instanceGroups[nIndex].m_nIndexCount = nIndexCount; instanceGroups[nIndex].m_pMaterial = pLastMaterial; instanceGroups[nIndex].m_pActualMaterial = pLastActualMaterial; if ( nInstanceCount > nMaxInstanceCount ) { nMaxInstanceCount = nInstanceCount; } } nInstanceCount = 0; pFirstInstance = &renderData; nIndexCount = batch.indexCount; pLastMaterial = renderData.m_pMaterial;
// This is necessary for shadow depth rendering. We need to re-bind
// for every alpha tested material
pLastActualMaterial = renderData.m_nIsAlphaTested ? batch.pMaterial : pLastMaterial; } else { nIndexCount = nNextIndexCount; }
++nInstanceCount; }
if ( nInstanceCount > 0 ) { int nIndex = instanceGroups.AddToTail(); instanceGroups[nIndex].m_pRenderData = pFirstInstance; instanceGroups[nIndex].m_nCount = nInstanceCount; instanceGroups[nIndex].m_nIndexCount = nIndexCount; instanceGroups[nIndex].m_pMaterial = pLastMaterial; instanceGroups[nIndex].m_pActualMaterial = pLastActualMaterial; if ( nInstanceCount > nMaxInstanceCount ) { nMaxInstanceCount = nInstanceCount; } }
return nMaxInstanceCount; }
//-----------------------------------------------------------------------------
// Draws an opaque (parts of a) brush model
//-----------------------------------------------------------------------------
bool CBrushBatchRender::DrawSortedBatchList( IMatRenderContext* pRenderContext, int nCount, BrushInstanceGroup_t *pInstanceGroup, int nMaxInstanceCount ) { VPROF( "DrawSortedBatchList" ); PIXEVENT( pRenderContext, "DrawSortedBatchList()" );
// Needed to allow the system to detect which samplers are bound to lightmap textures
pRenderContext->BindLightmapPage( 0 );
MeshInstanceData_t *pInstance = (MeshInstanceData_t*)stackalloc( nMaxInstanceCount * sizeof(MeshInstanceData_t) );
bool bHasPaintedSurfaces = false; for ( int i = 0; i < nCount; i++ ) { BrushInstanceGroup_t &group = pInstanceGroup[i];
pRenderContext->Bind( group.m_pMaterial, NULL );
// Only writing indices
// FIXME: Can we make this a static index buffer?
IIndexBuffer *pBuildIndexBuffer = pRenderContext->GetDynamicIndexBuffer(); CIndexBuilder indexBuilder( pBuildIndexBuffer, MATERIAL_INDEX_FORMAT_16BIT ); indexBuilder.Lock( group.m_nIndexCount, 0 ); int nIndexOffset = indexBuilder.Offset() / sizeof(uint16);
group.m_nHasPaintedSurfaces = false; for ( int j = 0; j < group.m_nCount; ++j ) { BrushBatchRenderData_t &renderData = group.m_pRenderData[j]; const brushrenderbatch_t &batch = renderData.m_pBrushRender->pBatches[ renderData.m_nBatchIndex ]; renderData.m_nHasPaintedSurfaces = false; const model_t *pModel = renderData.m_pInstanceData->m_pBrushModel; SurfaceHandle_t firstSurfID = SurfaceHandleFromIndex( pModel->brush.firstmodelsurface, pModel->brush.pShared ); for ( int k = 0; k < batch.surfaceCount; k++ ) { const brushrendersurface_t &surface = renderData.m_pBrushRender->pSurfaces[batch.firstSurface + k]; SurfaceHandle_t surfID = firstSurfID + surface.surfaceIndex;
Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) ); BuildIndicesForSurface( indexBuilder, surfID ); if ( MSurf_Flags( surfID ) & SURFDRAW_PAINTED ) { group.m_nHasPaintedSurfaces = true; renderData.m_nHasPaintedSurfaces = true; bHasPaintedSurfaces = true; } }
MeshInstanceData_t &instance = pInstance[ j ]; instance.m_pEnvCubemap = NULL; instance.m_pPoseToWorld = renderData.m_pInstanceData->m_pBrushToWorld; instance.m_pLightingState = NULL; instance.m_nBoneCount = 1; instance.m_pBoneRemap = NULL; instance.m_nIndexOffset = nIndexOffset; instance.m_nIndexCount = batch.indexCount; instance.m_nPrimType = MATERIAL_TRIANGLES; instance.m_pColorBuffer = NULL; instance.m_nColorVertexOffsetInBytes = 0; instance.m_pStencilState = renderData.m_pInstanceData->m_pStencilState; instance.m_pVertexBuffer = g_WorldStaticMeshes[ batch.sortID ]; instance.m_pIndexBuffer = pBuildIndexBuffer; instance.m_nVertexOffsetInBytes = 0; instance.m_DiffuseModulation.Init( 1.0f, 1.0f, 1.0f, 1.0f ); instance.m_nLightmapPageId = renderData.m_nLightmapPage; instance.m_bColorBufferHasIndirectLightingOnly = false;
nIndexOffset += batch.indexCount; }
indexBuilder.End( ); pRenderContext->DrawInstances( group.m_nCount, pInstance ); }
return bHasPaintedSurfaces; }
void CBrushBatchRender::DrawPaintForBatches( IMatRenderContext* pRenderContext, int nCount, const BrushInstanceGroup_t *pInstanceGroup, int nMaxInstanceCount ) { MeshInstanceData_t *pInstance = (MeshInstanceData_t*)stackalloc( nMaxInstanceCount * sizeof(MeshInstanceData_t) );
pRenderContext->SetRenderingPaint( true ); PIXEVENT( pRenderContext, "Paint" );
for ( int i = 0; i < nCount; i++ ) { const BrushInstanceGroup_t &group = pInstanceGroup[i]; if ( !group.m_nHasPaintedSurfaces ) continue;
pRenderContext->Bind( group.m_pMaterial, NULL );
// Only writing indices, we're potentially allocating too many, but that's ok.
// Unused ones will be freed up
// FIXME: Can we make this a static index buffer?
IIndexBuffer *pBuildIndexBuffer = pRenderContext->GetDynamicIndexBuffer(); CIndexBuilder indexBuilder( pBuildIndexBuffer, MATERIAL_INDEX_FORMAT_16BIT ); indexBuilder.Lock( group.m_nIndexCount, 0 ); int nIndexOffset = indexBuilder.Offset() / sizeof(uint16); int nGroupCount = 0; for ( int j = 0; j < group.m_nCount; ++j ) { const BrushBatchRenderData_t &renderData = group.m_pRenderData[j]; if ( !renderData.m_nHasPaintedSurfaces ) continue;
const brushrenderbatch_t &batch = renderData.m_pBrushRender->pBatches[ renderData.m_nBatchIndex ]; const model_t *pModel = renderData.m_pInstanceData->m_pBrushModel; SurfaceHandle_t firstSurfID = SurfaceHandleFromIndex( pModel->brush.firstmodelsurface, pModel->brush.pShared ); int nBatchIndexCount = 0; for ( int k = 0; k < batch.surfaceCount; k++ ) { const brushrendersurface_t &surface = renderData.m_pBrushRender->pSurfaces[batch.firstSurface + k]; SurfaceHandle_t surfID = firstSurfID + surface.surfaceIndex; if ( ( MSurf_Flags( surfID ) & SURFDRAW_PAINTED ) == 0 ) continue;
int nTriCount = BuildIndicesForSurface( indexBuilder, surfID ); nBatchIndexCount += nTriCount * 3; }
MeshInstanceData_t &instance = pInstance[ nGroupCount ]; instance.m_pEnvCubemap = NULL; instance.m_pPoseToWorld = renderData.m_pInstanceData->m_pBrushToWorld; instance.m_pLightingState = NULL; instance.m_nBoneCount = 1; instance.m_pBoneRemap = NULL; instance.m_nIndexOffset = nIndexOffset; instance.m_nIndexCount = nBatchIndexCount; instance.m_nPrimType = MATERIAL_TRIANGLES; instance.m_pColorBuffer = NULL; instance.m_nColorVertexOffsetInBytes = 0; instance.m_pStencilState = renderData.m_pInstanceData->m_pStencilState; instance.m_pVertexBuffer = g_WorldStaticMeshes[ batch.sortID ]; instance.m_pIndexBuffer = pBuildIndexBuffer; instance.m_nVertexOffsetInBytes = 0; instance.m_DiffuseModulation.Init( 1.0f, 1.0f, 1.0f, 1.0f ); instance.m_nLightmapPageId = renderData.m_nLightmapPage; instance.m_bColorBufferHasIndirectLightingOnly = false;
nIndexOffset += nBatchIndexCount; ++nGroupCount; }
indexBuilder.End( ); pRenderContext->DrawInstances( nGroupCount, pInstance ); }
pRenderContext->SetRenderingPaint( false ); }
// Draw decals
void CBrushBatchRender::DrawDecalsForBatches( IMatRenderContext *pRenderContext, int nCount, const BrushArrayInstanceData_t *pInstanceData, brushrender_t **ppBrushRender ) { // FIXME: This could be better optimized by rendering across instances
// but that's a deeper change: we'd need to have per-instance transforms
// for each decal + shadow
for ( int i = 0; i < nCount; ++i ) { // Clear out the render list of decals
DecalSurfacesInit( true );
// Clear out the render lists of shadows
g_pShadowMgr->ClearShadowRenderList( );
const BrushArrayInstanceData_t &instance = pInstanceData[i]; const brushrender_t *pRender = ppBrushRender[i]; if ( !pRender ) continue;
SurfaceHandle_t firstSurfID = SurfaceHandleFromIndex( instance.m_pBrushModel->brush.firstmodelsurface, instance.m_pBrushModel->brush.pShared );
bool bEncounteredDecals = false; for ( int s = 0; s < pRender->surfaceCount; ++s ) { const brushrendersurface_t &surface = pRender->pSurfaces[s]; SurfaceHandle_t surfID = firstSurfID + surface.surfaceIndex;
if ( SurfaceHasDecals( surfID ) ) { bEncounteredDecals = true; DecalSurfaceAdd( surfID, BRUSHMODEL_DECAL_SORT_GROUP ); }
// Add overlay fragments to list.
// FIXME: A little code support is necessary to get overlays working on brush models
// OverlayMgr()->AddFragmentListToRenderList( MSurf_OverlayFragmentList( surfID ), false );
// Add render-to-texture shadows too....
ShadowDecalHandle_t decalHandle = MSurf_ShadowDecals( surfID ); if ( decalHandle != SHADOW_DECAL_HANDLE_INVALID ) { bEncounteredDecals = true; g_pShadowMgr->AddShadowsOnSurfaceToRenderList( decalHandle ); } }
if ( bEncounteredDecals ) { CBrushModelTransform pushTransform( *instance.m_pBrushToWorld, pRenderContext );
// Draw all shadows on the brush
g_pShadowMgr->RenderProjectedTextures( pRenderContext );
DecalSurfaceDraw( pRenderContext, BRUSHMODEL_DECAL_SORT_GROUP, instance.m_DiffuseModulation.w );
// draw the flashlight lighting for the decals on the brush.
g_pShadowMgr->DrawFlashlightDecals( pRenderContext, BRUSHMODEL_DECAL_SORT_GROUP, false, instance.m_DiffuseModulation.w );
// Retire decals on opaque brushmodel surfaces
R_DecalFlushDestroyList();
} } }
void CBrushBatchRender::DrawArrayDebugInformation( IMatRenderContext *pRenderContext, int nCount, const BrushBatchRenderData_t *pRenderData ) { if ( !g_ShaderDebug.anydebug ) return;
const Vector &vecViewOrigin = g_EngineRenderer->ViewOrigin();
for ( int r = 0; r < nCount; ++r ) { const BrushBatchRenderData_t &renderData = pRenderData[r]; const brushrenderbatch_t &batch = renderData.m_pBrushRender->pBatches[ renderData.m_nBatchIndex ]; const model_t *pModel = renderData.m_pInstanceData->m_pBrushModel; SurfaceHandle_t firstSurfID = SurfaceHandleFromIndex( pModel->brush.firstmodelsurface, pModel->brush.pShared );
Vector vecModelSpaceViewOrigin; VectorITransform( vecViewOrigin, *renderData.m_pInstanceData->m_pBrushToWorld, vecModelSpaceViewOrigin );
CUtlVectorFixedGrowable< msurface2_t *, 512 > surfaceList; for ( int k = 0; k < batch.surfaceCount; k++ ) { brushrendersurface_t &surface = renderData.m_pBrushRender->pSurfaces[batch.firstSurface + k]; SurfaceHandle_t surfID = firstSurfID + surface.surfaceIndex;
if ( MSurf_Flags(surfID) & SURFDRAW_TRANS ) continue;
if ( ( MSurf_Flags( surfID ) & SURFDRAW_NOCULL) == 0 ) { // Do a full backface cull here; we're not culling elsewhere in the pipeline
// Yes, it's expensive, but this is a debug mode, so who cares?
cplane_t *pSurfacePlane = renderData.m_pBrushRender->pPlanes[surface.planeIndex]; float flDot = DotProduct( vecModelSpaceViewOrigin, pSurfacePlane->normal ) - pSurfacePlane->dist; bool bIsBackfacing = ( flDot < BACKFACE_EPSILON ) ? true : false; if ( bIsBackfacing != false ) continue; }
surfaceList.AddToTail( surfID ); }
// now draw debug for each drawn surface
DrawDebugInformation( pRenderContext, *renderData.m_pInstanceData->m_pBrushToWorld, surfaceList.Base(), surfaceList.Count() ); } }
//-----------------------------------------------------------------------------
// Main entry point for rendering an array of brush models
//-----------------------------------------------------------------------------
void CBrushBatchRender::DrawBrushModelArray( IMatRenderContext* pRenderContext, int nCount, const BrushArrayInstanceData_t *pInstanceData ) { brushrender_t **ppBrushRender = (brushrender_t**)stackalloc( nCount * sizeof(brushrender_t*) ); CUtlVectorFixedGrowable< BrushBatchRenderData_t, 1024 > batchesToRender; BuildBatchListToDraw( nCount, pInstanceData, batchesToRender, ppBrushRender );
int nBatchCount = batchesToRender.Count(); BrushBatchRenderData_t *pBatchData = batchesToRender.Base();
ComputeLightmapPages( nBatchCount, pBatchData );
std::make_heap( pBatchData, pBatchData + nBatchCount, BatchSortLessFunc ); std::sort_heap( pBatchData, pBatchData + nBatchCount, BatchSortLessFunc );
CUtlVectorFixedGrowable< BrushInstanceGroup_t, 512 > instanceGroups; int nMaxInstanceCount = ComputeInstanceGroups( pRenderContext, nBatchCount, pBatchData, instanceGroups );
bool bHasPaintedSurfaces = DrawSortedBatchList( pRenderContext, instanceGroups.Count(), instanceGroups.Base(), nMaxInstanceCount ); if ( bHasPaintedSurfaces ) { DrawPaintForBatches( pRenderContext, instanceGroups.Count(), instanceGroups.Base(), nMaxInstanceCount ); }
DrawDecalsForBatches( pRenderContext, nCount, pInstanceData, ppBrushRender );
DrawArrayDebugInformation( pRenderContext, nBatchCount, pBatchData ); }
//-----------------------------------------------------------------------------
// Builds the lists of shadow batches to draw
//-----------------------------------------------------------------------------
void CBrushBatchRender::BuildShadowBatchListToDraw( int nCount, const BrushArrayInstanceData_t *pInstanceData, CUtlVectorFixedGrowable< BrushBatchRenderData_t, 1024 > &batchesToRender, int nModelTypeFlags ) { for ( int i = 0; i < nCount; ++i ) { const BrushArrayInstanceData_t &instance = pInstanceData[i]; brushrender_t *pRender = g_BrushBatchRenderer.FindOrCreateRenderBatch( const_cast< model_t* >( instance.m_pBrushModel ) ); if ( !pRender ) continue;
for ( int m = 0; m < pRender->meshCount; ++m ) { brushrendermesh_t &mesh = pRender->pMeshes[m]; int nBatchIndex = mesh.firstBatch; for ( int j = 0; j < mesh.batchCount; ++j, ++nBatchIndex ) { // Select proper override material
const brushrenderbatch_t &renderBatch = pRender->pBatches[ nBatchIndex ]; int nAlphaTest = (int)renderBatch.pMaterial->IsAlphaTested(); int nNoCull = (int)renderBatch.pMaterial->IsTwoSided();
IMaterial *pDepthWriteMaterial;
if ( nModelTypeFlags & STUDIO_SSAODEPTHTEXTURE ) { pDepthWriteMaterial = g_pMaterialSSAODepthWrite[ nAlphaTest ][ nNoCull ]; } else { pDepthWriteMaterial = g_pMaterialDepthWrite[ nAlphaTest ][ nNoCull ]; }
int nIndex = batchesToRender.AddToTail(); BrushBatchRenderData_t &batch = batchesToRender[nIndex]; batch.m_pInstanceData = &instance; batch.m_pBrushRender = pRender; batch.m_nBatchIndex = nBatchIndex; batch.m_nIsAlphaTested = nAlphaTest; batch.m_pMaterial = pDepthWriteMaterial; } } } }
//-----------------------------------------------------------------------------
// Sorts shadows
//-----------------------------------------------------------------------------
inline bool __cdecl CBrushBatchRender::ShadowSortLessFunc( const BrushBatchRenderData_t &left, const BrushBatchRenderData_t &right ) { if ( left.m_pMaterial != right.m_pMaterial ) return left.m_pMaterial < right.m_pMaterial; if ( left.m_pInstanceData->m_pBrushToWorld != right.m_pInstanceData->m_pBrushToWorld ) return left.m_pInstanceData->m_pBrushToWorld < right.m_pInstanceData->m_pBrushToWorld; return false; }
//-----------------------------------------------------------------------------
// Draws an opaque (parts of a) brush model
//-----------------------------------------------------------------------------
void CBrushBatchRender::DrawShadowBatchList( IMatRenderContext* pRenderContext, int nCount, BrushInstanceGroup_t *pInstanceGroup, int nMaxInstanceCount ) { VPROF( "DrawShadowBatchList" ); PIXEVENT( pRenderContext, "DrawShadowBatchList()" );
pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP );
MeshInstanceData_t *pInstance = (MeshInstanceData_t*)stackalloc( nMaxInstanceCount * sizeof(MeshInstanceData_t) );
for ( int i = 0; i < nCount; i++ ) { BrushInstanceGroup_t &group = pInstanceGroup[i];
if ( group.m_pRenderData->m_nIsAlphaTested ) { static unsigned int originalTextureVarCache = 0; IMaterialVar *pOriginalTextureVar = group.m_pActualMaterial->FindVarFast( "$basetexture", &originalTextureVarCache ); static unsigned int originalTextureFrameVarCache = 0; IMaterialVar *pOriginalTextureFrameVar = group.m_pActualMaterial->FindVarFast( "$frame", &originalTextureFrameVarCache ); static unsigned int originalAlphaRefCache = 0; IMaterialVar *pOriginalAlphaRefVar = group.m_pActualMaterial->FindVarFast( "$AlphaTestReference", &originalAlphaRefCache );
static unsigned int textureVarCache = 0; IMaterialVar *pTextureVar = group.m_pMaterial->FindVarFast( "$basetexture", &textureVarCache ); static unsigned int textureFrameVarCache = 0; IMaterialVar *pTextureFrameVar = group.m_pMaterial->FindVarFast( "$frame", &textureFrameVarCache ); static unsigned int alphaRefCache = 0; IMaterialVar *pAlphaRefVar = group.m_pMaterial->FindVarFast( "$AlphaTestReference", &alphaRefCache );
if( pTextureVar && pOriginalTextureVar ) { pTextureVar->SetTextureValue( pOriginalTextureVar->GetTextureValue() ); }
if( pTextureFrameVar && pOriginalTextureFrameVar ) { pTextureFrameVar->SetIntValue( pOriginalTextureFrameVar->GetIntValue() ); }
if( pAlphaRefVar && pOriginalAlphaRefVar ) { pAlphaRefVar->SetFloatValue( pOriginalAlphaRefVar->GetFloatValue() ); } }
pRenderContext->Bind( group.m_pMaterial, NULL );
// Only writing indices
// FIXME: Can we make this a static index buffer?
IIndexBuffer *pBuildIndexBuffer = pRenderContext->GetDynamicIndexBuffer(); CIndexBuilder indexBuilder( pBuildIndexBuffer, MATERIAL_INDEX_FORMAT_16BIT ); indexBuilder.Lock( group.m_nIndexCount, 0 ); int nIndexOffset = indexBuilder.Offset() / sizeof(uint16);
for ( int j = 0; j < group.m_nCount; ++j ) { BrushBatchRenderData_t &renderData = group.m_pRenderData[j]; const brushrenderbatch_t &batch = renderData.m_pBrushRender->pBatches[ renderData.m_nBatchIndex ]; const model_t *pModel = renderData.m_pInstanceData->m_pBrushModel; SurfaceHandle_t firstSurfID = SurfaceHandleFromIndex( pModel->brush.firstmodelsurface, pModel->brush.pShared ); for ( int k = 0; k < batch.surfaceCount; k++ ) { const brushrendersurface_t &surface = renderData.m_pBrushRender->pSurfaces[batch.firstSurface + k]; SurfaceHandle_t surfID = firstSurfID + surface.surfaceIndex;
Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) ); BuildIndicesForSurface( indexBuilder, surfID ); }
MeshInstanceData_t &instance = pInstance[ j ]; instance.m_pEnvCubemap = NULL; instance.m_pPoseToWorld = renderData.m_pInstanceData->m_pBrushToWorld; instance.m_pLightingState = NULL; instance.m_nBoneCount = 1; instance.m_pBoneRemap = NULL; instance.m_nIndexOffset = nIndexOffset; instance.m_nIndexCount = batch.indexCount; instance.m_nPrimType = MATERIAL_TRIANGLES; instance.m_pColorBuffer = NULL; instance.m_nColorVertexOffsetInBytes = 0; instance.m_pStencilState = renderData.m_pInstanceData->m_pStencilState; instance.m_pVertexBuffer = g_WorldStaticMeshes[ batch.sortID ]; instance.m_pIndexBuffer = pBuildIndexBuffer; instance.m_nVertexOffsetInBytes = 0; instance.m_DiffuseModulation.Init( 1.0f, 1.0f, 1.0f, 1.0f ); instance.m_nLightmapPageId = MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP; instance.m_bColorBufferHasIndirectLightingOnly = false;
nIndexOffset += batch.indexCount; }
indexBuilder.End( ); pRenderContext->DrawInstances( group.m_nCount, pInstance ); } }
//-----------------------------------------------------------------------------
// Main entry point for rendering an array of brush model shadows
//-----------------------------------------------------------------------------
void CBrushBatchRender::DrawBrushModelShadowArray( IMatRenderContext* pRenderContext, int nCount, const BrushArrayInstanceData_t *pInstanceData, int nModelTypeFlags ) { CUtlVectorFixedGrowable< BrushBatchRenderData_t, 1024 > batchesToRender; BuildShadowBatchListToDraw( nCount, pInstanceData, batchesToRender, nModelTypeFlags );
int nBatchCount = batchesToRender.Count(); BrushBatchRenderData_t *pBatchData = batchesToRender.Base();
std::make_heap( pBatchData, pBatchData + nBatchCount, ShadowSortLessFunc ); std::sort_heap( pBatchData, pBatchData + nBatchCount, ShadowSortLessFunc );
CUtlVectorFixedGrowable< BrushInstanceGroup_t, 512 > instanceGroups; int nMaxInstanceCount = ComputeInstanceGroups( pRenderContext, nBatchCount, pBatchData, instanceGroups );
DrawShadowBatchList( pRenderContext, instanceGroups.Count(), instanceGroups.Base(), nMaxInstanceCount ); }
|