csgo-2018-source/studiorender/studiorender.cpp
2021-07-24 21:11:47 -07:00

2711 lines
100 KiB
C++

//===== Copyright (c) 1996-2007, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include <stdlib.h>
#include <algorithm>
#include "studiorender.h"
#include "studiorendercontext.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/imesh.h"
#include "optimize.h"
#include "mathlib/vmatrix.h"
#include "tier0/vprof.h"
#include "tier1/strtools.h"
#include "tier1/keyvalues.h"
#include "tier0/memalloc.h"
#include "convar.h"
#include "materialsystem/itexture.h"
#include "tier2/tier2.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
CStudioRender g_StudioRender;
CStudioRender *g_pStudioRenderImp = &g_StudioRender;
//-----------------------------------------------------------------------------
// Activate to get stats
//-----------------------------------------------------------------------------
//#define REPORT_FLEX_STATS 1
#ifdef REPORT_FLEX_STATS
static int s_nModelsDrawn = 0;
static int s_nActiveFlexCount = 0;
static ConVar r_flexstats( "r_flexstats", "0", FCVAR_CHEAT );
#endif
// Multiplicative factor on LOD switch points. See GetLODForMetric() in studio.h
static ConVar r_lod_switch_scale( "r_lod_switch_scale", "1", FCVAR_HIDDEN );
#ifndef _CERT
static ConVar mat_rendered_faces_count( "mat_rendered_faces_count", "0", FCVAR_CHEAT, "Set to N to count how many faces each model draws each frame and spew the top N offenders from the last 150 frames (use 'mat_rendered_faces_spew' to spew all models rendered in the current frame)" );
static ConVar mat_print_top_model_vert_counts( "mat_print_top_model_vert_counts", "0", 0, "Constantly print to screen the top N models as measured by total faces rendered this frame");
bool ModelFaceCountHashCompareFunc( studiohwdata_t *const &a, studiohwdata_t *const &b ) { return a == b; }
uint32 ModelFaceCountHashKeyFunc( studiohwdata_t *const &a ) { return HashIntConventional( (int32)(intp)a ); }
#endif // !_CERT
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CStudioRender::CStudioRender()
#ifndef _CERT
: m_ModelFaceCountHash( 1024, 0, 0, ModelFaceCountHashCompareFunc, ModelFaceCountHashKeyFunc )
#endif
{
m_pRC = NULL;
m_pBoneToWorld = NULL;
m_pFlexWeights = NULL;
m_pFlexDelayedWeights = NULL;
m_pStudioHdr = NULL;
m_pStudioMeshes = NULL;
m_pSubModel = NULL;
m_pStudioHWData = NULL;
m_pGlintTexture = NULL;
m_GlintWidth = 0;
m_GlintHeight = 0;
m_pCurrentFlashlight = 0;
// Cache-align our important matrices
g_pMemAlloc->PushAllocDbgInfo( __FILE__, __LINE__ );
m_PoseToWorld = (matrix3x4_t*)MemAlloc_AllocAligned( MAXSTUDIOBONES * sizeof(matrix3x4_t), 32 );
m_PoseToDecal = (matrix3x4_t*)MemAlloc_AllocAligned( MAXSTUDIOBONES * sizeof(matrix3x4_t), 32 );
g_pMemAlloc->PopAllocDbgInfo();
m_nDecalId = 1;
}
CStudioRender::~CStudioRender()
{
MemAlloc_FreeAligned(m_PoseToWorld);
MemAlloc_FreeAligned(m_PoseToDecal);
}
void CStudioRender::InitDebugMaterials( void )
{
// Four Wireframe Materials: ( ZBuffer, DisplacementMapped )
m_pMaterialWireframe[0][0] = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmrmwireframe", TEXTURE_GROUP_OTHER, true );
m_pMaterialWireframe[0][0]->IncrementReferenceCount();
m_pMaterialWireframe[1][0] = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmrmwireframezbuffer", TEXTURE_GROUP_OTHER, true );
m_pMaterialWireframe[1][0]->IncrementReferenceCount();
m_pMaterialWireframe[0][1] = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmrmwireframedisplaced", TEXTURE_GROUP_OTHER, true );
m_pMaterialWireframe[0][1]->IncrementReferenceCount();
m_pMaterialWireframe[1][1] = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmrmwireframezbufferdisplaced", TEXTURE_GROUP_OTHER, true );
m_pMaterialWireframe[1][1]->IncrementReferenceCount();
m_pMaterialMRMNormals = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmrmnormals", TEXTURE_GROUP_OTHER, true );
m_pMaterialMRMNormals->IncrementReferenceCount();
m_pMaterialTangentFrame = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugvertexcolor", TEXTURE_GROUP_OTHER, true );
m_pMaterialTangentFrame->IncrementReferenceCount();
m_pMaterialTranslucentModelHulls = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugtranslucentmodelhulls", TEXTURE_GROUP_OTHER, true );
m_pMaterialTranslucentModelHulls->IncrementReferenceCount();
m_pMaterialSolidModelHulls = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugsolidmodelhulls", TEXTURE_GROUP_OTHER, true );
m_pMaterialSolidModelHulls->IncrementReferenceCount();
m_pMaterialAdditiveVertexColorVertexAlpha = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/additivevertexcolorvertexalpha", TEXTURE_GROUP_OTHER, true );
m_pMaterialAdditiveVertexColorVertexAlpha->IncrementReferenceCount();
m_pMaterialModelBones = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmodelbones", TEXTURE_GROUP_OTHER, true );
m_pMaterialModelBones->IncrementReferenceCount();
m_pMaterialModelEnvCubemap = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/env_cubemap_model", TEXTURE_GROUP_OTHER, true );
m_pMaterialModelEnvCubemap->IncrementReferenceCount();
m_pMaterialWorldWireframe = g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugworldwireframe", TEXTURE_GROUP_OTHER, true );
m_pMaterialWorldWireframe->IncrementReferenceCount();
KeyValues *pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 0 );
pVMTKeyValues->SetInt( "$treesway", 0 );
pVMTKeyValues->SetInt("$color_depth", 0);
m_pDepthWrite[0][0][0] = g_pMaterialSystem->FindProceduralMaterial("__DepthWrite000", TEXTURE_GROUP_OTHER, pVMTKeyValues);
m_pDepthWrite[0][0][0]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$treesway", 0 );
pVMTKeyValues->SetInt("$color_depth", 0);
m_pDepthWrite[0][1][0] = g_pMaterialSystem->FindProceduralMaterial("__DepthWrite010", TEXTURE_GROUP_OTHER, pVMTKeyValues);
m_pDepthWrite[0][1][0]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 0 );
pVMTKeyValues->SetInt( "$treesway", 0 );
pVMTKeyValues->SetInt("$color_depth", 0);
m_pDepthWrite[1][0][0] = g_pMaterialSystem->FindProceduralMaterial("__DepthWrite100", TEXTURE_GROUP_OTHER, pVMTKeyValues);
m_pDepthWrite[1][0][0]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$treesway", 0 );
pVMTKeyValues->SetInt("$color_depth", 0);
m_pDepthWrite[1][1][0] = g_pMaterialSystem->FindProceduralMaterial("__DepthWrite110", TEXTURE_GROUP_OTHER, pVMTKeyValues);
m_pDepthWrite[1][1][0]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 0 );
pVMTKeyValues->SetInt( "$treesway", 1 );
pVMTKeyValues->SetInt("$color_depth", 0);
m_pDepthWrite[0][0][1] = g_pMaterialSystem->FindProceduralMaterial("__DepthWrite001", TEXTURE_GROUP_OTHER, pVMTKeyValues);
m_pDepthWrite[0][0][1]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$treesway", 1 );
pVMTKeyValues->SetInt("$color_depth", 0);
m_pDepthWrite[0][1][1] = g_pMaterialSystem->FindProceduralMaterial("__DepthWrite011", TEXTURE_GROUP_OTHER, pVMTKeyValues);
m_pDepthWrite[0][1][1]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 0 );
pVMTKeyValues->SetInt( "$treesway", 1 );
pVMTKeyValues->SetInt("$color_depth", 0);
m_pDepthWrite[1][0][1] = g_pMaterialSystem->FindProceduralMaterial("__DepthWrite101", TEXTURE_GROUP_OTHER, pVMTKeyValues);
m_pDepthWrite[1][0][1]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$treesway", 1 );
pVMTKeyValues->SetInt("$color_depth", 0);
m_pDepthWrite[1][1][1] = g_pMaterialSystem->FindProceduralMaterial("__DepthWrite111", TEXTURE_GROUP_OTHER, pVMTKeyValues);
m_pDepthWrite[1][1][1]->IncrementReferenceCount();
// Full frame depth as color (r32f)
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 0 );
pVMTKeyValues->SetInt( "$treesway", 0 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[ 0 ][ 0 ][ 0 ] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite000", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[ 0 ][ 0 ][ 0 ]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$treesway", 0 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[ 0 ][ 1 ][ 0 ] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite010", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[ 0 ][ 1 ][ 0 ]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 0 );
pVMTKeyValues->SetInt( "$treesway", 0 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[ 1 ][ 0 ][ 0 ] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite100", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[ 1 ][ 0 ][ 0 ]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$treesway", 0 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[ 1 ][ 1 ][ 0 ] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite110", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[ 1 ][ 1 ][ 0 ]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 0 );
pVMTKeyValues->SetInt( "$treesway", 1 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[ 0 ][ 0 ][ 1 ] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite001", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[ 0 ][ 0 ][ 1 ]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$treesway", 1 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[ 0 ][ 1 ][ 1 ] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite011", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[ 0 ][ 1 ][ 1 ]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 0 );
pVMTKeyValues->SetInt( "$treesway", 1 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[ 1 ][ 0 ][ 1 ] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite101", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[ 1 ][ 0 ][ 1 ]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$treesway", 1 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[ 1 ][ 1 ][ 1 ] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite111", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[ 1 ][ 1 ][ 1 ]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "EyeGlint" );
m_pGlintBuildMaterial = g_pMaterialSystem->CreateMaterial( "___glintbuildmaterial", pVMTKeyValues );
pVMTKeyValues = new KeyValues( "unlitgeneric" );
pVMTKeyValues->SetInt( "$color", 0 );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$writez", 0 );
m_pMaterialSolidBackfacePrepass = g_pMaterialSystem->FindProceduralMaterial( "__utilBackfacePrepass", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pMaterialSolidBackfacePrepass->IncrementReferenceCount();
}
void CStudioRender::ShutdownDebugMaterials( void )
{
for ( int i=0; i<2; i++ )
{
for ( int j=0; j<2; j++ )
{
if ( m_pMaterialWireframe[i][j] )
{
m_pMaterialWireframe[i][j]->DecrementReferenceCount();
m_pMaterialWireframe[i][j] = NULL;
}
}
}
if ( m_pMaterialMRMNormals )
{
m_pMaterialMRMNormals->DecrementReferenceCount();
m_pMaterialMRMNormals = NULL;
}
if ( m_pMaterialTangentFrame )
{
m_pMaterialTangentFrame->DecrementReferenceCount();
m_pMaterialTangentFrame = NULL;
}
if ( m_pMaterialTranslucentModelHulls )
{
m_pMaterialTranslucentModelHulls->DecrementReferenceCount();
m_pMaterialTranslucentModelHulls = NULL;
}
if ( m_pMaterialSolidModelHulls )
{
m_pMaterialSolidModelHulls->DecrementReferenceCount();
m_pMaterialSolidModelHulls = NULL;
}
if ( m_pMaterialAdditiveVertexColorVertexAlpha )
{
m_pMaterialAdditiveVertexColorVertexAlpha->DecrementReferenceCount();
m_pMaterialAdditiveVertexColorVertexAlpha = NULL;
}
if ( m_pMaterialModelBones )
{
m_pMaterialModelBones->DecrementReferenceCount();
m_pMaterialModelBones = NULL;
}
if ( m_pMaterialModelEnvCubemap )
{
m_pMaterialModelEnvCubemap->DecrementReferenceCount();
m_pMaterialModelEnvCubemap = NULL;
}
if ( m_pMaterialWorldWireframe )
{
m_pMaterialWorldWireframe->DecrementReferenceCount();
m_pMaterialWorldWireframe = NULL;
}
// DepthWrite materials
for ( int32 i = 0; i < 8; i++ )
{
if ( m_pDepthWrite[ ( i & 0x4 ) >> 2 ][ ( i & 0x2 ) >> 1 ][ i & 0x1 ] )
{
m_pDepthWrite[ ( i & 0x4 ) >> 2 ][ ( i & 0x2 ) >> 1 ][ i & 0x1 ]->DecrementReferenceCount();
}
if ( m_pSSAODepthWrite[ ( i & 0x4 ) >> 2 ][ ( i & 0x2 ) >> 1 ][ i & 0x1 ] )
{
m_pSSAODepthWrite[ ( i & 0x4 ) >> 2 ][ ( i & 0x2 ) >> 1 ][ i & 0x1 ]->DecrementReferenceCount();
}
}
if ( m_pGlintBuildMaterial )
{
m_pGlintBuildMaterial->DecrementReferenceCount();
m_pGlintBuildMaterial = NULL;
}
if ( m_pMaterialSolidBackfacePrepass )
{
m_pMaterialSolidBackfacePrepass->DecrementReferenceCount();
m_pMaterialSolidBackfacePrepass = NULL;
}
}
static void ReleaseMaterialSystemObjects( int nChangeFlags )
{
// g_StudioRender.UncacheGlint();
}
static void RestoreMaterialSystemObjects( int nChangeFlags )
{
// g_StudioRender.PrecacheGlint();
}
//-----------------------------------------------------------------------------
// Init, shutdown
//-----------------------------------------------------------------------------
InitReturnVal_t CStudioRender::Init()
{
if ( g_pMaterialSystem && g_pMaterialSystemHardwareConfig )
{
g_pMaterialSystem->AddReleaseFunc( ReleaseMaterialSystemObjects );
g_pMaterialSystem->AddRestoreFunc( RestoreMaterialSystemObjects );
InitDebugMaterials();
return INIT_OK;
}
return INIT_FAILED;
}
void CStudioRender::Shutdown( void )
{
UncacheGlint();
ShutdownDebugMaterials();
if ( g_pMaterialSystem )
{
g_pMaterialSystem->RemoveReleaseFunc( ReleaseMaterialSystemObjects );
g_pMaterialSystem->RemoveRestoreFunc( RestoreMaterialSystemObjects );
}
}
//-----------------------------------------------------------------------------
// Begin/End frame methods
//-----------------------------------------------------------------------------
void CStudioRender::BeginFrame( void )
{
#ifndef _CERT
// Clear the model face count hash table for the coming frame
if ( mat_rendered_faces_count.GetBool() || mat_print_top_model_vert_counts.GetBool() )
m_ModelFaceCountHash.RemoveAll();
#endif // !_CERT
PrecacheGlint();
}
void CStudioRender::EndFrame( void )
{
#ifndef _CERT
UpdateModelFaceCounts();
#endif // !_CERT
CleanupDecals();
}
//-----------------------------------------------------------------------------
// Sets the lighting render state
//-----------------------------------------------------------------------------
void CStudioRender::SetLightingRenderState()
{
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
// FIXME: What happens when we use the fixed function pipeline but vertex shaders
// are active? For the time being this only works because everything that does
// vertex lighting does, in fact, have a vertex shader which is used to render it.
pRenderContext->SetAmbientLightCube( m_pRC->m_LightBoxColors );
if ( m_pRC->m_Config.bSoftwareLighting )
{
pRenderContext->DisableAllLocalLights();
}
else
{
pRenderContext->SetLights( m_pRC->m_NumLocalLights, m_pRC->m_LocalLights );
}
}
//-----------------------------------------------------------------------------
// Shadow state (affects the models as they are rendered)
//-----------------------------------------------------------------------------
void CStudioRender::AddShadow( IMaterial* pMaterial, void* pProxyData, FlashlightState_t *pFlashlightState, VMatrix *pWorldToTexture, ITexture *pFlashlightDepthTexture )
{
int i = m_ShadowState.AddToTail();
ShadowState_t& state = m_ShadowState[i];
state.m_pMaterial = pMaterial;
state.m_pProxyData = pProxyData;
state.m_pFlashlightState = pFlashlightState;
state.m_pWorldToTexture = pWorldToTexture;
state.m_pFlashlightDepthTexture = pFlashlightDepthTexture;
}
void CStudioRender::ClearAllShadows()
{
m_ShadowState.RemoveAll();
}
void CStudioRender::GetFlexStats( )
{
#ifdef REPORT_FLEX_STATS
static bool s_bLastFlexStats = false;
bool bDoStats = r_flexstats.GetInt() != 0;
if ( bDoStats )
{
if ( !s_bLastFlexStats )
{
s_nModelsDrawn = 0;
s_nActiveFlexCount = 0;
}
// Count number of active weights
int nActiveFlexCount = 0;
for ( int i = 0; i < MAXSTUDIOFLEXDESC; ++i )
{
if ( fabs( m_FlexWeights[i] ) >= 0.001f || fabs( m_FlexDelayedWeights[i] ) >= 0.001f )
{
++nActiveFlexCount;
}
}
++s_nModelsDrawn;
s_nActiveFlexCount += nActiveFlexCount;
}
else
{
if ( s_bLastFlexStats )
{
if ( s_nModelsDrawn )
{
Msg( "Average number of flexes/model: %d\n", s_nActiveFlexCount / s_nModelsDrawn );
}
else
{
Msg( "No models rendered to take stats of\n" );
}
s_nModelsDrawn = 0;
s_nActiveFlexCount = 0;
}
}
s_bLastFlexStats = bDoStats;
#endif
}
ConVar cl_skipslowpath( "cl_skipslowpath", "0", FCVAR_CHEAT | FCVAR_MATERIAL_SYSTEM_THREAD, "Set to 1 to skip any models that don't go through the model fast path" );
//-----------------------------------------------------------------------------
// Main model rendering entry point
//-----------------------------------------------------------------------------
void CStudioRender::DrawModel( const DrawModelInfo_t& info, const StudioRenderContext_t &rc,
matrix3x4_t *pBoneToWorld, const FlexWeights_t &flex, int flags )
{
if ( cl_skipslowpath.GetBool () )
return;
VPROF( "CStudioRender::DrawModel");
if ( ( flags & STUDIORENDER_MODEL_IS_CACHEABLE ) && !g_pMDLCache->IsDataLoaded( VoidPtrToMDLHandle( info.m_pStudioHdr->VirtualModel() ), MDLCACHE_STUDIOHWDATA ) )
{
// cacheable models may have had their hw data evicted while they were queued for rendering
return;
}
m_pRC = const_cast< StudioRenderContext_t* >( &rc );
m_pFlexWeights = flex.m_pFlexWeights;
m_pFlexDelayedWeights = flex.m_pFlexDelayedWeights;
m_pBoneToWorld = pBoneToWorld;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
// Disable flex if we're told to...
bool flexConfig = m_pRC->m_Config.bFlex;
if (flags & STUDIORENDER_DRAW_NO_FLEXES)
{
m_pRC->m_Config.bFlex = false;
}
// Enable wireframe if we're told to...
bool bWireframe = m_pRC->m_Config.bWireframe;
if ( flags & STUDIORENDER_DRAW_WIREFRAME )
{
m_pRC->m_Config.bWireframe = true;
}
int boneMask = BONE_USED_BY_VERTEX_AT_LOD( info.m_Lod );
// Preserve the matrices if we're skinning
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
m_VertexCache.StartModel();
m_pStudioHdr = info.m_pStudioHdr;
m_pStudioMeshes = info.m_pHardwareData->m_pLODs[info.m_Lod].m_pMeshData;
m_pStudioHWData = info.m_pHardwareData;
#if PIX_ENABLE
char szPIXEventName[128];
sprintf( szPIXEventName, "%s*", m_pStudioHdr->name ); // PIX
PIXEVENT( pRenderContext, szPIXEventName );
#endif
// Bone to world must be set before calling drawmodel; it uses that here
ComputePoseToWorld( m_PoseToWorld, m_pStudioHdr, boneMask, m_pRC->m_ViewOrigin, pBoneToWorld );
bool bOldFlashlightState = false;
if ( pRenderContext->IsCullingEnabledForSinglePassFlashlight() && IsGameConsole() )
{
bOldFlashlightState = pRenderContext->GetFlashlightMode();
pRenderContext->SetFlashlightMode( m_ShadowState.Count() > 0 );
}
if ( ( flags & STUDIORENDER_NO_PRIMARY_DRAW ) == 0 ) // if this flag is set, then we are drawing multiple shadows in separate calls ( probably for capture )
{
R_StudioRenderModel( pRenderContext, info.m_Skin, info.m_Body, info.m_HitboxSet, info.m_pClientEntity,
info.m_pHardwareData->m_pLODs[info.m_Lod].ppMaterials,
info.m_pHardwareData->m_pLODs[info.m_Lod].pMaterialFlags, flags, boneMask, info.m_Lod, info.m_pColorMeshes);
}
if ( pRenderContext->IsCullingEnabledForSinglePassFlashlight() && IsGameConsole() )
{
pRenderContext->SetFlashlightMode( bOldFlashlightState );
}
// Draw all the decals on this model
// If the model is not in memory, this code may not function correctly
// This code assumes the model has been rendered!
// So skip if the model hasn't been rendered
// Also, skip if we're rendering to the shadow depth map
if ( ( m_pStudioMeshes != 0 ) && !( flags & ( STUDIORENDER_SHADOWDEPTHTEXTURE | STUDIORENDER_SSAODEPTHTEXTURE ) ) )
{
// Draw shadows
if ( !( flags & STUDIORENDER_DRAW_NO_SHADOWS ) )
{
DrawShadows( info, flags, boneMask );
}
if ( ( ( flags & STUDIORENDER_DRAW_GROUP_MASK ) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY ) && !( flags & STUDIORENDER_SKIP_DECALS ) )
{
DrawDecal( info, info.m_Lod, info.m_Body );
}
if( (flags & STUDIORENDER_DRAW_GROUP_MASK) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY &&
!( flags & STUDIORENDER_DRAW_NO_SHADOWS ) && !( flags & STUDIORENDER_SKIP_DECALS ) )
{
DrawFlashlightDecals( info, info.m_Lod );
}
}
// Restore the matrices if we're skinning
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
// Restore the configs
m_pRC->m_Config.bFlex = flexConfig;
m_pRC->m_Config.bWireframe = bWireframe;
#ifdef REPORT_FLEX_STATS
GetFlexStats();
#endif
pRenderContext->SetNumBoneWeights( 0 );
m_pRC = NULL;
m_pBoneToWorld = NULL;
m_pFlexWeights = NULL;
m_pFlexDelayedWeights = NULL;
m_pStudioHdr = NULL;
m_pStudioMeshes = NULL;
m_pStudioHWData = NULL;
}
void CStudioRender::DrawModelStaticProp( const DrawModelInfo_t& info,
const StudioRenderContext_t &rc, const matrix3x4_t& rootToWorld, int flags )
{
if ( cl_skipslowpath.GetBool () )
return;
VPROF( "CStudioRender::DrawModelStaticProp");
m_pRC = const_cast<StudioRenderContext_t*>( &rc );
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
memcpy( &m_StaticPropRootToWorld, &rootToWorld, sizeof(matrix3x4_t) );
memcpy( &m_PoseToWorld[0], &rootToWorld, sizeof(matrix3x4_t) );
m_pBoneToWorld = &m_StaticPropRootToWorld;
bool flexConfig = m_pRC->m_Config.bFlex;
m_pRC->m_Config.bFlex = false;
bool bWireframe = m_pRC->m_Config.bWireframe;
if ( flags & STUDIORENDER_DRAW_WIREFRAME )
{
m_pRC->m_Config.bWireframe = true;
}
int lod = info.m_Lod;
m_pStudioHdr = info.m_pStudioHdr;
m_pStudioMeshes = info.m_pHardwareData->m_pLODs[lod].m_pMeshData;
m_pStudioHWData = info.m_pHardwareData;
R_StudioRenderModel( pRenderContext, info.m_Skin, info.m_Body, info.m_HitboxSet, info.m_pClientEntity,
info.m_pHardwareData->m_pLODs[lod].ppMaterials,
info.m_pHardwareData->m_pLODs[lod].pMaterialFlags, flags, BONE_USED_BY_ANYTHING, lod, info.m_pColorMeshes);
// If we're not shadow depth mapping
if ( ( flags & ( STUDIORENDER_SHADOWDEPTHTEXTURE | STUDIORENDER_SSAODEPTHTEXTURE ) ) == 0 )
{
// Draw shadows
if ( !( flags & STUDIORENDER_DRAW_NO_SHADOWS ) )
{
DrawShadows( info, flags, BONE_USED_BY_ANYTHING );
}
// FIXME: Should this occur in a separate call?
// Draw all the decals on this model
if ( ( ( flags & STUDIORENDER_DRAW_GROUP_MASK ) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY ) && !( flags & STUDIORENDER_SKIP_DECALS ) )
{
DrawDecal( info, lod, info.m_Body );
}
if( (flags & STUDIORENDER_DRAW_GROUP_MASK) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY &&
!( flags & STUDIORENDER_DRAW_NO_SHADOWS ) && !( flags & STUDIORENDER_SKIP_DECALS ) )
{
DrawFlashlightDecals( info, lod );
}
}
// Restore the configs
m_pRC->m_Config.bFlex = flexConfig;
m_pRC->m_Config.bWireframe = bWireframe;
pRenderContext->SetNumBoneWeights( 0 );
m_pBoneToWorld = NULL;
m_pRC = NULL;
m_pStudioHdr = NULL;
m_pStudioMeshes = NULL;
m_pStudioHWData = NULL;
}
//-----------------------------------------------------------------------------
// Used to render instances
//-----------------------------------------------------------------------------
struct BaseMeshRenderData_t
{
studiomeshgroup_t *m_pGroup;
mstudiomesh_t *m_pMesh;
IMaterial *m_pMaterial;
};
struct ShadowMeshRenderData_t : public BaseMeshRenderData_t
{
StudioShadowArrayInstanceData_t *m_pInstance;
IMaterial *m_pSrcMaterial;
VertexCompressionType_t m_nCompressionType;
int m_nMeshBoneCount;
bool m_bIsAlphaTested;
bool m_bUsesTreeSway;
};
struct MeshRenderData_t : public BaseMeshRenderData_t
{
StudioArrayInstanceData_t *m_pInstance;
};
struct MeshRenderData2_t : public BaseMeshRenderData_t
{
StudioArrayInstanceData_t *m_pInstance;
int16 m_nCompressionType; // VertexCompressionType_t smooshed to 16 bits
int16 m_nMeshBoneCount;
};
//-----------------------------------------------------------------------------
// Counts the number of meshes to draw
//-----------------------------------------------------------------------------
int CStudioRender::CountMeshesToDraw( const StudioModelArrayInfo_t &drawInfo, int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride, int nTimesRendered )
{
VPROF( "CStudioRender::CountMeshesToDraw" );
#ifndef _CERT
bool bCountRenderedFaces = mat_rendered_faces_count.GetBool() || mat_print_top_model_vert_counts.GetBool();
#endif // !_CERT
StudioArrayInstanceData_t *pCurInstance = pInstanceData;
int nTotalMeshCount = 0;
for ( int i = 0; i < nCount; ++i, pCurInstance = (StudioArrayInstanceData_t*)( (char*)pCurInstance + nInstanceStride ) )
{
// This subarray has the same skin + body + lod
int nLod = pCurInstance->m_nLOD;
// get the studio mesh data for this lod
studiomeshdata_t *pMeshDataBase = drawInfo.m_pHardwareData->m_pLODs[nLod].m_pMeshData;
#ifndef _CERT
// Each model counts how many rendered faces it accounts for each frame:
if ( bCountRenderedFaces )
drawInfo.m_pHardwareData->UpdateFacesRenderedCount( drawInfo.m_pStudioHdr, m_ModelFaceCountHash, nLod, nTimesRendered );
#endif // !_CERT
int nBody = pCurInstance->m_nBody;
for ( int body = 0; body < drawInfo.m_pStudioHdr->numbodyparts; ++body )
{
mstudiobodyparts_t *pbodypart = drawInfo.m_pStudioHdr->pBodypart( body );
int index = nBody / pbodypart->base;
index = index % pbodypart->nummodels;
mstudiomodel_t *pSubmodel = pbodypart->pModel( index );
for ( int meshIndex = 0; meshIndex < pSubmodel->nummeshes; ++meshIndex )
{
mstudiomesh_t *pMesh = pSubmodel->pMesh(meshIndex);
studiomeshdata_t *pMeshData = &pMeshDataBase[pMesh->meshid];
nTotalMeshCount += pMeshData->m_NumGroup;
}
}
}
return nTotalMeshCount;
}
//-----------------------------------------------------------------------------
// Counts the number of meshes to draw
//-----------------------------------------------------------------------------
int CStudioRender::CountMeshesToDraw( const StudioModelArrayInfo2_t &drawInfo, int nCount, StudioArrayData_t *pArrayData, int nInstanceStride, int nTimesRendered )
{
VPROF( "CStudioRender::CountMeshesToDraw" );
#ifndef _CERT
bool bCountRenderedFaces = mat_rendered_faces_count.GetBool() || mat_print_top_model_vert_counts.GetBool();
#endif // !_CERT
int nTotalMeshCount = 0;
for ( int i = 0; i < nCount; ++i )
{
StudioArrayData_t &arrayData = pArrayData[i];
StudioArrayInstanceData_t *pCurInstance = (StudioArrayInstanceData_t*)( arrayData.m_pInstanceData );
for ( int j = 0; j < arrayData.m_nCount; ++j, pCurInstance = (StudioArrayInstanceData_t*)( (char*)pCurInstance + nInstanceStride ) )
{
// This subarray has the same skin + body + lod
int nLod = pCurInstance->m_nLOD;
// get the studio mesh data for this lod
studiomeshdata_t *pMeshDataBase = arrayData.m_pHardwareData->m_pLODs[nLod].m_pMeshData;
#ifndef _CERT
// Each model counts how many rendered faces it accounts for each frame:
if ( bCountRenderedFaces )
arrayData.m_pHardwareData->UpdateFacesRenderedCount( arrayData.m_pStudioHdr, m_ModelFaceCountHash, nLod, nTimesRendered );
#endif // !_CERT
int nBody = pCurInstance->m_nBody;
for ( int body = 0; body < arrayData.m_pStudioHdr->numbodyparts; ++body )
{
mstudiobodyparts_t *pbodypart = arrayData.m_pStudioHdr->pBodypart( body );
int index = nBody / pbodypart->base;
index = index % pbodypart->nummodels;
mstudiomodel_t *pSubmodel = pbodypart->pModel( index );
for ( int meshIndex = 0; meshIndex < pSubmodel->nummeshes; ++meshIndex )
{
mstudiomesh_t *pMesh = pSubmodel->pMesh(meshIndex);
studiomeshdata_t *pMeshData = &pMeshDataBase[pMesh->meshid];
nTotalMeshCount += pMeshData->m_NumGroup;
}
}
}
}
return nTotalMeshCount;
}
//-----------------------------------------------------------------------------
// Sort models function
//-----------------------------------------------------------------------------
inline bool CStudioRender::SortLessFunc( const MeshRenderData_t &left, const MeshRenderData_t &right )
{
if ( left.m_pMaterial != right.m_pMaterial )
return left.m_pMaterial > right.m_pMaterial;
if ( left.m_pGroup->m_pMesh != right.m_pGroup->m_pMesh )
return left.m_pGroup->m_pMesh > right.m_pGroup->m_pMesh;
if ( left.m_pInstance->m_pEnvCubemapTexture != right.m_pInstance->m_pEnvCubemapTexture )
return left.m_pInstance->m_pEnvCubemapTexture > right.m_pInstance->m_pEnvCubemapTexture;
bool bLeftHasLighting = ( left.m_pInstance->m_pLightingState != NULL );
bool bRightHasLighting = ( right.m_pInstance->m_pLightingState != NULL );
if ( bLeftHasLighting != bRightHasLighting )
return bLeftHasLighting;
if ( !bLeftHasLighting )
return false;
return left.m_pInstance->m_pLightingState->m_nLocalLightCount > right.m_pInstance->m_pLightingState->m_nLocalLightCount;
}
//-----------------------------------------------------------------------------
// Sort models function
//-----------------------------------------------------------------------------
inline bool CStudioRender::SortLessFunc2( const MeshRenderData2_t &left, const MeshRenderData2_t &right )
{
if ( left.m_pMaterial != right.m_pMaterial )
return left.m_pMaterial > right.m_pMaterial;
if ( left.m_nCompressionType != right.m_nCompressionType )
return left.m_nCompressionType > right.m_nCompressionType;
if ( left.m_nMeshBoneCount != right.m_nMeshBoneCount )
return left.m_nMeshBoneCount > right.m_nMeshBoneCount;
bool bLeftHasColorMesh = ( left.m_pInstance->m_pColorMeshInfo != NULL );
bool bRightHasColorMesh = ( right.m_pInstance->m_pColorMeshInfo != NULL );
if ( bLeftHasColorMesh != bRightHasColorMesh )
return bLeftHasColorMesh < bRightHasColorMesh;
if ( left.m_pInstance->m_pEnvCubemapTexture != right.m_pInstance->m_pEnvCubemapTexture )
return left.m_pInstance->m_pEnvCubemapTexture > right.m_pInstance->m_pEnvCubemapTexture;
if ( left.m_pGroup->m_pMesh != right.m_pGroup->m_pMesh )
return left.m_pGroup->m_pMesh > right.m_pGroup->m_pMesh;
bool bLeftHasLighting = ( left.m_pInstance->m_pLightingState != NULL );
bool bRightHasLighting = ( right.m_pInstance->m_pLightingState != NULL );
if ( bLeftHasLighting != bRightHasLighting )
return bLeftHasLighting;
if ( !bLeftHasLighting )
return false;
return left.m_pInstance->m_pLightingState->m_nLocalLightCount > right.m_pInstance->m_pLightingState->m_nLocalLightCount;
}
//-----------------------------------------------------------------------------
// Builds the list of things to render in what order
//-----------------------------------------------------------------------------
int CStudioRender::BuildSortedRenderList( MeshRenderData_t *pRenderData, int *pTotalStripCount, const StudioModelArrayInfo_t &drawInfo,
int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride, int nFlags )
{
SNPROF( "CStudioRender::BuildSortedRenderList" );
studiohdr_t *pStudioHdr = drawInfo.m_pStudioHdr;
short *pSkinRefBase = pStudioHdr->pSkinref( 0 );
bool bSkipTranslucent = ( nFlags & STUDIORENDER_DRAW_OPAQUE_ONLY ) != 0;
bool bSkipOpaque = ( nFlags & STUDIORENDER_DRAW_TRANSLUCENT_ONLY ) != 0;
bool bSelectiveOverride = ( m_pRC->m_nForcedMaterialType == OVERRIDE_SELECTIVE );
*pTotalStripCount = 0;
StudioArrayInstanceData_t *pCurInstance = pInstanceData;
int nRenderDataCount = 0;
for ( int i = 0; i < nCount; ++i, pCurInstance = (StudioArrayInstanceData_t*)( (char*)pCurInstance + nInstanceStride ) )
{
// This subarray has the same skin + body + lod
int nLod = pCurInstance->m_nLOD;
// get the studio mesh data for this lod
studiomeshdata_t *pMeshDataBase = drawInfo.m_pHardwareData->m_pLODs[nLod].m_pMeshData;
IMaterial **ppMaterials = drawInfo.m_pHardwareData->m_pLODs[nLod].ppMaterials;
short *pSkinRef = pSkinRefBase;
int skin = pCurInstance->m_nSkin;
if ( skin > 0 && skin < pStudioHdr->numskinfamilies )
{
pSkinRef += ( skin * pStudioHdr->numskinref );
}
int nBody = pCurInstance->m_nBody;
for ( int body = 0; body < pStudioHdr->numbodyparts; ++body )
{
mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( body );
int index = nBody / pbodypart->base;
index = index % pbodypart->nummodels;
mstudiomodel_t *pSubmodel = pbodypart->pModel( index );
for ( int meshIndex = 0; meshIndex < pSubmodel->nummeshes; ++meshIndex )
{
mstudiomesh_t *pMesh = pSubmodel->pMesh(meshIndex);
studiomeshdata_t *pMeshData = &pMeshDataBase[ pMesh->meshid ];
IMaterial *pMaterial = ppMaterials[ pSkinRef[ pMesh->material ] ];
bool bIsTranslucent = pMaterial->IsTranslucentUnderModulation( pCurInstance->m_DiffuseModulation.w );
if ( bSkipTranslucent && bIsTranslucent )
continue;
else if ( bSkipOpaque && !bIsTranslucent )
continue;
Assert( pMeshData );
// Assert( pMeshData->m_NumGroup ); // can't Assert on m_NumGroup since it can be zero on lods.
for ( int g = 0; g < pMeshData->m_NumGroup; ++g )
{
studiomeshgroup_t* pGroup = &pMeshData->m_pMeshGroup[g];
MeshRenderData_t &data = pRenderData[nRenderDataCount++];
int nOverrideIndex = GetForcedMaterialOverrideIndex( pSkinRef[ pMesh->material ] );
if ( bSelectiveOverride && nOverrideIndex != -1 )
{
data.m_pMaterial = m_pRC->m_pForcedMaterial[ nOverrideIndex ];
}
else
{
data.m_pMaterial = pMaterial;
}
data.m_pGroup = pGroup;
data.m_pInstance = pCurInstance;
data.m_pMesh = pMesh;
*pTotalStripCount += pGroup->m_NumStrips;
}
}
}
}
std::make_heap( pRenderData, pRenderData + nRenderDataCount, SortLessFunc );
std::sort_heap( pRenderData, pRenderData + nRenderDataCount, SortLessFunc );
return nRenderDataCount;
}
//-----------------------------------------------------------------------------
// Builds the list of things to render in what order
//-----------------------------------------------------------------------------
int CStudioRender::BuildSortedRenderList( MeshRenderData2_t *pRenderData, int *pTotalStripCount, const StudioModelArrayInfo2_t &drawInfo,
int nCount, StudioArrayData_t *pArrayData, int nInstanceStride, int nFlags )
{
SNPROF( "CStudioRender::BuildSortedRenderList" );
bool bSkipTranslucent = ( nFlags & STUDIORENDER_DRAW_OPAQUE_ONLY ) != 0;
bool bSkipOpaque = ( nFlags & STUDIORENDER_DRAW_TRANSLUCENT_ONLY ) != 0;
bool bSelectiveOverride = ( m_pRC->m_nForcedMaterialType == OVERRIDE_SELECTIVE );
*pTotalStripCount = 0;
int nRenderDataCount = 0;
for ( int a = 0; a < nCount; ++a )
{
StudioArrayData_t &arrayData = pArrayData[a];
studiohdr_t *pStudioHdr = arrayData.m_pStudioHdr;
short *pSkinRefBase = pStudioHdr->pSkinref( 0 );
StudioArrayInstanceData_t *pCurInstance = (StudioArrayInstanceData_t*)( arrayData.m_pInstanceData );
for ( int i = 0; i < arrayData.m_nCount; ++i, pCurInstance = (StudioArrayInstanceData_t*)( (char*)pCurInstance + nInstanceStride ) )
{
// This subarray has the same skin + body + lod
int nLod = pCurInstance->m_nLOD;
// get the studio mesh data for this lod
studiomeshdata_t *pMeshDataBase = arrayData.m_pHardwareData->m_pLODs[nLod].m_pMeshData;
IMaterial **ppMaterials = arrayData.m_pHardwareData->m_pLODs[nLod].ppMaterials;
short *pSkinRef = pSkinRefBase;
int skin = pCurInstance->m_nSkin;
if ( skin > 0 && skin < pStudioHdr->numskinfamilies )
{
pSkinRef += ( skin * pStudioHdr->numskinref );
}
int nBody = pCurInstance->m_nBody;
for ( int body = 0; body < pStudioHdr->numbodyparts; ++body )
{
mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( body );
int index = nBody / pbodypart->base;
index = index % pbodypart->nummodels;
mstudiomodel_t *pSubmodel = pbodypart->pModel( index );
for ( int meshIndex = 0; meshIndex < pSubmodel->nummeshes; ++meshIndex )
{
mstudiomesh_t *pMesh = pSubmodel->pMesh(meshIndex);
studiomeshdata_t *pMeshData = &pMeshDataBase[ pMesh->meshid ];
IMaterial *pMaterial = ppMaterials[ pSkinRef[ pMesh->material ] ];
bool bIsTranslucent = pMaterial->IsTranslucentUnderModulation( pCurInstance->m_DiffuseModulation.w );
if ( bSkipTranslucent && bIsTranslucent )
continue;
else if ( bSkipOpaque && !bIsTranslucent )
continue;
Assert( pMeshData );
// Assert( pMeshData->m_NumGroup ); // can't Assert on m_NumGroup since it can be zero on lods.
for ( int g = 0; g < pMeshData->m_NumGroup; ++g )
{
studiomeshgroup_t* pGroup = &pMeshData->m_pMeshGroup[g];
MeshRenderData2_t &data = pRenderData[nRenderDataCount++];
int nOverrideIndex = GetForcedMaterialOverrideIndex( pSkinRef[ pMesh->material ] );
if ( bSelectiveOverride && nOverrideIndex != -1 )
{
data.m_pMaterial = m_pRC->m_pForcedMaterial[ nOverrideIndex ];
}
else
{
data.m_pMaterial = pMaterial;
}
data.m_pGroup = pGroup;
data.m_pInstance = pCurInstance;
data.m_pMesh = pMesh;
data.m_nCompressionType = CompressionType( pGroup->m_pMesh->GetVertexFormat() );
data.m_nMeshBoneCount = NumBoneWeights( pGroup->m_pMesh->GetVertexFormat() );
data.m_pInstance->m_bColorMeshHasIndirectLightingOnly = ( pStudioHdr->flags & STUDIOHDR_BAKED_VERTEX_LIGHTING_IS_INDIRECT_ONLY ) ? true : false;
*pTotalStripCount += pGroup->m_NumStrips;
}
}
}
}
}
std::make_heap( pRenderData, pRenderData + nRenderDataCount, SortLessFunc2 );
std::sort_heap( pRenderData, pRenderData + nRenderDataCount, SortLessFunc2 );
return nRenderDataCount;
}
//-----------------------------------------------------------------------------
// BuildForcedMaterialRenderList
//-----------------------------------------------------------------------------
void CStudioRender::BuildForcedMaterialRenderList( MeshRenderData_t *pRenderData,
int *pTotalStripCount, const StudioModelArrayInfo_t &drawInfo, const StudioRenderContext_t &rc,
int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride )
{
VPROF( "CStudioRender::BuildForcedMaterialRenderList" );
studiohdr_t *pStudioHdr = drawInfo.m_pStudioHdr;
*pTotalStripCount = 0;
StudioArrayInstanceData_t *pCurInstance = pInstanceData;
int nRenderDataCount = 0;
for ( int i = 0; i < nCount; ++i, pCurInstance = (StudioArrayInstanceData_t*)( (char*)pCurInstance + nInstanceStride ) )
{
// This subarray has the same skin + body + lod
int nLod = pCurInstance->m_nLOD;
// get the studio mesh data for this lod
studiomeshdata_t *pMeshDataBase = drawInfo.m_pHardwareData->m_pLODs[nLod].m_pMeshData;
int nBody = pCurInstance->m_nBody;
for ( int body = 0; body < pStudioHdr->numbodyparts; ++body )
{
mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( body );
int index = nBody / pbodypart->base;
index = index % pbodypart->nummodels;
mstudiomodel_t *pSubmodel = pbodypart->pModel( index );
for ( int meshIndex = 0; meshIndex < pSubmodel->nummeshes; ++meshIndex )
{
mstudiomesh_t *pMesh = pSubmodel->pMesh(meshIndex);
studiomeshdata_t *pMeshData = &pMeshDataBase[pMesh->meshid];
Assert( pMeshData && pMeshData->m_NumGroup );
for ( int g = 0; g < pMeshData->m_NumGroup; ++g )
{
studiomeshgroup_t* pGroup = &pMeshData->m_pMeshGroup[g];
MeshRenderData_t &data = pRenderData[nRenderDataCount++];
data.m_pMaterial = rc.m_pForcedMaterial[ 0 ];
data.m_pGroup = pGroup;
data.m_pInstance = pCurInstance;
data.m_pMesh = pMesh;
*pTotalStripCount += pGroup->m_NumStrips;
}
}
}
}
}
//-----------------------------------------------------------------------------
// BuildForcedMaterialRenderList
//-----------------------------------------------------------------------------
void CStudioRender::BuildForcedMaterialRenderList( MeshRenderData2_t *pRenderData,
int *pTotalStripCount, const StudioModelArrayInfo2_t &drawInfo, const StudioRenderContext_t &rc,
int nCount, StudioArrayData_t *pArrayData, int nInstanceStride )
{
VPROF( "CStudioRender::BuildForcedMaterialRenderList" );
*pTotalStripCount = 0;
int nRenderDataCount = 0;
for ( int a = 0; a < nCount; ++a )
{
StudioArrayData_t &arrayData = pArrayData[a];
studiohdr_t *pStudioHdr = arrayData.m_pStudioHdr;
StudioArrayInstanceData_t *pCurInstance = (StudioArrayInstanceData_t*)( arrayData.m_pInstanceData );
for ( int i = 0; i < arrayData.m_nCount; ++i, pCurInstance = (StudioArrayInstanceData_t*)( (char*)pCurInstance + nInstanceStride ) )
{
// This subarray has the same skin + body + lod
int nLod = pCurInstance->m_nLOD;
// get the studio mesh data for this lod
studiomeshdata_t *pMeshDataBase = arrayData.m_pHardwareData->m_pLODs[nLod].m_pMeshData;
int nBody = pCurInstance->m_nBody;
for ( int body = 0; body < pStudioHdr->numbodyparts; ++body )
{
mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( body );
int index = nBody / pbodypart->base;
index = index % pbodypart->nummodels;
mstudiomodel_t *pSubmodel = pbodypart->pModel( index );
for ( int meshIndex = 0; meshIndex < pSubmodel->nummeshes; ++meshIndex )
{
mstudiomesh_t *pMesh = pSubmodel->pMesh(meshIndex);
studiomeshdata_t *pMeshData = &pMeshDataBase[pMesh->meshid];
Assert( pMeshData && pMeshData->m_NumGroup );
for ( int g = 0; g < pMeshData->m_NumGroup; ++g )
{
studiomeshgroup_t* pGroup = &pMeshData->m_pMeshGroup[g];
MeshRenderData2_t &data = pRenderData[nRenderDataCount++];
data.m_pMaterial = rc.m_pForcedMaterial[ 0 ];
data.m_pGroup = pGroup;
data.m_pInstance = pCurInstance;
data.m_pMesh = pMesh;
data.m_nCompressionType = CompressionType( pGroup->m_pMesh->GetVertexFormat() );
data.m_nMeshBoneCount = NumBoneWeights( pGroup->m_pMesh->GetVertexFormat() );
*pTotalStripCount += pGroup->m_NumStrips;
}
}
}
}
}
}
//-----------------------------------------------------------------------------
// Restores meshes, if necessary
//-----------------------------------------------------------------------------
void CStudioRender::RestoreMeshes( int nCount, BaseMeshRenderData_t *pRenderData, int nStride )
{
#ifdef IS_WINDOWS_PC
// FIXME: Can we build a list of unique studiomeshdata_ts?
for ( int i = 0; i < nCount; ++i, pRenderData = (BaseMeshRenderData_t*)( (unsigned char*)pRenderData + nStride ) )
{
BaseMeshRenderData_t &data = *pRenderData;
studiomeshgroup_t *pGroup = data.m_pGroup;
// Older models are merely flexed while new ones are also delta flexed
Assert( !( pGroup->m_Flags & MESHGROUP_IS_DELTA_FLEXED ) );
// Needed when we switch back and forth between hardware + software lighting
if ( !pGroup->m_MeshNeedsRestore )
continue;
IMesh *pMesh = pGroup->m_pMesh;
VertexCompressionType_t compressionType = CompressionType( pMesh->GetVertexFormat() );
switch ( compressionType )
{
case VERTEX_COMPRESSION_ON:
R_StudioRestoreMesh<VERTEX_COMPRESSION_ON>( data.m_pMesh, pGroup );
break;
case VERTEX_COMPRESSION_NONE:
default:
R_StudioRestoreMesh<VERTEX_COMPRESSION_NONE>( data.m_pMesh, pGroup );
break;
}
pGroup->m_MeshNeedsRestore = false;
}
#endif
}
//-----------------------------------------------------------------------------
// Allocate temporary arrays either on the stack, or from the heap.
// Prevents using all the stack when *lots* of objects are rendered to CSM's.
//-----------------------------------------------------------------------------
#if defined( CSTRIKE15 ) // 7ls && !defined( _GAMECONSOLE )
#define STUDIORENDER_TEMP_DATA_MALLOC( typeName, p, n ) const int nTempDataSize##p = (n); void *pvFree##p = NULL; typeName *p = (typeName *) ( ( nTempDataSize##p < 64*1024 ) ? stackalloc( nTempDataSize##p ) : ( pvFree##p = malloc( nTempDataSize##p ) ) );
#define STUDIORENDER_TEMP_DATA_FREE( p ) free( pvFree##p )
#else
#define STUDIORENDER_TEMP_DATA_MALLOC( typeName, p, n ) typeName *p = (typeName *) stackalloc(n);
#define STUDIORENDER_TEMP_DATA_FREE( p )
#endif
//-----------------------------------------------------------------------------
// Draws meshes
//-----------------------------------------------------------------------------
void CStudioRender::DrawMeshRenderData( IMatRenderContext *pRenderContext,
const StudioModelArrayInfo_t &drawInfo, int nCount, MeshRenderData_t *pRenderData, int nTotalStripCount, int nFlashlightMask )
{
SNPROF( "CStudioRender::DrawMeshRenderData" );
int nInstanceCount = 0;
STUDIORENDER_TEMP_DATA_MALLOC( MeshInstanceData_t, pInstance, nTotalStripCount * sizeof(MeshInstanceData_t) );
IMaterial *pLastMaterial = NULL;
IMesh *pLastMesh = NULL;
#ifdef _GAMECONSOLE
bool bLastUsingFlashlight = false;
bool bSavedFlashlightEnable = pRenderContext->GetFlashlightMode();
#endif // _GAMECONSOLE
int nMaxBoneCount = 0;
int nMaxLightCount = 0;
bool bIsSkinned = drawInfo.m_pStudioHdr->numbones > 1;
#if PIX_ENABLE
char szPIXEventName[128];
sprintf( szPIXEventName, "%s*", drawInfo.m_pStudioHdr->name ); // PIX
PIXEVENT( pRenderContext, szPIXEventName );
#endif
for ( int i = 0; i < nCount; ++i )
{
MeshRenderData_t &data = pRenderData[i];
StudioArrayInstanceData_t *pCurrInstance = data.m_pInstance;
// Skip models not affected by this flashlight
if ( nFlashlightMask && ( ( nFlashlightMask & pCurrInstance->m_nFlashlightUsage ) == 0 ) )
continue;
if ( ( pLastMaterial != data.m_pMaterial ) || ( pLastMesh != data.m_pGroup->m_pMesh )
#ifdef _GAMECONSOLE
|| ( bLastUsingFlashlight != ( pCurrInstance->m_nFlashlightUsage != 0 ) )
#endif // _GAMECONSOLE
)
{
if ( nInstanceCount > 0 )
{
#ifdef _GAMECONSOLE
if ( pRenderContext->IsCullingEnabledForSinglePassFlashlight() )
{
pRenderContext->SetFlashlightMode( bLastUsingFlashlight );
}
#endif // _GAMECONSOLE
pRenderContext->SetNumBoneWeights( bIsSkinned ? nMaxBoneCount : 0 );
pRenderContext->Bind( pLastMaterial, NULL );
pRenderContext->DrawInstances( nInstanceCount, pInstance );
}
nInstanceCount = 0;
nMaxBoneCount = 0;
nMaxLightCount = 0;
pLastMesh = data.m_pGroup->m_pMesh;
pLastMaterial = data.m_pMaterial;
#ifdef _GAMECONSOLE
bLastUsingFlashlight = pCurrInstance->m_nFlashlightUsage != 0;
#endif // _GAMECONSOLE
}
studiomeshgroup_t* pGroup = data.m_pGroup;
for ( int j = 0; j < pGroup->m_NumStrips; ++j )
{
OptimizedModel::StripHeader_t* pStrip = &pGroup->m_pStripData[j];
Assert( nInstanceCount < nTotalStripCount );
MeshInstanceData_t &instance = pInstance[nInstanceCount++];
instance.m_pEnvCubemap = pCurrInstance->m_pEnvCubemapTexture;
instance.m_pPoseToWorld = pCurrInstance->m_pPoseToWorld;
instance.m_pLightingState = pCurrInstance->m_pLightingState;
if ( pCurrInstance->m_pColorMeshInfo )
{
const ColorMeshInfo_t &colorMesh = pCurrInstance->m_pColorMeshInfo[ pGroup->m_ColorMeshID ];
instance.m_pColorBuffer = colorMesh.m_pMesh;
instance.m_nColorVertexOffsetInBytes = colorMesh.m_nVertOffsetInBytes;
}
else
{
instance.m_pColorBuffer = NULL;
instance.m_nColorVertexOffsetInBytes = 0;
}
instance.m_nBoneCount = pStrip->numBoneStateChanges;
instance.m_pBoneRemap = ( instance.m_nBoneCount > 0 ) ? (MeshBoneRemap_t*)( pStrip->pBoneStateChange(0) ) : NULL;
instance.m_nIndexOffset = pStrip->indexOffset;
instance.m_nIndexCount = pStrip->numIndices;
instance.m_nPrimType = MATERIAL_TRIANGLES;
instance.m_pVertexBuffer = data.m_pGroup->m_pMesh;
instance.m_pIndexBuffer = data.m_pGroup->m_pMesh;
instance.m_nVertexOffsetInBytes = 0;
instance.m_pStencilState = pCurrInstance->m_pStencilState;
instance.m_DiffuseModulation = pCurrInstance->m_DiffuseModulation;
instance.m_nLightmapPageId = MATERIAL_SYSTEM_LIGHTMAP_PAGE_INVALID;
instance.m_bColorBufferHasIndirectLightingOnly = ( drawInfo.m_pStudioHdr->flags & STUDIOHDR_BAKED_VERTEX_LIGHTING_IS_INDIRECT_ONLY ) ? true : false;
nMaxBoneCount = MAX( nMaxBoneCount, pStrip->numBones );
}
}
if ( nInstanceCount > 0 )
{
#ifdef _GAMECONSOLE
if ( pRenderContext->IsCullingEnabledForSinglePassFlashlight() )
{
pRenderContext->SetFlashlightMode( bLastUsingFlashlight );
}
#endif // _GAMECONSOLE
pRenderContext->SetNumBoneWeights( bIsSkinned ? nMaxBoneCount : 0 );
pRenderContext->Bind( pLastMaterial, NULL );
pRenderContext->DrawInstances( nInstanceCount, pInstance );
}
#ifdef _GAMECONSOLE
pRenderContext->SetFlashlightMode( bSavedFlashlightEnable );
#endif // _GAMECONSOLE
pRenderContext->SetNumBoneWeights( 0 );
STUDIORENDER_TEMP_DATA_FREE( pInstance );
}
//-----------------------------------------------------------------------------
// Draws meshes
//-----------------------------------------------------------------------------
void CStudioRender::DrawMeshRenderData( IMatRenderContext *pRenderContext,
const StudioModelArrayInfo2_t &drawInfo, int nCount, MeshRenderData2_t *pRenderData, int nTotalStripCount, int nFlashlightMask )
{
SNPROF( "CStudioRender::DrawMeshRenderData" );
int nInstanceCount = 0;
int nMaxBatchSize = IsGameConsole() ? CONSOLE_MAX_MODEL_FAST_PATH_BATCH_SIZE : nTotalStripCount;
STUDIORENDER_TEMP_DATA_MALLOC( MeshInstanceData_t, pInstance, nMaxBatchSize * sizeof(MeshInstanceData_t) );
IMaterial *pLastMaterial = NULL;
int nMaxBoneCount = 0;
int nMaxLightCount = 0;
int nLastMeshBoneCount = 0;
VertexCompressionType_t nLastCompressionType = VERTEX_COMPRESSION_INVALID;
bool bLastMeshUsedColorMesh = false;
#ifdef _GAMECONSOLE
bool bLastUsingFlashlight = false;
bool bSavedFlashlightEnable = pRenderContext->GetFlashlightMode();
#endif // _GAMECONSOLE
int nStartingStripIndex = 0; // used to interrupt batching within a group if the number of strips exceeds the max batch size
for ( int i = 0; i < nCount; ++i )
{
MeshRenderData2_t &data = pRenderData[i];
StudioArrayInstanceData_t *pCurrInstance = data.m_pInstance;
// Skip models not affected by this flashlight
if ( nFlashlightMask && ( ( nFlashlightMask & pCurrInstance->m_nFlashlightUsage ) == 0 ) )
continue;
bool bUsingColorMesh = ( pCurrInstance->m_pColorMeshInfo != NULL );
if ( ( pLastMaterial != data.m_pMaterial ) || // shadow material is different
( nLastCompressionType != ( int )data.m_nCompressionType ) || // compression type is different
( nLastMeshBoneCount != data.m_nMeshBoneCount ) || // # of bones in the mesh data is different
( bLastMeshUsedColorMesh != bUsingColorMesh ) || // Lighting type is different
( IsGameConsole() && ( nInstanceCount >= nMaxBatchSize ) ) // max # of batches to render at once due to stack limitations on console
#ifdef _GAMECONSOLE
|| ( bLastUsingFlashlight != ( pCurrInstance->m_nFlashlightUsage != 0 ) )
#endif // _GAMECONSOLE
)
{
if ( nInstanceCount > 0 )
{
#ifdef _GAMECONSOLE
if ( pRenderContext->IsCullingEnabledForSinglePassFlashlight() )
{
pRenderContext->SetFlashlightMode( bLastUsingFlashlight );
}
#endif // _GAMECONSOLE
pRenderContext->SetNumBoneWeights( nLastMeshBoneCount > 0 ? nMaxBoneCount : 0 );
pRenderContext->Bind( pLastMaterial, NULL );
pRenderContext->DrawInstances( nInstanceCount, pInstance );
}
nInstanceCount = 0;
nMaxBoneCount = 0;
nMaxLightCount = 0;
nLastCompressionType = ( VertexCompressionType_t )( int )data.m_nCompressionType;
pLastMaterial = data.m_pMaterial;
nLastMeshBoneCount = data.m_nMeshBoneCount;
bLastMeshUsedColorMesh = bUsingColorMesh;
#ifdef _GAMECONSOLE
bLastUsingFlashlight = pCurrInstance->m_nFlashlightUsage != 0;
#endif // _GAMECONSOLE
}
studiomeshgroup_t* pGroup = data.m_pGroup;
int j;
for ( j = nStartingStripIndex; j < pGroup->m_NumStrips; ++j )
{
OptimizedModel::StripHeader_t* pStrip = &pGroup->m_pStripData[j];
MeshInstanceData_t &instance = pInstance[nInstanceCount++];
instance.m_pEnvCubemap = pCurrInstance->m_pEnvCubemapTexture;
instance.m_pPoseToWorld = pCurrInstance->m_pPoseToWorld;
instance.m_pLightingState = pCurrInstance->m_pLightingState;
if ( pCurrInstance->m_pColorMeshInfo )
{
const ColorMeshInfo_t &colorMesh = pCurrInstance->m_pColorMeshInfo[ pGroup->m_ColorMeshID ];
instance.m_pColorBuffer = colorMesh.m_pMesh;
instance.m_nColorVertexOffsetInBytes = colorMesh.m_nVertOffsetInBytes;
}
else
{
instance.m_pColorBuffer = NULL;
instance.m_nColorVertexOffsetInBytes = 0;
}
instance.m_nBoneCount = pStrip->numBoneStateChanges;
instance.m_pBoneRemap = ( instance.m_nBoneCount > 0 ) ? (MeshBoneRemap_t*)( pStrip->pBoneStateChange(0) ) : NULL;
instance.m_nIndexOffset = pStrip->indexOffset;
instance.m_nIndexCount = pStrip->numIndices;
instance.m_nPrimType = MATERIAL_TRIANGLES;
instance.m_pVertexBuffer = data.m_pGroup->m_pMesh;
instance.m_pIndexBuffer = data.m_pGroup->m_pMesh;
instance.m_nVertexOffsetInBytes = 0;
instance.m_pStencilState = pCurrInstance->m_pStencilState;
instance.m_DiffuseModulation = pCurrInstance->m_DiffuseModulation;
instance.m_nLightmapPageId = MATERIAL_SYSTEM_LIGHTMAP_PAGE_INVALID;
instance.m_bColorBufferHasIndirectLightingOnly = pCurrInstance->m_bColorMeshHasIndirectLightingOnly;
nMaxBoneCount = MAX( nMaxBoneCount, pStrip->numBones );
if ( IsGameConsole() && nInstanceCount >= nMaxBatchSize )
{
break;
}
}
if ( IsGameConsole() && j < pGroup->m_NumStrips )
{
// We're going to have to process this pRenderData[] entry again,
// but start iterating from a higher strip index next time.
nStartingStripIndex = j + 1;
-- i;
}
else
{
nStartingStripIndex = 0;
}
}
if ( nInstanceCount > 0 )
{
#ifdef _GAMECONSOLE
if ( pRenderContext->IsCullingEnabledForSinglePassFlashlight() )
{
pRenderContext->SetFlashlightMode( bLastUsingFlashlight );
}
#endif // _GAMECONSOLE
pRenderContext->SetNumBoneWeights( nLastMeshBoneCount > 0 ? nMaxBoneCount : 0 );
pRenderContext->Bind( pLastMaterial, NULL );
pRenderContext->DrawInstances( nInstanceCount, pInstance );
}
#ifdef _GAMECONSOLE
pRenderContext->SetFlashlightMode( bSavedFlashlightEnable );
#endif // _GAMECONSOLE
pRenderContext->SetNumBoneWeights( 0 );
STUDIORENDER_TEMP_DATA_FREE( pInstance );
}
//-----------------------------------------------------------------------------
// Draws meshes illuminated by the flashlight
//-----------------------------------------------------------------------------
extern ConVar r_flashlightscissor;
void CStudioRender::DrawModelArrayFlashlight( IMatRenderContext *pRenderContext, const StudioModelArrayInfo_t &drawInfo, int nCount, MeshRenderData_t *pRenderData, int nTotalStripCount )
{
int nFlashlightCount = drawInfo.m_nFlashlightCount;
if ( !nFlashlightCount )
return;
pRenderContext->SetFlashlightMode( true );
bool bDoScissor = r_flashlightscissor.GetBool() && ( pRenderContext->GetRenderTarget() == NULL );
int i;
for ( i = 0; i < nFlashlightCount; ++i )
{
FlashlightInstance_t &flashlight = drawInfo.m_pFlashlights[i];
const FlashlightState_t& state = flashlight.m_FlashlightState;
if ( bDoScissor )
{
if ( state.DoScissor() )
{
pRenderContext->PushScissorRect( state.GetLeft(), state.GetTop(), state.GetRight(), state.GetBottom() );
}
}
if ( !flashlight.m_pDebugMaterial )
{
pRenderContext->SetFlashlightStateEx( state, flashlight.m_WorldToTexture, flashlight.m_pFlashlightDepthTexture );
DrawMeshRenderData( pRenderContext, drawInfo, nCount, pRenderData, nTotalStripCount, ( 1 << i ) );
}
else
{
// Debugging mode where we render wireframe on models hit by the flashlight
// FIXME: Could make this faster, but why bother? It's a debugging mode.
int nSizeInBytes = nCount * sizeof(MeshRenderData_t);
STUDIORENDER_TEMP_DATA_MALLOC( MeshRenderData_t, pTempData, nSizeInBytes );
memcpy( pTempData, pRenderData, nSizeInBytes );
for ( int j = 0; j < nCount; ++j )
{
pTempData[j].m_pMaterial = flashlight.m_pDebugMaterial;
}
pRenderContext->SetFlashlightMode( false );
DrawMeshRenderData( pRenderContext, drawInfo, nCount, pTempData, nTotalStripCount, ( 1 << i ) );
pRenderContext->SetFlashlightMode( true );
STUDIORENDER_TEMP_DATA_FREE( pTempData );
}
if ( bDoScissor && state.DoScissor() )
{
pRenderContext->PopScissorRect();
}
}
pRenderContext->SetFlashlightMode( false );
}
//-----------------------------------------------------------------------------
// Draws meshes illuminated by the flashlight
//-----------------------------------------------------------------------------
void CStudioRender::DrawModelArrayFlashlight( IMatRenderContext *pRenderContext, const StudioModelArrayInfo2_t &drawInfo, int nCount, MeshRenderData2_t *pRenderData, int nTotalStripCount )
{
int nFlashlightCount = drawInfo.m_nFlashlightCount;
if ( !nFlashlightCount )
return;
pRenderContext->SetFlashlightMode( true );
bool bDoScissor = r_flashlightscissor.GetBool() && ( pRenderContext->GetRenderTarget() == NULL );
int i;
for ( i = 0; i < nFlashlightCount; ++i )
{
FlashlightInstance_t &flashlight = drawInfo.m_pFlashlights[i];
const FlashlightState_t& state = flashlight.m_FlashlightState;
if ( bDoScissor )
{
if ( state.DoScissor() )
{
pRenderContext->PushScissorRect( state.GetLeft(), state.GetTop(), state.GetRight(), state.GetBottom() );
}
}
if ( !flashlight.m_pDebugMaterial )
{
pRenderContext->SetFlashlightStateEx( state, flashlight.m_WorldToTexture, flashlight.m_pFlashlightDepthTexture );
DrawMeshRenderData( pRenderContext, drawInfo, nCount, pRenderData, nTotalStripCount, ( 1 << i ) );
}
else
{
// Debugging mode where we render wireframe on models hit by the flashlight
// FIXME: Could make this faster, but why bother? It's a debugging mode.
int nSizeInBytes = nCount * sizeof(MeshRenderData2_t);
STUDIORENDER_TEMP_DATA_MALLOC( MeshRenderData2_t, pTempData, nSizeInBytes );
memcpy( pTempData, pRenderData, nSizeInBytes );
for ( int j = 0; j < nCount; ++j )
{
pTempData[j].m_pMaterial = flashlight.m_pDebugMaterial;
}
pRenderContext->SetFlashlightMode( false );
DrawMeshRenderData( pRenderContext, drawInfo, nCount, pTempData, nTotalStripCount, ( 1 << i ) );
pRenderContext->SetFlashlightMode( true );
STUDIORENDER_TEMP_DATA_FREE( pTempData );
}
if ( bDoScissor && state.DoScissor() )
{
pRenderContext->PopScissorRect();
}
}
pRenderContext->SetFlashlightMode( false );
}
//-----------------------------------------------------------------------------
// Draws decals illuminated by the flashlight
//-----------------------------------------------------------------------------
void CStudioRender::DrawModelArrayFlashlightDecals( IMatRenderContext *pRenderContext, studiohdr_t *pStudioHdr, int nFlashlightCount, FlashlightInstance_t *pFlashlights, int nCount, DecalRenderData_t *pRenderData )
{
if ( !nFlashlightCount )
return;
pRenderContext->SetFlashlightMode( true );
bool bDoScissor = r_flashlightscissor.GetBool() && ( pRenderContext->GetRenderTarget() == NULL );
int i;
for ( i = 0; i < nFlashlightCount; ++i )
{
FlashlightInstance_t &flashlight = pFlashlights[i];
const FlashlightState_t& state = flashlight.m_FlashlightState;
if ( bDoScissor )
{
if ( state.DoScissor() )
{
pRenderContext->PushScissorRect( state.GetLeft(), state.GetTop(), state.GetRight(), state.GetBottom() );
}
}
if ( !flashlight.m_pDebugMaterial )
{
pRenderContext->SetFlashlightStateEx( state, flashlight.m_WorldToTexture, flashlight.m_pFlashlightDepthTexture );
DrawModelArrayDecals( pRenderContext, pStudioHdr, nCount, pRenderData, ( 1 << i ) );
}
else
{
// Debugging mode where we render wireframe on models hit by the flashlight
// FIXME: Could make this faster, but why bother? It's a debugging mode.
int nSizeInBytes = nCount * sizeof(DecalRenderData_t);
STUDIORENDER_TEMP_DATA_MALLOC( DecalRenderData_t, pTempData, nSizeInBytes );
memcpy( pTempData, pRenderData, nSizeInBytes );
for ( int j = 0; j < nCount; ++j )
{
pTempData[j].m_pRenderMaterial = flashlight.m_pDebugMaterial;
}
pRenderContext->SetFlashlightMode( false );
DrawModelArrayDecals( pRenderContext, pStudioHdr, nCount, pTempData, ( 1 << i ) );
pRenderContext->SetFlashlightMode( true );
STUDIORENDER_TEMP_DATA_FREE( pTempData );
}
if ( bDoScissor && state.DoScissor() )
{
pRenderContext->PopScissorRect();
}
}
pRenderContext->SetFlashlightMode( false );
}
int CStudioRender::CountDecalMeshesToDraw( int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride )
{
VPROF( "CStudioRender::CountDecalMeshesToDraw" );
int nDecalMeshCount = 0;
StudioArrayInstanceData_t *pCurInstance = pInstanceData;
for ( int i = 0; i < nCount; ++i, pCurInstance = (StudioArrayInstanceData_t*)( (char*)pCurInstance + nInstanceStride ) )
{
StudioDecalHandle_t handle = pCurInstance->m_Decals;
if ( handle == STUDIORENDER_DECAL_INVALID )
continue;
const DecalModelList_t& list = m_DecalList[(intp)handle];
unsigned short mat = list.m_pLod[pCurInstance->m_nLOD].m_FirstMaterial;
for ( ; mat != m_DecalMaterial.InvalidIndex(); mat = m_DecalMaterial.Next(mat))
{
++nDecalMeshCount;
}
}
return nDecalMeshCount;
}
//-----------------------------------------------------------------------------
// Sort decals function
//-----------------------------------------------------------------------------
inline bool CStudioRender::SortDecalsLessFunc( const DecalRenderData_t &left, const DecalRenderData_t &right )
{
if ( left.m_pDecalMaterial->m_pvProxyUserData != right.m_pDecalMaterial->m_pvProxyUserData )
{
int nUniqueID_Left = int( reinterpret_cast< uintp >( left.m_pDecalMaterial->m_pvProxyUserData ) ) & 0xFFFFFF;
int nUniqueID_Right = int( reinterpret_cast< uintp >( right.m_pDecalMaterial->m_pvProxyUserData ) ) & 0xFFFFFF;
if ( nUniqueID_Left != nUniqueID_Right )
{
if ( nUniqueID_Left && nUniqueID_Right )
return nUniqueID_Left < nUniqueID_Right;
else
return nUniqueID_Left > nUniqueID_Right; // if left is player decal, and right is non-proxied then true means to render player decal earlier
}
}
if ( left.m_pDecalMaterial->m_pMaterial != right.m_pDecalMaterial->m_pMaterial )
return left.m_pDecalMaterial->m_pMaterial > right.m_pDecalMaterial->m_pMaterial;
if ( left.m_pInstance->m_pEnvCubemapTexture != right.m_pInstance->m_pEnvCubemapTexture )
return left.m_pInstance->m_pEnvCubemapTexture > right.m_pInstance->m_pEnvCubemapTexture;
bool bLeftHasLighting = ( left.m_pInstance->m_pLightingState != NULL );
bool bRightHasLighting = ( right.m_pInstance->m_pLightingState != NULL );
if ( bLeftHasLighting != bRightHasLighting )
return bLeftHasLighting;
if ( !bLeftHasLighting )
return false;
return left.m_pInstance->m_pLightingState->m_nLocalLightCount > right.m_pInstance->m_pLightingState->m_nLocalLightCount;
}
//-----------------------------------------------------------------------------
// Sorts decals to render
//-----------------------------------------------------------------------------
int CStudioRender::BuildSortedDecalRenderList( DecalRenderData_t *pDecalRenderData, int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride )
{
VPROF( "CStudioRender::BuildSortedDecalRenderList" );
int nDecalMeshCount = 0;
StudioArrayInstanceData_t *pCurInstance = pInstanceData;
for ( int i = 0; i < nCount; ++i, pCurInstance = (StudioArrayInstanceData_t*)( (char*)pCurInstance + nInstanceStride ) )
{
StudioDecalHandle_t handle = pCurInstance->m_Decals;
if ( handle == STUDIORENDER_DECAL_INVALID )
continue;
const DecalModelList_t& list = m_DecalList[(intp)handle];
unsigned short mat = list.m_pLod[pCurInstance->m_nLOD].m_FirstMaterial;
for ( ; mat != m_DecalMaterial.InvalidIndex(); mat = m_DecalMaterial.Next(mat))
{
DecalMaterial_t *pDecalMaterial = &m_DecalMaterial[mat];
// It's possible for the index count to become zero due to decal retirement
int indexCount = pDecalMaterial->m_Indices.Count();
if ( indexCount == 0 )
continue;
DecalRenderData_t &decalRenderData = pDecalRenderData[nDecalMeshCount++];
decalRenderData.m_pDecalMaterial = pDecalMaterial;
decalRenderData.m_pInstance = pCurInstance;
decalRenderData.m_pRenderMaterial = pDecalMaterial->m_pMaterial;
decalRenderData.m_bIsVertexLit = pDecalMaterial->m_pMaterial->IsVertexLit();
}
}
// Debug mode
if ( m_pRC->m_Config.bWireframeDecals )
{
for ( int i = 0; i < nDecalMeshCount; ++i )
{
pDecalRenderData[i].m_pRenderMaterial = m_pMaterialWireframe[0][0]; // TODO: support displacement mapping
}
}
std::make_heap( pDecalRenderData, pDecalRenderData + nDecalMeshCount, SortDecalsLessFunc );
std::sort_heap( pDecalRenderData, pDecalRenderData + nDecalMeshCount, SortDecalsLessFunc );
return nDecalMeshCount;
}
//-----------------------------------------------------------------------------
// Main entry point for drawing an array of instances
//-----------------------------------------------------------------------------
void CStudioRender::DrawModelArray( const StudioModelArrayInfo_t &drawInfo, const StudioRenderContext_t &rc,
int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride, int nFlags )
{
m_pRC = const_cast< StudioRenderContext_t* >( &rc );
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
// Preserve the matrices if we're skinning
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
// Count number of meshes per instance
bool bDoShadows = ( ( nFlags & STUDIORENDER_DRAW_NO_SHADOWS ) == 0 );
bool bSinglePassFlashlight = IsGameConsole();
int nTimesRendered = 1 + ( bSinglePassFlashlight ? 0 : ( bDoShadows ? drawInfo.m_nFlashlightCount : 1 ) );
int nTotalMeshCount = CountMeshesToDraw( drawInfo, nCount, pInstanceData, nInstanceStride, nTimesRendered );
// Build list of meshes to render
int nTotalStripCount;
STUDIORENDER_TEMP_DATA_MALLOC( MeshRenderData_t, pRenderData, nTotalMeshCount * sizeof(MeshRenderData_t) );
if ( !rc.m_pForcedMaterial[ 0 ] || rc.m_nForcedMaterialType == OVERRIDE_SELECTIVE )
{
nTotalMeshCount = BuildSortedRenderList( pRenderData, &nTotalStripCount, drawInfo, nCount, pInstanceData, nInstanceStride, nFlags );
}
else
{
BuildForcedMaterialRenderList( pRenderData, &nTotalStripCount, drawInfo, rc, nCount, pInstanceData, nInstanceStride );
}
// Restore meshes, if necessary
RestoreMeshes( nTotalMeshCount, pRenderData, sizeof(MeshRenderData_t) );
// Draw, baby, draw!
DrawMeshRenderData( pRenderContext, drawInfo, nTotalMeshCount, pRenderData, nTotalStripCount, 0 );
// Draw all flashlights
if ( bDoShadows && !bSinglePassFlashlight )
{
DrawModelArrayFlashlight( pRenderContext, drawInfo, nTotalMeshCount, pRenderData, nTotalStripCount );
}
// Count number of decals meshes per instance
int nTotalDecalCount = CountDecalMeshesToDraw( nCount, pInstanceData, nInstanceStride );
STUDIORENDER_TEMP_DATA_MALLOC( DecalRenderData_t, pDecalData, nTotalDecalCount * sizeof(DecalRenderData_t) );
// Build list of decals to render
nTotalDecalCount = BuildSortedDecalRenderList( pDecalData, nCount, pInstanceData, nInstanceStride );
// Draw all decals
DrawModelArrayDecals( pRenderContext, drawInfo.m_pStudioHdr, nTotalDecalCount, pDecalData, 0 );
// Illuminate decals by the flashlight
if ( bDoShadows && !bSinglePassFlashlight )
{
DrawModelArrayFlashlightDecals( pRenderContext, drawInfo.m_pStudioHdr, drawInfo.m_nFlashlightCount, drawInfo.m_pFlashlights, nTotalDecalCount, pDecalData );
}
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
pRenderContext->SetNumBoneWeights( 0 );
STUDIORENDER_TEMP_DATA_FREE( pRenderData );
STUDIORENDER_TEMP_DATA_FREE( pDecalData );
}
//-----------------------------------------------------------------------------
// More optimal version?
//-----------------------------------------------------------------------------
void CStudioRender::DrawModelArray2( const StudioModelArrayInfo2_t &drawInfo, const StudioRenderContext_t &rc,
int nCount, StudioArrayData_t *pArrayData, int nInstanceStride, int nFlags )
{
m_pRC = const_cast< StudioRenderContext_t* >( &rc );
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
// Preserve the matrices if we're skinning
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
// Count number of meshes per instance
bool bDoShadows = ( ( nFlags & STUDIORENDER_DRAW_NO_SHADOWS ) == 0 );
bool bSinglePassFlashlight = IsGameConsole();
int nTimesRendered = 1 + ( bSinglePassFlashlight ? 0 : ( bDoShadows ? drawInfo.m_nFlashlightCount : 1 ) );
int nTotalMeshCount = CountMeshesToDraw( drawInfo, nCount, pArrayData, nInstanceStride, nTimesRendered );
// Build list of meshes to render
int nTotalStripCount;
STUDIORENDER_TEMP_DATA_MALLOC( MeshRenderData2_t, pRenderData, nTotalMeshCount * sizeof(MeshRenderData2_t) );
if ( !rc.m_pForcedMaterial[ 0 ] || rc.m_nForcedMaterialType == OVERRIDE_SELECTIVE )
{
nTotalMeshCount = BuildSortedRenderList( pRenderData, &nTotalStripCount, drawInfo, nCount, pArrayData, nInstanceStride, nFlags );
}
else
{
BuildForcedMaterialRenderList( pRenderData, &nTotalStripCount, drawInfo, rc, nCount, pArrayData, nInstanceStride );
}
// Restore meshes, if necessary
RestoreMeshes( nTotalMeshCount, pRenderData, sizeof(MeshRenderData2_t) );
// Draw, baby, draw!
DrawMeshRenderData( pRenderContext, drawInfo, nTotalMeshCount, pRenderData, nTotalStripCount, 0 );
// Draw all flashlights
if ( bDoShadows && !bSinglePassFlashlight )
{
DrawModelArrayFlashlight( pRenderContext, drawInfo, nTotalMeshCount, pRenderData, nTotalStripCount );
}
// Count number of decals meshes per instance
for ( int i = 0; i < nCount; ++i )
{
StudioArrayData_t &arrayData = pArrayData[i];
StudioArrayInstanceData_t *pGroupInstances = ( StudioArrayInstanceData_t * )( arrayData.m_pInstanceData );
int nTotalDecalCount = CountDecalMeshesToDraw( arrayData.m_nCount, pGroupInstances, nInstanceStride );
STUDIORENDER_TEMP_DATA_MALLOC( DecalRenderData_t, pDecalData, nTotalDecalCount * sizeof(DecalRenderData_t) );
// Build list of decals to render
nTotalDecalCount = BuildSortedDecalRenderList( pDecalData, arrayData.m_nCount, pGroupInstances, nInstanceStride );
// Draw all decals
DrawModelArrayDecals( pRenderContext, arrayData.m_pStudioHdr, nTotalDecalCount, pDecalData, 0 );
// Illuminate decals by the flashlight
if ( bDoShadows && !bSinglePassFlashlight )
{
DrawModelArrayFlashlightDecals( pRenderContext, arrayData.m_pStudioHdr, drawInfo.m_nFlashlightCount, drawInfo.m_pFlashlights, nTotalDecalCount, pDecalData );
}
STUDIORENDER_TEMP_DATA_FREE( pDecalData );
}
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
pRenderContext->SetNumBoneWeights( 0 );
STUDIORENDER_TEMP_DATA_FREE( pRenderData );
}
//-----------------------------------------------------------------------------
// Counts the number of meshes to draw ( shadow rendering )
//-----------------------------------------------------------------------------
int CStudioRender::CountMeshesToDraw( int nCount, StudioArrayData_t *pShadowData, int nInstanceStride )
{
VPROF( "CStudioRender::CountMeshesToDraw (shadow)" );
#ifndef _CERT
bool bCountRenderedFaces = mat_rendered_faces_count.GetBool() || mat_print_top_model_vert_counts.GetBool();
#endif // !_CERT
int nTotalMeshCount = 0;
for ( int i = 0; i < nCount; ++i )
{
StudioArrayData_t &shadow = pShadowData[i];
StudioShadowArrayInstanceData_t *pCurInstance = ( StudioShadowArrayInstanceData_t* )shadow.m_pInstanceData;
for ( int j = 0; j < shadow.m_nCount; ++j, pCurInstance = ( StudioShadowArrayInstanceData_t* )( (uint8*)pCurInstance + nInstanceStride ) )
{
// This subarray has the same skin + body + lod
int nLod = pCurInstance->m_nLOD;
// get the studio mesh data for this lod
studiomeshdata_t *pMeshDataBase = shadow.m_pHardwareData->m_pLODs[nLod].m_pMeshData;
#ifndef _CERT
// Each model counts how many rendered faces it accounts for each frame:
if ( bCountRenderedFaces )
shadow.m_pHardwareData->UpdateFacesRenderedCount( shadow.m_pStudioHdr, m_ModelFaceCountHash, nLod, 1 );
#endif // !_CERT
int nBody = pCurInstance->m_nBody;
for ( int body = 0; body < shadow.m_pStudioHdr->numbodyparts; ++body )
{
mstudiobodyparts_t *pbodypart = shadow.m_pStudioHdr->pBodypart( body );
int index = nBody / pbodypart->base;
index = index % pbodypart->nummodels;
mstudiomodel_t *pSubmodel = pbodypart->pModel( index );
for ( int meshIndex = 0; meshIndex < pSubmodel->nummeshes; ++meshIndex )
{
mstudiomesh_t *pMesh = pSubmodel->pMesh(meshIndex);
studiomeshdata_t *pMeshData = &pMeshDataBase[pMesh->meshid];
nTotalMeshCount += pMeshData->m_NumGroup;
}
}
}
}
return nTotalMeshCount;
}
//-----------------------------------------------------------------------------
// Sets up a depth write material based on an initial material
//-----------------------------------------------------------------------------
void CStudioRender::GetDepthWriteMaterial( IMaterial** ppDepthMaterial, bool *pIsAlphaTested, bool *pUsesTreeSway, IMaterial *pSrcMaterial, bool bIsTranslucentUnderModulation, bool bIsSSAODepthWrite )
{
static unsigned int originalTextureVarCache = 0;
static unsigned int nOriginalTreeSwayVarCache = 0;
IMaterialVar *pOriginalTextureVar = pSrcMaterial->FindVarFast( "$basetexture", &originalTextureVarCache );
IMaterialVar *pOriginalTreeSwayVar = pSrcMaterial->FindVarFast( "$treesway", &nOriginalTreeSwayVarCache );
// Select proper override material
int nAlphaTest = (int) ( ( pSrcMaterial->IsAlphaTested() || bIsTranslucentUnderModulation ) && pOriginalTextureVar->IsTexture() ); // alpha tested base texture
int nNoCull = (int) pSrcMaterial->IsTwoSided();
int nTreeSway = (int) ( pOriginalTreeSwayVar && pOriginalTreeSwayVar->GetIntValue() );
if ( !bIsSSAODepthWrite )
{
*ppDepthMaterial = m_pDepthWrite[ nAlphaTest ][ nNoCull ][ nTreeSway ];
}
else
{
*ppDepthMaterial = m_pSSAODepthWrite[ nAlphaTest ][ nNoCull ][ nTreeSway ];
}
*pIsAlphaTested = ( nAlphaTest != 0 );
*pUsesTreeSway = ( nTreeSway != 0 );
}
//-----------------------------------------------------------------------------
// Sets up a depth write material based on an initial material
//-----------------------------------------------------------------------------
void CStudioRender::SetupAlphaTestedDepthWrite( IMaterial* pDepthMaterial, IMaterial *pSrcMaterial )
{
static unsigned int originalTextureVarCache = 0;
IMaterialVar *pOriginalTextureVar = pSrcMaterial->FindVarFast( "$basetexture", &originalTextureVarCache );
static unsigned int originalTextureFrameVarCache = 0;
IMaterialVar *pOriginalTextureFrameVar = pSrcMaterial->FindVarFast( "$frame", &originalTextureFrameVarCache );
static unsigned int originalAlphaRefCache = 0;
IMaterialVar *pOriginalAlphaRefVar = pSrcMaterial->FindVarFast( "$AlphaTestReference", &originalAlphaRefCache );
static unsigned int textureVarCache = 0;
IMaterialVar *pTextureVar = pDepthMaterial->FindVarFast( "$basetexture", &textureVarCache );
static unsigned int textureFrameVarCache = 0;
IMaterialVar *pTextureFrameVar = pDepthMaterial->FindVarFast( "$frame", &textureFrameVarCache );
static unsigned int alphaRefCache = 0;
IMaterialVar *pAlphaRefVar = pDepthMaterial->FindVarFast( "$AlphaTestReference", &alphaRefCache );
if ( pOriginalTextureVar->IsTexture() ) // If $basetexture is defined
{
if( pTextureVar && pOriginalTextureVar )
{
pTextureVar->SetTextureValue( pOriginalTextureVar->GetTextureValue() );
}
if( pTextureFrameVar && pOriginalTextureFrameVar )
{
pTextureFrameVar->SetIntValue( pOriginalTextureFrameVar->GetIntValue() );
}
if( pAlphaRefVar && pOriginalAlphaRefVar )
{
pAlphaRefVar->SetFloatValue( pOriginalAlphaRefVar->GetFloatValue() );
}
}
}
//-----------------------------------------------------------------------------
// Sets up a depth write material based on an initial material
//-----------------------------------------------------------------------------
void CStudioRender::SetupTreeSwayDepthWrite( IMaterial* pDepthMaterial, IMaterial *pSrcMaterial )
{
static const char* paramNames[14] =
{
"$treeswayheight",
"$treeswaystartheight",
"$treeswayradius",
"$treeswaystartradius",
"$treeswayspeed",
"$treeswayspeedhighwindmultiplier",
"$treeswaystrength",
"$treeswayscrumblespeed",
"$treeswayscrumblestrength",
"$treeswayscrumblefrequency",
"$treeswayfalloffexp",
"$treeswayscrumblefalloffexp",
"$treeswayspeedlerpstart",
"$treeswayspeedlerpend",
};
static unsigned int originalVarCache[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned int varCache[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IMaterialVar *pOriginalVar = NULL;
IMaterialVar *pVar = NULL;
for ( int32 i = 0; i < ARRAYSIZE( paramNames ); i++ )
{
pOriginalVar = pSrcMaterial->FindVarFast( paramNames[i], originalVarCache + i );
pVar = pDepthMaterial->FindVarFast( paramNames[i], varCache + i );
if( pOriginalVar && pVar )
{
pVar->SetFloatValue( pOriginalVar->GetFloatValue() );
}
}
}
//-----------------------------------------------------------------------------
// Sort models function
//-----------------------------------------------------------------------------
inline bool CStudioRender::ShadowSortLessFunc( const ShadowMeshRenderData_t &left, const ShadowMeshRenderData_t &right )
{
if ( left.m_pMaterial != right.m_pMaterial )
return left.m_pMaterial > right.m_pMaterial;
if ( left.m_bIsAlphaTested && right.m_bIsAlphaTested )
{
if ( left.m_pSrcMaterial != right.m_pSrcMaterial )
return left.m_pSrcMaterial > right.m_pSrcMaterial;
}
if ( left.m_bUsesTreeSway && right.m_bUsesTreeSway )
{
if ( left.m_pSrcMaterial != right.m_pSrcMaterial )
return left.m_pSrcMaterial > right.m_pSrcMaterial;
}
if ( left.m_nCompressionType != right.m_nCompressionType )
return left.m_nCompressionType > right.m_nCompressionType;
if ( left.m_nMeshBoneCount != right.m_nMeshBoneCount )
return left.m_nMeshBoneCount > right.m_nMeshBoneCount;
return left.m_pGroup->m_pMesh > right.m_pGroup->m_pMesh;
}
//-----------------------------------------------------------------------------
// BuildShadowRenderList
//-----------------------------------------------------------------------------
int CStudioRender::BuildShadowRenderList( ShadowMeshRenderData_t *pRenderData, int *pTotalStripCount,
int nCount, StudioArrayData_t *pShadowData, int nInstanceStride, int flags )
{
VPROF( "CStudioRender::BuildShadowRenderList" );
*pTotalStripCount = 0;
int nRenderDataCount = 0;
for ( int i = 0; i < nCount; ++i )
{
StudioArrayData_t &shadow = pShadowData[i];
studiohdr_t *pStudioHdr = shadow.m_pStudioHdr;
short *pSkinRefBase = pStudioHdr->pSkinref( 0 );
StudioShadowArrayInstanceData_t *pCurInstance = ( StudioShadowArrayInstanceData_t* )shadow.m_pInstanceData;
for ( int j = 0; j < shadow.m_nCount; ++j, pCurInstance = ( StudioShadowArrayInstanceData_t* )( (uint8*)pCurInstance + nInstanceStride ) )
{
// This subarray has the same skin + body + lod
int nLod = pCurInstance->m_nLOD;
// get the studio mesh data for this lod
studiomeshdata_t *pMeshDataBase = shadow.m_pHardwareData->m_pLODs[nLod].m_pMeshData;
IMaterial **ppMaterials = shadow.m_pHardwareData->m_pLODs[nLod].ppMaterials;
short *pSkinRef = pSkinRefBase;
int skin = pCurInstance->m_nSkin;
if ( skin > 0 && skin < pStudioHdr->numskinfamilies )
{
pSkinRef += ( skin * pStudioHdr->numskinref );
}
int nBody = pCurInstance->m_nBody;
for ( int body = 0; body < pStudioHdr->numbodyparts; ++body )
{
mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( body );
int index = nBody / pbodypart->base;
index = index % pbodypart->nummodels;
mstudiomodel_t *pSubmodel = pbodypart->pModel( index );
for ( int meshIndex = 0; meshIndex < pSubmodel->nummeshes; ++meshIndex )
{
mstudiomesh_t *pMesh = pSubmodel->pMesh(meshIndex);
studiomeshdata_t *pMeshData = &pMeshDataBase[pMesh->meshid];
Assert( pMeshData );
//Assert( pMeshData->m_NumGroup ); // Can't assume that this is non-zero for lods with removed meshes.
IMaterial *pMaterial = ppMaterials[ pSkinRef[ pMesh->material ] ];
// Used on cstrike15 to efficiently render translucent renderables into csm shadow buffers
if ( ( flags & STUDIORENDER_SHADOWDEPTHTEXTURE_INCLUDE_TRANSLUCENT_MATERIALS ) == 0 )
{
// Bail if the material is still considered translucent after setting the AlphaModulate to 1.0
if ( pMaterial->IsTranslucentUnderModulation() )
continue;
}
IMaterial *pDepthMaterial;
bool bIsAlphaTested = false;
bool bUsesTreeSway = false;
GetDepthWriteMaterial( &pDepthMaterial, &bIsAlphaTested, &bUsesTreeSway, pMaterial, pMaterial->IsTranslucentUnderModulation(), ( flags & STUDIORENDER_SSAODEPTHTEXTURE ) ? true : false );
for ( int g = 0; g < pMeshData->m_NumGroup; ++g )
{
studiomeshgroup_t* pGroup = &pMeshData->m_pMeshGroup[g];
VertexCompressionType_t nCompressionType = CompressionType( pGroup->m_pMesh->GetVertexFormat() );
ShadowMeshRenderData_t &data = pRenderData[nRenderDataCount++];
data.m_pGroup = pGroup;
data.m_pInstance = pCurInstance;
data.m_pMesh = pMesh;
data.m_pMaterial = pDepthMaterial;
data.m_pSrcMaterial = pMaterial;
data.m_nCompressionType = nCompressionType;
data.m_nMeshBoneCount = NumBoneWeights( pGroup->m_pMesh->GetVertexFormat() );
data.m_bIsAlphaTested = bIsAlphaTested;
data.m_bUsesTreeSway = bUsesTreeSway;
*pTotalStripCount += pGroup->m_NumStrips;
}
}
}
}
}
std::make_heap( pRenderData, pRenderData + nRenderDataCount, ShadowSortLessFunc );
std::sort_heap( pRenderData, pRenderData + nRenderDataCount, ShadowSortLessFunc );
return nRenderDataCount;
}
//-----------------------------------------------------------------------------
// Draws shadow meshes
//-----------------------------------------------------------------------------
void CStudioRender::DrawShadowMeshRenderData( IMatRenderContext *pRenderContext,
int nCount, ShadowMeshRenderData_t *pRenderData, int nTotalStripCount )
{
VPROF( "CStudioRender::DrawShadowMeshRenderData" );
int nInstanceCount = 0;
STUDIORENDER_TEMP_DATA_MALLOC( MeshInstanceData_t, pInstance, nTotalStripCount * sizeof(MeshInstanceData_t) );
int nMaxBoneCount = 0;
int nLastMeshBoneCount = 0;
IMaterial *pLastMaterial = NULL;
IMaterial *pLastSrcMaterial = NULL;
bool bIsAlphaTested = false;
bool bUsesTreeSway = false;
VertexCompressionType_t nLastCompressionType = VERTEX_COMPRESSION_INVALID;
for ( int i = 0; i < nCount; ++i )
{
ShadowMeshRenderData_t &data = pRenderData[i];
StudioShadowArrayInstanceData_t *pCurrInstance = data.m_pInstance;
if ( ( pLastMaterial != data.m_pMaterial ) || // shadow material is different
( bIsAlphaTested && ( pLastSrcMaterial != data.m_pSrcMaterial ) ) || // alpha channel is different
( bUsesTreeSway && ( pLastSrcMaterial != data.m_pSrcMaterial ) ) || // tree sway params are different
( nLastCompressionType != data.m_nCompressionType ) || // compression type is different
( nLastMeshBoneCount != data.m_nMeshBoneCount ) ) // # of bones in the mesh data is different
{
if ( nInstanceCount > 0 )
{
if ( bIsAlphaTested )
{
SetupAlphaTestedDepthWrite( pLastMaterial, pLastSrcMaterial );
}
if ( bUsesTreeSway )
{
SetupTreeSwayDepthWrite( pLastMaterial, pLastSrcMaterial );
}
pRenderContext->SetNumBoneWeights( nLastMeshBoneCount > 0 ? nMaxBoneCount : 0 );
pRenderContext->Bind( pLastMaterial, NULL );
pRenderContext->DrawInstances( nInstanceCount, pInstance );
}
nInstanceCount = 0;
nMaxBoneCount = 0;
nLastCompressionType = data.m_nCompressionType;
pLastMaterial = data.m_pMaterial;
pLastSrcMaterial = data.m_pSrcMaterial;
nLastMeshBoneCount = data.m_nMeshBoneCount;
bIsAlphaTested = data.m_bIsAlphaTested;
bUsesTreeSway = data.m_bUsesTreeSway;
}
studiomeshgroup_t* pGroup = data.m_pGroup;
for ( int j = 0; j < pGroup->m_NumStrips; ++j )
{
OptimizedModel::StripHeader_t* pStrip = &pGroup->m_pStripData[j];
Assert( nInstanceCount < nTotalStripCount );
MeshInstanceData_t &instance = pInstance[nInstanceCount++];
instance.m_pEnvCubemap = NULL;
instance.m_pPoseToWorld = pCurrInstance->m_pPoseToWorld;
instance.m_pLightingState = NULL;
instance.m_nBoneCount = pStrip->numBoneStateChanges;
instance.m_pBoneRemap = ( instance.m_nBoneCount > 0 ) ? (MeshBoneRemap_t*)( pStrip->pBoneStateChange(0) ) : NULL;
instance.m_nIndexOffset = pStrip->indexOffset;
instance.m_nIndexCount = pStrip->numIndices;
instance.m_nPrimType = MATERIAL_TRIANGLES;
instance.m_pColorBuffer = NULL;
instance.m_nColorVertexOffsetInBytes = 0;
instance.m_pStencilState = NULL;
instance.m_pVertexBuffer = pGroup->m_pMesh;
instance.m_pIndexBuffer = pGroup->m_pMesh;
instance.m_nVertexOffsetInBytes = 0;
instance.m_DiffuseModulation.Init( 1.0f, 1.0f, 1.0f, 1.0f );
instance.m_nLightmapPageId = MATERIAL_SYSTEM_LIGHTMAP_PAGE_INVALID;
instance.m_bColorBufferHasIndirectLightingOnly = false;
nMaxBoneCount = MAX( nMaxBoneCount, pStrip->numBones );
}
}
if ( nInstanceCount > 0 )
{
if ( bIsAlphaTested )
{
SetupAlphaTestedDepthWrite( pLastMaterial, pLastSrcMaterial );
}
if ( bUsesTreeSway )
{
SetupTreeSwayDepthWrite( pLastMaterial, pLastSrcMaterial );
}
pRenderContext->SetNumBoneWeights( nLastMeshBoneCount > 0 ? nMaxBoneCount : 0 );
pRenderContext->Bind( pLastMaterial, NULL );
pRenderContext->DrawInstances( nInstanceCount, pInstance );
}
pRenderContext->SetNumBoneWeights( 0 );
STUDIORENDER_TEMP_DATA_FREE( pInstance );
}
//-----------------------------------------------------------------------------
// Draws all models to the shadow depth buffer
//-----------------------------------------------------------------------------
void CStudioRender::DrawModelShadowArray( const StudioRenderContext_t &rc, int nCount, StudioArrayData_t *pShadowData, int nInstanceStride, int flags )
{
m_pRC = const_cast< StudioRenderContext_t* >( &rc );
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
// Preserve the matrices if we're skinning
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
// Count number of meshes to draw
int nTotalMeshCount = CountMeshesToDraw( nCount, pShadowData, nInstanceStride );
// Build list of meshes to render
int nTotalStripCount;
STUDIORENDER_TEMP_DATA_MALLOC( ShadowMeshRenderData_t, pRenderData, nTotalMeshCount * sizeof(ShadowMeshRenderData_t) );
nTotalMeshCount = BuildShadowRenderList( pRenderData, &nTotalStripCount, nCount, pShadowData, nInstanceStride, flags );
// Restore meshes, if necessary
RestoreMeshes( nTotalMeshCount, pRenderData, sizeof(ShadowMeshRenderData_t) );
// Draw, baby, draw!
DrawShadowMeshRenderData( pRenderContext, nTotalMeshCount, pRenderData, nTotalStripCount );
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
pRenderContext->SetNumBoneWeights( 0 );
STUDIORENDER_TEMP_DATA_FREE( pRenderData );
}
void CStudioRender::ComputeDiffuseModulation( Vector4D *pDiffuseModulation )
{
if ( ( m_pRC->m_nForcedMaterialType != OVERRIDE_DEPTH_WRITE ) && ( m_pRC->m_nForcedMaterialType != OVERRIDE_SSAO_DEPTH_WRITE ) )
{
pDiffuseModulation->Init( m_pRC->m_ColorMod[0], m_pRC->m_ColorMod[1], m_pRC->m_ColorMod[2], m_pRC->m_AlphaMod );
}
else
{
pDiffuseModulation->Init( 1.0f, 1.0f, 1.0f, 1.0f );
}
}
// this is a fast path that does not support debug modes or transparency (or skeletons obviously)
// pass in an array of instances and draw them
void CStudioRender::DrawModelArrayStaticProp( const DrawModelInfo_t& info,
const StudioRenderContext_t &rc, int nInstanceCount, const MeshInstanceData_t *pInstanceData, ColorMeshInfo_t **pColorMeshes )
{
VPROF( "CStudioRender::DrawModelArrayStaticProp");
m_pRC = const_cast<StudioRenderContext_t*>( &rc );
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
pRenderContext->SetNumBoneWeights( 0 );
pRenderContext->MatrixMode( MATERIAL_MODEL );
// this is needed by R_SetupSkinAndLighting
bool flexConfig = m_pRC->m_Config.bFlex;
m_pRC->m_Config.bFlex = false;
bool bWireframe = m_pRC->m_Config.bWireframe;
m_pRC->m_Config.bWireframe = false;
m_bSkippedMeshes = false;
m_bDrawTranslucentSubModels = false;
int lod = info.m_Lod;
m_pStudioHdr = info.m_pStudioHdr;
m_pStudioMeshes = info.m_pHardwareData->m_pLODs[lod].m_pMeshData;
m_pStudioHWData = info.m_pHardwareData;
#if PIX_ENABLE
char szPIXEventName[128];
sprintf( szPIXEventName, "%s*", m_pStudioHdr->name ); // PIX
PIXEVENT( pRenderContext, szPIXEventName );
#endif
// Build list of submodels
Assert(m_pStudioHdr->numbodyparts==1);
mstudiobodyparts_t *pbodypart = m_pStudioHdr->pBodypart( 0 );
m_pSubModel = pbodypart->pModel( 0 );
// get skinref array
int skin = info.m_Skin;
int *pMaterialFlags = info.m_pHardwareData->m_pLODs[lod].pMaterialFlags;
short *pskinref = m_pStudioHdr->pSkinref( 0 );
if ( skin > 0 && skin < m_pStudioHdr->numskinfamilies )
{
pskinref += ( skin * m_pStudioHdr->numskinref );
}
#ifndef _CERT
int nFacesPerModel = 0;
#endif // !_CERT
// draw each mesh
for ( int i = 0; i < m_pSubModel->nummeshes; ++i)
{
mstudiomesh_t *pmesh = m_pSubModel->pMesh(i);
studiomeshdata_t *pMeshData = &m_pStudioMeshes[pmesh->meshid];
Assert( pMeshData );
if ( !pMeshData->m_NumGroup )
continue;
if ( !pMaterialFlags )
continue;
int materialFlags = pMaterialFlags[pskinref[pmesh->material]];
StudioModelLighting_t lighting = LIGHTING_HARDWARE;
IMaterial* pMaterial = R_StudioSetupSkinAndLighting( pRenderContext, pskinref[ pmesh->material ], info.m_pHardwareData->m_pLODs[lod].ppMaterials, materialFlags, info.m_pClientEntity, info.m_pColorMeshes, lighting );
if ( !pMaterial )
continue;
// if this fails you've got a static prop with an eyeball!
Assert( pmesh->materialtype != 1 );
for ( int j = 0; j < pMeshData->m_NumGroup; ++j )
{
studiomeshgroup_t* pGroup = &pMeshData->m_pMeshGroup[j];
// Needed when we switch back and forth between hardware + software lighting
#ifdef IS_WINDOWS_PC
if ( IsPC() && pGroup->m_MeshNeedsRestore )
{
VertexCompressionType_t compressionType = CompressionType( pGroup->m_pMesh->GetVertexFormat() );
switch ( compressionType )
{
case VERTEX_COMPRESSION_ON:
R_StudioRestoreMesh<VERTEX_COMPRESSION_ON>( pmesh, pGroup );
case VERTEX_COMPRESSION_NONE:
default:
R_StudioRestoreMesh<VERTEX_COMPRESSION_NONE>( pmesh, pGroup );
break;
}
pGroup->m_MeshNeedsRestore = false;
}
#endif
IMesh *pMesh = pGroup->m_pMesh;
for ( int k = 0; k < nInstanceCount; k++ )
{
const MeshInstanceData_t &instance = pInstanceData[k];
if ( instance.m_pEnvCubemap )
{
pRenderContext->BindLocalCubemap( const_cast<ITexture *>( instance.m_pEnvCubemap ) );
}
if ( pColorMeshes[k] )
{
pMesh->SetColorMesh( pColorMeshes[k][pGroup->m_ColorMeshID].m_pMesh, pColorMeshes[k][pGroup->m_ColorMeshID].m_nVertOffsetInBytes );
}
else
{
pMesh->SetColorMesh( NULL, 0 );
}
pRenderContext->LoadMatrix( *instance.m_pPoseToWorld );
for ( int strip = 0; strip < pGroup->m_NumStrips; ++strip )
{
OptimizedModel::StripHeader_t* pStrip = &pGroup->m_pStripData[strip];
pMesh->SetPrimitiveType( GetPrimitiveTypeForStripHeaderFlags( pStrip->flags ) );
pMesh->DrawModulated( instance.m_DiffuseModulation, pStrip->indexOffset, pStrip->numIndices );
#ifndef _CERT
// Count # faces per instance for the first instance only
if ( k == 0 )
{
// This code does not work with SubD but we're not really using that anyways
Assert( GetPrimitiveTypeForStripHeaderFlags( pStrip->flags ) == MATERIAL_TRIANGLES );
nFacesPerModel += pStrip->numIndices / 3;
}
#endif // !_CERT
}
}
pMesh->SetColorMesh( NULL, 0 );
}
}
#ifndef _CERT
if ( mat_rendered_faces_count.GetBool() || mat_print_top_model_vert_counts.GetBool() )
{
// Each model counts how many rendered faces it accounts for each frame:
m_pStudioHWData->UpdateFacesRenderedCount( m_pStudioHdr, m_ModelFaceCountHash, lod, nInstanceCount, nFacesPerModel );
}
#endif // !_CERT
// Restore the configs
m_pRC->m_Config.bFlex = flexConfig;
m_pRC->m_Config.bWireframe = bWireframe;
m_pRC = NULL;
m_pStudioHdr = NULL;
m_pStudioMeshes = NULL;
m_pStudioHWData = NULL;
}
#ifndef _CERT
bool FacesRenderedInfoCompareFunc( IStudioRender::FacesRenderedInfo_t const &a, IStudioRender::FacesRenderedInfo_t const &b ) { return a.pStudioHdr == b.pStudioHdr; }
uint32 FacesRenderedInfoKeyFunc( IStudioRender::FacesRenderedInfo_t const &a ) { return HashIntConventional( (int32)(intp)a.pStudioHdr ); }
int FacesRenderedInfoSort( const void *a, const void *b )
{
IStudioRender::FacesRenderedInfo_t *A = (IStudioRender::FacesRenderedInfo_t *)a, *B = (IStudioRender::FacesRenderedInfo_t *)b;
return ( A->nFaceCount > B->nFaceCount ) ? -1 : +1;
}
void UpdateAndSpewFacesRenderedHistory( CUtlVector< IStudioRender::FacesRenderedInfo_t > &newItems, int nTotal, int nSpewFromCurrentFrame, int nSpewFromHistory, bool bClearHistory )
{
static const int NUM_ITEMS_TO_TRACK = 20;
static const int NUM_FRAMES_TO_TRACK = 20;
static IStudioRender::FacesRenderedInfo_t history[ NUM_FRAMES_TO_TRACK ][ NUM_ITEMS_TO_TRACK ];
static int nItems = 0, nOldestItem = 0;
IStudioRender::FacesRenderedInfo_t emptyItem = { NULL, 0 };
if ( bClearHistory )
{
nItems = nOldestItem = 0;
return;
}
if ( !nTotal )
return;
// Record the top 'NUM_ITEMS_TO_TRACK' models for the last rendered frame:
int nCurItem = ( nOldestItem + nItems ) % NUM_FRAMES_TO_TRACK;
if ( nItems == NUM_FRAMES_TO_TRACK )
nOldestItem = ( nOldestItem + 1 ) % NUM_FRAMES_TO_TRACK;
nItems = MIN( ( nItems + 1 ), NUM_FRAMES_TO_TRACK );
for ( int i = 0; i < NUM_ITEMS_TO_TRACK; i++ )
{
history[ nCurItem ][ i ] = ( i < newItems.Count() ) ? newItems[ i ] : emptyItem;
}
nSpewFromCurrentFrame = MIN( nSpewFromCurrentFrame, newItems.Count() );
if ( nSpewFromCurrentFrame )
{
// Spew the top N offenders from this frame to the console
Msg( "Faces rendered this frame, by model:\n" );
for ( int i = 0; i < nSpewFromCurrentFrame; i++ )
{
Msg( "%-7d (%-3d times, %-7d avg) %s\n", newItems[ i ].nFaceCount, newItems[ i ].nRenderCount, ( int )( ( float )newItems[ i ].nFaceCount / ( float )newItems[ i ].nRenderCount ), newItems[ i ].pStudioHdr->name );
}
}
else
{
static float lastSpewTime = 0.0f;
if ( Plat_FloatTime() < ( lastSpewTime + 0.25f ) )
return;
lastSpewTime = Plat_FloatTime();
// Spew the N most expensive models over the last 'NUM_FRAMES_TO_TRACK' frames:
CUtlHash< IStudioRender::FacesRenderedInfo_t > topItemHash( 64, 0, 0, FacesRenderedInfoCompareFunc, FacesRenderedInfoKeyFunc );
for ( int i = 0; i < nItems; i++ )
{
IStudioRender::FacesRenderedInfo_t *items = history[ ( nOldestItem + i ) % NUM_FRAMES_TO_TRACK ];
for ( int j = 0; j < NUM_ITEMS_TO_TRACK; j++ )
{
if ( !items[ j ].nFaceCount )
continue;
UtlHashHandle_t h = topItemHash.Find( items[ j ] );
if ( h != topItemHash.InvalidHandle() )
{
IStudioRender::FacesRenderedInfo_t &topItem = topItemHash[ h ];
topItem.nFaceCount = MAX( items[ j ].nFaceCount, topItem.nFaceCount );
continue;
}
topItemHash.Insert( items[ j ] );
}
}
CUtlVector< IStudioRender::FacesRenderedInfo_t > topItems;
for ( UtlHashHandle_t h = topItemHash.GetFirstHandle(); h != topItemHash.InvalidHandle(); h = topItemHash.GetNextHandle( h ) )
{
topItems.AddToTail( topItemHash[ h ] );
}
qsort( topItems.Base(), topItems.Count(), sizeof( topItems[0] ), FacesRenderedInfoSort );
for ( int j = 0; j < MIN( nSpewFromHistory, NUM_ITEMS_TO_TRACK ); j++ )
{
if ( j < topItems.Count() )
ConMsg( "%-7d (%-3d times, %-7d avg) %s\n", topItems[ j ].nFaceCount, topItems[ j ].nRenderCount, ( int )( ( float )topItems[ j ].nFaceCount / ( float )topItems[ j ].nRenderCount ), topItems[ j ].pStudioHdr->name );
}
ConMsg( "%-7d total model faces rendered this frame (mat_rendered_faces_count)\n", nTotal );
}
}
int BuildFacesRenderedInfoListForMostRecentFrame( CUtlVector< IStudioRender::FacesRenderedInfo_t > &items, CUtlHash< studiohwdata_t * > &hash )
{
int nTotal = 0;
for ( UtlHashHandle_t h = hash.GetFirstHandle(); h != hash.InvalidHandle(); h = hash.GetNextHandle( h ) )
{
studiohwdata_t *pHwData = hash[ h ];
if ( pHwData->m_pStudioHdr )
{
IStudioRender::FacesRenderedInfo_t item = { pHwData->m_pStudioHdr, pHwData->m_NumFacesRenderedThisFrame, pHwData->m_NumTimesRenderedThisFrame };
items.AddToTail( item );
nTotal += pHwData->m_NumFacesRenderedThisFrame;
}
}
// Sort models by face count (biggest first)
qsort( items.Base(), items.Count(), sizeof( items[0] ), FacesRenderedInfoSort );
return nTotal;
}
void CStudioRender::UpdateModelFaceCounts( int nSpewFromCurrentFrame, bool bClearHistory )
{
CUtlVector< IStudioRender::FacesRenderedInfo_t > items;
if ( bClearHistory )
{
UpdateAndSpewFacesRenderedHistory( items, 0, 0, 0, bClearHistory );
}
else if ( mat_rendered_faces_count.GetBool() )
{
int nTotal = BuildFacesRenderedInfoListForMostRecentFrame( items, m_ModelFaceCountHash );
UpdateAndSpewFacesRenderedHistory( items, nTotal, nSpewFromCurrentFrame, mat_rendered_faces_count.GetInt(), false );
mat_rendered_faces_count.SetValue( 0 ); // set back to 0 so we don't spew anymore
}
}
int CStudioRender::GetForcedMaterialOverrideIndex( int nMaterialIndex )
{
if ( m_pRC )
{
for ( int i = 0; i < m_pRC->m_nForcedMaterialIndexCount; i++ )
{
if ( m_pRC->m_nForcedMaterialIndex[ i ] == nMaterialIndex )
{
return i;
}
}
}
return -1;
}
void CStudioRender::GatherRenderedFaceInfo( IStudioRender::FaceInfoCallbackFunc_t pFunc )
{
int nTopN = mat_print_top_model_vert_counts.GetInt();
if ( nTopN )
{
CUtlVector< IStudioRender::FacesRenderedInfo_t > items;
int nTotal = BuildFacesRenderedInfoListForMostRecentFrame( items, m_ModelFaceCountHash );
if ( items.Count() > 0 )
{
nTopN = MIN( nTopN, items.Count());
pFunc( nTopN, items.Base(), nTotal );
}
}
}
CON_COMMAND( mat_rendered_faces_spew, "'mat_rendered_faces_spew <n>' Spew the number of faces rendered for the top N models used this frame (mat_rendered_faces_count must be set to use this)" )
{
int nNumToSpew = ( args.ArgC() > 1 ) ? Q_atoi( args[ 1 ] ) : INT_MAX;
if ( !mat_rendered_faces_count.GetBool() )
{
Msg( "ERROR: mat_rendered_faces_count must be set in order to use mat_rendered_faces_spew\n" );
return;
}
g_StudioRender.UpdateModelFaceCounts( nNumToSpew );
}
#endif // !_CERT