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.

1067 lines
32 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Game & Client shared functions moved from physics.cpp
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "vcollide_parse.h"
  8. #include "filesystem.h"
  9. #include "movevars_shared.h"
  10. #include "engine/ivmodelinfo.h"
  11. #include "physics_shared.h"
  12. #include "solidsetdefaults.h"
  13. #include "model_types.h"
  14. #include "bone_setup.h"
  15. #include "vphysics/object_hash.h"
  16. #include "vphysics/friction.h"
  17. #include "coordsize.h"
  18. #include <keyvalues.h>
  19. #include "decals.h"
  20. #include "IEffects.h"
  21. #include "SoundEmitterSystem/isoundemittersystembase.h"
  22. #include "particle_parse.h"
  23. #include "physics_saverestore.h"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. //
  27. IPhysics *physics = NULL;
  28. IPhysicsObject *g_PhysWorldObject = NULL;
  29. IPhysicsCollision *physcollision = NULL;
  30. IPhysicsEnvironment *physenv = NULL;
  31. #ifdef PORTAL
  32. IPhysicsEnvironment *physenv_main = NULL;
  33. #endif
  34. IPhysicsSurfaceProps *physprops = NULL;
  35. // UNDONE: This hash holds both entity & IPhysicsObject pointer pairs
  36. // UNDONE: Split into separate hashes?
  37. IPhysicsObjectPairHash *g_EntityCollisionHash = NULL;
  38. const char *SURFACEPROP_MANIFEST_FILE = "scripts/surfaceproperties_manifest.txt";
  39. const objectparams_t g_PhysDefaultObjectParams =
  40. {
  41. NULL,
  42. 1.0, //mass
  43. 1.0, // inertia
  44. 0.1f, // damping
  45. 0.1f, // rotdamping
  46. 0.05f, // rotIntertiaLimit
  47. "DEFAULT",
  48. NULL,// game data
  49. 0.f, // volume (leave 0 if you don't have one or call physcollision->CollideVolume() to compute it)
  50. 1.0f, // drag coefficient
  51. true,// enable collisions?
  52. };
  53. PRECACHE_REGISTER_BEGIN( GLOBAL, PhysFrictionEffect )
  54. #ifndef DOTA_DLL
  55. PRECACHE( PARTICLE_SYSTEM, "impact_physics_dust" )
  56. PRECACHE( PARTICLE_SYSTEM, "impact_physics_sparks" )
  57. #endif
  58. PRECACHE_REGISTER_END()
  59. void CSolidSetDefaults::SetDefaults( void *pData )
  60. {
  61. solid_t *pSolid = (solid_t *)pData;
  62. pSolid->params = g_PhysDefaultObjectParams;
  63. }
  64. CSolidSetDefaults g_SolidSetup;
  65. //-----------------------------------------------------------------------------
  66. // Purpose:
  67. // Input : &mins -
  68. // &maxs -
  69. // Output : CPhysCollide
  70. //-----------------------------------------------------------------------------
  71. CPhysCollide *PhysCreateBbox( const Vector &minsIn, const Vector &maxsIn )
  72. {
  73. // UNDONE: Track down why this causes errors for the player controller and adjust/enable
  74. //float radius = 0.5 - DIST_EPSILON;
  75. Vector mins = minsIn;// + Vector(radius, radius, radius);
  76. Vector maxs = maxsIn;// - Vector(radius, radius, radius);
  77. // VPHYSICS caches/cleans up these
  78. CPhysCollide *pResult = physcollision->BBoxToCollide( mins, maxs );
  79. g_pPhysSaveRestoreManager->NoteBBox( mins, maxs, pResult );
  80. return pResult;
  81. }
  82. //-----------------------------------------------------------------------------
  83. // Purpose:
  84. // Input : *pEntity -
  85. // &mins -
  86. // &maxs -
  87. // &origin -
  88. // isStatic -
  89. // Output : static IPhysicsObject
  90. //-----------------------------------------------------------------------------
  91. IPhysicsObject *PhysModelCreateBox( CBaseEntity *pEntity, const Vector &mins, const Vector &maxs, const Vector &origin, bool isStatic )
  92. {
  93. int modelIndex = pEntity->GetModelIndex();
  94. const char *pSurfaceProps = "flesh";
  95. solid_t solid;
  96. PhysGetDefaultAABBSolid( solid );
  97. Vector dims = maxs - mins;
  98. solid.params.volume = dims.x * dims.y * dims.z;
  99. if ( modelIndex )
  100. {
  101. const model_t *model = modelinfo->GetModel( modelIndex );
  102. if ( model )
  103. {
  104. CStudioHdr studioHdr( modelinfo->GetStudiomodel( model ), mdlcache );
  105. if ( studioHdr.IsValid() )
  106. {
  107. pSurfaceProps = Studio_GetDefaultSurfaceProps( &studioHdr );
  108. }
  109. }
  110. }
  111. Q_strncpy( solid.surfaceprop, pSurfaceProps, sizeof( solid.surfaceprop ) );
  112. CPhysCollide *pCollide = PhysCreateBbox( mins, maxs );
  113. if ( !pCollide )
  114. return NULL;
  115. return PhysModelCreateCustom( pEntity, pCollide, origin, vec3_angle, STRING(pEntity->GetModelName()), isStatic, &solid );
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Purpose:
  119. // Input : *pEntity -
  120. // &mins -
  121. // &maxs -
  122. // &origin -
  123. // isStatic -
  124. // Output : static IPhysicsObject
  125. //-----------------------------------------------------------------------------
  126. IPhysicsObject *PhysModelCreateOBB( CBaseEntity *pEntity, const Vector &mins, const Vector &maxs, const Vector &origin, const QAngle &angle, bool isStatic )
  127. {
  128. int modelIndex = pEntity->GetModelIndex();
  129. const char *pSurfaceProps = "flesh";
  130. solid_t solid;
  131. PhysGetDefaultAABBSolid( solid );
  132. Vector dims = maxs - mins;
  133. solid.params.volume = dims.x * dims.y * dims.z;
  134. if ( modelIndex )
  135. {
  136. const model_t *model = modelinfo->GetModel( modelIndex );
  137. if ( model )
  138. {
  139. CStudioHdr studioHdr( modelinfo->GetStudiomodel( model ), mdlcache );
  140. if (studioHdr.IsValid())
  141. {
  142. pSurfaceProps = Studio_GetDefaultSurfaceProps( &studioHdr );
  143. }
  144. }
  145. }
  146. Q_strncpy( solid.surfaceprop, pSurfaceProps, sizeof( solid.surfaceprop ) );
  147. CPhysCollide *pCollide = PhysCreateBbox( mins, maxs );
  148. if ( !pCollide )
  149. return NULL;
  150. return PhysModelCreateCustom( pEntity, pCollide, origin, angle, STRING(pEntity->GetModelName()), isStatic, &solid );
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. // Input : &solid -
  155. // *pEntity -
  156. // modelIndex -
  157. // solidIndex -
  158. // Output : Returns true on success, false on failure.
  159. //-----------------------------------------------------------------------------
  160. bool PhysModelParseSolidByIndex( solid_t &solid, CBaseEntity *pEntity, int modelIndex, int solidIndex )
  161. {
  162. vcollide_t *pCollide = modelinfo->GetVCollide( modelIndex );
  163. if ( !pCollide )
  164. return false;
  165. bool parsed = false;
  166. memset( &solid, 0, sizeof(solid) );
  167. solid.params = g_PhysDefaultObjectParams;
  168. IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide );
  169. while ( !pParse->Finished() )
  170. {
  171. const char *pBlock = pParse->GetCurrentBlockName();
  172. if ( !strcmpi( pBlock, "solid" ) )
  173. {
  174. solid_t tmpSolid;
  175. memset( &tmpSolid, 0, sizeof(tmpSolid) );
  176. tmpSolid.params = g_PhysDefaultObjectParams;
  177. pParse->ParseSolid( &tmpSolid, &g_SolidSetup );
  178. if ( solidIndex < 0 || tmpSolid.index == solidIndex )
  179. {
  180. parsed = true;
  181. solid = tmpSolid;
  182. // just to be sure we aren't ever getting a non-zero solid by accident
  183. Assert( solidIndex >= 0 || solid.index == 0 );
  184. break;
  185. }
  186. }
  187. else
  188. {
  189. pParse->SkipBlock();
  190. }
  191. }
  192. physcollision->VPhysicsKeyParserDestroy( pParse );
  193. // collisions are off by default
  194. solid.params.enableCollisions = true;
  195. solid.params.pGameData = static_cast<void *>(pEntity);
  196. solid.params.pName = STRING(pEntity->GetModelName());
  197. return parsed;
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Purpose:
  201. // Input : &solid -
  202. // *pEntity -
  203. // modelIndex -
  204. // Output : Returns true on success, false on failure.
  205. //-----------------------------------------------------------------------------
  206. bool PhysModelParseSolid( solid_t &solid, CBaseEntity *pEntity, int modelIndex )
  207. {
  208. return PhysModelParseSolidByIndex( solid, pEntity, modelIndex, -1 );
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Purpose:
  212. // Input : &solid -
  213. // *pEntity -
  214. // *pCollide -
  215. // solidIndex -
  216. // Output : Returns true on success, false on failure.
  217. //-----------------------------------------------------------------------------
  218. bool PhysModelParseSolidByIndex( solid_t &solid, CBaseEntity *pEntity, vcollide_t *pCollide, int solidIndex )
  219. {
  220. bool parsed = false;
  221. memset( &solid, 0, sizeof(solid) );
  222. solid.params = g_PhysDefaultObjectParams;
  223. IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide );
  224. while ( !pParse->Finished() )
  225. {
  226. const char *pBlock = pParse->GetCurrentBlockName();
  227. if ( !strcmpi( pBlock, "solid" ) )
  228. {
  229. solid_t tmpSolid;
  230. memset( &tmpSolid, 0, sizeof(tmpSolid) );
  231. tmpSolid.params = g_PhysDefaultObjectParams;
  232. pParse->ParseSolid( &tmpSolid, &g_SolidSetup );
  233. if ( solidIndex < 0 || tmpSolid.index == solidIndex )
  234. {
  235. parsed = true;
  236. solid = tmpSolid;
  237. // just to be sure we aren't ever getting a non-zero solid by accident
  238. Assert( solidIndex >= 0 || solid.index == 0 );
  239. break;
  240. }
  241. }
  242. else
  243. {
  244. pParse->SkipBlock();
  245. }
  246. }
  247. physcollision->VPhysicsKeyParserDestroy( pParse );
  248. // collisions are off by default
  249. solid.params.enableCollisions = true;
  250. solid.params.pGameData = static_cast<void *>(pEntity);
  251. solid.params.pName = STRING(pEntity->GetModelName());
  252. return parsed;
  253. }
  254. //-----------------------------------------------------------------------------
  255. // Purpose:
  256. // Input : *pEntity -
  257. // modelIndex -
  258. // &origin -
  259. // &angles -
  260. // *pSolid -
  261. // Output : IPhysicsObject
  262. //-----------------------------------------------------------------------------
  263. IPhysicsObject *PhysModelCreate( CBaseEntity *pEntity, int modelIndex, const Vector &origin, const QAngle &angles, solid_t *pSolid )
  264. {
  265. if ( !physenv )
  266. return NULL;
  267. vcollide_t *pCollide = modelinfo->GetVCollide( modelIndex );
  268. if ( !pCollide || !pCollide->solidCount )
  269. return NULL;
  270. solid_t tmpSolid;
  271. if ( !pSolid )
  272. {
  273. pSolid = &tmpSolid;
  274. if ( !PhysModelParseSolidByIndex( tmpSolid, pEntity, pCollide, -1 ) )
  275. return NULL;
  276. }
  277. int surfaceProp = -1;
  278. if ( pSolid->surfaceprop[0] )
  279. {
  280. surfaceProp = physprops->GetSurfaceIndex( pSolid->surfaceprop );
  281. }
  282. IPhysicsObject *pObject = physenv->CreatePolyObject( pCollide->solids[pSolid->index], surfaceProp, origin, angles, &pSolid->params );
  283. //PhysCheckAdd( pObject, STRING(pEntity->m_iClassname) );
  284. if ( pObject )
  285. {
  286. if ( modelinfo->GetModelType(modelinfo->GetModel(modelIndex)) == mod_brush )
  287. {
  288. unsigned int contents = modelinfo->GetModelContents( modelIndex );
  289. Assert(contents!=0);
  290. // HACKHACK: contents is used to filter collisions
  291. // HACKHACK: So keep solid on for water brushes since they should pass collision rules (as triggers)
  292. if ( contents & MASK_WATER )
  293. {
  294. contents |= CONTENTS_SOLID;
  295. }
  296. if ( contents != pObject->GetContents() && contents != 0 )
  297. {
  298. pObject->SetContents( contents );
  299. pObject->RecheckCollisionFilter();
  300. }
  301. }
  302. g_pPhysSaveRestoreManager->AssociateModel( pObject, modelIndex);
  303. }
  304. return pObject;
  305. }
  306. //-----------------------------------------------------------------------------
  307. // Purpose:
  308. // Input : *pEntity -
  309. // modelIndex -
  310. // &origin -
  311. // &angles -
  312. // Output : IPhysicsObject
  313. //-----------------------------------------------------------------------------
  314. IPhysicsObject *PhysModelCreateUnmoveable( CBaseEntity *pEntity, int modelIndex, const Vector &origin, const QAngle &angles )
  315. {
  316. if ( !physenv )
  317. return NULL;
  318. vcollide_t *pCollide = modelinfo->GetVCollide( modelIndex );
  319. if ( !pCollide || !pCollide->solidCount )
  320. return NULL;
  321. solid_t solid;
  322. if ( !PhysModelParseSolidByIndex( solid, pEntity, pCollide, -1 ) )
  323. return NULL;
  324. // collisions are off by default
  325. solid.params.enableCollisions = true;
  326. //solid.params.mass = 1.0;
  327. int surfaceProp = -1;
  328. if ( solid.surfaceprop[0] )
  329. {
  330. surfaceProp = physprops->GetSurfaceIndex( solid.surfaceprop );
  331. }
  332. solid.params.pGameData = static_cast<void *>(pEntity);
  333. solid.params.pName = STRING(pEntity->GetModelName());
  334. IPhysicsObject *pObject = physenv->CreatePolyObjectStatic( pCollide->solids[0], surfaceProp, origin, angles, &solid.params );
  335. //PhysCheckAdd( pObject, STRING(pEntity->m_iClassname) );
  336. if ( pObject )
  337. {
  338. if ( modelinfo->GetModelType(modelinfo->GetModel(modelIndex)) == mod_brush )
  339. {
  340. unsigned int contents = modelinfo->GetModelContents( modelIndex );
  341. Assert(contents!=0);
  342. if ( contents != pObject->GetContents() && contents != 0 )
  343. {
  344. pObject->SetContents( contents );
  345. pObject->RecheckCollisionFilter();
  346. }
  347. }
  348. g_pPhysSaveRestoreManager->AssociateModel( pObject, modelIndex);
  349. }
  350. return pObject;
  351. }
  352. //-----------------------------------------------------------------------------
  353. // Purpose: Create a vphysics object based on an existing collision model
  354. // Input : *pEntity -
  355. // *pModel -
  356. // &origin -
  357. // &angles -
  358. // *pName -
  359. // isStatic -
  360. // *pSolid -
  361. // Output : IPhysicsObject
  362. //-----------------------------------------------------------------------------
  363. IPhysicsObject *PhysModelCreateCustom( CBaseEntity *pEntity, const CPhysCollide *pModel, const Vector &origin, const QAngle &angles, const char *pName, bool isStatic, solid_t *pSolid )
  364. {
  365. if ( !physenv )
  366. return NULL;
  367. solid_t tmpSolid;
  368. if ( !pSolid )
  369. {
  370. PhysGetDefaultAABBSolid( tmpSolid );
  371. pSolid = &tmpSolid;
  372. }
  373. int surfaceProp = physprops->GetSurfaceIndex( pSolid->surfaceprop );
  374. pSolid->params.pGameData = static_cast<void *>(pEntity);
  375. pSolid->params.pName = pName;
  376. IPhysicsObject *pObject = NULL;
  377. if ( isStatic )
  378. {
  379. pObject = physenv->CreatePolyObjectStatic( pModel, surfaceProp, origin, angles, &pSolid->params );
  380. }
  381. else
  382. {
  383. pObject = physenv->CreatePolyObject( pModel, surfaceProp, origin, angles, &pSolid->params );
  384. }
  385. if ( pObject )
  386. g_pPhysSaveRestoreManager->AssociateModel( pObject, pModel);
  387. return pObject;
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Purpose:
  391. // Input : *pEntity -
  392. // radius -
  393. // &origin -
  394. // &solid -
  395. // Output : IPhysicsObject
  396. //-----------------------------------------------------------------------------
  397. IPhysicsObject *PhysSphereCreate( CBaseEntity *pEntity, float radius, const Vector &origin, solid_t &solid )
  398. {
  399. if ( !physenv )
  400. return NULL;
  401. int surfaceProp = -1;
  402. if ( solid.surfaceprop[0] )
  403. {
  404. surfaceProp = physprops->GetSurfaceIndex( solid.surfaceprop );
  405. }
  406. solid.params.pGameData = static_cast<void *>(pEntity);
  407. IPhysicsObject *pObject = physenv->CreateSphereObject( radius, surfaceProp, origin, vec3_angle, &solid.params, false );
  408. return pObject;
  409. }
  410. //-----------------------------------------------------------------------------
  411. // Purpose:
  412. //-----------------------------------------------------------------------------
  413. void PhysGetDefaultAABBSolid( solid_t &solid )
  414. {
  415. solid.params = g_PhysDefaultObjectParams;
  416. solid.params.mass = 85.0f;
  417. solid.params.inertia = 1e24f;
  418. Q_strncpy( solid.surfaceprop, "default", sizeof( solid.surfaceprop ) );
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Purpose: Destroy a physics object
  422. // Input : *pObject -
  423. //-----------------------------------------------------------------------------
  424. void PhysDestroyObject( IPhysicsObject *pObject, CBaseEntity *pEntity )
  425. {
  426. g_pPhysSaveRestoreManager->ForgetModel( pObject );
  427. if ( pObject )
  428. pObject->SetGameData( NULL );
  429. g_EntityCollisionHash->RemoveAllPairsForObject( pObject );
  430. if ( pEntity && pEntity->IsMarkedForDeletion() )
  431. {
  432. g_EntityCollisionHash->RemoveAllPairsForObject( pEntity );
  433. }
  434. if ( physenv )
  435. {
  436. physenv->DestroyObject( pObject );
  437. }
  438. }
  439. void AddSurfacepropFile( const char *pFileName, IPhysicsSurfaceProps *pProps, IFileSystem *pFileSystem )
  440. {
  441. // Load file into memory
  442. FileHandle_t file = pFileSystem->Open( pFileName, "rb", "GAME" );
  443. if ( file )
  444. {
  445. int len = pFileSystem->Size( file );
  446. // read the file
  447. int nBufSize = len+1;
  448. char *buffer = (char *)stackalloc( nBufSize );
  449. pFileSystem->ReadEx( buffer, nBufSize, len, file );
  450. pFileSystem->Close( file );
  451. buffer[len] = 0;
  452. pProps->ParseSurfaceData( pFileName, buffer );
  453. // buffer is on the stack, no need to free
  454. }
  455. else
  456. {
  457. Error( "Unable to load surface prop file '%s' (referenced by manifest file '%s')\n", pFileName, SURFACEPROP_MANIFEST_FILE );
  458. }
  459. }
  460. void PhysParseSurfaceData( IPhysicsSurfaceProps *pProps, IFileSystem *pFileSystem )
  461. {
  462. KeyValues *manifest = new KeyValues( SURFACEPROP_MANIFEST_FILE );
  463. if ( manifest->LoadFromFile( pFileSystem, SURFACEPROP_MANIFEST_FILE, "GAME" ) )
  464. {
  465. for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
  466. {
  467. if ( !Q_stricmp( sub->GetName(), "file" ) )
  468. {
  469. // Add
  470. AddSurfacepropFile( sub->GetString(), pProps, pFileSystem );
  471. continue;
  472. }
  473. Warning( "surfaceprops::Init: Manifest '%s' with bogus file type '%s', expecting 'file'\n",
  474. SURFACEPROP_MANIFEST_FILE, sub->GetName() );
  475. }
  476. }
  477. else
  478. {
  479. Error( "Unable to load manifest file '%s'\n", SURFACEPROP_MANIFEST_FILE );
  480. }
  481. manifest->deleteThis();
  482. }
  483. void PhysCreateVirtualTerrain( CBaseEntity *pWorld, const objectparams_t &defaultParams )
  484. {
  485. if ( !physenv )
  486. return;
  487. char nameBuf[1024];
  488. for ( int i = 0; i < MAX_MAP_DISPINFO; i++ )
  489. {
  490. CPhysCollide *pCollide = modelinfo->GetCollideForVirtualTerrain( i );
  491. if ( pCollide )
  492. {
  493. solid_t solid;
  494. solid.params = defaultParams;
  495. solid.params.enableCollisions = true;
  496. solid.params.pGameData = static_cast<void *>(pWorld);
  497. Q_snprintf(nameBuf, sizeof(nameBuf), "vdisp_%04d", i );
  498. solid.params.pName = nameBuf;
  499. int surfaceData = physprops->GetSurfaceIndex( "default" );
  500. // create this as part of the world
  501. IPhysicsObject *pObject = physenv->CreatePolyObjectStatic( pCollide, surfaceData, vec3_origin, vec3_angle, &solid.params );
  502. pObject->SetCallbackFlags( pObject->GetCallbackFlags() | CALLBACK_NEVER_DELETED );
  503. pObject->SetCollisionHints( COLLISION_HINT_STATICSOLID );
  504. }
  505. }
  506. }
  507. IPhysicsObject *PhysCreateWorld_Shared( CBaseEntity *pWorld, vcollide_t *pWorldCollide, const objectparams_t &defaultParams )
  508. {
  509. solid_t solid;
  510. fluid_t fluid;
  511. if ( !physenv )
  512. return NULL;
  513. int surfaceData = physprops->GetSurfaceIndex( "default" );
  514. objectparams_t params = defaultParams;
  515. params.pGameData = static_cast<void *>(pWorld);
  516. params.pName = "world";
  517. IPhysicsObject *pWorldPhysics = physenv->CreatePolyObjectStatic(
  518. pWorldCollide->solids[0], surfaceData, vec3_origin, vec3_angle, &params );
  519. // hint - saves vphysics some work
  520. pWorldPhysics->SetCallbackFlags( pWorldPhysics->GetCallbackFlags() | CALLBACK_NEVER_DELETED );
  521. //PhysCheckAdd( world, "World" );
  522. // walk the world keys in case there are some fluid volumes to create
  523. IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pWorldCollide );
  524. bool bCreateVirtualTerrain = false;
  525. while ( !pParse->Finished() )
  526. {
  527. const char *pBlock = pParse->GetCurrentBlockName();
  528. if ( !strcmpi( pBlock, "solid" ) || !strcmpi( pBlock, "staticsolid" ) )
  529. {
  530. solid.params = defaultParams;
  531. pParse->ParseSolid( &solid, &g_SolidSetup );
  532. solid.params.enableCollisions = true;
  533. solid.params.pGameData = static_cast<void *>(pWorld);
  534. solid.params.pName = "world";
  535. int surfaceData = physprops->GetSurfaceIndex( "default" );
  536. // already created world above
  537. if ( solid.index == 0 )
  538. continue;
  539. if ( !pWorldCollide->solids[solid.index] )
  540. {
  541. // this implies that the collision model is a mopp and the physics DLL doesn't support that.
  542. bCreateVirtualTerrain = true;
  543. continue;
  544. }
  545. // create this as part of the world
  546. IPhysicsObject *pObject = physenv->CreatePolyObjectStatic( pWorldCollide->solids[solid.index],
  547. surfaceData, vec3_origin, vec3_angle, &solid.params );
  548. // invalid collision model or can't create, ignore
  549. if (!pObject)
  550. continue;
  551. pObject->SetCallbackFlags( pObject->GetCallbackFlags() | CALLBACK_NEVER_DELETED );
  552. Assert( solid.contents != 0 );
  553. pObject->SetContents( solid.contents );
  554. if ( solid.contents & CONTENTS_SOLID )
  555. {
  556. pObject->SetCollisionHints(COLLISION_HINT_STATICSOLID);
  557. }
  558. if ( !pWorldPhysics )
  559. {
  560. pWorldPhysics = pObject;
  561. }
  562. }
  563. else if ( !strcmpi( pBlock, "fluid" ) )
  564. {
  565. pParse->ParseFluid( &fluid, NULL );
  566. // create a fluid for floating
  567. if ( fluid.index > 0 )
  568. {
  569. solid.params = defaultParams; // copy world's params
  570. solid.params.enableCollisions = true;
  571. solid.params.pName = "fluid";
  572. solid.params.pGameData = static_cast<void *>(pWorld);
  573. fluid.params.pGameData = static_cast<void *>(pWorld);
  574. int surfaceData = physprops->GetSurfaceIndex( fluid.surfaceprop );
  575. // create this as part of the world
  576. IPhysicsObject *pWater = physenv->CreatePolyObjectStatic( pWorldCollide->solids[fluid.index],
  577. surfaceData, vec3_origin, vec3_angle, &solid.params );
  578. pWater->SetCallbackFlags( pWater->GetCallbackFlags() | CALLBACK_NEVER_DELETED );
  579. physenv->CreateFluidController( pWater, &fluid.params );
  580. }
  581. }
  582. else if ( !strcmpi( pBlock, "materialtable" ) )
  583. {
  584. int surfaceTable[128];
  585. memset( surfaceTable, 0, sizeof(surfaceTable) );
  586. pParse->ParseSurfaceTable( surfaceTable, NULL );
  587. physprops->SetWorldMaterialIndexTable( surfaceTable, 128 );
  588. }
  589. else if ( !strcmpi(pBlock, "virtualterrain" ) )
  590. {
  591. bCreateVirtualTerrain = true;
  592. pParse->SkipBlock();
  593. }
  594. else
  595. {
  596. // unknown chunk???
  597. pParse->SkipBlock();
  598. }
  599. }
  600. physcollision->VPhysicsKeyParserDestroy( pParse );
  601. if ( bCreateVirtualTerrain && physcollision->SupportsVirtualMesh() )
  602. {
  603. PhysCreateVirtualTerrain( pWorld, defaultParams );
  604. }
  605. return pWorldPhysics;
  606. }
  607. //=============================================================================
  608. //
  609. // Physics Game Trace
  610. //
  611. class CPhysicsGameTrace : public IPhysicsGameTrace
  612. {
  613. public:
  614. void VehicleTraceRay( const Ray_t &ray, void *pVehicle, trace_t *pTrace );
  615. void VehicleTraceRayWithWater( const Ray_t &ray, void *pVehicle, trace_t *pTrace );
  616. bool VehiclePointInWater( const Vector &vecPoint );
  617. };
  618. CPhysicsGameTrace g_PhysGameTrace;
  619. IPhysicsGameTrace *physgametrace = &g_PhysGameTrace;
  620. //-----------------------------------------------------------------------------
  621. // Purpose: Game ray-traces in vphysics.
  622. //-----------------------------------------------------------------------------
  623. void CPhysicsGameTrace::VehicleTraceRay( const Ray_t &ray, void *pVehicle, trace_t *pTrace )
  624. {
  625. CBaseEntity *pBaseEntity = static_cast<CBaseEntity*>( pVehicle );
  626. UTIL_TraceRay( ray, MASK_SOLID, pBaseEntity, COLLISION_GROUP_NONE, pTrace );
  627. }
  628. //-----------------------------------------------------------------------------
  629. // Purpose: Game ray-traces in vphysics.
  630. //-----------------------------------------------------------------------------
  631. void CPhysicsGameTrace::VehicleTraceRayWithWater( const Ray_t &ray, void *pVehicle, trace_t *pTrace )
  632. {
  633. CBaseEntity *pBaseEntity = static_cast<CBaseEntity*>( pVehicle );
  634. UTIL_TraceRay( ray, MASK_SOLID|MASK_WATER, pBaseEntity, COLLISION_GROUP_NONE, pTrace );
  635. }
  636. //-----------------------------------------------------------------------------
  637. // Purpose: Test to see if a vehicle point is in water.
  638. //-----------------------------------------------------------------------------
  639. bool CPhysicsGameTrace::VehiclePointInWater( const Vector &vecPoint )
  640. {
  641. return ( ( UTIL_PointContents( vecPoint, MASK_WATER ) & MASK_WATER ) != 0 );
  642. }
  643. void PhysRecheckObjectPair( IPhysicsObject *pObject0, IPhysicsObject *pObject1 )
  644. {
  645. if ( !pObject0->IsStatic() )
  646. {
  647. pObject0->RecheckCollisionFilter();
  648. }
  649. if ( !pObject1->IsStatic() )
  650. {
  651. pObject1->RecheckCollisionFilter();
  652. }
  653. }
  654. void PhysEnableEntityCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 )
  655. {
  656. if ( !pObject0 || !pObject1 )
  657. return;
  658. CBaseEntity *pEntity0 = static_cast<CBaseEntity *>(pObject0->GetGameData());
  659. CBaseEntity *pEntity1 = static_cast<CBaseEntity *>(pObject1->GetGameData());
  660. g_EntityCollisionHash->RemoveObjectPair( pEntity0, pEntity1 );
  661. pEntity0->CollisionRulesChanged();
  662. pEntity1->CollisionRulesChanged();
  663. }
  664. // disables collisions between entities (each entity may contain multiple objects)
  665. void PhysDisableEntityCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 )
  666. {
  667. if ( !pObject0 || !pObject1 )
  668. return;
  669. CBaseEntity *pEntity0 = static_cast<CBaseEntity *>(pObject0->GetGameData());
  670. CBaseEntity *pEntity1 = static_cast<CBaseEntity *>(pObject1->GetGameData());
  671. g_EntityCollisionHash->AddObjectPair( pEntity0, pEntity1 );
  672. pEntity0->CollisionRulesChanged();
  673. pEntity1->CollisionRulesChanged();
  674. }
  675. void PhysDisableEntityCollisions( CBaseEntity *pEntity0, CBaseEntity *pEntity1 )
  676. {
  677. if ( !pEntity0 || !pEntity1 )
  678. return;
  679. g_EntityCollisionHash->AddObjectPair( pEntity0, pEntity1 );
  680. pEntity0->CollisionRulesChanged();
  681. pEntity1->CollisionRulesChanged();
  682. }
  683. void PhysEnableEntityCollisions( CBaseEntity *pEntity0, CBaseEntity *pEntity1 )
  684. {
  685. if ( !pEntity0 || !pEntity1 )
  686. return;
  687. g_EntityCollisionHash->RemoveObjectPair( pEntity0, pEntity1 );
  688. pEntity0->CollisionRulesChanged();
  689. pEntity1->CollisionRulesChanged();
  690. }
  691. bool PhysEntityCollisionsAreDisabled( CBaseEntity *pEntity0, CBaseEntity *pEntity1 )
  692. {
  693. return g_EntityCollisionHash->IsObjectPairInHash( pEntity0, pEntity1 );
  694. }
  695. void PhysEnableObjectCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 )
  696. {
  697. if ( !pObject0 || !pObject1 )
  698. return;
  699. g_EntityCollisionHash->RemoveObjectPair( pObject0, pObject1 );
  700. PhysRecheckObjectPair( pObject0, pObject1 );
  701. }
  702. // disables collisions between entities (each entity may contain multiple objects)
  703. void PhysDisableObjectCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 )
  704. {
  705. if ( !pObject0 || !pObject1 )
  706. return;
  707. g_EntityCollisionHash->AddObjectPair( pObject0, pObject1 );
  708. PhysRecheckObjectPair( pObject0, pObject1 );
  709. }
  710. void PhysComputeSlideDirection( IPhysicsObject *pPhysics, const Vector &inputVelocity, const AngularImpulse &inputAngularVelocity,
  711. Vector *pOutputVelocity, Vector *pOutputAngularVelocity, float minMass )
  712. {
  713. Vector velocity = inputVelocity;
  714. AngularImpulse angVel = inputAngularVelocity;
  715. Vector pos;
  716. IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot();
  717. while ( pSnapshot->IsValid() )
  718. {
  719. IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
  720. if ( !pOther->IsMoveable() || pOther->GetMass() > minMass )
  721. {
  722. Vector normal;
  723. pSnapshot->GetSurfaceNormal( normal );
  724. // BUGBUG: Figure out the correct rotation clipping equation
  725. if ( pOutputAngularVelocity )
  726. {
  727. angVel = normal * DotProduct( angVel, normal );
  728. #if 0
  729. pSnapshot->GetContactPoint( point );
  730. Vector point, dummy;
  731. AngularImpulse angularClip, clip2;
  732. pPhysics->CalculateVelocityOffset( normal, point, dummy, angularClip );
  733. VectorNormalize( angularClip );
  734. float proj = DotProduct( angVel, angularClip );
  735. if ( proj > 0 )
  736. {
  737. angVel -= angularClip * proj;
  738. }
  739. CrossProduct( angularClip, normal, clip2 );
  740. proj = DotProduct( angVel, clip2 );
  741. if ( proj > 0 )
  742. {
  743. angVel -= clip2 * proj;
  744. }
  745. //NDebugOverlay::Line( point, point - normal * 20, 255, 0, 0, true, 0.1 );
  746. #endif
  747. }
  748. // Determine how far along plane to slide based on incoming direction.
  749. // NOTE: Normal points away from this object
  750. float proj = DotProduct( velocity, normal );
  751. if ( proj > 0.0f )
  752. {
  753. velocity -= normal * proj;
  754. }
  755. }
  756. pSnapshot->NextFrictionData();
  757. }
  758. pPhysics->DestroyFrictionSnapshot( pSnapshot );
  759. //NDebugOverlay::Line( pos, pos + unitVel * 20, 0, 0, 255, true, 0.1 );
  760. if ( pOutputVelocity )
  761. {
  762. *pOutputVelocity = velocity;
  763. }
  764. if ( pOutputAngularVelocity )
  765. {
  766. *pOutputAngularVelocity = angVel;
  767. }
  768. }
  769. bool PhysHasContactWithOtherInDirection( IPhysicsObject *pPhysics, const Vector &dir )
  770. {
  771. bool hit = false;
  772. void *pGameData = pPhysics->GetGameData();
  773. IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot();
  774. while ( pSnapshot->IsValid() )
  775. {
  776. IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
  777. if ( pOther->GetGameData() != pGameData )
  778. {
  779. Vector normal;
  780. pSnapshot->GetSurfaceNormal( normal );
  781. if ( DotProduct(normal,dir) > 0 )
  782. {
  783. hit = true;
  784. break;
  785. }
  786. }
  787. pSnapshot->NextFrictionData();
  788. }
  789. pPhysics->DestroyFrictionSnapshot( pSnapshot );
  790. return hit;
  791. }
  792. void PhysForceClearVelocity( IPhysicsObject *pPhys )
  793. {
  794. IPhysicsFrictionSnapshot *pSnapshot = pPhys->CreateFrictionSnapshot();
  795. // clear the velocity of the rigid body
  796. Vector vel;
  797. AngularImpulse angVel;
  798. vel.Init();
  799. angVel.Init();
  800. pPhys->SetVelocity( &vel, &angVel );
  801. // now clear the "strain" stored in the contact points
  802. while ( pSnapshot->IsValid() )
  803. {
  804. pSnapshot->ClearFrictionForce();
  805. pSnapshot->RecomputeFriction();
  806. pSnapshot->NextFrictionData();
  807. }
  808. pPhys->DestroyFrictionSnapshot( pSnapshot );
  809. }
  810. void PhysFrictionEffect( Vector &vecPos, Vector vecVel, float energy, int surfaceProps, int surfacePropsHit )
  811. {
  812. QAngle angDirection;
  813. VectorAngles( vecVel, angDirection );
  814. surfacedata_t *psurf = physprops->GetSurfaceData( surfaceProps );
  815. surfacedata_t *phit = physprops->GetSurfaceData( surfacePropsHit );
  816. switch ( phit->game.material )
  817. {
  818. case CHAR_TEX_DIRT:
  819. if ( energy < MASS10_SPEED2ENERGY(15) )
  820. break;
  821. DispatchParticleEffect( "impact_physics_dust", vecPos, angDirection );
  822. break;
  823. case CHAR_TEX_CONCRETE:
  824. if ( energy < MASS10_SPEED2ENERGY(28) )
  825. break;
  826. DispatchParticleEffect( "impact_physics_dust", vecPos, angDirection );
  827. break;
  828. }
  829. //Metal sparks
  830. if ( energy > MASS10_SPEED2ENERGY(50) )
  831. {
  832. // make sparks for metal/concrete scrapes with enough energy
  833. if ( psurf->game.material == CHAR_TEX_METAL || psurf->game.material == CHAR_TEX_GRATE )
  834. {
  835. switch ( phit->game.material )
  836. {
  837. case CHAR_TEX_CONCRETE:
  838. case CHAR_TEX_METAL:
  839. DispatchParticleEffect( "impact_physics_sparks", vecPos, angDirection );
  840. break;
  841. }
  842. }
  843. }
  844. }
  845. void PhysFrictionSound( CBaseEntity *pEntity, IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit )
  846. {
  847. if ( !pEntity || energy < 75.0f || surfaceProps < 0 )
  848. return;
  849. // don't make noise for hidden/invisible/sky materials
  850. surfacedata_t *phit = physprops->GetSurfaceData( surfacePropsHit );
  851. surfacedata_t *psurf = physprops->GetSurfaceData( surfaceProps );
  852. if ( phit->game.material == 'X' || psurf->game.material == 'X' )
  853. return;
  854. // rescale the incoming energy
  855. energy *= ENERGY_VOLUME_SCALE;
  856. // volume of scrape is proportional to square of energy (steeper rolloff at low energies)
  857. float volume = energy * energy;
  858. unsigned short soundName = psurf->sounds.scrapeRough;
  859. HSOUNDSCRIPTHASH *soundHandle = &psurf->soundhandles.scrapeRough;
  860. if ( psurf->sounds.scrapeSmooth && phit->audio.roughnessFactor < psurf->audio.roughThreshold )
  861. {
  862. soundName = psurf->sounds.scrapeSmooth;
  863. soundHandle = &psurf->soundhandles.scrapeRough;
  864. }
  865. const char *pSoundName = physprops->GetString( soundName );
  866. PhysFrictionSound( pEntity, pObject, pSoundName, *soundHandle, volume );
  867. }
  868. //-----------------------------------------------------------------------------
  869. // Purpose: Precaches a surfaceproperties string name if it's set.
  870. // Input : idx -
  871. // Output : static void
  872. //-----------------------------------------------------------------------------
  873. static HSOUNDSCRIPTHASH PrecachePhysicsSoundByStringIndex( int idx )
  874. {
  875. // Only precache if a value was set in the script file...
  876. if ( idx != 0 )
  877. {
  878. return CBaseEntity::PrecacheScriptSound( physprops->GetString( idx ) );
  879. }
  880. return SOUNDEMITTER_INVALID_HASH;
  881. }
  882. //-----------------------------------------------------------------------------
  883. // Purpose: Iterates all surfacedata sounds and precaches them
  884. // Output : static void
  885. //-----------------------------------------------------------------------------
  886. void PrecachePhysicsSounds()
  887. {
  888. // precache the surface prop sounds
  889. for ( int i = 0; i < physprops->SurfacePropCount(); i++ )
  890. {
  891. surfacedata_t *pprop = physprops->GetSurfaceData( i );
  892. Assert( pprop );
  893. pprop->soundhandles.walkStepLeft = PrecachePhysicsSoundByStringIndex( pprop->sounds.walkStepLeft );
  894. pprop->soundhandles.walkStepRight = PrecachePhysicsSoundByStringIndex( pprop->sounds.walkStepRight );
  895. pprop->soundhandles.runStepLeft = PrecachePhysicsSoundByStringIndex( pprop->sounds.runStepLeft );
  896. pprop->soundhandles.runStepRight = PrecachePhysicsSoundByStringIndex( pprop->sounds.runStepRight );
  897. pprop->soundhandles.impactSoft = PrecachePhysicsSoundByStringIndex( pprop->sounds.impactSoft );
  898. pprop->soundhandles.impactHard = PrecachePhysicsSoundByStringIndex( pprop->sounds.impactHard );
  899. pprop->soundhandles.scrapeSmooth = PrecachePhysicsSoundByStringIndex( pprop->sounds.scrapeSmooth );
  900. pprop->soundhandles.scrapeRough = PrecachePhysicsSoundByStringIndex( pprop->sounds.scrapeRough );
  901. pprop->soundhandles.bulletImpact = PrecachePhysicsSoundByStringIndex( pprop->sounds.bulletImpact );
  902. pprop->soundhandles.rolling = PrecachePhysicsSoundByStringIndex( pprop->sounds.rolling );
  903. pprop->soundhandles.breakSound = PrecachePhysicsSoundByStringIndex( pprop->sounds.breakSound );
  904. pprop->soundhandles.strainSound = PrecachePhysicsSoundByStringIndex( pprop->sounds.strainSound );
  905. }
  906. }
  907. float PhysGetEntityMass( CBaseEntity *pEntity )
  908. {
  909. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  910. int physCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  911. float otherMass = 0;
  912. for ( int i = 0; i < physCount; i++ )
  913. {
  914. otherMass += pList[i]->GetMass();
  915. }
  916. return otherMass;
  917. }