aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/client/clientleafsystem.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/client/clientleafsystem.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/client/clientleafsystem.cpp')
-rw-r--r--mp/src/game/client/clientleafsystem.cpp3536
1 files changed, 1768 insertions, 1768 deletions
diff --git a/mp/src/game/client/clientleafsystem.cpp b/mp/src/game/client/clientleafsystem.cpp
index 38ee2ae3..0a4018f1 100644
--- a/mp/src/game/client/clientleafsystem.cpp
+++ b/mp/src/game/client/clientleafsystem.cpp
@@ -1,1768 +1,1768 @@
-//========= 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:
-
- bool EnumerateLeaf( int leaf, int context );
-
- // 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
- static unsigned short& FirstRenderableInLeaf( int leaf )
- {
- return s_ClientLeafSystem.m_Leaf[leaf].m_FirstElement;
- }
-
- static unsigned short& FirstLeafInRenderable( unsigned short renderable )
- {
- 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;
- unsigned short m_LeafList; // What leafs is it in?
- unsigned short m_RenderLeaf; // What leaf do I render in?
- 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
- {
- unsigned short m_FirstElement;
- 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
- CBidirectionalSet< int, ClientRenderHandle_t, unsigned short, unsigned int > m_RenderablesInLeaf;
-
- // 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.
- C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity();
- 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;
- info.m_RenderLeaf = 0xFFFF;
- 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
- unsigned short i = m_RenderablesInLeaf.FirstElement( leaf );
- 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
- m_RenderablesInLeaf.AddElementToBucket( leaf, renderable );
-
- 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
-//-----------------------------------------------------------------------------
-bool CClientLeafSystem::EnumerateLeaf( int leaf, int context )
-{
- 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();
- pQuery->EnumerateLeavesInBox( absMins, absMaxs, this, (int)&list );
-
- 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;
- int leaf = 0;
- for ( i = 0; i < count; ++i )
- {
- leaf = pLeafList[i];
- orderedList.AddToTail( LeafToMarker( leaf ) );
-
- // iterate over all elements in this leaf
- unsigned short idx = m_RenderablesInLeaf.FirstElement(leaf);
- 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.
- unsigned short idx = m_RenderablesInLeaf.FirstElement(leaf);
- 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 );
- }
- }
-}
+//========= 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:
+
+ bool EnumerateLeaf( int leaf, int context );
+
+ // 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
+ static unsigned short& FirstRenderableInLeaf( int leaf )
+ {
+ return s_ClientLeafSystem.m_Leaf[leaf].m_FirstElement;
+ }
+
+ static unsigned short& FirstLeafInRenderable( unsigned short renderable )
+ {
+ 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;
+ unsigned short m_LeafList; // What leafs is it in?
+ unsigned short m_RenderLeaf; // What leaf do I render in?
+ 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
+ {
+ unsigned short m_FirstElement;
+ 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
+ CBidirectionalSet< int, ClientRenderHandle_t, unsigned short, unsigned int > m_RenderablesInLeaf;
+
+ // 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.
+ C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity();
+ 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;
+ info.m_RenderLeaf = 0xFFFF;
+ 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
+ unsigned short i = m_RenderablesInLeaf.FirstElement( leaf );
+ 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
+ m_RenderablesInLeaf.AddElementToBucket( leaf, renderable );
+
+ 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
+//-----------------------------------------------------------------------------
+bool CClientLeafSystem::EnumerateLeaf( int leaf, int context )
+{
+ 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();
+ pQuery->EnumerateLeavesInBox( absMins, absMaxs, this, (int)&list );
+
+ 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;
+ int leaf = 0;
+ for ( i = 0; i < count; ++i )
+ {
+ leaf = pLeafList[i];
+ orderedList.AddToTail( LeafToMarker( leaf ) );
+
+ // iterate over all elements in this leaf
+ unsigned short idx = m_RenderablesInLeaf.FirstElement(leaf);
+ 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.
+ unsigned short idx = m_RenderablesInLeaf.FirstElement(leaf);
+ 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 );
+ }
+ }
+}