You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1624 lines
62 KiB
1624 lines
62 KiB
//===== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======// |
|
// |
|
// Purpose: |
|
// |
|
// $Revision: $ |
|
// $NoKeywords: $ |
|
// |
|
// Fast path model rendering |
|
// |
|
//===========================================================================// |
|
|
|
#include "cbase.h" |
|
#include "modelrendersystem.h" |
|
#include "model_types.h" |
|
#include "iviewrender.h" |
|
#include "tier3/tier3.h" |
|
#include <algorithm> |
|
#include "tier1/memstack.h" |
|
#include "engine/ivdebugoverlay.h" |
|
#include "shaderapi/ishaderapi.h" |
|
#include "materialsystem/materialsystemutil.h" |
|
#include "tier0/vprof.h" |
|
|
|
// NOTE: This has to be the last file included! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Convars defined by other systems |
|
//----------------------------------------------------------------------------- |
|
ConVar r_lod( "r_lod", "-1" ); |
|
//ConVar r_shadowlod( "r_shadowlod", "-1" ); |
|
ConVar r_drawmodellightorigin( "r_DrawModelLightOrigin", "0", FCVAR_CHEAT ); |
|
extern ConVar g_CV_FlexSmooth; |
|
extern ConVar r_fastzreject; |
|
|
|
//----------------------------------------------------------------------------- |
|
// The client leaf system |
|
//----------------------------------------------------------------------------- |
|
class CModelRenderSystem : public CAutoGameSystem, public IModelRenderSystem |
|
{ |
|
// Methods of IModelRenderSystem |
|
public: |
|
virtual void DrawModels( ModelRenderSystemData_t *pEntities, int nCount, ModelRenderMode_t renderMode ); |
|
virtual void ComputeTranslucentRenderData( ModelRenderSystemData_t *pModels, int nCount, TranslucentInstanceRenderData_t *pRenderData, TranslucentTempData_t *pTempData ); |
|
virtual void CleanupTranslucentTempData( TranslucentTempData_t *pTempData ); |
|
virtual IMaterial *GetFastPathColorMaterial() { return m_DebugMaterial; } |
|
|
|
// Methods of IGameSystem |
|
public: |
|
virtual void LevelInitPostEntity(); |
|
virtual void LevelShutdownPreEntity(); |
|
|
|
// Other public methods |
|
public: |
|
CModelRenderSystem(); |
|
virtual ~CModelRenderSystem(); |
|
|
|
private: |
|
struct ModelListNode_t |
|
{ |
|
ModelRenderSystemData_t m_Entry; |
|
int32 m_nInitialListIndex : 24; |
|
int32 m_bBoneMerge : 1; |
|
int32 m_nLOD : 7; |
|
ShaderStencilState_t *m_pStencilState; |
|
ModelListNode_t *m_pNext; |
|
}; |
|
|
|
struct RenderModelInfo_t : public StudioArrayInstanceData_t |
|
{ |
|
ModelRenderSystemData_t m_Entry; |
|
ModelInstanceHandle_t m_hInstance; |
|
matrix3x4a_t* m_pBoneToWorld; |
|
uint32 m_nInitialListIndex : 24; |
|
uint32 m_bSetupBonesOnly : 1; |
|
uint32 m_bBoneMerge : 1; |
|
}; |
|
|
|
struct ModelListByType_t : public StudioModelArrayInfo_t |
|
{ |
|
RenderableLightingModel_t m_nLightingModel; |
|
const model_t *m_pModel; |
|
ModelListNode_t *m_pFirstNode; |
|
int m_nCount; |
|
int m_nSetupBoneCount; |
|
uint32 m_nParentDepth : 31; |
|
uint32 m_bWantsStencil : 1; |
|
RenderModelInfo_t *m_pRenderModels; |
|
ModelListByType_t *m_pNextLightingModel; |
|
|
|
// speed up std::sort by implementing these |
|
ModelListByType_t &operator=( const ModelListByType_t &rhs ) |
|
{ |
|
memcpy( this, &rhs, sizeof( ModelListByType_t ) ); |
|
return *this; |
|
} |
|
|
|
ModelListByType_t() {} |
|
|
|
ModelListByType_t( const ModelListByType_t &rhs ) |
|
{ |
|
memcpy( this, &rhs, sizeof( ModelListByType_t ) ); |
|
} |
|
}; |
|
|
|
struct LightingList_t |
|
{ |
|
ModelListByType_t *m_pFirstModel; |
|
int m_nCount; |
|
int m_nTotalModelCount; |
|
}; |
|
|
|
private: |
|
int BucketModelsByMDL( ModelListByType_t *pModelList, ModelListNode_t *pModelListNodes, ModelRenderSystemData_t *pEntities, int nCount, ModelRenderMode_t renderMode, int *pModelsRenderingStencilCountOut ); |
|
bool AddModelToLists( int &nModelTypeCount, ModelListByType_t *pModelList, int &nModelNodeCount, ModelListNode_t *pModelListNodes, int nDataIndex, ModelRenderSystemData_t &data, ModelRenderMode_t renderMode ); |
|
void SortBucketsByDependency( int nModelTypeCount, ModelListByType_t *pModelList, LightingList_t *pLightingList ); |
|
void ComputeModelLODs( int nModelTypeCount, ModelListByType_t *pModelList, ModelListNode_t *pModelListNode, ModelRenderMode_t renderMode ); |
|
void SlamModelLODs( int nLOD, int nModelTypeCount, ModelListByType_t *pModelList, ModelListNode_t *pModelListNode ); |
|
void SortModels( RenderModelInfo_t *pSortedModelListNode, int nModelTypeCount, ModelListByType_t *pModelList, ModelListNode_t *pModelListNode ); |
|
static bool SortLessFunc( const RenderModelInfo_t &left, const RenderModelInfo_t &right ); |
|
void SetupBones( int nModelTypeCount, ModelListByType_t *pModelList ); |
|
void SetupFlexes( int nModelTypeCount, ModelListByType_t *pModelList ); |
|
void ComputeLightingOrigin( ModelListByType_t &list, LightingQuery_t *pLightingQuery, int nQueryStride ); |
|
int SetupLighting( LightingList_t *pLightingList, int nModelTypeCount, ModelListByType_t *pModelList, DataCacheHandle_t *pColorMeshHandles, ModelRenderMode_t renderMode ); |
|
void RenderModels( StudioModelArrayInfo2_t *pInfo, int nModelTypeCount, ModelListByType_t *pModelList, int nTotalModelCount, ModelRenderMode_t renderMode ); |
|
void SetupTranslucentData( int nModelTypeCount, ModelListByType_t *pModelList, int nTotalModelCount, TranslucentInstanceRenderData_t *pRenderData ); |
|
void SetupFlashlightsAndDecals( StudioModelArrayInfo2_t *pInfo, int nModelTypeCount, ModelListByType_t *pModelList, int nTotalModelCount, RenderModelInfo_t *pModelInfo, ModelRenderMode_t renderMode ); |
|
void SetupPerInstanceColorModulation( int nModelTypeCount, ModelListByType_t *pModelList ); |
|
void DebugDrawLightingOrigin( const ModelListByType_t &list, const RenderModelInfo_t &model ); |
|
int BuildLightingList( ModelListByType_t **ppLists, unsigned char *pFlags, int *pTotalModels, const LightingList_t &lightingList ); |
|
int SetupStaticPropLighting( LightingList_t &lightingList, DataCacheHandle_t *pColorMeshHandles ); |
|
void SetupStandardLighting( LightingList_t &lightingList ); |
|
int SetupPhysicsPropLighting( LightingList_t &lightingList, DataCacheHandle_t *pColorMeshHandles ); |
|
|
|
|
|
void HookUpStaticLightingState( int nCount, ModelListByType_t **ppLists, unsigned char *pFlags, ITexture **ppEnvCubemap, MaterialLightingState_t *pLightingState, MaterialLightingState_t *pDecalLightingState, ColorMeshInfo_t **ppColorMeshInfo ); |
|
void RenderDebugOverlays( int nModelTypeCount, ModelListByType_t *pModelList, ModelRenderMode_t renderMode ); |
|
void RenderVCollideDebugOverlay( int nModelTypeCount, ModelListByType_t *pModelList ); |
|
void RenderBBoxDebugOverlay( int nModelTypeCount, ModelListByType_t *pModelList ); |
|
int ComputeParentDepth( C_BaseEntity *pEnt ); |
|
static bool DependencySortLessFunc( const ModelListByType_t &left, const ModelListByType_t &right ); |
|
static bool StencilSortLessFunc( const ModelListByType_t &left, const ModelListByType_t &right ); |
|
|
|
CMemoryStack m_BoneToWorld; |
|
CTextureReference m_DefaultCubemap; |
|
CMaterialReference m_DebugMaterial; |
|
CMaterialReference m_ShadowBuild; |
|
IMatRenderContext *m_pRenderContext; |
|
|
|
CUtlMemoryFixedGrowable< DataCacheHandle_t, 1024 > m_ColorMeshHandles; |
|
CUtlMemoryFixedGrowable< ModelListByType_t, 128 > m_ModelList; |
|
CUtlMemoryFixedGrowable< ModelListNode_t, 1024 > m_ModelListNode; |
|
CUtlMemoryFixedGrowable< RenderModelInfo_t, 1024 > m_RenderModelInfo; |
|
int m_nColorMeshHandles; |
|
int m_nModelTypeCount; |
|
int m_nTotalModelCount; |
|
bool m_bShadowDepth; |
|
bool m_bHasInstanceData; |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton accessor |
|
//----------------------------------------------------------------------------- |
|
static CModelRenderSystem s_ModelRenderSystem; |
|
extern IModelRenderSystem *g_pModelRenderSystem = &s_ModelRenderSystem; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
CModelRenderSystem::CModelRenderSystem() |
|
{ |
|
m_bHasInstanceData = false; |
|
m_BoneToWorld.Init( 1 * 1024 * 1024, 32 * 1024, 0, 32 ); |
|
} |
|
|
|
CModelRenderSystem::~CModelRenderSystem() |
|
{ |
|
m_BoneToWorld.Term(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Level init, shutdown |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::LevelInitPostEntity() |
|
{ |
|
m_DefaultCubemap.Init( "engine/defaultcubemap", TEXTURE_GROUP_CUBE_MAP ); |
|
m_DebugMaterial.Init( "debug/debugempty", TEXTURE_GROUP_OTHER ); |
|
m_ShadowBuild.Init( "engine/shadowbuild", TEXTURE_GROUP_OTHER ); |
|
} |
|
|
|
void CModelRenderSystem::LevelShutdownPreEntity() |
|
{ |
|
m_DefaultCubemap.Shutdown(); |
|
m_DebugMaterial.Shutdown(); |
|
m_ShadowBuild.Shutdown(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// returns bone setup dependency depth |
|
//----------------------------------------------------------------------------- |
|
int CModelRenderSystem::ComputeParentDepth( C_BaseEntity *pEnt ) |
|
{ |
|
if ( !pEnt ) |
|
return 0; |
|
|
|
int nDepth = 0; |
|
while ( pEnt->IsFollowingEntity() || ( pEnt->GetMoveParent() && pEnt->GetParentAttachment() > 0 ) ) |
|
{ |
|
++nDepth; |
|
pEnt = pEnt->GetMoveParent(); |
|
} |
|
|
|
return nDepth; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Adds a model to the appropriate render lists |
|
//----------------------------------------------------------------------------- |
|
bool CModelRenderSystem::AddModelToLists( int &nModelTypeCount, ModelListByType_t *pModelList, |
|
int &nModelNodeCount, ModelListNode_t *pModelListNodes, int nInitialListIndex, ModelRenderSystemData_t &data, ModelRenderMode_t renderMode ) |
|
{ |
|
// NOTE: we actually are bucketing both by model + also by lighting model |
|
// Bucketing by lighting model is not strictly necessary, but doing so |
|
// simplifies the code a lot in exchange for having two batches if the |
|
// same model is used but a different lighting model, something that could |
|
// theoretically happen if a static prop + physics prop use the same .mdl |
|
// My thought is that even if this split happens, it will be rare, and |
|
// we still will get a lot of sharing. |
|
|
|
// L4D2: We'll also bucket by whether the model renders stencil or not. |
|
// This allows us to keep stenciling models in the fastpath but group them together |
|
// to be rendered AFTER the 360 Z prepass ends. (360 doesn't allow stencil rendering |
|
// during the Z prepass). |
|
|
|
const model_t *pModel = data.m_pRenderable->GetModel(); |
|
Assert( modelinfo->GetModelType( pModel ) == mod_studio ); |
|
|
|
RenderableLightingModel_t nLightingModel = LIGHTING_MODEL_NONE; |
|
uint bWantsStencil = 0; |
|
ShaderStencilState_t tempStencil; |
|
if ( data.m_pModelRenderable ) |
|
{ |
|
data.m_pModelRenderable->GetRenderData( &nLightingModel, MODEL_DATA_LIGHTING_MODEL ); |
|
if ( renderMode == MODEL_RENDER_MODE_NORMAL ) |
|
{ |
|
// I considered making a MODEL_DATA_STENCIL_ENABLE renderdata type that would only return a bool |
|
// if stencil was enabled, but it turns out most of the work for MODEL_DATA_STENCIL is computing |
|
// that bool, so pulling this out into a separate piece didn't turn out to be a perf win. |
|
bWantsStencil = data.m_pModelRenderable->GetRenderData( &tempStencil, MODEL_DATA_STENCIL ) ? 1 : 0; |
|
} |
|
} |
|
else |
|
{ |
|
ExecuteOnce( DevWarning( "data.m_pModelRenderable is NULL for %s\n", modelinfo->GetModelName( pModel ) ) ); |
|
} |
|
|
|
bool bRetVal = bWantsStencil ? true : false; |
|
|
|
int j; |
|
for ( j = 0; j < nModelTypeCount; ++j ) |
|
{ |
|
if ( pModelList[j].m_pModel == pModel && |
|
pModelList[j].m_nLightingModel == nLightingModel && |
|
pModelList[j].m_bWantsStencil == bWantsStencil ) |
|
break; |
|
} |
|
|
|
if ( j == nModelTypeCount ) |
|
{ |
|
// Bail if we're rendering into shadow depth map and this model doesn't cast shadows |
|
// NOTE: if m_pModelRenderable is NULL, it's a dependent bone setup so we need to keep it |
|
studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( pModel ); |
|
if ( ( renderMode != MODEL_RENDER_MODE_NORMAL ) && data.m_pModelRenderable && ( ( pStudioHdr->flags & STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS ) != 0 ) ) |
|
{ |
|
return bRetVal; |
|
} |
|
|
|
MDLHandle_t hMDL = modelinfo->GetCacheHandle( pModel ); |
|
studiohwdata_t *pHardwareData = g_pMDLCache->GetHardwareData( hMDL ); |
|
|
|
// This can occur if there was an error loading the model; for instance |
|
// if the vtx and mdl are out of sync. |
|
if ( !pHardwareData || !pHardwareData->m_pLODs ) |
|
{ |
|
AssertMsg( 0, UTIL_VarArgs( "%s failed to load and is causing EVIL in the model render system!!", pStudioHdr->name ) ); |
|
return bRetVal; |
|
} |
|
|
|
ModelListByType_t &list = pModelList[ nModelTypeCount ]; |
|
list.m_pModel = pModel; |
|
list.m_nLightingModel = nLightingModel; |
|
list.m_bWantsStencil = bWantsStencil; |
|
list.m_pStudioHdr = pStudioHdr; |
|
list.m_pHardwareData = pHardwareData; |
|
list.m_nFlashlightCount = 0; |
|
list.m_pFlashlights = NULL; |
|
list.m_nCount = 0; |
|
list.m_pFirstNode = 0; |
|
list.m_pRenderModels = 0; |
|
list.m_nParentDepth = 0; |
|
list.m_pNextLightingModel = NULL; |
|
j = nModelTypeCount++; |
|
} |
|
|
|
C_BaseEntity *pEntity = data.m_pRenderable->GetIClientUnknown()->GetBaseEntity(); |
|
uint nParentDepth = ComputeParentDepth( pEntity ); |
|
|
|
ModelListByType_t &list = pModelList[ j ]; |
|
ModelListNode_t &node = pModelListNodes[ nModelNodeCount++ ]; |
|
node.m_Entry = data; |
|
node.m_nInitialListIndex = nInitialListIndex; |
|
node.m_bBoneMerge = pEntity && pEntity->IsEffectActive( EF_BONEMERGE ); |
|
|
|
if ( bWantsStencil && ( renderMode == MODEL_RENDER_MODE_NORMAL ) ) |
|
{ |
|
CMatRenderData< ShaderStencilState_t > rdStencil( m_pRenderContext, 1 ); |
|
memcpy( &rdStencil[0], &tempStencil, sizeof( tempStencil ) ); |
|
node.m_pStencilState = &rdStencil[0]; |
|
} |
|
else |
|
{ |
|
node.m_pStencilState = NULL; |
|
} |
|
|
|
node.m_pNext = list.m_pFirstNode; |
|
list.m_nParentDepth = MAX( list.m_nParentDepth, nParentDepth ); |
|
list.m_pFirstNode = &node; |
|
++list.m_nCount; |
|
|
|
return bRetVal; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// bucket models by type, return # of unique types |
|
//----------------------------------------------------------------------------- |
|
int CModelRenderSystem::BucketModelsByMDL( ModelListByType_t *pModelList, ModelListNode_t *pModelListNodes, ModelRenderSystemData_t *pEntities, int nCount, ModelRenderMode_t renderMode, |
|
int *pModelsRenderingStencilCountOut ) |
|
{ |
|
int nModelTypeCount = 0; |
|
int nModelNodeCount = 0; |
|
*pModelsRenderingStencilCountOut = 0; |
|
for ( int i = 0; i < nCount; ++i ) |
|
{ |
|
bool bModelWantsStencil = AddModelToLists( nModelTypeCount, pModelList, nModelNodeCount, pModelListNodes, i, pEntities[i], renderMode ); |
|
*pModelsRenderingStencilCountOut += bModelWantsStencil ? 1 : 0; |
|
} |
|
return nModelTypeCount; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sort model types function |
|
//----------------------------------------------------------------------------- |
|
inline bool CModelRenderSystem::DependencySortLessFunc( const ModelListByType_t &left, const ModelListByType_t &right ) |
|
{ |
|
// Ensures bone setup occurs in the correct order |
|
if ( left.m_nParentDepth != right.m_nParentDepth ) |
|
return left.m_nParentDepth < right.m_nParentDepth; |
|
|
|
// Ensure stenciling models are at the end of the list. |
|
// This doesn't guarantee that stencil stuff is at the end because parent depth trumps it, |
|
// so we'll have to sort again before rendering. |
|
if ( left.m_bWantsStencil != right.m_bWantsStencil ) |
|
{ |
|
return left.m_bWantsStencil < right.m_bWantsStencil; |
|
} |
|
|
|
// Keep same models with different lighting types together |
|
return left.m_pModel < right.m_pModel; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sorts so that bone setup occurs in the appropriate order (parents set up first) |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::SortBucketsByDependency( int nModelTypeCount, ModelListByType_t *pModelList, LightingList_t *pLightingList ) |
|
{ |
|
std::sort( pModelList, pModelList + nModelTypeCount, DependencySortLessFunc ); |
|
|
|
// Assign models to the appropriate lighting list |
|
for ( int i = nModelTypeCount; --i >= 0; ) |
|
{ |
|
ModelListByType_t &list = pModelList[ i ]; |
|
|
|
// Hook into lighting list |
|
if ( list.m_nLightingModel == LIGHTING_MODEL_NONE ) |
|
continue; |
|
|
|
LightingList_t &lightList = pLightingList[ list.m_nLightingModel ]; |
|
list.m_pNextLightingModel = lightList.m_pFirstModel; |
|
lightList.m_pFirstModel = &list; |
|
++lightList.m_nCount; |
|
lightList.m_nTotalModelCount += list.m_nCount; |
|
} |
|
|
|
#ifdef _DEBUG |
|
// Don't want to allow some MDLs of type A to depend on MDLs of type B |
|
// and other MDLS of type B to depend on type A because that would |
|
// dramatically increase complexity of the system here. With this assumption, |
|
// we can always have all models of the same type be set up at the same time, |
|
// also improving cache efficiency. |
|
for ( int i =0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[ i ]; |
|
if ( list.m_nParentDepth == 0 ) |
|
continue; |
|
|
|
for( ModelListNode_t *pNode = list.m_pFirstNode; pNode; pNode = pNode->m_pNext ) |
|
{ |
|
C_BaseEntity *pEnt = pNode->m_Entry.m_pRenderable->GetIClientUnknown()->GetBaseEntity(); |
|
if ( !pEnt ) |
|
continue; |
|
|
|
C_BaseEntity *pTest = pEnt; |
|
while ( pEnt->IsFollowingEntity() || ( pEnt->GetMoveParent() && pEnt->GetParentAttachment() > 0 ) ) |
|
{ |
|
pEnt = pEnt->GetMoveParent(); |
|
const model_t *pModel = pEnt->GetModel(); |
|
|
|
bool bFound = false; |
|
for ( int j = 0; j < nModelTypeCount; ++j ) |
|
{ |
|
if ( pModelList[j].m_pModel != pModel ) |
|
continue; |
|
|
|
if ( pModelList[j].m_nParentDepth >= list.m_nParentDepth ) |
|
{ |
|
// NOTE: GetClassname() stores the name in a global, hence need to do the warning on 2 lines |
|
Warning( "Bone setup dependency ordering issue [ent %s ", pTest->GetClassname() ); |
|
Warning( " depends on ent %s]!\n", pEnt->GetClassname() ); |
|
} |
|
|
|
for( ModelListNode_t *pParentNode = pModelList[j].m_pFirstNode; pParentNode; pParentNode = pParentNode->m_pNext ) |
|
{ |
|
if ( pParentNode->m_Entry.m_pRenderable == pEnt->GetClientRenderable() ) |
|
{ |
|
bFound = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if ( !bFound ) |
|
{ |
|
// NOTE: GetClassname() stores the name in a global, hence need to do the warning on 2 lines |
|
// Warning( "Missing bone setup dependency [ent %s ", pTest->GetClassname() ); |
|
// Warning( "depends on ent %s]!\n", pEnt->GetClassname() ); |
|
} |
|
} |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Slam model LODs to the appropriate level |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::SlamModelLODs( int nLOD, int nModelTypeCount, ModelListByType_t *pModelList, ModelListNode_t *pModelListNode ) |
|
{ |
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
int nLODCount = list.m_pHardwareData->m_NumLODs; |
|
int nRootLOD = list.m_pHardwareData->m_RootLOD; |
|
bool bHasShadowLOD = ( list.m_pStudioHdr->flags & STUDIOHDR_FLAGS_HASSHADOWLOD ) != 0; |
|
int nMaxLOD = bHasShadowLOD ? nLODCount - 2 : nLODCount - 1; |
|
|
|
for ( ModelListNode_t *pNode = list.m_pFirstNode; pNode; pNode = pNode->m_pNext ) |
|
{ |
|
pNode->m_nLOD = clamp( nLOD, nRootLOD, nMaxLOD ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Compute model LODs |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::ComputeModelLODs( int nModelTypeCount, ModelListByType_t *pModelList, ModelListNode_t *pModelListNode, ModelRenderMode_t renderMode ) |
|
{ |
|
if ( renderMode == MODEL_RENDER_MODE_RTT_SHADOWS ) |
|
{ |
|
// Slam to shadow lod |
|
//int nShadowLodConVar = r_shadowlod.GetInt(); |
|
|
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
int nLODCount = list.m_pHardwareData->m_NumLODs; |
|
//int nRootLOD = list.m_pHardwareData->m_RootLOD; |
|
int nMaxLOD = nLODCount - 1; |
|
|
|
for ( ModelListNode_t *pNode = list.m_pFirstNode; pNode; pNode = pNode->m_pNext ) |
|
{ |
|
// Just always use the lowest LOD right now |
|
//int nLOD = nShadowLodConVar; |
|
//pNode->m_nLOD = clamp( nLOD, nRootLOD, nMaxLOD ); |
|
pNode->m_nLOD = nMaxLOD; |
|
} |
|
} |
|
return; |
|
} |
|
|
|
int nLOD = r_lod.GetInt(); |
|
if ( nLOD >= 0 ) |
|
{ |
|
SlamModelLODs( nLOD, nModelTypeCount, pModelList, pModelListNode ); |
|
return; |
|
} |
|
|
|
ScreenSizeComputeInfo_t info; |
|
ComputeScreenSizeInfo( &info ); |
|
|
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
int nLODCount = list.m_pHardwareData->m_NumLODs; |
|
int nRootLOD = list.m_pHardwareData->m_RootLOD; |
|
int nMaxLOD = nLODCount - 1; |
|
|
|
for ( ModelListNode_t *pNode = list.m_pFirstNode; pNode; pNode = pNode->m_pNext ) |
|
{ |
|
// FIXME: SIMD-ize, eliminate all extraneous calls (get view render state outside of loop) |
|
const Vector &vecRenderOrigin = pNode->m_Entry.m_pRenderable->GetRenderOrigin(); |
|
|
|
// NOTE: The 2.0 is for legacy reasons |
|
float flScreenSize = 2.0f * ComputeScreenSize( vecRenderOrigin, 0.5f, info ); |
|
|
|
float flMetric = list.m_pHardwareData->LODMetric( flScreenSize ); |
|
nLOD = list.m_pHardwareData->GetLODForMetric( flMetric ); |
|
pNode->m_nLOD = clamp( nLOD, nRootLOD, nMaxLOD ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sort models function |
|
//----------------------------------------------------------------------------- |
|
inline bool CModelRenderSystem::SortLessFunc( const RenderModelInfo_t &left, const RenderModelInfo_t &right ) |
|
{ |
|
// NOTE: Could do this, but it is not faster, because the cost of an integer multiply is about three |
|
// times that of a branch penalty: |
|
// int nLeft = left.m_nSkin * 1000000 + left.m_nLOD * 1000 + left.m_nBody; |
|
// int nRight = right.m_nSkin * 1000000 + right.m_nLOD * 1000 + right.m_nBody; |
|
// return nLeft > nRight; |
|
if ( left.m_bSetupBonesOnly != right.m_bSetupBonesOnly ) |
|
return !left.m_bSetupBonesOnly; |
|
if ( left.m_nSkin != right.m_nSkin ) |
|
return left.m_nSkin > right.m_nSkin; |
|
if ( left.m_nLOD != right.m_nLOD ) |
|
return left.m_nLOD > right.m_nLOD; |
|
return left.m_nBody > right.m_nBody; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sort models |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::SortModels( RenderModelInfo_t *pRenderModelInfo, |
|
int nModelTypeCount, ModelListByType_t *pModelList, ModelListNode_t *pModelListNode ) |
|
{ |
|
// First place them in arrays |
|
RenderModelInfo_t *pCurrInfo = pRenderModelInfo; |
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
list.m_pRenderModels = pCurrInfo; |
|
list.m_nSetupBoneCount = 0; |
|
for ( ModelListNode_t *pNode = list.m_pFirstNode; pNode; pNode = pNode->m_pNext ) |
|
{ |
|
memset( pCurrInfo, 0, sizeof( RenderModelInfo_t ) ); |
|
pCurrInfo->m_Entry = pNode->m_Entry; |
|
pCurrInfo->m_nLOD = pNode->m_nLOD; |
|
pCurrInfo->m_nSkin = pNode->m_Entry.m_pRenderable->GetSkin(); |
|
pCurrInfo->m_nBody = pNode->m_Entry.m_pRenderable->GetBody(); |
|
pCurrInfo->m_hInstance = pNode->m_Entry.m_pRenderable->GetModelInstance(); |
|
pCurrInfo->m_Decals = STUDIORENDER_DECAL_INVALID; |
|
pCurrInfo->m_nInitialListIndex = pNode->m_nInitialListIndex; |
|
pCurrInfo->m_bBoneMerge = pNode->m_bBoneMerge; |
|
pCurrInfo->m_bSetupBonesOnly = ( pNode->m_Entry.m_pModelRenderable == NULL ); |
|
pCurrInfo->m_pStencilState = pNode->m_pStencilState; |
|
list.m_nSetupBoneCount += pCurrInfo->m_bSetupBonesOnly; |
|
++pCurrInfo; |
|
} |
|
|
|
// Sort within this model type. skin first, then LOD, then body. |
|
Assert( pCurrInfo - list.m_pRenderModels == list.m_nCount ); |
|
std::sort( list.m_pRenderModels, list.m_pRenderModels + list.m_nCount, SortLessFunc ); |
|
|
|
list.m_nCount -= list.m_nSetupBoneCount; |
|
list.m_nSetupBoneCount += list.m_nCount; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets up bones on all models |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::SetupBones( int nModelTypeCount, ModelListByType_t *pModelList ) |
|
{ |
|
// FIXME: Can we make parallel bone setup faster? Yes, we can! |
|
const float flCurTime = gpGlobals->curtime; |
|
matrix3x4a_t pPoseToBone[MAXSTUDIOBONES]; |
|
|
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
const int nBoneCount = list.m_pStudioHdr->numbones; |
|
|
|
// Force setup of attachments if we're going to use an illumposition |
|
const int nAttachmentMask = ( list.m_pStudioHdr->IllumPositionAttachmentIndex() > 0 ) ? BONE_USED_BY_ATTACHMENT : 0; |
|
for ( int j = 0; j < list.m_nSetupBoneCount; ++j ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
const int nBoneMask = BONE_USED_BY_VERTEX_AT_LOD( pModel->m_nLOD ) | nAttachmentMask; |
|
pModel->m_pBoneToWorld = (matrix3x4a_t*)m_BoneToWorld.Alloc( nBoneCount * sizeof(matrix3x4a_t) ); |
|
const bool bOk = pModel->m_Entry.m_pRenderable->SetupBones( pModel->m_pBoneToWorld, nBoneCount, nBoneMask, flCurTime ); |
|
if ( !bOk ) |
|
{ |
|
for ( int k = 0; k < nBoneCount; ++k) |
|
{ |
|
SetIdentityMatrix( pModel->m_pBoneToWorld[k] ); |
|
} |
|
} |
|
} |
|
|
|
if ( list.m_nCount == 0 ) |
|
continue; |
|
|
|
// Get the pose to bone for the model |
|
if ( !list.m_pStudioHdr->pLinearBones() ) |
|
{ |
|
// convert bone to world transformations into pose to world transformations |
|
for (int k = 0; k < nBoneCount; k++) |
|
{ |
|
mstudiobone_t *pCurBone = list.m_pStudioHdr->pBone( k ); |
|
MatrixCopy( pCurBone->poseToBone, pPoseToBone[k] ); |
|
} |
|
} |
|
else |
|
{ |
|
mstudiolinearbone_t *pLinearBones = list.m_pStudioHdr->pLinearBones(); |
|
|
|
// convert bone to world transformations into pose to world transformations |
|
for ( int k = 0; k < nBoneCount; k++) |
|
{ |
|
MatrixCopy( pLinearBones->poseToBone(k), pPoseToBone[k] ); |
|
} |
|
} |
|
|
|
// Apply the pose-to-bone matrix to all instances |
|
// NOTE: We should be able to optimize this a ton since it's very parallelizable |
|
// NOTE: We may well want to compute the aggregate bone to world here also. |
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
CMatRenderData< matrix3x4a_t > rdPoseToWorld( m_pRenderContext, nBoneCount ); |
|
pModel->m_pPoseToWorld = rdPoseToWorld.Base(); |
|
for ( int b = 0; b < nBoneCount; b++ ) |
|
{ |
|
ConcatTransforms_Aligned( pModel->m_pBoneToWorld[b], pPoseToBone[b], pModel->m_pPoseToWorld[b] ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets up flexes on all models |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::SetupFlexes( int nModelTypeCount, ModelListByType_t *pModelList ) |
|
{ |
|
bool bUsesDelayedWeights = g_CV_FlexSmooth.GetBool(); |
|
|
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
const int nFlexCount = list.m_pStudioHdr->numflexdesc; |
|
if ( !nFlexCount ) |
|
continue; |
|
|
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
CMatRenderData< float > rdFlexWeights( m_pRenderContext ); |
|
CMatRenderData< float > rdDelayedFlexWeights( m_pRenderContext ); |
|
pModel->m_pFlexWeights = rdFlexWeights.Lock( nFlexCount ); |
|
if ( bUsesDelayedWeights ) |
|
{ |
|
pModel->m_pDelayedFlexWeights = rdDelayedFlexWeights.Lock( nFlexCount ); |
|
} |
|
pModel->m_Entry.m_pRenderable->SetupWeights( pModel->m_pBoneToWorld, nFlexCount, pModel->m_pFlexWeights, pModel->m_pDelayedFlexWeights ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Draws debugging information for lighting |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::DebugDrawLightingOrigin( const ModelListByType_t &list, const RenderModelInfo_t &model ) |
|
{ |
|
if ( !model.m_pLightingState ) |
|
return; |
|
|
|
const Vector& lightOrigin = model.m_pLightingState->m_vecLightingOrigin; |
|
const matrix3x4_t &modelToWorld = model.m_Entry.m_pRenderable->RenderableToWorldTransform(); |
|
|
|
// draw z planar cross at lighting origin |
|
Vector pt0; |
|
Vector pt1; |
|
pt0 = lightOrigin; |
|
pt1 = lightOrigin; |
|
pt0.x -= 4; |
|
pt1.x += 4; |
|
debugoverlay->AddLineOverlay( pt0, pt1, 0, 255, 0, true, 0.0f ); |
|
pt0 = lightOrigin; |
|
pt1 = lightOrigin; |
|
pt0.y -= 4; |
|
pt1.y += 4; |
|
debugoverlay->AddLineOverlay( pt0, pt1, 0, 255, 0, true, 0.0f ); |
|
|
|
// draw lines from the light origin to the hull boundaries to identify model |
|
Vector pt; |
|
pt0.x = list.m_pStudioHdr->hull_min.x; |
|
pt0.y = list.m_pStudioHdr->hull_min.y; |
|
pt0.z = list.m_pStudioHdr->hull_min.z; |
|
VectorTransform( pt0, modelToWorld, pt1 ); |
|
debugoverlay->AddLineOverlay( lightOrigin, pt1, 100, 100, 150, true, 0.0f ); |
|
pt0.x = list.m_pStudioHdr->hull_min.x; |
|
pt0.y = list.m_pStudioHdr->hull_max.y; |
|
pt0.z = list.m_pStudioHdr->hull_min.z; |
|
VectorTransform( pt0, modelToWorld, pt1 ); |
|
debugoverlay->AddLineOverlay( lightOrigin, pt1, 100, 100, 150, true, 0.0f ); |
|
pt0.x = list.m_pStudioHdr->hull_max.x; |
|
pt0.y = list.m_pStudioHdr->hull_max.y; |
|
pt0.z = list.m_pStudioHdr->hull_min.z; |
|
VectorTransform( pt0, modelToWorld, pt1 ); |
|
debugoverlay->AddLineOverlay( lightOrigin, pt1, 100, 100, 150, true, 0.0f ); |
|
pt0.x = list.m_pStudioHdr->hull_max.x; |
|
pt0.y = list.m_pStudioHdr->hull_min.y; |
|
pt0.z = list.m_pStudioHdr->hull_min.z; |
|
VectorTransform( pt0, modelToWorld, pt1 ); |
|
debugoverlay->AddLineOverlay( lightOrigin, pt1, 100, 100, 150, true, 0.0f ); |
|
|
|
pt0.x = list.m_pStudioHdr->hull_min.x; |
|
pt0.y = list.m_pStudioHdr->hull_min.y; |
|
pt0.z = list.m_pStudioHdr->hull_max.z; |
|
VectorTransform( pt0, modelToWorld, pt1 ); |
|
debugoverlay->AddLineOverlay( lightOrigin, pt1, 100, 100, 150, true, 0.0f ); |
|
pt0.x = list.m_pStudioHdr->hull_min.x; |
|
pt0.y = list.m_pStudioHdr->hull_max.y; |
|
pt0.z = list.m_pStudioHdr->hull_max.z; |
|
VectorTransform( pt0, modelToWorld, pt1 ); |
|
debugoverlay->AddLineOverlay( lightOrigin, pt1, 100, 100, 150, true, 0.0f ); |
|
pt0.x = list.m_pStudioHdr->hull_max.x; |
|
pt0.y = list.m_pStudioHdr->hull_max.y; |
|
pt0.z = list.m_pStudioHdr->hull_max.z; |
|
VectorTransform( pt0, modelToWorld, pt1 ); |
|
debugoverlay->AddLineOverlay( lightOrigin, pt1, 100, 100, 150, true, 0.0f ); |
|
pt0.x = list.m_pStudioHdr->hull_max.x; |
|
pt0.y = list.m_pStudioHdr->hull_min.y; |
|
pt0.z = list.m_pStudioHdr->hull_max.z; |
|
VectorTransform( pt0, modelToWorld, pt1 ); |
|
debugoverlay->AddLineOverlay( lightOrigin, pt1, 100, 100, 150, true, 0.0f ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Compute lighting origin on all models |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::ComputeLightingOrigin( ModelListByType_t &list, LightingQuery_t *pLightingQueryBase, int nQueryStride ) |
|
{ |
|
LightingQuery_t *pLightingQuery = pLightingQueryBase; |
|
|
|
//int nBoneMergeCount = 0; |
|
int nAttachmentIndex = list.m_pStudioHdr->IllumPositionAttachmentIndex(); |
|
bool bAmbientBoost = ( list.m_pStudioHdr->flags & STUDIOHDR_FLAGS_AMBIENT_BOOST ) != 0; |
|
if ( nAttachmentIndex <= 0 || nAttachmentIndex > list.m_pStudioHdr->GetNumAttachments() ) |
|
{ |
|
const Vector &vecIllumPosition = list.m_pStudioHdr->illumposition; |
|
for ( int j = 0; j < list.m_nCount; ++j, pLightingQuery = (LightingQuery_t*)( (unsigned char*)pLightingQuery + nQueryStride ) ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
//nBoneMergeCount += pModel->m_bBoneMerge; |
|
const matrix3x4_t &renderToWorld = pModel->m_Entry.m_pRenderable->RenderableToWorldTransform(); |
|
pLightingQuery->m_InstanceHandle = pModel->m_hInstance; |
|
VectorTransform( vecIllumPosition, renderToWorld, pLightingQuery->m_LightingOrigin ); |
|
//pLightingQuery->m_ParentInstanceHandle = MODEL_INSTANCE_INVALID; |
|
pLightingQuery->m_bAmbientBoost = bAmbientBoost; |
|
} |
|
} |
|
else |
|
{ |
|
// NOTE: We don't care about orientation here. |
|
// Assume the attachment->bone matrix has identity rotation. Given that, |
|
// compute attachment->world = bone->world * attachment->bone. |
|
// We only care about the translation component of attachment->world |
|
// which can be obtained by transforming the attachment offset by bone->world |
|
|
|
// Oh, and tough noogies if you want an illumposition offset also. |
|
// Just make an attachment exactly where you want it. Otherwise this is slower. |
|
#ifdef _DEBUG |
|
if ( list.m_pStudioHdr->illumposition != vec3_origin ) |
|
{ |
|
static int nWarnCount = 0; |
|
if ( nWarnCount++ < 10 ) |
|
{ |
|
AssertMsg( false, "Model fast path: illumposition must be (0,0,0) if an attachment is being used!\n" ); |
|
} |
|
} |
|
#endif |
|
|
|
// Attachment index is 1 too large, 0 means no attachment |
|
--nAttachmentIndex; |
|
|
|
Vector attachmentOffset; |
|
const mstudioattachment_t &attachment = list.m_pStudioHdr->pAttachment( nAttachmentIndex ); |
|
MatrixGetColumn( attachment.local, 3, attachmentOffset ); |
|
int iBone = list.m_pStudioHdr->GetAttachmentBone( nAttachmentIndex ); |
|
|
|
for ( int j = 0; j < list.m_nCount; ++j, pLightingQuery = (LightingQuery_t*)( (unsigned char*)pLightingQuery + nQueryStride ) ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
//nBoneMergeCount += pModel->m_bBoneMerge; |
|
pLightingQuery->m_InstanceHandle = pModel->m_hInstance; |
|
VectorTransform( attachmentOffset, pModel->m_pBoneToWorld[iBone], pLightingQuery->m_LightingOrigin ); |
|
//pLightingQuery->m_ParentInstanceHandle = MODEL_INSTANCE_INVALID; |
|
pLightingQuery->m_bAmbientBoost = bAmbientBoost; |
|
} |
|
} |
|
|
|
#if 0 |
|
// NOTE: This is more expensive, but hopefully is uncommon |
|
// Bonemerged models will copy the lighting environment from their parent entity. |
|
// This fixes issues with L4D2 infected wounds where the wounds would sometimes receive different lighting |
|
// than the body they're embedded in. |
|
if ( nBoneMergeCount > 0 ) |
|
{ |
|
pLightingQuery = pLightingQueryBase; |
|
for ( int j = 0; j < list.m_nCount; ++j, pLightingQuery = (LightingQuery_t*)( (unsigned char*)pLightingQuery + nQueryStride ) ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
if ( !pModel->m_bBoneMerge ) |
|
continue; |
|
|
|
C_BaseEntity *pEnt = pModel->m_Entry.m_pRenderable->GetIClientUnknown()->GetBaseEntity(); |
|
C_BaseEntity *pParent = pEnt->GetMoveParent(); |
|
if ( !pParent ) |
|
continue; |
|
|
|
pLightingQuery->m_ParentInstanceHandle = pParent->GetModelInstance(); |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Builds the model lighting list |
|
//----------------------------------------------------------------------------- |
|
enum |
|
{ |
|
LIGHTING_USES_ENV_CUBEMAP = 0x1, |
|
LIGHTING_IS_VERTEX_LIT = 0x2, |
|
LIGHTING_IS_STATIC_LIT = 0x4, |
|
}; |
|
|
|
int CModelRenderSystem::BuildLightingList( ModelListByType_t **ppLists, unsigned char *pFlags, int *pTotalModels, const LightingList_t &lightingList ) |
|
{ |
|
// FIXME: This may be better placed in the engine to avoid all the virtual calls? |
|
int nSetupCount = 0; |
|
*pTotalModels = 0; |
|
for ( ModelListByType_t* pList = lightingList.m_pFirstModel; pList; pList = pList->m_pNextLightingModel ) |
|
{ |
|
// FIXME: Under what conditions can the static prop skip lighting? [unlit materials] |
|
bool bIsLit = modelinfo->IsModelVertexLit( pList->m_pModel ); |
|
bool bUsesEnvCubemap = modelinfo->UsesEnvCubemap( pList->m_pModel ); |
|
bool bIsStaticLit = modelinfo->UsesStaticLighting( pList->m_pModel ); |
|
if ( !bIsLit && !bUsesEnvCubemap && !bIsStaticLit ) |
|
continue; |
|
ppLists[ nSetupCount ] = pList; |
|
pFlags[ nSetupCount ] = ( bIsStaticLit << 2 ) | ( bIsLit << 1 ) | ( bUsesEnvCubemap << 0 ); |
|
*pTotalModels += pList->m_nCount; |
|
++nSetupCount; |
|
} |
|
|
|
return nSetupCount; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Hook up computed lighting state |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::HookUpStaticLightingState( int nCount, ModelListByType_t **ppLists, |
|
unsigned char *pFlags, ITexture **ppEnvCubemap, MaterialLightingState_t *pLightingState, |
|
MaterialLightingState_t *pDecalLightingState, ColorMeshInfo_t **ppColorMeshInfo ) |
|
{ |
|
// FIXME: This has got to be more efficient that this |
|
for ( int i = 0; i < nCount; ++i ) |
|
{ |
|
ModelListByType_t &list = *( ppLists[i] ); |
|
if ( pFlags[i] & LIGHTING_USES_ENV_CUBEMAP ) |
|
{ |
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
pModel->m_pEnvCubemapTexture = ppEnvCubemap[j] ? ppEnvCubemap[j] : m_DefaultCubemap; |
|
} |
|
} |
|
|
|
if ( pFlags[i] & LIGHTING_IS_VERTEX_LIT ) |
|
{ |
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
pModel->m_pLightingState = &pLightingState[j]; |
|
pModel->m_pDecalLightingState = &pDecalLightingState[j]; |
|
} |
|
} |
|
|
|
if ( pFlags[i] & LIGHTING_IS_STATIC_LIT ) |
|
{ |
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
pModel->m_pColorMeshInfo = ppColorMeshInfo[j]; |
|
} |
|
} |
|
|
|
ppEnvCubemap += list.m_nCount; |
|
pLightingState += list.m_nCount; |
|
pDecalLightingState += list.m_nCount; |
|
ppColorMeshInfo += list.m_nCount; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets up lighting on all models |
|
//----------------------------------------------------------------------------- |
|
int CModelRenderSystem::SetupStaticPropLighting( LightingList_t &lightingList, DataCacheHandle_t *pColorMeshHandle ) |
|
{ |
|
if ( lightingList.m_nCount == 0 ) |
|
return 0; |
|
|
|
// Build list of everything that needs lighting |
|
int nTotalModels; |
|
ModelListByType_t **ppLists = (ModelListByType_t**)stackalloc( lightingList.m_nCount * sizeof(ModelListByType_t*) ); |
|
unsigned char *pFlags = (unsigned char*)stackalloc( lightingList.m_nCount * sizeof(unsigned char) ); |
|
int nSetupCount = BuildLightingList( ppLists, pFlags, &nTotalModels, lightingList ); |
|
if ( nSetupCount == 0 ) |
|
return 0; |
|
|
|
// Build queries used to compute lighting |
|
StaticLightingQuery_t *pLightingQuery = (StaticLightingQuery_t*)stackalloc( nTotalModels * sizeof(StaticLightingQuery_t) ); |
|
int nOffset = 0; |
|
for ( int i = 0; i < nSetupCount; ++i ) |
|
{ |
|
ModelListByType_t &list = *( ppLists[i] ); |
|
|
|
for ( int j = 0; j < list.m_nCount; ++j, ++nOffset ) |
|
{ |
|
pLightingQuery[ nOffset ].m_pRenderable = list.m_pRenderModels[j].m_Entry.m_pRenderable; |
|
pLightingQuery[ nOffset ].m_InstanceHandle = list.m_pRenderModels[j].m_hInstance; |
|
pLightingQuery[ nOffset ].m_bAmbientBoost = false; |
|
} |
|
} |
|
|
|
// Compute lighting origins |
|
staticpropmgr->GetLightingOrigins( &pLightingQuery[0].m_LightingOrigin, |
|
sizeof(StaticLightingQuery_t), nTotalModels, &pLightingQuery[0].m_pRenderable, sizeof(StaticLightingQuery_t) ); |
|
|
|
// Does all lighting computations for all models |
|
ColorMeshInfo_t **ppColorMeshInfo = (ColorMeshInfo_t**)stackalloc( nTotalModels * sizeof(ColorMeshInfo_t*) ); |
|
ITexture **ppEnvCubemap = (ITexture**)stackalloc( nTotalModels * sizeof(ITexture*) ); |
|
|
|
CMatRenderData< MaterialLightingState_t > rdLightingState( m_pRenderContext, 2 * nTotalModels ); |
|
MaterialLightingState_t *pLightingState = rdLightingState.Base(); |
|
MaterialLightingState_t *pDecalLightingState = &rdLightingState[ nTotalModels ]; |
|
modelrender->ComputeStaticLightingState( nTotalModels, pLightingQuery, pLightingState, pDecalLightingState, ppColorMeshInfo, ppEnvCubemap, pColorMeshHandle ); |
|
|
|
// Hook up pointers |
|
HookUpStaticLightingState( nSetupCount, ppLists, pFlags, ppEnvCubemap, pLightingState, pDecalLightingState, ppColorMeshInfo ); |
|
|
|
return nTotalModels; |
|
} |
|
|
|
|
|
void CModelRenderSystem::SetupStandardLighting( LightingList_t &lightingList ) |
|
{ |
|
if ( lightingList.m_nCount == 0 ) |
|
return; |
|
|
|
// Determine which groups need lighting |
|
ModelListByType_t **ppLists = (ModelListByType_t**)stackalloc( lightingList.m_nCount * sizeof(ModelListByType_t*) ); |
|
unsigned char *pFlags = (unsigned char*)stackalloc( lightingList.m_nCount * sizeof(unsigned char) ); |
|
int nTotalModels = 0; |
|
int nSetupCount = BuildLightingList( ppLists, pFlags, &nTotalModels, lightingList ); |
|
if ( nSetupCount == 0 ) |
|
return; |
|
|
|
// Compute data necessary for lighting computations |
|
int nOffset = 0; |
|
LightingQuery_t *pLightingQuery = (LightingQuery_t*)stackalloc( nTotalModels * sizeof(LightingQuery_t) ); |
|
CMatRenderData<MaterialLightingState_t> rdLightingState( m_pRenderContext, nTotalModels ); |
|
MaterialLightingState_t *pLightingState = rdLightingState.Base(); |
|
memset( pLightingState, 0, nTotalModels * sizeof(MaterialLightingState_t) ); |
|
for ( int i = 0; i < nSetupCount; ++i ) |
|
{ |
|
ModelListByType_t &list = *( ppLists[i] ); |
|
ComputeLightingOrigin( list, &pLightingQuery[nOffset], sizeof(LightingQuery_t) ); |
|
nOffset += list.m_nCount; |
|
} |
|
|
|
// Does all lighting computations for all models |
|
ITexture **ppEnvCubemap = (ITexture**)stackalloc( nTotalModels * sizeof(ITexture*) ); |
|
modelrender->ComputeLightingState( nTotalModels, pLightingQuery, pLightingState, ppEnvCubemap ); |
|
|
|
// Hook up pointers |
|
MaterialLightingState_t *pCurrState = pLightingState; |
|
for ( int i = 0; i < nSetupCount; ++i ) |
|
{ |
|
ModelListByType_t &list = *( ppLists[i] ); |
|
if ( pFlags[i] & 0x1 ) |
|
{ |
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
pModel->m_pEnvCubemapTexture = ppEnvCubemap[j] ? ppEnvCubemap[j] : m_DefaultCubemap; |
|
} |
|
} |
|
|
|
if ( pFlags[i] & 0x2 ) |
|
{ |
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
pModel->m_pLightingState = &pCurrState[j]; |
|
} |
|
} |
|
|
|
ppEnvCubemap += list.m_nCount; |
|
pCurrState += list.m_nCount; |
|
} |
|
} |
|
|
|
int CModelRenderSystem::SetupPhysicsPropLighting( LightingList_t &lightingList, DataCacheHandle_t *pColorMeshHandle ) |
|
{ |
|
if ( lightingList.m_nCount == 0 ) |
|
return 0; |
|
|
|
// NOTE: Physics prop lighting is the same as static prop lighting, only |
|
// the static lighting is *always* used (the system goes to the standard path |
|
// for physics props which are moving or which use bumpmapping). |
|
ModelListByType_t **ppLists = (ModelListByType_t**)stackalloc( lightingList.m_nCount * sizeof(ModelListByType_t*) ); |
|
unsigned char *pFlags = (unsigned char*)stackalloc( lightingList.m_nCount * sizeof(unsigned char) ); |
|
int nTotalModels = 0; |
|
int nSetupCount = BuildLightingList( ppLists, pFlags, &nTotalModels, lightingList ); |
|
if ( nSetupCount == 0 ) |
|
return 0; |
|
|
|
StaticLightingQuery_t *pLightingQuery = (StaticLightingQuery_t*)stackalloc( nTotalModels * sizeof(StaticLightingQuery_t) ); |
|
int nOffset = 0; |
|
for ( int i = 0; i < nSetupCount; ++i ) |
|
{ |
|
ModelListByType_t &list = *( ppLists[i] ); |
|
|
|
ComputeLightingOrigin( list, &pLightingQuery[nOffset], sizeof(StaticLightingQuery_t) ); |
|
for ( int j = 0; j < list.m_nCount; ++j, ++nOffset ) |
|
{ |
|
pLightingQuery[ nOffset ].m_pRenderable = list.m_pRenderModels[j].m_Entry.m_pRenderable; |
|
} |
|
} |
|
|
|
// Does all lighting computations for all models |
|
ColorMeshInfo_t **ppColorMeshInfo = (ColorMeshInfo_t**)stackalloc( nTotalModels * sizeof(ColorMeshInfo_t*) ); |
|
ITexture **ppEnvCubemap = (ITexture**)stackalloc( nTotalModels * sizeof(ITexture*) ); |
|
CMatRenderData< MaterialLightingState_t > rdLightingState( m_pRenderContext, 2 * nTotalModels ); |
|
MaterialLightingState_t *pLightingState = rdLightingState.Base(); |
|
MaterialLightingState_t *pDecalLightingState = &pLightingState[ nTotalModels ]; |
|
modelrender->ComputeStaticLightingState( nTotalModels, pLightingQuery, pLightingState, pDecalLightingState, ppColorMeshInfo, ppEnvCubemap, pColorMeshHandle ); |
|
|
|
// Hook up pointers |
|
HookUpStaticLightingState( nSetupCount, ppLists, pFlags, ppEnvCubemap, pLightingState, pDecalLightingState, ppColorMeshInfo ); |
|
return nTotalModels; |
|
} |
|
|
|
int CModelRenderSystem::SetupLighting( LightingList_t *pLightingList, int nModelTypeCount, ModelListByType_t *pModelList, DataCacheHandle_t *pColorMeshHandles, ModelRenderMode_t renderMode ) |
|
{ |
|
if ( renderMode != MODEL_RENDER_MODE_NORMAL ) |
|
{ |
|
return 0; |
|
} |
|
|
|
int nCount = SetupStaticPropLighting( pLightingList[ LIGHTING_MODEL_STATIC_PROP ], pColorMeshHandles ); |
|
pColorMeshHandles += nCount; |
|
SetupStandardLighting( pLightingList[ LIGHTING_MODEL_STANDARD ] ); |
|
nCount += SetupPhysicsPropLighting( pLightingList[ LIGHTING_MODEL_PHYSICS_PROP ], pColorMeshHandles ); |
|
|
|
// Debugging info |
|
if ( r_drawmodellightorigin.GetBool() ) |
|
{ |
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
const ModelListByType_t &list = pModelList[ i ]; |
|
if ( list.m_nLightingModel == LIGHTING_MODEL_NONE ) |
|
continue; |
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
const RenderModelInfo_t &info = list.m_pRenderModels[j]; |
|
DebugDrawLightingOrigin( list, info ); |
|
} |
|
} |
|
} |
|
|
|
return nCount; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Setup render state related to flashlights and decals |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::SetupFlashlightsAndDecals( StudioModelArrayInfo2_t *pInfo, int nModelTypeCount, ModelListByType_t *pModelList, int nTotalModelCount, RenderModelInfo_t *pRenderModels, ModelRenderMode_t renderMode ) |
|
{ |
|
// Skip lighting + decals if we don't need it |
|
if ( renderMode != MODEL_RENDER_MODE_NORMAL ) |
|
return; |
|
|
|
ShadowHandle_t pFlashlights[MAX_FLASHLIGHTS_PER_INSTANCE_DRAW_CALL]; |
|
int nInstCount = 0; |
|
ModelInstanceHandle_t *pModelInstanceHandle = (ModelInstanceHandle_t*)stackalloc( nTotalModelCount * sizeof(ModelInstanceHandle_t) ); |
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[ i ]; |
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
pModelInstanceHandle[nInstCount++] = pModel->m_hInstance; |
|
} |
|
} |
|
|
|
Assert( nTotalModelCount == nInstCount ); |
|
|
|
if ( nTotalModelCount != nInstCount ) |
|
{ |
|
DevWarning( "CModelRenderSystem::SetupFlashlightsAndDecals sorted model list count incorrect! A model was probably unable to load!" ); |
|
nTotalModelCount = nInstCount; |
|
} |
|
|
|
// Gets all decals |
|
StudioDecalHandle_t *pDecals = &pRenderModels->m_Decals; |
|
modelrender->GetModelDecalHandles( pDecals, sizeof(RenderModelInfo_t), nTotalModelCount, pModelInstanceHandle ); |
|
|
|
// Builds a list of all flashlights affecting this model |
|
uint32 *pFlashlightUsage = &pRenderModels->m_nFlashlightUsage; |
|
pInfo->m_nFlashlightCount = shadowmgr->SetupFlashlightRenderInstanceInfo( pFlashlights, pFlashlightUsage, sizeof(RenderModelInfo_t), nTotalModelCount, pModelInstanceHandle ); |
|
if ( pInfo->m_nFlashlightCount ) |
|
{ |
|
// Copy over the flashlight state |
|
// FIXME: Should we do this over the entire list of all instances? |
|
// There's going to be a fair amount of copying of flashlight_ts |
|
CMatRenderData< FlashlightInstance_t > rdFlashlights( m_pRenderContext, pInfo->m_nFlashlightCount ); |
|
pInfo->m_pFlashlights = rdFlashlights.Base(); |
|
shadowmgr->GetFlashlightRenderInfo( pInfo->m_pFlashlights, pInfo->m_nFlashlightCount, pFlashlights ); |
|
} |
|
else |
|
{ |
|
pInfo->m_pFlashlights = NULL; |
|
} |
|
|
|
// FIXME: Hack! |
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[ i ]; |
|
list.m_nFlashlightCount = pInfo->m_nFlashlightCount; |
|
list.m_pFlashlights = pInfo->m_pFlashlights; |
|
} |
|
} |
|
|
|
void CModelRenderSystem::SetupPerInstanceColorModulation( int nModelTypeCount, ModelListByType_t *pModelList ) |
|
{ |
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[ i ]; |
|
if ( !list.m_nCount ) |
|
continue; |
|
|
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j]; |
|
IClientRenderable *pRenderable = pModel->m_Entry.m_pRenderable; |
|
#if 0 |
|
Vector diffuseModulation; |
|
pRenderable->GetColorModulation( diffuseModulation.Base() ); |
|
pModel->m_DiffuseModulation.x = diffuseModulation.x; |
|
pModel->m_DiffuseModulation.y = diffuseModulation.y; |
|
pModel->m_DiffuseModulation.z = diffuseModulation.z; |
|
#else // preferred to do it this way, because it avoids a load-hit-store on 360 |
|
pRenderable->GetColorModulation( pModel->m_DiffuseModulation.AsVector3D().Base() ); |
|
#endif |
|
pModel->m_DiffuseModulation.w = pModel->m_Entry.m_InstanceData.m_nAlpha * ( 1.0f / 255.0f ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Call into studiorender |
|
//----------------------------------------------------------------------------- |
|
ConVar cl_colorfastpath( "cl_colorfastpath", "0" ); |
|
void CModelRenderSystem::RenderModels( StudioModelArrayInfo2_t *pInfo, int nModelTypeCount, ModelListByType_t *pModelList, int nTotalModelCount, ModelRenderMode_t renderMode ) |
|
{ |
|
if ( renderMode == MODEL_RENDER_MODE_NORMAL ) |
|
{ |
|
bool bColorize = cl_colorfastpath.GetBool(); |
|
if ( bColorize ) |
|
{ |
|
g_pStudioRender->ForcedMaterialOverride( m_DebugMaterial ); |
|
} |
|
|
|
const int nFlags = STUDIORENDER_DRAW_OPAQUE_ONLY; |
|
CMatRenderData< StudioArrayData_t > rdArray( m_pRenderContext, nModelTypeCount ); |
|
|
|
#ifdef _DEBUG |
|
bool bFoundStencil = false; |
|
#endif |
|
|
|
int nNonStencilModelTypeCount = 0; |
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
rdArray[ i ].m_pStudioHdr = list.m_pStudioHdr; |
|
rdArray[ i ].m_pHardwareData = list.m_pHardwareData; |
|
rdArray[ i ].m_pInstanceData = list.m_pRenderModels; |
|
rdArray[ i ].m_nCount = list.m_nCount; |
|
nNonStencilModelTypeCount += list.m_bWantsStencil ? 0 : 1; |
|
|
|
#ifdef _DEBUG |
|
if ( list.m_bWantsStencil ) |
|
{ |
|
bFoundStencil = true; |
|
} |
|
else |
|
{ |
|
Assert( !bFoundStencil ); |
|
} |
|
#endif |
|
} |
|
if ( IsX360() && r_fastzreject.GetBool() && ( nNonStencilModelTypeCount != nModelTypeCount ) ) |
|
{ |
|
// Render all models without stencil |
|
g_pStudioRender->DrawModelArray( *pInfo, nNonStencilModelTypeCount, rdArray.Base(), sizeof(RenderModelInfo_t), nFlags ); |
|
|
|
#ifdef _X360 |
|
// end z prepass here |
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); |
|
pRenderContext->End360ZPass(); |
|
#endif |
|
|
|
// Render all models with stencil |
|
g_pStudioRender->DrawModelArray( *pInfo, nModelTypeCount - nNonStencilModelTypeCount, rdArray.Base() + nNonStencilModelTypeCount, |
|
sizeof(RenderModelInfo_t), nFlags ); |
|
} |
|
else |
|
{ |
|
// PC renders all models in one go regardless of stencil state |
|
g_pStudioRender->DrawModelArray( *pInfo, nModelTypeCount, rdArray.Base(), sizeof(RenderModelInfo_t), nFlags ); |
|
} |
|
g_pStudioRender->ForcedMaterialOverride( NULL ); |
|
} |
|
else if ( renderMode == MODEL_RENDER_MODE_SHADOW_DEPTH ) |
|
{ |
|
// NOTE: Use this path because we can aggregate draw calls across mdls |
|
|
|
const int nFlags = STUDIORENDER_SHADOWDEPTHTEXTURE | STUDIORENDER_DRAW_OPAQUE_ONLY; |
|
CMatRenderData< StudioArrayData_t > rdShadow( m_pRenderContext, nModelTypeCount ); |
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
rdShadow[ i ].m_pStudioHdr = list.m_pStudioHdr; |
|
rdShadow[ i ].m_pHardwareData = list.m_pHardwareData; |
|
rdShadow[ i ].m_pInstanceData = list.m_pRenderModels; |
|
rdShadow[ i ].m_nCount = list.m_nCount; |
|
} |
|
g_pStudioRender->DrawModelShadowArray( nModelTypeCount, rdShadow.Base(), sizeof(RenderModelInfo_t), nFlags ); |
|
} |
|
else if ( renderMode == MODEL_RENDER_MODE_RTT_SHADOWS ) |
|
{ |
|
// shouldn't get here unless the code is ported from l4d2 to drive this properly. |
|
Assert(0); |
|
#if 0 |
|
// HACK: Assume all models in this batch use the same material. This only works because we submit batches of 1 model from the client shadow manager at the moment |
|
IMaterial* pShadowDrawMaterial = pModelList[0].m_pFirstNode->m_Entry.m_pRenderable->GetShadowDrawMaterial(); |
|
g_pStudioRender->ForcedMaterialOverride( pShadowDrawMaterial ? pShadowDrawMaterial : m_ShadowBuild, OVERRIDE_BUILD_SHADOWS ); |
|
|
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
g_pStudioRender->DrawModelArray( list, list.m_nCount, list.m_pRenderModels, sizeof(RenderModelInfo_t), STUDIORENDER_DRAW_OPAQUE_ONLY ); |
|
} |
|
|
|
g_pStudioRender->ForcedMaterialOverride( NULL ); |
|
#endif |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Call into studiorender |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::SetupTranslucentData( int nModelTypeCount, ModelListByType_t *pModelList, int nTotalModelCount, TranslucentInstanceRenderData_t *pRenderData ) |
|
{ |
|
memset( pRenderData, 0, nTotalModelCount * sizeof( TranslucentInstanceRenderData_t ) ); |
|
CMatRenderData< StudioModelArrayInfo_t > arrayInfo( m_pRenderContext, nModelTypeCount ); |
|
CMatRenderData< StudioArrayInstanceData_t > instanceData( m_pRenderContext, nTotalModelCount ); |
|
|
|
int nCurInstance = 0; |
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
StudioModelArrayInfo_t *pModelInfo = &arrayInfo[i]; |
|
memcpy( pModelInfo, &list, sizeof( StudioModelArrayInfo_t ) ); |
|
|
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
RenderModelInfo_t &info = list.m_pRenderModels[j]; |
|
|
|
StudioArrayInstanceData_t *pInstanceData = &instanceData[nCurInstance++]; |
|
memcpy( pInstanceData, &info, sizeof( StudioArrayInstanceData_t ) ); |
|
|
|
TranslucentInstanceRenderData_t &data = pRenderData[ info.m_nInitialListIndex ]; |
|
data.m_pModelInfo = pModelInfo; |
|
data.m_pInstanceData = pInstanceData; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Renders debug overlays |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::RenderVCollideDebugOverlay( int nModelTypeCount, ModelListByType_t *pModelList ) |
|
{ |
|
if ( !vcollide_wireframe.GetBool() ) |
|
return; |
|
|
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
IClientRenderable *pRenderable = list.m_pRenderModels[j].m_Entry.m_pRenderable; |
|
C_BaseAnimating *pAnim = dynamic_cast< C_BaseAnimating * >( pRenderable ); |
|
if ( pAnim && pAnim->IsRagdoll() ) |
|
{ |
|
pAnim->m_pRagdoll->DrawWireframe(); |
|
continue; |
|
} |
|
|
|
ICollideable *pCollideable = pRenderable->GetIClientUnknown()->GetCollideable(); |
|
if ( pCollideable && ( pCollideable->GetSolid() == SOLID_VPHYSICS ) && |
|
IsSolid( pCollideable->GetSolid(), pCollideable->GetSolidFlags() ) ) |
|
{ |
|
vcollide_t *pCollide = modelinfo->GetVCollide( pCollideable->GetCollisionModel() ); |
|
if ( pCollide && pCollide->solidCount == 1 ) |
|
{ |
|
static color32 debugColor = {0,255,255,0}; |
|
engine->DebugDrawPhysCollide( pCollide->solids[0], NULL, pCollideable->CollisionToWorldTransform(), debugColor ); |
|
|
|
C_BaseEntity *pEntity = pRenderable->GetIClientUnknown()->GetBaseEntity(); |
|
if ( pEntity && pEntity->VPhysicsGetObject() ) |
|
{ |
|
static color32 debugColorPhys = {255,0,0,0}; |
|
matrix3x4_t matrix; |
|
pEntity->VPhysicsGetObject()->GetPositionMatrix( &matrix ); |
|
engine->DebugDrawPhysCollide( pCollide->solids[0], NULL, matrix, debugColorPhys ); |
|
} |
|
} |
|
continue; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
void CModelRenderSystem::RenderBBoxDebugOverlay( int nModelTypeCount, ModelListByType_t *pModelList ) |
|
{ |
|
for ( int i = 0; i < nModelTypeCount; ++i ) |
|
{ |
|
ModelListByType_t &list = pModelList[i]; |
|
for ( int j = 0; j < list.m_nCount; ++j ) |
|
{ |
|
IClientRenderable *pRenderable = list.m_pRenderModels[j].m_Entry.m_pRenderable; |
|
if ( !pRenderable->GetIClientUnknown() ) |
|
continue; |
|
C_BaseEntity *pEntity = pRenderable->GetIClientUnknown()->GetBaseEntity(); |
|
if ( !pEntity ) |
|
continue; |
|
|
|
pEntity->DrawBBoxVisualizations(); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Renders debug overlays |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::RenderDebugOverlays( int nModelTypeCount, ModelListByType_t *pModelList, ModelRenderMode_t renderMode ) |
|
{ |
|
if ( renderMode != MODEL_RENDER_MODE_NORMAL ) |
|
{ |
|
return; |
|
} |
|
|
|
RenderVCollideDebugOverlay( nModelTypeCount, pModelList ); |
|
RenderBBoxDebugOverlay( nModelTypeCount, pModelList ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sort model types function |
|
//----------------------------------------------------------------------------- |
|
inline bool CModelRenderSystem::StencilSortLessFunc( const ModelListByType_t &left, const ModelListByType_t &right ) |
|
{ |
|
// Ensure stenciling models are at the end of the list |
|
if ( left.m_bWantsStencil != right.m_bWantsStencil ) |
|
{ |
|
return left.m_bWantsStencil < right.m_bWantsStencil; |
|
} |
|
|
|
// Keep same models with different lighting types together |
|
return left.m_pModel < right.m_pModel; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Draw models |
|
//----------------------------------------------------------------------------- |
|
static ConVar cl_skipfastpath( "cl_skipfastpath", "0", FCVAR_CHEAT, "Set to 1 to stop all models that go through the model fast path from rendering" ); |
|
void CModelRenderSystem::DrawModels( ModelRenderSystemData_t *pEntities, int nCount, ModelRenderMode_t renderMode ) |
|
{ |
|
if ( nCount == 0 || cl_skipfastpath.GetInt() ) |
|
return; |
|
|
|
VPROF_BUDGET( "CModelRenderSystem::DrawModels", VPROF_BUDGETGROUP_MODEL_FAST_PATH_RENDERING ); |
|
MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); |
|
|
|
// While doing this, we need materialsystem to keep around its temp allocations |
|
// which we use for bone matrices + flexes |
|
CMatRenderContextPtr matRenderContext( g_pMaterialSystem ); |
|
m_pRenderContext = matRenderContext; |
|
|
|
PIXEVENT( m_pRenderContext, "CModelRenderSystem::DrawModels (FASTPATH)" ); |
|
|
|
CMatRenderDataReference rdLock( m_pRenderContext ); |
|
|
|
// FIXME: This is infected-specific for perf test reasons. |
|
// Will break into a more fixed pipeline at a later date |
|
DataCacheHandle_t *pColorMeshHandles = NULL; |
|
if ( renderMode == MODEL_RENDER_MODE_NORMAL ) |
|
{ |
|
pColorMeshHandles = (DataCacheHandle_t*)stackalloc( nCount * sizeof(DataCacheHandle_t) ); |
|
} |
|
ModelListByType_t *pModelList = (ModelListByType_t*)stackalloc( nCount * sizeof(ModelListByType_t) ); |
|
ModelListNode_t *pModelListNode = (ModelListNode_t*)stackalloc( nCount * sizeof(ModelListNode_t) ); |
|
int nModelsRenderingStencilCount = 0; |
|
int nModelTypeCount = BucketModelsByMDL( pModelList, pModelListNode, pEntities, nCount, renderMode, &nModelsRenderingStencilCount ); |
|
|
|
LightingList_t pLightingList[ LIGHTING_MODEL_COUNT ]; |
|
memset( pLightingList, 0, LIGHTING_MODEL_COUNT * sizeof(LightingList_t) ); |
|
SortBucketsByDependency( nModelTypeCount, pModelList, pLightingList ); |
|
|
|
// Compute LODs for each model |
|
ComputeModelLODs( nModelTypeCount, pModelList, pModelListNode, renderMode ); |
|
|
|
// Sort processing list by body, lod, skin, etc. |
|
CMatRenderData< RenderModelInfo_t > rdRenderModelInfo( m_pRenderContext, nCount ); |
|
RenderModelInfo_t *pSortedModelListNode = rdRenderModelInfo.Base(); |
|
SortModels( pSortedModelListNode, nModelTypeCount, pModelList, pModelListNode ); |
|
|
|
// Setup bones |
|
SetupBones( nModelTypeCount, pModelList ); |
|
|
|
// Setup flexes |
|
if ( renderMode != MODEL_RENDER_MODE_RTT_SHADOWS ) |
|
{ |
|
SetupFlexes( nModelTypeCount, pModelList ); |
|
} |
|
|
|
// Setup lighting |
|
int nColorMeshHandles = SetupLighting( pLightingList, nModelTypeCount, pModelList, pColorMeshHandles, renderMode ); |
|
|
|
// Setup flashlights + decals |
|
StudioModelArrayInfo2_t info; |
|
SetupFlashlightsAndDecals( &info, nModelTypeCount, pModelList, nCount, pSortedModelListNode, renderMode ); |
|
|
|
// Setup per-instance color modulation |
|
SetupPerInstanceColorModulation( nModelTypeCount, pModelList ); |
|
|
|
// Setup per-instance wound data |
|
//SetupInfectedWoundRenderData( nModelTypeCount, pModelList, nCount, renderMode ); |
|
|
|
if ( IsX360() && ( renderMode == MODEL_RENDER_MODE_NORMAL ) && ( nModelsRenderingStencilCount > 0) ) |
|
{ |
|
// resort here to make sure all models rendering stencil come last |
|
std::sort( pModelList, pModelList + nModelTypeCount, StencilSortLessFunc ); |
|
} |
|
|
|
// Draw models |
|
RenderModels( &info, nModelTypeCount, pModelList, nCount, renderMode ); |
|
|
|
rdLock.Release(); |
|
|
|
if ( renderMode == MODEL_RENDER_MODE_NORMAL ) |
|
{ |
|
modelrender->CleanupStaticLightingState( nColorMeshHandles, pColorMeshHandles ); |
|
stackfree( pColorMeshHandles ); |
|
} |
|
|
|
// Blat out temporary memory for bone-to-world transforms |
|
m_BoneToWorld.FreeAll( false ); |
|
|
|
RenderDebugOverlays( nModelTypeCount, pModelList, renderMode ); |
|
|
|
m_pRenderContext = NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Computes per-instance data for fast path rendering |
|
//----------------------------------------------------------------------------- |
|
void CModelRenderSystem::ComputeTranslucentRenderData( ModelRenderSystemData_t *pModels, int nCount, TranslucentInstanceRenderData_t *pRenderData, TranslucentTempData_t *pTempData ) |
|
{ |
|
if ( nCount == 0 ) |
|
{ |
|
pTempData->m_nColorMeshHandleCount = 0; |
|
pTempData->m_bReleaseRenderData = false; |
|
return; |
|
} |
|
|
|
VPROF_BUDGET( "CModelRenderSystem::ComputeTranslucentRenderData", VPROF_BUDGETGROUP_MODEL_FAST_PATH_RENDERING ); |
|
MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); |
|
|
|
// While doing this, we need materialsystem to keep around its temp allocations |
|
// which we use for bone matrices + flexes |
|
CMatRenderContextPtr matRenderContext( g_pMaterialSystem ); |
|
m_pRenderContext = matRenderContext; |
|
|
|
PIXEVENT( m_pRenderContext, "CModelRenderSystem::ComputeTranslucentRenderData (FASTPATH)" ); |
|
|
|
m_pRenderContext->AddRefRenderData(); |
|
pTempData->m_bReleaseRenderData = true; |
|
|
|
ModelRenderMode_t renderMode = MODEL_RENDER_MODE_NORMAL; |
|
|
|
// FIXME: This is infected-specific for perf test reasons. |
|
// Will break into a more fixed pipeline at a later date |
|
DataCacheHandle_t *pColorMeshHandles = pTempData->m_pColorMeshHandles; |
|
ModelListByType_t *pModelList = (ModelListByType_t*)stackalloc( nCount * sizeof(ModelListByType_t) ); |
|
ModelListNode_t *pModelListNode = (ModelListNode_t*)stackalloc( nCount * sizeof(ModelListNode_t) ); |
|
int nModelsRenderingStencilCount = 0; |
|
int nModelTypeCount = BucketModelsByMDL( pModelList, pModelListNode, pModels, nCount, renderMode, &nModelsRenderingStencilCount ); |
|
|
|
LightingList_t pLightingList[ LIGHTING_MODEL_COUNT ]; |
|
memset( pLightingList, 0, LIGHTING_MODEL_COUNT * sizeof(LightingList_t) ); |
|
SortBucketsByDependency( nModelTypeCount, pModelList, pLightingList ); |
|
|
|
// Compute LODs for each model |
|
ComputeModelLODs( nModelTypeCount, pModelList, pModelListNode, renderMode ); |
|
|
|
// Sort processing list by body, lod, skin, etc. |
|
RenderModelInfo_t *pSortedModelListNode = (RenderModelInfo_t*)stackalloc( nCount * sizeof(RenderModelInfo_t) ); |
|
SortModels( pSortedModelListNode, nModelTypeCount, pModelList, pModelListNode ); |
|
|
|
// Setup bones |
|
SetupBones( nModelTypeCount, pModelList ); |
|
|
|
// Setup flexes |
|
SetupFlexes( nModelTypeCount, pModelList ); |
|
|
|
// Setup lighting |
|
pTempData->m_nColorMeshHandleCount = SetupLighting( pLightingList, nModelTypeCount, pModelList, pColorMeshHandles, renderMode ); |
|
|
|
// Setup flashlights + decals |
|
StudioModelArrayInfo2_t info; |
|
SetupFlashlightsAndDecals( &info, nModelTypeCount, pModelList, nCount, pSortedModelListNode, renderMode ); |
|
|
|
// Setup per-instance color modulation |
|
SetupPerInstanceColorModulation( nModelTypeCount, pModelList ); |
|
|
|
// Setup per-instance wound data |
|
// SetupInfectedWoundRenderData( nModelTypeCount, pModelList, nCount, renderMode ); |
|
|
|
// Draw models |
|
SetupTranslucentData( nModelTypeCount, pModelList, nCount, pRenderData ); |
|
|
|
// Blat out temporary memory for bone-to-world transforms |
|
m_BoneToWorld.FreeAll( false ); |
|
|
|
RenderDebugOverlays( nModelTypeCount, pModelList, renderMode ); |
|
|
|
m_pRenderContext = NULL; |
|
} |
|
|
|
void CModelRenderSystem::CleanupTranslucentTempData( TranslucentTempData_t *pTempData ) |
|
{ |
|
if ( pTempData->m_bReleaseRenderData ) |
|
{ |
|
modelrender->CleanupStaticLightingState( pTempData->m_nColorMeshHandleCount, pTempData->m_pColorMeshHandles ); |
|
CMatRenderContextPtr matRenderContext( g_pMaterialSystem ); |
|
matRenderContext->ReleaseRenderData(); |
|
} |
|
}
|
|
|