From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/client/clientleafsystem.cpp | 1768 +++++++++++++++++++++++++++++++ 1 file changed, 1768 insertions(+) create mode 100644 mp/src/game/client/clientleafsystem.cpp (limited to 'mp/src/game/client/clientleafsystem.cpp') diff --git a/mp/src/game/client/clientleafsystem.cpp b/mp/src/game/client/clientleafsystem.cpp new file mode 100644 index 00000000..38ee2ae3 --- /dev/null +++ b/mp/src/game/client/clientleafsystem.cpp @@ -0,0 +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 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(( (leaf) << 1 ) | 1) + #define IsLeafMarker( p ) (bool)((reinterpret_cast(p)) & 1) + #define MarkerToLeaf( p ) (int)((reinterpret_cast(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 orderedList; // @MULTICORE (toml 8/30/2006): will need to make non-static if thread this function + static CUtlVector 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 ); + } + } +} -- cgit v1.2.3