-
Notifications
You must be signed in to change notification settings - Fork 49
Expand file tree
/
Copy pathclientleafsystem.cpp
More file actions
2726 lines (2300 loc) · 92.2 KB
/
clientleafsystem.cpp
File metadata and controls
2726 lines (2300 loc) · 92.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//===== Copyright 1996-2007, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $Revision: $
// $NoKeywords: $
//
// This file contains code to allow us to associate client data with bsp leaves.
//===========================================================================//
#include "cbase.h"
#include "ClientLeafSystem.h"
#include "UtlBidirectionalSet.h"
#include "model_types.h"
#include "IVRenderView.h"
#include "tier0/vprof.h"
#include "BSPTreeData.h"
#include "DetailObjectSystem.h"
#include "engine/IStaticPropMgr.h"
#include "engine/IVDebugOverlay.h"
#include "vstdlib/jobthread.h"
#include "tier1/utllinkedlist.h"
#include "datacache/imdlcache.h"
#include "view.h"
#include "iviewrender.h"
#include "viewrender.h"
#include "clientalphaproperty.h"
#include "con_nprint.h"
//#include "tier0/miniprofiler.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class VMatrix; // forward decl
//extern LinkedMiniProfiler *g_pMiniProfilers;
//LinkedMiniProfiler g_mpRecomputeLeaves("CClientLeafSystem::RecomputeRenderableLeaves", &g_pMiniProfilers);
//LinkedMiniProfiler g_mpComputeBounds("CClientLeafSystem::ComputeBounds", &g_pMiniProfilers);
static ConVar cl_drawleaf("cl_drawleaf", "-1", FCVAR_CHEAT );
static ConVar r_PortalTestEnts( "r_PortalTestEnts", "1", FCVAR_CHEAT, "Clip entities against portal frustums." );
static ConVar r_portalsopenall( "r_portalsopenall", "0", FCVAR_CHEAT, "Open all portals" );
static ConVar r_shadows_on_renderables_enable( "r_shadows_on_renderables_enable", "0", 0, "Support casting RTT shadows onto other renderables" );
static ConVar cl_leafsystemvis( "cl_leafsystemvis", "0", FCVAR_CHEAT );
DEFINE_FIXEDSIZE_ALLOCATOR( CClientRenderablesList, 1, CUtlMemoryPool::GROW_SLOW );
//-----------------------------------------------------------------------------
// Threading helpers
//-----------------------------------------------------------------------------
static void FrameLock()
{
mdlcache->BeginLock();
}
static void FrameUnlock()
{
mdlcache->EndLock();
}
//-----------------------------------------------------------------------------
// The client leaf system
//-----------------------------------------------------------------------------
class CClientLeafSystem : public IClientLeafSystem, public ISpatialLeafEnumerator, public IClientAlphaPropertyMgr
{
public:
virtual char const *Name() { return "CClientLeafSystem"; }
// constructor, destructor
CClientLeafSystem();
virtual ~CClientLeafSystem();
// Methods of IClientSystem
bool Init() { return true; }
void PostInit() {}
void Shutdown() {}
virtual bool IsPerFrame() { return true; }
void PreRender();
void PostRender() { }
void Update( float frametime ) { m_nDebugIndex = 0; }
void LevelInitPreEntity();
void LevelInitPostEntity() {}
void LevelShutdownPreEntity();
void LevelShutdownPostEntity();
virtual void OnSave() {}
virtual void OnRestore() {}
virtual void SafeRemoveIfDesired() {}
// Methods of IClientAlphaPropertyMgr
public:
virtual IClientAlphaProperty *CreateClientAlphaProperty( IClientUnknown *pUnknown );
virtual void DestroyClientAlphaProperty( IClientAlphaProperty *pAlphaProperty );
// Methods of IClientLeafSystem
public:
virtual void AddRenderable( IClientRenderable* pRenderable, bool bRenderWithViewModels, RenderableTranslucencyType_t nType, RenderableModelType_t nModelType, uint32 nSplitscreenEnabledFlags );
virtual bool IsRenderableInPVS( IClientRenderable *pRenderable );
virtual void CreateRenderableHandle( IClientRenderable* pRenderable, bool bRenderWithViewModels, RenderableTranslucencyType_t nType, RenderableModelType_t nModelType, uint32 nSplitscreenEnabled );
virtual void RemoveRenderable( ClientRenderHandle_t handle );
virtual void SetSubSystemDataInLeaf( int leaf, int nSubSystemIdx, CClientLeafSubSystemData *pData );
virtual CClientLeafSubSystemData *GetSubSystemDataInLeaf( int leaf, int nSubSystemIdx );
// FIXME: There's an incestuous relationship between DetailObjectSystem
// and the ClientLeafSystem. Maybe they should be the same system?
virtual void GetDetailObjectsInLeaf( int leaf, int& firstDetailObject, int& detailObjectCount );
virtual void SetDetailObjectsInLeaf( int leaf, int firstDetailObject, int detailObjectCount );
virtual void DrawDetailObjectsInLeaf( int leaf, int frameNumber, int& nFirstDetailObject, int& nDetailObjectCount );
virtual bool ShouldDrawDetailObjectsInLeaf( int leaf, int frameNumber );
virtual void RenderableChanged( ClientRenderHandle_t handle );
virtual void CollateViewModelRenderables( CViewModelRenderablesList *pList );
virtual void BuildRenderablesList( const SetupRenderInfo_t &info );
virtual void DrawStaticProps( bool enable );
virtual void DrawSmallEntities( bool enable );
virtual void EnableAlternateSorting( ClientRenderHandle_t handle, bool bEnable );
virtual void RenderWithViewModels( ClientRenderHandle_t handle, bool bEnable );
virtual bool IsRenderingWithViewModels( ClientRenderHandle_t handle ) const;
virtual void SetTranslucencyType( ClientRenderHandle_t handle, RenderableTranslucencyType_t nType );
virtual RenderableTranslucencyType_t GetTranslucencyType( ClientRenderHandle_t handle ) const;
virtual void SetModelType( ClientRenderHandle_t handle, RenderableModelType_t nType );
virtual void EnableSplitscreenRendering( ClientRenderHandle_t handle, uint32 nFlags );
virtual void EnableRendering( ClientRenderHandle_t handle, bool bEnable );
virtual void EnableBloatedBounds( ClientRenderHandle_t handle, bool bEnable );
virtual void DisableCachedRenderBounds( ClientRenderHandle_t handle, bool bDisable );
// Adds a renderable to a set of leaves
virtual void AddRenderableToLeaves( ClientRenderHandle_t handle, int nLeafCount, unsigned short *pLeaves );
void AddRenderableToLeaves( ClientRenderHandle_t handle, int nLeafCount, unsigned short *pLeaves, bool bReceiveShadows );
// The following methods are related to shadows...
virtual ClientLeafShadowHandle_t AddShadow( ClientShadowHandle_t userId, unsigned short flags );
virtual void RemoveShadow( ClientLeafShadowHandle_t h );
virtual void ProjectShadow( ClientLeafShadowHandle_t handle, int nLeafCount, const int *pLeafList );
virtual void ProjectFlashlight( ClientLeafShadowHandle_t handle, int nLeafCount, const int *pLeafList );
// Find all shadow casters in a set of leaves
virtual void EnumerateShadowsInLeaves( int leafCount, WorldListLeafData_t* pLeaves, IClientLeafShadowEnum* pEnum );
virtual void RecomputeRenderableLeaves();
virtual void DisableLeafReinsertion( bool bDisable );
//Assuming the renderable would be in a properly built render list, generate a render list entry
virtual RenderGroup_t GenerateRenderListEntry( IClientRenderable *pRenderable, CClientRenderablesList::CEntry &entryOut );
// methods of ISpatialLeafEnumerator
public:
bool EnumerateLeaf( int leaf, int context );
// Adds a shadow to a leaf
void AddShadowToLeaf( int leaf, ClientLeafShadowHandle_t handle, bool bFlashlight );
// 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:
enum
{
RENDER_FLAGS_DISABLE_RENDERING = 0x01,
RENDER_FLAGS_HASCHANGED = 0x02,
RENDER_FLAGS_ALTERNATE_SORTING = 0x04,
RENDER_FLAGS_RENDER_WITH_VIEWMODELS = 0x08,
RENDER_FLAGS_BLOAT_BOUNDS = 0x10,
RENDER_FLAGS_BOUNDS_VALID = 0x20,
RENDER_FLAGS_BOUNDS_ALWAYS_RECOMPUTE = 0x40,
};
// All the information associated with a particular handle
struct RenderableInfo_t
{
IClientRenderable* m_pRenderable;
CClientAlphaProperty *m_pAlphaProperty;
int m_EnumCount; // Have I been added to a particular shadow yet?
int m_nRenderFrame;
unsigned short m_FirstShadow; // The first shadow caster that cast on it
unsigned short m_LeafList; // What leafs is it in?
short m_Area; // -1 if the renderable spans multiple areas.
uint16 m_Flags : 10; // rendering flags
uint16 m_nSplitscreenEnabled : 2; // splitscreen rendering flags
uint16 m_nTranslucencyType : 2; // RenderableTranslucencyType_t
uint16 m_nModelType : 2; // RenderableModelType_t
Vector m_vecBloatedAbsMins; // Use this for tree insertion
Vector m_vecBloatedAbsMaxs;
Vector m_vecAbsMins; // NOTE: These members are not threadsafe!!
Vector m_vecAbsMaxs; // They can be updated from any viewpoint (based on RENDER_FLAGS_BOUNDS_VALID)
};
// The leaf contains an index into a list of renderables
struct ClientLeaf_t
{
unsigned short m_FirstElement;
unsigned short m_FirstShadow;
unsigned short m_FirstDetailProp;
unsigned short m_DetailPropCount;
int m_DetailPropRenderFrame;
CClientLeafSubSystemData *m_pSubSystemData[N_CLSUBSYSTEMS];
};
// Shadow information
struct ShadowInfo_t
{
unsigned short m_FirstLeaf;
unsigned short m_FirstRenderable;
int m_EnumCount;
ClientShadowHandle_t m_Shadow;
unsigned short m_Flags;
};
struct EnumResult_t
{
int leaf;
EnumResult_t *pNext;
};
struct EnumResultList_t
{
EnumResult_t *pHead;
ClientRenderHandle_t handle;
};
struct BuildRenderListInfo_t
{
Vector m_vecMins;
Vector m_vecMaxs;
short m_nArea;
uint8 m_nAlpha;
bool m_bPerformOcclusionTest : 1;
bool m_bIgnoreZBuffer : 1;
};
struct AlphaInfo_t
{
CClientAlphaProperty *m_pAlphaProperty;
Vector m_vecCenter;
float m_flRadius;
float m_flFadeFactor;
};
private:
// Adds a renderable to the list of renderables
void AddRenderableToLeaf( int leaf, ClientRenderHandle_t handle, bool bReceiveShadows );
void SortEntities( const Vector &vecRenderOrigin, const Vector &vecRenderForward, CClientRenderablesList::CEntry *pEntities, int nEntities );
// Returns -1 if the renderable spans more than one area. If it's totally in one area, then this returns the leaf.
short GetRenderableArea( ClientRenderHandle_t handle );
// remove renderables from leaves
void RemoveFromTree( ClientRenderHandle_t handle );
void InsertIntoTree( ClientRenderHandle_t &handle, const Vector &absMins, const Vector &absMaxs );
// 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 );
// 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 related to renderable list building
int ExtractStaticProps( int nCount, RenderableInfo_t **ppRenderables );
int ExtractSplitscreenRenderables( int nCount, RenderableInfo_t **ppRenderables );
int ExtractTranslucentRenderables( int nCount, RenderableInfo_t **ppRenderables );
int ExtractDuplicates( int nFrameNumber, int nCount, RenderableInfo_t **ppRenderables );
void ComputeBounds( int nCount, RenderableInfo_t **ppRenderables, BuildRenderListInfo_t *pRLInfo );
int ExtractCulledRenderables( int nCount, RenderableInfo_t **ppRenderables, BuildRenderListInfo_t *pRLInfo );
int ExtractOccludedRenderables( int nCount, RenderableInfo_t **ppRenderables, BuildRenderListInfo_t *pRLInfo );
void AddRenderablesToRenderLists( const SetupRenderInfo_t &info, int nCount, RenderableInfo_t **ppRenderables, BuildRenderListInfo_t *pRLInfo, int nDetailCount, DetailRenderableInfo_t *pDetailInfo );
void AddDependentRenderables( const SetupRenderInfo_t &info );
int ComputeTranslucency( int nFrameNumber, int nViewID, int nCount, RenderableInfo_t **ppRenderables, BuildRenderListInfo_t *pRLInfo );
void ComputeDistanceFade( int nCount, AlphaInfo_t *pAlphaInfo, BuildRenderListInfo_t *pRLInfo );
void ComputeScreenFade( const ScreenSizeComputeInfo_t &info, float flMinScreenWidth, float flMaxScreenWidth, int nCount, AlphaInfo_t *pAlphaInfo );
void CalcRenderableWorldSpaceAABB_Bloated( const RenderableInfo_t &info, Vector &absMin, Vector &absMax );
bool ShouldRenderableBeIgnored(IClientRenderable* pRenderable);
// Methods associated with the various bi-directional sets
static unsigned short& FirstRenderableInLeaf( int leaf )
{
return s_ClientLeafSystem.m_Leaf[leaf].m_FirstElement;
}
static unsigned short& FirstLeafInRenderable( unsigned short renderable )
{
return s_ClientLeafSystem.m_Renderables[renderable].m_LeafList;
}
static unsigned short& FirstShadowInLeaf( int leaf )
{
return s_ClientLeafSystem.m_Leaf[leaf].m_FirstShadow;
}
static unsigned short& FirstLeafInShadow( ClientLeafShadowHandle_t shadow )
{
return s_ClientLeafSystem.m_Shadows[shadow].m_FirstLeaf;
}
static unsigned short& FirstShadowOnRenderable( unsigned short renderable )
{
return s_ClientLeafSystem.m_Renderables[renderable].m_FirstShadow;
}
static unsigned short& FirstRenderableInShadow( ClientLeafShadowHandle_t shadow )
{
return s_ClientLeafSystem.m_Shadows[shadow].m_FirstRenderable;
}
void FrameLock()
{
mdlcache->BeginLock();
}
void FrameUnlock()
{
mdlcache->EndLock();
}
// Stores data associated with each leaf.
CUtlVector< ClientLeaf_t > m_Leaf;
// Stores all unique non-detail renderables
CUtlLinkedList< RenderableInfo_t, ClientRenderHandle_t, false, unsigned int > m_Renderables;
// Information associated with shadows registered with the client leaf system
CUtlLinkedList< ShadowInfo_t, ClientLeafShadowHandle_t, false, unsigned int > m_Shadows;
// Maintains the list of all renderables in a particular leaf
CBidirectionalSet< int, ClientRenderHandle_t, unsigned short, unsigned int > m_RenderablesInLeaf;
// Maintains a list of all shadows in a particular leaf
CBidirectionalSet< int, ClientLeafShadowHandle_t, unsigned short, unsigned int > m_ShadowsInLeaf;
// Maintains a list of all shadows cast on a particular renderable
CBidirectionalSet< ClientRenderHandle_t, ClientLeafShadowHandle_t, unsigned short, unsigned int > m_ShadowsOnRenderable;
// Dirty list of renderables
CUtlVector< ClientRenderHandle_t > m_DirtyRenderables;
// List of renderables in view model render groups
CUtlVector< ClientRenderHandle_t > m_ViewModels;
// Should I draw static props?
bool m_DrawStaticProps;
bool m_DrawSmallObjects;
bool m_bDisableLeafReinsertion;
// A little enumerator to help us when adding shadows to renderables
int m_ShadowEnum;
// Does anything use alternate sorting?
int m_nAlternateSortCount;
// Number of alpha properties out there
int m_nAlphaPropertyCount;
CUtlMemoryPool m_AlphaPropertyPool;
int m_nDebugIndex;
};
//-----------------------------------------------------------------------------
// Methods of IClientAlphaPropertyMgr
//-----------------------------------------------------------------------------
IClientAlphaProperty *CClientLeafSystem::CreateClientAlphaProperty( IClientUnknown *pUnk )
{
++m_nAlphaPropertyCount;
CClientAlphaProperty *pProperty = (CClientAlphaProperty*)m_AlphaPropertyPool.Alloc( sizeof(CClientAlphaProperty) );
Construct( pProperty );
pProperty->Init( pUnk );
return pProperty;
}
void CClientLeafSystem::DestroyClientAlphaProperty( IClientAlphaProperty *pAlphaProperty )
{
if ( !pAlphaProperty )
return;
Destruct( static_cast<CClientAlphaProperty*>( pAlphaProperty ) );
m_AlphaPropertyPool.Free( pAlphaProperty );
Assert( m_nAlphaPropertyCount > 0 );
if ( --m_nAlphaPropertyCount == 0 )
{
m_AlphaPropertyPool.Clear();
}
}
//-----------------------------------------------------------------------------
// Expose IClientLeafSystem to the client dll.
//-----------------------------------------------------------------------------
CClientLeafSystem CClientLeafSystem::s_ClientLeafSystem;
IClientLeafSystem *g_pClientLeafSystem = &CClientLeafSystem::s_ClientLeafSystem;
IClientAlphaPropertyMgr *g_pClientAlphaPropertyMgr = &CClientLeafSystem::s_ClientLeafSystem;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CClientLeafSystem, IClientLeafSystem, CLIENTLEAFSYSTEM_INTERFACE_VERSION, CClientLeafSystem::s_ClientLeafSystem );
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CClientLeafSystem, IClientAlphaPropertyMgr, CLIENT_ALPHA_PROPERTY_MGR_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() || ( pEnt->GetParentAttachment() > 0 ) ) )
{
C_BaseEntity *pParent = pEnt->GetMoveParent();
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();
if (angles == vec3_angle)
{
const Vector& origin = pRenderable->GetRenderOrigin();
VectorAdd( mins, origin, absMins );
VectorAdd( maxs, origin, absMaxs );
}
else
{
TransformAABB( pRenderable->RenderableToWorldTransform(), 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() || ( pEnt->GetMoveParent() && ( pEnt->GetParentAttachment() > 0 ) ) ) )
{
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),
m_AlphaPropertyPool( sizeof( CClientAlphaProperty ), 1024, CUtlMemoryPool::GROW_SLOW, "CClientAlphaProperty" )
{
// Set up the bi-directional lists...
m_RenderablesInLeaf.Init( FirstRenderableInLeaf, FirstLeafInRenderable );
m_ShadowsInLeaf.Init( FirstShadowInLeaf, FirstLeafInShadow );
m_ShadowsOnRenderable.Init( FirstShadowOnRenderable, FirstRenderableInShadow );
m_nAlternateSortCount = 0;
m_bDisableLeafReinsertion = false;
}
CClientLeafSystem::~CClientLeafSystem()
{
}
//-----------------------------------------------------------------------------
// Activate, deactivate static props
//-----------------------------------------------------------------------------
void CClientLeafSystem::DrawStaticProps( bool enable )
{
m_DrawStaticProps = enable;
}
void CClientLeafSystem::DrawSmallEntities( bool enable )
{
m_DrawSmallObjects = enable;
}
void CClientLeafSystem::DisableLeafReinsertion( bool bDisable )
{
m_bDisableLeafReinsertion = bDisable;
}
//-----------------------------------------------------------------------------
// Level init, shutdown
//-----------------------------------------------------------------------------
void CClientLeafSystem::LevelInitPreEntity()
{
MEM_ALLOC_CREDIT();
m_Renderables.EnsureCapacity( 1024 );
m_RenderablesInLeaf.EnsureCapacity( 1024 );
m_ShadowsInLeaf.EnsureCapacity( 256 );
m_ShadowsOnRenderable.EnsureCapacity( 256 );
m_DirtyRenderables.EnsureCapacity( 256 );
// Add all the leaves we'll need
int leafCount = engine->LevelLeafCount();
m_Leaf.EnsureCapacity( leafCount );
ClientLeaf_t newLeaf;
newLeaf.m_FirstElement = m_RenderablesInLeaf.InvalidIndex();
newLeaf.m_FirstShadow = m_ShadowsInLeaf.InvalidIndex();
memset( newLeaf.m_pSubSystemData, 0, sizeof( newLeaf.m_pSubSystemData ) );
newLeaf.m_FirstDetailProp = 0;
newLeaf.m_DetailPropCount = 0;
newLeaf.m_DetailPropRenderFrame = -1;
while ( --leafCount >= 0 )
{
m_Leaf.AddToTail( newLeaf );
}
}
void CClientLeafSystem::LevelShutdownPreEntity()
{
}
void CClientLeafSystem::LevelShutdownPostEntity()
{
m_nAlternateSortCount = 0;
m_ViewModels.Purge();
m_Renderables.Purge();
m_RenderablesInLeaf.Purge();
m_Shadows.Purge();
// delete subsystem data
for( int i = 0; i < m_Leaf.Count() ; i++ )
{
for( int j = 0 ; j < ARRAYSIZE( m_Leaf[i].m_pSubSystemData ) ; j++ )
{
if ( m_Leaf[i].m_pSubSystemData[j] )
{
delete m_Leaf[i].m_pSubSystemData[j];
m_Leaf[i].m_pSubSystemData[j] = NULL;
}
}
}
m_Leaf.Purge();
m_ShadowsInLeaf.Purge();
m_ShadowsOnRenderable.Purge();
m_DirtyRenderables.Purge();
}
//-----------------------------------------------------------------------------
// Computes a bloated bounding box to reduce insertions into the tree
//-----------------------------------------------------------------------------
#define BBOX_GRANULARITY 32.0f
#define MIN_SHRINK_VOLUME ( 32.0f * 32.0f * 32.0f )
void CClientLeafSystem::CalcRenderableWorldSpaceAABB_Bloated( const RenderableInfo_t &info, Vector &absMin, Vector &absMax )
{
CalcRenderableWorldSpaceAABB_Fast( info.m_pRenderable, absMin, absMax );
// Bloat bounds to avoid reinsertion into tree
absMin.x = floor( absMin.x / BBOX_GRANULARITY ) * BBOX_GRANULARITY;
absMin.y = floor( absMin.y / BBOX_GRANULARITY ) * BBOX_GRANULARITY;
absMin.z = floor( absMin.z / BBOX_GRANULARITY ) * BBOX_GRANULARITY;
absMax.x = ceil( absMax.x / BBOX_GRANULARITY ) * BBOX_GRANULARITY;
absMax.y = ceil( absMax.y / BBOX_GRANULARITY ) * BBOX_GRANULARITY;
absMax.z = ceil( absMax.z / BBOX_GRANULARITY ) * BBOX_GRANULARITY;
// Optimization to make particle systems not re-insert themselves
if ( info.m_Flags & RENDER_FLAGS_BLOAT_BOUNDS )
{
Vector vecTempMin, vecTempMax;
VectorMin( info.m_vecBloatedAbsMins, absMin, vecTempMin );
VectorMax( info.m_vecBloatedAbsMaxs, absMax, vecTempMax );
float flTempVolume = ComputeVolume( vecTempMin, vecTempMax );
float flCurrVolume = ComputeVolume( absMin, absMax );
if ( ( flTempVolume <= MIN_SHRINK_VOLUME ) || ( flCurrVolume * 2.0f >= flTempVolume ) )
{
absMin = vecTempMin;
absMax = vecTempMax;
}
}
}
//-----------------------------------------------------------------------------
// This is what happens before rendering a particular view
//-----------------------------------------------------------------------------
void CClientLeafSystem::PreRender()
{
// Assert( m_DirtyRenderables.Count() == 0 );
// FIXME: This should never need to happen here!
// At the moment, it's necessary because of the horrid viewmodel/combatweapon
// confusion in the code where a combat weapon changes its rendering model
// per view.
RecomputeRenderableLeaves();
}
// Use this to make sure we're not adding the same renderables to the list while we're going through and re-inserting them into the clientleafsystem
static bool s_bIsInRecomputeRenderableLeaves = false;
void CClientLeafSystem::RecomputeRenderableLeaves()
{
// MiniProfilerGuard mpGuard(&g_mpRecomputeLeaves);
int i;
int nIterations = 0;
bool bDebugLeafSystem = !IsX360() && cl_leafsystemvis.GetBool();
Vector absMins, absMaxs;
while ( m_DirtyRenderables.Count() )
{
if ( ++nIterations > 10 )
{
Warning( "Too many dirty renderables!\n" );
break;
}
s_bIsInRecomputeRenderableLeaves = true;
int nDirty = m_DirtyRenderables.Count();
for ( i = nDirty; --i >= 0; )
{
ClientRenderHandle_t handle = m_DirtyRenderables[i];
RenderableInfo_t &info = m_Renderables[ handle ];
Assert( info.m_Flags & RENDER_FLAGS_HASCHANGED );
// See note below
info.m_Flags &= ~RENDER_FLAGS_HASCHANGED;
if ( info.m_Flags & RENDER_FLAGS_RENDER_WITH_VIEWMODELS )
continue;
CalcRenderableWorldSpaceAABB_Bloated( info, absMins, absMaxs );
if ( absMins != info.m_vecBloatedAbsMins || absMaxs != info.m_vecBloatedAbsMaxs )
{
// Update position in leaf system
RemoveFromTree( handle );
InsertIntoTree( m_DirtyRenderables[i], absMins, absMaxs );
if ( bDebugLeafSystem )
{
debugoverlay->AddBoxOverlay( vec3_origin, absMins, absMaxs, QAngle( 0, 0, 0 ), 0, 255, 0, 0, 0 );
}
}
}
s_bIsInRecomputeRenderableLeaves = false;
// NOTE: If we get the following error displayed in the console spew
// "Re-entrancy found in CClientLeafSystem::RenderableChanged\n"
// We'll have to reenable this code and remove the line that
// removes the RENDER_FLAGS_HASCHANGED in the loop above.
/*
for ( i = nDirty; --i >= 0; )
{
// Cache off the area it's sitting in.
ClientRenderHandle_t handle = m_DirtyRenderables[i];
RenderableInfo_t& renderable = m_Renderables[ handle ];
renderable.m_Flags &= ~RENDER_FLAGS_HASCHANGED;
}
*/
m_DirtyRenderables.RemoveMultiple( 0, nDirty );
}
}
//-----------------------------------------------------------------------------
// Creates a new renderable
//-----------------------------------------------------------------------------
void CClientLeafSystem::CreateRenderableHandle( IClientRenderable* pRenderable, bool bRenderWithViewModels, RenderableTranslucencyType_t nType, RenderableModelType_t nModelType, uint32 nSplitscreenEnabled )
{
Assert( pRenderable );
Assert( pRenderable->RenderHandle() == INVALID_CLIENT_RENDER_HANDLE );
ClientRenderHandle_t handle = m_Renderables.AddToTail();
RenderableInfo_t &info = m_Renderables[handle];
if ( nModelType == RENDERABLE_MODEL_UNKNOWN_TYPE )
{
int nType = modelinfo->GetModelType( pRenderable->GetModel() );
switch( nType )
{
default: nModelType = RENDERABLE_MODEL_ENTITY; break;
case mod_brush: nModelType = RENDERABLE_MODEL_BRUSH; break;
case mod_studio: nModelType = RENDERABLE_MODEL_STUDIOMDL; break;
}
}
#ifdef _DEBUG
// We need to know if it's a brush model for shadows
int modelType = modelinfo->GetModelType( pRenderable->GetModel() );
switch ( modelType )
{
case mod_brush: Assert( nModelType == RENDERABLE_MODEL_BRUSH ); break;
case mod_studio: Assert( nModelType == RENDERABLE_MODEL_STUDIOMDL || nModelType == RENDERABLE_MODEL_STATIC_PROP ); break;
case mod_sprite: default: Assert( nModelType == RENDERABLE_MODEL_ENTITY ); break;
}
#endif
info.m_pRenderable = pRenderable;
info.m_pAlphaProperty = static_cast< CClientAlphaProperty* >( pRenderable->GetIClientUnknown()->GetClientAlphaProperty() );
info.m_FirstShadow = m_ShadowsOnRenderable.InvalidIndex();
info.m_LeafList = m_RenderablesInLeaf.InvalidIndex();
info.m_Flags = 0;
info.m_nRenderFrame = -1;
info.m_EnumCount = 0;
info.m_nSplitscreenEnabled = nSplitscreenEnabled & 0x3;
info.m_nTranslucencyType = nType;
info.m_nModelType = nModelType;
info.m_vecBloatedAbsMins.Init( FLT_MAX, FLT_MAX, FLT_MAX );
info.m_vecBloatedAbsMaxs.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
info.m_vecAbsMins.Init();
info.m_vecAbsMaxs.Init();
pRenderable->RenderHandle() = handle;
RenderWithViewModels( handle, bRenderWithViewModels );
}
//-----------------------------------------------------------------------------
// Call this if the model changes
//-----------------------------------------------------------------------------
void CClientLeafSystem::SetTranslucencyType( ClientRenderHandle_t handle, RenderableTranslucencyType_t nType )
{
if ( handle == INVALID_CLIENT_RENDER_HANDLE )
return;
RenderableInfo_t &info = m_Renderables[handle];
info.m_nTranslucencyType = nType;
}
RenderableTranslucencyType_t CClientLeafSystem::GetTranslucencyType( ClientRenderHandle_t handle ) const
{
if ( handle == INVALID_CLIENT_RENDER_HANDLE )
return RENDERABLE_IS_OPAQUE;
const RenderableInfo_t &info = m_Renderables[handle];
return (RenderableTranslucencyType_t)info.m_nTranslucencyType;
}
void CClientLeafSystem::EnableSplitscreenRendering( ClientRenderHandle_t handle, uint32 nFlags )
{
if ( handle == INVALID_CLIENT_RENDER_HANDLE )
return;
RenderableInfo_t &info = m_Renderables[handle];
info.m_nSplitscreenEnabled = nFlags & 0x3;
}
void CClientLeafSystem::SetModelType( ClientRenderHandle_t handle, RenderableModelType_t nModelType )
{
if ( handle == INVALID_CLIENT_RENDER_HANDLE )
return;
RenderableInfo_t &info = m_Renderables[handle];
if ( nModelType == RENDERABLE_MODEL_UNKNOWN_TYPE )
{
int nType = modelinfo->GetModelType( info.m_pRenderable->GetModel() );
switch( nType )
{
default: nModelType = RENDERABLE_MODEL_ENTITY; break;
case mod_brush: nModelType = RENDERABLE_MODEL_BRUSH; break;
case mod_studio: nModelType = RENDERABLE_MODEL_STUDIOMDL; break;
}
}
if ( info.m_nModelType != nModelType )
{
info.m_nModelType = nModelType;
RenderableChanged( handle );
}
}
void CClientLeafSystem::EnableRendering( ClientRenderHandle_t handle, bool bEnable )
{
if ( handle == INVALID_CLIENT_RENDER_HANDLE )
return;
RenderableInfo_t &info = m_Renderables[handle];
if ( bEnable )
{
info.m_Flags &= ~RENDER_FLAGS_DISABLE_RENDERING;
}
else
{
info.m_Flags |= RENDER_FLAGS_DISABLE_RENDERING;
}
}
void CClientLeafSystem::EnableBloatedBounds( ClientRenderHandle_t handle, bool bEnable )
{
if ( handle == INVALID_CLIENT_RENDER_HANDLE )
return;
RenderableInfo_t &info = m_Renderables[handle];
if ( bEnable )
{
info.m_Flags |= RENDER_FLAGS_BLOAT_BOUNDS;
}
else
{
if ( info.m_Flags & RENDER_FLAGS_BLOAT_BOUNDS )
{
info.m_Flags &= ~RENDER_FLAGS_BLOAT_BOUNDS;
// Necessary to generate unbloated bounds later
RenderableChanged( handle );
}
}
}
void CClientLeafSystem::DisableCachedRenderBounds( ClientRenderHandle_t handle, bool bDisable )
{
if ( handle == INVALID_CLIENT_RENDER_HANDLE )
return;
RenderableInfo_t &info = m_Renderables[handle];
if ( bDisable )
{
info.m_Flags |= RENDER_FLAGS_BOUNDS_ALWAYS_RECOMPUTE;
}
else
{
info.m_Flags &= ~RENDER_FLAGS_BOUNDS_ALWAYS_RECOMPUTE;
}
}
//-----------------------------------------------------------------------------
// 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 )
{
if ( ( info.m_Flags & RENDER_FLAGS_ALTERNATE_SORTING ) == 0 )
{
++m_nAlternateSortCount;
info.m_Flags |= RENDER_FLAGS_ALTERNATE_SORTING;
}
}
else
{
if ( ( info.m_Flags & RENDER_FLAGS_ALTERNATE_SORTING ) != 0 )
{
--m_nAlternateSortCount;
info.m_Flags &= ~RENDER_FLAGS_ALTERNATE_SORTING;
}
}
}
//-----------------------------------------------------------------------------
// Should this render with viewmodels?
//-----------------------------------------------------------------------------
void CClientLeafSystem::RenderWithViewModels( ClientRenderHandle_t handle, bool bEnable )
{
if ( handle == INVALID_CLIENT_RENDER_HANDLE )
return;
RenderableInfo_t &info = m_Renderables[handle];
if ( bEnable )
{
if ( ( info.m_Flags & RENDER_FLAGS_RENDER_WITH_VIEWMODELS ) == 0 )
{
info.m_Flags |= RENDER_FLAGS_RENDER_WITH_VIEWMODELS;
AddToViewModelList( handle );
RemoveFromTree( handle );
}
}
else
{
if ( ( info.m_Flags & RENDER_FLAGS_RENDER_WITH_VIEWMODELS ) != 0 )
{
info.m_Flags &= ~RENDER_FLAGS_RENDER_WITH_VIEWMODELS;
RemoveFromViewModelList( handle );
RenderableChanged( handle );
}
}
}
bool CClientLeafSystem::IsRenderingWithViewModels( ClientRenderHandle_t handle ) const
{
if ( handle == INVALID_CLIENT_RENDER_HANDLE )
return false;
return ( m_Renderables[handle].m_Flags & RENDER_FLAGS_RENDER_WITH_VIEWMODELS ) != 0;
}
//-----------------------------------------------------------------------------
// Add/remove renderable
//-----------------------------------------------------------------------------
void CClientLeafSystem::AddRenderable( IClientRenderable* pRenderable, bool bRenderWithViewModels, RenderableTranslucencyType_t nType, RenderableModelType_t nModelType, uint32 nSplitscreenEnabled )
{
// force a relink we we try to draw it for the first time
CreateRenderableHandle( pRenderable, bRenderWithViewModels, nType, nModelType, nSplitscreenEnabled );
ClientRenderHandle_t handle = pRenderable->RenderHandle();
RenderableChanged( 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;
int nFlags = m_Renderables[handle].m_Flags;
if ( nFlags & RENDER_FLAGS_ALTERNATE_SORTING )
{
--m_nAlternateSortCount;
}
// Remove the renderable from the dirty list
if ( nFlags & RENDER_FLAGS_HASCHANGED )