From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/shared/collisionproperty.cpp | 1422 ++++++++++++++++++++++++++++++ 1 file changed, 1422 insertions(+) create mode 100644 mp/src/game/shared/collisionproperty.cpp (limited to 'mp/src/game/shared/collisionproperty.cpp') diff --git a/mp/src/game/shared/collisionproperty.cpp b/mp/src/game/shared/collisionproperty.cpp new file mode 100644 index 00000000..9def468e --- /dev/null +++ b/mp/src/game/shared/collisionproperty.cpp @@ -0,0 +1,1422 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "collisionproperty.h" +#include "igamesystem.h" +#include "utlvector.h" +#include "tier0/threadtools.h" +#include "tier0/tslist.h" + +#ifdef CLIENT_DLL + +#include "c_baseentity.h" +#include "c_baseanimating.h" +#include "recvproxy.h" + +#else + +#include "baseentity.h" +#include "baseanimating.h" +#include "sendproxy.h" +#include "hierarchy.h" +#endif + +#include "predictable_entity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// KD tree query callbacks +//----------------------------------------------------------------------------- +class CDirtySpatialPartitionEntityList : public CAutoGameSystem, public IPartitionQueryCallback +{ +public: + CDirtySpatialPartitionEntityList( char const *name ); + + // Members of IGameSystem + virtual bool Init(); + virtual void Shutdown(); + virtual void LevelShutdownPostEntity(); + + // Members of IPartitionQueryCallback + virtual void OnPreQuery_V1() { Assert( 0 ); } + virtual void OnPreQuery( SpatialPartitionListMask_t listMask ); + virtual void OnPostQuery( SpatialPartitionListMask_t listMask ); + + void AddEntity( CBaseEntity *pEntity ); + + ~CDirtySpatialPartitionEntityList(); + void LockPartitionForRead() + { + if ( m_readLockCount == 0 ) + { + m_partitionMutex.LockForRead(); + } + m_readLockCount++; + } + void UnlockPartitionForRead() + { + m_readLockCount--; + if ( m_readLockCount == 0 ) + { + m_partitionMutex.UnlockRead(); + } + } + + +private: + CTSListWithFreeList m_DirtyEntities; + CThreadSpinRWLock m_partitionMutex; + uint32 m_partitionWriteId; + CThreadLocalInt<> m_readLockCount; +}; + + +//----------------------------------------------------------------------------- +// Singleton instance +//----------------------------------------------------------------------------- +static CDirtySpatialPartitionEntityList s_DirtyKDTree( "CDirtySpatialPartitionEntityList" ); + + +//----------------------------------------------------------------------------- +// Force spatial partition updates (to avoid threading problems caused by lazy update) +//----------------------------------------------------------------------------- +void UpdateDirtySpatialPartitionEntities() +{ + SpatialPartitionListMask_t listMask; +#ifdef CLIENT_DLL + listMask = PARTITION_CLIENT_GAME_EDICTS; +#else + listMask = PARTITION_SERVER_GAME_EDICTS; +#endif + s_DirtyKDTree.OnPreQuery( listMask ); + s_DirtyKDTree.OnPostQuery( listMask ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CDirtySpatialPartitionEntityList::CDirtySpatialPartitionEntityList( char const *name ) : CAutoGameSystem( name ) +{ + m_DirtyEntities.Purge(); + m_readLockCount = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Deconstructor. +//----------------------------------------------------------------------------- +CDirtySpatialPartitionEntityList::~CDirtySpatialPartitionEntityList() +{ + m_DirtyEntities.Purge(); +} + +//----------------------------------------------------------------------------- +// Initialization, shutdown +//----------------------------------------------------------------------------- +bool CDirtySpatialPartitionEntityList::Init() +{ + partition->InstallQueryCallback( this ); + return true; +} + +void CDirtySpatialPartitionEntityList::Shutdown() +{ + partition->RemoveQueryCallback( this ); +} + + +//----------------------------------------------------------------------------- +// Makes sure all entries in the KD tree are in the correct position +//----------------------------------------------------------------------------- +void CDirtySpatialPartitionEntityList::AddEntity( CBaseEntity *pEntity ) +{ + m_DirtyEntities.PushItem( pEntity->GetRefEHandle() ); +} + + +//----------------------------------------------------------------------------- +// Members of IGameSystem +//----------------------------------------------------------------------------- +void CDirtySpatialPartitionEntityList::LevelShutdownPostEntity() +{ + m_DirtyEntities.RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Makes sure all entries in the KD tree are in the correct position +//----------------------------------------------------------------------------- +void CDirtySpatialPartitionEntityList::OnPreQuery( SpatialPartitionListMask_t listMask ) +{ +#ifdef CLIENT_DLL + const int validMask = PARTITION_CLIENT_GAME_EDICTS; +#else + const int validMask = PARTITION_SERVER_GAME_EDICTS; +#endif + + if ( !( listMask & validMask ) ) + return; + + if ( m_partitionWriteId != 0 && m_partitionWriteId == ThreadGetCurrentId() ) + return; + +#ifdef CLIENT_DLL + // FIXME: This should really be an assertion... feh! + if ( !C_BaseEntity::IsAbsRecomputationsEnabled() ) + { + LockPartitionForRead(); + return; + } +#endif + + // if you're holding a read lock, then these are entities that were still dirty after your trace started + // or became dirty due to some other thread or callback. Updating them may cause corruption further up the + // stack (e.g. partition iterator). Ignoring the state change should be safe since it happened after the + // trace was requested or was unable to be resolved in a previous attempt (still dirty). + if ( m_DirtyEntities.Count() && !m_readLockCount ) + { + CUtlVector< CBaseHandle > vecStillDirty; + m_partitionMutex.LockForWrite(); + m_partitionWriteId = ThreadGetCurrentId(); + CTSListWithFreeList::Node_t *pCurrent, *pNext; + while ( ( pCurrent = m_DirtyEntities.Detach() ) != NULL ) + { + while ( pCurrent ) + { + CBaseHandle handle = pCurrent->elem; + pNext = (CTSListWithFreeList::Node_t *)pCurrent->Next; + m_DirtyEntities.FreeNode( pCurrent ); + pCurrent = pNext; + +#ifndef CLIENT_DLL + CBaseEntity *pEntity = gEntList.GetBaseEntity( handle ); +#else + CBaseEntity *pEntity = cl_entitylist->GetBaseEntityFromHandle( handle ); +#endif + + if ( pEntity ) + { + // If an entity is in the middle of bone setup, don't call UpdatePartition + // which can cause it to redo bone setup on the same frame causing a recursive + // call to bone setup. + if ( !pEntity->IsEFlagSet( EFL_SETTING_UP_BONES ) ) + { + pEntity->CollisionProp()->UpdatePartition(); + } + else + { + vecStillDirty.AddToTail( handle ); + } + } + } + } + if ( vecStillDirty.Count() > 0 ) + { + for ( int i = 0; i < vecStillDirty.Count(); i++ ) + { + m_DirtyEntities.PushItem( vecStillDirty[i] ); + } + } + m_partitionWriteId = 0; + m_partitionMutex.UnlockWrite(); + } + LockPartitionForRead(); +} + +//----------------------------------------------------------------------------- +// Makes sure all entries in the KD tree are in the correct position +//----------------------------------------------------------------------------- +void CDirtySpatialPartitionEntityList::OnPostQuery( SpatialPartitionListMask_t listMask ) +{ +#ifdef CLIENT_DLL + if ( !( listMask & PARTITION_CLIENT_GAME_EDICTS ) ) + return; +#else + if ( !( listMask & PARTITION_SERVER_GAME_EDICTS ) ) + return; +#endif + + if ( m_partitionWriteId != 0 ) + return; + + UnlockPartitionForRead(); +} + + +//----------------------------------------------------------------------------- +// Save/load +//----------------------------------------------------------------------------- + +#ifndef CLIENT_DLL + + BEGIN_DATADESC_NO_BASE( CCollisionProperty ) + +// DEFINE_FIELD( m_pOuter, FIELD_CLASSPTR ), + DEFINE_GLOBAL_FIELD( m_vecMinsPreScaled, FIELD_VECTOR ), + DEFINE_GLOBAL_FIELD( m_vecMaxsPreScaled, FIELD_VECTOR ), + DEFINE_GLOBAL_FIELD( m_vecMins, FIELD_VECTOR ), + DEFINE_GLOBAL_FIELD( m_vecMaxs, FIELD_VECTOR ), + DEFINE_KEYFIELD( m_nSolidType, FIELD_CHARACTER, "solid" ), + DEFINE_FIELD( m_usSolidFlags, FIELD_SHORT ), + DEFINE_FIELD( m_nSurroundType, FIELD_CHARACTER ), + DEFINE_FIELD( m_flRadius, FIELD_FLOAT ), + DEFINE_FIELD( m_triggerBloat, FIELD_CHARACTER ), + DEFINE_FIELD( m_vecSpecifiedSurroundingMinsPreScaled, FIELD_VECTOR ), + DEFINE_FIELD( m_vecSpecifiedSurroundingMaxsPreScaled, FIELD_VECTOR ), + DEFINE_FIELD( m_vecSpecifiedSurroundingMins, FIELD_VECTOR ), + DEFINE_FIELD( m_vecSpecifiedSurroundingMaxs, FIELD_VECTOR ), + DEFINE_FIELD( m_vecSurroundingMins, FIELD_VECTOR ), + DEFINE_FIELD( m_vecSurroundingMaxs, FIELD_VECTOR ), +// DEFINE_FIELD( m_Partition, FIELD_SHORT ), +// DEFINE_PHYSPTR( m_pPhysicsObject ), + + END_DATADESC() + +#else + +//----------------------------------------------------------------------------- +// Prediction +//----------------------------------------------------------------------------- +BEGIN_PREDICTION_DATA_NO_BASE( CCollisionProperty ) + + DEFINE_PRED_FIELD( m_vecMinsPreScaled, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_vecMaxsPreScaled, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_vecMins, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_vecMaxs, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_nSolidType, FIELD_CHARACTER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_usSolidFlags, FIELD_SHORT, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_triggerBloat, FIELD_CHARACTER, FTYPEDESC_INSENDTABLE ), + +END_PREDICTION_DATA() + +#endif + +//----------------------------------------------------------------------------- +// Networking +//----------------------------------------------------------------------------- +#ifdef CLIENT_DLL + +static void RecvProxy_Solid( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + ((CCollisionProperty*)pStruct)->SetSolid( (SolidType_t)pData->m_Value.m_Int ); +} + +static void RecvProxy_SolidFlags( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + ((CCollisionProperty*)pStruct)->SetSolidFlags( pData->m_Value.m_Int ); +} + +static void RecvProxy_OBBMinsPreScaled( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + CCollisionProperty *pProp = ((CCollisionProperty*)pStruct); + Vector &vecMins = *((Vector*)pData->m_Value.m_Vector); + pProp->SetCollisionBounds( vecMins, pProp->OBBMaxsPreScaled() ); +} + +static void RecvProxy_OBBMaxsPreScaled( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + CCollisionProperty *pProp = ((CCollisionProperty*)pStruct); + Vector &vecMaxs = *((Vector*)pData->m_Value.m_Vector); + pProp->SetCollisionBounds( pProp->OBBMinsPreScaled(), vecMaxs ); +} + +static void RecvProxy_VectorDirtySurround( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + Vector &vecold = *((Vector*)pOut); + Vector vecnew( pData->m_Value.m_Vector[0], pData->m_Value.m_Vector[1], pData->m_Value.m_Vector[2] ); + + if ( vecold != vecnew ) + { + vecold = vecnew; + ((CCollisionProperty*)pStruct)->MarkSurroundingBoundsDirty(); + } +} + +static void RecvProxy_IntDirtySurround( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + if ( *((unsigned char*)pOut) != pData->m_Value.m_Int ) + { + *((unsigned char*)pOut) = pData->m_Value.m_Int; + ((CCollisionProperty*)pStruct)->MarkSurroundingBoundsDirty(); + } +} + +#else + +static void SendProxy_Solid( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ) +{ + pOut->m_Int = ((CCollisionProperty*)pStruct)->GetSolid(); +} + +static void SendProxy_SolidFlags( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ) +{ + pOut->m_Int = ((CCollisionProperty*)pStruct)->GetSolidFlags(); +} + +#endif + +BEGIN_NETWORK_TABLE_NOBASE( CCollisionProperty, DT_CollisionProperty ) + +#ifdef CLIENT_DLL + RecvPropVector( RECVINFO(m_vecMinsPreScaled), 0, RecvProxy_OBBMinsPreScaled ), + RecvPropVector( RECVINFO(m_vecMaxsPreScaled), 0, RecvProxy_OBBMaxsPreScaled ), + RecvPropVector( RECVINFO(m_vecMins), 0 ), + RecvPropVector( RECVINFO(m_vecMaxs), 0 ), + RecvPropInt( RECVINFO( m_nSolidType ), 0, RecvProxy_Solid ), + RecvPropInt( RECVINFO( m_usSolidFlags ), 0, RecvProxy_SolidFlags ), + RecvPropInt( RECVINFO(m_nSurroundType), 0, RecvProxy_IntDirtySurround ), + RecvPropInt( RECVINFO(m_triggerBloat), 0, RecvProxy_IntDirtySurround ), + RecvPropVector( RECVINFO(m_vecSpecifiedSurroundingMinsPreScaled), 0, RecvProxy_VectorDirtySurround ), + RecvPropVector( RECVINFO(m_vecSpecifiedSurroundingMaxsPreScaled), 0, RecvProxy_VectorDirtySurround ), + RecvPropVector( RECVINFO(m_vecSpecifiedSurroundingMins), 0, RecvProxy_VectorDirtySurround ), + RecvPropVector( RECVINFO(m_vecSpecifiedSurroundingMaxs), 0, RecvProxy_VectorDirtySurround ), +#else + SendPropVector( SENDINFO(m_vecMinsPreScaled), 0, SPROP_NOSCALE), + SendPropVector( SENDINFO(m_vecMaxsPreScaled), 0, SPROP_NOSCALE), + SendPropVector( SENDINFO(m_vecMins), 0, SPROP_NOSCALE), + SendPropVector( SENDINFO(m_vecMaxs), 0, SPROP_NOSCALE), + SendPropInt( SENDINFO( m_nSolidType ), 3, SPROP_UNSIGNED, SendProxy_Solid ), + SendPropInt( SENDINFO( m_usSolidFlags ), FSOLID_MAX_BITS, SPROP_UNSIGNED, SendProxy_SolidFlags ), + SendPropInt( SENDINFO( m_nSurroundType ), SURROUNDING_TYPE_BIT_COUNT, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_triggerBloat), 0, SPROP_UNSIGNED), + SendPropVector( SENDINFO(m_vecSpecifiedSurroundingMinsPreScaled), 0, SPROP_NOSCALE), + SendPropVector( SENDINFO(m_vecSpecifiedSurroundingMaxsPreScaled), 0, SPROP_NOSCALE), + SendPropVector( SENDINFO(m_vecSpecifiedSurroundingMins), 0, SPROP_NOSCALE), + SendPropVector( SENDINFO(m_vecSpecifiedSurroundingMaxs), 0, SPROP_NOSCALE), +#endif + +END_NETWORK_TABLE() + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CCollisionProperty::CCollisionProperty() +{ + m_Partition = PARTITION_INVALID_HANDLE; + Init( NULL ); +} + +CCollisionProperty::~CCollisionProperty() +{ + DestroyPartitionHandle(); +} + + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +void CCollisionProperty::Init( CBaseEntity *pEntity ) +{ + m_pOuter = pEntity; + m_vecMinsPreScaled.GetForModify().Init(); + m_vecMaxsPreScaled.GetForModify().Init(); + m_vecMins.GetForModify().Init(); + m_vecMaxs.GetForModify().Init(); + m_flRadius = 0.0f; + m_triggerBloat = 0; + m_usSolidFlags = 0; + m_nSolidType = SOLID_NONE; + + // NOTE: This replicates previous behavior; we may always want to use BEST_COLLISION_BOUNDS + m_nSurroundType = USE_OBB_COLLISION_BOUNDS; + m_vecSurroundingMins = vec3_origin; + m_vecSurroundingMaxs = vec3_origin; + m_vecSpecifiedSurroundingMinsPreScaled.GetForModify().Init(); + m_vecSpecifiedSurroundingMaxsPreScaled.GetForModify().Init(); + m_vecSpecifiedSurroundingMins.GetForModify().Init(); + m_vecSpecifiedSurroundingMaxs.GetForModify().Init(); +} + + +//----------------------------------------------------------------------------- +// EntityHandle +//----------------------------------------------------------------------------- +IHandleEntity *CCollisionProperty::GetEntityHandle() +{ + return m_pOuter; +} + + +//----------------------------------------------------------------------------- +// Collision group +//----------------------------------------------------------------------------- +int CCollisionProperty::GetCollisionGroup() const +{ + return m_pOuter->GetCollisionGroup(); +} + + +bool CCollisionProperty::ShouldTouchTrigger( int triggerSolidFlags ) const +{ + // debris only touches certain triggers + if ( GetCollisionGroup() == COLLISION_GROUP_DEBRIS ) + { + if ( triggerSolidFlags & FSOLID_TRIGGER_TOUCH_DEBRIS ) + return true; + + return false; + } + + // triggers don't touch other triggers (might be solid to other ents as well as trigger) + if ( IsSolidFlagSet( FSOLID_TRIGGER ) ) + return false; + + return true; +} + +const matrix3x4_t *CCollisionProperty::GetRootParentToWorldTransform() const +{ + if ( IsSolidFlagSet( FSOLID_ROOT_PARENT_ALIGNED ) ) + { + CBaseEntity *pEntity = m_pOuter->GetRootMoveParent(); + Assert(pEntity); + if ( pEntity ) + { + return &pEntity->CollisionProp()->CollisionToWorldTransform(); + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// IClientUnknown +//----------------------------------------------------------------------------- +IClientUnknown* CCollisionProperty::GetIClientUnknown() +{ +#ifdef CLIENT_DLL + return m_pOuter->GetIClientUnknown(); +#else + return NULL; +#endif +} + + + +//----------------------------------------------------------------------------- +// Check for untouch +//----------------------------------------------------------------------------- +void CCollisionProperty::CheckForUntouch() +{ +#ifndef CLIENT_DLL + if ( !IsSolid() && !IsSolidFlagSet(FSOLID_TRIGGER)) + { + // If this ent's touch list isn't empty, it's transitioning to not solid + if ( m_pOuter->IsCurrentlyTouching() ) + { + // mark ent so that at the end of frame it will check to + // see if it's no longer touching ents + m_pOuter->SetCheckUntouch( true ); + } + } +#endif +} + + +//----------------------------------------------------------------------------- +// Sets the solid type +//----------------------------------------------------------------------------- +void CCollisionProperty::SetSolid( SolidType_t val ) +{ + if ( m_nSolidType == val ) + return; + +#ifndef CLIENT_DLL + bool bWasNotSolid = IsSolid(); +#endif + + MarkSurroundingBoundsDirty(); + + // OBB is not yet implemented + if ( val == SOLID_BSP ) + { + if ( GetOuter()->GetMoveParent() ) + { + if ( GetOuter()->GetRootMoveParent()->GetSolid() != SOLID_BSP ) + { + // must be SOLID_VPHYSICS because parent might rotate + val = SOLID_VPHYSICS; + } + } +#ifndef CLIENT_DLL + // UNDONE: This should be fine in the client DLL too. Move GetAllChildren() into shared code. + // If the root of the hierarchy is SOLID_BSP, then assume that the designer + // wants the collisions to rotate with this hierarchy so that the player can + // move while riding the hierarchy. + if ( !GetOuter()->GetMoveParent() ) + { + // NOTE: This assumes things don't change back from SOLID_BSP + // NOTE: This is 100% true for HL2 - need to support removing the flag to support changing from SOLID_BSP + CUtlVector list; + GetAllChildren( GetOuter(), list ); + for ( int i = list.Count()-1; i>=0; --i ) + { + list[i]->AddSolidFlags( FSOLID_ROOT_PARENT_ALIGNED ); + } + } +#endif + } + + m_nSolidType = val; + +#ifndef CLIENT_DLL + m_pOuter->CollisionRulesChanged(); + + UpdateServerPartitionMask( ); + + if ( bWasNotSolid != IsSolid() ) + { + CheckForUntouch(); + } +#endif +} + +SolidType_t CCollisionProperty::GetSolid() const +{ + return (SolidType_t)m_nSolidType.Get(); +} + + +//----------------------------------------------------------------------------- +// Sets the solid flags +//----------------------------------------------------------------------------- +void CCollisionProperty::SetSolidFlags( int flags ) +{ + int oldFlags = m_usSolidFlags; + m_usSolidFlags = (unsigned short)(flags & 0xFFFF); + if ( oldFlags == m_usSolidFlags ) + return; + + // These two flags, if changed, can produce different surrounding bounds + if ( (oldFlags & (FSOLID_FORCE_WORLD_ALIGNED | FSOLID_USE_TRIGGER_BOUNDS)) != + (m_usSolidFlags & (FSOLID_FORCE_WORLD_ALIGNED | FSOLID_USE_TRIGGER_BOUNDS)) ) + { + MarkSurroundingBoundsDirty(); + } + + if ( (oldFlags & (FSOLID_NOT_SOLID|FSOLID_TRIGGER)) != (m_usSolidFlags & (FSOLID_NOT_SOLID|FSOLID_TRIGGER)) ) + { + m_pOuter->CollisionRulesChanged(); + } + +#ifndef CLIENT_DLL + if ( (oldFlags & (FSOLID_NOT_SOLID | FSOLID_TRIGGER)) != (m_usSolidFlags & (FSOLID_NOT_SOLID | FSOLID_TRIGGER)) ) + { + UpdateServerPartitionMask( ); + CheckForUntouch(); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Coordinate system of the collision model +//----------------------------------------------------------------------------- +const Vector& CCollisionProperty::GetCollisionOrigin() const +{ + return m_pOuter->GetAbsOrigin(); +} + +const QAngle& CCollisionProperty::GetCollisionAngles() const +{ + if ( IsBoundsDefinedInEntitySpace() ) + { + return m_pOuter->GetAbsAngles(); + } + + return vec3_angle; +} + +const matrix3x4_t& CCollisionProperty::CollisionToWorldTransform() const +{ + static matrix3x4_t s_matTemp[4]; + static int s_nIndex = 0; + + matrix3x4_t &matResult = s_matTemp[s_nIndex]; + s_nIndex = (s_nIndex+1) & 0x3; + + if ( IsBoundsDefinedInEntitySpace() ) + { + return m_pOuter->EntityToWorldTransform(); + } + + SetIdentityMatrix( matResult ); + MatrixSetColumn( GetCollisionOrigin(), 3, matResult ); + return matResult; +} + + +//----------------------------------------------------------------------------- +// Sets the collision bounds + the size +//----------------------------------------------------------------------------- +void CCollisionProperty::SetCollisionBounds( const Vector &mins, const Vector &maxs ) +{ + if ( ( m_vecMinsPreScaled != mins ) || ( m_vecMaxsPreScaled != maxs ) ) + { + m_vecMinsPreScaled = mins; + m_vecMaxsPreScaled = maxs; + } + + bool bDirty = false; + + // Check if it's a scaled model + CBaseAnimating *pAnim = GetOuter()->GetBaseAnimating(); + if ( pAnim && pAnim->GetModelScale() != 1.0f ) + { + // Do the scaling + Vector vecNewMins = mins * pAnim->GetModelScale(); + Vector vecNewMaxs = maxs * pAnim->GetModelScale(); + + if ( ( m_vecMins != vecNewMins ) || ( m_vecMaxs != vecNewMaxs ) ) + { + m_vecMins = vecNewMins; + m_vecMaxs = vecNewMaxs; + bDirty = true; + } + } + else + { + // No scaling needed! + if ( ( m_vecMins != mins ) || ( m_vecMaxs != maxs ) ) + { + m_vecMins = mins; + m_vecMaxs = maxs; + bDirty = true; + } + } + + if ( bDirty ) + { + //ASSERT_COORD( m_vecMins.Get() ); + //ASSERT_COORD( m_vecMaxs.Get() ); + + Vector vecSize; + VectorSubtract( m_vecMaxs, m_vecMins, vecSize ); + m_flRadius = vecSize.Length() * 0.5f; + + MarkSurroundingBoundsDirty(); + } +} + +//----------------------------------------------------------------------------- +// Rebuilds the scaled bounds from the prescaled bounds after a model's scale has changed +//----------------------------------------------------------------------------- +void CCollisionProperty::RefreshScaledCollisionBounds( void ) +{ + SetCollisionBounds( m_vecMinsPreScaled, m_vecMaxsPreScaled ); + + SurroundingBoundsType_t nSurroundType = static_cast< SurroundingBoundsType_t >( m_nSurroundType.Get() ); + if ( nSurroundType == USE_SPECIFIED_BOUNDS ) + { + SetSurroundingBoundsType( nSurroundType, + &(m_vecSpecifiedSurroundingMinsPreScaled.Get()), + &(m_vecSpecifiedSurroundingMaxsPreScaled.Get()) ); + } + else + { + SetSurroundingBoundsType( nSurroundType ); + } +} + + +//----------------------------------------------------------------------------- +// Lazily calculates the 2D bounding radius. If we do this enough, we should +// calculate this in SetCollisionBounds above and cache the results in a data member! +//----------------------------------------------------------------------------- +float CCollisionProperty::BoundingRadius2D() const +{ + Vector vecSize; + VectorSubtract( m_vecMaxs, m_vecMins, vecSize ); + + vecSize.z = 0; + return vecSize.Length() * 0.5f; +} + + +//----------------------------------------------------------------------------- +// Special trigger representation (OBB) +//----------------------------------------------------------------------------- +void CCollisionProperty::WorldSpaceTriggerBounds( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) const +{ + WorldSpaceAABB( pVecWorldMins, pVecWorldMaxs ); + if ( ( GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS ) == 0 ) + return; + + // Don't bloat below, we don't want to trigger it with our heads + pVecWorldMins->x -= m_triggerBloat; + pVecWorldMins->y -= m_triggerBloat; + + pVecWorldMaxs->x += m_triggerBloat; + pVecWorldMaxs->y += m_triggerBloat; + pVecWorldMaxs->z += (float)m_triggerBloat * 0.5f; +} + +void CCollisionProperty::UseTriggerBounds( bool bEnable, float flBloat ) +{ + Assert( flBloat <= 127.0f ); + m_triggerBloat = (char )flBloat; + if ( bEnable ) + { + AddSolidFlags( FSOLID_USE_TRIGGER_BOUNDS ); + Assert( flBloat > 0.0f ); + } + else + { + RemoveSolidFlags( FSOLID_USE_TRIGGER_BOUNDS ); + } +} + + +//----------------------------------------------------------------------------- +// Collision model (BSP) +//----------------------------------------------------------------------------- +int CCollisionProperty::GetCollisionModelIndex() +{ + return m_pOuter->GetModelIndex(); +} + +const model_t* CCollisionProperty::GetCollisionModel() +{ + return m_pOuter->GetModel(); +} + + +//----------------------------------------------------------------------------- +// Collision methods implemented in the entity +// FIXME: This shouldn't happen there!! +//----------------------------------------------------------------------------- +bool CCollisionProperty::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) +{ + return m_pOuter->TestCollision( ray, fContentsMask, tr ); +} + +bool CCollisionProperty::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) +{ + return m_pOuter->TestHitboxes( ray, fContentsMask, tr ); +} + + +//----------------------------------------------------------------------------- +// Computes a "normalized" point (range 0,0,0 - 1,1,1) in collision space +//----------------------------------------------------------------------------- +const Vector & CCollisionProperty::NormalizedToCollisionSpace( const Vector &in, Vector *pResult ) const +{ + pResult->x = Lerp( in.x, m_vecMins.Get().x, m_vecMaxs.Get().x ); + pResult->y = Lerp( in.y, m_vecMins.Get().y, m_vecMaxs.Get().y ); + pResult->z = Lerp( in.z, m_vecMins.Get().z, m_vecMaxs.Get().z ); + return *pResult; +} + + +//----------------------------------------------------------------------------- +// Transforms a point in collision space to normalized space +//----------------------------------------------------------------------------- +const Vector & CCollisionProperty::CollisionToNormalizedSpace( const Vector &in, Vector *pResult ) const +{ + Vector vecSize = OBBSize( ); + pResult->x = ( vecSize.x != 0.0f ) ? ( in.x - m_vecMins.Get().x ) / vecSize.x : 0.5f; + pResult->y = ( vecSize.y != 0.0f ) ? ( in.y - m_vecMins.Get().y ) / vecSize.y : 0.5f; + pResult->z = ( vecSize.z != 0.0f ) ? ( in.z - m_vecMins.Get().z ) / vecSize.z : 0.5f; + return *pResult; +} + + +//----------------------------------------------------------------------------- +// Computes a "normalized" point (range 0,0,0 - 1,1,1) in world space +//----------------------------------------------------------------------------- +const Vector & CCollisionProperty::NormalizedToWorldSpace( const Vector &in, Vector *pResult ) const +{ + Vector vecCollisionSpace; + NormalizedToCollisionSpace( in, &vecCollisionSpace ); + CollisionToWorldSpace( vecCollisionSpace, pResult ); + return *pResult; +} + + +//----------------------------------------------------------------------------- +// Transforms a point in world space to normalized space +//----------------------------------------------------------------------------- +const Vector & CCollisionProperty::WorldToNormalizedSpace( const Vector &in, Vector *pResult ) const +{ + Vector vecCollisionSpace; + WorldToCollisionSpace( in, &vecCollisionSpace ); + CollisionToNormalizedSpace( vecCollisionSpace, pResult ); + return *pResult; +} + + +//----------------------------------------------------------------------------- +// Selects a random point in the bounds given the normalized 0-1 bounds +//----------------------------------------------------------------------------- +void CCollisionProperty::RandomPointInBounds( const Vector &vecNormalizedMins, const Vector &vecNormalizedMaxs, Vector *pPoint) const +{ + Vector vecNormalizedSpace; + vecNormalizedSpace.x = random->RandomFloat( vecNormalizedMins.x, vecNormalizedMaxs.x ); + vecNormalizedSpace.y = random->RandomFloat( vecNormalizedMins.y, vecNormalizedMaxs.y ); + vecNormalizedSpace.z = random->RandomFloat( vecNormalizedMins.z, vecNormalizedMaxs.z ); + NormalizedToWorldSpace( vecNormalizedSpace, pPoint ); +} + + +//----------------------------------------------------------------------------- +// Transforms an AABB measured in entity space to a box that surrounds it in world space +//----------------------------------------------------------------------------- +void CCollisionProperty::CollisionAABBToWorldAABB( const Vector &entityMins, + const Vector &entityMaxs, Vector *pWorldMins, Vector *pWorldMaxs ) const +{ + if ( !IsBoundsDefinedInEntitySpace() || (GetCollisionAngles() == vec3_angle) ) + { + VectorAdd( entityMins, GetCollisionOrigin(), *pWorldMins ); + VectorAdd( entityMaxs, GetCollisionOrigin(), *pWorldMaxs ); + } + else + { + TransformAABB( CollisionToWorldTransform(), entityMins, entityMaxs, *pWorldMins, *pWorldMaxs ); + } +} + +/* +void CCollisionProperty::WorldAABBToCollisionAABB( const Vector &worldMins, const Vector &worldMaxs, Vector *pEntityMins, Vector *pEntityMaxs ) const +{ + if ( !IsBoundsDefinedInEntitySpace() || (GetCollisionAngles() == vec3_angle) ) + { + VectorSubtract( worldMins, GetAbsOrigin(), *pEntityMins ); + VectorSubtract( worldMaxs, GetAbsOrigin(), *pEntityMaxs ); + } + else + { + ITransformAABB( CollisionToWorldTransform(), worldMins, worldMaxs, *pEntityMins, *pEntityMaxs ); + } +} +*/ + + +//----------------------------------------------------------------------------- +// Is a worldspace point within the bounds of the OBB? +//----------------------------------------------------------------------------- +bool CCollisionProperty::IsPointInBounds( const Vector &vecWorldPt ) const +{ + Vector vecLocalSpace; + WorldToCollisionSpace( vecWorldPt, &vecLocalSpace ); + return ( ( vecLocalSpace.x >= m_vecMins.Get().x && vecLocalSpace.x <= m_vecMaxs.Get().x ) && + ( vecLocalSpace.y >= m_vecMins.Get().y && vecLocalSpace.y <= m_vecMaxs.Get().y ) && + ( vecLocalSpace.z >= m_vecMins.Get().z && vecLocalSpace.z <= m_vecMaxs.Get().z ) ); +} + + +//----------------------------------------------------------------------------- +// Computes the nearest point in the OBB to a point specified in world space +//----------------------------------------------------------------------------- +void CCollisionProperty::CalcNearestPoint( const Vector &vecWorldPt, Vector *pVecNearestWorldPt ) const +{ + // Calculate physics force + Vector localPt, localClosestPt; + WorldToCollisionSpace( vecWorldPt, &localPt ); + CalcClosestPointOnAABB( m_vecMins.Get(), m_vecMaxs.Get(), localPt, localClosestPt ); + CollisionToWorldSpace( localClosestPt, pVecNearestWorldPt ); +} + + +//----------------------------------------------------------------------------- +// Computes the nearest point in the OBB to a point specified in world space +//----------------------------------------------------------------------------- +float CCollisionProperty::CalcDistanceFromPoint( const Vector &vecWorldPt ) const +{ + // Calculate physics force + Vector localPt, localClosestPt; + WorldToCollisionSpace( vecWorldPt, &localPt ); + CalcClosestPointOnAABB( m_vecMins.Get(), m_vecMaxs.Get(), localPt, localClosestPt ); + return localPt.DistTo( localClosestPt ); +} + + +//----------------------------------------------------------------------------- +// Compute the largest dot product of the OBB and the specified direction vector +//----------------------------------------------------------------------------- +float CCollisionProperty::ComputeSupportMap( const Vector &vecDirection ) const +{ + Vector vecCollisionDir; + WorldDirectionToCollisionSpace( vecDirection, &vecCollisionDir ); + + float flResult = DotProduct( GetCollisionOrigin(), vecDirection ); + flResult += (( vecCollisionDir.x >= 0.0f ) ? m_vecMaxs.Get().x : m_vecMins.Get().x) * vecCollisionDir.x; + flResult += (( vecCollisionDir.y >= 0.0f ) ? m_vecMaxs.Get().y : m_vecMins.Get().y) * vecCollisionDir.y; + flResult += (( vecCollisionDir.z >= 0.0f ) ? m_vecMaxs.Get().z : m_vecMins.Get().z) * vecCollisionDir.z; + + return flResult; +} + + +//----------------------------------------------------------------------------- +// Expand trigger bounds.. +//----------------------------------------------------------------------------- +void CCollisionProperty::ComputeVPhysicsSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) +{ + bool bSetBounds = false; + IPhysicsObject *pPhysicsObject = GetOuter()->VPhysicsGetObject(); + if ( pPhysicsObject ) + { + if ( pPhysicsObject->GetCollide() ) + { + physcollision->CollideGetAABB( pVecWorldMins, pVecWorldMaxs, + pPhysicsObject->GetCollide(), GetCollisionOrigin(), GetCollisionAngles() ); + bSetBounds = true; + } + else if ( pPhysicsObject->GetSphereRadius( ) ) + { + float flRadius = pPhysicsObject->GetSphereRadius( ); + Vector vecExtents( flRadius, flRadius, flRadius ); + VectorSubtract( GetCollisionOrigin(), vecExtents, *pVecWorldMins ); + VectorAdd( GetCollisionOrigin(), vecExtents, *pVecWorldMaxs ); + bSetBounds = true; + } + } + + if ( !bSetBounds ) + { + *pVecWorldMins = GetCollisionOrigin(); + *pVecWorldMaxs = *pVecWorldMins; + } + + // Also, lets expand for the trigger bounds also + if ( IsSolidFlagSet( FSOLID_USE_TRIGGER_BOUNDS ) ) + { + Vector vecWorldTriggerMins, vecWorldTriggerMaxs; + WorldSpaceTriggerBounds( &vecWorldTriggerMins, &vecWorldTriggerMaxs ); + VectorMin( vecWorldTriggerMins, *pVecWorldMins, *pVecWorldMins ); + VectorMax( vecWorldTriggerMaxs, *pVecWorldMaxs, *pVecWorldMaxs ); + } +} + + +//----------------------------------------------------------------------------- +// Expand trigger bounds.. +//----------------------------------------------------------------------------- +bool CCollisionProperty::ComputeHitboxSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) +{ + CBaseAnimating *pAnim = GetOuter()->GetBaseAnimating(); + if (pAnim) + { + return pAnim->ComputeHitboxSurroundingBox( pVecWorldMins, pVecWorldMaxs ); + } + + return false; +} + +//----------------------------------------------------------------------------- +// Expand trigger bounds.. +//----------------------------------------------------------------------------- +bool CCollisionProperty::ComputeEntitySpaceHitboxSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) +{ + CBaseAnimating *pAnim = GetOuter()->GetBaseAnimating(); + if (pAnim) + { + return pAnim->ComputeEntitySpaceHitboxSurroundingBox( pVecWorldMins, pVecWorldMaxs ); + } + + return false; +} + +//----------------------------------------------------------------------------- +// Computes the surrounding collision bounds from the the OBB (not vphysics) +//----------------------------------------------------------------------------- +void CCollisionProperty::ComputeRotationExpandedBounds( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) +{ + if ( !IsBoundsDefinedInEntitySpace() ) + { + *pVecWorldMins = m_vecMins; + *pVecWorldMaxs = m_vecMaxs; + } + else + { + float flMaxVal; + flMaxVal = MAX( FloatMakePositive(m_vecMins.Get().x), FloatMakePositive(m_vecMaxs.Get().x) ); + pVecWorldMins->x = -flMaxVal; + pVecWorldMaxs->x = flMaxVal; + + flMaxVal = MAX( FloatMakePositive(m_vecMins.Get().y), FloatMakePositive(m_vecMaxs.Get().y) ); + pVecWorldMins->y = -flMaxVal; + pVecWorldMaxs->y = flMaxVal; + + flMaxVal = MAX( FloatMakePositive(m_vecMins.Get().z), FloatMakePositive(m_vecMaxs.Get().z) ); + pVecWorldMins->z = -flMaxVal; + pVecWorldMaxs->z = flMaxVal; + } +} + + +//----------------------------------------------------------------------------- +// Computes the surrounding collision bounds based on whatever algorithm we want... +//----------------------------------------------------------------------------- +void CCollisionProperty::ComputeCollisionSurroundingBox( bool bUseVPhysics, Vector *pVecWorldMins, Vector *pVecWorldMaxs ) +{ + Assert( GetSolid() != SOLID_CUSTOM ); + + // NOTE: For solid none, we are still going to use the bounds; necessary because + // the surrounding box is used for the PVS... + // FIXME: Should we make some other call for the PVS stuff?? If so, we should return + // a point bounds for SOLID_NONE... +// if ( GetSolid() == SOLID_NONE ) +// { +// *pVecWorldMins = GetCollisionOrigin(); +// *pVecWorldMaxs = *pVecWorldMins; +// return; +// } + + if ( bUseVPhysics ) + { + ComputeVPhysicsSurroundingBox( pVecWorldMins, pVecWorldMaxs ); + } + else + { + // Will expand the bounds for the trigger, if it is a trigger + WorldSpaceTriggerBounds( pVecWorldMins, pVecWorldMaxs ); + } +} + + +//----------------------------------------------------------------------------- +// Computes the surrounding collision bounds based on whatever algorithm we want... +//----------------------------------------------------------------------------- +void CCollisionProperty::ComputeSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) +{ + if (( GetSolid() == SOLID_CUSTOM ) && (m_nSurroundType != USE_GAME_CODE )) + { + // NOTE: This can only happen in transition periods, say during network + // reception on the client. We expect USE_GAME_CODE to be used with SOLID_CUSTOM + *pVecWorldMins = GetCollisionOrigin(); + *pVecWorldMaxs = *pVecWorldMins; + return; + } + + switch( m_nSurroundType ) + { + case USE_OBB_COLLISION_BOUNDS: + { + Assert( GetSolid() != SOLID_CUSTOM ); + bool bUseVPhysics = false; + if ( ( GetSolid() == SOLID_VPHYSICS ) && ( GetOuter()->GetMoveType() == MOVETYPE_VPHYSICS ) ) + { + // UNDONE: This may not be necessary any more. + IPhysicsObject *pPhysics = GetOuter()->VPhysicsGetObject(); + bUseVPhysics = pPhysics && pPhysics->IsAsleep(); + } + ComputeCollisionSurroundingBox( bUseVPhysics, pVecWorldMins, pVecWorldMaxs ); + } + break; + + case USE_BEST_COLLISION_BOUNDS: + Assert( GetSolid() != SOLID_CUSTOM ); + ComputeCollisionSurroundingBox( (GetSolid() == SOLID_VPHYSICS), pVecWorldMins, pVecWorldMaxs ); + break; + + case USE_COLLISION_BOUNDS_NEVER_VPHYSICS: + Assert( GetSolid() != SOLID_CUSTOM ); + ComputeCollisionSurroundingBox( false, pVecWorldMins, pVecWorldMaxs ); + break; + + case USE_HITBOXES: + ComputeHitboxSurroundingBox( pVecWorldMins, pVecWorldMaxs ); + break; + + case USE_ROTATION_EXPANDED_BOUNDS: + ComputeRotationExpandedBounds( pVecWorldMins, pVecWorldMaxs ); + break; + + case USE_SPECIFIED_BOUNDS: + VectorAdd( GetCollisionOrigin(), m_vecSpecifiedSurroundingMins, *pVecWorldMins ); + VectorAdd( GetCollisionOrigin(), m_vecSpecifiedSurroundingMaxs, *pVecWorldMaxs ); + break; + + case USE_GAME_CODE: + GetOuter()->ComputeWorldSpaceSurroundingBox( pVecWorldMins, pVecWorldMaxs ); + Assert( pVecWorldMins->x <= pVecWorldMaxs->x ); + Assert( pVecWorldMins->y <= pVecWorldMaxs->y ); + Assert( pVecWorldMins->z <= pVecWorldMaxs->z ); + return; + } + +#ifdef DEBUG + /* + // For debugging purposes, make sure the bounds actually does surround the thing. + // Otherwise the optimization we were using isn't really all that great, is it? + Vector vecTestMins, vecTestMaxs; + ComputeCollisionSurroundingBox( (GetSolid() == SOLID_VPHYSICS), &vecTestMins, &vecTestMaxs ); + + // Now that we have the basics, let's expand for hitboxes if appropriate + Vector vecWorldHitboxMins, vecWorldHitboxMaxs; + if ( ComputeHitboxSurroundingBox( &vecWorldHitboxMins, &vecWorldHitboxMaxs ) ) + { + VectorMin( vecWorldHitboxMaxs, vecTestMins, vecTestMins ); + VectorMax( vecWorldHitboxMaxs, vecTestMaxs, vecTestMaxs ); + } + + Assert( vecTestMins.x >= pVecWorldMins->x && vecTestMins.y >= pVecWorldMins->y && vecTestMins.z >= pVecWorldMins->z ); + Assert( vecTestMaxs.x <= pVecWorldMaxs->x && vecTestMaxs.y <= pVecWorldMaxs->y && vecTestMaxs.z <= pVecWorldMaxs->z ); + */ +#endif +} + + +//----------------------------------------------------------------------------- +// Sets the method by which the surrounding collision bounds is set +//----------------------------------------------------------------------------- +void CCollisionProperty::SetSurroundingBoundsType( SurroundingBoundsType_t type, const Vector *pMins, const Vector *pMaxs ) +{ + m_nSurroundType = type; + if (type != USE_SPECIFIED_BOUNDS) + { + Assert( !pMins && !pMaxs ); + MarkSurroundingBoundsDirty(); + } + else + { + Assert( pMins && pMaxs ); + m_vecSpecifiedSurroundingMinsPreScaled = *pMins; + m_vecSpecifiedSurroundingMaxsPreScaled = *pMaxs; + + // Check if it's a scaled model + CBaseAnimating *pAnim = GetOuter()->GetBaseAnimating(); + if ( pAnim && pAnim->GetModelScale() != 1.0f ) + { + // Do the scaling + Vector vecNewMins = *pMins * pAnim->GetModelScale(); + Vector vecNewMaxs = *pMaxs * pAnim->GetModelScale(); + + m_vecSpecifiedSurroundingMins = vecNewMins; + m_vecSpecifiedSurroundingMaxs = vecNewMaxs; + m_vecSurroundingMins = vecNewMins; + m_vecSurroundingMaxs = vecNewMaxs; + + } + else + { + // No scaling needed! + m_vecSpecifiedSurroundingMins = *pMins; + m_vecSpecifiedSurroundingMaxs = *pMaxs; + m_vecSurroundingMins = *pMins; + m_vecSurroundingMaxs = *pMaxs; + + } + + ASSERT_COORD( m_vecSurroundingMins ); + ASSERT_COORD( m_vecSurroundingMaxs ); + } +} + + +//----------------------------------------------------------------------------- +// Marks the entity has having a dirty surrounding box +//----------------------------------------------------------------------------- +void CCollisionProperty::MarkSurroundingBoundsDirty() +{ + GetOuter()->AddEFlags( EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS ); + MarkPartitionHandleDirty(); + +#ifdef CLIENT_DLL + g_pClientShadowMgr->MarkRenderToTextureShadowDirty( GetOuter()->GetShadowHandle() ); +#else + GetOuter()->NetworkProp()->MarkPVSInformationDirty(); +#endif +} + + +//----------------------------------------------------------------------------- +// Does VPhysicsUpdate make us need to recompute the surrounding box? +//----------------------------------------------------------------------------- +bool CCollisionProperty::DoesVPhysicsInvalidateSurroundingBox( ) const +{ + switch ( m_nSurroundType ) + { + case USE_BEST_COLLISION_BOUNDS: + return true; + + case USE_OBB_COLLISION_BOUNDS: + return (GetSolid() == SOLID_VPHYSICS) && (GetOuter()->GetMoveType() == MOVETYPE_VPHYSICS) && GetOuter()->VPhysicsGetObject(); + + // In the case of game code, we don't really know, so we have to assume it does + case USE_GAME_CODE: + return true; + + case USE_COLLISION_BOUNDS_NEVER_VPHYSICS: + case USE_HITBOXES: + case USE_ROTATION_EXPANDED_BOUNDS: + case USE_SPECIFIED_BOUNDS: + return false; + + default: + Assert(0); + return true; + } +} + + +//----------------------------------------------------------------------------- +// Computes the surrounding collision bounds based on whatever algorithm we want... +//----------------------------------------------------------------------------- +void CCollisionProperty::WorldSpaceSurroundingBounds( Vector *pVecMins, Vector *pVecMaxs ) +{ + const Vector &vecAbsOrigin = GetCollisionOrigin(); + if ( GetOuter()->IsEFlagSet( EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS )) + { + GetOuter()->RemoveEFlags( EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS ); + ComputeSurroundingBox( pVecMins, pVecMaxs ); + VectorSubtract( *pVecMins, vecAbsOrigin, m_vecSurroundingMins ); + VectorSubtract( *pVecMaxs, vecAbsOrigin, m_vecSurroundingMaxs ); + + ASSERT_COORD( m_vecSurroundingMins ); + ASSERT_COORD( m_vecSurroundingMaxs ); + } + else + { + VectorAdd( m_vecSurroundingMins, vecAbsOrigin, *pVecMins ); + VectorAdd( m_vecSurroundingMaxs, vecAbsOrigin, *pVecMaxs ); + } +} + + +//----------------------------------------------------------------------------- +// Spatial partition +//----------------------------------------------------------------------------- +void CCollisionProperty::CreatePartitionHandle() +{ + // Put the entity into the spatial partition. + Assert( m_Partition == PARTITION_INVALID_HANDLE ); + m_Partition = partition->CreateHandle( GetEntityHandle() ); +} + +void CCollisionProperty::DestroyPartitionHandle() +{ + if ( m_Partition != PARTITION_INVALID_HANDLE ) + { + partition->DestroyHandle( m_Partition ); + m_Partition = PARTITION_INVALID_HANDLE; + } +} + + +//----------------------------------------------------------------------------- +// Updates the spatial partition +//----------------------------------------------------------------------------- +void CCollisionProperty::UpdateServerPartitionMask( ) +{ +#ifndef CLIENT_DLL + SpatialPartitionHandle_t handle = GetPartitionHandle(); + if ( handle == PARTITION_INVALID_HANDLE ) + return; + + // Remove it from whatever lists it may be in at the moment + // We'll re-add it below if we need to. + partition->Remove( handle ); + + // Don't bother with deleted things + if ( !m_pOuter->edict() ) + return; + + // don't add the world + if ( m_pOuter->entindex() == 0 ) + return; + + // Make sure it's in the list of all entities + bool bIsSolid = IsSolid() || IsSolidFlagSet(FSOLID_TRIGGER); + if ( bIsSolid || m_pOuter->IsEFlagSet(EFL_USE_PARTITION_WHEN_NOT_SOLID) ) + { + partition->Insert( PARTITION_ENGINE_NON_STATIC_EDICTS, handle ); + } + + if ( !bIsSolid ) + return; + + // Insert it into the appropriate lists. + // We have to continually reinsert it because its solid type may have changed + SpatialPartitionListMask_t mask = 0; + if ( !IsSolidFlagSet(FSOLID_NOT_SOLID) ) + { + mask |= PARTITION_ENGINE_SOLID_EDICTS; + } + if ( IsSolidFlagSet(FSOLID_TRIGGER) ) + { + mask |= PARTITION_ENGINE_TRIGGER_EDICTS; + } + Assert( mask != 0 ); + partition->Insert( mask, handle ); +#endif +} + + +//----------------------------------------------------------------------------- +// Marks the spatial partition dirty +//----------------------------------------------------------------------------- +void CCollisionProperty::MarkPartitionHandleDirty() +{ + // don't bother with the world + if ( m_pOuter->entindex() == 0 ) + return; + + if ( !m_pOuter->IsEFlagSet( EFL_DIRTY_SPATIAL_PARTITION ) ) + { + m_pOuter->AddEFlags( EFL_DIRTY_SPATIAL_PARTITION ); + s_DirtyKDTree.AddEntity( m_pOuter ); + } + +#ifdef CLIENT_DLL + GetOuter()->MarkRenderHandleDirty(); + g_pClientShadowMgr->AddToDirtyShadowList( GetOuter() ); +#endif +} + + +//----------------------------------------------------------------------------- +// Updates the spatial partition +//----------------------------------------------------------------------------- +void CCollisionProperty::UpdatePartition( ) +{ + if ( m_pOuter->IsEFlagSet( EFL_DIRTY_SPATIAL_PARTITION ) ) + { + m_pOuter->RemoveEFlags( EFL_DIRTY_SPATIAL_PARTITION ); + +#ifndef CLIENT_DLL + Assert( m_pOuter->entindex() != 0 ); + + // Don't bother with deleted things + if ( !m_pOuter->edict() ) + return; + + if ( GetPartitionHandle() == PARTITION_INVALID_HANDLE ) + { + CreatePartitionHandle(); + UpdateServerPartitionMask(); + } +#else + if ( GetPartitionHandle() == PARTITION_INVALID_HANDLE ) + return; +#endif + + // We don't need to bother if it's not a trigger or solid + if ( IsSolid() || IsSolidFlagSet( FSOLID_TRIGGER ) || m_pOuter->IsEFlagSet( EFL_USE_PARTITION_WHEN_NOT_SOLID ) ) + { + // Bloat a little bit... + if ( BoundingRadius() != 0.0f ) + { + Vector vecSurroundMins, vecSurroundMaxs; + WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs ); + vecSurroundMins -= Vector( 1, 1, 1 ); + vecSurroundMaxs += Vector( 1, 1, 1 ); + partition->ElementMoved( GetPartitionHandle(), vecSurroundMins, vecSurroundMaxs ); + } + else + { + partition->ElementMoved( GetPartitionHandle(), GetCollisionOrigin(), GetCollisionOrigin() ); + } + } + } +} + + -- cgit v1.2.3