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.

1514 lines
45 KiB

  1. //========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "vcollide_parse.h"
  9. #include "filesystem.h"
  10. #include "engine/IStaticPropMgr.h"
  11. #include "solidsetdefaults.h"
  12. #include "engine/IEngineSound.h"
  13. #include "vphysics_sound.h"
  14. #include "movevars_shared.h"
  15. #include "engine/ivmodelinfo.h"
  16. #include "fx.h"
  17. #include "tier0/vprof.h"
  18. #include "c_world.h"
  19. #include "vphysics/object_hash.h"
  20. #include "vphysics/collision_set.h"
  21. #include "soundenvelope.h"
  22. #include "fx_water.h"
  23. #include "positionwatcher.h"
  24. #include "vphysics/constraints.h"
  25. #include "tier0/miniprofiler.h"
  26. #include "engine/ivdebugoverlay.h"
  27. #ifdef IVP_MINIPROFILER
  28. #include "../ivp/ivp_utility/ivu_miniprofiler.h"
  29. #else
  30. #define PHYS_PROFILE(ID)
  31. #endif
  32. #include "tier1/fmtstr.h"
  33. #include "vphysics/friction.h"
  34. #include "prediction.h"
  35. // memdbgon must be the last include file in a .cpp file!!!
  36. #include "tier0/memdbgon.h"
  37. // file system interface
  38. extern IFileSystem *filesystem;
  39. static ConVar cl_phys_timescale( "cl_phys_timescale", "1.0", FCVAR_CHEAT, "Sets the scale of time for client-side physics (ragdolls)" );
  40. static ConVar cl_phys_maxticks( "cl_phys_maxticks", IsGameConsole() ? "2" : "0", FCVAR_NONE, "Sets the max number of physics ticks allowed for client-side physics (ragdolls)" );
  41. ConVar cl_ragdoll_gravity( "cl_ragdoll_gravity", "600", FCVAR_CHEAT, "Sets the gravity client-side ragdolls" );
  42. ConVar phys_debug_check_contacts("phys_debug_check_contacts", "0", FCVAR_CHEAT|FCVAR_REPLICATED);
  43. // blocked entity detecting
  44. static ConVar cl_phys_block_fraction("cl_phys_block_fraction", "0.1");
  45. static ConVar cl_phys_block_dist("cl_phys_block_dist","1.0");
  46. void PrecachePhysicsSounds( void );
  47. //FIXME: Replicated from server end, consolidate?
  48. CUtlLinkedList<C_BaseEntity *> g_ShadowEntities;
  49. void PhysAddShadow( C_BaseEntity *pEntity )
  50. {
  51. if( g_ShadowEntities.Find( pEntity ) == g_ShadowEntities.InvalidIndex() )
  52. {
  53. g_ShadowEntities.AddToTail( pEntity );
  54. }
  55. }
  56. void PhysRemoveShadow( C_BaseEntity *pEntity )
  57. {
  58. g_ShadowEntities.FindAndRemove( pEntity );
  59. }
  60. extern IVEngineClient *engine;
  61. struct penetrateevent_t
  62. {
  63. C_BaseEntity *pEntity0;
  64. C_BaseEntity *pEntity1;
  65. float startTime;
  66. float timeStamp;
  67. };
  68. class CCollisionEvent : public IPhysicsCollisionEvent, public IPhysicsCollisionSolver, public IPhysicsObjectEvent
  69. {
  70. public:
  71. CCollisionEvent( void );
  72. void ObjectSound( int index, vcollisionevent_t *pEvent );
  73. void PreCollision( vcollisionevent_t *pEvent ) {}
  74. void PostCollision( vcollisionevent_t *pEvent );
  75. void Friction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit, IPhysicsCollisionData *pData );
  76. void BufferTouchEvents( bool enable ) { m_bBufferTouchEvents = enable; }
  77. void StartTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData );
  78. void EndTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData );
  79. void FluidStartTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid );
  80. void FluidEndTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid );
  81. void PostSimulationFrame() {}
  82. virtual void ObjectEnterTrigger( IPhysicsObject *pTrigger, IPhysicsObject *pObject ) {}
  83. virtual void ObjectLeaveTrigger( IPhysicsObject *pTrigger, IPhysicsObject *pObject ) {}
  84. float DeltaTimeSinceLastFluid( CBaseEntity *pEntity );
  85. void FrameUpdate( void );
  86. void UpdateFluidEvents( void );
  87. void UpdateTouchEvents( void );
  88. void UpdatePenetrateEvents();
  89. void LevelShutdown();
  90. // IPhysicsCollisionSolver
  91. int ShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 );
  92. #if _DEBUG
  93. int ShouldCollide_2( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 );
  94. #endif
  95. // debugging collision problem in TF2
  96. int ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1, float dt );
  97. bool ShouldFreezeObject( IPhysicsObject *pObject )
  98. {
  99. // shadow controlled objects are probably server side.
  100. // UNDONE: Explicitly flag server side objects?
  101. if ( pObject->GetShadowController() )
  102. return false;
  103. return true;
  104. }
  105. int AdditionalCollisionChecksThisTick( int currentChecksDone ) { return 0; }
  106. bool ShouldFreezeContacts( IPhysicsObject **pObjectList, int objectCount ) { return true; }
  107. // IPhysicsObjectEvent
  108. virtual void ObjectWake( IPhysicsObject *pObject )
  109. {
  110. C_BaseEntity *pEntity = static_cast<C_BaseEntity *>(pObject->GetGameData());
  111. if (pEntity && pEntity->HasDataObjectType(VPHYSICSWATCHER))
  112. {
  113. ReportVPhysicsStateChanged( pObject, pEntity, true );
  114. }
  115. }
  116. virtual void ObjectSleep( IPhysicsObject *pObject )
  117. {
  118. C_BaseEntity *pEntity = static_cast<C_BaseEntity *>(pObject->GetGameData());
  119. if ( pEntity && pEntity->HasDataObjectType( VPHYSICSWATCHER ) )
  120. {
  121. ReportVPhysicsStateChanged( pObject, pEntity, false );
  122. }
  123. }
  124. friction_t *FindFriction( CBaseEntity *pObject );
  125. void ShutdownFriction( friction_t &friction );
  126. void UpdateFrictionSounds();
  127. bool IsInCallback() { return m_inCallback > 0 ? true : false; }
  128. private:
  129. class CallbackContext
  130. {
  131. public:
  132. explicit CallbackContext(CCollisionEvent *pOuter)
  133. {
  134. m_pOuter = pOuter;
  135. m_pOuter->m_inCallback++;
  136. }
  137. ~CallbackContext()
  138. {
  139. m_pOuter->m_inCallback--;
  140. }
  141. private:
  142. CCollisionEvent *m_pOuter;
  143. };
  144. friend class CallbackContext;
  145. void AddTouchEvent( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, int touchType, const Vector &point, const Vector &normal );
  146. void DispatchStartTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, const Vector &point, const Vector &normal );
  147. void DispatchEndTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1 );
  148. void FindOrAddPenetrateEvent( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1 );
  149. friction_t m_current[8];
  150. CUtlVector<fluidevent_t> m_fluidEvents;
  151. CUtlVector<touchevent_t> m_touchEvents;
  152. CUtlVector<penetrateevent_t> m_penetrateEvents;
  153. int m_inCallback;
  154. bool m_bBufferTouchEvents;
  155. float m_flLastSplashTime;
  156. };
  157. CCollisionEvent g_Collisions;
  158. bool PhysIsInCallback()
  159. {
  160. if ( (physenv && physenv->IsInSimulation()) || g_Collisions.IsInCallback() )
  161. return true;
  162. return false;
  163. }
  164. bool PhysicsDLLInit( CreateInterfaceFn physicsFactory )
  165. {
  166. if ((physics = (IPhysics *)physicsFactory( VPHYSICS_INTERFACE_VERSION, NULL )) == NULL ||
  167. (physprops = (IPhysicsSurfaceProps *)physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL )) == NULL ||
  168. (physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL )) == NULL )
  169. {
  170. return false;
  171. }
  172. PhysParseSurfaceData( physprops, filesystem );
  173. return true;
  174. }
  175. extern ConVar_ServerBounded *cl_predict;
  176. ConVar cl_predictphysics( "cl_predictphysics", "0", 0, "Use a prediction-friendly physics interface on the client" );
  177. void PhysicsLevelInit( void )
  178. {
  179. physenv = physics->CreateEnvironment();
  180. assert( physenv );
  181. if( gpGlobals->IsRemoteClient() && g_pGameRules->IsMultiplayer() && cl_predictphysics.GetBool() )
  182. {
  183. physenv->SetPredicted( true );
  184. }
  185. #ifdef PORTAL
  186. physenv_main = physenv;
  187. #endif
  188. {
  189. MEM_ALLOC_CREDIT();
  190. g_EntityCollisionHash = physics->CreateObjectPairHash();
  191. }
  192. // TODO: need to get the right factory function here
  193. //physenv->SetDebugOverlay( appSystemFactory );
  194. physenv->SetGravity( Vector(0, 0, -sv_gravity.GetFloat() ) );
  195. physenv->SetAlternateGravity( Vector(0, 0, -cl_ragdoll_gravity.GetFloat() ) );
  196. // NOTE: Always run client physics at a rate >= 45Hz - helps keep ragdolls stable
  197. const float defaultPhysicsTick = 1.0f / 60.0f; // 60Hz to stay in sync with x360 framerate of 30Hz
  198. physenv->SetSimulationTimestep( defaultPhysicsTick );
  199. physenv->SetCollisionEventHandler( &g_Collisions );
  200. physenv->SetCollisionSolver( &g_Collisions );
  201. C_World *pWorld = GetClientWorldEntity();
  202. g_PhysWorldObject = PhysCreateWorld_Shared( pWorld, modelinfo->GetVCollide(1), g_PhysDefaultObjectParams );
  203. staticpropmgr->CreateVPhysicsRepresentations( physenv, &g_SolidSetup, pWorld );
  204. }
  205. void PhysicsReset()
  206. {
  207. if ( !physenv )
  208. return;
  209. physenv->ResetSimulationClock();
  210. }
  211. static CBaseEntity *FindPhysicsBlocker( IPhysicsObject *pPhysics )
  212. {
  213. IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot();
  214. CBaseEntity *pBlocker = NULL;
  215. float maxVel = 10.0f;
  216. while ( pSnapshot->IsValid() )
  217. {
  218. IPhysicsObject *pOther = pSnapshot->GetObject(1);
  219. if ( pOther->IsMoveable() )
  220. {
  221. CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData());
  222. // dot with this if you have a direction
  223. //Vector normal;
  224. //pSnapshot->GetSurfaceNormal(normal);
  225. float force = pSnapshot->GetNormalForce();
  226. float vel = force * pOther->GetInvMass();
  227. if ( vel > maxVel )
  228. {
  229. pBlocker = pOtherEntity;
  230. maxVel = vel;
  231. }
  232. }
  233. pSnapshot->NextFrictionData();
  234. }
  235. pPhysics->DestroyFrictionSnapshot( pSnapshot );
  236. return pBlocker;
  237. }
  238. ConVar cl_ragdoll_collide( "cl_ragdoll_collide", "0" );
  239. int CCollisionEvent::ShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 )
  240. #if _DEBUG
  241. {
  242. int x0 = ShouldCollide_2(pObj0, pObj1, pGameData0, pGameData1);
  243. int x1 = ShouldCollide_2(pObj1, pObj0, pGameData1, pGameData0);
  244. Assert(x0==x1);
  245. return x0;
  246. }
  247. int CCollisionEvent::ShouldCollide_2( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 )
  248. #endif
  249. {
  250. CallbackContext callback(this);
  251. C_BaseEntity *pEntity0 = static_cast<C_BaseEntity *>(pGameData0);
  252. C_BaseEntity *pEntity1 = static_cast<C_BaseEntity *>(pGameData1);
  253. if ( !pEntity0 || !pEntity1 )
  254. return 1;
  255. unsigned short gameFlags0 = pObj0->GetGameFlags();
  256. unsigned short gameFlags1 = pObj1->GetGameFlags();
  257. if ( pEntity0 == pEntity1 )
  258. {
  259. // allow all-or-nothing per-entity disable
  260. if ( (gameFlags0 | gameFlags1) & FVPHYSICS_NO_SELF_COLLISIONS )
  261. return 0;
  262. IPhysicsCollisionSet *pSet = physics->FindCollisionSet( pEntity0->GetModelIndex() );
  263. if ( pSet )
  264. return pSet->ShouldCollide( pObj0->GetGameIndex(), pObj1->GetGameIndex() );
  265. return 1;
  266. }
  267. if ( (pObj0->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL) && (pObj1->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL) )
  268. {
  269. return cl_ragdoll_collide.GetBool();
  270. }
  271. // Obey collision group rules
  272. Assert(GameRules());
  273. if ( GameRules() )
  274. {
  275. if (!GameRules()->ShouldCollide( pEntity0->GetCollisionGroup(), pEntity1->GetCollisionGroup() ))
  276. return 0;
  277. }
  278. // check contents
  279. if ( !(pObj0->GetContents() & pEntity1->PhysicsSolidMaskForEntity()) || !(pObj1->GetContents() & pEntity0->PhysicsSolidMaskForEntity()) )
  280. return 0;
  281. if ( g_EntityCollisionHash->IsObjectPairInHash( pGameData0, pGameData1 ) )
  282. return 0;
  283. if ( g_EntityCollisionHash->IsObjectPairInHash( pObj0, pObj1 ) )
  284. return 0;
  285. #if 0
  286. int solid0 = pEntity0->GetSolid();
  287. int solid1 = pEntity1->GetSolid();
  288. int nSolidFlags0 = pEntity0->GetSolidFlags();
  289. int nSolidFlags1 = pEntity1->GetSolidFlags();
  290. #endif
  291. int movetype0 = pEntity0->GetMoveType();
  292. int movetype1 = pEntity1->GetMoveType();
  293. // entities with non-physical move parents or entities with MOVETYPE_PUSH
  294. // are considered as "AI movers". They are unchanged by collision; they exert
  295. // physics forces on the rest of the system.
  296. bool aiMove0 = (movetype0 == MOVETYPE_PUSH || movetype0 == MOVETYPE_NONE) ? true : false;
  297. bool aiMove1 = (movetype1 == MOVETYPE_PUSH || movetype1 == MOVETYPE_NONE) ? true : false;
  298. // Anything with custom movement and a shadow controller is assumed to do its own world/AI collisions
  299. if ( movetype0 == MOVETYPE_CUSTOM && pObj0->GetShadowController() )
  300. {
  301. aiMove0 = true;
  302. }
  303. if ( movetype1 == MOVETYPE_CUSTOM && pObj1->GetShadowController() )
  304. {
  305. aiMove1 = true;
  306. }
  307. if ( pEntity0->GetMoveParent() )
  308. {
  309. // if the object & its parent are both MOVETYPE_VPHYSICS, then this must be a special case
  310. // like a prop_ragdoll_attached
  311. if ( !(movetype0 == MOVETYPE_VPHYSICS && pEntity0->GetRootMoveParent()->GetMoveType() == MOVETYPE_VPHYSICS) )
  312. {
  313. aiMove0 = true;
  314. }
  315. }
  316. if ( pEntity1->GetMoveParent() )
  317. {
  318. // if the object & its parent are both MOVETYPE_VPHYSICS, then this must be a special case.
  319. if ( !(movetype1 == MOVETYPE_VPHYSICS && pEntity1->GetRootMoveParent()->GetMoveType() == MOVETYPE_VPHYSICS) )
  320. {
  321. aiMove1 = true;
  322. }
  323. }
  324. // AI movers don't collide with the world/static/pinned objects or other AI movers
  325. if ( (aiMove0 && !pObj1->IsMoveable()) ||
  326. (aiMove1 && !pObj0->IsMoveable()) ||
  327. (aiMove0 && aiMove1) )
  328. return 0;
  329. // two objects under shadow control should not collide. The AI will figure it out
  330. if ( pObj0->GetShadowController() && pObj1->GetShadowController() )
  331. return 0;
  332. return 1;
  333. }
  334. int CCollisionEvent::ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1, float dt )
  335. {
  336. CallbackContext callback(this);
  337. C_BaseEntity *pEntity0 = static_cast<C_BaseEntity *>(pGameData0);
  338. C_BaseEntity *pEntity1 = static_cast<C_BaseEntity *>(pGameData1);
  339. // solve it yourself here and return 0, or have the default implementation do it
  340. if ( pEntity0 > pEntity1 )
  341. {
  342. // swap sort
  343. CBaseEntity *pTmp = pEntity0;
  344. pEntity0 = pEntity1;
  345. pEntity1 = pTmp;
  346. IPhysicsObject *pTmpObj = pObj0;
  347. pObj0 = pObj1;
  348. pObj1 = pTmpObj;
  349. }
  350. if ( !pEntity0 || !pEntity1 )
  351. return 1;
  352. unsigned short gameFlags0 = pObj0->GetGameFlags();
  353. unsigned short gameFlags1 = pObj1->GetGameFlags();
  354. // solve it yourself here and return 0, or have the default implementation do it
  355. if ( pGameData0 == pGameData1 )
  356. {
  357. if ( gameFlags0 & FVPHYSICS_PART_OF_RAGDOLL )
  358. {
  359. // this is a ragdoll, self penetrating
  360. C_BaseEntity *pEnt = reinterpret_cast<C_BaseEntity *>(pGameData0);
  361. C_BaseAnimating *pAnim = pEnt->GetBaseAnimating();
  362. if ( pAnim && pAnim->m_pRagdoll )
  363. {
  364. IPhysicsConstraintGroup *pGroup = pAnim->m_pRagdoll->GetConstraintGroup();
  365. if ( pGroup )
  366. {
  367. pGroup->SolvePenetration( pObj0, pObj1 );
  368. return false;
  369. }
  370. }
  371. }
  372. }
  373. else if ( (gameFlags0|gameFlags1) & FVPHYSICS_PART_OF_RAGDOLL )
  374. {
  375. // ragdoll penetrating shadow object, just give up for now
  376. if ( pObj0->GetShadowController() || pObj1->GetShadowController() )
  377. {
  378. FindOrAddPenetrateEvent( pEntity0, pEntity1 );
  379. return true;
  380. }
  381. }
  382. return true;
  383. }
  384. // A class that implements an IClientSystem for physics
  385. class CPhysicsSystem : public CAutoGameSystemPerFrame
  386. {
  387. public:
  388. explicit CPhysicsSystem( char const *name ) : CAutoGameSystemPerFrame( name )
  389. {
  390. }
  391. // HACKHACK: PhysicsDLLInit() is called explicitly because it requires a parameter
  392. virtual bool Init();
  393. virtual void Shutdown();
  394. // Level init, shutdown
  395. virtual void LevelInitPreEntity();
  396. virtual void LevelInitPostEntity();
  397. // The level is shutdown in two parts
  398. virtual void LevelShutdownPreEntity();
  399. virtual void LevelShutdownPostEntity();
  400. void AddImpactSound( void *pGameData, IPhysicsObject *pObject, int surfaceProps, int surfacePropsHit, float volume, float speed );
  401. virtual void Update( float frametime );
  402. void PhysicsSimulate();
  403. private:
  404. physicssound::soundlist_t m_impactSounds;
  405. };
  406. static CPhysicsSystem g_PhysicsSystem( "CPhysicsSystem" );
  407. // singleton to hook into the client system
  408. IGameSystem *PhysicsGameSystem( void )
  409. {
  410. return &g_PhysicsSystem;
  411. }
  412. // HACKHACK: PhysicsDLLInit() is called explicitly because it requires a parameter
  413. bool CPhysicsSystem::Init()
  414. {
  415. return true;
  416. }
  417. void CPhysicsSystem::Shutdown()
  418. {
  419. }
  420. // Level init, shutdown
  421. void CPhysicsSystem::LevelInitPreEntity( void )
  422. {
  423. m_impactSounds.RemoveAll();
  424. PrecachePhysicsSounds();
  425. }
  426. void CPhysicsSystem::LevelInitPostEntity( void )
  427. {
  428. PhysicsLevelInit();
  429. }
  430. // The level is shutdown in two parts
  431. void CPhysicsSystem::LevelShutdownPreEntity()
  432. {
  433. if ( physenv )
  434. {
  435. // we may have deleted multiple objects including the world by now, so
  436. // don't try to wake them up
  437. physenv->SetQuickDelete( true );
  438. }
  439. }
  440. void CPhysicsSystem::LevelShutdownPostEntity()
  441. {
  442. g_Collisions.LevelShutdown();
  443. if ( physenv )
  444. {
  445. // environment destroys all objects
  446. // entities are gone, so this is safe now
  447. physics->DestroyEnvironment( physenv );
  448. }
  449. physics->DestroyObjectPairHash( g_EntityCollisionHash );
  450. g_EntityCollisionHash = NULL;
  451. physics->DestroyAllCollisionSets();
  452. physenv = NULL;
  453. g_PhysWorldObject = NULL;
  454. }
  455. void CPhysicsSystem::AddImpactSound( void *pGameData, IPhysicsObject *pObject, int surfaceProps, int surfacePropsHit, float volume, float speed )
  456. {
  457. physicssound::AddImpactSound( m_impactSounds, pGameData, SOUND_FROM_WORLD, CHAN_STATIC, pObject, surfaceProps, surfacePropsHit, volume, speed );
  458. }
  459. void CPhysicsSystem::Update( float frametime )
  460. {
  461. // THIS WAS MOVED TO POST-ENTITY SIM
  462. //PhysicsSimulate();
  463. }
  464. //#ifdef _LINUX
  465. //DLL_IMPORT CLinkedMiniProfiler *g_pPhysicsMiniProfilers;
  466. //#else
  467. CLinkedMiniProfiler *g_pPhysicsMiniProfilers;
  468. //#endif
  469. CLinkedMiniProfiler g_mp_PhysicsSimulate("PhysicsSimulate",&g_pPhysicsMiniProfilers);
  470. CLinkedMiniProfiler g_mp_active_object_count("active_object_count",&g_pPhysicsMiniProfilers);
  471. //ConVar cl_visualize_physics_shadows("cl_visualize_physics_shadows","0");
  472. struct blocklist_t
  473. {
  474. C_BaseEntity *pEntity;
  475. int firstBlockFrame;
  476. int lastBlockFrame;
  477. };
  478. static blocklist_t g_BlockList[4];
  479. bool IsBlockedShouldDisableCollisions( C_BaseEntity *pEntity )
  480. {
  481. int listCount = ARRAYSIZE(g_BlockList);
  482. int available = -1;
  483. for ( int i = 0; i < listCount; i++ )
  484. {
  485. if ( gpGlobals->framecount - g_BlockList[i].lastBlockFrame > 4 )
  486. {
  487. available = i;
  488. g_BlockList[i].pEntity = NULL;
  489. }
  490. if ( g_BlockList[i].pEntity == pEntity )
  491. {
  492. available = i;
  493. break;
  494. }
  495. }
  496. if ( available )
  497. {
  498. if ( g_BlockList[available].pEntity != pEntity )
  499. {
  500. g_BlockList[available].pEntity = pEntity;
  501. g_BlockList[available].firstBlockFrame = gpGlobals->framecount;
  502. }
  503. g_BlockList[available].lastBlockFrame = gpGlobals->framecount;
  504. if ( g_BlockList[available].lastBlockFrame - g_BlockList[available].firstBlockFrame > 2 )
  505. return true;
  506. }
  507. return false;
  508. }
  509. ConVar cl_phys_show_active( "cl_phys_show_active", "0", FCVAR_CHEAT );
  510. void CPhysicsSystem::PhysicsSimulate()
  511. {
  512. CMiniProfilerGuard mpg(&g_mp_PhysicsSimulate);
  513. VPROF_BUDGET( "CPhysicsSystem::PhysicsSimulate", VPROF_BUDGETGROUP_PHYSICS );
  514. float frametime = gpGlobals->frametime;
  515. if ( physenv )
  516. {
  517. if( physenv->IsPredicted() )
  518. {
  519. if( !prediction->InPrediction() )
  520. return;
  521. if( !prediction->IsFirstTimePredicted() )
  522. {
  523. //Don't actually simulate. Fake it while restoring results from the first time
  524. physenv->RestorePredictedSimulation();
  525. int activeCount = physenv->GetActiveObjectCount();
  526. if ( activeCount )
  527. {
  528. IPhysicsObject **pActiveList = NULL;
  529. pActiveList = (IPhysicsObject **)stackalloc( sizeof(IPhysicsObject *)*activeCount );
  530. physenv->GetActiveObjects( pActiveList );
  531. for ( int i = 0; i < activeCount; i++ )
  532. {
  533. CBaseEntity *pEntity = reinterpret_cast<CBaseEntity *>(pActiveList[i]->GetGameData());
  534. if ( pEntity )
  535. {
  536. if ( pEntity->CollisionProp()->DoesVPhysicsInvalidateSurroundingBox() )
  537. {
  538. pEntity->CollisionProp()->MarkSurroundingBoundsDirty();
  539. }
  540. pEntity->VPhysicsUpdate( pActiveList[i] );
  541. }
  542. }
  543. stackfree( pActiveList );
  544. }
  545. if( g_ShadowEntities.Count() > 0 )
  546. {
  547. VPROF( "PhysFrame VPhysicsShadowUpdate" );
  548. for ( int i = g_ShadowEntities.Head(); i != g_ShadowEntities.InvalidIndex(); i = g_ShadowEntities.Next(i) )
  549. {
  550. CBaseEntity *pEntity = g_ShadowEntities[i];
  551. IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject();
  552. // apply updates
  553. if ( pPhysics && !pPhysics->IsAsleep() )
  554. {
  555. pEntity->VPhysicsShadowUpdate( pPhysics );
  556. }
  557. }
  558. }
  559. return;
  560. }
  561. }
  562. g_Collisions.BufferTouchEvents( true );
  563. if( phys_debug_check_contacts.GetBool() && physenv )
  564. {
  565. physenv->DebugCheckContacts();
  566. }
  567. frametime *= cl_phys_timescale.GetFloat();
  568. int maxTicks = cl_phys_maxticks.GetInt();
  569. if ( maxTicks )
  570. {
  571. float maxFrameTime = physenv->GetDeltaFrameTime( maxTicks ) - 1e-4f;
  572. frametime = clamp( frametime, 0, maxFrameTime );
  573. }
  574. physenv->Simulate( frametime );
  575. int activeCount = physenv->GetActiveObjectCount();
  576. g_mp_active_object_count.Add(activeCount);
  577. IPhysicsObject **pActiveList = NULL;
  578. if ( activeCount )
  579. {
  580. PHYS_PROFILE(aUpdateActiveObjects)
  581. pActiveList = (IPhysicsObject **)stackalloc( sizeof(IPhysicsObject *)*activeCount );
  582. physenv->GetActiveObjects( pActiveList );
  583. for ( int i = 0; i < activeCount; i++ )
  584. {
  585. C_BaseEntity *pEntity = reinterpret_cast<C_BaseEntity *>(pActiveList[i]->GetGameData());
  586. if ( pEntity )
  587. {
  588. //const CCollisionProperty *collProp = pEntity->CollisionProp();
  589. //debugoverlay->AddBoxOverlay( collProp->GetCollisionOrigin(), collProp->OBBMins(), collProp->OBBMaxs(), collProp->GetCollisionAngles(), 190, 190, 0, 0, 0.01 );
  590. if ( pEntity->CollisionProp()->DoesVPhysicsInvalidateSurroundingBox() )
  591. {
  592. pEntity->CollisionProp()->MarkSurroundingBoundsDirty();
  593. }
  594. pEntity->VPhysicsUpdate( pActiveList[i] );
  595. IPhysicsShadowController *pShadow = pActiveList[i]->GetShadowController();
  596. if ( pShadow )
  597. {
  598. // active shadow object, check for error
  599. Vector pos, targetPos;
  600. QAngle rot, targetAngles;
  601. pShadow->GetTargetPosition( &targetPos, &targetAngles );
  602. pActiveList[i]->GetPosition( &pos, &rot );
  603. Vector delta = targetPos - pos;
  604. float dist = VectorNormalize(delta);
  605. bool bBlocked = false;
  606. if ( dist > cl_phys_block_dist.GetFloat() )
  607. {
  608. Vector vel;
  609. pActiveList[i]->GetImplicitVelocity( &vel, NULL );
  610. float proj = DotProduct(vel, delta);
  611. if ( proj < dist * cl_phys_block_fraction.GetFloat() )
  612. {
  613. bBlocked = true;
  614. //Msg("%s was blocked %.3f (%.3f proj)!\n", pEntity->GetClassname(), dist, proj );
  615. }
  616. }
  617. Vector targetAxis;
  618. float deltaTargetAngle;
  619. RotationDeltaAxisAngle( rot, targetAngles, targetAxis, deltaTargetAngle );
  620. if ( fabsf(deltaTargetAngle) > 0.5f )
  621. {
  622. AngularImpulse angVel;
  623. pActiveList[i]->GetImplicitVelocity( NULL, &angVel );
  624. float proj = DotProduct( angVel, targetAxis ) * Sign(deltaTargetAngle);
  625. if ( proj < (fabsf(deltaTargetAngle) * cl_phys_block_fraction.GetFloat()) )
  626. {
  627. bBlocked = true;
  628. //Msg("%s was rot blocked %.3f proj %.3f!\n", pEntity->GetClassname(), deltaTargetAngle, proj );
  629. }
  630. }
  631. if ( bBlocked )
  632. {
  633. C_BaseEntity *pBlocker = FindPhysicsBlocker( pActiveList[i] );
  634. if ( pBlocker )
  635. {
  636. if ( IsBlockedShouldDisableCollisions( pEntity ) )
  637. {
  638. PhysDisableEntityCollisions( pEntity, pBlocker );
  639. pActiveList[i]->RecheckContactPoints();
  640. // GetClassname returns a pointer to the same buffer always!
  641. //Msg("%s blocked !", pEntity->GetClassname() ); Msg("by %s\n", pBlocker->GetClassname() );
  642. }
  643. }
  644. }
  645. }
  646. }
  647. }
  648. if ( cl_phys_show_active.GetBool() )
  649. {
  650. for ( int i = 0; i < activeCount; i++ )
  651. {
  652. CBaseEntity *pEntity = reinterpret_cast<CBaseEntity *>(pActiveList[i]->GetGameData());
  653. if ( pEntity )
  654. {
  655. //debugoverlay->Cross3D( pEntity->GetAbsOrigin(), 12, 255, 0, 0, false, 0 );
  656. debugoverlay->AddBoxOverlay( pEntity->GetAbsOrigin(), pEntity->CollisionProp()->OBBMins(), pEntity->CollisionProp()->OBBMaxs(), pEntity->GetAbsAngles(), 255, 255, 0, 8, 0 );
  657. }
  658. }
  659. }
  660. }
  661. if( g_ShadowEntities.Count() > 0 )
  662. {
  663. VPROF( "PhysFrame VPhysicsShadowUpdate" );
  664. for ( int i = g_ShadowEntities.Head(); i != g_ShadowEntities.InvalidIndex(); i = g_ShadowEntities.Next(i) )
  665. {
  666. CBaseEntity *pEntity = g_ShadowEntities[i];
  667. IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject();
  668. // apply updates
  669. if ( pPhysics && !pPhysics->IsAsleep() )
  670. {
  671. pEntity->VPhysicsShadowUpdate( pPhysics );
  672. }
  673. }
  674. }
  675. #if 0
  676. if ( cl_visualize_physics_shadows.GetBool() )
  677. {
  678. int entityCount = NUM_ENT_ENTRIES;
  679. for ( int i = 0; i < entityCount; i++ )
  680. {
  681. IClientEntity *pClientEnt = cl_entitylist->GetClientEntity(i);
  682. if ( !pClientEnt )
  683. continue;
  684. C_BaseEntity *pEntity = pClientEnt->GetBaseEntity();
  685. if ( !pEntity )
  686. continue;
  687. Vector pos;
  688. QAngle angle;
  689. IPhysicsObject *pObj = pEntity->VPhysicsGetObject();
  690. if ( !pObj || !pObj->GetShadowController() )
  691. continue;
  692. pObj->GetShadowPosition( &pos, &angle );
  693. debugoverlay->AddBoxOverlay( pos, pEntity->CollisionProp()->OBBMins(), pEntity->CollisionProp()->OBBMaxs(), angle, 255, 255, 0, 32, 0 );
  694. char tmp[256];
  695. V_snprintf( tmp, sizeof(tmp),"%s, (%s)\n", pEntity->GetClassname(), VecToString(angle) );
  696. debugoverlay->AddTextOverlay( pos, 0, tmp );
  697. }
  698. }
  699. #endif
  700. g_Collisions.BufferTouchEvents( false );
  701. g_Collisions.FrameUpdate();
  702. }
  703. physicssound::PlayImpactSounds( m_impactSounds );
  704. }
  705. void PhysicsSimulate()
  706. {
  707. g_PhysicsSystem.PhysicsSimulate();
  708. }
  709. void PhysSetPredictionCommandNum( int iCommandNum )
  710. {
  711. physenv->SetPredictionCommandNum( iCommandNum );
  712. }
  713. CCollisionEvent::CCollisionEvent( void )
  714. {
  715. m_flLastSplashTime = 0.0f;
  716. }
  717. void CCollisionEvent::ObjectSound( int index, vcollisionevent_t *pEvent )
  718. {
  719. IPhysicsObject *pObject = pEvent->pObjects[index];
  720. if ( !pObject || pObject->IsStatic() )
  721. return;
  722. float speed = pEvent->collisionSpeed * pEvent->collisionSpeed;
  723. int surfaceProps = pEvent->surfaceProps[index];
  724. void *pGameData = pObject->GetGameData();
  725. if ( pGameData )
  726. {
  727. float volume = speed * (1.0f/(320.0f*320.0f)); // max volume at 320 in/s
  728. if ( volume > 1.0f )
  729. volume = 1.0f;
  730. if ( surfaceProps >= 0 )
  731. {
  732. g_PhysicsSystem.AddImpactSound( pGameData, pObject, surfaceProps, pEvent->surfaceProps[!index], volume, speed );
  733. }
  734. }
  735. }
  736. void CCollisionEvent::PostCollision( vcollisionevent_t *pEvent )
  737. {
  738. CallbackContext callback(this);
  739. if ( pEvent->deltaCollisionTime > 0.1f && pEvent->collisionSpeed > 70 )
  740. {
  741. ObjectSound( 0, pEvent );
  742. ObjectSound( 1, pEvent );
  743. }
  744. }
  745. //-----------------------------------------------------------------------------
  746. // Purpose:
  747. //-----------------------------------------------------------------------------
  748. void CCollisionEvent::FrameUpdate( void )
  749. {
  750. UpdateFrictionSounds();
  751. UpdateTouchEvents();
  752. UpdateFluidEvents();
  753. UpdatePenetrateEvents();
  754. }
  755. void CCollisionEvent::LevelShutdown()
  756. {
  757. m_penetrateEvents.RemoveAll();
  758. m_flLastSplashTime = 0.0f;
  759. }
  760. //-----------------------------------------------------------------------------
  761. // Purpose:
  762. //-----------------------------------------------------------------------------
  763. void CCollisionEvent::UpdateTouchEvents( void )
  764. {
  765. // Turn on buffering in case new touch events occur during processing
  766. bool bOldTouchEvents = m_bBufferTouchEvents;
  767. m_bBufferTouchEvents = true;
  768. for ( int i = 0; i < m_touchEvents.Count(); i++ )
  769. {
  770. const touchevent_t &event = m_touchEvents[i];
  771. if ( event.touchType == TOUCH_START )
  772. {
  773. DispatchStartTouch( event.pEntity0, event.pEntity1, event.endPoint, event.normal );
  774. }
  775. else
  776. {
  777. // TOUCH_END
  778. DispatchEndTouch( event.pEntity0, event.pEntity1 );
  779. }
  780. }
  781. m_touchEvents.RemoveAll();
  782. m_bBufferTouchEvents = bOldTouchEvents;
  783. }
  784. //-----------------------------------------------------------------------------
  785. // Purpose:
  786. // Input : *pEntity0 -
  787. // *pEntity1 -
  788. // touchType -
  789. //-----------------------------------------------------------------------------
  790. void CCollisionEvent::AddTouchEvent( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, int touchType, const Vector &point, const Vector &normal )
  791. {
  792. if ( !pEntity0 || !pEntity1 )
  793. return;
  794. int index = m_touchEvents.AddToTail();
  795. touchevent_t &event = m_touchEvents[index];
  796. event.pEntity0 = pEntity0;
  797. event.pEntity1 = pEntity1;
  798. event.touchType = touchType;
  799. event.endPoint = point;
  800. event.normal = normal;
  801. }
  802. //-----------------------------------------------------------------------------
  803. // Purpose:
  804. // Input : *pObject1 -
  805. // *pObject2 -
  806. // *pTouchData -
  807. //-----------------------------------------------------------------------------
  808. void CCollisionEvent::StartTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData )
  809. {
  810. CallbackContext callback(this);
  811. C_BaseEntity *pEntity1 = static_cast<C_BaseEntity *>(pObject1->GetGameData());
  812. C_BaseEntity *pEntity2 = static_cast<C_BaseEntity *>(pObject2->GetGameData());
  813. if ( !pEntity1 || !pEntity2 )
  814. return;
  815. Vector endPoint, normal;
  816. pTouchData->GetContactPoint( endPoint );
  817. pTouchData->GetSurfaceNormal( normal );
  818. if ( !m_bBufferTouchEvents )
  819. {
  820. DispatchStartTouch( pEntity1, pEntity2, endPoint, normal );
  821. }
  822. else
  823. {
  824. AddTouchEvent( pEntity1, pEntity2, TOUCH_START, endPoint, normal );
  825. }
  826. }
  827. //-----------------------------------------------------------------------------
  828. // Purpose:
  829. // Input : *pEntity0 -
  830. // *pEntity1 -
  831. //-----------------------------------------------------------------------------
  832. void CCollisionEvent::DispatchStartTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1, const Vector &point, const Vector &normal )
  833. {
  834. trace_t trace;
  835. memset( &trace, 0, sizeof(trace) );
  836. trace.endpos = point;
  837. trace.plane.dist = DotProduct( point, normal );
  838. trace.plane.normal = normal;
  839. // NOTE: This sets up the touch list for both entities, no call to pEntity1 is needed
  840. pEntity0->PhysicsMarkEntitiesAsTouchingEventDriven( pEntity1, trace );
  841. }
  842. //-----------------------------------------------------------------------------
  843. // Purpose:
  844. // Input : *pObject1 -
  845. // *pObject2 -
  846. // *pTouchData -
  847. //-----------------------------------------------------------------------------
  848. void CCollisionEvent::EndTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData )
  849. {
  850. CallbackContext callback(this);
  851. C_BaseEntity *pEntity1 = static_cast<C_BaseEntity *>(pObject1->GetGameData());
  852. C_BaseEntity *pEntity2 = static_cast<C_BaseEntity *>(pObject2->GetGameData());
  853. if ( !pEntity1 || !pEntity2 )
  854. return;
  855. if ( !m_bBufferTouchEvents )
  856. {
  857. DispatchEndTouch( pEntity1, pEntity2 );
  858. }
  859. else
  860. {
  861. AddTouchEvent( pEntity1, pEntity2, TOUCH_END, vec3_origin, vec3_origin );
  862. }
  863. }
  864. //-----------------------------------------------------------------------------
  865. // Purpose:
  866. // Input : *pEntity0 -
  867. // *pEntity1 -
  868. //-----------------------------------------------------------------------------
  869. void CCollisionEvent::DispatchEndTouch( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1 )
  870. {
  871. // frees the event-driven touchlinks
  872. pEntity0->PhysicsNotifyOtherOfUntouch( pEntity0, pEntity1 );
  873. pEntity1->PhysicsNotifyOtherOfUntouch( pEntity1, pEntity0 );
  874. }
  875. // NOTE: This assumes entity pointers are sorted to simplify search!
  876. void CCollisionEvent::FindOrAddPenetrateEvent( C_BaseEntity *pEntity0, C_BaseEntity *pEntity1 )
  877. {
  878. int count = m_penetrateEvents.Count();
  879. for ( int i = 0; i < count; i++ )
  880. {
  881. if ( m_penetrateEvents[i].pEntity0 == pEntity0 && m_penetrateEvents[i].pEntity1 == pEntity1 )
  882. {
  883. m_penetrateEvents[i].timeStamp = gpGlobals->curtime;
  884. return;
  885. }
  886. }
  887. int index = m_penetrateEvents.AddToTail();
  888. m_penetrateEvents[index].pEntity0 = pEntity0;
  889. m_penetrateEvents[index].pEntity1 = pEntity1;
  890. m_penetrateEvents[index].startTime = gpGlobals->curtime;
  891. m_penetrateEvents[index].timeStamp = gpGlobals->curtime;
  892. }
  893. // NOTE: This assumes entity pointers are sorted to simplify search!
  894. void CCollisionEvent::UpdatePenetrateEvents()
  895. {
  896. const float MAX_PENETRATION_TIME = 3.0f;
  897. for ( int i = m_penetrateEvents.Count()-1; i >= 0; --i )
  898. {
  899. float timeSincePenetration = gpGlobals->curtime - m_penetrateEvents[i].timeStamp;
  900. if ( timeSincePenetration > 0.1f )
  901. {
  902. m_penetrateEvents.FastRemove(i);
  903. continue;
  904. }
  905. float timeInPenetration = m_penetrateEvents[i].timeStamp - m_penetrateEvents[i].startTime;
  906. // it's been too long, just give up and disable collisions
  907. if ( timeInPenetration > MAX_PENETRATION_TIME )
  908. {
  909. PhysDisableEntityCollisions( m_penetrateEvents[i].pEntity0, m_penetrateEvents[i].pEntity1 );
  910. m_penetrateEvents.FastRemove(i);
  911. continue;
  912. }
  913. }
  914. }
  915. void CCollisionEvent::Friction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit, IPhysicsCollisionData *pData )
  916. {
  917. CallbackContext callback(this);
  918. if ( energy < 0.05f || surfaceProps < 0 )
  919. return;
  920. //Get our friction information
  921. Vector vecPos, vecVel;
  922. pData->GetContactPoint( vecPos );
  923. pObject->GetVelocityAtPoint( vecPos, &vecVel );
  924. CBaseEntity *pEntity = reinterpret_cast<CBaseEntity *>(pObject->GetGameData());
  925. if ( pEntity )
  926. {
  927. if ( pEntity->m_bClientSideRagdoll )
  928. return;
  929. friction_t *pFriction = g_Collisions.FindFriction( pEntity );
  930. if ( (gpGlobals->maxClients > 1) && pFriction && pFriction->pObject)
  931. {
  932. // in MP mode play sound and effects once every 500 msecs,
  933. // no ongoing updates, takes too much bandwidth
  934. if ( (pFriction->flLastEffectTime + 0.5f) > gpGlobals->curtime)
  935. {
  936. pFriction->flLastUpdateTime = gpGlobals->curtime;
  937. return;
  938. }
  939. }
  940. PhysFrictionSound( pEntity, pObject, energy, surfaceProps, surfacePropsHit );
  941. }
  942. PhysFrictionEffect( vecPos, vecVel, energy, surfaceProps, surfacePropsHit );
  943. }
  944. friction_t *CCollisionEvent::FindFriction( CBaseEntity *pObject )
  945. {
  946. friction_t *pFree = NULL;
  947. for ( int i = 0; i < ARRAYSIZE(m_current); i++ )
  948. {
  949. if ( !m_current[i].pObject && !pFree )
  950. pFree = &m_current[i];
  951. if ( m_current[i].pObject == pObject )
  952. return &m_current[i];
  953. }
  954. return pFree;
  955. }
  956. void CCollisionEvent::ShutdownFriction( friction_t &friction )
  957. {
  958. // Msg( "Scrape Stop %s \n", STRING(friction.pObject->m_iClassname) );
  959. CSoundEnvelopeController::GetController().SoundDestroy( friction.patch );
  960. friction.patch = NULL;
  961. friction.pObject = NULL;
  962. }
  963. void CCollisionEvent::UpdateFrictionSounds( void )
  964. {
  965. for ( int i = 0; i < ARRAYSIZE(m_current); i++ )
  966. {
  967. if ( m_current[i].patch )
  968. {
  969. if ( m_current[i].flLastUpdateTime < (gpGlobals->curtime-0.1f) )
  970. {
  971. // friction wasn't updated the last 100msec, assume fiction finished
  972. ShutdownFriction( m_current[i] );
  973. }
  974. }
  975. }
  976. }
  977. //-----------------------------------------------------------------------------
  978. // Purpose:
  979. // Input : &matrix -
  980. // &normal -
  981. // Output : static int
  982. //-----------------------------------------------------------------------------
  983. static int BestAxisMatchingNormal( matrix3x4_t &matrix, const Vector &normal )
  984. {
  985. float bestDot = -1;
  986. int best = 0;
  987. for ( int i = 0; i < 3; i++ )
  988. {
  989. Vector tmp;
  990. MatrixGetColumn( matrix, i, tmp );
  991. float dot = fabs(DotProduct( tmp, normal ));
  992. if ( dot > bestDot )
  993. {
  994. bestDot = dot;
  995. best = i;
  996. }
  997. }
  998. return best;
  999. }
  1000. //-----------------------------------------------------------------------------
  1001. // Purpose:
  1002. // Input : *pFluid -
  1003. // *pObject -
  1004. // *pEntity -
  1005. //-----------------------------------------------------------------------------
  1006. void PhysicsSplash( IPhysicsFluidController *pFluid, IPhysicsObject *pObject, CBaseEntity *pEntity )
  1007. {
  1008. //FIXME: For now just allow ragdolls for E3 - jdw
  1009. if ( ( pObject->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL ) == false )
  1010. return;
  1011. Vector velocity;
  1012. pObject->GetVelocity( &velocity, NULL );
  1013. float impactSpeed = velocity.Length();
  1014. if ( impactSpeed < 25.0f )
  1015. return;
  1016. Vector normal;
  1017. float dist;
  1018. pFluid->GetSurfacePlane( &normal, &dist );
  1019. matrix3x4_t &matrix = pEntity->EntityToWorldTransform();
  1020. // Find the local axis that best matches the water surface normal
  1021. int bestAxis = BestAxisMatchingNormal( matrix, normal );
  1022. Vector tangent, binormal;
  1023. MatrixGetColumn( matrix, (bestAxis+1)%3, tangent );
  1024. binormal = CrossProduct( normal, tangent );
  1025. VectorNormalize( binormal );
  1026. tangent = CrossProduct( binormal, normal );
  1027. VectorNormalize( tangent );
  1028. // Now we have a basis tangent to the surface that matches the object's local orientation as well as possible
  1029. // compute an OBB using this basis
  1030. // Get object extents in basis
  1031. Vector tanPts[2], binPts[2];
  1032. tanPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -tangent );
  1033. tanPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), tangent );
  1034. binPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -binormal );
  1035. binPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), binormal );
  1036. // now compute the centered bbox
  1037. float mins[2], maxs[2], center[2], extents[2];
  1038. mins[0] = DotProduct( tanPts[0], tangent );
  1039. maxs[0] = DotProduct( tanPts[1], tangent );
  1040. mins[1] = DotProduct( binPts[0], binormal );
  1041. maxs[1] = DotProduct( binPts[1], binormal );
  1042. center[0] = 0.5 * (mins[0] + maxs[0]);
  1043. center[1] = 0.5 * (mins[1] + maxs[1]);
  1044. extents[0] = maxs[0] - center[0];
  1045. extents[1] = maxs[1] - center[1];
  1046. Vector centerPoint = center[0] * tangent + center[1] * binormal + dist * normal;
  1047. Vector axes[2];
  1048. axes[0] = (maxs[0] - center[0]) * tangent;
  1049. axes[1] = (maxs[1] - center[1]) * binormal;
  1050. // visualize OBB hit
  1051. /*
  1052. Vector corner1 = centerPoint - axes[0] - axes[1];
  1053. Vector corner2 = centerPoint + axes[0] - axes[1];
  1054. Vector corner3 = centerPoint + axes[0] + axes[1];
  1055. Vector corner4 = centerPoint - axes[0] + axes[1];
  1056. NDebugOverlay::Line( corner1, corner2, 0, 0, 255, false, 10 );
  1057. NDebugOverlay::Line( corner2, corner3, 0, 0, 255, false, 10 );
  1058. NDebugOverlay::Line( corner3, corner4, 0, 0, 255, false, 10 );
  1059. NDebugOverlay::Line( corner4, corner1, 0, 0, 255, false, 10 );
  1060. */
  1061. Vector corner[4];
  1062. corner[0] = centerPoint - axes[0] - axes[1];
  1063. corner[1] = centerPoint + axes[0] - axes[1];
  1064. corner[2] = centerPoint + axes[0] + axes[1];
  1065. corner[3] = centerPoint - axes[0] + axes[1];
  1066. int contents = enginetrace->GetPointContents( centerPoint-Vector(0,0,2), MASK_WATER );
  1067. bool bInSlime = ( contents & CONTENTS_SLIME ) ? true : false;
  1068. Vector color = vec3_origin;
  1069. float luminosity = 1.0f;
  1070. if ( !bInSlime )
  1071. {
  1072. // Get our lighting information
  1073. FX_GetSplashLighting( centerPoint + ( normal * 8.0f ), &color, &luminosity );
  1074. }
  1075. if ( impactSpeed > 150 )
  1076. {
  1077. if ( bInSlime )
  1078. {
  1079. FX_GunshotSlimeSplash( centerPoint, normal, random->RandomFloat( 8, 10 ) );
  1080. }
  1081. else
  1082. {
  1083. FX_GunshotSplash( centerPoint, normal, random->RandomFloat( 8, 10 ) );
  1084. }
  1085. }
  1086. else if ( !bInSlime )
  1087. {
  1088. FX_WaterRipple( centerPoint, 1.5f, &color, 1.5f, luminosity );
  1089. }
  1090. int splashes = 4;
  1091. Vector point;
  1092. for ( int i = 0; i < splashes; i++ )
  1093. {
  1094. point = RandomVector( -32.0f, 32.0f );
  1095. point[2] = 0.0f;
  1096. point += corner[i];
  1097. if ( impactSpeed > 150 )
  1098. {
  1099. if ( bInSlime )
  1100. {
  1101. FX_GunshotSlimeSplash( centerPoint, normal, random->RandomFloat( 4, 6 ) );
  1102. }
  1103. else
  1104. {
  1105. FX_GunshotSplash( centerPoint, normal, random->RandomFloat( 4, 6 ) );
  1106. }
  1107. }
  1108. else if ( !bInSlime )
  1109. {
  1110. FX_WaterRipple( point, random->RandomFloat( 0.25f, 0.5f ), &color, luminosity, random->RandomFloat( 0.5f, 1.0f ) );
  1111. }
  1112. }
  1113. }
  1114. //-----------------------------------------------------------------------------
  1115. // Purpose:
  1116. //-----------------------------------------------------------------------------
  1117. void CCollisionEvent::UpdateFluidEvents( void )
  1118. {
  1119. for ( int i = m_fluidEvents.Count()-1; i >= 0; --i )
  1120. {
  1121. if ( (gpGlobals->curtime - m_fluidEvents[i].impactTime) > FLUID_TIME_MAX )
  1122. {
  1123. m_fluidEvents.FastRemove(i);
  1124. }
  1125. }
  1126. }
  1127. //-----------------------------------------------------------------------------
  1128. // Purpose:
  1129. // Input : *pEntity -
  1130. // Output : float
  1131. //-----------------------------------------------------------------------------
  1132. float CCollisionEvent::DeltaTimeSinceLastFluid( CBaseEntity *pEntity )
  1133. {
  1134. for ( int i = m_fluidEvents.Count()-1; i >= 0; --i )
  1135. {
  1136. if ( m_fluidEvents[i].hEntity.Get() == pEntity )
  1137. {
  1138. return gpGlobals->curtime - m_fluidEvents[i].impactTime;
  1139. }
  1140. }
  1141. int index = m_fluidEvents.AddToTail();
  1142. m_fluidEvents[index].hEntity = pEntity;
  1143. m_fluidEvents[index].impactTime = gpGlobals->curtime;
  1144. return FLUID_TIME_MAX;
  1145. }
  1146. //-----------------------------------------------------------------------------
  1147. // Purpose:
  1148. // Input : *pObject -
  1149. // *pFluid -
  1150. //-----------------------------------------------------------------------------
  1151. void CCollisionEvent::FluidStartTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid )
  1152. {
  1153. CallbackContext callback(this);
  1154. if ( ( pObject == NULL ) || ( pFluid == NULL ) )
  1155. return;
  1156. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObject->GetGameData());
  1157. if ( pEntity )
  1158. {
  1159. float timeSinceLastCollision = DeltaTimeSinceLastFluid( pEntity );
  1160. if ( timeSinceLastCollision < 0.5f )
  1161. return;
  1162. // We are generating too many splashes in CStrike15 as well, so enable this
  1163. #if defined( INFESTED_DLL ) || defined( CSTRIKE15 )
  1164. // prevent too many splashes spawning at once across different entities
  1165. float flGlobalTimeSinceLastSplash = gpGlobals->curtime - m_flLastSplashTime;
  1166. if ( flGlobalTimeSinceLastSplash < 0.1f )
  1167. return;
  1168. #endif
  1169. //Msg( "ent %d %s doing splash. delta = %f\n", pEntity->entindex(), pEntity->GetModelName(), timeSinceLastCollision );
  1170. PhysicsSplash( pFluid, pObject, pEntity );
  1171. m_flLastSplashTime = gpGlobals->curtime;
  1172. }
  1173. }
  1174. //-----------------------------------------------------------------------------
  1175. // Purpose:
  1176. // Input : *pObject -
  1177. // *pFluid -
  1178. //-----------------------------------------------------------------------------
  1179. void CCollisionEvent::FluidEndTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid )
  1180. {
  1181. CallbackContext callback(this);
  1182. //FIXME: Do nothing for now
  1183. }
  1184. IPhysicsObject *GetWorldPhysObject ( void )
  1185. {
  1186. return g_PhysWorldObject;
  1187. }
  1188. void PhysFrictionSound( CBaseEntity *pEntity, IPhysicsObject *pObject, const char *pSoundName, HSOUNDSCRIPTHASH& handle, float flVolume )
  1189. {
  1190. if ( !pEntity )
  1191. return;
  1192. // cut out the quiet sounds
  1193. // UNDONE: Separate threshold for starting a sound vs. continuing?
  1194. flVolume = clamp( flVolume, 0.0f, 1.0f );
  1195. if ( flVolume > (1.0f/128.0f) )
  1196. {
  1197. friction_t *pFriction = g_Collisions.FindFriction( pEntity );
  1198. if ( !pFriction )
  1199. return;
  1200. CSoundParameters params;
  1201. if ( !CBaseEntity::GetParametersForSound( pSoundName, handle, params, NULL ) )
  1202. return;
  1203. if ( !pFriction->pObject )
  1204. {
  1205. // don't create really quiet scrapes
  1206. if ( params.volume * flVolume <= 0.1f )
  1207. return;
  1208. pFriction->pObject = pEntity;
  1209. CPASAttenuationFilter filter( pEntity, params.soundlevel );
  1210. int entindex = pEntity->entindex();
  1211. // clientside created entites doesn't have a valid entindex, let 'world' play the sound for them
  1212. if ( entindex < 0 )
  1213. entindex = 0;
  1214. pFriction->patch = CSoundEnvelopeController::GetController().SoundCreate(
  1215. filter, entindex, CHAN_BODY, pSoundName, params.soundlevel );
  1216. CSoundEnvelopeController::GetController().Play( pFriction->patch, params.volume * flVolume, params.pitch );
  1217. }
  1218. else
  1219. {
  1220. float pitch = (flVolume * (params.pitchhigh - params.pitchlow)) + params.pitchlow;
  1221. CSoundEnvelopeController::GetController().SoundChangeVolume( pFriction->patch, params.volume * flVolume, 0.1f );
  1222. CSoundEnvelopeController::GetController().SoundChangePitch( pFriction->patch, pitch, 0.1f );
  1223. }
  1224. pFriction->flLastUpdateTime = gpGlobals->curtime;
  1225. pFriction->flLastEffectTime = gpGlobals->curtime;
  1226. }
  1227. }
  1228. void PhysCleanupFrictionSounds( CBaseEntity *pEntity )
  1229. {
  1230. friction_t *pFriction = g_Collisions.FindFriction( pEntity );
  1231. if ( pFriction && pFriction->patch )
  1232. {
  1233. g_Collisions.ShutdownFriction( *pFriction );
  1234. }
  1235. }
  1236. float PhysGetNextSimTime()
  1237. {
  1238. return physenv->GetSimulationTime() + gpGlobals->frametime * cl_phys_timescale.GetFloat();
  1239. }
  1240. float PhysGetSyncCreateTime()
  1241. {
  1242. float nextTime = physenv->GetNextFrameTime();
  1243. float simTime = PhysGetNextSimTime();
  1244. if ( nextTime < simTime )
  1245. {
  1246. // The next simulation frame begins before the end of this frame
  1247. // so create physics objects at that time so that they will reach the current
  1248. // position at curtime. Otherwise the physics object will simulate forward from curtime
  1249. // and pop into the future a bit at this point of transition
  1250. return gpGlobals->curtime + nextTime - simTime;
  1251. }
  1252. return gpGlobals->curtime;
  1253. }
  1254. void VPhysicsShadowDataChanged( bool bCreate, C_BaseEntity *pEntity )
  1255. {
  1256. // client-side vphysics shadow management
  1257. if ( bCreate && !pEntity->VPhysicsGetObject() && !(pEntity->GetSolidFlags() & FSOLID_NOT_SOLID) )
  1258. {
  1259. if ( pEntity->GetSolid() != SOLID_BSP )
  1260. {
  1261. pEntity->SetSolid(SOLID_VPHYSICS);
  1262. }
  1263. if ( pEntity->GetSolidFlags() & FSOLID_NOT_MOVEABLE )
  1264. {
  1265. pEntity->VPhysicsInitStatic();
  1266. }
  1267. else
  1268. {
  1269. pEntity->VPhysicsInitShadow( false, false );
  1270. }
  1271. }
  1272. else if ( pEntity->VPhysicsGetObject() && !pEntity->VPhysicsGetObject()->IsStatic() )
  1273. {
  1274. float interpTime = pEntity->GetInterpolationAmount(LATCH_SIMULATION_VAR);
  1275. // this is the client time the network origin will become the entity's render origin
  1276. float schedTime = pEntity->m_flSimulationTime + interpTime;
  1277. // how far is that from now
  1278. float deltaTime = schedTime - gpGlobals->curtime;
  1279. // Compute that time on the client vphysics clock
  1280. float physTime = physenv->GetSimulationTime() + deltaTime + gpGlobals->frametime;
  1281. // arrival time is relative to the next tick
  1282. float arrivalTime = physTime - physenv->GetNextFrameTime();
  1283. if ( arrivalTime < 0 )
  1284. arrivalTime = 0;
  1285. pEntity->VPhysicsGetObject()->UpdateShadow( pEntity->GetNetworkOrigin(), pEntity->GetNetworkAngles(), false, arrivalTime );
  1286. }
  1287. }