//===== Copyright 1996-2005, 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 "BSPTreeData.h" #include "model_types.h" #include "IVRenderView.h" #include "tier0/vprof.h" #include "DetailObjectSystem.h" #include "engine/IStaticPropMgr.h" #include "engine/IVDebugOverlay.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", "1", FCVAR_CHEAT, "Open all portals" ); //----------------------------------------------------------------------------- // 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 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 ); // 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, LeafIndex_t *pLeafList, LeafFogVolume_t *pLeafFogVolumeList, int frameNumber ); virtual void CollateViewModelRenderables( CUtlVector< IClientRenderable * >& opaque, CUtlVector< IClientRenderable * >& translucent ); virtual void CollateRenderablesInLeaf( int leaf, int worldListLeafIndex, 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( unsigned short userId, unsigned short flags ); virtual void RemoveShadow( ClientLeafShadowHandle_t h ); virtual void ProjectShadow( ClientLeafShadowHandle_t handle, const Vector& origin, const Vector& dir, const Vector2D& size, float maxDist ); virtual void ProjectFlashlight( ClientLeafShadowHandle_t handle, const VMatrix &worldToShadow ); // 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 ); // 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 ); // insert, 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; } 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? 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. }; // The leaf contains an index into a list of renderables struct ClientLeaf_t { unsigned short m_FirstElement; unsigned short m_FirstShadow; // An optimization for detail objects since there are tens // of thousands of them, and since we're assuming they lie in // exactly one leaf (a bogus assumption, but too bad) unsigned short m_FirstDetailProp; unsigned short m_DetailPropCount; int m_DetailPropRenderFrame; }; // Shadow information struct ShadowInfo_t { unsigned short m_FirstLeaf; unsigned short m_FirstRenderable; int m_EnumCount; unsigned short m_Shadow; unsigned short m_Flags; }; // Stores data associated with each leaf. CUtlVector< ClientLeaf_t > m_Leaf; // Stores all unique non-detail renderables CUtlLinkedList< RenderableInfo_t, ClientRenderHandle_t > m_Renderables; // Information associated with shadows registered with the client leaf system CUtlLinkedList< ShadowInfo_t, ClientLeafShadowHandle_t > m_Shadows; // Maintains the list of all renderables in a particular leaf CBidirectionalSet< int, ClientRenderHandle_t, unsigned short > m_RenderablesInLeaf; // Maintains a list of all shadows in a particular leaf CBidirectionalSet< int, ClientLeafShadowHandle_t, unsigned short > m_ShadowsInLeaf; // Maintains a list of all shadows cast on a particular renderable CBidirectionalSet< ClientRenderHandle_t, ClientLeafShadowHandle_t, unsigned short > 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; }; //----------------------------------------------------------------------------- // 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(); 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(); 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( "CClientLeafSystem::PreRender" ); // Iterate through all renderables and tell them to compute their FX blend for ( int i = m_DirtyRenderables.Count(); --i >= 0; ) { ClientRenderHandle_t handle = m_DirtyRenderables[i]; RenderableInfo_t& renderable = m_Renderables[ handle ]; Assert( renderable.m_Flags & RENDER_FLAGS_HASCHANGED ); // Update position in leaf system RemoveFromTree( handle ); InsertIntoTree( handle ); renderable.m_Flags &= ~RENDER_FLAGS_HASCHANGED; } m_DirtyRenderables.RemoveAll(); } //----------------------------------------------------------------------------- // 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_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 = modelinfo->IsTranslucentTwoPass( pRenderable->GetModel() ); 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 ); } //----------------------------------------------------------------------------- // 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( unsigned short 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; } i = m_RenderablesInLeaf.NextElement(i); } } void CClientLeafSystem::RemoveShadowFromLeaves( ClientLeafShadowHandle_t handle ) { m_ShadowsInLeaf.RemoveElement( handle ); } //----------------------------------------------------------------------------- // Adds a shadow to all leaves along a ray //----------------------------------------------------------------------------- class CShadowLeafEnum : public ISpatialLeafEnumerator { public: bool EnumerateLeaf( int leaf, int context ) { CClientLeafSystem::s_ClientLeafSystem.AddShadowToLeaf( leaf, (ClientLeafShadowHandle_t)context ); return true; } }; void CClientLeafSystem::ProjectShadow( ClientLeafShadowHandle_t handle, const Vector& origin, const Vector& dir, const Vector2D& size, float maxDist ) { // 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; // Create a ray starting at the origin, with a boxsize == to the // maximum size, and cast it along the direction of the shadow // Then mark each leaf that the ray hits with the shadow Ray_t ray; VectorCopy( origin, ray.m_Start ); VectorMultiply( dir, maxDist, ray.m_Delta ); ray.m_StartOffset.Init( 0, 0, 0 ); float maxsize = max( size.x, size.y ) * 0.5f; ray.m_Extents.Init( maxsize, maxsize, maxsize ); ray.m_IsRay = false; ray.m_IsSwept = true; CShadowLeafEnum leafEnum; ISpatialQuery* pQuery = engine->GetBSPTreeQuery(); pQuery->EnumerateLeavesAlongRay( ray, &leafEnum, handle ); } void CClientLeafSystem::ProjectFlashlight( ClientLeafShadowHandle_t handle, const VMatrix &worldToShadow ) { // 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; // Use an AABB around the frustum to enumerate leaves. Vector mins, maxs; CalculateAABBFromProjectionMatrix( worldToShadow, &mins, &maxs ); CShadowLeafEnum leafEnum; ISpatialQuery* pQuery = engine->GetBSPTreeQuery(); pQuery->EnumerateLeavesInBox( mins, maxs, &leafEnum, 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 ) { 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 ) { ClientRenderHandle_t handle = (ClientRenderHandle_t)context; AddRenderableToLeaf( leaf, handle ); return true; } void CClientLeafSystem::InsertIntoTree( ClientRenderHandle_t handle ) { // When we insert into the tree, increase the shadow enumerator // to make sure each shadow is added exactly once to each renderable ++m_ShadowEnum; // 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, handle ); // Cache off the area it's sitting in. m_Renderables[handle].m_Area = GetRenderableArea( handle ); } //----------------------------------------------------------------------------- // 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 ) ); } //----------------------------------------------------------------------------- // Compute which leaf the translucent renderables should render in //----------------------------------------------------------------------------- void CClientLeafSystem::ComputeTranslucentRenderLeaf( int count, LeafIndex_t *pLeafList, LeafFogVolume_t *pLeafFogVolumeList, int frameNumber ) { VPROF( "CClientLeafSystem::ComputeTranslucentRenderLeaf" ); // 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 for (int i = 0; i < count; ++i ) { int leaf = pLeafList[i]; // 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_RenderFrame != frameNumber ) { // Compute translucency info.m_pRenderable->ComputeFxBlend(); if( info.m_RenderGroup == RENDER_GROUP_TRANSLUCENT_ENTITY ) { info.m_RenderLeaf = leaf; } info.m_RenderFrame = frameNumber; } else if ( info.m_Flags & RENDER_FLAGS_ALTERNATE_SORTING ) { if( info.m_RenderGroup == RENDER_GROUP_TRANSLUCENT_ENTITY ) { info.m_RenderLeaf = leaf; } } idx = m_RenderablesInLeaf.NextElement(idx); } } } //----------------------------------------------------------------------------- // Adds a renderable to the list of renderables to render this frame //----------------------------------------------------------------------------- inline void AddRenderableToRenderList( CRenderList &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 < CRenderList::MAX_GROUP_ENTITIES ) { Assert( (iLeaf >= 0) && (iLeaf <= 65535) ); CRenderList::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 CRenderList 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 ); } } } void CClientLeafSystem::CollateRenderablesInLeaf( int leaf, int worldListLeafIndex, SetupRenderInfo_t &info ) { bool portalTestEnts = r_PortalTestEnts.GetBool() && !r_portalsopenall.GetBool(); // 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 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; } // 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. unsigned char 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; } if( renderable.m_RenderGroup != RENDER_GROUP_TRANSLUCENT_ENTITY ) { RenderGroup_t group = (RenderGroup_t)renderable.m_RenderGroup; AddRenderableToRenderList( *info.m_pRenderList, renderable.m_pRenderable, worldListLeafIndex, group, handle); } else { bool bTwoPass = ((renderable.m_Flags & RENDER_FLAGS_TWOPASS) != 0) && ( nAlpha == 255 ); AddRenderableToRenderList( *info.m_pRenderList, renderable.m_pRenderable, worldListLeafIndex, (RenderGroup_t)renderable.m_RenderGroup, handle, bTwoPass ); // Add to both lists if it's a two-pass model... if (bTwoPass) { AddRenderableToRenderList( *info.m_pRenderList, renderable.m_pRenderable, worldListLeafIndex, RENDER_GROUP_OPAQUE_ENTITY, handle, bTwoPass ); } } } // Do detail objects. // These don't have render handles! if ( IsPC() && 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() ) { // Lots of the detail entities are invsible 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; } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 5821 | Knut Wikstrom |
Added Valve Source code. This is NOT to be commited to other than new code from Valve. |