aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/client/physics.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/client/physics.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/client/physics.cpp')
-rw-r--r--mp/src/game/client/physics.cpp2102
1 files changed, 1051 insertions, 1051 deletions
diff --git a/mp/src/game/client/physics.cpp b/mp/src/game/client/physics.cpp
index a3a32b59..fb182ab0 100644
--- a/mp/src/game/client/physics.cpp
+++ b/mp/src/game/client/physics.cpp
@@ -1,1051 +1,1051 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "vcollide_parse.h"
-#include "filesystem.h"
-#include "engine/IStaticPropMgr.h"
-#include "solidsetdefaults.h"
-#include "engine/IEngineSound.h"
-#include "vphysics_sound.h"
-#include "movevars_shared.h"
-#include "engine/ivmodelinfo.h"
-#include "fx.h"
-#include "tier0/vprof.h"
-#include "c_world.h"
-#include "vphysics/object_hash.h"
-#include "vphysics/collision_set.h"
-#include "soundenvelope.h"
-#include "fx_water.h"
-#include "positionwatcher.h"
-#include "vphysics/constraints.h"
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-// file system interface
-extern IFileSystem *filesystem;
-
-ConVar cl_phys_timescale( "cl_phys_timescale", "1.0", FCVAR_CHEAT, "Sets the scale of time for client-side physics (ragdolls)" );
-
-void PrecachePhysicsSounds( void );
-
-//FIXME: Replicated from server end, consolidate?
-
-
-extern IVEngineClient *engine;
-
-class CCollisionEvent : public IPhysicsCollisionEvent, public IPhysicsCollisionSolver, public IPhysicsObjectEvent
-{
-public:
- CCollisionEvent( void );
-
- void ObjectSound( int index, vcollisionevent_t *pEvent );
- void PreCollision( vcollisionevent_t *pEvent ) {}
- void PostCollision( vcollisionevent_t *pEvent );
- void Friction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit, IPhysicsCollisionData *pData );
-
- void BufferTouchEvents( bool enable ) { m_bBufferTouchEvents = enable; }
-
- void StartTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData );
- void EndTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData );
-
- void FluidStartTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid );
- void FluidEndTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid );
- void PostSimulationFrame() {}
-
- virtual void ObjectEnterTrigger( IPhysicsObject *pTrigger, IPhysicsObject *pObject ) {}
- virtual void ObjectLeaveTrigger( IPhysicsObject *pTrigger, IPhysicsObject *pObject ) {}
-
- float DeltaTimeSinceLastFluid( CBaseEntity *pEntity );
- void FrameUpdate( void );
-
- void UpdateFluidEvents( void );
- void UpdateTouchEvents( void );
-
- // IPhysicsCollisionSolver
- int ShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 );
-#if _DEBUG
- int ShouldCollide_2( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 );
-#endif
- // debugging collision problem in TF2
- int ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1, float dt );
- bool ShouldFreezeObject( IPhysicsObject *pObject ) { return true; }
- int AdditionalCollisionChecksThisTick( int currentChecksDone ) { return 0; }
- bool ShouldFreezeContacts( IPhysicsObject **pObjectList, int objectCount ) { return true; }
-
- // IPhysicsObjectEvent
- virtual void ObjectWake( IPhysicsObject *pObject )
- {
- C_BaseEntity *pEntity = static_cast<C_BaseEntity *>(pObject->GetGameData());
- if (pEntity && pEntity->HasDataObjectType(VPHYSICSWATCHER))
- {
- ReportVPhysicsStateChanged( pObject, pEntity, true );
- }
- }
-
- virtual void ObjectSleep( IPhysicsObject *pObject )
- {
- C_BaseEntity *pEntity = static_cast<C_BaseEntity *>(pObject->GetGameData());
- if ( pEntity && pEntity->HasDataObjectType( VPHYSICSWATCHER ) )
- {
- ReportVPhysicsStateChanged( pObject, pEntity, false );
- }
- }
-
-
- friction_t *FindFriction( CBaseEntity *pObject );
- void ShutdownFriction( friction_t &friction );
- void UpdateFrictionSounds();
- bool IsInCallback() { return m_inCallback > 0 ? true : false; }
-
-private:
- class CallbackContext
- {
- public:
- CallbackContext(CCollisionEvent *pOuter)
- {
- m_pOuter = pOuter;
- m_pOuter->m_inCallback++;
- }
- ~CallbackContext()
- {
- m_pOuter->m_inCallback--;
- }
- private:
- CCollisionEvent *m_pOuter;
- };
- friend class CallbackContext;
-
- void AddTouchEvent( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, int touchType, const Vector &point, const Vector &normal );
- void DispatchStartTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, const Vector &point, const Vector &normal );
- void DispatchEndTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1 );
-
- friction_t m_current[8];
- CUtlVector<fluidevent_t> m_fluidEvents;
- CUtlVector<touchevent_t> m_touchEvents;
- int m_inCallback;
- bool m_bBufferTouchEvents;
-};
-
-CCollisionEvent g_Collisions;
-
-bool PhysIsInCallback()
-{
- if ( (physenv && physenv->IsInSimulation()) || g_Collisions.IsInCallback() )
- return true;
-
- return false;
-}
-
-bool PhysicsDLLInit( CreateInterfaceFn physicsFactory )
-{
- if ((physics = (IPhysics *)physicsFactory( VPHYSICS_INTERFACE_VERSION, NULL )) == NULL ||
- (physprops = (IPhysicsSurfaceProps *)physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL )) == NULL ||
- (physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL )) == NULL )
- {
- return false;
- }
-
- if ( IsX360() )
- {
- // Reduce timescale to save perf on 360
- cl_phys_timescale.SetValue(0.9f);
- }
- PhysParseSurfaceData( physprops, filesystem );
- return true;
-}
-
-#define DEFAULT_XBOX_CLIENT_VPHYSICS_TICK 0.025 // 25ms ticks on xbox ragdolls
-void PhysicsLevelInit( void )
-{
- physenv = physics->CreateEnvironment();
- assert( physenv );
-#ifdef PORTAL
- physenv_main = physenv;
-#endif
- {
- MEM_ALLOC_CREDIT();
- g_EntityCollisionHash = physics->CreateObjectPairHash();
- }
-
- // TODO: need to get the right factory function here
- //physenv->SetDebugOverlay( appSystemFactory );
- physenv->SetGravity( Vector(0, 0, -GetCurrentGravity() ) );
- // 15 ms per tick
- // NOTE: Always run client physics at this rate - helps keep ragdolls stable
- physenv->SetSimulationTimestep( IsXbox() ? DEFAULT_XBOX_CLIENT_VPHYSICS_TICK : DEFAULT_TICK_INTERVAL );
- physenv->SetCollisionEventHandler( &g_Collisions );
- physenv->SetCollisionSolver( &g_Collisions );
-
- g_PhysWorldObject = PhysCreateWorld_Shared( GetClientWorldEntity(), modelinfo->GetVCollide(1), g_PhysDefaultObjectParams );
-
- staticpropmgr->CreateVPhysicsRepresentations( physenv, &g_SolidSetup, NULL );
-}
-
-void PhysicsReset()
-{
- if ( !physenv )
- return;
-
- physenv->ResetSimulationClock();
-}
-
-
-ConVar cl_ragdoll_collide( "cl_ragdoll_collide", "0" );
-
-int CCollisionEvent::ShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 )
-#if _DEBUG
-{
- int x0 = ShouldCollide_2(pObj0, pObj1, pGameData0, pGameData1);
- int x1 = ShouldCollide_2(pObj1, pObj0, pGameData1, pGameData0);
- Assert(x0==x1);
- return x0;
-}
-int CCollisionEvent::ShouldCollide_2( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 )
-#endif
-{
- CallbackContext callback(this);
-
- C_BaseEntity *pEntity0 = static_cast<C_BaseEntity *>(pGameData0);
- C_BaseEntity *pEntity1 = static_cast<C_BaseEntity *>(pGameData1);
-
- if ( !pEntity0 || !pEntity1 )
- return 1;
-
- unsigned short gameFlags0 = pObj0->GetGameFlags();
- unsigned short gameFlags1 = pObj1->GetGameFlags();
-
- if ( pEntity0 == pEntity1 )
- {
- // allow all-or-nothing per-entity disable
- if ( (gameFlags0 | gameFlags1) & FVPHYSICS_NO_SELF_COLLISIONS )
- return 0;
-
- IPhysicsCollisionSet *pSet = physics->FindCollisionSet( pEntity0->GetModelIndex() );
- if ( pSet )
- return pSet->ShouldCollide( pObj0->GetGameIndex(), pObj1->GetGameIndex() );
-
- return 1;
- }
- // Obey collision group rules
- Assert(GameRules());
- if ( GameRules() )
- {
- if (!GameRules()->ShouldCollide( pEntity0->GetCollisionGroup(), pEntity1->GetCollisionGroup() ))
- return 0;
- }
-
- if ( (pObj0->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL) && (pObj1->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL) )
- {
- if ( !cl_ragdoll_collide.GetBool() )
- return 0;
- }
-
- // check contents
- if ( !(pObj0->GetContents() & pEntity1->PhysicsSolidMaskForEntity()) || !(pObj1->GetContents() & pEntity0->PhysicsSolidMaskForEntity()) )
- return 0;
-
- if ( g_EntityCollisionHash->IsObjectPairInHash( pGameData0, pGameData1 ) )
- return 0;
-
- if ( g_EntityCollisionHash->IsObjectPairInHash( pObj0, pObj1 ) )
- return 0;
-
-#if 0
- int solid0 = pEntity0->GetSolid();
- int solid1 = pEntity1->GetSolid();
- int nSolidFlags0 = pEntity0->GetSolidFlags();
- int nSolidFlags1 = pEntity1->GetSolidFlags();
-#endif
-
- int movetype0 = pEntity0->GetMoveType();
- int movetype1 = pEntity1->GetMoveType();
-
- // entities with non-physical move parents or entities with MOVETYPE_PUSH
- // are considered as "AI movers". They are unchanged by collision; they exert
- // physics forces on the rest of the system.
- bool aiMove0 = (movetype0==MOVETYPE_PUSH) ? true : false;
- bool aiMove1 = (movetype1==MOVETYPE_PUSH) ? true : false;
-
- if ( pEntity0->GetMoveParent() )
- {
- // if the object & its parent are both MOVETYPE_VPHYSICS, then this must be a special case
- // like a prop_ragdoll_attached
- if ( !(movetype0 == MOVETYPE_VPHYSICS && pEntity0->GetRootMoveParent()->GetMoveType() == MOVETYPE_VPHYSICS) )
- {
- aiMove0 = true;
- }
- }
- if ( pEntity1->GetMoveParent() )
- {
- // if the object & its parent are both MOVETYPE_VPHYSICS, then this must be a special case.
- if ( !(movetype1 == MOVETYPE_VPHYSICS && pEntity1->GetRootMoveParent()->GetMoveType() == MOVETYPE_VPHYSICS) )
- {
- aiMove1 = true;
- }
- }
-
- // AI movers don't collide with the world/static/pinned objects or other AI movers
- if ( (aiMove0 && !pObj1->IsMoveable()) ||
- (aiMove1 && !pObj0->IsMoveable()) ||
- (aiMove0 && aiMove1) )
- return 0;
-
- // two objects under shadow control should not collide. The AI will figure it out
- if ( pObj0->GetShadowController() && pObj1->GetShadowController() )
- return 0;
- return 1;
-}
-
-int CCollisionEvent::ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1, float dt )
-{
- CallbackContext callback(this);
- // solve it yourself here and return 0, or have the default implementation do it
- if ( pGameData0 == pGameData1 )
- {
- if ( pObj0->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL )
- {
- // this is a ragdoll, self penetrating
- C_BaseEntity *pEnt = reinterpret_cast<C_BaseEntity *>(pGameData0);
- C_BaseAnimating *pAnim = pEnt->GetBaseAnimating();
-
- if ( pAnim && pAnim->m_pRagdoll )
- {
- IPhysicsConstraintGroup *pGroup = pAnim->m_pRagdoll->GetConstraintGroup();
- if ( pGroup )
- {
- pGroup->SolvePenetration( pObj0, pObj1 );
- return false;
- }
- }
- }
- }
-
- return true;
-}
-
-
-// A class that implements an IClientSystem for physics
-class CPhysicsSystem : public CAutoGameSystemPerFrame
-{
-public:
- CPhysicsSystem( char const *name ) : CAutoGameSystemPerFrame( name )
- {
- }
-
- // HACKHACK: PhysicsDLLInit() is called explicitly because it requires a parameter
- virtual bool Init();
- virtual void Shutdown();
-
- // Level init, shutdown
- virtual void LevelInitPreEntity();
- virtual void LevelInitPostEntity();
-
- // The level is shutdown in two parts
- virtual void LevelShutdownPreEntity();
-
- virtual void LevelShutdownPostEntity();
-
- void AddImpactSound( void *pGameData, IPhysicsObject *pObject, int surfaceProps, int surfacePropsHit, float volume, float speed );
-
- virtual void Update( float frametime );
-
- void PhysicsSimulate();
-
-private:
- physicssound::soundlist_t m_impactSounds;
-};
-
-static CPhysicsSystem g_PhysicsSystem( "CPhysicsSystem" );
-// singleton to hook into the client system
-IGameSystem *PhysicsGameSystem( void )
-{
- return &g_PhysicsSystem;
-}
-
-
-// HACKHACK: PhysicsDLLInit() is called explicitly because it requires a parameter
-bool CPhysicsSystem::Init()
-{
- return true;
-}
-
-void CPhysicsSystem::Shutdown()
-{
-}
-
-// Level init, shutdown
-void CPhysicsSystem::LevelInitPreEntity( void )
-{
- m_impactSounds.RemoveAll();
- PrecachePhysicsSounds();
-}
-
-void CPhysicsSystem::LevelInitPostEntity( void )
-{
- PhysicsLevelInit();
-}
-
-// The level is shutdown in two parts
-void CPhysicsSystem::LevelShutdownPreEntity()
-{
- if ( physenv )
- {
- // we may have deleted multiple objects including the world by now, so
- // don't try to wake them up
- physenv->SetQuickDelete( true );
- }
-}
-
-void CPhysicsSystem::LevelShutdownPostEntity()
-{
- if ( physenv )
- {
- // environment destroys all objects
- // entities are gone, so this is safe now
- physics->DestroyEnvironment( physenv );
- }
- physics->DestroyObjectPairHash( g_EntityCollisionHash );
- g_EntityCollisionHash = NULL;
-
- physics->DestroyAllCollisionSets();
-
- physenv = NULL;
- g_PhysWorldObject = NULL;
-}
-
-void CPhysicsSystem::AddImpactSound( void *pGameData, IPhysicsObject *pObject, int surfaceProps, int surfacePropsHit, float volume, float speed )
-{
- physicssound::AddImpactSound( m_impactSounds, pGameData, SOUND_FROM_WORLD, CHAN_STATIC, pObject, surfaceProps, surfacePropsHit, volume, speed );
-}
-
-
-void CPhysicsSystem::Update( float frametime )
-{
- // THIS WAS MOVED TO POST-ENTITY SIM
- //PhysicsSimulate();
-}
-
-
-void CPhysicsSystem::PhysicsSimulate()
-{
- VPROF_BUDGET( "CPhysicsSystem::PhysicsSimulate", VPROF_BUDGETGROUP_PHYSICS );
- float frametime = gpGlobals->frametime;
-
- if ( physenv )
- {
- tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %d", __FUNCTION__, physenv->GetActiveObjectCount() );
-
- g_Collisions.BufferTouchEvents( true );
-#ifdef _DEBUG
- physenv->DebugCheckContacts();
-#endif
- physenv->Simulate( frametime * cl_phys_timescale.GetFloat() );
-
- int activeCount = physenv->GetActiveObjectCount();
- IPhysicsObject **pActiveList = NULL;
- if ( activeCount )
- {
- pActiveList = (IPhysicsObject **)stackalloc( sizeof(IPhysicsObject *)*activeCount );
- physenv->GetActiveObjects( pActiveList );
-
- for ( int i = 0; i < activeCount; i++ )
- {
- C_BaseEntity *pEntity = reinterpret_cast<C_BaseEntity *>(pActiveList[i]->GetGameData());
- if ( pEntity )
- {
- if ( pEntity->CollisionProp()->DoesVPhysicsInvalidateSurroundingBox() )
- {
- pEntity->CollisionProp()->MarkSurroundingBoundsDirty();
- }
- pEntity->VPhysicsUpdate( pActiveList[i] );
- }
- }
- }
-
- g_Collisions.BufferTouchEvents( false );
- g_Collisions.FrameUpdate();
- }
- physicssound::PlayImpactSounds( m_impactSounds );
-}
-
-
-void PhysicsSimulate()
-{
- g_PhysicsSystem.PhysicsSimulate();
-}
-
-
-
-CCollisionEvent::CCollisionEvent( void )
-{
-}
-
-void CCollisionEvent::ObjectSound( int index, vcollisionevent_t *pEvent )
-{
- IPhysicsObject *pObject = pEvent->pObjects[index];
- if ( !pObject || pObject->IsStatic() )
- return;
-
- float speed = pEvent->collisionSpeed * pEvent->collisionSpeed;
- int surfaceProps = pEvent->surfaceProps[index];
-
- void *pGameData = pObject->GetGameData();
-
- if ( pGameData )
- {
- float volume = speed * (1.0f/(320.0f*320.0f)); // max volume at 320 in/s
-
- if ( volume > 1.0f )
- volume = 1.0f;
-
- if ( surfaceProps >= 0 )
- {
- g_PhysicsSystem.AddImpactSound( pGameData, pObject, surfaceProps, pEvent->surfaceProps[!index], volume, speed );
- }
- }
-}
-
-void CCollisionEvent::PostCollision( vcollisionevent_t *pEvent )
-{
- CallbackContext callback(this);
- if ( pEvent->deltaCollisionTime > 0.1f && pEvent->collisionSpeed > 70 )
- {
- ObjectSound( 0, pEvent );
- ObjectSound( 1, pEvent );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCollisionEvent::FrameUpdate( void )
-{
- UpdateFrictionSounds();
- UpdateTouchEvents();
- UpdateFluidEvents();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCollisionEvent::UpdateTouchEvents( void )
-{
- // Turn on buffering in case new touch events occur during processing
- bool bOldTouchEvents = m_bBufferTouchEvents;
- m_bBufferTouchEvents = true;
- for ( int i = 0; i < m_touchEvents.Count(); i++ )
- {
- const touchevent_t &event = m_touchEvents[i];
- if ( event.touchType == TOUCH_START )
- {
- DispatchStartTouch( event.pEntity0, event.pEntity1, event.endPoint, event.normal );
- }
- else
- {
- // TOUCH_END
- DispatchEndTouch( event.pEntity0, event.pEntity1 );
- }
- }
-
- m_touchEvents.RemoveAll();
- m_bBufferTouchEvents = bOldTouchEvents;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity0 -
-// *pEntity1 -
-// touchType -
-//-----------------------------------------------------------------------------
-void CCollisionEvent::AddTouchEvent( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, int touchType, const Vector &point, const Vector &normal )
-{
- if ( !pEntity0 || !pEntity1 )
- return;
-
- int index = m_touchEvents.AddToTail();
- touchevent_t &event = m_touchEvents[index];
- event.pEntity0 = pEntity0;
- event.pEntity1 = pEntity1;
- event.touchType = touchType;
- event.endPoint = point;
- event.normal = normal;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pObject1 -
-// *pObject2 -
-// *pTouchData -
-//-----------------------------------------------------------------------------
-void CCollisionEvent::StartTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData )
-{
- CallbackContext callback(this);
- C_BaseEntity *pEntity1 = static_cast<C_BaseEntity *>(pObject1->GetGameData());
- C_BaseEntity *pEntity2 = static_cast<C_BaseEntity *>(pObject2->GetGameData());
-
- if ( !pEntity1 || !pEntity2 )
- return;
-
- Vector endPoint, normal;
- pTouchData->GetContactPoint( endPoint );
- pTouchData->GetSurfaceNormal( normal );
- if ( !m_bBufferTouchEvents )
- {
- DispatchStartTouch( pEntity1, pEntity2, endPoint, normal );
- }
- else
- {
- AddTouchEvent( pEntity1, pEntity2, TOUCH_START, endPoint, normal );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity0 -
-// *pEntity1 -
-//-----------------------------------------------------------------------------
-void CCollisionEvent::DispatchStartTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, const Vector &point, const Vector &normal )
-{
- trace_t trace;
- memset( &trace, 0, sizeof(trace) );
- trace.endpos = point;
- trace.plane.dist = DotProduct( point, normal );
- trace.plane.normal = normal;
-
- // NOTE: This sets up the touch list for both entities, no call to pEntity1 is needed
- pEntity0->PhysicsMarkEntitiesAsTouchingEventDriven( pEntity1, trace );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pObject1 -
-// *pObject2 -
-// *pTouchData -
-//-----------------------------------------------------------------------------
-void CCollisionEvent::EndTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData )
-{
- CallbackContext callback(this);
- C_BaseEntity *pEntity1 = static_cast<C_BaseEntity *>(pObject1->GetGameData());
- C_BaseEntity *pEntity2 = static_cast<C_BaseEntity *>(pObject2->GetGameData());
-
- if ( !pEntity1 || !pEntity2 )
- return;
-
- if ( !m_bBufferTouchEvents )
- {
- DispatchEndTouch( pEntity1, pEntity2 );
- }
- else
- {
- AddTouchEvent( pEntity1, pEntity2, TOUCH_END, vec3_origin, vec3_origin );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity0 -
-// *pEntity1 -
-//-----------------------------------------------------------------------------
-void CCollisionEvent::DispatchEndTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1 )
-{
- // frees the event-driven touchlinks
- pEntity0->PhysicsNotifyOtherOfUntouch( pEntity0, pEntity1 );
- pEntity1->PhysicsNotifyOtherOfUntouch( pEntity1, pEntity0 );
-}
-
-void CCollisionEvent::Friction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit, IPhysicsCollisionData *pData )
-{
- CallbackContext callback(this);
- if ( energy < 0.05f || surfaceProps < 0 )
- return;
-
- //Get our friction information
- Vector vecPos, vecVel;
- pData->GetContactPoint( vecPos );
- pObject->GetVelocityAtPoint( vecPos, &vecVel );
-
- CBaseEntity *pEntity = reinterpret_cast<CBaseEntity *>(pObject->GetGameData());
-
- if ( pEntity )
- {
- friction_t *pFriction = g_Collisions.FindFriction( pEntity );
-
- if ( (gpGlobals->maxClients > 1) && pFriction && pFriction->pObject)
- {
- // in MP mode play sound and effects once every 500 msecs,
- // no ongoing updates, takes too much bandwidth
- if ( (pFriction->flLastEffectTime + 0.5f) > gpGlobals->curtime)
- {
- pFriction->flLastUpdateTime = gpGlobals->curtime;
- return;
- }
- }
-
- PhysFrictionSound( pEntity, pObject, energy, surfaceProps, surfacePropsHit );
- }
-
- PhysFrictionEffect( vecPos, vecVel, energy, surfaceProps, surfacePropsHit );
-}
-
-friction_t *CCollisionEvent::FindFriction( CBaseEntity *pObject )
-{
- friction_t *pFree = NULL;
-
- for ( int i = 0; i < ARRAYSIZE(m_current); i++ )
- {
- if ( !m_current[i].pObject && !pFree )
- pFree = &m_current[i];
-
- if ( m_current[i].pObject == pObject )
- return &m_current[i];
- }
-
- return pFree;
-}
-
-void CCollisionEvent::ShutdownFriction( friction_t &friction )
-{
-// Msg( "Scrape Stop %s \n", STRING(friction.pObject->m_iClassname) );
- CSoundEnvelopeController::GetController().SoundDestroy( friction.patch );
- friction.patch = NULL;
- friction.pObject = NULL;
-}
-
-void CCollisionEvent::UpdateFrictionSounds( void )
-{
- for ( int i = 0; i < ARRAYSIZE(m_current); i++ )
- {
- if ( m_current[i].patch )
- {
- if ( m_current[i].flLastUpdateTime < (gpGlobals->curtime-0.1f) )
- {
- // friction wasn't updated the last 100msec, assume fiction finished
- ShutdownFriction( m_current[i] );
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &matrix -
-// &normal -
-// Output : static int
-//-----------------------------------------------------------------------------
-static int BestAxisMatchingNormal( matrix3x4_t &matrix, const Vector &normal )
-{
- float bestDot = -1;
- int best = 0;
- for ( int i = 0; i < 3; i++ )
- {
- Vector tmp;
- MatrixGetColumn( matrix, i, tmp );
- float dot = fabs(DotProduct( tmp, normal ));
- if ( dot > bestDot )
- {
- bestDot = dot;
- best = i;
- }
- }
-
- return best;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pFluid -
-// *pObject -
-// *pEntity -
-//-----------------------------------------------------------------------------
-void PhysicsSplash( IPhysicsFluidController *pFluid, IPhysicsObject *pObject, CBaseEntity *pEntity )
-{
- //FIXME: For now just allow ragdolls for E3 - jdw
- if ( ( pObject->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL ) == false )
- return;
-
- Vector velocity;
- pObject->GetVelocity( &velocity, NULL );
-
- float impactSpeed = velocity.Length();
-
- if ( impactSpeed < 25.0f )
- return;
-
- Vector normal;
- float dist;
- pFluid->GetSurfacePlane( &normal, &dist );
-
- matrix3x4_t &matrix = pEntity->EntityToWorldTransform();
-
- // Find the local axis that best matches the water surface normal
- int bestAxis = BestAxisMatchingNormal( matrix, normal );
-
- Vector tangent, binormal;
- MatrixGetColumn( matrix, (bestAxis+1)%3, tangent );
- binormal = CrossProduct( normal, tangent );
- VectorNormalize( binormal );
- tangent = CrossProduct( binormal, normal );
- VectorNormalize( tangent );
-
- // Now we have a basis tangent to the surface that matches the object's local orientation as well as possible
- // compute an OBB using this basis
-
- // Get object extents in basis
- Vector tanPts[2], binPts[2];
- tanPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -tangent );
- tanPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), tangent );
- binPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -binormal );
- binPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), binormal );
-
- // now compute the centered bbox
- float mins[2], maxs[2], center[2], extents[2];
- mins[0] = DotProduct( tanPts[0], tangent );
- maxs[0] = DotProduct( tanPts[1], tangent );
-
- mins[1] = DotProduct( binPts[0], binormal );
- maxs[1] = DotProduct( binPts[1], binormal );
-
- center[0] = 0.5 * (mins[0] + maxs[0]);
- center[1] = 0.5 * (mins[1] + maxs[1]);
-
- extents[0] = maxs[0] - center[0];
- extents[1] = maxs[1] - center[1];
-
- Vector centerPoint = center[0] * tangent + center[1] * binormal + dist * normal;
-
- Vector axes[2];
- axes[0] = (maxs[0] - center[0]) * tangent;
- axes[1] = (maxs[1] - center[1]) * binormal;
-
- // visualize OBB hit
- /*
- Vector corner1 = centerPoint - axes[0] - axes[1];
- Vector corner2 = centerPoint + axes[0] - axes[1];
- Vector corner3 = centerPoint + axes[0] + axes[1];
- Vector corner4 = centerPoint - axes[0] + axes[1];
- NDebugOverlay::Line( corner1, corner2, 0, 0, 255, false, 10 );
- NDebugOverlay::Line( corner2, corner3, 0, 0, 255, false, 10 );
- NDebugOverlay::Line( corner3, corner4, 0, 0, 255, false, 10 );
- NDebugOverlay::Line( corner4, corner1, 0, 0, 255, false, 10 );
- */
-
- Vector corner[4];
-
- corner[0] = centerPoint - axes[0] - axes[1];
- corner[1] = centerPoint + axes[0] - axes[1];
- corner[2] = centerPoint + axes[0] + axes[1];
- corner[3] = centerPoint - axes[0] + axes[1];
-
- int contents = enginetrace->GetPointContents( centerPoint-Vector(0,0,2) );
-
- bool bInSlime = ( contents & CONTENTS_SLIME ) ? true : false;
-
- Vector color = vec3_origin;
- float luminosity = 1.0f;
-
- if ( !bInSlime )
- {
- // Get our lighting information
- FX_GetSplashLighting( centerPoint + ( normal * 8.0f ), &color, &luminosity );
- }
-
- if ( impactSpeed > 150 )
- {
- if ( bInSlime )
- {
- FX_GunshotSlimeSplash( centerPoint, normal, random->RandomFloat( 8, 10 ) );
- }
- else
- {
- FX_GunshotSplash( centerPoint, normal, random->RandomFloat( 8, 10 ) );
- }
- }
- else if ( !bInSlime )
- {
- FX_WaterRipple( centerPoint, 1.5f, &color, 1.5f, luminosity );
- }
-
- int splashes = 4;
- Vector point;
-
- for ( int i = 0; i < splashes; i++ )
- {
- point = RandomVector( -32.0f, 32.0f );
- point[2] = 0.0f;
-
- point += corner[i];
-
- if ( impactSpeed > 150 )
- {
- if ( bInSlime )
- {
- FX_GunshotSlimeSplash( centerPoint, normal, random->RandomFloat( 4, 6 ) );
- }
- else
- {
- FX_GunshotSplash( centerPoint, normal, random->RandomFloat( 4, 6 ) );
- }
- }
- else if ( !bInSlime )
- {
- FX_WaterRipple( point, random->RandomFloat( 0.25f, 0.5f ), &color, luminosity, random->RandomFloat( 0.5f, 1.0f ) );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCollisionEvent::UpdateFluidEvents( void )
-{
- for ( int i = m_fluidEvents.Count()-1; i >= 0; --i )
- {
- if ( (gpGlobals->curtime - m_fluidEvents[i].impactTime) > FLUID_TIME_MAX )
- {
- m_fluidEvents.FastRemove(i);
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity -
-// Output : float
-//-----------------------------------------------------------------------------
-float CCollisionEvent::DeltaTimeSinceLastFluid( CBaseEntity *pEntity )
-{
- for ( int i = m_fluidEvents.Count()-1; i >= 0; --i )
- {
- if ( m_fluidEvents[i].hEntity.Get() == pEntity )
- {
- return gpGlobals->curtime - m_fluidEvents[i].impactTime;
- }
- }
-
- int index = m_fluidEvents.AddToTail();
- m_fluidEvents[index].hEntity = pEntity;
- m_fluidEvents[index].impactTime = gpGlobals->curtime;
- return FLUID_TIME_MAX;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pObject -
-// *pFluid -
-//-----------------------------------------------------------------------------
-void CCollisionEvent::FluidStartTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid )
-{
- CallbackContext callback(this);
- if ( ( pObject == NULL ) || ( pFluid == NULL ) )
- return;
-
- CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObject->GetGameData());
-
- if ( pEntity )
- {
- float timeSinceLastCollision = DeltaTimeSinceLastFluid( pEntity );
-
- if ( timeSinceLastCollision < 0.5f )
- return;
-
- PhysicsSplash( pFluid, pObject, pEntity );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pObject -
-// *pFluid -
-//-----------------------------------------------------------------------------
-void CCollisionEvent::FluidEndTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid )
-{
- CallbackContext callback(this);
- //FIXME: Do nothing for now
-}
-
-IPhysicsObject *GetWorldPhysObject ( void )
-{
- return g_PhysWorldObject;
-}
-
-void PhysFrictionSound( CBaseEntity *pEntity, IPhysicsObject *pObject, const char *pSoundName, HSOUNDSCRIPTHANDLE& handle, float flVolume )
-{
- if ( !pEntity )
- return;
-
- // cut out the quiet sounds
- // UNDONE: Separate threshold for starting a sound vs. continuing?
- flVolume = clamp( flVolume, 0.0f, 1.0f );
- if ( flVolume > (1.0f/128.0f) )
- {
- friction_t *pFriction = g_Collisions.FindFriction( pEntity );
- if ( !pFriction )
- return;
-
- CSoundParameters params;
- if ( !CBaseEntity::GetParametersForSound( pSoundName, handle, params, NULL ) )
- return;
-
- if ( !pFriction->pObject )
- {
- // don't create really quiet scrapes
- if ( params.volume * flVolume <= 0.1f )
- return;
-
- pFriction->pObject = pEntity;
- CPASAttenuationFilter filter( pEntity, params.soundlevel );
- int entindex = pEntity->entindex();
-
- // clientside created entites doesn't have a valid entindex, let 'world' play the sound for them
- if ( entindex < 0 )
- entindex = 0;
-
- pFriction->patch = CSoundEnvelopeController::GetController().SoundCreate(
- filter, entindex, CHAN_BODY, pSoundName, params.soundlevel );
- CSoundEnvelopeController::GetController().Play( pFriction->patch, params.volume * flVolume, params.pitch );
- }
- else
- {
- float pitch = (flVolume * (params.pitchhigh - params.pitchlow)) + params.pitchlow;
- CSoundEnvelopeController::GetController().SoundChangeVolume( pFriction->patch, params.volume * flVolume, 0.1f );
- CSoundEnvelopeController::GetController().SoundChangePitch( pFriction->patch, pitch, 0.1f );
- }
-
- pFriction->flLastUpdateTime = gpGlobals->curtime;
- pFriction->flLastEffectTime = gpGlobals->curtime;
- }
-}
-
-void PhysCleanupFrictionSounds( CBaseEntity *pEntity )
-{
- friction_t *pFriction = g_Collisions.FindFriction( pEntity );
- if ( pFriction && pFriction->patch )
- {
- g_Collisions.ShutdownFriction( *pFriction );
- }
-}
-
-float PhysGetNextSimTime()
-{
- return physenv->GetSimulationTime() + gpGlobals->frametime * cl_phys_timescale.GetFloat();
-}
-
-float PhysGetSyncCreateTime()
-{
- float nextTime = physenv->GetNextFrameTime();
- float simTime = PhysGetNextSimTime();
- if ( nextTime < simTime )
- {
- // The next simulation frame begins before the end of this frame
- // so create physics objects at that time so that they will reach the current
- // position at curtime. Otherwise the physics object will simulate forward from curtime
- // and pop into the future a bit at this point of transition
- return gpGlobals->curtime + nextTime - simTime;
- }
- return gpGlobals->curtime;
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "vcollide_parse.h"
+#include "filesystem.h"
+#include "engine/IStaticPropMgr.h"
+#include "solidsetdefaults.h"
+#include "engine/IEngineSound.h"
+#include "vphysics_sound.h"
+#include "movevars_shared.h"
+#include "engine/ivmodelinfo.h"
+#include "fx.h"
+#include "tier0/vprof.h"
+#include "c_world.h"
+#include "vphysics/object_hash.h"
+#include "vphysics/collision_set.h"
+#include "soundenvelope.h"
+#include "fx_water.h"
+#include "positionwatcher.h"
+#include "vphysics/constraints.h"
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// file system interface
+extern IFileSystem *filesystem;
+
+ConVar cl_phys_timescale( "cl_phys_timescale", "1.0", FCVAR_CHEAT, "Sets the scale of time for client-side physics (ragdolls)" );
+
+void PrecachePhysicsSounds( void );
+
+//FIXME: Replicated from server end, consolidate?
+
+
+extern IVEngineClient *engine;
+
+class CCollisionEvent : public IPhysicsCollisionEvent, public IPhysicsCollisionSolver, public IPhysicsObjectEvent
+{
+public:
+ CCollisionEvent( void );
+
+ void ObjectSound( int index, vcollisionevent_t *pEvent );
+ void PreCollision( vcollisionevent_t *pEvent ) {}
+ void PostCollision( vcollisionevent_t *pEvent );
+ void Friction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit, IPhysicsCollisionData *pData );
+
+ void BufferTouchEvents( bool enable ) { m_bBufferTouchEvents = enable; }
+
+ void StartTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData );
+ void EndTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData );
+
+ void FluidStartTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid );
+ void FluidEndTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid );
+ void PostSimulationFrame() {}
+
+ virtual void ObjectEnterTrigger( IPhysicsObject *pTrigger, IPhysicsObject *pObject ) {}
+ virtual void ObjectLeaveTrigger( IPhysicsObject *pTrigger, IPhysicsObject *pObject ) {}
+
+ float DeltaTimeSinceLastFluid( CBaseEntity *pEntity );
+ void FrameUpdate( void );
+
+ void UpdateFluidEvents( void );
+ void UpdateTouchEvents( void );
+
+ // IPhysicsCollisionSolver
+ int ShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 );
+#if _DEBUG
+ int ShouldCollide_2( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 );
+#endif
+ // debugging collision problem in TF2
+ int ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1, float dt );
+ bool ShouldFreezeObject( IPhysicsObject *pObject ) { return true; }
+ int AdditionalCollisionChecksThisTick( int currentChecksDone ) { return 0; }
+ bool ShouldFreezeContacts( IPhysicsObject **pObjectList, int objectCount ) { return true; }
+
+ // IPhysicsObjectEvent
+ virtual void ObjectWake( IPhysicsObject *pObject )
+ {
+ C_BaseEntity *pEntity = static_cast<C_BaseEntity *>(pObject->GetGameData());
+ if (pEntity && pEntity->HasDataObjectType(VPHYSICSWATCHER))
+ {
+ ReportVPhysicsStateChanged( pObject, pEntity, true );
+ }
+ }
+
+ virtual void ObjectSleep( IPhysicsObject *pObject )
+ {
+ C_BaseEntity *pEntity = static_cast<C_BaseEntity *>(pObject->GetGameData());
+ if ( pEntity && pEntity->HasDataObjectType( VPHYSICSWATCHER ) )
+ {
+ ReportVPhysicsStateChanged( pObject, pEntity, false );
+ }
+ }
+
+
+ friction_t *FindFriction( CBaseEntity *pObject );
+ void ShutdownFriction( friction_t &friction );
+ void UpdateFrictionSounds();
+ bool IsInCallback() { return m_inCallback > 0 ? true : false; }
+
+private:
+ class CallbackContext
+ {
+ public:
+ CallbackContext(CCollisionEvent *pOuter)
+ {
+ m_pOuter = pOuter;
+ m_pOuter->m_inCallback++;
+ }
+ ~CallbackContext()
+ {
+ m_pOuter->m_inCallback--;
+ }
+ private:
+ CCollisionEvent *m_pOuter;
+ };
+ friend class CallbackContext;
+
+ void AddTouchEvent( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, int touchType, const Vector &point, const Vector &normal );
+ void DispatchStartTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, const Vector &point, const Vector &normal );
+ void DispatchEndTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1 );
+
+ friction_t m_current[8];
+ CUtlVector<fluidevent_t> m_fluidEvents;
+ CUtlVector<touchevent_t> m_touchEvents;
+ int m_inCallback;
+ bool m_bBufferTouchEvents;
+};
+
+CCollisionEvent g_Collisions;
+
+bool PhysIsInCallback()
+{
+ if ( (physenv && physenv->IsInSimulation()) || g_Collisions.IsInCallback() )
+ return true;
+
+ return false;
+}
+
+bool PhysicsDLLInit( CreateInterfaceFn physicsFactory )
+{
+ if ((physics = (IPhysics *)physicsFactory( VPHYSICS_INTERFACE_VERSION, NULL )) == NULL ||
+ (physprops = (IPhysicsSurfaceProps *)physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL )) == NULL ||
+ (physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL )) == NULL )
+ {
+ return false;
+ }
+
+ if ( IsX360() )
+ {
+ // Reduce timescale to save perf on 360
+ cl_phys_timescale.SetValue(0.9f);
+ }
+ PhysParseSurfaceData( physprops, filesystem );
+ return true;
+}
+
+#define DEFAULT_XBOX_CLIENT_VPHYSICS_TICK 0.025 // 25ms ticks on xbox ragdolls
+void PhysicsLevelInit( void )
+{
+ physenv = physics->CreateEnvironment();
+ assert( physenv );
+#ifdef PORTAL
+ physenv_main = physenv;
+#endif
+ {
+ MEM_ALLOC_CREDIT();
+ g_EntityCollisionHash = physics->CreateObjectPairHash();
+ }
+
+ // TODO: need to get the right factory function here
+ //physenv->SetDebugOverlay( appSystemFactory );
+ physenv->SetGravity( Vector(0, 0, -GetCurrentGravity() ) );
+ // 15 ms per tick
+ // NOTE: Always run client physics at this rate - helps keep ragdolls stable
+ physenv->SetSimulationTimestep( IsXbox() ? DEFAULT_XBOX_CLIENT_VPHYSICS_TICK : DEFAULT_TICK_INTERVAL );
+ physenv->SetCollisionEventHandler( &g_Collisions );
+ physenv->SetCollisionSolver( &g_Collisions );
+
+ g_PhysWorldObject = PhysCreateWorld_Shared( GetClientWorldEntity(), modelinfo->GetVCollide(1), g_PhysDefaultObjectParams );
+
+ staticpropmgr->CreateVPhysicsRepresentations( physenv, &g_SolidSetup, NULL );
+}
+
+void PhysicsReset()
+{
+ if ( !physenv )
+ return;
+
+ physenv->ResetSimulationClock();
+}
+
+
+ConVar cl_ragdoll_collide( "cl_ragdoll_collide", "0" );
+
+int CCollisionEvent::ShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 )
+#if _DEBUG
+{
+ int x0 = ShouldCollide_2(pObj0, pObj1, pGameData0, pGameData1);
+ int x1 = ShouldCollide_2(pObj1, pObj0, pGameData1, pGameData0);
+ Assert(x0==x1);
+ return x0;
+}
+int CCollisionEvent::ShouldCollide_2( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 )
+#endif
+{
+ CallbackContext callback(this);
+
+ C_BaseEntity *pEntity0 = static_cast<C_BaseEntity *>(pGameData0);
+ C_BaseEntity *pEntity1 = static_cast<C_BaseEntity *>(pGameData1);
+
+ if ( !pEntity0 || !pEntity1 )
+ return 1;
+
+ unsigned short gameFlags0 = pObj0->GetGameFlags();
+ unsigned short gameFlags1 = pObj1->GetGameFlags();
+
+ if ( pEntity0 == pEntity1 )
+ {
+ // allow all-or-nothing per-entity disable
+ if ( (gameFlags0 | gameFlags1) & FVPHYSICS_NO_SELF_COLLISIONS )
+ return 0;
+
+ IPhysicsCollisionSet *pSet = physics->FindCollisionSet( pEntity0->GetModelIndex() );
+ if ( pSet )
+ return pSet->ShouldCollide( pObj0->GetGameIndex(), pObj1->GetGameIndex() );
+
+ return 1;
+ }
+ // Obey collision group rules
+ Assert(GameRules());
+ if ( GameRules() )
+ {
+ if (!GameRules()->ShouldCollide( pEntity0->GetCollisionGroup(), pEntity1->GetCollisionGroup() ))
+ return 0;
+ }
+
+ if ( (pObj0->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL) && (pObj1->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL) )
+ {
+ if ( !cl_ragdoll_collide.GetBool() )
+ return 0;
+ }
+
+ // check contents
+ if ( !(pObj0->GetContents() & pEntity1->PhysicsSolidMaskForEntity()) || !(pObj1->GetContents() & pEntity0->PhysicsSolidMaskForEntity()) )
+ return 0;
+
+ if ( g_EntityCollisionHash->IsObjectPairInHash( pGameData0, pGameData1 ) )
+ return 0;
+
+ if ( g_EntityCollisionHash->IsObjectPairInHash( pObj0, pObj1 ) )
+ return 0;
+
+#if 0
+ int solid0 = pEntity0->GetSolid();
+ int solid1 = pEntity1->GetSolid();
+ int nSolidFlags0 = pEntity0->GetSolidFlags();
+ int nSolidFlags1 = pEntity1->GetSolidFlags();
+#endif
+
+ int movetype0 = pEntity0->GetMoveType();
+ int movetype1 = pEntity1->GetMoveType();
+
+ // entities with non-physical move parents or entities with MOVETYPE_PUSH
+ // are considered as "AI movers". They are unchanged by collision; they exert
+ // physics forces on the rest of the system.
+ bool aiMove0 = (movetype0==MOVETYPE_PUSH) ? true : false;
+ bool aiMove1 = (movetype1==MOVETYPE_PUSH) ? true : false;
+
+ if ( pEntity0->GetMoveParent() )
+ {
+ // if the object & its parent are both MOVETYPE_VPHYSICS, then this must be a special case
+ // like a prop_ragdoll_attached
+ if ( !(movetype0 == MOVETYPE_VPHYSICS && pEntity0->GetRootMoveParent()->GetMoveType() == MOVETYPE_VPHYSICS) )
+ {
+ aiMove0 = true;
+ }
+ }
+ if ( pEntity1->GetMoveParent() )
+ {
+ // if the object & its parent are both MOVETYPE_VPHYSICS, then this must be a special case.
+ if ( !(movetype1 == MOVETYPE_VPHYSICS && pEntity1->GetRootMoveParent()->GetMoveType() == MOVETYPE_VPHYSICS) )
+ {
+ aiMove1 = true;
+ }
+ }
+
+ // AI movers don't collide with the world/static/pinned objects or other AI movers
+ if ( (aiMove0 && !pObj1->IsMoveable()) ||
+ (aiMove1 && !pObj0->IsMoveable()) ||
+ (aiMove0 && aiMove1) )
+ return 0;
+
+ // two objects under shadow control should not collide. The AI will figure it out
+ if ( pObj0->GetShadowController() && pObj1->GetShadowController() )
+ return 0;
+ return 1;
+}
+
+int CCollisionEvent::ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1, float dt )
+{
+ CallbackContext callback(this);
+ // solve it yourself here and return 0, or have the default implementation do it
+ if ( pGameData0 == pGameData1 )
+ {
+ if ( pObj0->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL )
+ {
+ // this is a ragdoll, self penetrating
+ C_BaseEntity *pEnt = reinterpret_cast<C_BaseEntity *>(pGameData0);
+ C_BaseAnimating *pAnim = pEnt->GetBaseAnimating();
+
+ if ( pAnim && pAnim->m_pRagdoll )
+ {
+ IPhysicsConstraintGroup *pGroup = pAnim->m_pRagdoll->GetConstraintGroup();
+ if ( pGroup )
+ {
+ pGroup->SolvePenetration( pObj0, pObj1 );
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+
+// A class that implements an IClientSystem for physics
+class CPhysicsSystem : public CAutoGameSystemPerFrame
+{
+public:
+ CPhysicsSystem( char const *name ) : CAutoGameSystemPerFrame( name )
+ {
+ }
+
+ // HACKHACK: PhysicsDLLInit() is called explicitly because it requires a parameter
+ virtual bool Init();
+ virtual void Shutdown();
+
+ // Level init, shutdown
+ virtual void LevelInitPreEntity();
+ virtual void LevelInitPostEntity();
+
+ // The level is shutdown in two parts
+ virtual void LevelShutdownPreEntity();
+
+ virtual void LevelShutdownPostEntity();
+
+ void AddImpactSound( void *pGameData, IPhysicsObject *pObject, int surfaceProps, int surfacePropsHit, float volume, float speed );
+
+ virtual void Update( float frametime );
+
+ void PhysicsSimulate();
+
+private:
+ physicssound::soundlist_t m_impactSounds;
+};
+
+static CPhysicsSystem g_PhysicsSystem( "CPhysicsSystem" );
+// singleton to hook into the client system
+IGameSystem *PhysicsGameSystem( void )
+{
+ return &g_PhysicsSystem;
+}
+
+
+// HACKHACK: PhysicsDLLInit() is called explicitly because it requires a parameter
+bool CPhysicsSystem::Init()
+{
+ return true;
+}
+
+void CPhysicsSystem::Shutdown()
+{
+}
+
+// Level init, shutdown
+void CPhysicsSystem::LevelInitPreEntity( void )
+{
+ m_impactSounds.RemoveAll();
+ PrecachePhysicsSounds();
+}
+
+void CPhysicsSystem::LevelInitPostEntity( void )
+{
+ PhysicsLevelInit();
+}
+
+// The level is shutdown in two parts
+void CPhysicsSystem::LevelShutdownPreEntity()
+{
+ if ( physenv )
+ {
+ // we may have deleted multiple objects including the world by now, so
+ // don't try to wake them up
+ physenv->SetQuickDelete( true );
+ }
+}
+
+void CPhysicsSystem::LevelShutdownPostEntity()
+{
+ if ( physenv )
+ {
+ // environment destroys all objects
+ // entities are gone, so this is safe now
+ physics->DestroyEnvironment( physenv );
+ }
+ physics->DestroyObjectPairHash( g_EntityCollisionHash );
+ g_EntityCollisionHash = NULL;
+
+ physics->DestroyAllCollisionSets();
+
+ physenv = NULL;
+ g_PhysWorldObject = NULL;
+}
+
+void CPhysicsSystem::AddImpactSound( void *pGameData, IPhysicsObject *pObject, int surfaceProps, int surfacePropsHit, float volume, float speed )
+{
+ physicssound::AddImpactSound( m_impactSounds, pGameData, SOUND_FROM_WORLD, CHAN_STATIC, pObject, surfaceProps, surfacePropsHit, volume, speed );
+}
+
+
+void CPhysicsSystem::Update( float frametime )
+{
+ // THIS WAS MOVED TO POST-ENTITY SIM
+ //PhysicsSimulate();
+}
+
+
+void CPhysicsSystem::PhysicsSimulate()
+{
+ VPROF_BUDGET( "CPhysicsSystem::PhysicsSimulate", VPROF_BUDGETGROUP_PHYSICS );
+ float frametime = gpGlobals->frametime;
+
+ if ( physenv )
+ {
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %d", __FUNCTION__, physenv->GetActiveObjectCount() );
+
+ g_Collisions.BufferTouchEvents( true );
+#ifdef _DEBUG
+ physenv->DebugCheckContacts();
+#endif
+ physenv->Simulate( frametime * cl_phys_timescale.GetFloat() );
+
+ int activeCount = physenv->GetActiveObjectCount();
+ IPhysicsObject **pActiveList = NULL;
+ if ( activeCount )
+ {
+ pActiveList = (IPhysicsObject **)stackalloc( sizeof(IPhysicsObject *)*activeCount );
+ physenv->GetActiveObjects( pActiveList );
+
+ for ( int i = 0; i < activeCount; i++ )
+ {
+ C_BaseEntity *pEntity = reinterpret_cast<C_BaseEntity *>(pActiveList[i]->GetGameData());
+ if ( pEntity )
+ {
+ if ( pEntity->CollisionProp()->DoesVPhysicsInvalidateSurroundingBox() )
+ {
+ pEntity->CollisionProp()->MarkSurroundingBoundsDirty();
+ }
+ pEntity->VPhysicsUpdate( pActiveList[i] );
+ }
+ }
+ }
+
+ g_Collisions.BufferTouchEvents( false );
+ g_Collisions.FrameUpdate();
+ }
+ physicssound::PlayImpactSounds( m_impactSounds );
+}
+
+
+void PhysicsSimulate()
+{
+ g_PhysicsSystem.PhysicsSimulate();
+}
+
+
+
+CCollisionEvent::CCollisionEvent( void )
+{
+}
+
+void CCollisionEvent::ObjectSound( int index, vcollisionevent_t *pEvent )
+{
+ IPhysicsObject *pObject = pEvent->pObjects[index];
+ if ( !pObject || pObject->IsStatic() )
+ return;
+
+ float speed = pEvent->collisionSpeed * pEvent->collisionSpeed;
+ int surfaceProps = pEvent->surfaceProps[index];
+
+ void *pGameData = pObject->GetGameData();
+
+ if ( pGameData )
+ {
+ float volume = speed * (1.0f/(320.0f*320.0f)); // max volume at 320 in/s
+
+ if ( volume > 1.0f )
+ volume = 1.0f;
+
+ if ( surfaceProps >= 0 )
+ {
+ g_PhysicsSystem.AddImpactSound( pGameData, pObject, surfaceProps, pEvent->surfaceProps[!index], volume, speed );
+ }
+ }
+}
+
+void CCollisionEvent::PostCollision( vcollisionevent_t *pEvent )
+{
+ CallbackContext callback(this);
+ if ( pEvent->deltaCollisionTime > 0.1f && pEvent->collisionSpeed > 70 )
+ {
+ ObjectSound( 0, pEvent );
+ ObjectSound( 1, pEvent );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCollisionEvent::FrameUpdate( void )
+{
+ UpdateFrictionSounds();
+ UpdateTouchEvents();
+ UpdateFluidEvents();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCollisionEvent::UpdateTouchEvents( void )
+{
+ // Turn on buffering in case new touch events occur during processing
+ bool bOldTouchEvents = m_bBufferTouchEvents;
+ m_bBufferTouchEvents = true;
+ for ( int i = 0; i < m_touchEvents.Count(); i++ )
+ {
+ const touchevent_t &event = m_touchEvents[i];
+ if ( event.touchType == TOUCH_START )
+ {
+ DispatchStartTouch( event.pEntity0, event.pEntity1, event.endPoint, event.normal );
+ }
+ else
+ {
+ // TOUCH_END
+ DispatchEndTouch( event.pEntity0, event.pEntity1 );
+ }
+ }
+
+ m_touchEvents.RemoveAll();
+ m_bBufferTouchEvents = bOldTouchEvents;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEntity0 -
+// *pEntity1 -
+// touchType -
+//-----------------------------------------------------------------------------
+void CCollisionEvent::AddTouchEvent( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, int touchType, const Vector &point, const Vector &normal )
+{
+ if ( !pEntity0 || !pEntity1 )
+ return;
+
+ int index = m_touchEvents.AddToTail();
+ touchevent_t &event = m_touchEvents[index];
+ event.pEntity0 = pEntity0;
+ event.pEntity1 = pEntity1;
+ event.touchType = touchType;
+ event.endPoint = point;
+ event.normal = normal;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pObject1 -
+// *pObject2 -
+// *pTouchData -
+//-----------------------------------------------------------------------------
+void CCollisionEvent::StartTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData )
+{
+ CallbackContext callback(this);
+ C_BaseEntity *pEntity1 = static_cast<C_BaseEntity *>(pObject1->GetGameData());
+ C_BaseEntity *pEntity2 = static_cast<C_BaseEntity *>(pObject2->GetGameData());
+
+ if ( !pEntity1 || !pEntity2 )
+ return;
+
+ Vector endPoint, normal;
+ pTouchData->GetContactPoint( endPoint );
+ pTouchData->GetSurfaceNormal( normal );
+ if ( !m_bBufferTouchEvents )
+ {
+ DispatchStartTouch( pEntity1, pEntity2, endPoint, normal );
+ }
+ else
+ {
+ AddTouchEvent( pEntity1, pEntity2, TOUCH_START, endPoint, normal );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEntity0 -
+// *pEntity1 -
+//-----------------------------------------------------------------------------
+void CCollisionEvent::DispatchStartTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, const Vector &point, const Vector &normal )
+{
+ trace_t trace;
+ memset( &trace, 0, sizeof(trace) );
+ trace.endpos = point;
+ trace.plane.dist = DotProduct( point, normal );
+ trace.plane.normal = normal;
+
+ // NOTE: This sets up the touch list for both entities, no call to pEntity1 is needed
+ pEntity0->PhysicsMarkEntitiesAsTouchingEventDriven( pEntity1, trace );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pObject1 -
+// *pObject2 -
+// *pTouchData -
+//-----------------------------------------------------------------------------
+void CCollisionEvent::EndTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData )
+{
+ CallbackContext callback(this);
+ C_BaseEntity *pEntity1 = static_cast<C_BaseEntity *>(pObject1->GetGameData());
+ C_BaseEntity *pEntity2 = static_cast<C_BaseEntity *>(pObject2->GetGameData());
+
+ if ( !pEntity1 || !pEntity2 )
+ return;
+
+ if ( !m_bBufferTouchEvents )
+ {
+ DispatchEndTouch( pEntity1, pEntity2 );
+ }
+ else
+ {
+ AddTouchEvent( pEntity1, pEntity2, TOUCH_END, vec3_origin, vec3_origin );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEntity0 -
+// *pEntity1 -
+//-----------------------------------------------------------------------------
+void CCollisionEvent::DispatchEndTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1 )
+{
+ // frees the event-driven touchlinks
+ pEntity0->PhysicsNotifyOtherOfUntouch( pEntity0, pEntity1 );
+ pEntity1->PhysicsNotifyOtherOfUntouch( pEntity1, pEntity0 );
+}
+
+void CCollisionEvent::Friction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit, IPhysicsCollisionData *pData )
+{
+ CallbackContext callback(this);
+ if ( energy < 0.05f || surfaceProps < 0 )
+ return;
+
+ //Get our friction information
+ Vector vecPos, vecVel;
+ pData->GetContactPoint( vecPos );
+ pObject->GetVelocityAtPoint( vecPos, &vecVel );
+
+ CBaseEntity *pEntity = reinterpret_cast<CBaseEntity *>(pObject->GetGameData());
+
+ if ( pEntity )
+ {
+ friction_t *pFriction = g_Collisions.FindFriction( pEntity );
+
+ if ( (gpGlobals->maxClients > 1) && pFriction && pFriction->pObject)
+ {
+ // in MP mode play sound and effects once every 500 msecs,
+ // no ongoing updates, takes too much bandwidth
+ if ( (pFriction->flLastEffectTime + 0.5f) > gpGlobals->curtime)
+ {
+ pFriction->flLastUpdateTime = gpGlobals->curtime;
+ return;
+ }
+ }
+
+ PhysFrictionSound( pEntity, pObject, energy, surfaceProps, surfacePropsHit );
+ }
+
+ PhysFrictionEffect( vecPos, vecVel, energy, surfaceProps, surfacePropsHit );
+}
+
+friction_t *CCollisionEvent::FindFriction( CBaseEntity *pObject )
+{
+ friction_t *pFree = NULL;
+
+ for ( int i = 0; i < ARRAYSIZE(m_current); i++ )
+ {
+ if ( !m_current[i].pObject && !pFree )
+ pFree = &m_current[i];
+
+ if ( m_current[i].pObject == pObject )
+ return &m_current[i];
+ }
+
+ return pFree;
+}
+
+void CCollisionEvent::ShutdownFriction( friction_t &friction )
+{
+// Msg( "Scrape Stop %s \n", STRING(friction.pObject->m_iClassname) );
+ CSoundEnvelopeController::GetController().SoundDestroy( friction.patch );
+ friction.patch = NULL;
+ friction.pObject = NULL;
+}
+
+void CCollisionEvent::UpdateFrictionSounds( void )
+{
+ for ( int i = 0; i < ARRAYSIZE(m_current); i++ )
+ {
+ if ( m_current[i].patch )
+ {
+ if ( m_current[i].flLastUpdateTime < (gpGlobals->curtime-0.1f) )
+ {
+ // friction wasn't updated the last 100msec, assume fiction finished
+ ShutdownFriction( m_current[i] );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &matrix -
+// &normal -
+// Output : static int
+//-----------------------------------------------------------------------------
+static int BestAxisMatchingNormal( matrix3x4_t &matrix, const Vector &normal )
+{
+ float bestDot = -1;
+ int best = 0;
+ for ( int i = 0; i < 3; i++ )
+ {
+ Vector tmp;
+ MatrixGetColumn( matrix, i, tmp );
+ float dot = fabs(DotProduct( tmp, normal ));
+ if ( dot > bestDot )
+ {
+ bestDot = dot;
+ best = i;
+ }
+ }
+
+ return best;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pFluid -
+// *pObject -
+// *pEntity -
+//-----------------------------------------------------------------------------
+void PhysicsSplash( IPhysicsFluidController *pFluid, IPhysicsObject *pObject, CBaseEntity *pEntity )
+{
+ //FIXME: For now just allow ragdolls for E3 - jdw
+ if ( ( pObject->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL ) == false )
+ return;
+
+ Vector velocity;
+ pObject->GetVelocity( &velocity, NULL );
+
+ float impactSpeed = velocity.Length();
+
+ if ( impactSpeed < 25.0f )
+ return;
+
+ Vector normal;
+ float dist;
+ pFluid->GetSurfacePlane( &normal, &dist );
+
+ matrix3x4_t &matrix = pEntity->EntityToWorldTransform();
+
+ // Find the local axis that best matches the water surface normal
+ int bestAxis = BestAxisMatchingNormal( matrix, normal );
+
+ Vector tangent, binormal;
+ MatrixGetColumn( matrix, (bestAxis+1)%3, tangent );
+ binormal = CrossProduct( normal, tangent );
+ VectorNormalize( binormal );
+ tangent = CrossProduct( binormal, normal );
+ VectorNormalize( tangent );
+
+ // Now we have a basis tangent to the surface that matches the object's local orientation as well as possible
+ // compute an OBB using this basis
+
+ // Get object extents in basis
+ Vector tanPts[2], binPts[2];
+ tanPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -tangent );
+ tanPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), tangent );
+ binPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -binormal );
+ binPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), binormal );
+
+ // now compute the centered bbox
+ float mins[2], maxs[2], center[2], extents[2];
+ mins[0] = DotProduct( tanPts[0], tangent );
+ maxs[0] = DotProduct( tanPts[1], tangent );
+
+ mins[1] = DotProduct( binPts[0], binormal );
+ maxs[1] = DotProduct( binPts[1], binormal );
+
+ center[0] = 0.5 * (mins[0] + maxs[0]);
+ center[1] = 0.5 * (mins[1] + maxs[1]);
+
+ extents[0] = maxs[0] - center[0];
+ extents[1] = maxs[1] - center[1];
+
+ Vector centerPoint = center[0] * tangent + center[1] * binormal + dist * normal;
+
+ Vector axes[2];
+ axes[0] = (maxs[0] - center[0]) * tangent;
+ axes[1] = (maxs[1] - center[1]) * binormal;
+
+ // visualize OBB hit
+ /*
+ Vector corner1 = centerPoint - axes[0] - axes[1];
+ Vector corner2 = centerPoint + axes[0] - axes[1];
+ Vector corner3 = centerPoint + axes[0] + axes[1];
+ Vector corner4 = centerPoint - axes[0] + axes[1];
+ NDebugOverlay::Line( corner1, corner2, 0, 0, 255, false, 10 );
+ NDebugOverlay::Line( corner2, corner3, 0, 0, 255, false, 10 );
+ NDebugOverlay::Line( corner3, corner4, 0, 0, 255, false, 10 );
+ NDebugOverlay::Line( corner4, corner1, 0, 0, 255, false, 10 );
+ */
+
+ Vector corner[4];
+
+ corner[0] = centerPoint - axes[0] - axes[1];
+ corner[1] = centerPoint + axes[0] - axes[1];
+ corner[2] = centerPoint + axes[0] + axes[1];
+ corner[3] = centerPoint - axes[0] + axes[1];
+
+ int contents = enginetrace->GetPointContents( centerPoint-Vector(0,0,2) );
+
+ bool bInSlime = ( contents & CONTENTS_SLIME ) ? true : false;
+
+ Vector color = vec3_origin;
+ float luminosity = 1.0f;
+
+ if ( !bInSlime )
+ {
+ // Get our lighting information
+ FX_GetSplashLighting( centerPoint + ( normal * 8.0f ), &color, &luminosity );
+ }
+
+ if ( impactSpeed > 150 )
+ {
+ if ( bInSlime )
+ {
+ FX_GunshotSlimeSplash( centerPoint, normal, random->RandomFloat( 8, 10 ) );
+ }
+ else
+ {
+ FX_GunshotSplash( centerPoint, normal, random->RandomFloat( 8, 10 ) );
+ }
+ }
+ else if ( !bInSlime )
+ {
+ FX_WaterRipple( centerPoint, 1.5f, &color, 1.5f, luminosity );
+ }
+
+ int splashes = 4;
+ Vector point;
+
+ for ( int i = 0; i < splashes; i++ )
+ {
+ point = RandomVector( -32.0f, 32.0f );
+ point[2] = 0.0f;
+
+ point += corner[i];
+
+ if ( impactSpeed > 150 )
+ {
+ if ( bInSlime )
+ {
+ FX_GunshotSlimeSplash( centerPoint, normal, random->RandomFloat( 4, 6 ) );
+ }
+ else
+ {
+ FX_GunshotSplash( centerPoint, normal, random->RandomFloat( 4, 6 ) );
+ }
+ }
+ else if ( !bInSlime )
+ {
+ FX_WaterRipple( point, random->RandomFloat( 0.25f, 0.5f ), &color, luminosity, random->RandomFloat( 0.5f, 1.0f ) );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCollisionEvent::UpdateFluidEvents( void )
+{
+ for ( int i = m_fluidEvents.Count()-1; i >= 0; --i )
+ {
+ if ( (gpGlobals->curtime - m_fluidEvents[i].impactTime) > FLUID_TIME_MAX )
+ {
+ m_fluidEvents.FastRemove(i);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEntity -
+// Output : float
+//-----------------------------------------------------------------------------
+float CCollisionEvent::DeltaTimeSinceLastFluid( CBaseEntity *pEntity )
+{
+ for ( int i = m_fluidEvents.Count()-1; i >= 0; --i )
+ {
+ if ( m_fluidEvents[i].hEntity.Get() == pEntity )
+ {
+ return gpGlobals->curtime - m_fluidEvents[i].impactTime;
+ }
+ }
+
+ int index = m_fluidEvents.AddToTail();
+ m_fluidEvents[index].hEntity = pEntity;
+ m_fluidEvents[index].impactTime = gpGlobals->curtime;
+ return FLUID_TIME_MAX;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pObject -
+// *pFluid -
+//-----------------------------------------------------------------------------
+void CCollisionEvent::FluidStartTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid )
+{
+ CallbackContext callback(this);
+ if ( ( pObject == NULL ) || ( pFluid == NULL ) )
+ return;
+
+ CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObject->GetGameData());
+
+ if ( pEntity )
+ {
+ float timeSinceLastCollision = DeltaTimeSinceLastFluid( pEntity );
+
+ if ( timeSinceLastCollision < 0.5f )
+ return;
+
+ PhysicsSplash( pFluid, pObject, pEntity );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pObject -
+// *pFluid -
+//-----------------------------------------------------------------------------
+void CCollisionEvent::FluidEndTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid )
+{
+ CallbackContext callback(this);
+ //FIXME: Do nothing for now
+}
+
+IPhysicsObject *GetWorldPhysObject ( void )
+{
+ return g_PhysWorldObject;
+}
+
+void PhysFrictionSound( CBaseEntity *pEntity, IPhysicsObject *pObject, const char *pSoundName, HSOUNDSCRIPTHANDLE& handle, float flVolume )
+{
+ if ( !pEntity )
+ return;
+
+ // cut out the quiet sounds
+ // UNDONE: Separate threshold for starting a sound vs. continuing?
+ flVolume = clamp( flVolume, 0.0f, 1.0f );
+ if ( flVolume > (1.0f/128.0f) )
+ {
+ friction_t *pFriction = g_Collisions.FindFriction( pEntity );
+ if ( !pFriction )
+ return;
+
+ CSoundParameters params;
+ if ( !CBaseEntity::GetParametersForSound( pSoundName, handle, params, NULL ) )
+ return;
+
+ if ( !pFriction->pObject )
+ {
+ // don't create really quiet scrapes
+ if ( params.volume * flVolume <= 0.1f )
+ return;
+
+ pFriction->pObject = pEntity;
+ CPASAttenuationFilter filter( pEntity, params.soundlevel );
+ int entindex = pEntity->entindex();
+
+ // clientside created entites doesn't have a valid entindex, let 'world' play the sound for them
+ if ( entindex < 0 )
+ entindex = 0;
+
+ pFriction->patch = CSoundEnvelopeController::GetController().SoundCreate(
+ filter, entindex, CHAN_BODY, pSoundName, params.soundlevel );
+ CSoundEnvelopeController::GetController().Play( pFriction->patch, params.volume * flVolume, params.pitch );
+ }
+ else
+ {
+ float pitch = (flVolume * (params.pitchhigh - params.pitchlow)) + params.pitchlow;
+ CSoundEnvelopeController::GetController().SoundChangeVolume( pFriction->patch, params.volume * flVolume, 0.1f );
+ CSoundEnvelopeController::GetController().SoundChangePitch( pFriction->patch, pitch, 0.1f );
+ }
+
+ pFriction->flLastUpdateTime = gpGlobals->curtime;
+ pFriction->flLastEffectTime = gpGlobals->curtime;
+ }
+}
+
+void PhysCleanupFrictionSounds( CBaseEntity *pEntity )
+{
+ friction_t *pFriction = g_Collisions.FindFriction( pEntity );
+ if ( pFriction && pFriction->patch )
+ {
+ g_Collisions.ShutdownFriction( *pFriction );
+ }
+}
+
+float PhysGetNextSimTime()
+{
+ return physenv->GetSimulationTime() + gpGlobals->frametime * cl_phys_timescale.GetFloat();
+}
+
+float PhysGetSyncCreateTime()
+{
+ float nextTime = physenv->GetNextFrameTime();
+ float simTime = PhysGetNextSimTime();
+ if ( nextTime < simTime )
+ {
+ // The next simulation frame begins before the end of this frame
+ // so create physics objects at that time so that they will reach the current
+ // position at curtime. Otherwise the physics object will simulate forward from curtime
+ // and pop into the future a bit at this point of transition
+ return gpGlobals->curtime + nextTime - simTime;
+ }
+ return gpGlobals->curtime;
+}