Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3037 lines
88 KiB

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