Counter Strike : Global Offensive Source Code
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.

1505 lines
49 KiB

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