mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2024-12-23 01:59:43 +08:00
1408 lines
49 KiB
C++
1408 lines
49 KiB
C++
//===== 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;
|
|
}
|
|
}
|
|
} |