source-engine/game/client/clientleafsystem.cpp

1769 lines
58 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Revision: $
// $NoKeywords: $
//
// This file contains code to allow us to associate client data with bsp leaves.
//===========================================================================//
#include "cbase.h"
#include "clientleafsystem.h"
#include "utlbidirectionalset.h"
#include "model_types.h"
#include "ivrenderview.h"
#include "tier0/vprof.h"
#include "bsptreedata.h"
#include "detailobjectsystem.h"
#include "engine/IStaticPropMgr.h"
#include "engine/ivdebugoverlay.h"
#include "vstdlib/jobthread.h"
#include "tier1/utllinkedlist.h"
#include "datacache/imdlcache.h"
#include "view.h"
#include "viewrender.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class VMatrix; // forward decl
static ConVar cl_drawleaf("cl_drawleaf", "-1", FCVAR_CHEAT );
static ConVar r_PortalTestEnts( "r_PortalTestEnts", "1", FCVAR_CHEAT, "Clip entities against portal frustums." );
static ConVar r_portalsopenall( "r_portalsopenall", "0", FCVAR_CHEAT, "Open all portals" );
static ConVar cl_threaded_client_leaf_system("cl_threaded_client_leaf_system", "0" );
DEFINE_FIXEDSIZE_ALLOCATOR( CClientRenderablesList, 1, CUtlMemoryPool::GROW_SLOW );
//-----------------------------------------------------------------------------
// Threading helpers
//-----------------------------------------------------------------------------
static void FrameLock()
{
mdlcache->BeginLock();
}
static void FrameUnlock()
{
mdlcache->EndLock();
}
static void CallComputeFXBlend( IClientRenderable *&pRenderable )
{
pRenderable->ComputeFxBlend();
}
//-----------------------------------------------------------------------------
// The client leaf system
//-----------------------------------------------------------------------------
class CClientLeafSystem : public IClientLeafSystem, public ISpatialLeafEnumerator
{
public:
virtual char const *Name() { return "CClientLeafSystem"; }
// constructor, destructor
CClientLeafSystem();
virtual ~CClientLeafSystem();
// Methods of IClientSystem
bool Init() { return true; }
void PostInit() {}
void Shutdown() {}
virtual bool IsPerFrame() { return true; }
void PreRender();
void PostRender() { }
void Update( float frametime ) { }
void LevelInitPreEntity();
void LevelInitPostEntity() {}
void LevelShutdownPreEntity();
void LevelShutdownPostEntity();
virtual void OnSave() {}
virtual void OnRestore() {}
virtual void SafeRemoveIfDesired() {}
// Methods of IClientLeafSystem
public:
virtual void AddRenderable( IClientRenderable* pRenderable, RenderGroup_t group );
virtual bool IsRenderableInPVS( IClientRenderable *pRenderable );
virtual void CreateRenderableHandle( IClientRenderable* pRenderable, bool bIsStaticProp );
virtual void RemoveRenderable( ClientRenderHandle_t handle );
virtual void SetSubSystemDataInLeaf( int leaf, int nSubSystemIdx, CClientLeafSubSystemData *pData );
virtual CClientLeafSubSystemData *GetSubSystemDataInLeaf( int leaf, int nSubSystemIdx );
// FIXME: There's an incestuous relationship between DetailObjectSystem
// and the ClientLeafSystem. Maybe they should be the same system?
virtual void GetDetailObjectsInLeaf( int leaf, int& firstDetailObject, int& detailObjectCount );
virtual void SetDetailObjectsInLeaf( int leaf, int firstDetailObject, int detailObjectCount );
virtual void DrawDetailObjectsInLeaf( int leaf, int frameNumber, int& nFirstDetailObject, int& nDetailObjectCount );
virtual bool ShouldDrawDetailObjectsInLeaf( int leaf, int frameNumber );
virtual void RenderableChanged( ClientRenderHandle_t handle );
virtual void SetRenderGroup( ClientRenderHandle_t handle, RenderGroup_t group );
virtual void ComputeTranslucentRenderLeaf( int count, const LeafIndex_t *pLeafList, const LeafFogVolume_t *pLeafFogVolumeList, int frameNumber, int viewID );
virtual void CollateViewModelRenderables( CUtlVector< IClientRenderable * >& opaque, CUtlVector< IClientRenderable * >& translucent );
virtual void BuildRenderablesList( const SetupRenderInfo_t &info );
void CollateRenderablesInLeaf( int leaf, int worldListLeafIndex, const SetupRenderInfo_t &info );
virtual void DrawStaticProps( bool enable );
virtual void DrawSmallEntities( bool enable );
virtual void EnableAlternateSorting( ClientRenderHandle_t handle, bool bEnable );
// Adds a renderable to a set of leaves
virtual void AddRenderableToLeaves( ClientRenderHandle_t handle, int nLeafCount, unsigned short *pLeaves );
// The following methods are related to shadows...
virtual ClientLeafShadowHandle_t AddShadow( ClientShadowHandle_t userId, unsigned short flags );
virtual void RemoveShadow( ClientLeafShadowHandle_t h );
virtual void ProjectShadow( ClientLeafShadowHandle_t handle, int nLeafCount, const int *pLeafList );
virtual void ProjectFlashlight( ClientLeafShadowHandle_t handle, int nLeafCount, const int *pLeafList );
// Find all shadow casters in a set of leaves
virtual void EnumerateShadowsInLeaves( int leafCount, LeafIndex_t* pLeaves, IClientLeafShadowEnum* pEnum );
// methods of ISpatialLeafEnumerator
public:
2022-02-23 11:50:30 +00:00
bool EnumerateLeaf( int leaf, intp context );
2020-04-22 16:56:21 +00:00
// Adds a shadow to a leaf
void AddShadowToLeaf( int leaf, ClientLeafShadowHandle_t handle );
// Fill in a list of the leaves this renderable is in.
// Returns -1 if the handle is invalid.
int GetRenderableLeaves( ClientRenderHandle_t handle, int leaves[128] );
// Get leaves this renderable is in
virtual bool GetRenderableLeaf ( ClientRenderHandle_t handle, int* pOutLeaf, const int* pInIterator = 0, int* pOutIterator = 0 );
// Singleton instance...
static CClientLeafSystem s_ClientLeafSystem;
private:
// Creates a new renderable
void NewRenderable( IClientRenderable* pRenderable, RenderGroup_t type, int flags = 0 );
// Adds a renderable to the list of renderables
void AddRenderableToLeaf( int leaf, ClientRenderHandle_t handle );
void SortEntities( const Vector &vecRenderOrigin, const Vector &vecRenderForward, CClientRenderablesList::CEntry *pEntities, int nEntities );
// Returns -1 if the renderable spans more than one area. If it's totally in one area, then this returns the leaf.
short GetRenderableArea( ClientRenderHandle_t handle );
// remove renderables from leaves
void InsertIntoTree( ClientRenderHandle_t &handle );
void RemoveFromTree( ClientRenderHandle_t handle );
// Returns if it's a view model render group
inline bool IsViewModelRenderGroup( RenderGroup_t group ) const;
// Adds, removes renderables from view model list
void AddToViewModelList( ClientRenderHandle_t handle );
void RemoveFromViewModelList( ClientRenderHandle_t handle );
// Insert translucent renderables into list of translucent objects
void InsertTranslucentRenderable( IClientRenderable* pRenderable,
int& count, IClientRenderable** pList, float* pDist );
// Used to change renderables from translucent to opaque
// Only really used by the static prop fading...
void ChangeRenderableRenderGroup( ClientRenderHandle_t handle, RenderGroup_t group );
// Adds a shadow to a leaf/removes shadow from renderable
void AddShadowToRenderable( ClientRenderHandle_t renderHandle, ClientLeafShadowHandle_t shadowHandle );
void RemoveShadowFromRenderables( ClientLeafShadowHandle_t handle );
// Adds a shadow to a leaf/removes shadow from renderable
bool ShouldRenderableReceiveShadow( ClientRenderHandle_t renderHandle, int nShadowFlags );
// Adds a shadow to a leaf/removes shadow from leaf
void RemoveShadowFromLeaves( ClientLeafShadowHandle_t handle );
// Methods associated with the various bi-directional sets
2022-03-01 20:00:42 +00:00
static unsigned short& FirstRenderableInLeaf( int leaf )
2020-04-22 16:56:21 +00:00
{
return s_ClientLeafSystem.m_Leaf[leaf].m_FirstElement;
}
2022-03-01 20:00:42 +00:00
static unsigned short& FirstLeafInRenderable( unsigned short renderable )
2020-04-22 16:56:21 +00:00
{
return s_ClientLeafSystem.m_Renderables[renderable].m_LeafList;
}
static unsigned short& FirstShadowInLeaf( int leaf )
{
return s_ClientLeafSystem.m_Leaf[leaf].m_FirstShadow;
}
static unsigned short& FirstLeafInShadow( ClientLeafShadowHandle_t shadow )
{
return s_ClientLeafSystem.m_Shadows[shadow].m_FirstLeaf;
}
static unsigned short& FirstShadowOnRenderable( unsigned short renderable )
{
return s_ClientLeafSystem.m_Renderables[renderable].m_FirstShadow;
}
static unsigned short& FirstRenderableInShadow( ClientLeafShadowHandle_t shadow )
{
return s_ClientLeafSystem.m_Shadows[shadow].m_FirstRenderable;
}
void FrameLock()
{
mdlcache->BeginLock();
}
void FrameUnlock()
{
mdlcache->EndLock();
}
private:
enum
{
RENDER_FLAGS_TWOPASS = 0x01,
RENDER_FLAGS_STATIC_PROP = 0x02,
RENDER_FLAGS_BRUSH_MODEL = 0x04,
RENDER_FLAGS_STUDIO_MODEL = 0x08,
RENDER_FLAGS_HASCHANGED = 0x10,
RENDER_FLAGS_ALTERNATE_SORTING = 0x20,
};
// All the information associated with a particular handle
struct RenderableInfo_t
{
IClientRenderable* m_pRenderable;
int m_RenderFrame; // which frame did I render it in?
int m_RenderFrame2;
int m_EnumCount; // Have I been added to a particular shadow yet?
int m_TranslucencyCalculated;
2022-03-01 20:00:42 +00:00
unsigned short m_LeafList; // What leafs is it in?
unsigned short m_RenderLeaf; // What leaf do I render in?
2020-04-22 16:56:21 +00:00
unsigned char m_Flags; // rendering flags
unsigned char m_RenderGroup; // RenderGroup_t type
unsigned short m_FirstShadow; // The first shadow caster that cast on it
short m_Area; // -1 if the renderable spans multiple areas.
signed char m_TranslucencyCalculatedView;
};
// The leaf contains an index into a list of renderables
struct ClientLeaf_t
{
2022-03-01 20:00:42 +00:00
unsigned short m_FirstElement;
2020-04-22 16:56:21 +00:00
unsigned short m_FirstShadow;
unsigned short m_FirstDetailProp;
unsigned short m_DetailPropCount;
int m_DetailPropRenderFrame;
CClientLeafSubSystemData *m_pSubSystemData[N_CLSUBSYSTEMS];
};
// Shadow information
struct ShadowInfo_t
{
unsigned short m_FirstLeaf;
unsigned short m_FirstRenderable;
int m_EnumCount;
ClientShadowHandle_t m_Shadow;
unsigned short m_Flags;
};
struct EnumResult_t
{
int leaf;
EnumResult_t *pNext;
};
struct EnumResultList_t
{
EnumResult_t *pHead;
ClientRenderHandle_t handle;
};
// Stores data associated with each leaf.
CUtlVector< ClientLeaf_t > m_Leaf;
// Stores all unique non-detail renderables
CUtlLinkedList< RenderableInfo_t, ClientRenderHandle_t, false, unsigned int > m_Renderables;
// Information associated with shadows registered with the client leaf system
CUtlLinkedList< ShadowInfo_t, ClientLeafShadowHandle_t, false, unsigned int > m_Shadows;
// Maintains the list of all renderables in a particular leaf
2022-03-01 20:00:42 +00:00
CBidirectionalSet< int, ClientRenderHandle_t, unsigned short, unsigned int > m_RenderablesInLeaf;
2020-04-22 16:56:21 +00:00
// Maintains a list of all shadows in a particular leaf
CBidirectionalSet< int, ClientLeafShadowHandle_t, unsigned short, unsigned int > m_ShadowsInLeaf;
// Maintains a list of all shadows cast on a particular renderable
CBidirectionalSet< ClientRenderHandle_t, ClientLeafShadowHandle_t, unsigned short, unsigned int > m_ShadowsOnRenderable;
// Dirty list of renderables
CUtlVector< ClientRenderHandle_t > m_DirtyRenderables;
// List of renderables in view model render groups
CUtlVector< ClientRenderHandle_t > m_ViewModels;
// Should I draw static props?
bool m_DrawStaticProps;
bool m_DrawSmallObjects;
// A little enumerator to help us when adding shadows to renderables
int m_ShadowEnum;
CTSList<EnumResultList_t> m_DeferredInserts;
};
//-----------------------------------------------------------------------------
// Expose IClientLeafSystem to the client dll.
//-----------------------------------------------------------------------------
CClientLeafSystem CClientLeafSystem::s_ClientLeafSystem;
IClientLeafSystem *g_pClientLeafSystem = &CClientLeafSystem::s_ClientLeafSystem;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CClientLeafSystem, IClientLeafSystem, CLIENTLEAFSYSTEM_INTERFACE_VERSION, CClientLeafSystem::s_ClientLeafSystem );
void CalcRenderableWorldSpaceAABB_Fast( IClientRenderable *pRenderable, Vector &absMin, Vector &absMax );
//-----------------------------------------------------------------------------
// Helper functions.
//-----------------------------------------------------------------------------
void DefaultRenderBoundsWorldspace( IClientRenderable *pRenderable, Vector &absMins, Vector &absMaxs )
{
// Tracker 37433: This fixes a bug where if the stunstick is being wielded by a combine soldier, the fact that the stick was
// attached to the soldier's hand would move it such that it would get frustum culled near the edge of the screen.
2022-03-01 20:00:42 +00:00
C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity();
2020-04-22 16:56:21 +00:00
if ( pEnt && pEnt->IsFollowingEntity() )
{
C_BaseEntity *pParent = pEnt->GetFollowedEntity();
if ( pParent )
{
// Get the parent's abs space world bounds.
CalcRenderableWorldSpaceAABB_Fast( pParent, absMins, absMaxs );
// Add the maximum of our local render bounds. This is making the assumption that we can be at any
// point and at any angle within the parent's world space bounds.
Vector vAddMins, vAddMaxs;
pEnt->GetRenderBounds( vAddMins, vAddMaxs );
// if our origin is actually farther away than that, expand again
float radius = pEnt->GetLocalOrigin().Length();
float flBloatSize = MAX( vAddMins.Length(), vAddMaxs.Length() );
flBloatSize = MAX(flBloatSize, radius);
absMins -= Vector( flBloatSize, flBloatSize, flBloatSize );
absMaxs += Vector( flBloatSize, flBloatSize, flBloatSize );
return;
}
}
Vector mins, maxs;
pRenderable->GetRenderBounds( mins, maxs );
// FIXME: Should I just use a sphere here?
// Another option is to pass the OBB down the tree; makes for a better fit
// Generate a world-aligned AABB
const QAngle& angles = pRenderable->GetRenderAngles();
const Vector& origin = pRenderable->GetRenderOrigin();
if (angles == vec3_angle)
{
VectorAdd( mins, origin, absMins );
VectorAdd( maxs, origin, absMaxs );
}
else
{
matrix3x4_t boxToWorld;
AngleMatrix( angles, origin, boxToWorld );
TransformAABB( boxToWorld, mins, maxs, absMins, absMaxs );
}
Assert( absMins.IsValid() && absMaxs.IsValid() );
}
// Figure out a world space bounding box that encloses the entity's local render bounds in world space.
inline void CalcRenderableWorldSpaceAABB(
IClientRenderable *pRenderable,
Vector &absMins,
Vector &absMaxs )
{
pRenderable->GetRenderBoundsWorldspace( absMins, absMaxs );
}
// This gets an AABB for the renderable, but it doesn't cause a parent's bones to be setup.
// This is used for placement in the leaves, but the more expensive version is used for culling.
void CalcRenderableWorldSpaceAABB_Fast( IClientRenderable *pRenderable, Vector &absMin, Vector &absMax )
{
C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity();
if ( pEnt && pEnt->IsFollowingEntity() )
{
C_BaseEntity *pParent = pEnt->GetMoveParent();
Assert( pParent );
// Get the parent's abs space world bounds.
CalcRenderableWorldSpaceAABB_Fast( pParent, absMin, absMax );
// Add the maximum of our local render bounds. This is making the assumption that we can be at any
// point and at any angle within the parent's world space bounds.
Vector vAddMins, vAddMaxs;
pEnt->GetRenderBounds( vAddMins, vAddMaxs );
// if our origin is actually farther away than that, expand again
float radius = pEnt->GetLocalOrigin().Length();
float flBloatSize = MAX( vAddMins.Length(), vAddMaxs.Length() );
flBloatSize = MAX(flBloatSize, radius);
absMin -= Vector( flBloatSize, flBloatSize, flBloatSize );
absMax += Vector( flBloatSize, flBloatSize, flBloatSize );
}
else
{
// Start out with our own render bounds. Since we don't have a parent, this won't incur any nasty
CalcRenderableWorldSpaceAABB( pRenderable, absMin, absMax );
}
}
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CClientLeafSystem::CClientLeafSystem() : m_DrawStaticProps(true), m_DrawSmallObjects(true)
{
// Set up the bi-directional lists...
m_RenderablesInLeaf.Init( FirstRenderableInLeaf, FirstLeafInRenderable );
m_ShadowsInLeaf.Init( FirstShadowInLeaf, FirstLeafInShadow );
m_ShadowsOnRenderable.Init( FirstShadowOnRenderable, FirstRenderableInShadow );
}
CClientLeafSystem::~CClientLeafSystem()
{
}
//-----------------------------------------------------------------------------
// Activate, deactivate static props
//-----------------------------------------------------------------------------
void CClientLeafSystem::DrawStaticProps( bool enable )
{
m_DrawStaticProps = enable;
}
void CClientLeafSystem::DrawSmallEntities( bool enable )
{
m_DrawSmallObjects = enable;
}
//-----------------------------------------------------------------------------
// Level init, shutdown
//-----------------------------------------------------------------------------
void CClientLeafSystem::LevelInitPreEntity()
{
MEM_ALLOC_CREDIT();
m_Renderables.EnsureCapacity( 1024 );
m_RenderablesInLeaf.EnsureCapacity( 1024 );
m_ShadowsInLeaf.EnsureCapacity( 256 );
m_ShadowsOnRenderable.EnsureCapacity( 256 );
m_DirtyRenderables.EnsureCapacity( 256 );
// Add all the leaves we'll need
int leafCount = engine->LevelLeafCount();
m_Leaf.EnsureCapacity( leafCount );
ClientLeaf_t newLeaf;
newLeaf.m_FirstElement = m_RenderablesInLeaf.InvalidIndex();
newLeaf.m_FirstShadow = m_ShadowsInLeaf.InvalidIndex();
memset( newLeaf.m_pSubSystemData, 0, sizeof( newLeaf.m_pSubSystemData ) );
newLeaf.m_FirstDetailProp = 0;
newLeaf.m_DetailPropCount = 0;
newLeaf.m_DetailPropRenderFrame = -1;
while ( --leafCount >= 0 )
{
m_Leaf.AddToTail( newLeaf );
}
}
void CClientLeafSystem::LevelShutdownPreEntity()
{
}
void CClientLeafSystem::LevelShutdownPostEntity()
{
m_ViewModels.Purge();
m_Renderables.Purge();
m_RenderablesInLeaf.Purge();
m_Shadows.Purge();
// delete subsystem data
for( int i = 0; i < m_Leaf.Count() ; i++ )
{
for( int j = 0 ; j < ARRAYSIZE( m_Leaf[i].m_pSubSystemData ) ; j++ )
{
if ( m_Leaf[i].m_pSubSystemData[j] )
{
delete m_Leaf[i].m_pSubSystemData[j];
m_Leaf[i].m_pSubSystemData[j] = NULL;
}
}
}
m_Leaf.Purge();
m_ShadowsInLeaf.Purge();
m_ShadowsOnRenderable.Purge();
m_DirtyRenderables.Purge();
}
//-----------------------------------------------------------------------------
// This is what happens before rendering a particular view
//-----------------------------------------------------------------------------
void CClientLeafSystem::PreRender()
{
VPROF_BUDGET( "CClientLeafSystem::PreRender", "PreRender" );
int i;
int nIterations = 0;
while ( m_DirtyRenderables.Count() )
{
if ( ++nIterations > 10 )
{
Warning( "Too many dirty renderables!\n" );
break;
}
int nDirty = m_DirtyRenderables.Count();
for ( i = nDirty; --i >= 0; )
{
ClientRenderHandle_t handle = m_DirtyRenderables[i];
Assert( m_Renderables[ handle ].m_Flags & RENDER_FLAGS_HASCHANGED );
// Update position in leaf system
RemoveFromTree( handle );
}
bool bThreaded = false;//( nDirty > 5 && cl_threaded_client_leaf_system.GetBool() && g_pThreadPool->NumThreads() );
if ( !bThreaded )
{
for ( i = nDirty; --i >= 0; )
{
InsertIntoTree( m_DirtyRenderables[i] );
}
}
else
{
// InsertIntoTree can result in new renderables being added, so copy:
ClientRenderHandle_t *pDirtyRenderables = (ClientRenderHandle_t *)alloca( sizeof(ClientRenderHandle_t) * nDirty );
memcpy( pDirtyRenderables, m_DirtyRenderables.Base(), sizeof(ClientRenderHandle_t) * nDirty );
ParallelProcess( "CClientLeafSystem::PreRender", pDirtyRenderables, nDirty, this, &CClientLeafSystem::InsertIntoTree, &CClientLeafSystem::FrameLock, &CClientLeafSystem::FrameUnlock );
}
if ( m_DeferredInserts.Count() )
{
EnumResultList_t enumResultList;
while ( m_DeferredInserts.PopItem( &enumResultList ) )
{
m_ShadowEnum++;
while ( enumResultList.pHead )
{
EnumResult_t *p = enumResultList.pHead;
enumResultList.pHead = p->pNext;
AddRenderableToLeaf( p->leaf, enumResultList.handle );
delete p;
}
}
}
for ( i = nDirty; --i >= 0; )
{
// Cache off the area it's sitting in.
ClientRenderHandle_t handle = m_DirtyRenderables[i];
RenderableInfo_t& renderable = m_Renderables[ handle ];
renderable.m_Flags &= ~RENDER_FLAGS_HASCHANGED;
m_Renderables[handle].m_Area = GetRenderableArea( handle );
}
m_DirtyRenderables.RemoveMultiple( 0, nDirty );
}
}
//-----------------------------------------------------------------------------
// Creates a new renderable
//-----------------------------------------------------------------------------
void CClientLeafSystem::NewRenderable( IClientRenderable* pRenderable, RenderGroup_t type, int flags )
{
Assert( pRenderable );
Assert( pRenderable->RenderHandle() == INVALID_CLIENT_RENDER_HANDLE );
ClientRenderHandle_t handle = m_Renderables.AddToTail();
RenderableInfo_t &info = m_Renderables[handle];
// We need to know if it's a brush model for shadows
int modelType = modelinfo->GetModelType( pRenderable->GetModel() );
if (modelType == mod_brush)
{
flags |= RENDER_FLAGS_BRUSH_MODEL;
}
else if ( modelType == mod_studio )
{
flags |= RENDER_FLAGS_STUDIO_MODEL;
}
info.m_pRenderable = pRenderable;
info.m_RenderFrame = -1;
info.m_RenderFrame2 = -1;
info.m_TranslucencyCalculated = -1;
info.m_TranslucencyCalculatedView = VIEW_ILLEGAL;
info.m_FirstShadow = m_ShadowsOnRenderable.InvalidIndex();
info.m_LeafList = m_RenderablesInLeaf.InvalidIndex();
info.m_Flags = flags;
info.m_RenderGroup = (unsigned char)type;
info.m_EnumCount = 0;
2022-03-01 20:00:42 +00:00
info.m_RenderLeaf = 0xFFFF;
2020-04-22 16:56:21 +00:00
if ( IsViewModelRenderGroup( (RenderGroup_t)info.m_RenderGroup ) )
{
AddToViewModelList( handle );
}
pRenderable->RenderHandle() = handle;
}
void CClientLeafSystem::CreateRenderableHandle( IClientRenderable* pRenderable, bool bIsStaticProp )
{
// FIXME: The argument is unnecessary if we could get this next line to work
// the reason why we can't is because currently there are IClientRenderables
// which don't correctly implement GetRefEHandle.
//bool bIsStaticProp = staticpropmgr->IsStaticProp( pRenderable->GetIClientUnknown() );
// Add the prop to all the leaves it lies in
RenderGroup_t group = pRenderable->IsTransparent() ? RENDER_GROUP_TRANSLUCENT_ENTITY : RENDER_GROUP_OPAQUE_ENTITY;
bool bTwoPass = false;
if ( group == RENDER_GROUP_TRANSLUCENT_ENTITY )
{
bTwoPass = pRenderable->IsTwoPass( );
}
int flags = 0;
if ( bIsStaticProp )
{
flags = RENDER_FLAGS_STATIC_PROP;
if ( group == RENDER_GROUP_OPAQUE_ENTITY )
{
group = RENDER_GROUP_OPAQUE_STATIC;
}
}
if (bTwoPass)
{
flags |= RENDER_FLAGS_TWOPASS;
}
NewRenderable( pRenderable, group, flags );
}
//-----------------------------------------------------------------------------
// Used to change renderables from translucent to opaque
//-----------------------------------------------------------------------------
void CClientLeafSystem::ChangeRenderableRenderGroup( ClientRenderHandle_t handle, RenderGroup_t group )
{
RenderableInfo_t &info = m_Renderables[handle];
info.m_RenderGroup = (unsigned char)group;
}
//-----------------------------------------------------------------------------
// Use alternate translucent sorting algorithm (draw translucent objects in the furthest leaf they lie in)
//-----------------------------------------------------------------------------
void CClientLeafSystem::EnableAlternateSorting( ClientRenderHandle_t handle, bool bEnable )
{
RenderableInfo_t &info = m_Renderables[handle];
if ( bEnable )
{
info.m_Flags |= RENDER_FLAGS_ALTERNATE_SORTING;
}
else
{
info.m_Flags &= ~RENDER_FLAGS_ALTERNATE_SORTING;
}
}
//-----------------------------------------------------------------------------
// Add/remove renderable
//-----------------------------------------------------------------------------
void CClientLeafSystem::AddRenderable( IClientRenderable* pRenderable, RenderGroup_t group )
{
// force a relink we we try to draw it for the first time
int flags = RENDER_FLAGS_HASCHANGED;
if ( group == RENDER_GROUP_TWOPASS )
{
group = RENDER_GROUP_TRANSLUCENT_ENTITY;
flags |= RENDER_FLAGS_TWOPASS;
}
NewRenderable( pRenderable, group, flags );
ClientRenderHandle_t handle = pRenderable->RenderHandle();
m_DirtyRenderables.AddToTail( handle );
}
void CClientLeafSystem::RemoveRenderable( ClientRenderHandle_t handle )
{
// This can happen upon level shutdown
if (!m_Renderables.IsValidIndex(handle))
return;
// Reset the render handle in the entity.
IClientRenderable *pRenderable = m_Renderables[handle].m_pRenderable;
Assert( handle == pRenderable->RenderHandle() );
pRenderable->RenderHandle() = INVALID_CLIENT_RENDER_HANDLE;
// Reemove the renderable from the dirty list
if ( m_Renderables[handle].m_Flags & RENDER_FLAGS_HASCHANGED )
{
// NOTE: This isn't particularly fast (linear search),
// but I'm assuming it's an unusual case where we remove
// renderables that are changing or that m_DirtyRenderables usually
// only has a couple entries
int i = m_DirtyRenderables.Find( handle );
Assert( i != m_DirtyRenderables.InvalidIndex() );
m_DirtyRenderables.FastRemove( i );
}
if ( IsViewModelRenderGroup( (RenderGroup_t)m_Renderables[handle].m_RenderGroup ) )
{
RemoveFromViewModelList( handle );
}
RemoveFromTree( handle );
m_Renderables.Remove( handle );
}
int CClientLeafSystem::GetRenderableLeaves( ClientRenderHandle_t handle, int leaves[128] )
{
if ( !m_Renderables.IsValidIndex( handle ) )
return -1;
RenderableInfo_t *pRenderable = &m_Renderables[handle];
if ( pRenderable->m_LeafList == m_RenderablesInLeaf.InvalidIndex() )
return -1;
int nLeaves = 0;
for ( int i=m_RenderablesInLeaf.FirstBucket( handle ); i != m_RenderablesInLeaf.InvalidIndex(); i = m_RenderablesInLeaf.NextBucket( i ) )
{
leaves[nLeaves++] = m_RenderablesInLeaf.Bucket( i );
if ( nLeaves >= 128 )
break;
}
return nLeaves;
}
//-----------------------------------------------------------------------------
// Retrieve leaf handles to leaves a renderable is in
// the pOutLeaf parameter is filled with the leaf the renderable is in.
// If pInIterator is not specified, pOutLeaf is the first leaf in the list.
// if pInIterator is specified, that iterator is used to return the next leaf
// in the list in pOutLeaf.
// the pOutIterator parameter is filled with the iterater which index to the pOutLeaf returned.
//
// Returns false on failure cases where pOutLeaf will be invalid. CHECK THE RETURN!
//-----------------------------------------------------------------------------
bool CClientLeafSystem::GetRenderableLeaf(ClientRenderHandle_t handle, int* pOutLeaf, const int* pInIterator /* = 0 */, int* pOutIterator /* = 0 */)
{
// bail on invalid handle
if ( !m_Renderables.IsValidIndex( handle ) )
return false;
// bail on no output value pointer
if ( !pOutLeaf )
return false;
// an iterator was specified
if ( pInIterator )
{
int iter = *pInIterator;
// test for invalid iterator
if ( iter == m_RenderablesInLeaf.InvalidIndex() )
return false;
int iterNext = m_RenderablesInLeaf.NextBucket( iter );
// test for end of list
if ( iterNext == m_RenderablesInLeaf.InvalidIndex() )
return false;
// Give the caller the iterator used
if ( pOutIterator )
{
*pOutIterator = iterNext;
}
// set output value to the next leaf
*pOutLeaf = m_RenderablesInLeaf.Bucket( iterNext );
}
else // no iter param, give them the first bucket in the renderable's list
{
int iter = m_RenderablesInLeaf.FirstBucket( handle );
if ( iter == m_RenderablesInLeaf.InvalidIndex() )
return false;
// Set output value to this leaf
*pOutLeaf = m_RenderablesInLeaf.Bucket( iter );
// give this iterator to caller
if ( pOutIterator )
{
*pOutIterator = iter;
}
}
return true;
}
bool CClientLeafSystem::IsRenderableInPVS( IClientRenderable *pRenderable )
{
ClientRenderHandle_t handle = pRenderable->RenderHandle();
int leaves[128];
int nLeaves = GetRenderableLeaves( handle, leaves );
if ( nLeaves == -1 )
return false;
// Ask the engine if this guy is visible.
return render->AreAnyLeavesVisible( leaves, nLeaves );
}
short CClientLeafSystem::GetRenderableArea( ClientRenderHandle_t handle )
{
int leaves[128];
int nLeaves = GetRenderableLeaves( handle, leaves );
if ( nLeaves == -1 )
return 0;
// Now ask the
return engine->GetLeavesArea( leaves, nLeaves );
}
void CClientLeafSystem::SetSubSystemDataInLeaf( int leaf, int nSubSystemIdx, CClientLeafSubSystemData *pData )
{
assert( nSubSystemIdx < N_CLSUBSYSTEMS );
if ( m_Leaf[leaf].m_pSubSystemData[nSubSystemIdx] )
delete m_Leaf[leaf].m_pSubSystemData[nSubSystemIdx];
m_Leaf[leaf].m_pSubSystemData[nSubSystemIdx] = pData;
}
CClientLeafSubSystemData *CClientLeafSystem::GetSubSystemDataInLeaf( int leaf, int nSubSystemIdx )
{
assert( nSubSystemIdx < N_CLSUBSYSTEMS );
return m_Leaf[leaf].m_pSubSystemData[nSubSystemIdx];
}
//-----------------------------------------------------------------------------
// Indicates which leaves detail objects are in
//-----------------------------------------------------------------------------
void CClientLeafSystem::SetDetailObjectsInLeaf( int leaf, int firstDetailObject,
int detailObjectCount )
{
m_Leaf[leaf].m_FirstDetailProp = firstDetailObject;
m_Leaf[leaf].m_DetailPropCount = detailObjectCount;
}
//-----------------------------------------------------------------------------
// Returns the detail objects in a leaf
//-----------------------------------------------------------------------------
void CClientLeafSystem::GetDetailObjectsInLeaf( int leaf, int& firstDetailObject,
int& detailObjectCount )
{
firstDetailObject = m_Leaf[leaf].m_FirstDetailProp;
detailObjectCount = m_Leaf[leaf].m_DetailPropCount;
}
//-----------------------------------------------------------------------------
// Create/destroy shadows...
//-----------------------------------------------------------------------------
ClientLeafShadowHandle_t CClientLeafSystem::AddShadow( ClientShadowHandle_t userId, unsigned short flags )
{
ClientLeafShadowHandle_t idx = m_Shadows.AddToTail();
m_Shadows[idx].m_Shadow = userId;
m_Shadows[idx].m_FirstLeaf = m_ShadowsInLeaf.InvalidIndex();
m_Shadows[idx].m_FirstRenderable = m_ShadowsOnRenderable.InvalidIndex();
m_Shadows[idx].m_EnumCount = 0;
m_Shadows[idx].m_Flags = flags;
return idx;
}
void CClientLeafSystem::RemoveShadow( ClientLeafShadowHandle_t handle )
{
// Remove the shadow from all leaves + renderables...
RemoveShadowFromLeaves( handle );
RemoveShadowFromRenderables( handle );
// Blow away the handle
m_Shadows.Remove( handle );
}
//-----------------------------------------------------------------------------
// Adds a shadow to a leaf/removes shadow from renderable
//-----------------------------------------------------------------------------
inline bool CClientLeafSystem::ShouldRenderableReceiveShadow( ClientRenderHandle_t renderHandle, int nShadowFlags )
{
RenderableInfo_t &renderable = m_Renderables[renderHandle];
if( !( renderable.m_Flags & ( RENDER_FLAGS_BRUSH_MODEL | RENDER_FLAGS_STATIC_PROP | RENDER_FLAGS_STUDIO_MODEL ) ) )
return false;
return renderable.m_pRenderable->ShouldReceiveProjectedTextures( nShadowFlags );
}
//-----------------------------------------------------------------------------
// Adds a shadow to a leaf/removes shadow from renderable
//-----------------------------------------------------------------------------
void CClientLeafSystem::AddShadowToRenderable( ClientRenderHandle_t renderHandle,
ClientLeafShadowHandle_t shadowHandle )
{
// Check if this renderable receives the type of projected texture that shadowHandle refers to.
int nShadowFlags = m_Shadows[shadowHandle].m_Flags;
if ( !ShouldRenderableReceiveShadow( renderHandle, nShadowFlags ) )
return;
m_ShadowsOnRenderable.AddElementToBucket( renderHandle, shadowHandle );
// Also, do some stuff specific to the particular types of renderables
// If the renderable is a brush model, then add this shadow to it
if (m_Renderables[renderHandle].m_Flags & RENDER_FLAGS_BRUSH_MODEL)
{
IClientRenderable* pRenderable = m_Renderables[renderHandle].m_pRenderable;
g_pClientShadowMgr->AddShadowToReceiver( m_Shadows[shadowHandle].m_Shadow,
pRenderable, SHADOW_RECEIVER_BRUSH_MODEL );
}
else if( m_Renderables[renderHandle].m_Flags & RENDER_FLAGS_STATIC_PROP )
{
IClientRenderable* pRenderable = m_Renderables[renderHandle].m_pRenderable;
g_pClientShadowMgr->AddShadowToReceiver( m_Shadows[shadowHandle].m_Shadow,
pRenderable, SHADOW_RECEIVER_STATIC_PROP );
}
else if( m_Renderables[renderHandle].m_Flags & RENDER_FLAGS_STUDIO_MODEL )
{
IClientRenderable* pRenderable = m_Renderables[renderHandle].m_pRenderable;
g_pClientShadowMgr->AddShadowToReceiver( m_Shadows[shadowHandle].m_Shadow,
pRenderable, SHADOW_RECEIVER_STUDIO_MODEL );
}
}
void CClientLeafSystem::RemoveShadowFromRenderables( ClientLeafShadowHandle_t handle )
{
m_ShadowsOnRenderable.RemoveElement( handle );
}
//-----------------------------------------------------------------------------
// Adds a shadow to a leaf/removes shadow from leaf
//-----------------------------------------------------------------------------
void CClientLeafSystem::AddShadowToLeaf( int leaf, ClientLeafShadowHandle_t shadow )
{
m_ShadowsInLeaf.AddElementToBucket( leaf, shadow );
// Add the shadow exactly once to all renderables in the leaf
2022-03-01 20:00:42 +00:00
unsigned short i = m_RenderablesInLeaf.FirstElement( leaf );
2020-04-22 16:56:21 +00:00
while ( i != m_RenderablesInLeaf.InvalidIndex() )
{
ClientRenderHandle_t renderable = m_RenderablesInLeaf.Element(i);
RenderableInfo_t& info = m_Renderables[renderable];
// Add each shadow exactly once to each renderable
if (info.m_EnumCount != m_ShadowEnum)
{
AddShadowToRenderable( renderable, shadow );
info.m_EnumCount = m_ShadowEnum;
}
Assert( m_ShadowsInLeaf.NumAllocated() < 2000 );
i = m_RenderablesInLeaf.NextElement(i);
}
}
void CClientLeafSystem::RemoveShadowFromLeaves( ClientLeafShadowHandle_t handle )
{
m_ShadowsInLeaf.RemoveElement( handle );
}
//-----------------------------------------------------------------------------
// Adds a shadow to all leaves listed
//-----------------------------------------------------------------------------
void CClientLeafSystem::ProjectShadow( ClientLeafShadowHandle_t handle, int nLeafCount, const int *pLeafList )
{
// Remove the shadow from any leaves it current exists in
RemoveShadowFromLeaves( handle );
RemoveShadowFromRenderables( handle );
Assert( ( m_Shadows[handle].m_Flags & SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) == SHADOW_FLAGS_SHADOW );
// This will help us to avoid adding the shadow multiple times to a renderable
++m_ShadowEnum;
for ( int i = 0; i < nLeafCount; ++i )
{
AddShadowToLeaf( pLeafList[i], handle );
}
}
void CClientLeafSystem::ProjectFlashlight( ClientLeafShadowHandle_t handle, int nLeafCount, const int *pLeafList )
{
VPROF_BUDGET( "CClientLeafSystem::ProjectFlashlight", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
// Remove the shadow from any leaves it current exists in
RemoveShadowFromLeaves( handle );
RemoveShadowFromRenderables( handle );
Assert( ( m_Shadows[handle].m_Flags & SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) == SHADOW_FLAGS_FLASHLIGHT );
// This will help us to avoid adding the shadow multiple times to a renderable
++m_ShadowEnum;
for ( int i = 0; i < nLeafCount; ++i )
{
AddShadowToLeaf( pLeafList[i], handle );
}
}
//-----------------------------------------------------------------------------
// Find all shadow casters in a set of leaves
//-----------------------------------------------------------------------------
void CClientLeafSystem::EnumerateShadowsInLeaves( int leafCount, LeafIndex_t* pLeaves, IClientLeafShadowEnum* pEnum )
{
if (leafCount == 0)
return;
// This will help us to avoid enumerating the shadow multiple times
++m_ShadowEnum;
for (int i = 0; i < leafCount; ++i)
{
int leaf = pLeaves[i];
unsigned short j = m_ShadowsInLeaf.FirstElement( leaf );
while ( j != m_ShadowsInLeaf.InvalidIndex() )
{
ClientLeafShadowHandle_t shadow = m_ShadowsInLeaf.Element(j);
ShadowInfo_t& info = m_Shadows[shadow];
if (info.m_EnumCount != m_ShadowEnum)
{
pEnum->EnumShadow(info.m_Shadow);
info.m_EnumCount = m_ShadowEnum;
}
j = m_ShadowsInLeaf.NextElement(j);
}
}
}
//-----------------------------------------------------------------------------
// Adds a renderable to a leaf
//-----------------------------------------------------------------------------
void CClientLeafSystem::AddRenderableToLeaf( int leaf, ClientRenderHandle_t renderable )
{
#ifdef VALIDATE_CLIENT_LEAF_SYSTEM
m_RenderablesInLeaf.ValidateAddElementToBucket( leaf, renderable );
#endif
2022-03-01 20:00:42 +00:00
m_RenderablesInLeaf.AddElementToBucket( leaf, renderable );
2020-04-22 16:56:21 +00:00
if ( !ShouldRenderableReceiveShadow( renderable, SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) )
return;
// Add all shadows in the leaf to the renderable...
unsigned short i = m_ShadowsInLeaf.FirstElement( leaf );
while (i != m_ShadowsInLeaf.InvalidIndex() )
{
ClientLeafShadowHandle_t shadow = m_ShadowsInLeaf.Element(i);
ShadowInfo_t& info = m_Shadows[shadow];
// Add each shadow exactly once to each renderable
if (info.m_EnumCount != m_ShadowEnum)
{
AddShadowToRenderable( renderable, shadow );
info.m_EnumCount = m_ShadowEnum;
}
i = m_ShadowsInLeaf.NextElement(i);
}
}
//-----------------------------------------------------------------------------
// Adds a renderable to a set of leaves
//-----------------------------------------------------------------------------
void CClientLeafSystem::AddRenderableToLeaves( ClientRenderHandle_t handle, int nLeafCount, unsigned short *pLeaves )
{
for (int j = 0; j < nLeafCount; ++j)
{
AddRenderableToLeaf( pLeaves[j], handle );
}
m_Renderables[handle].m_Area = GetRenderableArea( handle );
}
//-----------------------------------------------------------------------------
// Inserts an element into the tree
//-----------------------------------------------------------------------------
2022-02-23 11:50:30 +00:00
bool CClientLeafSystem::EnumerateLeaf( int leaf, intp context )
2020-04-22 16:56:21 +00:00
{
EnumResultList_t *pList = (EnumResultList_t *)context;
if ( ThreadInMainThread() )
{
AddRenderableToLeaf( leaf, pList->handle );
}
else
{
EnumResult_t *p = new EnumResult_t;
p->leaf = leaf;
p->pNext = pList->pHead;
pList->pHead = p;
}
return true;
}
void CClientLeafSystem::InsertIntoTree( ClientRenderHandle_t &handle )
{
if ( ThreadInMainThread() )
{
// When we insert into the tree, increase the shadow enumerator
// to make sure each shadow is added exactly once to each renderable
m_ShadowEnum++;
}
EnumResultList_t list = { NULL, handle };
// NOTE: The render bounds here are relative to the renderable's coordinate system
IClientRenderable* pRenderable = m_Renderables[handle].m_pRenderable;
Vector absMins, absMaxs;
CalcRenderableWorldSpaceAABB_Fast( pRenderable, absMins, absMaxs );
Assert( absMins.IsValid() && absMaxs.IsValid() );
ISpatialQuery* pQuery = engine->GetBSPTreeQuery();
2022-02-23 11:50:30 +00:00
pQuery->EnumerateLeavesInBox( absMins, absMaxs, this, (intp)&list );
2020-04-22 16:56:21 +00:00
if ( list.pHead )
{
m_DeferredInserts.PushItem( list );
}
}
//-----------------------------------------------------------------------------
// Removes an element from the tree
//-----------------------------------------------------------------------------
void CClientLeafSystem::RemoveFromTree( ClientRenderHandle_t handle )
{
m_RenderablesInLeaf.RemoveElement( handle );
// Remove all shadows cast onto the object
m_ShadowsOnRenderable.RemoveBucket( handle );
// If the renderable is a brush model, then remove all shadows from it
if (m_Renderables[handle].m_Flags & RENDER_FLAGS_BRUSH_MODEL)
{
g_pClientShadowMgr->RemoveAllShadowsFromReceiver(
m_Renderables[handle].m_pRenderable, SHADOW_RECEIVER_BRUSH_MODEL );
}
else if( m_Renderables[handle].m_Flags & RENDER_FLAGS_STUDIO_MODEL )
{
g_pClientShadowMgr->RemoveAllShadowsFromReceiver(
m_Renderables[handle].m_pRenderable, SHADOW_RECEIVER_STUDIO_MODEL );
}
}
//-----------------------------------------------------------------------------
// Call this when the renderable moves
//-----------------------------------------------------------------------------
void CClientLeafSystem::RenderableChanged( ClientRenderHandle_t handle )
{
Assert ( handle != INVALID_CLIENT_RENDER_HANDLE );
Assert( m_Renderables.IsValidIndex( handle ) );
if ( !m_Renderables.IsValidIndex( handle ) )
return;
if ( (m_Renderables[handle].m_Flags & RENDER_FLAGS_HASCHANGED ) == 0 )
{
m_Renderables[handle].m_Flags |= RENDER_FLAGS_HASCHANGED;
m_DirtyRenderables.AddToTail( handle );
}
#if _DEBUG
else
{
// It had better be in the list
Assert( m_DirtyRenderables.Find( handle ) != m_DirtyRenderables.InvalidIndex() );
}
#endif
}
//-----------------------------------------------------------------------------
// Returns if it's a view model render group
//-----------------------------------------------------------------------------
inline bool CClientLeafSystem::IsViewModelRenderGroup( RenderGroup_t group ) const
{
return (group == RENDER_GROUP_VIEW_MODEL_TRANSLUCENT) || (group == RENDER_GROUP_VIEW_MODEL_OPAQUE);
}
//-----------------------------------------------------------------------------
// Adds, removes renderables from view model list
//-----------------------------------------------------------------------------
void CClientLeafSystem::AddToViewModelList( ClientRenderHandle_t handle )
{
MEM_ALLOC_CREDIT();
Assert( m_ViewModels.Find( handle ) == m_ViewModels.InvalidIndex() );
m_ViewModels.AddToTail( handle );
}
void CClientLeafSystem::RemoveFromViewModelList( ClientRenderHandle_t handle )
{
int i = m_ViewModels.Find( handle );
Assert( i != m_ViewModels.InvalidIndex() );
m_ViewModels.FastRemove( i );
}
//-----------------------------------------------------------------------------
// Call this to change the render group
//-----------------------------------------------------------------------------
void CClientLeafSystem::SetRenderGroup( ClientRenderHandle_t handle, RenderGroup_t group )
{
RenderableInfo_t *pInfo = &m_Renderables[handle];
bool twoPass = false;
if ( group == RENDER_GROUP_TWOPASS )
{
twoPass = true;
group = RENDER_GROUP_TRANSLUCENT_ENTITY;
}
if ( twoPass )
{
pInfo->m_Flags |= RENDER_FLAGS_TWOPASS;
}
else
{
pInfo->m_Flags &= ~RENDER_FLAGS_TWOPASS;
}
bool bOldViewModelRenderGroup = IsViewModelRenderGroup( (RenderGroup_t)pInfo->m_RenderGroup );
bool bNewViewModelRenderGroup = IsViewModelRenderGroup( group );
if ( bOldViewModelRenderGroup != bNewViewModelRenderGroup )
{
if ( bOldViewModelRenderGroup )
{
RemoveFromViewModelList( handle );
}
else
{
AddToViewModelList( handle );
}
}
pInfo->m_RenderGroup = group;
}
//-----------------------------------------------------------------------------
// Detail system marks
//-----------------------------------------------------------------------------
void CClientLeafSystem::DrawDetailObjectsInLeaf( int leaf, int nFrameNumber, int& nFirstDetailObject, int& nDetailObjectCount )
{
ClientLeaf_t &leafInfo = m_Leaf[leaf];
leafInfo.m_DetailPropRenderFrame = nFrameNumber;
nFirstDetailObject = leafInfo.m_FirstDetailProp;
nDetailObjectCount = leafInfo.m_DetailPropCount;
}
//-----------------------------------------------------------------------------
// Are we close enough to this leaf to draw detail props *and* are there any props in the leaf?
//-----------------------------------------------------------------------------
bool CClientLeafSystem::ShouldDrawDetailObjectsInLeaf( int leaf, int frameNumber )
{
ClientLeaf_t &leafInfo = m_Leaf[leaf];
return ( (leafInfo.m_DetailPropRenderFrame == frameNumber ) &&
( ( leafInfo.m_DetailPropCount != 0 ) || ( leafInfo.m_pSubSystemData[CLSUBSYSTEM_DETAILOBJECTS] ) ) );
}
//-----------------------------------------------------------------------------
// Compute which leaf the translucent renderables should render in
//-----------------------------------------------------------------------------
void CClientLeafSystem::ComputeTranslucentRenderLeaf( int count, const LeafIndex_t *pLeafList, const LeafFogVolume_t *pLeafFogVolumeList, int frameNumber, int viewID )
{
ASSERT_NO_REENTRY();
VPROF_BUDGET( "CClientLeafSystem::ComputeTranslucentRenderLeaf", "ComputeTranslucentRenderLeaf" );
#define LeafToMarker( leaf ) reinterpret_cast<RenderableInfo_t *>(( (leaf) << 1 ) | 1)
#define IsLeafMarker( p ) (bool)((reinterpret_cast<size_t>(p)) & 1)
#define MarkerToLeaf( p ) (int)((reinterpret_cast<size_t>(p)) >> 1)
// For better sorting, we're gonna choose the leaf that is closest to the camera.
// The leaf list passed in here is sorted front to back
bool bThreaded = false;//( cl_threaded_client_leaf_system.GetBool() && g_pThreadPool->NumThreads() );
int globalFrameCount = gpGlobals->framecount;
int i;
static CUtlVector<RenderableInfo_t *> orderedList; // @MULTICORE (toml 8/30/2006): will need to make non-static if thread this function
static CUtlVector<IClientRenderable *> renderablesToUpdate;
2022-12-16 16:20:56 +00:00
intp leaf = 0;
2020-04-22 16:56:21 +00:00
for ( i = 0; i < count; ++i )
{
leaf = pLeafList[i];
orderedList.AddToTail( LeafToMarker( leaf ) );
// iterate over all elements in this leaf
2022-03-01 20:00:42 +00:00
unsigned short idx = m_RenderablesInLeaf.FirstElement(leaf);
2020-04-22 16:56:21 +00:00
while (idx != m_RenderablesInLeaf.InvalidIndex())
{
RenderableInfo_t& info = m_Renderables[m_RenderablesInLeaf.Element(idx)];
if ( info.m_TranslucencyCalculated != globalFrameCount || info.m_TranslucencyCalculatedView != viewID )
{
// Compute translucency
if ( bThreaded )
{
renderablesToUpdate.AddToTail( info.m_pRenderable );
}
else
{
info.m_pRenderable->ComputeFxBlend();
}
info.m_TranslucencyCalculated = globalFrameCount;
info.m_TranslucencyCalculatedView = viewID;
}
orderedList.AddToTail( &info );
idx = m_RenderablesInLeaf.NextElement(idx);
}
}
if ( bThreaded )
{
ParallelProcess( "CClientLeafSystem::ComputeTranslucentRenderLeaf", renderablesToUpdate.Base(), renderablesToUpdate.Count(), &CallComputeFXBlend, &::FrameLock, &::FrameUnlock );
renderablesToUpdate.RemoveAll();
}
for ( i = 0; i != orderedList.Count(); i++ )
{
RenderableInfo_t *pInfo = orderedList[i];
if ( !IsLeafMarker( pInfo ) )
{
if( pInfo->m_RenderFrame != frameNumber )
{
if( pInfo->m_RenderGroup == RENDER_GROUP_TRANSLUCENT_ENTITY )
{
pInfo->m_RenderLeaf = leaf;
}
pInfo->m_RenderFrame = frameNumber;
}
else if ( pInfo->m_Flags & RENDER_FLAGS_ALTERNATE_SORTING )
{
if( pInfo->m_RenderGroup == RENDER_GROUP_TRANSLUCENT_ENTITY )
{
pInfo->m_RenderLeaf = leaf;
}
}
}
else
{
leaf = MarkerToLeaf( pInfo );
}
}
orderedList.RemoveAll();
}
//-----------------------------------------------------------------------------
// Adds a renderable to the list of renderables to render this frame
//-----------------------------------------------------------------------------
inline void AddRenderableToRenderList( CClientRenderablesList &renderList, IClientRenderable *pRenderable,
int iLeaf, RenderGroup_t group, ClientRenderHandle_t renderHandle, bool bTwoPass = false )
{
#ifdef _DEBUG
if (cl_drawleaf.GetInt() >= 0)
{
if (iLeaf != cl_drawleaf.GetInt())
return;
}
#endif
Assert( group >= 0 && group < RENDER_GROUP_COUNT );
int &curCount = renderList.m_RenderGroupCounts[group];
if ( curCount < CClientRenderablesList::MAX_GROUP_ENTITIES )
{
Assert( (iLeaf >= 0) && (iLeaf <= 65535) );
CClientRenderablesList::CEntry *pEntry = &renderList.m_RenderGroups[group][curCount];
pEntry->m_pRenderable = pRenderable;
pEntry->m_iWorldListInfoLeaf = iLeaf;
pEntry->m_TwoPass = bTwoPass;
pEntry->m_RenderHandle = renderHandle;
curCount++;
}
else
{
engine->Con_NPrintf( 10, "Warning: overflowed CClientRenderablesList group %d", group );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : renderList -
// renderGroup -
//-----------------------------------------------------------------------------
void CClientLeafSystem::CollateViewModelRenderables( CUtlVector< IClientRenderable * >& opaque, CUtlVector< IClientRenderable * >& translucent )
{
for ( int i = m_ViewModels.Count()-1; i >= 0; --i )
{
ClientRenderHandle_t handle = m_ViewModels[i];
RenderableInfo_t& renderable = m_Renderables[handle];
// NOTE: In some cases, this removes the entity from the view model list
renderable.m_pRenderable->ComputeFxBlend();
// That's why we need to test RENDER_GROUP_OPAQUE_ENTITY - it may have changed in ComputeFXBlend()
if ( renderable.m_RenderGroup == RENDER_GROUP_VIEW_MODEL_OPAQUE || renderable.m_RenderGroup == RENDER_GROUP_OPAQUE_ENTITY )
{
opaque.AddToTail( renderable.m_pRenderable );
}
else
{
translucent.AddToTail( renderable.m_pRenderable );
}
}
}
static RenderGroup_t DetectBucketedRenderGroup( RenderGroup_t group, float fDimension )
{
float const arrThresholds[ 3 ] = {
200.f, // tree size
80.f, // player size
30.f, // crate size
};
Assert( ARRAYSIZE( arrThresholds ) + 1 >= RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS );
Assert( group >= RENDER_GROUP_OPAQUE_STATIC && group <= RENDER_GROUP_OPAQUE_ENTITY );
int bucketedGroupIndex;
if ( RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS <= 2 ||
fDimension >= arrThresholds[1] )
{
if ( RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS <= 1 ||
fDimension >= arrThresholds[0] )
bucketedGroupIndex = 0;
else
bucketedGroupIndex = 1;
}
else
{
if ( RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS <= 3 ||
fDimension >= arrThresholds[2] )
bucketedGroupIndex = 2;
else
bucketedGroupIndex = 3;
}
// Determine the new bucketed group
RenderGroup_t bucketedGroup = RenderGroup_t( group - ( ( RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS - 1 ) - bucketedGroupIndex ) * 2 );
Assert( bucketedGroup >= RENDER_GROUP_OPAQUE_STATIC_HUGE && bucketedGroup <= RENDER_GROUP_OPAQUE_ENTITY );
return bucketedGroup;
}
void CClientLeafSystem::CollateRenderablesInLeaf( int leaf, int worldListLeafIndex, const SetupRenderInfo_t &info )
{
bool portalTestEnts = r_PortalTestEnts.GetBool() && !r_portalsopenall.GetBool();
// Place a fake entity for static/opaque ents in this leaf
AddRenderableToRenderList( *info.m_pRenderList, NULL, worldListLeafIndex, RENDER_GROUP_OPAQUE_STATIC, NULL );
AddRenderableToRenderList( *info.m_pRenderList, NULL, worldListLeafIndex, RENDER_GROUP_OPAQUE_ENTITY, NULL );
// Collate everything.
2022-03-01 20:00:42 +00:00
unsigned short idx = m_RenderablesInLeaf.FirstElement(leaf);
2020-04-22 16:56:21 +00:00
for ( ;idx != m_RenderablesInLeaf.InvalidIndex(); idx = m_RenderablesInLeaf.NextElement(idx) )
{
ClientRenderHandle_t handle = m_RenderablesInLeaf.Element(idx);
RenderableInfo_t& renderable = m_Renderables[handle];
// Early out on static props if we don't want to render them
if ((!m_DrawStaticProps) && (renderable.m_Flags & RENDER_FLAGS_STATIC_PROP))
continue;
// Early out if we're told to not draw small objects (top view only,
/* that's why we don't check the z component).
if (!m_DrawSmallObjects)
{
CCachedRenderInfo& cachedInfo = m_CachedRenderInfos[renderable.m_CachedRenderInfo];
float sizeX = cachedInfo.m_Maxs.x - cachedInfo.m_Mins.x;
float sizeY = cachedInfo.m_Maxs.y - cachedInfo.m_Mins.y;
if ((sizeX < 50.f) && (sizeY < 50.f))
continue;
}*/
Assert( m_DrawSmallObjects ); // MOTODO
// Don't hit the same ent in multiple leaves twice.
if ( renderable.m_RenderGroup != RENDER_GROUP_TRANSLUCENT_ENTITY )
{
if ( renderable.m_RenderFrame2 == info.m_nRenderFrame )
continue;
renderable.m_RenderFrame2 = info.m_nRenderFrame;
}
else // translucent
{
// Shadow depth skips ComputeTranslucentRenderLeaf!
// Translucent entities already have had ComputeTranslucentRenderLeaf called on them
// so m_RenderLeaf should be set to the nearest leaf, so that's what we want here.
if ( renderable.m_RenderLeaf != leaf )
continue;
}
unsigned char nAlpha = 255;
if ( info.m_bDrawTranslucentObjects )
{
// Prevent culling if the renderable is invisible
// NOTE: OPAQUE objects can have alpha == 0.
// They are made to be opaque because they don't have to be sorted.
nAlpha = renderable.m_pRenderable->GetFxBlend();
if ( nAlpha == 0 )
continue;
}
Vector absMins, absMaxs;
CalcRenderableWorldSpaceAABB( renderable.m_pRenderable, absMins, absMaxs );
// If the renderable is inside an area, cull it using the frustum for that area.
if ( portalTestEnts && renderable.m_Area != -1 )
{
VPROF( "r_PortalTestEnts" );
if ( !engine->DoesBoxTouchAreaFrustum( absMins, absMaxs, renderable.m_Area ) )
continue;
}
else
{
// cull with main frustum
if ( engine->CullBox( absMins, absMaxs ) )
continue;
}
// UNDONE: Investigate speed tradeoffs of occlusion culling brush models too?
if ( renderable.m_Flags & RENDER_FLAGS_STUDIO_MODEL )
{
// test to see if this renderable is occluded by the engine's occlusion system
if ( engine->IsOccluded( absMins, absMaxs ) )
continue;
}
#ifdef INVASION_CLIENT_DLL
if (info.m_flRenderDistSq != 0.0f)
{
Vector mins, maxs;
renderable.m_pRenderable->GetRenderBounds( mins, maxs );
if ((maxs.z - mins.z) < 100)
{
Vector vCenter;
VectorLerp( mins, maxs, 0.5f, vCenter );
vCenter += renderable.m_pRenderable->GetRenderOrigin();
float flDistSq = info.m_vecRenderOrigin.DistToSqr( vCenter );
if (info.m_flRenderDistSq <= flDistSq)
continue;
}
}
#endif
if( renderable.m_RenderGroup != RENDER_GROUP_TRANSLUCENT_ENTITY )
{
RenderGroup_t group = (RenderGroup_t)renderable.m_RenderGroup;
// Determine object group offset
if ( RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS > 1 &&
group >= RENDER_GROUP_OPAQUE_STATIC &&
group <= RENDER_GROUP_OPAQUE_ENTITY )
{
Vector dims;
VectorSubtract( absMaxs, absMins, dims );
float const fDimension = MAX( MAX( fabs(dims.x), fabs(dims.y) ), fabs(dims.z) );
group = DetectBucketedRenderGroup( group, fDimension );
Assert( group >= RENDER_GROUP_OPAQUE_STATIC_HUGE && group <= RENDER_GROUP_OPAQUE_ENTITY );
}
AddRenderableToRenderList( *info.m_pRenderList, renderable.m_pRenderable,
worldListLeafIndex, group, handle);
}
else
{
bool bTwoPass = ((renderable.m_Flags & RENDER_FLAGS_TWOPASS) != 0) && ( nAlpha == 255 ); // Two pass?
// Add to appropriate list if drawing translucent objects (shadow depth mapping will skip this)
if ( info.m_bDrawTranslucentObjects )
{
AddRenderableToRenderList( *info.m_pRenderList, renderable.m_pRenderable,
worldListLeafIndex, (RenderGroup_t)renderable.m_RenderGroup, handle, bTwoPass );
}
if ( bTwoPass ) // Also add to opaque list if it's a two-pass model...
{
AddRenderableToRenderList( *info.m_pRenderList, renderable.m_pRenderable,
worldListLeafIndex, RENDER_GROUP_OPAQUE_ENTITY, handle, bTwoPass );
}
}
}
// Do detail objects.
// These don't have render handles!
if ( info.m_bDrawDetailObjects && ShouldDrawDetailObjectsInLeaf( leaf, info.m_nDetailBuildFrame ) )
{
idx = m_Leaf[leaf].m_FirstDetailProp;
int count = m_Leaf[leaf].m_DetailPropCount;
while( --count >= 0 )
{
IClientRenderable* pRenderable = DetailObjectSystem()->GetDetailModel(idx);
// FIXME: This if check here is necessary because the detail object system also maintains lists of sprites...
if (pRenderable)
{
if( pRenderable->IsTransparent() )
{
if ( info.m_bDrawTranslucentObjects ) // Don't draw translucent objects into shadow depth maps
{
// Lots of the detail entities are invisible so avoid sorting them and all that.
if( pRenderable->GetFxBlend() > 0 )
{
AddRenderableToRenderList( *info.m_pRenderList, pRenderable,
worldListLeafIndex, RENDER_GROUP_TRANSLUCENT_ENTITY, DETAIL_PROP_RENDER_HANDLE );
}
}
}
else
{
AddRenderableToRenderList( *info.m_pRenderList, pRenderable,
worldListLeafIndex, RENDER_GROUP_OPAQUE_ENTITY, DETAIL_PROP_RENDER_HANDLE );
}
}
++idx;
}
}
}
//-----------------------------------------------------------------------------
// Sort entities in a back-to-front ordering
//-----------------------------------------------------------------------------
void CClientLeafSystem::SortEntities( const Vector &vecRenderOrigin, const Vector &vecRenderForward, CClientRenderablesList::CEntry *pEntities, int nEntities )
{
// Don't sort if we only have 1 entity
if ( nEntities <= 1 )
return;
float dists[CClientRenderablesList::MAX_GROUP_ENTITIES];
// First get a distance for each entity.
int i;
for( i=0; i < nEntities; i++ )
{
IClientRenderable *pRenderable = pEntities[i].m_pRenderable;
// Compute the center of the object (needed for translucent brush models)
Vector boxcenter;
Vector mins,maxs;
pRenderable->GetRenderBounds( mins, maxs );
VectorAdd( mins, maxs, boxcenter );
VectorMA( pRenderable->GetRenderOrigin(), 0.5f, boxcenter, boxcenter );
// Compute distance...
Vector delta;
VectorSubtract( boxcenter, vecRenderOrigin, delta );
dists[i] = DotProduct( delta, vecRenderForward );
}
// H-sort.
int stepSize = 4;
while( stepSize )
{
int end = nEntities - stepSize;
for( i=0; i < end; i += stepSize )
{
if( dists[i] > dists[i+stepSize] )
{
::V_swap( pEntities[i], pEntities[i+stepSize] );
::V_swap( dists[i], dists[i+stepSize] );
if( i == 0 )
{
i = -stepSize;
}
else
{
i -= stepSize << 1;
}
}
}
stepSize >>= 1;
}
}
void CClientLeafSystem::BuildRenderablesList( const SetupRenderInfo_t &info )
{
VPROF_BUDGET( "BuildRenderablesList", "BuildRenderablesList" );
int leafCount = info.m_pWorldListInfo->m_LeafCount;
const Vector &vecRenderOrigin = info.m_vecRenderOrigin;
const Vector &vecRenderForward = info.m_vecRenderForward;
CClientRenderablesList::CEntry *pTranslucentEntries = info.m_pRenderList->m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY];
int &nTranslucentEntries = info.m_pRenderList->m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY];
for( int i = 0; i < leafCount; i++ )
{
int nTranslucent = nTranslucentEntries;
// Add renderables from this leaf...
CollateRenderablesInLeaf( info.m_pWorldListInfo->m_pLeafList[i], i, info );
int nNewTranslucent = nTranslucentEntries - nTranslucent;
if( (nNewTranslucent != 0 ) && info.m_bDrawTranslucentObjects )
{
// Sort the new translucent entities.
SortEntities( vecRenderOrigin, vecRenderForward, &pTranslucentEntries[nTranslucent], nNewTranslucent );
}
}
}