2023-10-03 17:23:56 +03:00
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
2020-04-22 12:56:21 -04:00
//
// 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"
2023-10-03 17:23:56 +03:00
# include "tier0/miniprofiler.h"
# include "engine/IVDebugOverlay.h"
# ifdef IVP_MINIPROFILER
# include "../ivp/ivp_utility/ivu_miniprofiler.h"
# else
# define PHYS_PROFILE(ID)
# endif
# include "tier1/fmtstr.h"
# include "vphysics/friction.h"
2020-04-22 12:56:21 -04:00
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
// file system interface
extern IFileSystem * filesystem ;
2023-10-03 17:23:56 +03:00
static ConVar cl_phys_timescale ( " cl_phys_timescale " , " 1.0 " , FCVAR_CHEAT , " Sets the scale of time for client-side physics (ragdolls) " ) ;
static ConVar cl_phys_maxticks ( " cl_phys_maxticks " , IsX360 ( ) ? " 2 " : " 0 " , FCVAR_NONE , " Sets the max number of physics ticks allowed for client-side physics (ragdolls) " ) ;
ConVar cl_ragdoll_gravity ( " cl_ragdoll_gravity " , " 386 " , FCVAR_CHEAT , " Sets the gravity client-side ragdolls " ) ;
// blocked entity detecting
static ConVar cl_phys_block_fraction ( " cl_phys_block_fraction " , " 0.1 " ) ;
static ConVar cl_phys_block_dist ( " cl_phys_block_dist " , " 1.0 " ) ;
2020-04-22 12:56:21 -04:00
void PrecachePhysicsSounds ( void ) ;
//FIXME: Replicated from server end, consolidate?
extern IVEngineClient * engine ;
2023-10-03 17:23:56 +03:00
struct penetrateevent_t
{
C_BaseEntity * pEntity0 ;
C_BaseEntity * pEntity1 ;
float startTime ;
float timeStamp ;
} ;
2020-04-22 12:56:21 -04:00
class CCollisionEvent : public IPhysicsCollisionEvent , public IPhysicsCollisionSolver , public IPhysicsObjectEvent
{
public :
2023-10-03 17:23:56 +03:00
CCollisionEvent ( void ) ;
2020-04-22 12:56:21 -04:00
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 ) ;
2023-10-03 17:23:56 +03:00
void UpdatePenetrateEvents ( ) ;
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
void LevelShutdown ( ) ;
2020-04-22 12:56:21 -04:00
// 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 ) ;
2023-10-03 17:23:56 +03:00
bool ShouldFreezeObject ( IPhysicsObject * pObject )
{
// shadow controlled objects are probably server side.
// UNDONE: Explicitly flag server side objects?
if ( pObject - > GetShadowController ( ) )
return false ;
return true ;
}
2020-04-22 12:56:21 -04:00
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 ) ;
2023-10-03 17:23:56 +03:00
void FindOrAddPenetrateEvent ( C_BaseEntity * pEntity0 , C_BaseEntity * pEntity1 ) ;
2020-04-22 12:56:21 -04:00
friction_t m_current [ 8 ] ;
CUtlVector < fluidevent_t > m_fluidEvents ;
CUtlVector < touchevent_t > m_touchEvents ;
2023-10-03 17:23:56 +03:00
CUtlVector < penetrateevent_t > m_penetrateEvents ;
2020-04-22 12:56:21 -04:00
int m_inCallback ;
bool m_bBufferTouchEvents ;
2023-10-03 17:23:56 +03:00
float m_flLastSplashTime ;
2020-04-22 12:56:21 -04:00
} ;
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 ;
}
2023-10-03 17:23:56 +03:00
2020-04-22 12:56:21 -04:00
PhysParseSurfaceData ( physprops , filesystem ) ;
return true ;
}
void PhysicsLevelInit ( void )
{
physenv = physics - > CreateEnvironment ( ) ;
assert ( physenv ) ;
2023-10-03 17:23:56 +03:00
2020-04-22 12:56:21 -04:00
{
MEM_ALLOC_CREDIT ( ) ;
g_EntityCollisionHash = physics - > CreateObjectPairHash ( ) ;
}
// TODO: need to get the right factory function here
//physenv->SetDebugOverlay( appSystemFactory );
2023-10-03 17:23:56 +03:00
physenv - > SetGravity ( Vector ( 0 , 0 , - sv_gravity . GetFloat ( ) ) ) ;
physenv - > SetAlternateGravity ( Vector ( 0 , 0 , - cl_ragdoll_gravity . GetFloat ( ) ) ) ;
// NOTE: Always run client physics at a rate >= 45Hz - helps keep ragdolls stable
const float defaultPhysicsTick = 1.0f / 60.0f ; // 60Hz to stay in sync with x360 framerate of 30Hz
physenv - > SetSimulationTimestep ( defaultPhysicsTick ) ;
2020-04-22 12:56:21 -04:00
physenv - > SetCollisionEventHandler ( & g_Collisions ) ;
physenv - > SetCollisionSolver ( & g_Collisions ) ;
2023-10-03 17:23:56 +03:00
C_World * pWorld = GetClientWorldEntity ( ) ;
g_PhysWorldObject = PhysCreateWorld_Shared ( pWorld , modelinfo - > GetVCollide ( 1 ) , g_PhysDefaultObjectParams ) ;
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
staticpropmgr - > CreateVPhysicsRepresentations ( physenv , & g_SolidSetup , pWorld ) ;
2020-04-22 12:56:21 -04:00
}
void PhysicsReset ( )
{
if ( ! physenv )
return ;
physenv - > ResetSimulationClock ( ) ;
}
2023-10-03 17:23:56 +03:00
static CBaseEntity * FindPhysicsBlocker ( IPhysicsObject * pPhysics )
{
IPhysicsFrictionSnapshot * pSnapshot = pPhysics - > CreateFrictionSnapshot ( ) ;
CBaseEntity * pBlocker = NULL ;
float maxVel = 10.0f ;
while ( pSnapshot - > IsValid ( ) )
{
IPhysicsObject * pOther = pSnapshot - > GetObject ( 1 ) ;
if ( pOther - > IsMoveable ( ) )
{
CBaseEntity * pOtherEntity = static_cast < CBaseEntity * > ( pOther - > GetGameData ( ) ) ;
// dot with this if you have a direction
//Vector normal;
//pSnapshot->GetSurfaceNormal(normal);
float force = pSnapshot - > GetNormalForce ( ) ;
float vel = force * pOther - > GetInvMass ( ) ;
if ( vel > maxVel )
{
pBlocker = pOtherEntity ;
maxVel = vel ;
}
}
pSnapshot - > NextFrictionData ( ) ;
}
pPhysics - > DestroyFrictionSnapshot ( pSnapshot ) ;
return pBlocker ;
}
2020-04-22 12:56:21 -04:00
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 ;
}
2023-10-03 17:23:56 +03:00
if ( ( pObj0 - > GetGameFlags ( ) & FVPHYSICS_PART_OF_RAGDOLL ) & & ( pObj1 - > GetGameFlags ( ) & FVPHYSICS_PART_OF_RAGDOLL ) )
{
return cl_ragdoll_collide . GetBool ( ) ;
}
2020-04-22 12:56:21 -04:00
// Obey collision group rules
Assert ( GameRules ( ) ) ;
if ( GameRules ( ) )
{
if ( ! GameRules ( ) - > ShouldCollide ( pEntity0 - > GetCollisionGroup ( ) , pEntity1 - > GetCollisionGroup ( ) ) )
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 ;
2023-10-03 17:23:56 +03:00
// Anything with custom movement and a shadow controller is assumed to do its own world/AI collisions
if ( movetype0 = = MOVETYPE_CUSTOM & & pObj0 - > GetShadowController ( ) )
{
aiMove0 = true ;
}
if ( movetype1 = = MOVETYPE_CUSTOM & & pObj1 - > GetShadowController ( ) )
{
aiMove1 = true ;
}
2020-04-22 12:56:21 -04:00
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 ) ;
2023-10-03 17:23:56 +03:00
C_BaseEntity * pEntity0 = static_cast < C_BaseEntity * > ( pGameData0 ) ;
C_BaseEntity * pEntity1 = static_cast < C_BaseEntity * > ( pGameData1 ) ;
// solve it yourself here and return 0, or have the default implementation do it
if ( pEntity0 > pEntity1 )
{
// swap sort
CBaseEntity * pTmp = pEntity0 ;
pEntity0 = pEntity1 ;
pEntity1 = pTmp ;
IPhysicsObject * pTmpObj = pObj0 ;
pObj0 = pObj1 ;
pObj1 = pTmpObj ;
}
if ( ! pEntity0 | | ! pEntity1 )
return 1 ;
unsigned short gameFlags0 = pObj0 - > GetGameFlags ( ) ;
unsigned short gameFlags1 = pObj1 - > GetGameFlags ( ) ;
2020-04-22 12:56:21 -04:00
// solve it yourself here and return 0, or have the default implementation do it
if ( pGameData0 = = pGameData1 )
{
2023-10-03 17:23:56 +03:00
if ( gameFlags0 & FVPHYSICS_PART_OF_RAGDOLL )
2020-04-22 12:56:21 -04:00
{
// 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 ;
}
}
}
}
2023-10-03 17:23:56 +03:00
else if ( ( gameFlags0 | gameFlags1 ) & FVPHYSICS_PART_OF_RAGDOLL )
{
// ragdoll penetrating shadow object, just give up for now
if ( pObj0 - > GetShadowController ( ) | | pObj1 - > GetShadowController ( ) )
{
FindOrAddPenetrateEvent ( pEntity0 , pEntity1 ) ;
return true ;
}
}
2020-04-22 12:56:21 -04:00
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 ( )
{
2023-10-03 17:23:56 +03:00
g_Collisions . LevelShutdown ( ) ;
2020-04-22 12:56:21 -04:00
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();
}
2023-10-03 17:23:56 +03:00
# ifdef _LINUX
DLL_IMPORT CLinkedMiniProfiler * g_pPhysicsMiniProfilers ;
# else
CLinkedMiniProfiler * g_pPhysicsMiniProfilers ;
# endif
CLinkedMiniProfiler g_mp_PhysicsSimulate ( " PhysicsSimulate " , & g_pPhysicsMiniProfilers ) ;
CLinkedMiniProfiler g_mp_active_object_count ( " active_object_count " , & g_pPhysicsMiniProfilers ) ;
//ConVar cl_visualize_physics_shadows("cl_visualize_physics_shadows","0");
struct blocklist_t
{
C_BaseEntity * pEntity ;
int firstBlockFrame ;
int lastBlockFrame ;
} ;
static blocklist_t g_BlockList [ 4 ] ;
bool IsBlockedShouldDisableCollisions ( C_BaseEntity * pEntity )
{
int listCount = ARRAYSIZE ( g_BlockList ) ;
int available = - 1 ;
for ( int i = 0 ; i < listCount ; i + + )
{
if ( gpGlobals - > framecount - g_BlockList [ i ] . lastBlockFrame > 4 )
{
available = i ;
g_BlockList [ i ] . pEntity = NULL ;
}
if ( g_BlockList [ i ] . pEntity = = pEntity )
{
available = i ;
break ;
}
}
if ( available )
{
if ( g_BlockList [ available ] . pEntity ! = pEntity )
{
g_BlockList [ available ] . pEntity = pEntity ;
g_BlockList [ available ] . firstBlockFrame = gpGlobals - > framecount ;
}
g_BlockList [ available ] . lastBlockFrame = gpGlobals - > framecount ;
if ( g_BlockList [ available ] . lastBlockFrame - g_BlockList [ available ] . firstBlockFrame > 2 )
return true ;
}
return false ;
}
2020-04-22 12:56:21 -04:00
void CPhysicsSystem : : PhysicsSimulate ( )
{
2023-10-03 17:23:56 +03:00
CMiniProfilerGuard mpg ( & g_mp_PhysicsSimulate ) ;
2020-04-22 12:56:21 -04:00
VPROF_BUDGET ( " CPhysicsSystem::PhysicsSimulate " , VPROF_BUDGETGROUP_PHYSICS ) ;
float frametime = gpGlobals - > frametime ;
if ( physenv )
{
g_Collisions . BufferTouchEvents ( true ) ;
# ifdef _DEBUG
physenv - > DebugCheckContacts ( ) ;
# endif
2023-10-03 17:23:56 +03:00
frametime * = cl_phys_timescale . GetFloat ( ) ;
int maxTicks = cl_phys_maxticks . GetInt ( ) ;
if ( maxTicks )
{
float maxFrameTime = physenv - > GetDeltaFrameTime ( maxTicks ) - 1e-4 f ;
frametime = clamp ( frametime , 0 , maxFrameTime ) ;
}
physenv - > Simulate ( frametime ) ;
2020-04-22 12:56:21 -04:00
int activeCount = physenv - > GetActiveObjectCount ( ) ;
2023-10-03 17:23:56 +03:00
g_mp_active_object_count . Add ( activeCount ) ;
2020-04-22 12:56:21 -04:00
IPhysicsObject * * pActiveList = NULL ;
if ( activeCount )
{
2023-10-03 17:23:56 +03:00
PHYS_PROFILE ( aUpdateActiveObjects )
2020-04-22 12:56:21 -04:00
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 )
{
2023-10-03 17:23:56 +03:00
//const CCollisionProperty *collProp = pEntity->CollisionProp();
//debugoverlay->AddBoxOverlay( collProp->GetCollisionOrigin(), collProp->OBBMins(), collProp->OBBMaxs(), collProp->GetCollisionAngles(), 190, 190, 0, 0, 0.01 );
2020-04-22 12:56:21 -04:00
if ( pEntity - > CollisionProp ( ) - > DoesVPhysicsInvalidateSurroundingBox ( ) )
{
pEntity - > CollisionProp ( ) - > MarkSurroundingBoundsDirty ( ) ;
}
pEntity - > VPhysicsUpdate ( pActiveList [ i ] ) ;
2023-10-03 17:23:56 +03:00
IPhysicsShadowController * pShadow = pActiveList [ i ] - > GetShadowController ( ) ;
if ( pShadow )
{
// active shadow object, check for error
Vector pos , targetPos ;
QAngle rot , targetAngles ;
pShadow - > GetTargetPosition ( & targetPos , & targetAngles ) ;
pActiveList [ i ] - > GetPosition ( & pos , & rot ) ;
Vector delta = targetPos - pos ;
float dist = VectorNormalize ( delta ) ;
bool bBlocked = false ;
if ( dist > cl_phys_block_dist . GetFloat ( ) )
{
Vector vel ;
pActiveList [ i ] - > GetImplicitVelocity ( & vel , NULL ) ;
float proj = DotProduct ( vel , delta ) ;
if ( proj < dist * cl_phys_block_fraction . GetFloat ( ) )
{
bBlocked = true ;
//Msg("%s was blocked %.3f (%.3f proj)!\n", pEntity->GetClassname(), dist, proj );
}
}
Vector targetAxis ;
float deltaTargetAngle ;
RotationDeltaAxisAngle ( rot , targetAngles , targetAxis , deltaTargetAngle ) ;
if ( fabsf ( deltaTargetAngle ) > 0.5f )
{
AngularImpulse angVel ;
pActiveList [ i ] - > GetImplicitVelocity ( NULL , & angVel ) ;
float proj = DotProduct ( angVel , targetAxis ) * Sign ( deltaTargetAngle ) ;
if ( proj < ( fabsf ( deltaTargetAngle ) * cl_phys_block_fraction . GetFloat ( ) ) )
{
bBlocked = true ;
//Msg("%s was rot blocked %.3f proj %.3f!\n", pEntity->GetClassname(), deltaTargetAngle, proj );
}
}
if ( bBlocked )
{
C_BaseEntity * pBlocker = FindPhysicsBlocker ( pActiveList [ i ] ) ;
if ( pBlocker )
{
if ( IsBlockedShouldDisableCollisions ( pEntity ) )
{
PhysDisableEntityCollisions ( pEntity , pBlocker ) ;
pActiveList [ i ] - > RecheckContactPoints ( ) ;
// GetClassname returns a pointer to the same buffer always!
//Msg("%s blocked !", pEntity->GetClassname() ); Msg("by %s\n", pBlocker->GetClassname() );
}
}
}
}
2020-04-22 12:56:21 -04:00
}
}
}
2023-10-03 17:23:56 +03:00
#if 0
if ( cl_visualize_physics_shadows . GetBool ( ) )
{
int entityCount = NUM_ENT_ENTRIES ;
for ( int i = 0 ; i < entityCount ; i + + )
{
IClientEntity * pClientEnt = cl_entitylist - > GetClientEntity ( i ) ;
if ( ! pClientEnt )
continue ;
C_BaseEntity * pEntity = pClientEnt - > GetBaseEntity ( ) ;
if ( ! pEntity )
continue ;
Vector pos ;
QAngle angle ;
IPhysicsObject * pObj = pEntity - > VPhysicsGetObject ( ) ;
if ( ! pObj | | ! pObj - > GetShadowController ( ) )
continue ;
pObj - > GetShadowPosition ( & pos , & angle ) ;
debugoverlay - > AddBoxOverlay ( pos , pEntity - > CollisionProp ( ) - > OBBMins ( ) , pEntity - > CollisionProp ( ) - > OBBMaxs ( ) , angle , 255 , 255 , 0 , 32 , 0 ) ;
char tmp [ 256 ] ;
V_snprintf ( tmp , sizeof ( tmp ) , " %s, (%s) \n " , pEntity - > GetClassname ( ) , VecToString ( angle ) ) ;
debugoverlay - > AddTextOverlay ( pos , 0 , tmp ) ;
}
}
# endif
2020-04-22 12:56:21 -04:00
g_Collisions . BufferTouchEvents ( false ) ;
g_Collisions . FrameUpdate ( ) ;
}
physicssound : : PlayImpactSounds ( m_impactSounds ) ;
}
void PhysicsSimulate ( )
{
g_PhysicsSystem . PhysicsSimulate ( ) ;
}
2023-10-03 17:23:56 +03:00
CCollisionEvent : : CCollisionEvent ( void )
{
m_flLastSplashTime = 0.0f ;
}
2020-04-22 12:56:21 -04:00
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 ( ) ;
2023-10-03 17:23:56 +03:00
UpdatePenetrateEvents ( ) ;
}
void CCollisionEvent : : LevelShutdown ( )
{
m_penetrateEvents . RemoveAll ( ) ;
m_flLastSplashTime = 0.0f ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// 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 ) ;
}
2023-10-03 17:23:56 +03:00
// NOTE: This assumes entity pointers are sorted to simplify search!
void CCollisionEvent : : FindOrAddPenetrateEvent ( C_BaseEntity * pEntity0 , C_BaseEntity * pEntity1 )
{
int count = m_penetrateEvents . Count ( ) ;
for ( int i = 0 ; i < count ; i + + )
{
if ( m_penetrateEvents [ i ] . pEntity0 = = pEntity0 & & m_penetrateEvents [ i ] . pEntity1 = = pEntity1 )
{
m_penetrateEvents [ i ] . timeStamp = gpGlobals - > curtime ;
return ;
}
}
int index = m_penetrateEvents . AddToTail ( ) ;
m_penetrateEvents [ index ] . pEntity0 = pEntity0 ;
m_penetrateEvents [ index ] . pEntity1 = pEntity1 ;
m_penetrateEvents [ index ] . startTime = gpGlobals - > curtime ;
m_penetrateEvents [ index ] . timeStamp = gpGlobals - > curtime ;
}
// NOTE: This assumes entity pointers are sorted to simplify search!
void CCollisionEvent : : UpdatePenetrateEvents ( )
{
const float MAX_PENETRATION_TIME = 3.0f ;
for ( int i = m_penetrateEvents . Count ( ) - 1 ; i > = 0 ; - - i )
{
float timeSincePenetration = gpGlobals - > curtime - m_penetrateEvents [ i ] . timeStamp ;
if ( timeSincePenetration > 0.1f )
{
m_penetrateEvents . FastRemove ( i ) ;
continue ;
}
float timeInPenetration = m_penetrateEvents [ i ] . timeStamp - m_penetrateEvents [ i ] . startTime ;
// it's been too long, just give up and disable collisions
if ( timeInPenetration > MAX_PENETRATION_TIME )
{
PhysDisableEntityCollisions ( m_penetrateEvents [ i ] . pEntity0 , m_penetrateEvents [ i ] . pEntity1 ) ;
m_penetrateEvents . FastRemove ( i ) ;
continue ;
}
}
}
2020-04-22 12:56:21 -04:00
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 )
{
2023-10-03 17:23:56 +03:00
if ( pEntity - > m_bClientSideRagdoll )
return ;
2020-04-22 12:56:21 -04:00
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 ] ;
2023-10-03 17:23:56 +03:00
int contents = enginetrace - > GetPointContents ( centerPoint - Vector ( 0 , 0 , 2 ) , MASK_WATER ) ;
2020-04-22 12:56:21 -04:00
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 ;
2023-10-03 17:23:56 +03:00
# ifdef INFESTED_DLL
// prevent too many splashes spawning at once across different entities
float flGlobalTimeSinceLastSplash = gpGlobals - > curtime - m_flLastSplashTime ;
if ( flGlobalTimeSinceLastSplash < 0.1f )
return ;
# endif
//Msg( "ent %d %s doing splash. delta = %f\n", pEntity->entindex(), pEntity->GetModelName(), timeSinceLastCollision );
2020-04-22 12:56:21 -04:00
PhysicsSplash ( pFluid , pObject , pEntity ) ;
2023-10-03 17:23:56 +03:00
m_flLastSplashTime = gpGlobals - > curtime ;
2020-04-22 12:56:21 -04:00
}
}
//-----------------------------------------------------------------------------
// 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 ;
}
2023-10-03 17:23:56 +03:00
void VPhysicsShadowDataChanged ( bool bCreate , C_BaseEntity * pEntity )
{
// client-side vphysics shadow management
if ( bCreate & & ! pEntity - > VPhysicsGetObject ( ) & & ! ( pEntity - > GetSolidFlags ( ) & FSOLID_NOT_SOLID ) )
{
if ( pEntity - > GetSolid ( ) ! = SOLID_BSP )
{
pEntity - > SetSolid ( SOLID_VPHYSICS ) ;
}
if ( pEntity - > GetSolidFlags ( ) & FSOLID_NOT_MOVEABLE )
{
pEntity - > VPhysicsInitStatic ( ) ;
}
else
{
pEntity - > VPhysicsInitShadow ( false , false ) ;
}
}
else if ( pEntity - > VPhysicsGetObject ( ) & & ! pEntity - > VPhysicsGetObject ( ) - > IsStatic ( ) )
{
float interpTime = pEntity - > GetInterpolationAmount ( LATCH_SIMULATION_VAR ) ;
// this is the client time the network origin will become the entity's render origin
float schedTime = pEntity - > m_flSimulationTime + interpTime ;
// how far is that from now
float deltaTime = schedTime - gpGlobals - > curtime ;
// Compute that time on the client vphysics clock
float physTime = physenv - > GetSimulationTime ( ) + deltaTime + gpGlobals - > frametime ;
// arrival time is relative to the next tick
float arrivalTime = physTime - physenv - > GetNextFrameTime ( ) ;
if ( arrivalTime < 0 )
arrivalTime = 0 ;
pEntity - > VPhysicsGetObject ( ) - > UpdateShadow ( pEntity - > GetNetworkOrigin ( ) , pEntity - > GetNetworkAngles ( ) , false , arrivalTime ) ;
}
}