Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1422 lines
46 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "collisionproperty.h"
  9. #include "igamesystem.h"
  10. #include "utlvector.h"
  11. #include "tier0/threadtools.h"
  12. #include "tier0/tslist.h"
  13. #ifdef CLIENT_DLL
  14. #include "c_baseentity.h"
  15. #include "c_baseanimating.h"
  16. #include "recvproxy.h"
  17. #else
  18. #include "baseentity.h"
  19. #include "baseanimating.h"
  20. #include "sendproxy.h"
  21. #include "hierarchy.h"
  22. #endif
  23. #include "predictable_entity.h"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. //-----------------------------------------------------------------------------
  27. // KD tree query callbacks
  28. //-----------------------------------------------------------------------------
  29. class CDirtySpatialPartitionEntityList : public CAutoGameSystem, public IPartitionQueryCallback
  30. {
  31. public:
  32. CDirtySpatialPartitionEntityList( char const *name );
  33. // Members of IGameSystem
  34. virtual bool Init();
  35. virtual void Shutdown();
  36. virtual void LevelShutdownPostEntity();
  37. // Members of IPartitionQueryCallback
  38. virtual void OnPreQuery_V1() { Assert( 0 ); }
  39. virtual void OnPreQuery( SpatialPartitionListMask_t listMask );
  40. virtual void OnPostQuery( SpatialPartitionListMask_t listMask );
  41. void AddEntity( CBaseEntity *pEntity );
  42. ~CDirtySpatialPartitionEntityList();
  43. void LockPartitionForRead()
  44. {
  45. if ( m_readLockCount == 0 )
  46. {
  47. m_partitionMutex.LockForRead();
  48. }
  49. m_readLockCount++;
  50. }
  51. void UnlockPartitionForRead()
  52. {
  53. m_readLockCount--;
  54. if ( m_readLockCount == 0 )
  55. {
  56. m_partitionMutex.UnlockRead();
  57. }
  58. }
  59. private:
  60. CTSListWithFreeList<CBaseHandle> m_DirtyEntities;
  61. CThreadSpinRWLock m_partitionMutex;
  62. uint32 m_partitionWriteId;
  63. CThreadLocalInt<> m_readLockCount;
  64. };
  65. //-----------------------------------------------------------------------------
  66. // Singleton instance
  67. //-----------------------------------------------------------------------------
  68. static CDirtySpatialPartitionEntityList s_DirtyKDTree( "CDirtySpatialPartitionEntityList" );
  69. //-----------------------------------------------------------------------------
  70. // Force spatial partition updates (to avoid threading problems caused by lazy update)
  71. //-----------------------------------------------------------------------------
  72. void UpdateDirtySpatialPartitionEntities()
  73. {
  74. SpatialPartitionListMask_t listMask;
  75. #ifdef CLIENT_DLL
  76. listMask = PARTITION_CLIENT_GAME_EDICTS;
  77. #else
  78. listMask = PARTITION_SERVER_GAME_EDICTS;
  79. #endif
  80. s_DirtyKDTree.OnPreQuery( listMask );
  81. s_DirtyKDTree.OnPostQuery( listMask );
  82. }
  83. //-----------------------------------------------------------------------------
  84. // Purpose: Constructor.
  85. //-----------------------------------------------------------------------------
  86. CDirtySpatialPartitionEntityList::CDirtySpatialPartitionEntityList( char const *name ) : CAutoGameSystem( name )
  87. {
  88. m_DirtyEntities.Purge();
  89. m_readLockCount = 0;
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose: Deconstructor.
  93. //-----------------------------------------------------------------------------
  94. CDirtySpatialPartitionEntityList::~CDirtySpatialPartitionEntityList()
  95. {
  96. m_DirtyEntities.Purge();
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Initialization, shutdown
  100. //-----------------------------------------------------------------------------
  101. bool CDirtySpatialPartitionEntityList::Init()
  102. {
  103. ::partition->InstallQueryCallback( this );
  104. return true;
  105. }
  106. void CDirtySpatialPartitionEntityList::Shutdown()
  107. {
  108. ::partition->RemoveQueryCallback( this );
  109. }
  110. //-----------------------------------------------------------------------------
  111. // Makes sure all entries in the KD tree are in the correct position
  112. //-----------------------------------------------------------------------------
  113. void CDirtySpatialPartitionEntityList::AddEntity( CBaseEntity *pEntity )
  114. {
  115. m_DirtyEntities.PushItem( pEntity->GetRefEHandle() );
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Members of IGameSystem
  119. //-----------------------------------------------------------------------------
  120. void CDirtySpatialPartitionEntityList::LevelShutdownPostEntity()
  121. {
  122. m_DirtyEntities.RemoveAll();
  123. }
  124. //-----------------------------------------------------------------------------
  125. // Makes sure all entries in the KD tree are in the correct position
  126. //-----------------------------------------------------------------------------
  127. void CDirtySpatialPartitionEntityList::OnPreQuery( SpatialPartitionListMask_t listMask )
  128. {
  129. #ifdef CLIENT_DLL
  130. const int validMask = PARTITION_CLIENT_GAME_EDICTS;
  131. #else
  132. const int validMask = PARTITION_SERVER_GAME_EDICTS;
  133. #endif
  134. if ( !( listMask & validMask ) )
  135. return;
  136. if ( m_partitionWriteId != 0 && m_partitionWriteId == ThreadGetCurrentId() )
  137. return;
  138. #ifdef CLIENT_DLL
  139. // FIXME: This should really be an assertion... feh!
  140. if ( !C_BaseEntity::IsAbsRecomputationsEnabled() )
  141. {
  142. LockPartitionForRead();
  143. return;
  144. }
  145. #endif
  146. // if you're holding a read lock, then these are entities that were still dirty after your trace started
  147. // or became dirty due to some other thread or callback. Updating them may cause corruption further up the
  148. // stack (e.g. partition iterator). Ignoring the state change should be safe since it happened after the
  149. // trace was requested or was unable to be resolved in a previous attempt (still dirty).
  150. if ( m_DirtyEntities.Count() && !m_readLockCount )
  151. {
  152. CUtlVector< CBaseHandle > vecStillDirty;
  153. m_partitionMutex.LockForWrite();
  154. m_partitionWriteId = ThreadGetCurrentId();
  155. CTSListWithFreeList<CBaseHandle>::Node_t *pCurrent, *pNext;
  156. while ( ( pCurrent = m_DirtyEntities.Detach() ) != NULL )
  157. {
  158. while ( pCurrent )
  159. {
  160. CBaseHandle handle = pCurrent->elem;
  161. pNext = (CTSListWithFreeList<CBaseHandle>::Node_t *)pCurrent->Next;
  162. m_DirtyEntities.FreeNode( pCurrent );
  163. pCurrent = pNext;
  164. #ifndef CLIENT_DLL
  165. CBaseEntity *pEntity = gEntList.GetBaseEntity( handle );
  166. #else
  167. CBaseEntity *pEntity = cl_entitylist->GetBaseEntityFromHandle( handle );
  168. #endif
  169. if ( pEntity )
  170. {
  171. // If an entity is in the middle of bone setup, don't call UpdatePartition
  172. // which can cause it to redo bone setup on the same frame causing a recursive
  173. // call to bone setup.
  174. if ( !pEntity->IsEFlagSet( EFL_SETTING_UP_BONES ) )
  175. {
  176. pEntity->CollisionProp()->UpdatePartition();
  177. }
  178. else
  179. {
  180. vecStillDirty.AddToTail( handle );
  181. }
  182. }
  183. }
  184. }
  185. if ( vecStillDirty.Count() > 0 )
  186. {
  187. for ( int i = 0; i < vecStillDirty.Count(); i++ )
  188. {
  189. m_DirtyEntities.PushItem( vecStillDirty[i] );
  190. }
  191. }
  192. m_partitionWriteId = 0;
  193. m_partitionMutex.UnlockWrite();
  194. }
  195. LockPartitionForRead();
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Makes sure all entries in the KD tree are in the correct position
  199. //-----------------------------------------------------------------------------
  200. void CDirtySpatialPartitionEntityList::OnPostQuery( SpatialPartitionListMask_t listMask )
  201. {
  202. #ifdef CLIENT_DLL
  203. if ( !( listMask & PARTITION_CLIENT_GAME_EDICTS ) )
  204. return;
  205. #else
  206. if ( !( listMask & PARTITION_SERVER_GAME_EDICTS ) )
  207. return;
  208. #endif
  209. if ( m_partitionWriteId != 0 )
  210. return;
  211. UnlockPartitionForRead();
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Save/load
  215. //-----------------------------------------------------------------------------
  216. #ifndef CLIENT_DLL
  217. BEGIN_DATADESC_NO_BASE( CCollisionProperty )
  218. // DEFINE_FIELD( m_pOuter, FIELD_CLASSPTR ),
  219. DEFINE_GLOBAL_FIELD( m_vecMinsPreScaled, FIELD_VECTOR ),
  220. DEFINE_GLOBAL_FIELD( m_vecMaxsPreScaled, FIELD_VECTOR ),
  221. DEFINE_GLOBAL_FIELD( m_vecMins, FIELD_VECTOR ),
  222. DEFINE_GLOBAL_FIELD( m_vecMaxs, FIELD_VECTOR ),
  223. DEFINE_KEYFIELD( m_nSolidType, FIELD_CHARACTER, "solid" ),
  224. DEFINE_FIELD( m_usSolidFlags, FIELD_SHORT ),
  225. DEFINE_FIELD( m_nSurroundType, FIELD_CHARACTER ),
  226. DEFINE_FIELD( m_flRadius, FIELD_FLOAT ),
  227. DEFINE_FIELD( m_triggerBloat, FIELD_CHARACTER ),
  228. DEFINE_FIELD( m_vecSpecifiedSurroundingMinsPreScaled, FIELD_VECTOR ),
  229. DEFINE_FIELD( m_vecSpecifiedSurroundingMaxsPreScaled, FIELD_VECTOR ),
  230. DEFINE_FIELD( m_vecSpecifiedSurroundingMins, FIELD_VECTOR ),
  231. DEFINE_FIELD( m_vecSpecifiedSurroundingMaxs, FIELD_VECTOR ),
  232. DEFINE_FIELD( m_vecSurroundingMins, FIELD_VECTOR ),
  233. DEFINE_FIELD( m_vecSurroundingMaxs, FIELD_VECTOR ),
  234. // DEFINE_FIELD( m_Partition, FIELD_SHORT ),
  235. // DEFINE_PHYSPTR( m_pPhysicsObject ),
  236. END_DATADESC()
  237. #else
  238. //-----------------------------------------------------------------------------
  239. // Prediction
  240. //-----------------------------------------------------------------------------
  241. BEGIN_PREDICTION_DATA_NO_BASE( CCollisionProperty )
  242. DEFINE_PRED_FIELD( m_vecMinsPreScaled, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
  243. DEFINE_PRED_FIELD( m_vecMaxsPreScaled, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
  244. DEFINE_PRED_FIELD( m_vecMins, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
  245. DEFINE_PRED_FIELD( m_vecMaxs, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
  246. DEFINE_PRED_FIELD( m_nSolidType, FIELD_CHARACTER, FTYPEDESC_INSENDTABLE ),
  247. DEFINE_PRED_FIELD( m_usSolidFlags, FIELD_SHORT, FTYPEDESC_INSENDTABLE ),
  248. DEFINE_PRED_FIELD( m_triggerBloat, FIELD_CHARACTER, FTYPEDESC_INSENDTABLE ),
  249. END_PREDICTION_DATA()
  250. #endif
  251. //-----------------------------------------------------------------------------
  252. // Networking
  253. //-----------------------------------------------------------------------------
  254. #ifdef CLIENT_DLL
  255. static void RecvProxy_Solid( const CRecvProxyData *pData, void *pStruct, void *pOut )
  256. {
  257. ((CCollisionProperty*)pStruct)->SetSolid( (SolidType_t)pData->m_Value.m_Int );
  258. }
  259. static void RecvProxy_SolidFlags( const CRecvProxyData *pData, void *pStruct, void *pOut )
  260. {
  261. ((CCollisionProperty*)pStruct)->SetSolidFlags( pData->m_Value.m_Int );
  262. }
  263. static void RecvProxy_OBBMinsPreScaled( const CRecvProxyData *pData, void *pStruct, void *pOut )
  264. {
  265. CCollisionProperty *pProp = ((CCollisionProperty*)pStruct);
  266. Vector &vecMins = *((Vector*)pData->m_Value.m_Vector);
  267. pProp->SetCollisionBounds( vecMins, pProp->OBBMaxsPreScaled() );
  268. }
  269. static void RecvProxy_OBBMaxsPreScaled( const CRecvProxyData *pData, void *pStruct, void *pOut )
  270. {
  271. CCollisionProperty *pProp = ((CCollisionProperty*)pStruct);
  272. Vector &vecMaxs = *((Vector*)pData->m_Value.m_Vector);
  273. pProp->SetCollisionBounds( pProp->OBBMinsPreScaled(), vecMaxs );
  274. }
  275. static void RecvProxy_VectorDirtySurround( const CRecvProxyData *pData, void *pStruct, void *pOut )
  276. {
  277. Vector &vecold = *((Vector*)pOut);
  278. Vector vecnew( pData->m_Value.m_Vector[0], pData->m_Value.m_Vector[1], pData->m_Value.m_Vector[2] );
  279. if ( vecold != vecnew )
  280. {
  281. vecold = vecnew;
  282. ((CCollisionProperty*)pStruct)->MarkSurroundingBoundsDirty();
  283. }
  284. }
  285. static void RecvProxy_IntDirtySurround( const CRecvProxyData *pData, void *pStruct, void *pOut )
  286. {
  287. if ( *((unsigned char*)pOut) != pData->m_Value.m_Int )
  288. {
  289. *((unsigned char*)pOut) = pData->m_Value.m_Int;
  290. ((CCollisionProperty*)pStruct)->MarkSurroundingBoundsDirty();
  291. }
  292. }
  293. #else
  294. static void SendProxy_Solid( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
  295. {
  296. pOut->m_Int = ((CCollisionProperty*)pStruct)->GetSolid();
  297. }
  298. static void SendProxy_SolidFlags( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
  299. {
  300. pOut->m_Int = ((CCollisionProperty*)pStruct)->GetSolidFlags();
  301. }
  302. #endif
  303. BEGIN_NETWORK_TABLE_NOBASE( CCollisionProperty, DT_CollisionProperty )
  304. #ifdef CLIENT_DLL
  305. RecvPropVector( RECVINFO(m_vecMinsPreScaled), 0, RecvProxy_OBBMinsPreScaled ),
  306. RecvPropVector( RECVINFO(m_vecMaxsPreScaled), 0, RecvProxy_OBBMaxsPreScaled ),
  307. RecvPropVector( RECVINFO(m_vecMins), 0 ),
  308. RecvPropVector( RECVINFO(m_vecMaxs), 0 ),
  309. RecvPropInt( RECVINFO( m_nSolidType ), 0, RecvProxy_Solid ),
  310. RecvPropInt( RECVINFO( m_usSolidFlags ), 0, RecvProxy_SolidFlags ),
  311. RecvPropInt( RECVINFO(m_nSurroundType), 0, RecvProxy_IntDirtySurround ),
  312. RecvPropInt( RECVINFO(m_triggerBloat), 0, RecvProxy_IntDirtySurround ),
  313. RecvPropVector( RECVINFO(m_vecSpecifiedSurroundingMinsPreScaled), 0, RecvProxy_VectorDirtySurround ),
  314. RecvPropVector( RECVINFO(m_vecSpecifiedSurroundingMaxsPreScaled), 0, RecvProxy_VectorDirtySurround ),
  315. RecvPropVector( RECVINFO(m_vecSpecifiedSurroundingMins), 0, RecvProxy_VectorDirtySurround ),
  316. RecvPropVector( RECVINFO(m_vecSpecifiedSurroundingMaxs), 0, RecvProxy_VectorDirtySurround ),
  317. #else
  318. SendPropVector( SENDINFO(m_vecMinsPreScaled), 0, SPROP_NOSCALE),
  319. SendPropVector( SENDINFO(m_vecMaxsPreScaled), 0, SPROP_NOSCALE),
  320. SendPropVector( SENDINFO(m_vecMins), 0, SPROP_NOSCALE),
  321. SendPropVector( SENDINFO(m_vecMaxs), 0, SPROP_NOSCALE),
  322. SendPropInt( SENDINFO( m_nSolidType ), 3, SPROP_UNSIGNED, SendProxy_Solid ),
  323. SendPropInt( SENDINFO( m_usSolidFlags ), FSOLID_MAX_BITS, SPROP_UNSIGNED, SendProxy_SolidFlags ),
  324. SendPropInt( SENDINFO( m_nSurroundType ), SURROUNDING_TYPE_BIT_COUNT, SPROP_UNSIGNED ),
  325. SendPropInt( SENDINFO(m_triggerBloat), 0, SPROP_UNSIGNED),
  326. SendPropVector( SENDINFO(m_vecSpecifiedSurroundingMinsPreScaled), 0, SPROP_NOSCALE),
  327. SendPropVector( SENDINFO(m_vecSpecifiedSurroundingMaxsPreScaled), 0, SPROP_NOSCALE),
  328. SendPropVector( SENDINFO(m_vecSpecifiedSurroundingMins), 0, SPROP_NOSCALE),
  329. SendPropVector( SENDINFO(m_vecSpecifiedSurroundingMaxs), 0, SPROP_NOSCALE),
  330. #endif
  331. END_NETWORK_TABLE()
  332. //-----------------------------------------------------------------------------
  333. // Constructor, destructor
  334. //-----------------------------------------------------------------------------
  335. CCollisionProperty::CCollisionProperty()
  336. {
  337. m_Partition = PARTITION_INVALID_HANDLE;
  338. Init( NULL );
  339. }
  340. CCollisionProperty::~CCollisionProperty()
  341. {
  342. DestroyPartitionHandle();
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Initialization
  346. //-----------------------------------------------------------------------------
  347. void CCollisionProperty::Init( CBaseEntity *pEntity )
  348. {
  349. m_pOuter = pEntity;
  350. m_vecMinsPreScaled.GetForModify().Init();
  351. m_vecMaxsPreScaled.GetForModify().Init();
  352. m_vecMins.GetForModify().Init();
  353. m_vecMaxs.GetForModify().Init();
  354. m_flRadius = 0.0f;
  355. m_triggerBloat = 0;
  356. m_usSolidFlags = 0;
  357. m_nSolidType = SOLID_NONE;
  358. // NOTE: This replicates previous behavior; we may always want to use BEST_COLLISION_BOUNDS
  359. m_nSurroundType = USE_OBB_COLLISION_BOUNDS;
  360. m_vecSurroundingMins = vec3_origin;
  361. m_vecSurroundingMaxs = vec3_origin;
  362. m_vecSpecifiedSurroundingMinsPreScaled.GetForModify().Init();
  363. m_vecSpecifiedSurroundingMaxsPreScaled.GetForModify().Init();
  364. m_vecSpecifiedSurroundingMins.GetForModify().Init();
  365. m_vecSpecifiedSurroundingMaxs.GetForModify().Init();
  366. }
  367. //-----------------------------------------------------------------------------
  368. // EntityHandle
  369. //-----------------------------------------------------------------------------
  370. IHandleEntity *CCollisionProperty::GetEntityHandle()
  371. {
  372. return m_pOuter;
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Collision group
  376. //-----------------------------------------------------------------------------
  377. int CCollisionProperty::GetCollisionGroup() const
  378. {
  379. return m_pOuter->GetCollisionGroup();
  380. }
  381. bool CCollisionProperty::ShouldTouchTrigger( int triggerSolidFlags ) const
  382. {
  383. // debris only touches certain triggers
  384. if ( GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
  385. {
  386. if ( triggerSolidFlags & FSOLID_TRIGGER_TOUCH_DEBRIS )
  387. return true;
  388. return false;
  389. }
  390. // triggers don't touch other triggers (might be solid to other ents as well as trigger)
  391. if ( IsSolidFlagSet( FSOLID_TRIGGER ) )
  392. return false;
  393. return true;
  394. }
  395. const matrix3x4_t *CCollisionProperty::GetRootParentToWorldTransform() const
  396. {
  397. if ( IsSolidFlagSet( FSOLID_ROOT_PARENT_ALIGNED ) )
  398. {
  399. CBaseEntity *pEntity = m_pOuter->GetRootMoveParent();
  400. Assert(pEntity);
  401. if ( pEntity )
  402. {
  403. return &pEntity->CollisionProp()->CollisionToWorldTransform();
  404. }
  405. }
  406. return NULL;
  407. }
  408. //-----------------------------------------------------------------------------
  409. // IClientUnknown
  410. //-----------------------------------------------------------------------------
  411. IClientUnknown* CCollisionProperty::GetIClientUnknown()
  412. {
  413. #ifdef CLIENT_DLL
  414. return m_pOuter->GetIClientUnknown();
  415. #else
  416. return NULL;
  417. #endif
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Check for untouch
  421. //-----------------------------------------------------------------------------
  422. void CCollisionProperty::CheckForUntouch()
  423. {
  424. #ifndef CLIENT_DLL
  425. if ( !IsSolid() && !IsSolidFlagSet(FSOLID_TRIGGER))
  426. {
  427. // If this ent's touch list isn't empty, it's transitioning to not solid
  428. if ( m_pOuter->IsCurrentlyTouching() )
  429. {
  430. // mark ent so that at the end of frame it will check to
  431. // see if it's no longer touching ents
  432. m_pOuter->SetCheckUntouch( true );
  433. }
  434. }
  435. #endif
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Sets the solid type
  439. //-----------------------------------------------------------------------------
  440. void CCollisionProperty::SetSolid( SolidType_t val )
  441. {
  442. if ( m_nSolidType == val )
  443. return;
  444. #ifndef CLIENT_DLL
  445. bool bWasNotSolid = IsSolid();
  446. #endif
  447. MarkSurroundingBoundsDirty();
  448. // OBB is not yet implemented
  449. if ( val == SOLID_BSP )
  450. {
  451. if ( GetOuter()->GetMoveParent() )
  452. {
  453. if ( GetOuter()->GetRootMoveParent()->GetSolid() != SOLID_BSP )
  454. {
  455. // must be SOLID_VPHYSICS because parent might rotate
  456. val = SOLID_VPHYSICS;
  457. }
  458. }
  459. #ifndef CLIENT_DLL
  460. // UNDONE: This should be fine in the client DLL too. Move GetAllChildren() into shared code.
  461. // If the root of the hierarchy is SOLID_BSP, then assume that the designer
  462. // wants the collisions to rotate with this hierarchy so that the player can
  463. // move while riding the hierarchy.
  464. if ( !GetOuter()->GetMoveParent() )
  465. {
  466. // NOTE: This assumes things don't change back from SOLID_BSP
  467. // NOTE: This is 100% true for HL2 - need to support removing the flag to support changing from SOLID_BSP
  468. CUtlVector<CBaseEntity *> list;
  469. GetAllChildren( GetOuter(), list );
  470. for ( int i = list.Count()-1; i>=0; --i )
  471. {
  472. list[i]->AddSolidFlags( FSOLID_ROOT_PARENT_ALIGNED );
  473. }
  474. }
  475. #endif
  476. }
  477. m_nSolidType = val;
  478. #ifndef CLIENT_DLL
  479. m_pOuter->CollisionRulesChanged();
  480. UpdateServerPartitionMask( );
  481. if ( bWasNotSolid != IsSolid() )
  482. {
  483. CheckForUntouch();
  484. }
  485. #endif
  486. }
  487. SolidType_t CCollisionProperty::GetSolid() const
  488. {
  489. return (SolidType_t)m_nSolidType.Get();
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Sets the solid flags
  493. //-----------------------------------------------------------------------------
  494. void CCollisionProperty::SetSolidFlags( int flags )
  495. {
  496. int oldFlags = m_usSolidFlags;
  497. m_usSolidFlags = (unsigned short)(flags & 0xFFFF);
  498. if ( oldFlags == m_usSolidFlags )
  499. return;
  500. // These two flags, if changed, can produce different surrounding bounds
  501. if ( (oldFlags & (FSOLID_FORCE_WORLD_ALIGNED | FSOLID_USE_TRIGGER_BOUNDS)) !=
  502. (m_usSolidFlags & (FSOLID_FORCE_WORLD_ALIGNED | FSOLID_USE_TRIGGER_BOUNDS)) )
  503. {
  504. MarkSurroundingBoundsDirty();
  505. }
  506. if ( (oldFlags & (FSOLID_NOT_SOLID|FSOLID_TRIGGER)) != (m_usSolidFlags & (FSOLID_NOT_SOLID|FSOLID_TRIGGER)) )
  507. {
  508. m_pOuter->CollisionRulesChanged();
  509. }
  510. #ifndef CLIENT_DLL
  511. if ( (oldFlags & (FSOLID_NOT_SOLID | FSOLID_TRIGGER)) != (m_usSolidFlags & (FSOLID_NOT_SOLID | FSOLID_TRIGGER)) )
  512. {
  513. UpdateServerPartitionMask( );
  514. CheckForUntouch();
  515. }
  516. #endif
  517. }
  518. //-----------------------------------------------------------------------------
  519. // Coordinate system of the collision model
  520. //-----------------------------------------------------------------------------
  521. const Vector& CCollisionProperty::GetCollisionOrigin() const
  522. {
  523. return m_pOuter->GetAbsOrigin();
  524. }
  525. const QAngle& CCollisionProperty::GetCollisionAngles() const
  526. {
  527. if ( IsBoundsDefinedInEntitySpace() )
  528. {
  529. return m_pOuter->GetAbsAngles();
  530. }
  531. return vec3_angle;
  532. }
  533. const matrix3x4_t& CCollisionProperty::CollisionToWorldTransform() const
  534. {
  535. static matrix3x4_t s_matTemp[4];
  536. static int s_nIndex = 0;
  537. matrix3x4_t &matResult = s_matTemp[s_nIndex];
  538. s_nIndex = (s_nIndex+1) & 0x3;
  539. if ( IsBoundsDefinedInEntitySpace() )
  540. {
  541. return m_pOuter->EntityToWorldTransform();
  542. }
  543. SetIdentityMatrix( matResult );
  544. MatrixSetColumn( GetCollisionOrigin(), 3, matResult );
  545. return matResult;
  546. }
  547. //-----------------------------------------------------------------------------
  548. // Sets the collision bounds + the size
  549. //-----------------------------------------------------------------------------
  550. void CCollisionProperty::SetCollisionBounds( const Vector &mins, const Vector &maxs )
  551. {
  552. if ( ( m_vecMinsPreScaled != mins ) || ( m_vecMaxsPreScaled != maxs ) )
  553. {
  554. m_vecMinsPreScaled = mins;
  555. m_vecMaxsPreScaled = maxs;
  556. }
  557. bool bDirty = false;
  558. // Check if it's a scaled model
  559. CBaseAnimating *pAnim = GetOuter()->GetBaseAnimating();
  560. if ( pAnim && pAnim->GetModelScale() != 1.0f )
  561. {
  562. // Do the scaling
  563. Vector vecNewMins = mins * pAnim->GetModelScale();
  564. Vector vecNewMaxs = maxs * pAnim->GetModelScale();
  565. if ( ( m_vecMins != vecNewMins ) || ( m_vecMaxs != vecNewMaxs ) )
  566. {
  567. m_vecMins = vecNewMins;
  568. m_vecMaxs = vecNewMaxs;
  569. bDirty = true;
  570. }
  571. }
  572. else
  573. {
  574. // No scaling needed!
  575. if ( ( m_vecMins != mins ) || ( m_vecMaxs != maxs ) )
  576. {
  577. m_vecMins = mins;
  578. m_vecMaxs = maxs;
  579. bDirty = true;
  580. }
  581. }
  582. if ( bDirty )
  583. {
  584. //ASSERT_COORD( m_vecMins.Get() );
  585. //ASSERT_COORD( m_vecMaxs.Get() );
  586. Vector vecSize;
  587. VectorSubtract( m_vecMaxs, m_vecMins, vecSize );
  588. m_flRadius = vecSize.Length() * 0.5f;
  589. MarkSurroundingBoundsDirty();
  590. }
  591. }
  592. //-----------------------------------------------------------------------------
  593. // Rebuilds the scaled bounds from the prescaled bounds after a model's scale has changed
  594. //-----------------------------------------------------------------------------
  595. void CCollisionProperty::RefreshScaledCollisionBounds( void )
  596. {
  597. SetCollisionBounds( m_vecMinsPreScaled, m_vecMaxsPreScaled );
  598. SurroundingBoundsType_t nSurroundType = static_cast< SurroundingBoundsType_t >( m_nSurroundType.Get() );
  599. if ( nSurroundType == USE_SPECIFIED_BOUNDS )
  600. {
  601. SetSurroundingBoundsType( nSurroundType,
  602. &(m_vecSpecifiedSurroundingMinsPreScaled.Get()),
  603. &(m_vecSpecifiedSurroundingMaxsPreScaled.Get()) );
  604. }
  605. else
  606. {
  607. SetSurroundingBoundsType( nSurroundType );
  608. }
  609. }
  610. //-----------------------------------------------------------------------------
  611. // Lazily calculates the 2D bounding radius. If we do this enough, we should
  612. // calculate this in SetCollisionBounds above and cache the results in a data member!
  613. //-----------------------------------------------------------------------------
  614. float CCollisionProperty::BoundingRadius2D() const
  615. {
  616. Vector vecSize;
  617. VectorSubtract( m_vecMaxs, m_vecMins, vecSize );
  618. vecSize.z = 0;
  619. return vecSize.Length() * 0.5f;
  620. }
  621. //-----------------------------------------------------------------------------
  622. // Special trigger representation (OBB)
  623. //-----------------------------------------------------------------------------
  624. void CCollisionProperty::WorldSpaceTriggerBounds( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) const
  625. {
  626. WorldSpaceAABB( pVecWorldMins, pVecWorldMaxs );
  627. if ( ( GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS ) == 0 )
  628. return;
  629. // Don't bloat below, we don't want to trigger it with our heads
  630. pVecWorldMins->x -= m_triggerBloat;
  631. pVecWorldMins->y -= m_triggerBloat;
  632. pVecWorldMaxs->x += m_triggerBloat;
  633. pVecWorldMaxs->y += m_triggerBloat;
  634. pVecWorldMaxs->z += (float)m_triggerBloat * 0.5f;
  635. }
  636. void CCollisionProperty::UseTriggerBounds( bool bEnable, float flBloat )
  637. {
  638. Assert( flBloat <= 127.0f );
  639. m_triggerBloat = (char )flBloat;
  640. if ( bEnable )
  641. {
  642. AddSolidFlags( FSOLID_USE_TRIGGER_BOUNDS );
  643. Assert( flBloat > 0.0f );
  644. }
  645. else
  646. {
  647. RemoveSolidFlags( FSOLID_USE_TRIGGER_BOUNDS );
  648. }
  649. }
  650. //-----------------------------------------------------------------------------
  651. // Collision model (BSP)
  652. //-----------------------------------------------------------------------------
  653. int CCollisionProperty::GetCollisionModelIndex()
  654. {
  655. return m_pOuter->GetModelIndex();
  656. }
  657. const model_t* CCollisionProperty::GetCollisionModel()
  658. {
  659. return m_pOuter->GetModel();
  660. }
  661. //-----------------------------------------------------------------------------
  662. // Collision methods implemented in the entity
  663. // FIXME: This shouldn't happen there!!
  664. //-----------------------------------------------------------------------------
  665. bool CCollisionProperty::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
  666. {
  667. return m_pOuter->TestCollision( ray, fContentsMask, tr );
  668. }
  669. bool CCollisionProperty::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
  670. {
  671. return m_pOuter->TestHitboxes( ray, fContentsMask, tr );
  672. }
  673. //-----------------------------------------------------------------------------
  674. // Computes a "normalized" point (range 0,0,0 - 1,1,1) in collision space
  675. //-----------------------------------------------------------------------------
  676. const Vector & CCollisionProperty::NormalizedToCollisionSpace( const Vector &in, Vector *pResult ) const
  677. {
  678. pResult->x = Lerp( in.x, m_vecMins.Get().x, m_vecMaxs.Get().x );
  679. pResult->y = Lerp( in.y, m_vecMins.Get().y, m_vecMaxs.Get().y );
  680. pResult->z = Lerp( in.z, m_vecMins.Get().z, m_vecMaxs.Get().z );
  681. return *pResult;
  682. }
  683. //-----------------------------------------------------------------------------
  684. // Transforms a point in collision space to normalized space
  685. //-----------------------------------------------------------------------------
  686. const Vector & CCollisionProperty::CollisionToNormalizedSpace( const Vector &in, Vector *pResult ) const
  687. {
  688. Vector vecSize = OBBSize( );
  689. pResult->x = ( vecSize.x != 0.0f ) ? ( in.x - m_vecMins.Get().x ) / vecSize.x : 0.5f;
  690. pResult->y = ( vecSize.y != 0.0f ) ? ( in.y - m_vecMins.Get().y ) / vecSize.y : 0.5f;
  691. pResult->z = ( vecSize.z != 0.0f ) ? ( in.z - m_vecMins.Get().z ) / vecSize.z : 0.5f;
  692. return *pResult;
  693. }
  694. //-----------------------------------------------------------------------------
  695. // Computes a "normalized" point (range 0,0,0 - 1,1,1) in world space
  696. //-----------------------------------------------------------------------------
  697. const Vector & CCollisionProperty::NormalizedToWorldSpace( const Vector &in, Vector *pResult ) const
  698. {
  699. Vector vecCollisionSpace;
  700. NormalizedToCollisionSpace( in, &vecCollisionSpace );
  701. CollisionToWorldSpace( vecCollisionSpace, pResult );
  702. return *pResult;
  703. }
  704. //-----------------------------------------------------------------------------
  705. // Transforms a point in world space to normalized space
  706. //-----------------------------------------------------------------------------
  707. const Vector & CCollisionProperty::WorldToNormalizedSpace( const Vector &in, Vector *pResult ) const
  708. {
  709. Vector vecCollisionSpace;
  710. WorldToCollisionSpace( in, &vecCollisionSpace );
  711. CollisionToNormalizedSpace( vecCollisionSpace, pResult );
  712. return *pResult;
  713. }
  714. //-----------------------------------------------------------------------------
  715. // Selects a random point in the bounds given the normalized 0-1 bounds
  716. //-----------------------------------------------------------------------------
  717. void CCollisionProperty::RandomPointInBounds( const Vector &vecNormalizedMins, const Vector &vecNormalizedMaxs, Vector *pPoint) const
  718. {
  719. Vector vecNormalizedSpace;
  720. vecNormalizedSpace.x = random->RandomFloat( vecNormalizedMins.x, vecNormalizedMaxs.x );
  721. vecNormalizedSpace.y = random->RandomFloat( vecNormalizedMins.y, vecNormalizedMaxs.y );
  722. vecNormalizedSpace.z = random->RandomFloat( vecNormalizedMins.z, vecNormalizedMaxs.z );
  723. NormalizedToWorldSpace( vecNormalizedSpace, pPoint );
  724. }
  725. //-----------------------------------------------------------------------------
  726. // Transforms an AABB measured in entity space to a box that surrounds it in world space
  727. //-----------------------------------------------------------------------------
  728. void CCollisionProperty::CollisionAABBToWorldAABB( const Vector &entityMins,
  729. const Vector &entityMaxs, Vector *pWorldMins, Vector *pWorldMaxs ) const
  730. {
  731. if ( !IsBoundsDefinedInEntitySpace() || (GetCollisionAngles() == vec3_angle) )
  732. {
  733. VectorAdd( entityMins, GetCollisionOrigin(), *pWorldMins );
  734. VectorAdd( entityMaxs, GetCollisionOrigin(), *pWorldMaxs );
  735. }
  736. else
  737. {
  738. TransformAABB( CollisionToWorldTransform(), entityMins, entityMaxs, *pWorldMins, *pWorldMaxs );
  739. }
  740. }
  741. /*
  742. void CCollisionProperty::WorldAABBToCollisionAABB( const Vector &worldMins, const Vector &worldMaxs, Vector *pEntityMins, Vector *pEntityMaxs ) const
  743. {
  744. if ( !IsBoundsDefinedInEntitySpace() || (GetCollisionAngles() == vec3_angle) )
  745. {
  746. VectorSubtract( worldMins, GetAbsOrigin(), *pEntityMins );
  747. VectorSubtract( worldMaxs, GetAbsOrigin(), *pEntityMaxs );
  748. }
  749. else
  750. {
  751. ITransformAABB( CollisionToWorldTransform(), worldMins, worldMaxs, *pEntityMins, *pEntityMaxs );
  752. }
  753. }
  754. */
  755. //-----------------------------------------------------------------------------
  756. // Is a worldspace point within the bounds of the OBB?
  757. //-----------------------------------------------------------------------------
  758. bool CCollisionProperty::IsPointInBounds( const Vector &vecWorldPt ) const
  759. {
  760. Vector vecLocalSpace;
  761. WorldToCollisionSpace( vecWorldPt, &vecLocalSpace );
  762. return ( ( vecLocalSpace.x >= m_vecMins.Get().x && vecLocalSpace.x <= m_vecMaxs.Get().x ) &&
  763. ( vecLocalSpace.y >= m_vecMins.Get().y && vecLocalSpace.y <= m_vecMaxs.Get().y ) &&
  764. ( vecLocalSpace.z >= m_vecMins.Get().z && vecLocalSpace.z <= m_vecMaxs.Get().z ) );
  765. }
  766. //-----------------------------------------------------------------------------
  767. // Computes the nearest point in the OBB to a point specified in world space
  768. //-----------------------------------------------------------------------------
  769. void CCollisionProperty::CalcNearestPoint( const Vector &vecWorldPt, Vector *pVecNearestWorldPt ) const
  770. {
  771. // Calculate physics force
  772. Vector localPt, localClosestPt;
  773. WorldToCollisionSpace( vecWorldPt, &localPt );
  774. CalcClosestPointOnAABB( m_vecMins.Get(), m_vecMaxs.Get(), localPt, localClosestPt );
  775. CollisionToWorldSpace( localClosestPt, pVecNearestWorldPt );
  776. }
  777. //-----------------------------------------------------------------------------
  778. // Computes the nearest point in the OBB to a point specified in world space
  779. //-----------------------------------------------------------------------------
  780. float CCollisionProperty::CalcDistanceFromPoint( const Vector &vecWorldPt ) const
  781. {
  782. // Calculate physics force
  783. Vector localPt, localClosestPt;
  784. WorldToCollisionSpace( vecWorldPt, &localPt );
  785. CalcClosestPointOnAABB( m_vecMins.Get(), m_vecMaxs.Get(), localPt, localClosestPt );
  786. return localPt.DistTo( localClosestPt );
  787. }
  788. //-----------------------------------------------------------------------------
  789. // Compute the largest dot product of the OBB and the specified direction vector
  790. //-----------------------------------------------------------------------------
  791. float CCollisionProperty::ComputeSupportMap( const Vector &vecDirection ) const
  792. {
  793. Vector vecCollisionDir;
  794. WorldDirectionToCollisionSpace( vecDirection, &vecCollisionDir );
  795. float flResult = DotProduct( GetCollisionOrigin(), vecDirection );
  796. flResult += (( vecCollisionDir.x >= 0.0f ) ? m_vecMaxs.Get().x : m_vecMins.Get().x) * vecCollisionDir.x;
  797. flResult += (( vecCollisionDir.y >= 0.0f ) ? m_vecMaxs.Get().y : m_vecMins.Get().y) * vecCollisionDir.y;
  798. flResult += (( vecCollisionDir.z >= 0.0f ) ? m_vecMaxs.Get().z : m_vecMins.Get().z) * vecCollisionDir.z;
  799. return flResult;
  800. }
  801. //-----------------------------------------------------------------------------
  802. // Expand trigger bounds..
  803. //-----------------------------------------------------------------------------
  804. void CCollisionProperty::ComputeVPhysicsSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
  805. {
  806. bool bSetBounds = false;
  807. IPhysicsObject *pPhysicsObject = GetOuter()->VPhysicsGetObject();
  808. if ( pPhysicsObject )
  809. {
  810. if ( pPhysicsObject->GetCollide() )
  811. {
  812. physcollision->CollideGetAABB( pVecWorldMins, pVecWorldMaxs,
  813. pPhysicsObject->GetCollide(), GetCollisionOrigin(), GetCollisionAngles() );
  814. bSetBounds = true;
  815. }
  816. else if ( pPhysicsObject->GetSphereRadius( ) )
  817. {
  818. float flRadius = pPhysicsObject->GetSphereRadius( );
  819. Vector vecExtents( flRadius, flRadius, flRadius );
  820. VectorSubtract( GetCollisionOrigin(), vecExtents, *pVecWorldMins );
  821. VectorAdd( GetCollisionOrigin(), vecExtents, *pVecWorldMaxs );
  822. bSetBounds = true;
  823. }
  824. }
  825. if ( !bSetBounds )
  826. {
  827. *pVecWorldMins = GetCollisionOrigin();
  828. *pVecWorldMaxs = *pVecWorldMins;
  829. }
  830. // Also, lets expand for the trigger bounds also
  831. if ( IsSolidFlagSet( FSOLID_USE_TRIGGER_BOUNDS ) )
  832. {
  833. Vector vecWorldTriggerMins, vecWorldTriggerMaxs;
  834. WorldSpaceTriggerBounds( &vecWorldTriggerMins, &vecWorldTriggerMaxs );
  835. VectorMin( vecWorldTriggerMins, *pVecWorldMins, *pVecWorldMins );
  836. VectorMax( vecWorldTriggerMaxs, *pVecWorldMaxs, *pVecWorldMaxs );
  837. }
  838. }
  839. //-----------------------------------------------------------------------------
  840. // Expand trigger bounds..
  841. //-----------------------------------------------------------------------------
  842. bool CCollisionProperty::ComputeHitboxSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
  843. {
  844. CBaseAnimating *pAnim = GetOuter()->GetBaseAnimating();
  845. if (pAnim)
  846. {
  847. return pAnim->ComputeHitboxSurroundingBox( pVecWorldMins, pVecWorldMaxs );
  848. }
  849. return false;
  850. }
  851. //-----------------------------------------------------------------------------
  852. // Expand trigger bounds..
  853. //-----------------------------------------------------------------------------
  854. bool CCollisionProperty::ComputeEntitySpaceHitboxSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
  855. {
  856. CBaseAnimating *pAnim = GetOuter()->GetBaseAnimating();
  857. if (pAnim)
  858. {
  859. return pAnim->ComputeEntitySpaceHitboxSurroundingBox( pVecWorldMins, pVecWorldMaxs );
  860. }
  861. return false;
  862. }
  863. //-----------------------------------------------------------------------------
  864. // Computes the surrounding collision bounds from the the OBB (not vphysics)
  865. //-----------------------------------------------------------------------------
  866. void CCollisionProperty::ComputeRotationExpandedBounds( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
  867. {
  868. if ( !IsBoundsDefinedInEntitySpace() )
  869. {
  870. *pVecWorldMins = m_vecMins;
  871. *pVecWorldMaxs = m_vecMaxs;
  872. }
  873. else
  874. {
  875. float flMaxVal;
  876. flMaxVal = MAX( FloatMakePositive(m_vecMins.Get().x), FloatMakePositive(m_vecMaxs.Get().x) );
  877. pVecWorldMins->x = -flMaxVal;
  878. pVecWorldMaxs->x = flMaxVal;
  879. flMaxVal = MAX( FloatMakePositive(m_vecMins.Get().y), FloatMakePositive(m_vecMaxs.Get().y) );
  880. pVecWorldMins->y = -flMaxVal;
  881. pVecWorldMaxs->y = flMaxVal;
  882. flMaxVal = MAX( FloatMakePositive(m_vecMins.Get().z), FloatMakePositive(m_vecMaxs.Get().z) );
  883. pVecWorldMins->z = -flMaxVal;
  884. pVecWorldMaxs->z = flMaxVal;
  885. }
  886. }
  887. //-----------------------------------------------------------------------------
  888. // Computes the surrounding collision bounds based on whatever algorithm we want...
  889. //-----------------------------------------------------------------------------
  890. void CCollisionProperty::ComputeCollisionSurroundingBox( bool bUseVPhysics, Vector *pVecWorldMins, Vector *pVecWorldMaxs )
  891. {
  892. Assert( GetSolid() != SOLID_CUSTOM );
  893. // NOTE: For solid none, we are still going to use the bounds; necessary because
  894. // the surrounding box is used for the PVS...
  895. // FIXME: Should we make some other call for the PVS stuff?? If so, we should return
  896. // a point bounds for SOLID_NONE...
  897. // if ( GetSolid() == SOLID_NONE )
  898. // {
  899. // *pVecWorldMins = GetCollisionOrigin();
  900. // *pVecWorldMaxs = *pVecWorldMins;
  901. // return;
  902. // }
  903. if ( bUseVPhysics )
  904. {
  905. ComputeVPhysicsSurroundingBox( pVecWorldMins, pVecWorldMaxs );
  906. }
  907. else
  908. {
  909. // Will expand the bounds for the trigger, if it is a trigger
  910. WorldSpaceTriggerBounds( pVecWorldMins, pVecWorldMaxs );
  911. }
  912. }
  913. //-----------------------------------------------------------------------------
  914. // Computes the surrounding collision bounds based on whatever algorithm we want...
  915. //-----------------------------------------------------------------------------
  916. void CCollisionProperty::ComputeSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
  917. {
  918. if (( GetSolid() == SOLID_CUSTOM ) && (m_nSurroundType != USE_GAME_CODE ))
  919. {
  920. // NOTE: This can only happen in transition periods, say during network
  921. // reception on the client. We expect USE_GAME_CODE to be used with SOLID_CUSTOM
  922. *pVecWorldMins = GetCollisionOrigin();
  923. *pVecWorldMaxs = *pVecWorldMins;
  924. return;
  925. }
  926. switch( m_nSurroundType )
  927. {
  928. case USE_OBB_COLLISION_BOUNDS:
  929. {
  930. Assert( GetSolid() != SOLID_CUSTOM );
  931. bool bUseVPhysics = false;
  932. if ( ( GetSolid() == SOLID_VPHYSICS ) && ( GetOuter()->GetMoveType() == MOVETYPE_VPHYSICS ) )
  933. {
  934. // UNDONE: This may not be necessary any more.
  935. IPhysicsObject *pPhysics = GetOuter()->VPhysicsGetObject();
  936. bUseVPhysics = pPhysics && pPhysics->IsAsleep();
  937. }
  938. ComputeCollisionSurroundingBox( bUseVPhysics, pVecWorldMins, pVecWorldMaxs );
  939. }
  940. break;
  941. case USE_BEST_COLLISION_BOUNDS:
  942. Assert( GetSolid() != SOLID_CUSTOM );
  943. ComputeCollisionSurroundingBox( (GetSolid() == SOLID_VPHYSICS), pVecWorldMins, pVecWorldMaxs );
  944. break;
  945. case USE_COLLISION_BOUNDS_NEVER_VPHYSICS:
  946. Assert( GetSolid() != SOLID_CUSTOM );
  947. ComputeCollisionSurroundingBox( false, pVecWorldMins, pVecWorldMaxs );
  948. break;
  949. case USE_HITBOXES:
  950. ComputeHitboxSurroundingBox( pVecWorldMins, pVecWorldMaxs );
  951. break;
  952. case USE_ROTATION_EXPANDED_BOUNDS:
  953. ComputeRotationExpandedBounds( pVecWorldMins, pVecWorldMaxs );
  954. break;
  955. case USE_SPECIFIED_BOUNDS:
  956. VectorAdd( GetCollisionOrigin(), m_vecSpecifiedSurroundingMins, *pVecWorldMins );
  957. VectorAdd( GetCollisionOrigin(), m_vecSpecifiedSurroundingMaxs, *pVecWorldMaxs );
  958. break;
  959. case USE_GAME_CODE:
  960. GetOuter()->ComputeWorldSpaceSurroundingBox( pVecWorldMins, pVecWorldMaxs );
  961. Assert( pVecWorldMins->x <= pVecWorldMaxs->x );
  962. Assert( pVecWorldMins->y <= pVecWorldMaxs->y );
  963. Assert( pVecWorldMins->z <= pVecWorldMaxs->z );
  964. return;
  965. }
  966. #ifdef DEBUG
  967. /*
  968. // For debugging purposes, make sure the bounds actually does surround the thing.
  969. // Otherwise the optimization we were using isn't really all that great, is it?
  970. Vector vecTestMins, vecTestMaxs;
  971. ComputeCollisionSurroundingBox( (GetSolid() == SOLID_VPHYSICS), &vecTestMins, &vecTestMaxs );
  972. // Now that we have the basics, let's expand for hitboxes if appropriate
  973. Vector vecWorldHitboxMins, vecWorldHitboxMaxs;
  974. if ( ComputeHitboxSurroundingBox( &vecWorldHitboxMins, &vecWorldHitboxMaxs ) )
  975. {
  976. VectorMin( vecWorldHitboxMaxs, vecTestMins, vecTestMins );
  977. VectorMax( vecWorldHitboxMaxs, vecTestMaxs, vecTestMaxs );
  978. }
  979. Assert( vecTestMins.x >= pVecWorldMins->x && vecTestMins.y >= pVecWorldMins->y && vecTestMins.z >= pVecWorldMins->z );
  980. Assert( vecTestMaxs.x <= pVecWorldMaxs->x && vecTestMaxs.y <= pVecWorldMaxs->y && vecTestMaxs.z <= pVecWorldMaxs->z );
  981. */
  982. #endif
  983. }
  984. //-----------------------------------------------------------------------------
  985. // Sets the method by which the surrounding collision bounds is set
  986. //-----------------------------------------------------------------------------
  987. void CCollisionProperty::SetSurroundingBoundsType( SurroundingBoundsType_t type, const Vector *pMins, const Vector *pMaxs )
  988. {
  989. m_nSurroundType = type;
  990. if (type != USE_SPECIFIED_BOUNDS)
  991. {
  992. Assert( !pMins && !pMaxs );
  993. MarkSurroundingBoundsDirty();
  994. }
  995. else
  996. {
  997. Assert( pMins && pMaxs );
  998. m_vecSpecifiedSurroundingMinsPreScaled = *pMins;
  999. m_vecSpecifiedSurroundingMaxsPreScaled = *pMaxs;
  1000. // Check if it's a scaled model
  1001. CBaseAnimating *pAnim = GetOuter()->GetBaseAnimating();
  1002. if ( pAnim && pAnim->GetModelScale() != 1.0f )
  1003. {
  1004. // Do the scaling
  1005. Vector vecNewMins = *pMins * pAnim->GetModelScale();
  1006. Vector vecNewMaxs = *pMaxs * pAnim->GetModelScale();
  1007. m_vecSpecifiedSurroundingMins = vecNewMins;
  1008. m_vecSpecifiedSurroundingMaxs = vecNewMaxs;
  1009. m_vecSurroundingMins = vecNewMins;
  1010. m_vecSurroundingMaxs = vecNewMaxs;
  1011. }
  1012. else
  1013. {
  1014. // No scaling needed!
  1015. m_vecSpecifiedSurroundingMins = *pMins;
  1016. m_vecSpecifiedSurroundingMaxs = *pMaxs;
  1017. m_vecSurroundingMins = *pMins;
  1018. m_vecSurroundingMaxs = *pMaxs;
  1019. }
  1020. ASSERT_COORD( m_vecSurroundingMins );
  1021. ASSERT_COORD( m_vecSurroundingMaxs );
  1022. }
  1023. }
  1024. //-----------------------------------------------------------------------------
  1025. // Marks the entity has having a dirty surrounding box
  1026. //-----------------------------------------------------------------------------
  1027. void CCollisionProperty::MarkSurroundingBoundsDirty()
  1028. {
  1029. GetOuter()->AddEFlags( EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS );
  1030. MarkPartitionHandleDirty();
  1031. #ifdef CLIENT_DLL
  1032. g_pClientShadowMgr->MarkRenderToTextureShadowDirty( GetOuter()->GetShadowHandle() );
  1033. #else
  1034. GetOuter()->NetworkProp()->MarkPVSInformationDirty();
  1035. #endif
  1036. }
  1037. //-----------------------------------------------------------------------------
  1038. // Does VPhysicsUpdate make us need to recompute the surrounding box?
  1039. //-----------------------------------------------------------------------------
  1040. bool CCollisionProperty::DoesVPhysicsInvalidateSurroundingBox( ) const
  1041. {
  1042. switch ( m_nSurroundType )
  1043. {
  1044. case USE_BEST_COLLISION_BOUNDS:
  1045. return true;
  1046. case USE_OBB_COLLISION_BOUNDS:
  1047. return (GetSolid() == SOLID_VPHYSICS) && (GetOuter()->GetMoveType() == MOVETYPE_VPHYSICS) && GetOuter()->VPhysicsGetObject();
  1048. // In the case of game code, we don't really know, so we have to assume it does
  1049. case USE_GAME_CODE:
  1050. return true;
  1051. case USE_COLLISION_BOUNDS_NEVER_VPHYSICS:
  1052. case USE_HITBOXES:
  1053. case USE_ROTATION_EXPANDED_BOUNDS:
  1054. case USE_SPECIFIED_BOUNDS:
  1055. return false;
  1056. default:
  1057. Assert(0);
  1058. return true;
  1059. }
  1060. }
  1061. //-----------------------------------------------------------------------------
  1062. // Computes the surrounding collision bounds based on whatever algorithm we want...
  1063. //-----------------------------------------------------------------------------
  1064. void CCollisionProperty::WorldSpaceSurroundingBounds( Vector *pVecMins, Vector *pVecMaxs )
  1065. {
  1066. const Vector &vecAbsOrigin = GetCollisionOrigin();
  1067. if ( GetOuter()->IsEFlagSet( EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS ))
  1068. {
  1069. GetOuter()->RemoveEFlags( EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS );
  1070. ComputeSurroundingBox( pVecMins, pVecMaxs );
  1071. VectorSubtract( *pVecMins, vecAbsOrigin, m_vecSurroundingMins );
  1072. VectorSubtract( *pVecMaxs, vecAbsOrigin, m_vecSurroundingMaxs );
  1073. ASSERT_COORD( m_vecSurroundingMins );
  1074. ASSERT_COORD( m_vecSurroundingMaxs );
  1075. }
  1076. else
  1077. {
  1078. VectorAdd( m_vecSurroundingMins, vecAbsOrigin, *pVecMins );
  1079. VectorAdd( m_vecSurroundingMaxs, vecAbsOrigin, *pVecMaxs );
  1080. }
  1081. }
  1082. //-----------------------------------------------------------------------------
  1083. // Spatial partition
  1084. //-----------------------------------------------------------------------------
  1085. void CCollisionProperty::CreatePartitionHandle()
  1086. {
  1087. // Put the entity into the spatial partition.
  1088. Assert( m_Partition == PARTITION_INVALID_HANDLE );
  1089. m_Partition = ::partition->CreateHandle( GetEntityHandle() );
  1090. }
  1091. void CCollisionProperty::DestroyPartitionHandle()
  1092. {
  1093. if ( m_Partition != PARTITION_INVALID_HANDLE )
  1094. {
  1095. ::partition->DestroyHandle( m_Partition );
  1096. m_Partition = PARTITION_INVALID_HANDLE;
  1097. }
  1098. }
  1099. //-----------------------------------------------------------------------------
  1100. // Updates the spatial partition
  1101. //-----------------------------------------------------------------------------
  1102. void CCollisionProperty::UpdateServerPartitionMask( )
  1103. {
  1104. #ifndef CLIENT_DLL
  1105. SpatialPartitionHandle_t handle = GetPartitionHandle();
  1106. if ( handle == PARTITION_INVALID_HANDLE )
  1107. return;
  1108. // Remove it from whatever lists it may be in at the moment
  1109. // We'll re-add it below if we need to.
  1110. ::partition->Remove( handle );
  1111. // Don't bother with deleted things
  1112. if ( !m_pOuter->edict() )
  1113. return;
  1114. // don't add the world
  1115. if ( m_pOuter->entindex() == 0 )
  1116. return;
  1117. // Make sure it's in the list of all entities
  1118. bool bIsSolid = IsSolid() || IsSolidFlagSet(FSOLID_TRIGGER);
  1119. if ( bIsSolid || m_pOuter->IsEFlagSet(EFL_USE_PARTITION_WHEN_NOT_SOLID) )
  1120. {
  1121. ::partition->Insert( PARTITION_ENGINE_NON_STATIC_EDICTS, handle );
  1122. }
  1123. if ( !bIsSolid )
  1124. return;
  1125. // Insert it into the appropriate lists.
  1126. // We have to continually reinsert it because its solid type may have changed
  1127. SpatialPartitionListMask_t mask = 0;
  1128. if ( !IsSolidFlagSet(FSOLID_NOT_SOLID) )
  1129. {
  1130. mask |= PARTITION_ENGINE_SOLID_EDICTS;
  1131. }
  1132. if ( IsSolidFlagSet(FSOLID_TRIGGER) )
  1133. {
  1134. mask |= PARTITION_ENGINE_TRIGGER_EDICTS;
  1135. }
  1136. Assert( mask != 0 );
  1137. ::partition->Insert( mask, handle );
  1138. #endif
  1139. }
  1140. //-----------------------------------------------------------------------------
  1141. // Marks the spatial partition dirty
  1142. //-----------------------------------------------------------------------------
  1143. void CCollisionProperty::MarkPartitionHandleDirty()
  1144. {
  1145. // don't bother with the world
  1146. if ( m_pOuter->entindex() == 0 )
  1147. return;
  1148. if ( !m_pOuter->IsEFlagSet( EFL_DIRTY_SPATIAL_PARTITION ) )
  1149. {
  1150. m_pOuter->AddEFlags( EFL_DIRTY_SPATIAL_PARTITION );
  1151. s_DirtyKDTree.AddEntity( m_pOuter );
  1152. }
  1153. #ifdef CLIENT_DLL
  1154. GetOuter()->MarkRenderHandleDirty();
  1155. g_pClientShadowMgr->AddToDirtyShadowList( GetOuter() );
  1156. #endif
  1157. }
  1158. //-----------------------------------------------------------------------------
  1159. // Updates the spatial partition
  1160. //-----------------------------------------------------------------------------
  1161. void CCollisionProperty::UpdatePartition( )
  1162. {
  1163. if ( m_pOuter->IsEFlagSet( EFL_DIRTY_SPATIAL_PARTITION ) )
  1164. {
  1165. m_pOuter->RemoveEFlags( EFL_DIRTY_SPATIAL_PARTITION );
  1166. #ifndef CLIENT_DLL
  1167. Assert( m_pOuter->entindex() != 0 );
  1168. // Don't bother with deleted things
  1169. if ( !m_pOuter->edict() )
  1170. return;
  1171. if ( GetPartitionHandle() == PARTITION_INVALID_HANDLE )
  1172. {
  1173. CreatePartitionHandle();
  1174. UpdateServerPartitionMask();
  1175. }
  1176. #else
  1177. if ( GetPartitionHandle() == PARTITION_INVALID_HANDLE )
  1178. return;
  1179. #endif
  1180. // We don't need to bother if it's not a trigger or solid
  1181. if ( IsSolid() || IsSolidFlagSet( FSOLID_TRIGGER ) || m_pOuter->IsEFlagSet( EFL_USE_PARTITION_WHEN_NOT_SOLID ) )
  1182. {
  1183. // Bloat a little bit...
  1184. if ( BoundingRadius() != 0.0f )
  1185. {
  1186. Vector vecSurroundMins, vecSurroundMaxs;
  1187. WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
  1188. vecSurroundMins -= Vector( 1, 1, 1 );
  1189. vecSurroundMaxs += Vector( 1, 1, 1 );
  1190. ::partition->ElementMoved( GetPartitionHandle(), vecSurroundMins, vecSurroundMaxs );
  1191. }
  1192. else
  1193. {
  1194. ::partition->ElementMoved( GetPartitionHandle(), GetCollisionOrigin(), GetCollisionOrigin() );
  1195. }
  1196. }
  1197. }
  1198. }