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

2933 lines
84 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Interface layer for ipion IVP physics.
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //===========================================================================//
  9. #include "cbase.h"
  10. #include "coordsize.h"
  11. #include "entitylist.h"
  12. #include "vcollide_parse.h"
  13. #include "soundenvelope.h"
  14. #include "game.h"
  15. #include "utlvector.h"
  16. #include "init_factory.h"
  17. #include "igamesystem.h"
  18. #include "hierarchy.h"
  19. #include "IEffects.h"
  20. #include "engine/IEngineSound.h"
  21. #include "world.h"
  22. #include "decals.h"
  23. #include "physics_fx.h"
  24. #include "vphysics_sound.h"
  25. #include "vphysics/vehicles.h"
  26. #include "vehicle_sounds.h"
  27. #include "movevars_shared.h"
  28. #include "physics_saverestore.h"
  29. #include "solidsetdefaults.h"
  30. #include "tier0/vprof.h"
  31. #include "engine/IStaticPropMgr.h"
  32. #include "physics_prop_ragdoll.h"
  33. #if HL2_EPISODIC
  34. #include "particle_parse.h"
  35. #endif
  36. #include "vphysics/object_hash.h"
  37. #include "vphysics/collision_set.h"
  38. #include "vphysics/friction.h"
  39. #include "fmtstr.h"
  40. #include "physics_npc_solver.h"
  41. #include "physics_collisionevent.h"
  42. #include "vphysics/performance.h"
  43. #include "positionwatcher.h"
  44. #include "tier1/callqueue.h"
  45. #include "vphysics/constraints.h"
  46. #ifdef PORTAL
  47. #include "portal_physics_collisionevent.h"
  48. #include "physicsshadowclone.h"
  49. #include "PortalSimulation.h"
  50. void PortalPhysFrame( float deltaTime ); //small wrapper for PhysFrame that simulates all 3 environments at once
  51. #endif
  52. void PrecachePhysicsSounds( void );
  53. // memdbgon must be the last include file in a .cpp file!!!
  54. #include "tier0/memdbgon.h"
  55. ConVar phys_speeds( "phys_speeds", "0" );
  56. // defined in phys_constraint
  57. extern IPhysicsConstraintEvent *g_pConstraintEvents;
  58. CEntityList *g_pShadowEntities = NULL;
  59. #ifdef PORTAL
  60. CEntityList *g_pShadowEntities_Main = NULL;
  61. #endif
  62. // local variables
  63. static float g_PhysAverageSimTime;
  64. CCallQueue g_PostSimulationQueue;
  65. // local routines
  66. static IPhysicsObject *PhysCreateWorld( CBaseEntity *pWorld );
  67. static void PhysFrame( float deltaTime );
  68. static bool IsDebris( int collisionGroup );
  69. void TimescaleChanged( IConVar *var, const char *pOldString, float flOldValue )
  70. {
  71. if ( physenv )
  72. {
  73. physenv->ResetSimulationClock();
  74. }
  75. }
  76. ConVar phys_timescale( "phys_timescale", "1", 0, "Scale time for physics", TimescaleChanged );
  77. #if _DEBUG
  78. ConVar phys_dontprintint( "phys_dontprintint", "1", FCVAR_NONE, "Don't print inter-penetration warnings." );
  79. #endif
  80. #ifdef PORTAL
  81. CPortal_CollisionEvent g_Collisions;
  82. #else
  83. CCollisionEvent g_Collisions;
  84. #endif
  85. IPhysicsCollisionSolver * const g_pCollisionSolver = &g_Collisions;
  86. IPhysicsCollisionEvent * const g_pCollisionEventHandler = &g_Collisions;
  87. IPhysicsObjectEvent * const g_pObjectEventHandler = &g_Collisions;
  88. struct vehiclescript_t
  89. {
  90. string_t scriptName;
  91. vehicleparams_t params;
  92. vehiclesounds_t sounds;
  93. };
  94. class CPhysicsHook : public CBaseGameSystemPerFrame
  95. {
  96. public:
  97. virtual const char *Name() { return "CPhysicsHook"; }
  98. virtual bool Init();
  99. virtual void LevelInitPreEntity();
  100. virtual void LevelInitPostEntity();
  101. virtual void LevelShutdownPreEntity();
  102. virtual void LevelShutdownPostEntity();
  103. virtual void FrameUpdatePostEntityThink();
  104. virtual void PreClientUpdate();
  105. bool FindOrAddVehicleScript( const char *pScriptName, vehicleparams_t *pVehicle, vehiclesounds_t *pSounds );
  106. void FlushVehicleScripts()
  107. {
  108. m_vehicleScripts.RemoveAll();
  109. }
  110. bool ShouldSimulate()
  111. {
  112. return (physenv && !m_bPaused) ? true : false;
  113. }
  114. physicssound::soundlist_t m_impactSounds;
  115. CUtlVector<physicssound::breaksound_t> m_breakSounds;
  116. CUtlVector<masscenteroverride_t> m_massCenterOverrides;
  117. CUtlVector<vehiclescript_t> m_vehicleScripts;
  118. float m_impactSoundTime;
  119. bool m_bPaused;
  120. bool m_isFinalTick;
  121. };
  122. CPhysicsHook g_PhysicsHook;
  123. //-----------------------------------------------------------------------------
  124. // Singleton access
  125. //-----------------------------------------------------------------------------
  126. IGameSystem* PhysicsGameSystem()
  127. {
  128. return &g_PhysicsHook;
  129. }
  130. //-----------------------------------------------------------------------------
  131. // Purpose: The physics hook callback implementations
  132. //-----------------------------------------------------------------------------
  133. bool CPhysicsHook::Init( void )
  134. {
  135. factorylist_t factories;
  136. // Get the list of interface factories to extract the physics DLL's factory
  137. FactoryList_Retrieve( factories );
  138. if ( !factories.physicsFactory )
  139. return false;
  140. if ((physics = (IPhysics *)factories.physicsFactory( VPHYSICS_INTERFACE_VERSION, NULL )) == NULL ||
  141. (physcollision = (IPhysicsCollision *)factories.physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL )) == NULL ||
  142. (physprops = (IPhysicsSurfaceProps *)factories.physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL )) == NULL
  143. )
  144. return false;
  145. PhysParseSurfaceData( physprops, filesystem );
  146. m_isFinalTick = true;
  147. m_impactSoundTime = 0;
  148. m_vehicleScripts.EnsureCapacity(4);
  149. return true;
  150. }
  151. // a little debug wrapper to help fix bugs when entity pointers get trashed
  152. #if 0
  153. struct physcheck_t
  154. {
  155. IPhysicsObject *pPhys;
  156. char string[512];
  157. };
  158. CUtlVector< physcheck_t > physCheck;
  159. void PhysCheckAdd( IPhysicsObject *pPhys, const char *pString )
  160. {
  161. physcheck_t tmp;
  162. tmp.pPhys = pPhys;
  163. Q_strncpy( tmp.string, pString ,sizeof(tmp.string));
  164. physCheck.AddToTail( tmp );
  165. }
  166. const char *PhysCheck( IPhysicsObject *pPhys )
  167. {
  168. for ( int i = 0; i < physCheck.Size(); i++ )
  169. {
  170. if ( physCheck[i].pPhys == pPhys )
  171. return physCheck[i].string;
  172. }
  173. return "unknown";
  174. }
  175. #endif
  176. void CPhysicsHook::LevelInitPreEntity()
  177. {
  178. physenv = physics->CreateEnvironment();
  179. physics_performanceparams_t params;
  180. params.Defaults();
  181. params.maxCollisionsPerObjectPerTimestep = 10;
  182. physenv->SetPerformanceSettings( &params );
  183. #ifdef PORTAL
  184. physenv_main = physenv;
  185. #endif
  186. {
  187. g_EntityCollisionHash = physics->CreateObjectPairHash();
  188. }
  189. factorylist_t factories;
  190. FactoryList_Retrieve( factories );
  191. physenv->SetDebugOverlay( factories.engineFactory );
  192. physenv->EnableDeleteQueue( true );
  193. physenv->SetCollisionSolver( &g_Collisions );
  194. physenv->SetCollisionEventHandler( &g_Collisions );
  195. physenv->SetConstraintEventHandler( g_pConstraintEvents );
  196. physenv->EnableConstraintNotify( true ); // callback when an object gets deleted that is attached to a constraint
  197. physenv->SetObjectEventHandler( &g_Collisions );
  198. physenv->SetSimulationTimestep( gpGlobals->interval_per_tick ); // 15 ms per tick
  199. // HL Game gravity, not real-world gravity
  200. physenv->SetGravity( Vector( 0, 0, -GetCurrentGravity() ) );
  201. g_PhysAverageSimTime = 0;
  202. g_PhysWorldObject = PhysCreateWorld( GetWorldEntity() );
  203. g_pShadowEntities = new CEntityList;
  204. #ifdef PORTAL
  205. g_pShadowEntities_Main = g_pShadowEntities;
  206. #endif
  207. PrecachePhysicsSounds();
  208. m_bPaused = true;
  209. }
  210. void CPhysicsHook::LevelInitPostEntity()
  211. {
  212. m_bPaused = false;
  213. }
  214. void CPhysicsHook::LevelShutdownPreEntity()
  215. {
  216. if ( !physenv )
  217. return;
  218. physenv->SetQuickDelete( true );
  219. }
  220. void CPhysicsHook::LevelShutdownPostEntity()
  221. {
  222. if ( !physenv )
  223. return;
  224. g_pPhysSaveRestoreManager->ForgetAllModels();
  225. g_Collisions.LevelShutdown();
  226. physics->DestroyEnvironment( physenv );
  227. physenv = NULL;
  228. physics->DestroyObjectPairHash( g_EntityCollisionHash );
  229. g_EntityCollisionHash = NULL;
  230. physics->DestroyAllCollisionSets();
  231. g_PhysWorldObject = NULL;
  232. delete g_pShadowEntities;
  233. g_pShadowEntities = NULL;
  234. m_impactSounds.RemoveAll();
  235. m_breakSounds.RemoveAll();
  236. m_massCenterOverrides.Purge();
  237. FlushVehicleScripts();
  238. }
  239. bool CPhysicsHook::FindOrAddVehicleScript( const char *pScriptName, vehicleparams_t *pVehicle, vehiclesounds_t *pSounds )
  240. {
  241. bool bLoadedSounds = false;
  242. int index = -1;
  243. for ( int i = 0; i < m_vehicleScripts.Count(); i++ )
  244. {
  245. if ( !Q_stricmp(m_vehicleScripts[i].scriptName.ToCStr(), pScriptName) )
  246. {
  247. index = i;
  248. bLoadedSounds = true;
  249. break;
  250. }
  251. }
  252. if ( index < 0 )
  253. {
  254. byte *pFile = UTIL_LoadFileForMe( pScriptName, NULL );
  255. if ( pFile )
  256. {
  257. // new script, parse it and write to the table
  258. index = m_vehicleScripts.AddToTail();
  259. m_vehicleScripts[index].scriptName = AllocPooledString(pScriptName);
  260. m_vehicleScripts[index].sounds.Init();
  261. IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( (char *)pFile );
  262. while ( !pParse->Finished() )
  263. {
  264. const char *pBlock = pParse->GetCurrentBlockName();
  265. if ( !strcmpi( pBlock, "vehicle" ) )
  266. {
  267. pParse->ParseVehicle( &m_vehicleScripts[index].params, NULL );
  268. }
  269. else if ( !Q_stricmp( pBlock, "vehicle_sounds" ) )
  270. {
  271. bLoadedSounds = true;
  272. CVehicleSoundsParser soundParser;
  273. pParse->ParseCustom( &m_vehicleScripts[index].sounds, &soundParser );
  274. }
  275. else
  276. {
  277. pParse->SkipBlock();
  278. }
  279. }
  280. physcollision->VPhysicsKeyParserDestroy( pParse );
  281. UTIL_FreeFile( pFile );
  282. }
  283. }
  284. if ( index >= 0 )
  285. {
  286. if ( pVehicle )
  287. {
  288. *pVehicle = m_vehicleScripts[index].params;
  289. }
  290. if ( pSounds )
  291. {
  292. // We must pass back valid data here!
  293. if ( bLoadedSounds == false )
  294. return false;
  295. *pSounds = m_vehicleScripts[index].sounds;
  296. }
  297. return true;
  298. }
  299. return false;
  300. }
  301. // called after entities think
  302. void CPhysicsHook::FrameUpdatePostEntityThink( )
  303. {
  304. VPROF_BUDGET( "CPhysicsHook::FrameUpdatePostEntityThink", VPROF_BUDGETGROUP_PHYSICS );
  305. // Tracker 24846: If game is paused, don't simulate vphysics
  306. float interval = ( gpGlobals->frametime > 0.0f ) ? TICK_INTERVAL : 0.0f;
  307. // update the physics simulation, not we don't use gpGlobals->frametime, since that can be 30 msec or 15 msec
  308. // depending on whether IsSimulatingOnAlternateTicks is true or not
  309. if ( CBaseEntity::IsSimulatingOnAlternateTicks() )
  310. {
  311. m_isFinalTick = false;
  312. #ifdef PORTAL //slight detour if we're the portal mod
  313. PortalPhysFrame( interval );
  314. #else
  315. PhysFrame( interval );
  316. #endif
  317. }
  318. m_isFinalTick = true;
  319. #ifdef PORTAL //slight detour if we're the portal mod
  320. PortalPhysFrame( interval );
  321. #else
  322. PhysFrame( interval );
  323. #endif
  324. }
  325. void CPhysicsHook::PreClientUpdate()
  326. {
  327. m_impactSoundTime += gpGlobals->frametime;
  328. if ( m_impactSoundTime > 0.05f )
  329. {
  330. physicssound::PlayImpactSounds( m_impactSounds );
  331. m_impactSoundTime = 0.0f;
  332. physicssound::PlayBreakSounds( m_breakSounds );
  333. }
  334. }
  335. bool PhysIsFinalTick()
  336. {
  337. return g_PhysicsHook.m_isFinalTick;
  338. }
  339. IPhysicsObject *PhysCreateWorld( CBaseEntity *pWorld )
  340. {
  341. staticpropmgr->CreateVPhysicsRepresentations( physenv, &g_SolidSetup, pWorld );
  342. return PhysCreateWorld_Shared( pWorld, modelinfo->GetVCollide(1), g_PhysDefaultObjectParams );
  343. }
  344. // vehicle wheels can only collide with things that can't get stuck in them during game physics
  345. // because they aren't in the game physics world at present
  346. static bool WheelCollidesWith( IPhysicsObject *pObj, CBaseEntity *pEntity )
  347. {
  348. #if defined( INVASION_DLL )
  349. if ( pEntity->GetCollisionGroup() == TFCOLLISION_GROUP_OBJECT )
  350. return false;
  351. #endif
  352. // Cull against interactive debris
  353. if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  354. return false;
  355. // Hit physics ents
  356. if ( pEntity->GetMoveType() == MOVETYPE_PUSH || pEntity->GetMoveType() == MOVETYPE_VPHYSICS || pObj->IsStatic() )
  357. return true;
  358. return false;
  359. }
  360. CCollisionEvent::CCollisionEvent()
  361. {
  362. m_inCallback = 0;
  363. m_bBufferTouchEvents = false;
  364. m_lastTickFrictionError = 0;
  365. }
  366. int CCollisionEvent::ShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 )
  367. #if _DEBUG
  368. {
  369. int x0 = ShouldCollide_2(pObj0, pObj1, pGameData0, pGameData1);
  370. int x1 = ShouldCollide_2(pObj1, pObj0, pGameData1, pGameData0);
  371. Assert(x0==x1);
  372. return x0;
  373. }
  374. int CCollisionEvent::ShouldCollide_2( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 )
  375. #endif
  376. {
  377. CallbackContext check(this);
  378. CBaseEntity *pEntity0 = static_cast<CBaseEntity *>(pGameData0);
  379. CBaseEntity *pEntity1 = static_cast<CBaseEntity *>(pGameData1);
  380. if ( !pEntity0 || !pEntity1 )
  381. return 1;
  382. unsigned short gameFlags0 = pObj0->GetGameFlags();
  383. unsigned short gameFlags1 = pObj1->GetGameFlags();
  384. if ( pEntity0 == pEntity1 )
  385. {
  386. // allow all-or-nothing per-entity disable
  387. if ( (gameFlags0 | gameFlags1) & FVPHYSICS_NO_SELF_COLLISIONS )
  388. return 0;
  389. IPhysicsCollisionSet *pSet = physics->FindCollisionSet( pEntity0->GetModelIndex() );
  390. if ( pSet )
  391. return pSet->ShouldCollide( pObj0->GetGameIndex(), pObj1->GetGameIndex() );
  392. return 1;
  393. }
  394. // objects that are both constrained to the world don't collide with each other
  395. if ( (gameFlags0 & gameFlags1) & FVPHYSICS_CONSTRAINT_STATIC )
  396. {
  397. return 0;
  398. }
  399. // Special collision rules for vehicle wheels
  400. // Their entity collides with stuff using the normal rules, but they
  401. // have different rules than the vehicle body for various reasons.
  402. // sort of a hack because we don't have spheres to represent them in the game
  403. // world for speculative collisions.
  404. if ( pObj0->GetCallbackFlags() & CALLBACK_IS_VEHICLE_WHEEL )
  405. {
  406. if ( !WheelCollidesWith( pObj1, pEntity1 ) )
  407. return false;
  408. }
  409. if ( pObj1->GetCallbackFlags() & CALLBACK_IS_VEHICLE_WHEEL )
  410. {
  411. if ( !WheelCollidesWith( pObj0, pEntity0 ) )
  412. return false;
  413. }
  414. if ( pEntity0->ForceVPhysicsCollide( pEntity1 ) || pEntity1->ForceVPhysicsCollide( pEntity0 ) )
  415. return 1;
  416. if ( pEntity0->edict() && pEntity1->edict() )
  417. {
  418. // don't collide with your owner
  419. if ( pEntity0->GetOwnerEntity() == pEntity1 || pEntity1->GetOwnerEntity() == pEntity0 )
  420. return 0;
  421. }
  422. if ( pEntity0->GetMoveParent() || pEntity1->GetMoveParent() )
  423. {
  424. CBaseEntity *pParent0 = pEntity0->GetRootMoveParent();
  425. CBaseEntity *pParent1 = pEntity1->GetRootMoveParent();
  426. // NOTE: Don't let siblings/parents collide. If you want this behavior, do it
  427. // with constraints, not hierarchy!
  428. if ( pParent0 == pParent1 )
  429. return 0;
  430. if ( g_EntityCollisionHash->IsObjectPairInHash( pParent0, pParent1 ) )
  431. return 0;
  432. IPhysicsObject *p0 = pParent0->VPhysicsGetObject();
  433. IPhysicsObject *p1 = pParent1->VPhysicsGetObject();
  434. if ( p0 && p1 )
  435. {
  436. if ( g_EntityCollisionHash->IsObjectPairInHash( p0, p1 ) )
  437. return 0;
  438. }
  439. }
  440. int solid0 = pEntity0->GetSolid();
  441. int solid1 = pEntity1->GetSolid();
  442. int nSolidFlags0 = pEntity0->GetSolidFlags();
  443. int nSolidFlags1 = pEntity1->GetSolidFlags();
  444. int movetype0 = pEntity0->GetMoveType();
  445. int movetype1 = pEntity1->GetMoveType();
  446. // entities with non-physical move parents or entities with MOVETYPE_PUSH
  447. // are considered as "AI movers". They are unchanged by collision; they exert
  448. // physics forces on the rest of the system.
  449. bool aiMove0 = (movetype0==MOVETYPE_PUSH) ? true : false;
  450. bool aiMove1 = (movetype1==MOVETYPE_PUSH) ? true : false;
  451. if ( pEntity0->GetMoveParent() )
  452. {
  453. // if the object & its parent are both MOVETYPE_VPHYSICS, then this must be a special case
  454. // like a prop_ragdoll_attached
  455. if ( !(movetype0 == MOVETYPE_VPHYSICS && pEntity0->GetRootMoveParent()->GetMoveType() == MOVETYPE_VPHYSICS) )
  456. {
  457. aiMove0 = true;
  458. }
  459. }
  460. if ( pEntity1->GetMoveParent() )
  461. {
  462. // if the object & its parent are both MOVETYPE_VPHYSICS, then this must be a special case.
  463. if ( !(movetype1 == MOVETYPE_VPHYSICS && pEntity1->GetRootMoveParent()->GetMoveType() == MOVETYPE_VPHYSICS) )
  464. {
  465. aiMove1 = true;
  466. }
  467. }
  468. // AI movers don't collide with the world/static/pinned objects or other AI movers
  469. if ( (aiMove0 && !pObj1->IsMoveable()) ||
  470. (aiMove1 && !pObj0->IsMoveable()) ||
  471. (aiMove0 && aiMove1) )
  472. return 0;
  473. // two objects under shadow control should not collide. The AI will figure it out
  474. if ( pObj0->GetShadowController() && pObj1->GetShadowController() )
  475. return 0;
  476. // BRJ 1/24/03
  477. // You can remove the assert if it's problematic; I *believe* this condition
  478. // should be met, but I'm not sure.
  479. //Assert ( (solid0 != SOLID_NONE) && (solid1 != SOLID_NONE) );
  480. if ( (solid0 == SOLID_NONE) || (solid1 == SOLID_NONE) )
  481. return 0;
  482. // not solid doesn't collide with anything
  483. if ( (nSolidFlags0|nSolidFlags1) & FSOLID_NOT_SOLID )
  484. {
  485. // might be a vphysics trigger, collide with everything but "not solid"
  486. if ( pObj0->IsTrigger() && !(nSolidFlags1 & FSOLID_NOT_SOLID) )
  487. return 1;
  488. if ( pObj1->IsTrigger() && !(nSolidFlags0 & FSOLID_NOT_SOLID) )
  489. return 1;
  490. return 0;
  491. }
  492. if ( (nSolidFlags0 & FSOLID_TRIGGER) &&
  493. !(solid1 == SOLID_VPHYSICS || solid1 == SOLID_BSP || movetype1 == MOVETYPE_VPHYSICS) )
  494. return 0;
  495. if ( (nSolidFlags1 & FSOLID_TRIGGER) &&
  496. !(solid0 == SOLID_VPHYSICS || solid0 == SOLID_BSP || movetype0 == MOVETYPE_VPHYSICS) )
  497. return 0;
  498. if ( !g_pGameRules->ShouldCollide( pEntity0->GetCollisionGroup(), pEntity1->GetCollisionGroup() ) )
  499. return 0;
  500. // check contents
  501. if ( !(pObj0->GetContents() & pEntity1->PhysicsSolidMaskForEntity()) || !(pObj1->GetContents() & pEntity0->PhysicsSolidMaskForEntity()) )
  502. return 0;
  503. if ( g_EntityCollisionHash->IsObjectPairInHash( pGameData0, pGameData1 ) )
  504. return 0;
  505. if ( g_EntityCollisionHash->IsObjectPairInHash( pObj0, pObj1 ) )
  506. return 0;
  507. return 1;
  508. }
  509. bool FindMaxContact( IPhysicsObject *pObject, float minForce, IPhysicsObject **pOtherObject, Vector *contactPos, Vector *pForce )
  510. {
  511. float mass = pObject->GetMass();
  512. float maxForce = minForce;
  513. *pOtherObject = NULL;
  514. IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot();
  515. while ( pSnapshot->IsValid() )
  516. {
  517. IPhysicsObject *pOther = pSnapshot->GetObject(1);
  518. if ( pOther->IsMoveable() && pOther->GetMass() > mass )
  519. {
  520. float force = pSnapshot->GetNormalForce();
  521. if ( force > maxForce )
  522. {
  523. *pOtherObject = pOther;
  524. pSnapshot->GetContactPoint( *contactPos );
  525. pSnapshot->GetSurfaceNormal( *pForce );
  526. *pForce *= force;
  527. }
  528. }
  529. pSnapshot->NextFrictionData();
  530. }
  531. pObject->DestroyFrictionSnapshot( pSnapshot );
  532. if ( *pOtherObject )
  533. return true;
  534. return false;
  535. }
  536. bool CCollisionEvent::ShouldFreezeObject( IPhysicsObject *pObject )
  537. {
  538. extern bool PropIsGib(CBaseEntity *pEntity);
  539. // for now, don't apply a per-object limit to ai MOVETYPE_PUSH objects
  540. // NOTE: If this becomes a problem (too many collision checks this tick) we should add a path
  541. // to inform the logic in VPhysicsUpdatePusher() about the limit being applied so
  542. // that it doesn't falsely block the object when it's simply been temporarily frozen
  543. // for performance reasons
  544. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObject->GetGameData());
  545. if ( pEntity )
  546. {
  547. if (pEntity->GetMoveType() == MOVETYPE_PUSH )
  548. return false;
  549. // don't limit vehicle collisions either, limit can make breaking through a pile of breakable
  550. // props very hitchy
  551. if (pEntity->GetServerVehicle() && !(pObject->GetCallbackFlags() & CALLBACK_IS_VEHICLE_WHEEL))
  552. return false;
  553. }
  554. // if we're freezing a debris object, then it's probably due to some kind of solver issue
  555. // usually this is a large object resting on the debris object in question which is not
  556. // very stable.
  557. // After doing the experiment of constraining the dynamic range of mass while solving friction
  558. // contacts, I like the results of this tradeoff better. So damage or remove the debris object
  559. // wherever possible once we hit this case:
  560. if ( IsDebris( pEntity->GetCollisionGroup()) && !pEntity->IsNPC() )
  561. {
  562. IPhysicsObject *pOtherObject = NULL;
  563. Vector contactPos;
  564. Vector force;
  565. // find the contact with the moveable object applying the most contact force
  566. if ( FindMaxContact( pObject, pObject->GetMass() * 10, &pOtherObject, &contactPos, &force ) )
  567. {
  568. CBaseEntity *pOther = static_cast<CBaseEntity *>(pOtherObject->GetGameData());
  569. // this object can take damage, crush it
  570. if ( pEntity->m_takedamage > DAMAGE_EVENTS_ONLY )
  571. {
  572. CTakeDamageInfo dmgInfo( pOther, pOther, force, contactPos, force.Length() * 0.1f, DMG_CRUSH );
  573. PhysCallbackDamage( pEntity, dmgInfo );
  574. }
  575. else
  576. {
  577. // can't be damaged, so do something else:
  578. if ( PropIsGib(pEntity) )
  579. {
  580. // it's always safe to delete gibs, so kill this one to avoid simulation problems
  581. PhysCallbackRemove( pEntity->NetworkProp() );
  582. }
  583. else
  584. {
  585. // not a gib, create a solver:
  586. // UNDONE: Add a property to override this in gameplay critical scenarios?
  587. g_PostSimulationQueue.QueueCall( EntityPhysics_CreateSolver, pOther, pEntity, true, 1.0f );
  588. }
  589. }
  590. }
  591. }
  592. return true;
  593. }
  594. bool CCollisionEvent::ShouldFreezeContacts( IPhysicsObject **pObjectList, int objectCount )
  595. {
  596. if ( m_lastTickFrictionError > gpGlobals->tickcount || m_lastTickFrictionError < (gpGlobals->tickcount-1) )
  597. {
  598. DevWarning("Performance Warning: large friction system (%d objects)!!!\n", objectCount );
  599. #if _DEBUG
  600. for ( int i = 0; i < objectCount; i++ )
  601. {
  602. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObjectList[i]->GetGameData());
  603. pEntity->m_debugOverlays |= OVERLAY_ABSBOX_BIT | OVERLAY_PIVOT_BIT;
  604. }
  605. #endif
  606. }
  607. m_lastTickFrictionError = gpGlobals->tickcount;
  608. return false;
  609. }
  610. // NOTE: these are fully edge triggered events
  611. // called when an object wakes up (starts simulating)
  612. void CCollisionEvent::ObjectWake( IPhysicsObject *pObject )
  613. {
  614. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObject->GetGameData());
  615. if ( pEntity && pEntity->HasDataObjectType( VPHYSICSWATCHER ) )
  616. {
  617. ReportVPhysicsStateChanged( pObject, pEntity, true );
  618. }
  619. }
  620. // called when an object goes to sleep (no longer simulating)
  621. void CCollisionEvent::ObjectSleep( IPhysicsObject *pObject )
  622. {
  623. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObject->GetGameData());
  624. if ( pEntity && pEntity->HasDataObjectType( VPHYSICSWATCHER ) )
  625. {
  626. ReportVPhysicsStateChanged( pObject, pEntity, false );
  627. }
  628. }
  629. bool PhysShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1 )
  630. {
  631. void *pGameData0 = pObj0->GetGameData();
  632. void *pGameData1 = pObj1->GetGameData();
  633. if ( !pGameData0 || !pGameData1 )
  634. return false;
  635. return g_Collisions.ShouldCollide( pObj0, pObj1, pGameData0, pGameData1 ) ? true : false;
  636. }
  637. bool PhysIsInCallback()
  638. {
  639. if ( (physenv && physenv->IsInSimulation()) || g_Collisions.IsInCallback() )
  640. return true;
  641. return false;
  642. }
  643. static void ReportPenetration( CBaseEntity *pEntity, float duration )
  644. {
  645. if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS )
  646. {
  647. if ( g_pDeveloper->GetInt() > 1 )
  648. {
  649. pEntity->m_debugOverlays |= OVERLAY_ABSBOX_BIT;
  650. }
  651. pEntity->AddTimedOverlay( UTIL_VarArgs("VPhysics Penetration Error (%s)!", pEntity->GetDebugName()), duration );
  652. }
  653. }
  654. static bool IsDebris( int collisionGroup )
  655. {
  656. switch ( collisionGroup )
  657. {
  658. case COLLISION_GROUP_DEBRIS:
  659. case COLLISION_GROUP_INTERACTIVE_DEBRIS:
  660. case COLLISION_GROUP_DEBRIS_TRIGGER:
  661. return true;
  662. default:
  663. break;
  664. }
  665. return false;
  666. }
  667. static void UpdateEntityPenetrationFlag( CBaseEntity *pEntity, bool isPenetrating )
  668. {
  669. if ( !pEntity )
  670. return;
  671. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  672. int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  673. for ( int i = 0; i < count; i++ )
  674. {
  675. if ( !pList[i]->IsStatic() )
  676. {
  677. if ( isPenetrating )
  678. {
  679. PhysSetGameFlags( pList[i], FVPHYSICS_PENETRATING );
  680. }
  681. else
  682. {
  683. PhysClearGameFlags( pList[i], FVPHYSICS_PENETRATING );
  684. }
  685. }
  686. }
  687. }
  688. void CCollisionEvent::GetListOfPenetratingEntities( CBaseEntity *pSearch, CUtlVector<CBaseEntity *> &list )
  689. {
  690. for ( int i = m_penetrateEvents.Count()-1; i >= 0; --i )
  691. {
  692. if ( m_penetrateEvents[i].hEntity0 == pSearch && m_penetrateEvents[i].hEntity1.Get() != NULL )
  693. {
  694. list.AddToTail( m_penetrateEvents[i].hEntity1 );
  695. }
  696. else if ( m_penetrateEvents[i].hEntity1 == pSearch && m_penetrateEvents[i].hEntity0.Get() != NULL )
  697. {
  698. list.AddToTail( m_penetrateEvents[i].hEntity0 );
  699. }
  700. }
  701. }
  702. void CCollisionEvent::UpdatePenetrateEvents( void )
  703. {
  704. for ( int i = m_penetrateEvents.Count()-1; i >= 0; --i )
  705. {
  706. CBaseEntity *pEntity0 = m_penetrateEvents[i].hEntity0;
  707. CBaseEntity *pEntity1 = m_penetrateEvents[i].hEntity1;
  708. if ( m_penetrateEvents[i].collisionState == COLLSTATE_TRYDISABLE )
  709. {
  710. if ( pEntity0 && pEntity1 )
  711. {
  712. IPhysicsObject *pObj0 = pEntity0->VPhysicsGetObject();
  713. if ( pObj0 )
  714. {
  715. PhysForceEntityToSleep( pEntity0, pObj0 );
  716. }
  717. IPhysicsObject *pObj1 = pEntity1->VPhysicsGetObject();
  718. if ( pObj1 )
  719. {
  720. PhysForceEntityToSleep( pEntity1, pObj1 );
  721. }
  722. m_penetrateEvents[i].collisionState = COLLSTATE_DISABLED;
  723. continue;
  724. }
  725. // missing entity or object, clear event
  726. }
  727. else if ( m_penetrateEvents[i].collisionState == COLLSTATE_TRYNPCSOLVER )
  728. {
  729. if ( pEntity0 && pEntity1 )
  730. {
  731. CAI_BaseNPC *pNPC = pEntity0->MyNPCPointer();
  732. CBaseEntity *pBlocker = pEntity1;
  733. if ( !pNPC )
  734. {
  735. pNPC = pEntity1->MyNPCPointer();
  736. Assert(pNPC);
  737. pBlocker = pEntity0;
  738. }
  739. NPCPhysics_CreateSolver( pNPC, pBlocker, true, 1.0f );
  740. }
  741. // transferred to solver, clear event
  742. }
  743. else if ( m_penetrateEvents[i].collisionState == COLLSTATE_TRYENTITYSOLVER )
  744. {
  745. if ( pEntity0 && pEntity1 )
  746. {
  747. if ( !IsDebris(pEntity1->GetCollisionGroup()) || pEntity1->GetMoveType() != MOVETYPE_VPHYSICS )
  748. {
  749. CBaseEntity *pTmp = pEntity0;
  750. pEntity0 = pEntity1;
  751. pEntity1 = pTmp;
  752. }
  753. EntityPhysics_CreateSolver( pEntity0, pEntity1, true, 1.0f );
  754. }
  755. // transferred to solver, clear event
  756. }
  757. else if ( gpGlobals->curtime - m_penetrateEvents[i].timeStamp > 1.0 )
  758. {
  759. if ( m_penetrateEvents[i].collisionState == COLLSTATE_DISABLED )
  760. {
  761. if ( pEntity0 && pEntity1 )
  762. {
  763. IPhysicsObject *pObj0 = pEntity0->VPhysicsGetObject();
  764. IPhysicsObject *pObj1 = pEntity1->VPhysicsGetObject();
  765. if ( pObj0 && pObj1 )
  766. {
  767. m_penetrateEvents[i].collisionState = COLLSTATE_ENABLED;
  768. continue;
  769. }
  770. }
  771. }
  772. // haven't penetrated for 1 second, so remove
  773. }
  774. else
  775. {
  776. // recent timestamp, don't remove the event yet
  777. continue;
  778. }
  779. // done, clear event
  780. m_penetrateEvents.FastRemove(i);
  781. UpdateEntityPenetrationFlag( pEntity0, false );
  782. UpdateEntityPenetrationFlag( pEntity1, false );
  783. }
  784. }
  785. penetrateevent_t &CCollisionEvent::FindOrAddPenetrateEvent( CBaseEntity *pEntity0, CBaseEntity *pEntity1 )
  786. {
  787. int index = -1;
  788. for ( int i = m_penetrateEvents.Count()-1; i >= 0; --i )
  789. {
  790. if ( m_penetrateEvents[i].hEntity0.Get() == pEntity0 && m_penetrateEvents[i].hEntity1.Get() == pEntity1 )
  791. {
  792. index = i;
  793. break;
  794. }
  795. }
  796. if ( index < 0 )
  797. {
  798. index = m_penetrateEvents.AddToTail();
  799. penetrateevent_t &event = m_penetrateEvents[index];
  800. event.hEntity0 = pEntity0;
  801. event.hEntity1 = pEntity1;
  802. event.startTime = gpGlobals->curtime;
  803. event.collisionState = COLLSTATE_ENABLED;
  804. UpdateEntityPenetrationFlag( pEntity0, true );
  805. UpdateEntityPenetrationFlag( pEntity1, true );
  806. }
  807. penetrateevent_t &event = m_penetrateEvents[index];
  808. event.timeStamp = gpGlobals->curtime;
  809. return event;
  810. }
  811. static ConVar phys_penetration_error_time( "phys_penetration_error_time", "10", 0, "Controls the duration of vphysics penetration error boxes." );
  812. static bool CanResolvePenetrationWithNPC( CBaseEntity *pEntity, IPhysicsObject *pObject )
  813. {
  814. if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS )
  815. {
  816. // hinged objects won't be able to be pushed out anyway, so don't try the npc solver
  817. if ( !pObject->IsHinged() && !pObject->IsAttachedToConstraint(true) )
  818. {
  819. if ( pObject->IsMoveable() || pEntity->GetServerVehicle() )
  820. return true;
  821. }
  822. }
  823. return false;
  824. }
  825. int CCollisionEvent::ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1, float dt )
  826. {
  827. CallbackContext check(this);
  828. // Pointers to the entity for each physics object
  829. CBaseEntity *pEntity0 = static_cast<CBaseEntity *>(pGameData0);
  830. CBaseEntity *pEntity1 = static_cast<CBaseEntity *>(pGameData1);
  831. // this can get called as entities are being constructed on the other side of a game load or level transition
  832. // Some entities may not be fully constructed, so don't call into their code until the level is running
  833. if ( g_PhysicsHook.m_bPaused )
  834. return true;
  835. // solve it yourself here and return 0, or have the default implementation do it
  836. if ( pEntity0 > pEntity1 )
  837. {
  838. // swap sort
  839. CBaseEntity *pTmp = pEntity0;
  840. pEntity0 = pEntity1;
  841. pEntity1 = pTmp;
  842. IPhysicsObject *pTmpObj = pObj0;
  843. pObj0 = pObj1;
  844. pObj1 = pTmpObj;
  845. }
  846. if ( pEntity0 == pEntity1 )
  847. {
  848. if ( pObj0->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL )
  849. {
  850. DevMsg(2, "Solving ragdoll self penetration! %s (%s) (%d v %d)\n", pObj0->GetName(), pEntity0->GetDebugName(), pObj0->GetGameIndex(), pObj1->GetGameIndex() );
  851. ragdoll_t *pRagdoll = Ragdoll_GetRagdoll( pEntity0 );
  852. pRagdoll->pGroup->SolvePenetration( pObj0, pObj1 );
  853. return false;
  854. }
  855. }
  856. penetrateevent_t &event = FindOrAddPenetrateEvent( pEntity0, pEntity1 );
  857. float eventTime = gpGlobals->curtime - event.startTime;
  858. // NPC vs. physics object. Create a game DLL solver and remove this event
  859. if ( (pEntity0->MyNPCPointer() && CanResolvePenetrationWithNPC(pEntity1, pObj1)) ||
  860. (pEntity1->MyNPCPointer() && CanResolvePenetrationWithNPC(pEntity0, pObj0)) )
  861. {
  862. event.collisionState = COLLSTATE_TRYNPCSOLVER;
  863. }
  864. if ( (IsDebris( pEntity0->GetCollisionGroup() ) && !pObj1->IsStatic()) || (IsDebris( pEntity1->GetCollisionGroup() ) && !pObj0->IsStatic()) )
  865. {
  866. if ( eventTime > 0.5f )
  867. {
  868. //Msg("Debris stuck in non-static!\n");
  869. event.collisionState = COLLSTATE_TRYENTITYSOLVER;
  870. }
  871. }
  872. #if _DEBUG
  873. if ( phys_dontprintint.GetBool() == false )
  874. {
  875. const char *pName1 = STRING(pEntity0->GetModelName());
  876. const char *pName2 = STRING(pEntity1->GetModelName());
  877. if ( pEntity0 == pEntity1 )
  878. {
  879. int index0 = physcollision->CollideIndex( pObj0->GetCollide() );
  880. int index1 = physcollision->CollideIndex( pObj1->GetCollide() );
  881. DevMsg(1, "***Inter-penetration on %s (%d & %d) (%.0f, %.0f)\n", pName1?pName1:"(null)", index0, index1, gpGlobals->curtime, eventTime );
  882. }
  883. else
  884. {
  885. DevMsg(1, "***Inter-penetration between %s(%s) AND %s(%s) (%.0f, %.0f)\n", pName1?pName1:"(null)", pEntity0->GetDebugName(), pName2?pName2:"(null)", pEntity1->GetDebugName(), gpGlobals->curtime, eventTime );
  886. }
  887. }
  888. #endif
  889. if ( eventTime > 3 )
  890. {
  891. // don't report penetrations on ragdolls with themselves, or outside of developer mode
  892. if ( g_pDeveloper->GetInt() && pEntity0 != pEntity1 )
  893. {
  894. ReportPenetration( pEntity0, phys_penetration_error_time.GetFloat() );
  895. ReportPenetration( pEntity1, phys_penetration_error_time.GetFloat() );
  896. }
  897. event.startTime = gpGlobals->curtime;
  898. // don't put players or game physics controlled objects to sleep
  899. if ( !pEntity0->IsPlayer() && !pEntity1->IsPlayer() && !pObj0->GetShadowController() && !pObj1->GetShadowController() )
  900. {
  901. // two objects have been stuck for more than 3 seconds, try disabling simulation
  902. event.collisionState = COLLSTATE_TRYDISABLE;
  903. return false;
  904. }
  905. }
  906. return true;
  907. }
  908. void CCollisionEvent::FluidStartTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid )
  909. {
  910. CallbackContext check(this);
  911. if ( ( pObject == NULL ) || ( pFluid == NULL ) )
  912. return;
  913. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObject->GetGameData());
  914. if ( !pEntity )
  915. return;
  916. pEntity->AddEFlags( EFL_TOUCHING_FLUID );
  917. pEntity->OnEntityEvent( ENTITY_EVENT_WATER_TOUCH, (void*)pFluid->GetContents() );
  918. float timeSinceLastCollision = DeltaTimeSinceLastFluid( pEntity );
  919. if ( timeSinceLastCollision < 0.5f )
  920. return;
  921. // UNDONE: Use this for splash logic instead?
  922. // UNDONE: Use angular term too - push splashes in rotAxs cross normal direction?
  923. Vector normal;
  924. float dist;
  925. pFluid->GetSurfacePlane( &normal, &dist );
  926. Vector vel;
  927. AngularImpulse angVel;
  928. pObject->GetVelocity( &vel, &angVel );
  929. Vector unitVel = vel;
  930. VectorNormalize( unitVel );
  931. // normal points out of the surface, we want the direction that points in
  932. float dragScale = pFluid->GetDensity() * physenv->GetSimulationTimestep();
  933. normal = -normal;
  934. float linearScale = 0.5f * DotProduct( unitVel, normal ) * pObject->CalculateLinearDrag( normal ) * dragScale;
  935. linearScale = clamp( linearScale, 0.0f, 1.0f );
  936. vel *= -linearScale;
  937. // UNDONE: Figure out how much of the surface area has crossed the water surface and scale angScale by that
  938. // For now assume 25%
  939. Vector rotAxis = angVel;
  940. VectorNormalize(rotAxis);
  941. float angScale = 0.25f * pObject->CalculateAngularDrag( angVel ) * dragScale;
  942. angScale = clamp( angScale, 0.0f, 1.0f );
  943. angVel *= -angScale;
  944. // compute the splash before we modify the velocity
  945. PhysicsSplash( pFluid, pObject, pEntity );
  946. // now damp out some motion toward the surface
  947. pObject->AddVelocity( &vel, &angVel );
  948. }
  949. void CCollisionEvent::FluidEndTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid )
  950. {
  951. CallbackContext check(this);
  952. if ( ( pObject == NULL ) || ( pFluid == NULL ) )
  953. return;
  954. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObject->GetGameData());
  955. if ( !pEntity )
  956. return;
  957. float timeSinceLastCollision = DeltaTimeSinceLastFluid( pEntity );
  958. if ( timeSinceLastCollision >= 0.5f )
  959. {
  960. PhysicsSplash( pFluid, pObject, pEntity );
  961. }
  962. pEntity->RemoveEFlags( EFL_TOUCHING_FLUID );
  963. pEntity->OnEntityEvent( ENTITY_EVENT_WATER_UNTOUCH, (void*)pFluid->GetContents() );
  964. }
  965. class CSkipKeys : public IVPhysicsKeyHandler
  966. {
  967. public:
  968. virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue ) {}
  969. virtual void SetDefaults( void *pData ) {}
  970. };
  971. void PhysSolidOverride( solid_t &solid, string_t overrideScript )
  972. {
  973. if ( overrideScript != NULL_STRING)
  974. {
  975. // parser destroys this data
  976. bool collisions = solid.params.enableCollisions;
  977. char pTmpString[4096];
  978. // write a header for a solid_t
  979. Q_strncpy( pTmpString, "solid { ", sizeof(pTmpString) );
  980. // suck out the comma delimited tokens and turn them into quoted key/values
  981. char szToken[256];
  982. const char *pStr = nexttoken(szToken, STRING(overrideScript), ',');
  983. while ( szToken[0] != 0 )
  984. {
  985. Q_strncat( pTmpString, "\"", sizeof(pTmpString), COPY_ALL_CHARACTERS );
  986. Q_strncat( pTmpString, szToken, sizeof(pTmpString), COPY_ALL_CHARACTERS );
  987. Q_strncat( pTmpString, "\" ", sizeof(pTmpString), COPY_ALL_CHARACTERS );
  988. pStr = nexttoken(szToken, pStr, ',');
  989. }
  990. // terminate the script
  991. Q_strncat( pTmpString, "}", sizeof(pTmpString), COPY_ALL_CHARACTERS );
  992. // parse that sucker
  993. IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pTmpString );
  994. CSkipKeys tmp;
  995. pParse->ParseSolid( &solid, &tmp );
  996. physcollision->VPhysicsKeyParserDestroy( pParse );
  997. // parser destroys this data
  998. solid.params.enableCollisions = collisions;
  999. }
  1000. }
  1001. void PhysSetMassCenterOverride( masscenteroverride_t &override )
  1002. {
  1003. if ( override.entityName != NULL_STRING )
  1004. {
  1005. g_PhysicsHook.m_massCenterOverrides.AddToTail( override );
  1006. }
  1007. }
  1008. // NOTE: This will remove the entry from the list as well
  1009. int PhysGetMassCenterOverrideIndex( string_t name )
  1010. {
  1011. if ( name != NULL_STRING && g_PhysicsHook.m_massCenterOverrides.Count() )
  1012. {
  1013. for ( int i = 0; i < g_PhysicsHook.m_massCenterOverrides.Count(); i++ )
  1014. {
  1015. if ( g_PhysicsHook.m_massCenterOverrides[i].entityName == name )
  1016. {
  1017. return i;
  1018. }
  1019. }
  1020. }
  1021. return -1;
  1022. }
  1023. void PhysGetMassCenterOverride( CBaseEntity *pEntity, vcollide_t *pCollide, solid_t &solidOut )
  1024. {
  1025. int index = PhysGetMassCenterOverrideIndex( pEntity->GetEntityName() );
  1026. if ( index >= 0 )
  1027. {
  1028. masscenteroverride_t &override = g_PhysicsHook.m_massCenterOverrides[index];
  1029. Vector massCenterWS = override.center;
  1030. switch ( override.alignType )
  1031. {
  1032. case masscenteroverride_t::ALIGN_POINT:
  1033. VectorITransform( massCenterWS, pEntity->EntityToWorldTransform(), solidOut.massCenterOverride );
  1034. break;
  1035. case masscenteroverride_t::ALIGN_AXIS:
  1036. {
  1037. Vector massCenterLocal, defaultMassCenterWS;
  1038. physcollision->CollideGetMassCenter( pCollide->solids[solidOut.index], &massCenterLocal );
  1039. VectorTransform( massCenterLocal, pEntity->EntityToWorldTransform(), defaultMassCenterWS );
  1040. massCenterWS += override.axis *
  1041. ( DotProduct(defaultMassCenterWS,override.axis) - DotProduct( override.axis, override.center ) );
  1042. VectorITransform( massCenterWS, pEntity->EntityToWorldTransform(), solidOut.massCenterOverride );
  1043. }
  1044. break;
  1045. }
  1046. g_PhysicsHook.m_massCenterOverrides.FastRemove( index );
  1047. if ( solidOut.massCenterOverride.Length() > DIST_EPSILON )
  1048. {
  1049. solidOut.params.massCenterOverride = &solidOut.massCenterOverride;
  1050. }
  1051. }
  1052. }
  1053. float PhysGetEntityMass( CBaseEntity *pEntity )
  1054. {
  1055. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1056. int physCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  1057. float otherMass = 0;
  1058. for ( int i = 0; i < physCount; i++ )
  1059. {
  1060. otherMass += pList[i]->GetMass();
  1061. }
  1062. return otherMass;
  1063. }
  1064. typedef void (*EntityCallbackFunction) ( CBaseEntity *pEntity );
  1065. void IterateActivePhysicsEntities( EntityCallbackFunction func )
  1066. {
  1067. int activeCount = physenv->GetActiveObjectCount();
  1068. IPhysicsObject **pActiveList = NULL;
  1069. if ( activeCount )
  1070. {
  1071. pActiveList = (IPhysicsObject **)stackalloc( sizeof(IPhysicsObject *)*activeCount );
  1072. physenv->GetActiveObjects( pActiveList );
  1073. for ( int i = 0; i < activeCount; i++ )
  1074. {
  1075. CBaseEntity *pEntity = reinterpret_cast<CBaseEntity *>(pActiveList[i]->GetGameData());
  1076. if ( pEntity )
  1077. {
  1078. func( pEntity );
  1079. }
  1080. }
  1081. }
  1082. }
  1083. static void CallbackHighlight( CBaseEntity *pEntity )
  1084. {
  1085. pEntity->m_debugOverlays |= OVERLAY_ABSBOX_BIT | OVERLAY_PIVOT_BIT;
  1086. }
  1087. static void CallbackReport( CBaseEntity *pEntity )
  1088. {
  1089. const char *pName = STRING(pEntity->GetEntityName());
  1090. if ( !Q_strlen(pName) )
  1091. {
  1092. pName = STRING(pEntity->GetModelName());
  1093. }
  1094. Msg( "%s - %s\n", pEntity->GetClassname(), pName );
  1095. }
  1096. CON_COMMAND(physics_highlight_active, "Turns on the absbox for all active physics objects")
  1097. {
  1098. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1099. return;
  1100. IterateActivePhysicsEntities( CallbackHighlight );
  1101. }
  1102. CON_COMMAND(physics_report_active, "Lists all active physics objects")
  1103. {
  1104. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1105. return;
  1106. IterateActivePhysicsEntities( CallbackReport );
  1107. }
  1108. CON_COMMAND_F(surfaceprop, "Reports the surface properties at the cursor", FCVAR_CHEAT )
  1109. {
  1110. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1111. return;
  1112. CBasePlayer *pPlayer = UTIL_GetCommandClient();
  1113. trace_t tr;
  1114. Vector forward;
  1115. pPlayer->EyeVectors( &forward );
  1116. UTIL_TraceLine(pPlayer->EyePosition(), pPlayer->EyePosition() + forward * MAX_COORD_RANGE,
  1117. MASK_SHOT_HULL|CONTENTS_GRATE|CONTENTS_DEBRIS, pPlayer, COLLISION_GROUP_NONE, &tr );
  1118. if ( tr.DidHit() )
  1119. {
  1120. const model_t *pModel = modelinfo->GetModel( tr.m_pEnt->GetModelIndex() );
  1121. const char *pModelName = STRING(tr.m_pEnt->GetModelName());
  1122. if ( tr.DidHitWorld() && tr.hitbox > 0 )
  1123. {
  1124. ICollideable *pCollide = staticpropmgr->GetStaticPropByIndex( tr.hitbox-1 );
  1125. pModel = pCollide->GetCollisionModel();
  1126. pModelName = modelinfo->GetModelName( pModel );
  1127. }
  1128. CFmtStr modelStuff;
  1129. if ( pModel )
  1130. {
  1131. modelStuff.sprintf("%s.%s ", modelinfo->IsTranslucent( pModel ) ? "Translucent" : "Opaque",
  1132. modelinfo->IsTranslucentTwoPass( pModel ) ? " Two-pass." : "" );
  1133. }
  1134. // Calculate distance to surface that was hit
  1135. Vector vecVelocity = tr.startpos - tr.endpos;
  1136. int length = vecVelocity.Length();
  1137. Msg("Hit surface \"%s\" (entity %s, model \"%s\" %s), texture \"%s\"\n", physprops->GetPropName( tr.surface.surfaceProps ), tr.m_pEnt->GetClassname(), pModelName, modelStuff.Access(), tr.surface.name);
  1138. Msg("Distance to surface: %d\n", length );
  1139. }
  1140. }
  1141. static void OutputVPhysicsDebugInfo( CBaseEntity *pEntity )
  1142. {
  1143. if ( pEntity )
  1144. {
  1145. Msg("Entity %s (%s) %s Collision Group %d\n", pEntity->GetClassname(), pEntity->GetDebugName(), pEntity->IsNavIgnored() ? "NAV IGNORE" : "", pEntity->GetCollisionGroup() );
  1146. CUtlVector<CBaseEntity *> list;
  1147. g_Collisions.GetListOfPenetratingEntities( pEntity, list );
  1148. for ( int i = 0; i < list.Count(); i++ )
  1149. {
  1150. Msg(" penetration with entity %s (%s)\n", list[i]->GetDebugName(), STRING(list[i]->GetModelName()) );
  1151. }
  1152. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1153. int physCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  1154. if ( physCount )
  1155. {
  1156. if ( physCount > 1 )
  1157. {
  1158. for ( int i = 0; i < physCount; i++ )
  1159. {
  1160. Msg("Object %d (of %d) =========================\n", i+1, physCount );
  1161. pList[i]->OutputDebugInfo();
  1162. }
  1163. }
  1164. else
  1165. {
  1166. pList[0]->OutputDebugInfo();
  1167. }
  1168. }
  1169. }
  1170. }
  1171. class CConstraintFloodEntry
  1172. {
  1173. public:
  1174. CConstraintFloodEntry() : isMarked(false), isConstraint(false) {}
  1175. CUtlVector<CBaseEntity *> linkList;
  1176. bool isMarked;
  1177. bool isConstraint;
  1178. };
  1179. class CConstraintFloodList
  1180. {
  1181. public:
  1182. CConstraintFloodList()
  1183. {
  1184. SetDefLessFunc( m_list );
  1185. m_list.EnsureCapacity(64);
  1186. m_entryList.EnsureCapacity(64);
  1187. }
  1188. bool IsWorldEntity( CBaseEntity *pEnt )
  1189. {
  1190. if ( pEnt->edict() )
  1191. return pEnt->IsWorld();
  1192. return false;
  1193. }
  1194. void AddLink( CBaseEntity *pEntity, CBaseEntity *pLink, bool bIsConstraint )
  1195. {
  1196. if ( !pEntity || !pLink || IsWorldEntity(pEntity) || IsWorldEntity(pLink) )
  1197. return;
  1198. int listIndex = m_list.Find(pEntity);
  1199. if ( listIndex == m_list.InvalidIndex() )
  1200. {
  1201. int entryIndex = m_entryList.AddToTail();
  1202. m_entryList[entryIndex].isConstraint = bIsConstraint;
  1203. listIndex = m_list.Insert( pEntity, entryIndex );
  1204. }
  1205. int entryIndex = m_list.Element(listIndex);
  1206. CConstraintFloodEntry &entry = m_entryList.Element(entryIndex);
  1207. Assert( entry.isConstraint == bIsConstraint );
  1208. if ( entry.linkList.Find(pLink) < 0 )
  1209. {
  1210. entry.linkList.AddToTail( pLink );
  1211. }
  1212. }
  1213. void BuildGraphFromEntity( CBaseEntity *pEntity, CUtlVector<CBaseEntity *> &constraintList )
  1214. {
  1215. int listIndex = m_list.Find(pEntity);
  1216. if ( listIndex != m_list.InvalidIndex() )
  1217. {
  1218. int entryIndex = m_list.Element(listIndex);
  1219. CConstraintFloodEntry &entry = m_entryList.Element(entryIndex);
  1220. if ( !entry.isMarked )
  1221. {
  1222. if ( entry.isConstraint )
  1223. {
  1224. Assert( constraintList.Find(pEntity) < 0);
  1225. constraintList.AddToTail( pEntity );
  1226. }
  1227. entry.isMarked = true;
  1228. for ( int i = 0; i < entry.linkList.Count(); i++ )
  1229. {
  1230. // now recursively traverse the graph from here
  1231. BuildGraphFromEntity( entry.linkList[i], constraintList );
  1232. }
  1233. }
  1234. }
  1235. }
  1236. CUtlMap<CBaseEntity *, int> m_list;
  1237. CUtlVector<CConstraintFloodEntry> m_entryList;
  1238. };
  1239. // traverses the graph of attachments (currently supports springs & constraints) starting at an entity
  1240. // Then turns on debug info for each link in the graph (springs/constraints are links)
  1241. static void DebugConstraints( CBaseEntity *pEntity )
  1242. {
  1243. extern bool GetSpringAttachments( CBaseEntity *pEntity, CBaseEntity *pAttach[2], IPhysicsObject *pAttachVPhysics[2] );
  1244. extern bool GetConstraintAttachments( CBaseEntity *pEntity, CBaseEntity *pAttach[2], IPhysicsObject *pAttachVPhysics[2] );
  1245. extern void DebugConstraint(CBaseEntity *pEntity);
  1246. if ( !pEntity )
  1247. return;
  1248. CBaseEntity *pAttach[2];
  1249. IPhysicsObject *pAttachVPhysics[2];
  1250. CConstraintFloodList list;
  1251. for ( CBaseEntity *pList = gEntList.FirstEnt(); pList != NULL; pList = gEntList.NextEnt(pList) )
  1252. {
  1253. if ( GetConstraintAttachments(pList, pAttach, pAttachVPhysics) || GetSpringAttachments(pList, pAttach, pAttachVPhysics) )
  1254. {
  1255. list.AddLink( pList, pAttach[0], true );
  1256. list.AddLink( pList, pAttach[1], true );
  1257. list.AddLink( pAttach[0], pList, false );
  1258. list.AddLink( pAttach[1], pList, false );
  1259. }
  1260. }
  1261. CUtlVector<CBaseEntity *> constraints;
  1262. list.BuildGraphFromEntity( pEntity, constraints );
  1263. for ( int i = 0; i < constraints.Count(); i++ )
  1264. {
  1265. if ( !GetConstraintAttachments(constraints[i], pAttach, pAttachVPhysics) )
  1266. {
  1267. GetSpringAttachments(constraints[i], pAttach, pAttachVPhysics);
  1268. }
  1269. const char *pName0 = "world";
  1270. const char *pName1 = "world";
  1271. const char *pModel0 = "";
  1272. const char *pModel1 = "";
  1273. int index0 = 0;
  1274. int index1 = 0;
  1275. if ( pAttach[0] )
  1276. {
  1277. pName0 = pAttach[0]->GetClassname();
  1278. pModel0 = STRING(pAttach[0]->GetModelName());
  1279. index0 = pAttachVPhysics[0]->GetGameIndex();
  1280. }
  1281. if ( pAttach[1] )
  1282. {
  1283. pName1 = pAttach[1]->GetClassname();
  1284. pModel1 = STRING(pAttach[1]->GetModelName());
  1285. index1 = pAttachVPhysics[1]->GetGameIndex();
  1286. }
  1287. Msg("**********************\n%s connects %s(%s:%d) to %s(%s:%d)\n", constraints[i]->GetClassname(), pName0, pModel0, index0, pName1, pModel1, index1 );
  1288. DebugConstraint(constraints[i]);
  1289. constraints[i]->m_debugOverlays |= OVERLAY_BBOX_BIT | OVERLAY_TEXT_BIT;
  1290. }
  1291. }
  1292. static void MarkVPhysicsDebug( CBaseEntity *pEntity )
  1293. {
  1294. if ( pEntity )
  1295. {
  1296. IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject();
  1297. if ( pPhysics )
  1298. {
  1299. unsigned short callbacks = pPhysics->GetCallbackFlags();
  1300. callbacks ^= CALLBACK_MARKED_FOR_TEST;
  1301. pPhysics->SetCallbackFlags( callbacks );
  1302. }
  1303. }
  1304. }
  1305. void PhysicsCommand( const CCommand &args, void (*func)( CBaseEntity *pEntity ) )
  1306. {
  1307. if ( args.ArgC() < 2 )
  1308. {
  1309. CBasePlayer *pPlayer = UTIL_GetCommandClient();
  1310. trace_t tr;
  1311. Vector forward;
  1312. pPlayer->EyeVectors( &forward );
  1313. UTIL_TraceLine(pPlayer->EyePosition(), pPlayer->EyePosition() + forward * MAX_COORD_RANGE,
  1314. MASK_SHOT_HULL|CONTENTS_GRATE|CONTENTS_DEBRIS, pPlayer, COLLISION_GROUP_NONE, &tr );
  1315. if ( tr.DidHit() )
  1316. {
  1317. func( tr.m_pEnt );
  1318. }
  1319. }
  1320. else
  1321. {
  1322. CBaseEntity *pEnt = NULL;
  1323. while ( ( pEnt = gEntList.FindEntityGeneric( pEnt, args[1] ) ) != NULL )
  1324. {
  1325. func( pEnt );
  1326. }
  1327. }
  1328. }
  1329. CON_COMMAND(physics_constraints, "Highlights constraint system graph for an entity")
  1330. {
  1331. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1332. return;
  1333. PhysicsCommand( args, DebugConstraints );
  1334. }
  1335. CON_COMMAND(physics_debug_entity, "Dumps debug info for an entity")
  1336. {
  1337. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1338. return;
  1339. PhysicsCommand( args, OutputVPhysicsDebugInfo );
  1340. }
  1341. CON_COMMAND(physics_select, "Dumps debug info for an entity")
  1342. {
  1343. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1344. return;
  1345. PhysicsCommand( args, MarkVPhysicsDebug );
  1346. }
  1347. CON_COMMAND( physics_budget, "Times the cost of each active object" )
  1348. {
  1349. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1350. return;
  1351. int activeCount = physenv->GetActiveObjectCount();
  1352. IPhysicsObject **pActiveList = NULL;
  1353. CUtlVector<CBaseEntity *> ents;
  1354. if ( activeCount )
  1355. {
  1356. int i;
  1357. pActiveList = (IPhysicsObject **)stackalloc( sizeof(IPhysicsObject *)*activeCount );
  1358. physenv->GetActiveObjects( pActiveList );
  1359. for ( i = 0; i < activeCount; i++ )
  1360. {
  1361. CBaseEntity *pEntity = reinterpret_cast<CBaseEntity *>(pActiveList[i]->GetGameData());
  1362. if ( pEntity )
  1363. {
  1364. int index = -1;
  1365. for ( int j = 0; j < ents.Count(); j++ )
  1366. {
  1367. if ( pEntity == ents[j] )
  1368. {
  1369. index = j;
  1370. break;
  1371. }
  1372. }
  1373. if ( index >= 0 )
  1374. continue;
  1375. ents.AddToTail( pEntity );
  1376. }
  1377. }
  1378. stackfree( pActiveList );
  1379. if ( !ents.Count() )
  1380. return;
  1381. CUtlVector<float> times;
  1382. float totalTime = 0.f;
  1383. g_Collisions.BufferTouchEvents( true );
  1384. float full = engine->Time();
  1385. physenv->Simulate( gpGlobals->interval_per_tick );
  1386. full = engine->Time() - full;
  1387. float lastTime = full;
  1388. times.SetSize( ents.Count() );
  1389. // NOTE: This is just a heuristic. Attempt to estimate cost by putting each object to sleep in turn.
  1390. // note that simulation may wake the objects again and some costs scale with sets of objects/constraints/etc
  1391. // so these are only generally useful for broad questions, not real metrics!
  1392. for ( i = 0; i < ents.Count(); i++ )
  1393. {
  1394. for ( int j = 0; j < i; j++ )
  1395. {
  1396. PhysForceEntityToSleep( ents[j], ents[j]->VPhysicsGetObject() );
  1397. }
  1398. float start = engine->Time();
  1399. physenv->Simulate( gpGlobals->interval_per_tick );
  1400. float end = engine->Time();
  1401. float elapsed = end - start;
  1402. float avgTime = lastTime - elapsed;
  1403. times[i] = clamp( avgTime, 0.00001f, 1.0f );
  1404. totalTime += times[i];
  1405. lastTime = elapsed;
  1406. }
  1407. totalTime = MAX( totalTime, 0.001 );
  1408. for ( i = 0; i < ents.Count(); i++ )
  1409. {
  1410. float fraction = times[i] / totalTime;
  1411. Msg( "%s (%s): %.3fms (%.3f%%) @ %s\n", ents[i]->GetClassname(), ents[i]->GetDebugName(), fraction * totalTime * 1000.0f, fraction * 100.0f, VecToString(ents[i]->GetAbsOrigin()) );
  1412. }
  1413. g_Collisions.BufferTouchEvents( false );
  1414. }
  1415. }
  1416. #ifdef PORTAL
  1417. ConVar sv_fullsyncclones("sv_fullsyncclones", "1", FCVAR_CHEAT );
  1418. void PortalPhysFrame( float deltaTime ) //small wrapper for PhysFrame that simulates all environments at once
  1419. {
  1420. CPortalSimulator::PrePhysFrame();
  1421. if( sv_fullsyncclones.GetBool() )
  1422. CPhysicsShadowClone::FullSyncAllClones();
  1423. g_Collisions.BufferTouchEvents( true );
  1424. PhysFrame( deltaTime );
  1425. g_Collisions.PortalPostSimulationFrame();
  1426. g_Collisions.BufferTouchEvents( false );
  1427. g_Collisions.FrameUpdate();
  1428. CPortalSimulator::PostPhysFrame();
  1429. }
  1430. #endif
  1431. // Advance physics by time (in seconds)
  1432. void PhysFrame( float deltaTime )
  1433. {
  1434. static int lastObjectCount = 0;
  1435. entitem_t *pItem;
  1436. if ( !g_PhysicsHook.ShouldSimulate() )
  1437. return;
  1438. // Trap interrupts and clock changes
  1439. if ( deltaTime > 1.0f || deltaTime < 0.0f )
  1440. {
  1441. deltaTime = 0;
  1442. Msg( "Reset physics clock\n" );
  1443. }
  1444. else if ( deltaTime > 0.1f ) // limit incoming time to 100ms
  1445. {
  1446. deltaTime = 0.1f;
  1447. }
  1448. float simRealTime = 0;
  1449. deltaTime *= phys_timescale.GetFloat();
  1450. // !!!HACKHACK -- hard limit scaled time to avoid spending too much time in here
  1451. // Limit to 100 ms
  1452. if ( deltaTime > 0.100f )
  1453. deltaTime = 0.100f;
  1454. bool bProfile = phys_speeds.GetBool();
  1455. if ( bProfile )
  1456. {
  1457. simRealTime = engine->Time();
  1458. }
  1459. #ifdef _DEBUG
  1460. physenv->DebugCheckContacts();
  1461. #endif
  1462. #ifndef PORTAL //instead of wrapping 1 simulation with this, portal needs to wrap 3
  1463. g_Collisions.BufferTouchEvents( true );
  1464. #endif
  1465. physenv->Simulate( deltaTime );
  1466. int activeCount = physenv->GetActiveObjectCount();
  1467. IPhysicsObject **pActiveList = NULL;
  1468. if ( activeCount )
  1469. {
  1470. pActiveList = (IPhysicsObject **)stackalloc( sizeof(IPhysicsObject *)*activeCount );
  1471. physenv->GetActiveObjects( pActiveList );
  1472. for ( int i = 0; i < activeCount; i++ )
  1473. {
  1474. CBaseEntity *pEntity = reinterpret_cast<CBaseEntity *>(pActiveList[i]->GetGameData());
  1475. if ( pEntity )
  1476. {
  1477. if ( pEntity->CollisionProp()->DoesVPhysicsInvalidateSurroundingBox() )
  1478. {
  1479. pEntity->CollisionProp()->MarkSurroundingBoundsDirty();
  1480. }
  1481. pEntity->VPhysicsUpdate( pActiveList[i] );
  1482. }
  1483. }
  1484. stackfree( pActiveList );
  1485. }
  1486. for ( pItem = g_pShadowEntities->m_pItemList; pItem; pItem = pItem->pNext )
  1487. {
  1488. CBaseEntity *pEntity = pItem->hEnt.Get();
  1489. if ( !pEntity )
  1490. {
  1491. Msg( "Dangling pointer to physics entity!!!\n" );
  1492. continue;
  1493. }
  1494. IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject();
  1495. // apply updates
  1496. if ( pPhysics && !pPhysics->IsAsleep() )
  1497. {
  1498. pEntity->VPhysicsShadowUpdate( pPhysics );
  1499. }
  1500. }
  1501. if ( bProfile )
  1502. {
  1503. simRealTime = engine->Time() - simRealTime;
  1504. if ( simRealTime < 0 )
  1505. simRealTime = 0;
  1506. g_PhysAverageSimTime *= 0.8;
  1507. g_PhysAverageSimTime += (simRealTime * 0.2);
  1508. if ( lastObjectCount != 0 || activeCount != 0 )
  1509. {
  1510. Msg( "Physics: %3d objects, %4.1fms / AVG: %4.1fms\n", activeCount, simRealTime * 1000, g_PhysAverageSimTime * 1000 );
  1511. }
  1512. lastObjectCount = activeCount;
  1513. }
  1514. #ifndef PORTAL //instead of wrapping 1 simulation with this, portal needs to wrap 3
  1515. g_Collisions.BufferTouchEvents( false );
  1516. g_Collisions.FrameUpdate();
  1517. #endif
  1518. }
  1519. void PhysAddShadow( CBaseEntity *pEntity )
  1520. {
  1521. g_pShadowEntities->AddEntity( pEntity );
  1522. }
  1523. void PhysRemoveShadow( CBaseEntity *pEntity )
  1524. {
  1525. g_pShadowEntities->DeleteEntity( pEntity );
  1526. }
  1527. bool PhysHasShadow( CBaseEntity *pEntity )
  1528. {
  1529. EHANDLE hTestEnt = pEntity;
  1530. entitem_t *pCurrent = g_pShadowEntities->m_pItemList;
  1531. while( pCurrent )
  1532. {
  1533. if( pCurrent->hEnt == hTestEnt )
  1534. {
  1535. return true;
  1536. }
  1537. pCurrent = pCurrent->pNext;
  1538. }
  1539. return false;
  1540. }
  1541. void PhysEnableFloating( IPhysicsObject *pObject, bool bEnable )
  1542. {
  1543. if ( pObject != NULL )
  1544. {
  1545. unsigned short flags = pObject->GetCallbackFlags();
  1546. if ( bEnable )
  1547. {
  1548. flags |= CALLBACK_DO_FLUID_SIMULATION;
  1549. }
  1550. else
  1551. {
  1552. flags &= ~CALLBACK_DO_FLUID_SIMULATION;
  1553. }
  1554. pObject->SetCallbackFlags( flags );
  1555. }
  1556. }
  1557. //-----------------------------------------------------------------------------
  1558. // CollisionEvent system
  1559. //-----------------------------------------------------------------------------
  1560. // NOTE: PreCollision/PostCollision ALWAYS come in matched pairs!!!
  1561. void CCollisionEvent::PreCollision( vcollisionevent_t *pEvent )
  1562. {
  1563. CallbackContext check(this);
  1564. m_gameEvent.Init( pEvent );
  1565. // gather the pre-collision data that the game needs to track
  1566. for ( int i = 0; i < 2; i++ )
  1567. {
  1568. IPhysicsObject *pObject = pEvent->pObjects[i];
  1569. if ( pObject )
  1570. {
  1571. if ( pObject->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  1572. {
  1573. CBaseEntity *pOtherEntity = reinterpret_cast<CBaseEntity *>(pEvent->pObjects[!i]->GetGameData());
  1574. if ( pOtherEntity && !pOtherEntity->IsPlayer() )
  1575. {
  1576. Vector velocity;
  1577. AngularImpulse angVel;
  1578. // HACKHACK: If we totally clear this out, then Havok will think the objects
  1579. // are penetrating and generate forces to separate them
  1580. // so make it fairly small and have a tiny collision instead.
  1581. pObject->GetVelocity( &velocity, &angVel );
  1582. float len = VectorNormalize(velocity);
  1583. len = MAX( len, 10 );
  1584. velocity *= len;
  1585. len = VectorNormalize(angVel);
  1586. len = MAX( len, 1 );
  1587. angVel *= len;
  1588. pObject->SetVelocity( &velocity, &angVel );
  1589. }
  1590. }
  1591. pObject->GetVelocity( &m_gameEvent.preVelocity[i], &m_gameEvent.preAngularVelocity[i] );
  1592. }
  1593. }
  1594. }
  1595. void CCollisionEvent::PostCollision( vcollisionevent_t *pEvent )
  1596. {
  1597. CallbackContext check(this);
  1598. bool isShadow[2] = {false,false};
  1599. int i;
  1600. for ( i = 0; i < 2; i++ )
  1601. {
  1602. IPhysicsObject *pObject = pEvent->pObjects[i];
  1603. if ( pObject )
  1604. {
  1605. CBaseEntity *pEntity = reinterpret_cast<CBaseEntity *>(pObject->GetGameData());
  1606. if ( !pEntity )
  1607. return;
  1608. // UNDONE: This is here to trap crashes due to NULLing out the game data on delete
  1609. m_gameEvent.pEntities[i] = pEntity;
  1610. unsigned int flags = pObject->GetCallbackFlags();
  1611. pObject->GetVelocity( &m_gameEvent.postVelocity[i], NULL );
  1612. if ( flags & CALLBACK_SHADOW_COLLISION )
  1613. {
  1614. isShadow[i] = true;
  1615. }
  1616. // Shouldn't get impacts with triggers
  1617. Assert( !pObject->IsTrigger() );
  1618. }
  1619. }
  1620. // copy off the post-collision variable data
  1621. m_gameEvent.collisionSpeed = pEvent->collisionSpeed;
  1622. m_gameEvent.pInternalData = pEvent->pInternalData;
  1623. // special case for hitting self, only make one non-shadow call
  1624. if ( m_gameEvent.pEntities[0] == m_gameEvent.pEntities[1] )
  1625. {
  1626. if ( pEvent->isCollision && m_gameEvent.pEntities[0] )
  1627. {
  1628. m_gameEvent.pEntities[0]->VPhysicsCollision( 0, &m_gameEvent );
  1629. }
  1630. return;
  1631. }
  1632. if ( isShadow[0] && isShadow[1] )
  1633. {
  1634. pEvent->isCollision = false;
  1635. }
  1636. for ( i = 0; i < 2; i++ )
  1637. {
  1638. if ( pEvent->isCollision )
  1639. {
  1640. m_gameEvent.pEntities[i]->VPhysicsCollision( i, &m_gameEvent );
  1641. }
  1642. if ( pEvent->isShadowCollision && isShadow[i] )
  1643. {
  1644. m_gameEvent.pEntities[i]->VPhysicsShadowCollision( i, &m_gameEvent );
  1645. }
  1646. }
  1647. }
  1648. void PhysForceEntityToSleep( CBaseEntity *pEntity, IPhysicsObject *pObject )
  1649. {
  1650. // UNDONE: Check to see if the object is touching the player first?
  1651. // Might get the player stuck?
  1652. if ( !pObject || !pObject->IsMoveable() )
  1653. return;
  1654. DevMsg(2, "Putting entity to sleep: %s\n", pEntity->GetClassname() );
  1655. MEM_ALLOC_CREDIT();
  1656. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1657. int physCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  1658. for ( int i = 0; i < physCount; i++ )
  1659. {
  1660. PhysForceClearVelocity( pList[i] );
  1661. pList[i]->Sleep();
  1662. }
  1663. }
  1664. void CCollisionEvent::Friction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit, IPhysicsCollisionData *pData )
  1665. {
  1666. CallbackContext check(this);
  1667. //Get our friction information
  1668. Vector vecPos, vecVel;
  1669. pData->GetContactPoint( vecPos );
  1670. pObject->GetVelocityAtPoint( vecPos, &vecVel );
  1671. CBaseEntity *pEntity = reinterpret_cast<CBaseEntity *>(pObject->GetGameData());
  1672. if ( pEntity )
  1673. {
  1674. friction_t *pFriction = g_Collisions.FindFriction( pEntity );
  1675. if ( pFriction && pFriction->pObject)
  1676. {
  1677. // in MP mode play sound and effects once every 500 msecs,
  1678. // no ongoing updates, takes too much bandwidth
  1679. if ( (pFriction->flLastEffectTime + 0.5f) > gpGlobals->curtime)
  1680. {
  1681. pFriction->flLastUpdateTime = gpGlobals->curtime;
  1682. return;
  1683. }
  1684. }
  1685. pEntity->VPhysicsFriction( pObject, energy, surfaceProps, surfacePropsHit );
  1686. }
  1687. PhysFrictionEffect( vecPos, vecVel, energy, surfaceProps, surfacePropsHit );
  1688. }
  1689. friction_t *CCollisionEvent::FindFriction( CBaseEntity *pObject )
  1690. {
  1691. friction_t *pFree = NULL;
  1692. for ( int i = 0; i < ARRAYSIZE(m_current); i++ )
  1693. {
  1694. if ( !m_current[i].pObject && !pFree )
  1695. pFree = &m_current[i];
  1696. if ( m_current[i].pObject == pObject )
  1697. return &m_current[i];
  1698. }
  1699. return pFree;
  1700. }
  1701. void CCollisionEvent::ShutdownFriction( friction_t &friction )
  1702. {
  1703. // Msg( "Scrape Stop %s \n", STRING(friction.pObject->m_iClassname) );
  1704. CSoundEnvelopeController::GetController().SoundDestroy( friction.patch );
  1705. friction.patch = NULL;
  1706. friction.pObject = NULL;
  1707. }
  1708. void CCollisionEvent::UpdateRemoveObjects()
  1709. {
  1710. Assert(!PhysIsInCallback());
  1711. for ( int i = 0 ; i < m_removeObjects.Count(); i++ )
  1712. {
  1713. UTIL_Remove(m_removeObjects[i]);
  1714. }
  1715. m_removeObjects.RemoveAll();
  1716. }
  1717. void CCollisionEvent::PostSimulationFrame()
  1718. {
  1719. UpdateDamageEvents();
  1720. g_PostSimulationQueue.CallQueued();
  1721. UpdateRemoveObjects();
  1722. }
  1723. void CCollisionEvent::FlushQueuedOperations()
  1724. {
  1725. int loopCount = 0;
  1726. while ( loopCount < 20 )
  1727. {
  1728. int count = m_triggerEvents.Count() + m_touchEvents.Count() + m_damageEvents.Count() + m_removeObjects.Count() + g_PostSimulationQueue.Count();
  1729. if ( !count )
  1730. break;
  1731. // testing, if this assert fires it proves we've fixed the crash
  1732. // after that the assert + warning can safely be removed
  1733. Assert(0);
  1734. Warning("Physics queue not empty, error!\n");
  1735. loopCount++;
  1736. UpdateTouchEvents();
  1737. UpdateDamageEvents();
  1738. g_PostSimulationQueue.CallQueued();
  1739. UpdateRemoveObjects();
  1740. }
  1741. }
  1742. void CCollisionEvent::FrameUpdate( void )
  1743. {
  1744. UpdateFrictionSounds();
  1745. UpdateTouchEvents();
  1746. UpdatePenetrateEvents();
  1747. UpdateFluidEvents();
  1748. UpdateDamageEvents(); // if there was no PSI in physics, we'll still need to do some of these because collisions are solved in between PSIs
  1749. g_PostSimulationQueue.CallQueued();
  1750. UpdateRemoveObjects();
  1751. // There are some queued operations that must complete each frame, iterate until these are done
  1752. FlushQueuedOperations();
  1753. }
  1754. // the delete list is getting flushed, clean up ours
  1755. void PhysOnCleanupDeleteList()
  1756. {
  1757. g_Collisions.FlushQueuedOperations();
  1758. if ( physenv )
  1759. {
  1760. physenv->CleanupDeleteList();
  1761. }
  1762. }
  1763. void CCollisionEvent::UpdateFluidEvents( void )
  1764. {
  1765. for ( int i = m_fluidEvents.Count()-1; i >= 0; --i )
  1766. {
  1767. if ( (gpGlobals->curtime - m_fluidEvents[i].impactTime) > FLUID_TIME_MAX )
  1768. {
  1769. m_fluidEvents.FastRemove(i);
  1770. }
  1771. }
  1772. }
  1773. float CCollisionEvent::DeltaTimeSinceLastFluid( CBaseEntity *pEntity )
  1774. {
  1775. for ( int i = m_fluidEvents.Count()-1; i >= 0; --i )
  1776. {
  1777. if ( m_fluidEvents[i].hEntity.Get() == pEntity )
  1778. {
  1779. return gpGlobals->curtime - m_fluidEvents[i].impactTime;
  1780. }
  1781. }
  1782. int index = m_fluidEvents.AddToTail();
  1783. m_fluidEvents[index].hEntity = pEntity;
  1784. m_fluidEvents[index].impactTime = gpGlobals->curtime;
  1785. return FLUID_TIME_MAX;
  1786. }
  1787. void CCollisionEvent::UpdateFrictionSounds( void )
  1788. {
  1789. for ( int i = 0; i < ARRAYSIZE(m_current); i++ )
  1790. {
  1791. if ( m_current[i].patch )
  1792. {
  1793. if ( m_current[i].flLastUpdateTime < (gpGlobals->curtime-0.1f) )
  1794. {
  1795. // friction wasn't updated the last 100msec, assume fiction finished
  1796. ShutdownFriction( m_current[i] );
  1797. }
  1798. }
  1799. }
  1800. }
  1801. void CCollisionEvent::DispatchStartTouch( CBaseEntity *pEntity0, CBaseEntity *pEntity1, const Vector &point, const Vector &normal )
  1802. {
  1803. trace_t trace;
  1804. memset( &trace, 0, sizeof(trace) );
  1805. trace.endpos = point;
  1806. trace.plane.dist = DotProduct( point, normal );
  1807. trace.plane.normal = normal;
  1808. // NOTE: This sets up the touch list for both entities, no call to pEntity1 is needed
  1809. pEntity0->PhysicsMarkEntitiesAsTouchingEventDriven( pEntity1, trace );
  1810. }
  1811. void CCollisionEvent::DispatchEndTouch( CBaseEntity *pEntity0, CBaseEntity *pEntity1 )
  1812. {
  1813. // frees the event-driven touchlinks
  1814. pEntity0->PhysicsNotifyOtherOfUntouch( pEntity0, pEntity1 );
  1815. pEntity1->PhysicsNotifyOtherOfUntouch( pEntity1, pEntity0 );
  1816. }
  1817. void CCollisionEvent::UpdateTouchEvents( void )
  1818. {
  1819. int i;
  1820. // Turn on buffering in case new touch events occur during processing
  1821. bool bOldTouchEvents = m_bBufferTouchEvents;
  1822. m_bBufferTouchEvents = true;
  1823. for ( i = 0; i < m_touchEvents.Count(); i++ )
  1824. {
  1825. const touchevent_t &event = m_touchEvents[i];
  1826. if ( event.touchType == TOUCH_START )
  1827. {
  1828. DispatchStartTouch( event.pEntity0, event.pEntity1, event.endPoint, event.normal );
  1829. }
  1830. else
  1831. {
  1832. // TOUCH_END
  1833. DispatchEndTouch( event.pEntity0, event.pEntity1 );
  1834. }
  1835. }
  1836. m_touchEvents.RemoveAll();
  1837. for ( i = 0; i < m_triggerEvents.Count(); i++ )
  1838. {
  1839. m_currentTriggerEvent = m_triggerEvents[i];
  1840. if ( m_currentTriggerEvent.bStart )
  1841. {
  1842. m_currentTriggerEvent.pTriggerEntity->StartTouch( m_currentTriggerEvent.pEntity );
  1843. }
  1844. else
  1845. {
  1846. m_currentTriggerEvent.pTriggerEntity->EndTouch( m_currentTriggerEvent.pEntity );
  1847. }
  1848. }
  1849. m_triggerEvents.RemoveAll();
  1850. m_currentTriggerEvent.Clear();
  1851. m_bBufferTouchEvents = bOldTouchEvents;
  1852. }
  1853. void CCollisionEvent::UpdateDamageEvents( void )
  1854. {
  1855. for ( int i = 0; i < m_damageEvents.Count(); i++ )
  1856. {
  1857. damageevent_t &event = m_damageEvents[i];
  1858. // Track changes in the entity's life state
  1859. int iEntBits = event.pEntity->IsAlive() ? 0x0001 : 0;
  1860. iEntBits |= event.pEntity->IsMarkedForDeletion() ? 0x0002 : 0;
  1861. iEntBits |= (event.pEntity->GetSolidFlags() & FSOLID_NOT_SOLID) ? 0x0004 : 0;
  1862. #if 0
  1863. // Go ahead and compute the current static stress when hit by a large object (with a force high enough to do damage).
  1864. // That way you die from the impact rather than the stress of the object resting on you whenever possible.
  1865. // This makes the damage effects cleaner.
  1866. if ( event.pInflictorPhysics && event.pInflictorPhysics->GetMass() > VPHYSICS_LARGE_OBJECT_MASS )
  1867. {
  1868. CBaseCombatCharacter *pCombat = event.pEntity->MyCombatCharacterPointer();
  1869. if ( pCombat )
  1870. {
  1871. vphysics_objectstress_t stressOut;
  1872. event.info.AddDamage( pCombat->CalculatePhysicsStressDamage( &stressOut, pCombat->VPhysicsGetObject() ) );
  1873. }
  1874. }
  1875. #endif
  1876. event.pEntity->TakeDamage( event.info );
  1877. int iEntBits2 = event.pEntity->IsAlive() ? 0x0001 : 0;
  1878. iEntBits2 |= event.pEntity->IsMarkedForDeletion() ? 0x0002 : 0;
  1879. iEntBits2 |= (event.pEntity->GetSolidFlags() & FSOLID_NOT_SOLID) ? 0x0004 : 0;
  1880. if ( event.bRestoreVelocity && iEntBits != iEntBits2 )
  1881. {
  1882. // UNDONE: Use ratio of masses to blend in a little of the collision response?
  1883. // UNDONE: Damage for future events is already computed - it would be nice to
  1884. // go back and recompute it now that the values have
  1885. // been adjusted
  1886. RestoreDamageInflictorState( event.pInflictorPhysics );
  1887. }
  1888. }
  1889. m_damageEvents.RemoveAll();
  1890. m_damageInflictors.RemoveAll();
  1891. }
  1892. void CCollisionEvent::RestoreDamageInflictorState( int inflictorStateIndex, float velocityBlend )
  1893. {
  1894. inflictorstate_t &state = m_damageInflictors[inflictorStateIndex];
  1895. if ( state.restored )
  1896. return;
  1897. // so we only restore this guy once
  1898. state.restored = true;
  1899. if ( velocityBlend > 0 )
  1900. {
  1901. Vector velocity;
  1902. AngularImpulse angVel;
  1903. state.pInflictorPhysics->GetVelocity( &velocity, &angVel );
  1904. state.savedVelocity = state.savedVelocity*velocityBlend + velocity*(1-velocityBlend);
  1905. state.savedAngularVelocity = state.savedAngularVelocity*velocityBlend + angVel*(1-velocityBlend);
  1906. state.pInflictorPhysics->SetVelocity( &state.savedVelocity, &state.savedAngularVelocity );
  1907. }
  1908. if ( state.nextIndex >= 0 )
  1909. {
  1910. RestoreDamageInflictorState( state.nextIndex, velocityBlend );
  1911. }
  1912. }
  1913. void CCollisionEvent::RestoreDamageInflictorState( IPhysicsObject *pInflictor )
  1914. {
  1915. if ( !pInflictor )
  1916. return;
  1917. int index = FindDamageInflictor( pInflictor );
  1918. if ( index >= 0 )
  1919. {
  1920. inflictorstate_t &state = m_damageInflictors[index];
  1921. if ( !state.restored )
  1922. {
  1923. float velocityBlend = 1.0;
  1924. float inflictorMass = state.pInflictorPhysics->GetMass();
  1925. if ( inflictorMass < VPHYSICS_LARGE_OBJECT_MASS && !(state.pInflictorPhysics->GetGameFlags() & FVPHYSICS_DMG_SLICE) )
  1926. {
  1927. float otherMass = state.otherMassMax > 0 ? state.otherMassMax : 1;
  1928. float massRatio = inflictorMass / otherMass;
  1929. massRatio = clamp( massRatio, 0.1f, 10.0f );
  1930. if ( massRatio < 1 )
  1931. {
  1932. velocityBlend = RemapVal( massRatio, 0.1, 1, 0, 0.5 );
  1933. }
  1934. else
  1935. {
  1936. velocityBlend = RemapVal( massRatio, 1.0, 10, 0.5, 1 );
  1937. }
  1938. }
  1939. RestoreDamageInflictorState( index, velocityBlend );
  1940. }
  1941. }
  1942. }
  1943. bool CCollisionEvent::GetInflictorVelocity( IPhysicsObject *pInflictor, Vector &velocity, AngularImpulse &angVelocity )
  1944. {
  1945. int index = FindDamageInflictor( pInflictor );
  1946. if ( index >= 0 )
  1947. {
  1948. inflictorstate_t &state = m_damageInflictors[index];
  1949. velocity = state.savedVelocity;
  1950. angVelocity = state.savedAngularVelocity;
  1951. return true;
  1952. }
  1953. return false;
  1954. }
  1955. bool PhysGetDamageInflictorVelocityStartOfFrame( IPhysicsObject *pInflictor, Vector &velocity, AngularImpulse &angVelocity )
  1956. {
  1957. return g_Collisions.GetInflictorVelocity( pInflictor, velocity, angVelocity );
  1958. }
  1959. void CCollisionEvent::AddTouchEvent( CBaseEntity *pEntity0, CBaseEntity *pEntity1, int touchType, const Vector &point, const Vector &normal )
  1960. {
  1961. if ( !pEntity0 || !pEntity1 )
  1962. return;
  1963. int index = m_touchEvents.AddToTail();
  1964. touchevent_t &event = m_touchEvents[index];
  1965. event.pEntity0 = pEntity0;
  1966. event.pEntity1 = pEntity1;
  1967. event.touchType = touchType;
  1968. event.endPoint = point;
  1969. event.normal = normal;
  1970. }
  1971. void CCollisionEvent::AddDamageEvent( CBaseEntity *pEntity, const CTakeDamageInfo &info, IPhysicsObject *pInflictorPhysics, bool bRestoreVelocity, const Vector &savedVel, const AngularImpulse &savedAngVel )
  1972. {
  1973. if ( pEntity->IsMarkedForDeletion() )
  1974. return;
  1975. int iTimeBasedDamage = g_pGameRules->Damage_GetTimeBased();
  1976. if ( !( info.GetDamageType() & (DMG_BURN | DMG_DROWN | iTimeBasedDamage | DMG_PREVENT_PHYSICS_FORCE) ) )
  1977. {
  1978. Assert( info.GetDamageForce() != vec3_origin && info.GetDamagePosition() != vec3_origin );
  1979. }
  1980. int index = m_damageEvents.AddToTail();
  1981. damageevent_t &event = m_damageEvents[index];
  1982. event.pEntity = pEntity;
  1983. event.info = info;
  1984. event.pInflictorPhysics = pInflictorPhysics;
  1985. event.bRestoreVelocity = bRestoreVelocity;
  1986. if ( !pInflictorPhysics || !pInflictorPhysics->IsMoveable() )
  1987. {
  1988. event.bRestoreVelocity = false;
  1989. }
  1990. if ( event.bRestoreVelocity )
  1991. {
  1992. float otherMass = pEntity->VPhysicsGetObject()->GetMass();
  1993. int inflictorIndex = FindDamageInflictor(pInflictorPhysics);
  1994. if ( inflictorIndex >= 0 )
  1995. {
  1996. // if this is a bigger mass, save that info
  1997. inflictorstate_t &state = m_damageInflictors[inflictorIndex];
  1998. if ( otherMass > state.otherMassMax )
  1999. {
  2000. state.otherMassMax = otherMass;
  2001. }
  2002. }
  2003. else
  2004. {
  2005. AddDamageInflictor( pInflictorPhysics, otherMass, savedVel, savedAngVel, true );
  2006. }
  2007. }
  2008. }
  2009. //-----------------------------------------------------------------------------
  2010. // Impulse events
  2011. //-----------------------------------------------------------------------------
  2012. static void PostSimulation_ImpulseEvent( IPhysicsObject *pObject, const Vector &centerForce, const AngularImpulse &centerTorque )
  2013. {
  2014. pObject->ApplyForceCenter( centerForce );
  2015. pObject->ApplyTorqueCenter( centerTorque );
  2016. }
  2017. void PostSimulation_SetVelocityEvent( IPhysicsObject *pPhysicsObject, const Vector &vecVelocity )
  2018. {
  2019. pPhysicsObject->SetVelocity( &vecVelocity, NULL );
  2020. }
  2021. void CCollisionEvent::AddRemoveObject(IServerNetworkable *pRemove)
  2022. {
  2023. if ( pRemove && m_removeObjects.Find(pRemove) == -1 )
  2024. {
  2025. m_removeObjects.AddToTail(pRemove);
  2026. }
  2027. }
  2028. int CCollisionEvent::FindDamageInflictor( IPhysicsObject *pInflictorPhysics )
  2029. {
  2030. // UNDONE: Linear search? Probably ok with a low count here
  2031. for ( int i = m_damageInflictors.Count()-1; i >= 0; --i )
  2032. {
  2033. const inflictorstate_t &state = m_damageInflictors[i];
  2034. if ( state.pInflictorPhysics == pInflictorPhysics )
  2035. return i;
  2036. }
  2037. return -1;
  2038. }
  2039. int CCollisionEvent::AddDamageInflictor( IPhysicsObject *pInflictorPhysics, float otherMass, const Vector &savedVel, const AngularImpulse &savedAngVel, bool addList )
  2040. {
  2041. // NOTE: Save off the state of the object before collision
  2042. // restore if the impact is a kill
  2043. // UNDONE: Should we absorb some energy here?
  2044. // NOTE: we can't save a delta because there could be subsequent post-fatal collisions
  2045. int addIndex = m_damageInflictors.AddToTail();
  2046. {
  2047. inflictorstate_t &state = m_damageInflictors[addIndex];
  2048. state.pInflictorPhysics = pInflictorPhysics;
  2049. state.savedVelocity = savedVel;
  2050. state.savedAngularVelocity = savedAngVel;
  2051. state.otherMassMax = otherMass;
  2052. state.restored = false;
  2053. state.nextIndex = -1;
  2054. }
  2055. if ( addList )
  2056. {
  2057. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pInflictorPhysics->GetGameData());
  2058. if ( pEntity )
  2059. {
  2060. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  2061. int physCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  2062. if ( physCount > 1 )
  2063. {
  2064. int currentIndex = addIndex;
  2065. for ( int i = 0; i < physCount; i++ )
  2066. {
  2067. if ( pList[i] != pInflictorPhysics )
  2068. {
  2069. Vector vel;
  2070. AngularImpulse angVel;
  2071. pList[i]->GetVelocity( &vel, &angVel );
  2072. int next = AddDamageInflictor( pList[i], otherMass, vel, angVel, false );
  2073. m_damageInflictors[currentIndex].nextIndex = next;
  2074. currentIndex = next;
  2075. }
  2076. }
  2077. }
  2078. }
  2079. }
  2080. return addIndex;
  2081. }
  2082. void CCollisionEvent::LevelShutdown( void )
  2083. {
  2084. for ( int i = 0; i < ARRAYSIZE(m_current); i++ )
  2085. {
  2086. if ( m_current[i].patch )
  2087. {
  2088. ShutdownFriction( m_current[i] );
  2089. }
  2090. }
  2091. }
  2092. void CCollisionEvent::StartTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData )
  2093. {
  2094. CallbackContext check(this);
  2095. CBaseEntity *pEntity1 = static_cast<CBaseEntity *>(pObject1->GetGameData());
  2096. CBaseEntity *pEntity2 = static_cast<CBaseEntity *>(pObject2->GetGameData());
  2097. if ( !pEntity1 || !pEntity2 )
  2098. return;
  2099. Vector endPoint, normal;
  2100. pTouchData->GetContactPoint( endPoint );
  2101. pTouchData->GetSurfaceNormal( normal );
  2102. if ( !m_bBufferTouchEvents )
  2103. {
  2104. DispatchStartTouch( pEntity1, pEntity2, endPoint, normal );
  2105. }
  2106. else
  2107. {
  2108. AddTouchEvent( pEntity1, pEntity2, TOUCH_START, endPoint, normal );
  2109. }
  2110. }
  2111. static int CountPhysicsObjectEntityContacts( IPhysicsObject *pObject, CBaseEntity *pEntity )
  2112. {
  2113. IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot();
  2114. int count = 0;
  2115. while ( pSnapshot->IsValid() )
  2116. {
  2117. IPhysicsObject *pOther = pSnapshot->GetObject(1);
  2118. CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData());
  2119. if ( pOtherEntity == pEntity )
  2120. count++;
  2121. pSnapshot->NextFrictionData();
  2122. }
  2123. pObject->DestroyFrictionSnapshot( pSnapshot );
  2124. return count;
  2125. }
  2126. void CCollisionEvent::EndTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData )
  2127. {
  2128. CallbackContext check(this);
  2129. CBaseEntity *pEntity1 = static_cast<CBaseEntity *>(pObject1->GetGameData());
  2130. CBaseEntity *pEntity2 = static_cast<CBaseEntity *>(pObject2->GetGameData());
  2131. if ( !pEntity1 || !pEntity2 )
  2132. return;
  2133. // contact point deleted, but entities are still touching?
  2134. IPhysicsObject *list[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  2135. int count = pEntity1->VPhysicsGetObjectList( list, ARRAYSIZE(list) );
  2136. int contactCount = 0;
  2137. for ( int i = 0; i < count; i++ )
  2138. {
  2139. contactCount += CountPhysicsObjectEntityContacts( list[i], pEntity2 );
  2140. // still touching
  2141. if ( contactCount > 1 )
  2142. return;
  2143. }
  2144. // should have exactly one contact point (the one getting deleted here)
  2145. //Assert( contactCount == 1 );
  2146. Vector endPoint, normal;
  2147. pTouchData->GetContactPoint( endPoint );
  2148. pTouchData->GetSurfaceNormal( normal );
  2149. if ( !m_bBufferTouchEvents )
  2150. {
  2151. DispatchEndTouch( pEntity1, pEntity2 );
  2152. }
  2153. else
  2154. {
  2155. AddTouchEvent( pEntity1, pEntity2, TOUCH_END, vec3_origin, vec3_origin );
  2156. }
  2157. }
  2158. // UNDONE: This is functional, but minimally.
  2159. void CCollisionEvent::ObjectEnterTrigger( IPhysicsObject *pTrigger, IPhysicsObject *pObject )
  2160. {
  2161. CBaseEntity *pTriggerEntity = static_cast<CBaseEntity *>(pTrigger->GetGameData());
  2162. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObject->GetGameData());
  2163. if ( pTriggerEntity && pEntity )
  2164. {
  2165. // UNDONE: Don't buffer these until we can solve generating touches at object creation time
  2166. if ( 0 && m_bBufferTouchEvents )
  2167. {
  2168. int index = m_triggerEvents.AddToTail();
  2169. m_triggerEvents[index].Init( pTriggerEntity, pTrigger, pEntity, pObject, true );
  2170. }
  2171. else
  2172. {
  2173. CallbackContext check(this);
  2174. m_currentTriggerEvent.Init( pTriggerEntity, pTrigger, pEntity, pObject, true );
  2175. pTriggerEntity->StartTouch( pEntity );
  2176. m_currentTriggerEvent.Clear();
  2177. }
  2178. }
  2179. }
  2180. void CCollisionEvent::ObjectLeaveTrigger( IPhysicsObject *pTrigger, IPhysicsObject *pObject )
  2181. {
  2182. CBaseEntity *pTriggerEntity = static_cast<CBaseEntity *>(pTrigger->GetGameData());
  2183. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObject->GetGameData());
  2184. if ( pTriggerEntity && pEntity )
  2185. {
  2186. // UNDONE: Don't buffer these until we can solve generating touches at object creation time
  2187. if ( 0 && m_bBufferTouchEvents )
  2188. {
  2189. int index = m_triggerEvents.AddToTail();
  2190. m_triggerEvents[index].Init( pTriggerEntity, pTrigger, pEntity, pObject, false );
  2191. }
  2192. else
  2193. {
  2194. CallbackContext check(this);
  2195. m_currentTriggerEvent.Init( pTriggerEntity, pTrigger, pEntity, pObject, false );
  2196. pTriggerEntity->EndTouch( pEntity );
  2197. m_currentTriggerEvent.Clear();
  2198. }
  2199. }
  2200. }
  2201. bool CCollisionEvent::GetTriggerEvent( triggerevent_t *pEvent, CBaseEntity *pTriggerEntity )
  2202. {
  2203. if ( pEvent && pTriggerEntity == m_currentTriggerEvent.pTriggerEntity )
  2204. {
  2205. *pEvent = m_currentTriggerEvent;
  2206. return true;
  2207. }
  2208. return false;
  2209. }
  2210. void PhysGetListOfPenetratingEntities( CBaseEntity *pSearch, CUtlVector<CBaseEntity *> &list )
  2211. {
  2212. g_Collisions.GetListOfPenetratingEntities( pSearch, list );
  2213. }
  2214. bool PhysGetTriggerEvent( triggerevent_t *pEvent, CBaseEntity *pTriggerEntity )
  2215. {
  2216. return g_Collisions.GetTriggerEvent( pEvent, pTriggerEntity );
  2217. }
  2218. //-----------------------------------------------------------------------------
  2219. //-----------------------------------------------------------------------------
  2220. // External interface to collision sounds
  2221. //-----------------------------------------------------------------------------
  2222. void PhysicsImpactSound( CBaseEntity *pEntity, IPhysicsObject *pPhysObject, int channel, int surfaceProps, int surfacePropsHit, float volume, float impactSpeed )
  2223. {
  2224. physicssound::AddImpactSound( g_PhysicsHook.m_impactSounds, pEntity, pEntity->entindex(), channel, pPhysObject, surfaceProps, surfacePropsHit, volume, impactSpeed );
  2225. }
  2226. void PhysCollisionSound( CBaseEntity *pEntity, IPhysicsObject *pPhysObject, int channel, int surfaceProps, int surfacePropsHit, float deltaTime, float speed )
  2227. {
  2228. if ( deltaTime < 0.05f || speed < 70.0f )
  2229. return;
  2230. float volume = speed * speed * (1.0f/(320.0f*320.0f)); // max volume at 320 in/s
  2231. if ( volume > 1.0f )
  2232. volume = 1.0f;
  2233. PhysicsImpactSound( pEntity, pPhysObject, channel, surfaceProps, surfacePropsHit, volume, speed );
  2234. }
  2235. void PhysBreakSound( CBaseEntity *pEntity, IPhysicsObject *pPhysObject, Vector vecOrigin )
  2236. {
  2237. if ( !pPhysObject)
  2238. return;
  2239. physicssound::AddBreakSound( g_PhysicsHook.m_breakSounds, vecOrigin, pPhysObject->GetMaterialIndex() );
  2240. }
  2241. ConVar collision_shake_amp("collision_shake_amp", "0.2");
  2242. ConVar collision_shake_freq("collision_shake_freq", "0.5");
  2243. ConVar collision_shake_time("collision_shake_time", "0.5");
  2244. void PhysCollisionScreenShake( gamevcollisionevent_t *pEvent, int index )
  2245. {
  2246. int otherIndex = !index;
  2247. float mass = pEvent->pObjects[index]->GetMass();
  2248. if ( mass >= VPHYSICS_LARGE_OBJECT_MASS && pEvent->pObjects[otherIndex]->IsStatic() &&
  2249. !(pEvent->pObjects[index]->GetGameFlags() & FVPHYSICS_PENETRATING) )
  2250. {
  2251. mass = clamp(mass, VPHYSICS_LARGE_OBJECT_MASS, 2000.f);
  2252. if ( pEvent->collisionSpeed > 30 && pEvent->deltaCollisionTime > 0.25f )
  2253. {
  2254. Vector vecPos;
  2255. pEvent->pInternalData->GetContactPoint( vecPos );
  2256. float impulse = pEvent->collisionSpeed * mass;
  2257. float amplitude = impulse * (collision_shake_amp.GetFloat() / (30.0f * VPHYSICS_LARGE_OBJECT_MASS));
  2258. UTIL_ScreenShake( vecPos, amplitude, collision_shake_freq.GetFloat(), collision_shake_time.GetFloat(), amplitude * 60, SHAKE_START );
  2259. }
  2260. }
  2261. }
  2262. #if HL2_EPISODIC
  2263. // Uses DispatchParticleEffect because, so far as I know, that is the new means of kicking
  2264. // off flinders for this kind of collision. Should this be in g_pEffects instead?
  2265. void PhysCollisionWarpEffect( gamevcollisionevent_t *pEvent, surfacedata_t *phit )
  2266. {
  2267. Vector vecPos;
  2268. QAngle vecAngles;
  2269. pEvent->pInternalData->GetContactPoint( vecPos );
  2270. {
  2271. Vector vecNormal;
  2272. pEvent->pInternalData->GetSurfaceNormal(vecNormal);
  2273. VectorAngles( vecNormal, vecAngles );
  2274. }
  2275. DispatchParticleEffect( "warp_shield_impact", vecPos, vecAngles );
  2276. }
  2277. #endif
  2278. void PhysCollisionDust( gamevcollisionevent_t *pEvent, surfacedata_t *phit )
  2279. {
  2280. switch ( phit->game.material )
  2281. {
  2282. case CHAR_TEX_SAND:
  2283. case CHAR_TEX_DIRT:
  2284. if ( pEvent->collisionSpeed < 200.0f )
  2285. return;
  2286. break;
  2287. case CHAR_TEX_CONCRETE:
  2288. if ( pEvent->collisionSpeed < 340.0f )
  2289. return;
  2290. break;
  2291. #if HL2_EPISODIC
  2292. // this is probably redundant because BaseEntity::VHandleCollision should have already dispatched us elsewhere
  2293. case CHAR_TEX_WARPSHIELD:
  2294. PhysCollisionWarpEffect(pEvent,phit);
  2295. return;
  2296. break;
  2297. #endif
  2298. default:
  2299. return;
  2300. }
  2301. //Kick up dust
  2302. Vector vecPos, vecVel;
  2303. pEvent->pInternalData->GetContactPoint( vecPos );
  2304. vecVel.Random( -1.0f, 1.0f );
  2305. vecVel.z = random->RandomFloat( 0.3f, 1.0f );
  2306. VectorNormalize( vecVel );
  2307. g_pEffects->Dust( vecPos, vecVel, 8.0f, pEvent->collisionSpeed );
  2308. }
  2309. void PhysFrictionSound( CBaseEntity *pEntity, IPhysicsObject *pObject, const char *pSoundName, HSOUNDSCRIPTHANDLE& handle, float flVolume )
  2310. {
  2311. if ( !pEntity )
  2312. return;
  2313. // cut out the quiet sounds
  2314. // UNDONE: Separate threshold for starting a sound vs. continuing?
  2315. flVolume = clamp( flVolume, 0.0f, 1.0f );
  2316. if ( flVolume > (1.0f/128.0f) )
  2317. {
  2318. friction_t *pFriction = g_Collisions.FindFriction( pEntity );
  2319. if ( !pFriction )
  2320. return;
  2321. CSoundParameters params;
  2322. if ( !CBaseEntity::GetParametersForSound( pSoundName, handle, params, NULL ) )
  2323. return;
  2324. if ( !pFriction->pObject )
  2325. {
  2326. // don't create really quiet scrapes
  2327. if ( params.volume * flVolume <= 0.1f )
  2328. return;
  2329. pFriction->pObject = pEntity;
  2330. CPASAttenuationFilter filter( pEntity, params.soundlevel );
  2331. pFriction->patch = CSoundEnvelopeController::GetController().SoundCreate(
  2332. filter, pEntity->entindex(), CHAN_BODY, pSoundName, params.soundlevel );
  2333. CSoundEnvelopeController::GetController().Play( pFriction->patch, params.volume * flVolume, params.pitch );
  2334. }
  2335. else
  2336. {
  2337. float pitch = (flVolume * (params.pitchhigh - params.pitchlow)) + params.pitchlow;
  2338. CSoundEnvelopeController::GetController().SoundChangeVolume( pFriction->patch, params.volume * flVolume, 0.1f );
  2339. CSoundEnvelopeController::GetController().SoundChangePitch( pFriction->patch, pitch, 0.1f );
  2340. }
  2341. pFriction->flLastUpdateTime = gpGlobals->curtime;
  2342. pFriction->flLastEffectTime = gpGlobals->curtime;
  2343. }
  2344. }
  2345. void PhysCleanupFrictionSounds( CBaseEntity *pEntity )
  2346. {
  2347. friction_t *pFriction = g_Collisions.FindFriction( pEntity );
  2348. if ( pFriction && pFriction->patch )
  2349. {
  2350. g_Collisions.ShutdownFriction( *pFriction );
  2351. }
  2352. }
  2353. //-----------------------------------------------------------------------------
  2354. // Applies force impulses at a later time
  2355. //-----------------------------------------------------------------------------
  2356. void PhysCallbackImpulse( IPhysicsObject *pPhysicsObject, const Vector &vecCenterForce, const AngularImpulse &vecCenterTorque )
  2357. {
  2358. Assert( physenv->IsInSimulation() );
  2359. g_PostSimulationQueue.QueueCall( PostSimulation_ImpulseEvent, pPhysicsObject, RefToVal(vecCenterForce), RefToVal(vecCenterTorque) );
  2360. }
  2361. void PhysCallbackSetVelocity( IPhysicsObject *pPhysicsObject, const Vector &vecVelocity )
  2362. {
  2363. Assert( physenv->IsInSimulation() );
  2364. g_PostSimulationQueue.QueueCall( PostSimulation_SetVelocityEvent, pPhysicsObject, RefToVal(vecVelocity) );
  2365. }
  2366. void PhysCallbackDamage( CBaseEntity *pEntity, const CTakeDamageInfo &info, gamevcollisionevent_t &event, int hurtIndex )
  2367. {
  2368. Assert( physenv->IsInSimulation() );
  2369. int otherIndex = !hurtIndex;
  2370. g_Collisions.AddDamageEvent( pEntity, info, event.pObjects[otherIndex], true, event.preVelocity[otherIndex], event.preAngularVelocity[otherIndex] );
  2371. }
  2372. void PhysCallbackDamage( CBaseEntity *pEntity, const CTakeDamageInfo &info )
  2373. {
  2374. if ( PhysIsInCallback() )
  2375. {
  2376. CBaseEntity *pInflictor = info.GetInflictor();
  2377. IPhysicsObject *pInflictorPhysics = (pInflictor) ? pInflictor->VPhysicsGetObject() : NULL;
  2378. g_Collisions.AddDamageEvent( pEntity, info, pInflictorPhysics, false, vec3_origin, vec3_origin );
  2379. if ( pEntity && info.GetInflictor() )
  2380. {
  2381. DevMsg( 2, "Warning: Physics damage event with no recovery info!\nObjects: %s, %s\n", pEntity->GetClassname(), info.GetInflictor()->GetClassname() );
  2382. }
  2383. }
  2384. else
  2385. {
  2386. pEntity->TakeDamage( info );
  2387. }
  2388. }
  2389. void PhysCallbackRemove(IServerNetworkable *pRemove)
  2390. {
  2391. if ( PhysIsInCallback() )
  2392. {
  2393. g_Collisions.AddRemoveObject(pRemove);
  2394. }
  2395. else
  2396. {
  2397. UTIL_Remove(pRemove);
  2398. }
  2399. }
  2400. void PhysSetEntityGameFlags( CBaseEntity *pEntity, unsigned short flags )
  2401. {
  2402. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  2403. int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  2404. for ( int i = 0; i < count; i++ )
  2405. {
  2406. PhysSetGameFlags( pList[i], flags );
  2407. }
  2408. }
  2409. bool PhysFindOrAddVehicleScript( const char *pScriptName, vehicleparams_t *pParams, vehiclesounds_t *pSounds )
  2410. {
  2411. return g_PhysicsHook.FindOrAddVehicleScript(pScriptName, pParams, pSounds);
  2412. }
  2413. void PhysFlushVehicleScripts()
  2414. {
  2415. g_PhysicsHook.FlushVehicleScripts();
  2416. }
  2417. IPhysicsObject *FindPhysicsObjectByName( const char *pName, CBaseEntity *pErrorEntity )
  2418. {
  2419. if ( !pName || !strlen(pName) )
  2420. return NULL;
  2421. CBaseEntity *pEntity = NULL;
  2422. IPhysicsObject *pBestObject = NULL;
  2423. while (1)
  2424. {
  2425. pEntity = gEntList.FindEntityByName( pEntity, pName );
  2426. if ( !pEntity )
  2427. break;
  2428. if ( pEntity->VPhysicsGetObject() )
  2429. {
  2430. if ( pBestObject )
  2431. {
  2432. const char *pErrorName = pErrorEntity ? pErrorEntity->GetClassname() : "Unknown";
  2433. Vector origin = pErrorEntity ? pErrorEntity->GetAbsOrigin() : vec3_origin;
  2434. DevWarning("entity %s at %s has physics attachment to more than one entity with the name %s!!!\n", pErrorName, VecToString(origin), pName );
  2435. while ( ( pEntity = gEntList.FindEntityByName( pEntity, pName ) ) != NULL )
  2436. {
  2437. DevWarning("Found %s\n", pEntity->GetClassname() );
  2438. }
  2439. break;
  2440. }
  2441. pBestObject = pEntity->VPhysicsGetObject();
  2442. }
  2443. }
  2444. return pBestObject;
  2445. }
  2446. void CC_AirDensity( const CCommand &args )
  2447. {
  2448. if ( !physenv )
  2449. return;
  2450. if ( args.ArgC() < 2 )
  2451. {
  2452. Msg( "air_density <value>\nCurrent air density is %.2f\n", physenv->GetAirDensity() );
  2453. }
  2454. else
  2455. {
  2456. float density = atof( args[1] );
  2457. physenv->SetAirDensity( density );
  2458. }
  2459. }
  2460. static ConCommand air_density("air_density", CC_AirDensity, "Changes the density of air for drag computations.", FCVAR_CHEAT);
  2461. void DebugDrawContactPoints(IPhysicsObject *pPhysics)
  2462. {
  2463. IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot();
  2464. while ( pSnapshot->IsValid() )
  2465. {
  2466. Vector pt, normal;
  2467. pSnapshot->GetContactPoint( pt );
  2468. pSnapshot->GetSurfaceNormal( normal );
  2469. NDebugOverlay::Box( pt, -Vector(1,1,1), Vector(1,1,1), 0, 255, 0, 32, 0 );
  2470. NDebugOverlay::Line( pt, pt - normal * 20, 0, 255, 0, false, 0 );
  2471. IPhysicsObject *pOther = pSnapshot->GetObject(1);
  2472. CBaseEntity *pEntity0 = static_cast<CBaseEntity *>(pOther->GetGameData());
  2473. CFmtStr str("%s (%s): %s [%0.2f]", pEntity0->GetClassname(), STRING(pEntity0->GetModelName()), pEntity0->GetDebugName(), pSnapshot->GetFrictionCoefficient() );
  2474. NDebugOverlay::Text( pt, str.Access(), false, 0 );
  2475. pSnapshot->NextFrictionData();
  2476. }
  2477. pSnapshot->DeleteAllMarkedContacts( true );
  2478. pPhysics->DestroyFrictionSnapshot( pSnapshot );
  2479. }
  2480. #if 0
  2481. #include "filesystem.h"
  2482. //-----------------------------------------------------------------------------
  2483. // Purpose: This will append a collide to a glview file. Then you can view the
  2484. // collisionmodels with glview.
  2485. // Input : *pCollide - collision model
  2486. // &origin - position of the instance of this model
  2487. // &angles - orientation of instance
  2488. // *pFilename - output text file
  2489. //-----------------------------------------------------------------------------
  2490. // examples:
  2491. // world:
  2492. // DumpCollideToGlView( pWorldCollide->solids[0], vec3_origin, vec3_origin, "jaycollide.txt" );
  2493. // static_prop:
  2494. // DumpCollideToGlView( info.m_pCollide->solids[0], info.m_Origin, info.m_Angles, "jaycollide.txt" );
  2495. //
  2496. //-----------------------------------------------------------------------------
  2497. void DumpCollideToGlView( CPhysCollide *pCollide, const Vector &origin, const QAngle &angles, const char *pFilename )
  2498. {
  2499. if ( !pCollide )
  2500. return;
  2501. printf("Writing %s...\n", pFilename );
  2502. Vector *outVerts;
  2503. int vertCount = physcollision->CreateDebugMesh( pCollide, &outVerts );
  2504. FileHandle_t fp = filesystem->Open( pFilename, "ab" );
  2505. int triCount = vertCount / 3;
  2506. int vert = 0;
  2507. VMatrix tmp = SetupMatrixOrgAngles( origin, angles );
  2508. int i;
  2509. for ( i = 0; i < vertCount; i++ )
  2510. {
  2511. outVerts[i] = tmp.VMul4x3( outVerts[i] );
  2512. }
  2513. for ( i = 0; i < triCount; i++ )
  2514. {
  2515. filesystem->FPrintf( fp, "3\n" );
  2516. filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f 1 0 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
  2517. vert++;
  2518. filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f 0 1 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
  2519. vert++;
  2520. filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f 0 0 1\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
  2521. vert++;
  2522. }
  2523. filesystem->Close( fp );
  2524. physcollision->DestroyDebugMesh( vertCount, outVerts );
  2525. }
  2526. #endif