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.
1786 lines
68 KiB
1786 lines
68 KiB
//===== Copyright (c) 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"
|
|
#undef max
|
|
#undef min
|
|
#include <algorithm>
|
|
#include "tier1/memstack.h"
|
|
#include "engine/ivdebugoverlay.h"
|
|
#include "shaderapi/ishaderapi.h"
|
|
#include "materialsystem/MaterialSystemUtil.h"
|
|
#include "engine_model_client.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, bool bShadowDepthIncludeTranslucentMaterials = false );
|
|
virtual void DrawBrushModels( ModelRenderSystemData_t *pModels, 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;
|
|
uint32 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 BrushModelList_t
|
|
{
|
|
ModelRenderSystemData_t m_Entry;
|
|
uint32 m_nParentDepth : 31;
|
|
uint32 m_bWantsStencil : 1;
|
|
BrushArrayInstanceData_t *m_pInstanceData;
|
|
|
|
BrushModelList_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 );
|
|
uint 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 nListTotal, 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, bool bShadowDepthIncludeTranslucentMaterials = false );
|
|
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 );
|
|
|
|
// Methods related to fastpath brush model rendering
|
|
void AddBrushModelToList( int nInitialListIndex, ModelRenderSystemData_t &data, ModelRenderMode_t renderMode,
|
|
BrushArrayInstanceData_t &instance, matrix3x4a_t &brushToWorld );
|
|
void SetupPerInstanceColorModulation( int nCount, ModelRenderSystemData_t *pModels, BrushArrayInstanceData_t *pInstanceData, ModelRenderMode_t renderMode );
|
|
|
|
CMemoryStack m_BoneToWorld;
|
|
CTextureReference m_DefaultCubemap;
|
|
CMaterialReference m_DebugMaterial;
|
|
CMaterialReference m_ShadowBuild;
|
|
IMatRenderContext *m_pRenderContext;
|
|
|
|
CUtlMemoryFixedGrowable< BrushModelList_t, 512 > m_BrushModelList;
|
|
int m_nColorMeshHandles;
|
|
int m_nModelTypeCount;
|
|
int m_nTotalModelCount;
|
|
bool m_bShadowDepth;
|
|
bool m_bHasInstanceData;
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Singleton accessor
|
|
//-----------------------------------------------------------------------------
|
|
static CModelRenderSystem s_ModelRenderSystem;
|
|
IModelRenderSystem *g_pModelRenderSystem = &s_ModelRenderSystem;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
CModelRenderSystem::CModelRenderSystem()
|
|
{
|
|
m_bHasInstanceData = false;
|
|
m_BoneToWorld.Init( "CModelRenderSystem::m_BoneToWorld", 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
|
|
//-----------------------------------------------------------------------------
|
|
uint CModelRenderSystem::AddModelToLists( int &nModelTypeCountInOut, ModelListByType_t *pModelList,
|
|
int &nModelNodeCount, ModelListNode_t *pModelListNodes, int nInitialListIndex, ModelRenderSystemData_t &data, ModelRenderMode_t renderMode )
|
|
{
|
|
int nModelTypeCount = nModelTypeCountInOut;
|
|
// 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 );
|
|
|
|
// PerfectWorld build needs to not render certain models from the exclude list, so just don't add those models to any lists
|
|
if ( EngineModelClientFlags( pModel ) & ENGINE_MODEL_CLIENT_MODELFLAG_RENDER_DISABLED )
|
|
return 0; // Such models don't render anywhere and don't need stencil
|
|
|
|
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 ) ) );
|
|
}
|
|
|
|
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 bWantsStencil;
|
|
}
|
|
|
|
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 )
|
|
return bWantsStencil;
|
|
|
|
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;
|
|
nModelTypeCountInOut = nModelTypeCount;
|
|
|
|
return bWantsStencil;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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;
|
|
int nModelWantingStencil = 0;
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
nModelWantingStencil += AddModelToLists( nModelTypeCount, pModelList, nModelNodeCount, pModelListNodes, i, pEntities[i], renderMode );
|
|
}
|
|
*pModelsRenderingStencilCountOut = nModelWantingStencil;
|
|
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;
|
|
}
|
|
|
|
#ifdef CSTRIKE15
|
|
// Always slam r_lod to 0 for CS:GO.
|
|
int nLOD = 0;
|
|
#else
|
|
int nLOD = r_lod.GetInt();
|
|
#endif
|
|
|
|
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 nListTotal,
|
|
int nModelTypeCount, ModelListByType_t *pModelList, ModelListNode_t *pModelListNode )
|
|
{
|
|
// First place them in arrays
|
|
Plat_FastMemset( pRenderModelInfo, 0, sizeof(RenderModelInfo_t) * nListTotal );
|
|
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 )
|
|
{
|
|
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++)
|
|
{
|
|
const mstudiobone_t *pCurBone = list.m_pStudioHdr->pBone( k );
|
|
MatrixCopy( pCurBone->poseToBone, pPoseToBone[k] );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mstudiolinearbone_t *pLinearBones = list.m_pStudioHdr->pLinearBones();
|
|
#if defined(_X360) || defined (_PS3)
|
|
const int iOffsetToCacheline = 2; // == 128/sizeof(mstudiolinearbone_t) == 128/64
|
|
int iNextPrefetch = 0;
|
|
#endif
|
|
|
|
// convert bone to world transformations into pose to world transformations
|
|
for ( int k = 0; k < nBoneCount; k++)
|
|
{
|
|
#if defined(_X360) || defined (_PS3)
|
|
if ( k == iNextPrefetch && (iNextPrefetch = k + iOffsetToCacheline) < nBoneCount )
|
|
{
|
|
PREFETCH360( &pLinearBones->poseToBone(iNextPrefetch), 0 );
|
|
}
|
|
#endif
|
|
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();
|
|
|
|
#if defined(_X360) || defined (_PS3)
|
|
if ( j + 1 < list.m_nCount )
|
|
{
|
|
PREFETCH360( list.m_pRenderModels[j + 1].m_pBoneToWorld, 0 );
|
|
PREFETCH360( pModel->m_pPoseToWorld + nBoneCount, 0 );
|
|
}
|
|
|
|
const int iOffsetToCacheline = 3; // == 128/sizeof(matrix3x4a_t) == 128/48
|
|
int iNextPrefetch = 0;
|
|
#endif
|
|
for ( int b = 0; b < nBoneCount; b++ )
|
|
{
|
|
#if defined(_X360) || defined (_PS3)
|
|
if ( b == iNextPrefetch && (iNextPrefetch = b + iOffsetToCacheline) < nBoneCount )
|
|
{
|
|
PREFETCH360( &pModel->m_pBoneToWorld[iNextPrefetch], 0 );
|
|
PREFETCH360( &pModel->m_pPoseToWorld[iNextPrefetch], 0 );
|
|
}
|
|
#endif
|
|
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 nAttachmentIndex = list.m_pStudioHdr->IllumPositionAttachmentIndex();
|
|
bool bAmbientBoost = ( list.m_pStudioHdr->flags & STUDIOHDR_FLAGS_AMBIENT_BOOST ) != 0;
|
|
const Vector &vecIllumPosition = list.m_pStudioHdr->illumposition;
|
|
|
|
// ($TODO): We may want to pull the functionality of ComputeLightingOrigin inlined into this loop to prevent the virtual function call overhead and refactor to reduce the conditionals.
|
|
for ( int j = 0; j < list.m_nCount; ++j, pLightingQuery = (LightingQuery_t*)( (unsigned char*)pLightingQuery + nQueryStride ) )
|
|
{
|
|
RenderModelInfo_t *pModel = &list.m_pRenderModels[j];
|
|
const matrix3x4_t &renderToWorld = pModel->m_Entry.m_pRenderable->RenderableToWorldTransform();
|
|
|
|
C_BaseEntity *pEnt = pModel->m_Entry.m_pRenderable->GetIClientUnknown()->GetBaseEntity();
|
|
pEnt->ComputeLightingOrigin( nAttachmentIndex, vecIllumPosition, renderToWorld, pLightingQuery->m_LightingOrigin );
|
|
|
|
//VectorTransform( vecIllumPosition, renderToWorld, pLightingQuery->m_LightingOrigin );
|
|
pLightingQuery->m_InstanceHandle = pModel->m_hInstance;
|
|
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 );
|
|
// bring this into cache and clear it, we're going to write to most of it anyway
|
|
Plat_FastMemset( rdLightingState.Base(), 0, sizeof(MaterialLightingState_t) * 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();
|
|
PREFETCH360( pLightingState, 0 );
|
|
PREFETCH360( pLightingState, 128 );
|
|
PREFETCH360( pLightingState, 256 );
|
|
PREFETCH360( pLightingState, 384 );
|
|
PREFETCH360( pLightingState, 512 );
|
|
PREFETCH360( pLightingState, 640 );
|
|
PREFETCH360( pLightingState, 768 );
|
|
PREFETCH360( pLightingState, 896 );
|
|
|
|
for ( int i = 0; i < nSetupCount; ++i )
|
|
{
|
|
ModelListByType_t &list = *( ppLists[i] );
|
|
ComputeLightingOrigin( list, &pLightingQuery[nOffset], sizeof(LightingQuery_t) );
|
|
nOffset += list.m_nCount;
|
|
}
|
|
|
|
memset( pLightingState, 0, nTotalModels * sizeof(MaterialLightingState_t) );
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
if ( nTotalModelCount != nInstCount )
|
|
{
|
|
// FIXME: custom player model scaffolds have no geometry (only bones) and when used will always trip this warning - this system needs to account for models that don't 'render' in the traditional sense.
|
|
// AssertMsgOnce( false, "Instance count does not match model count." );
|
|
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, bool bShadowDepthIncludeTranslucentMaterials )
|
|
{
|
|
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() /* && !IsPS3(), see below */ && r_fastzreject.GetBool() && ( nNonStencilModelTypeCount != nModelTypeCount ) )
|
|
{
|
|
// Render all models without stencil
|
|
g_pStudioRender->DrawModelArray( *pInfo, nNonStencilModelTypeCount, rdArray.Base(), sizeof(RenderModelInfo_t), nFlags );
|
|
|
|
#if defined( _GAMECONSOLE )
|
|
// end z prepass here
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
pRenderContext->EndConsoleZPass();
|
|
#endif
|
|
|
|
// Render all models with stencil
|
|
g_pStudioRender->DrawModelArray( *pInfo, nModelTypeCount - nNonStencilModelTypeCount, rdArray.Base() + nNonStencilModelTypeCount,
|
|
sizeof(RenderModelInfo_t), nFlags );
|
|
}
|
|
else
|
|
{
|
|
#if defined( _PS3 )
|
|
if( r_fastzreject.GetBool() )
|
|
{
|
|
// end z prepass here
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
pRenderContext->EndConsoleZPass();
|
|
}
|
|
#endif
|
|
|
|
// PC renders all models in one go regardless of stencil state
|
|
// PS/3 renders all models in one go because models have a lot of vertices and few pixels (as of Portal2). So we end Z Pass earlier, before we RenderModels()
|
|
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
|
|
|
|
int nFlags = STUDIORENDER_SHADOWDEPTHTEXTURE;
|
|
if ( bShadowDepthIncludeTranslucentMaterials )
|
|
nFlags |= STUDIORENDER_SHADOWDEPTHTEXTURE_INCLUDE_TRANSLUCENT_MATERIALS;
|
|
else
|
|
nFlags |= 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;
|
|
}
|
|
else if ( pCollideable && vcollide_wireframe.GetInt() > 1 )
|
|
{
|
|
vcollide_t *pCollide = modelinfo->GetVCollide( pCollideable->GetCollisionModel() );
|
|
if ( pCollide && pCollide->solidCount == 1 )
|
|
{
|
|
static color32 debugColor = {0,255,0,0};
|
|
engine->DebugDrawPhysCollide( pCollide->solids[0], NULL, pAnim->RenderableToWorldTransform(), debugColor );
|
|
}
|
|
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, bool bShadowDepthIncludeTranslucentMaterials )
|
|
{
|
|
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, nCount, 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 ( IsGameConsole() && ( 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, bShadowDepthIncludeTranslucentMaterials );
|
|
|
|
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, nCount, 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();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Brush model rendering system starts here
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Adds a model to the list of brush models to render
|
|
//-----------------------------------------------------------------------------
|
|
void CModelRenderSystem::AddBrushModelToList( int nInitialListIndex, ModelRenderSystemData_t &data,
|
|
ModelRenderMode_t renderMode, BrushArrayInstanceData_t &instance, matrix3x4a_t &brushToWorld )
|
|
{
|
|
const model_t *pModel = data.m_pRenderable->GetModel();
|
|
Assert( modelinfo->GetModelType( pModel ) == mod_brush );
|
|
|
|
brushToWorld = data.m_pRenderable->RenderableToWorldTransform();
|
|
instance.m_pBrushToWorld = &brushToWorld;
|
|
instance.m_pBrushModel = pModel;
|
|
instance.m_pStencilState = NULL;
|
|
|
|
uint bWantsStencil = 0;
|
|
if ( data.m_pModelRenderable )
|
|
{
|
|
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.
|
|
ShaderStencilState_t tempStencil;
|
|
bWantsStencil = data.m_pModelRenderable->GetRenderData( &tempStencil, MODEL_DATA_STENCIL ) ? 1 : 0;
|
|
if ( bWantsStencil )
|
|
{
|
|
CMatRenderData< ShaderStencilState_t > rdStencil( m_pRenderContext, 1 );
|
|
memcpy( &rdStencil[0], &tempStencil, sizeof( tempStencil ) );
|
|
instance.m_pStencilState = &rdStencil[0];
|
|
|
|
// Not working yet...
|
|
Assert( 0 );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ExecuteOnce( DevWarning( "data.m_pModelRenderable is NULL for %s\n", modelinfo->GetModelName( pModel ) ) );
|
|
}
|
|
}
|
|
|
|
void CModelRenderSystem::SetupPerInstanceColorModulation( int nCount, ModelRenderSystemData_t *pModels, BrushArrayInstanceData_t *pInstanceData, ModelRenderMode_t renderMode )
|
|
{
|
|
if ( renderMode != MODEL_RENDER_MODE_NORMAL )
|
|
return;
|
|
|
|
Vector vecColorModulation;
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
IClientRenderable *pRenderable = pModels[i].m_pRenderable;
|
|
pRenderable->GetColorModulation( pInstanceData[i].m_DiffuseModulation.AsVector3D().Base() );
|
|
pInstanceData[i].m_DiffuseModulation.w = pModels[i].m_InstanceData.m_nAlpha * ( 1.0f / 255.0f );
|
|
}
|
|
}
|
|
|
|
void CModelRenderSystem::DrawBrushModels( ModelRenderSystemData_t *pModels, int nCount, ModelRenderMode_t renderMode )
|
|
{
|
|
if ( nCount == 0 || cl_skipfastpath.GetInt() )
|
|
return;
|
|
|
|
VPROF_BUDGET( "CModelRenderSystem::DrawBrushModels", VPROF_BUDGETGROUP_BRUSH_FAST_PATH_RENDERING );
|
|
|
|
// 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::DrawBrushModels (FASTPATH)" );
|
|
|
|
CMatRenderDataReference rdLock( m_pRenderContext );
|
|
|
|
CMatRenderData< BrushArrayInstanceData_t > rdInstances( m_pRenderContext, nCount );
|
|
CMatRenderData< matrix3x4a_t > rdMatrices( m_pRenderContext, nCount );
|
|
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
AddBrushModelToList( i, pModels[i], renderMode, rdInstances[i], rdMatrices[i] );
|
|
}
|
|
|
|
SetupPerInstanceColorModulation( nCount, pModels, rdInstances.Base(), renderMode );
|
|
|
|
// Two pass is telling it to only do the opaque portion of the brush model
|
|
int nFlags = STUDIO_RENDER | STUDIO_TWOPASS;
|
|
switch( renderMode )
|
|
{
|
|
case MODEL_RENDER_MODE_NORMAL:
|
|
break;
|
|
|
|
case MODEL_RENDER_MODE_SHADOW_DEPTH:
|
|
nFlags |= STUDIO_SHADOWDEPTHTEXTURE;
|
|
break;
|
|
|
|
case MODEL_RENDER_MODE_RTT_SHADOWS:
|
|
nFlags |= STUDIO_SHADOWTEXTURE;
|
|
break;
|
|
};
|
|
|
|
render->DrawBrushModelArray( m_pRenderContext, nCount, rdInstances.Base(), nFlags );
|
|
|
|
rdLock.Release();
|
|
|
|
// RenderDebugOverlays( nModelTypeCount, pModelList, renderMode );
|
|
|
|
m_pRenderContext = NULL;
|
|
}
|