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

1055 lines
31 KiB

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