diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/shared/physics_shared.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/shared/physics_shared.cpp')
| -rw-r--r-- | mp/src/game/shared/physics_shared.cpp | 2110 |
1 files changed, 1055 insertions, 1055 deletions
diff --git a/mp/src/game/shared/physics_shared.cpp b/mp/src/game/shared/physics_shared.cpp index 68ffa7af..ba5e0d2a 100644 --- a/mp/src/game/shared/physics_shared.cpp +++ b/mp/src/game/shared/physics_shared.cpp @@ -1,1055 +1,1055 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Game & Client shared functions moved from physics.cpp
-//
-//=============================================================================//
-#include "cbase.h"
-#include "vcollide_parse.h"
-#include "filesystem.h"
-#include "movevars_shared.h"
-#include "engine/ivmodelinfo.h"
-#include "physics_shared.h"
-#include "solidsetdefaults.h"
-#include "model_types.h"
-#include "bone_setup.h"
-#include "vphysics/object_hash.h"
-#include "vphysics/friction.h"
-#include "coordsize.h"
-#include <KeyValues.h>
-#include "decals.h"
-#include "IEffects.h"
-#include "SoundEmitterSystem/isoundemittersystembase.h"
-
-#include "physics_saverestore.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-//
-IPhysics *physics = NULL;
-IPhysicsObject *g_PhysWorldObject = NULL;
-IPhysicsCollision *physcollision = NULL;
-IPhysicsEnvironment *physenv = NULL;
-#ifdef PORTAL
-IPhysicsEnvironment *physenv_main = NULL;
-#endif
-IPhysicsSurfaceProps *physprops = NULL;
-// UNDONE: This hash holds both entity & IPhysicsObject pointer pairs
-// UNDONE: Split into separate hashes?
-IPhysicsObjectPairHash *g_EntityCollisionHash = NULL;
-
-const char *SURFACEPROP_MANIFEST_FILE = "scripts/surfaceproperties_manifest.txt";
-
-const objectparams_t g_PhysDefaultObjectParams =
-{
- NULL,
- 1.0, //mass
- 1.0, // inertia
- 0.1f, // damping
- 0.1f, // rotdamping
- 0.05f, // rotIntertiaLimit
- "DEFAULT",
- NULL,// game data
- 0.f, // volume (leave 0 if you don't have one or call physcollision->CollideVolume() to compute it)
- 1.0f, // drag coefficient
- true,// enable collisions?
-};
-
-
-void CSolidSetDefaults::ParseKeyValue( void *pData, const char *pKey, const char *pValue )
-{
- if ( !Q_stricmp( pKey, "contents" ) )
- {
- m_contentsMask = atoi( pValue );
- }
-}
-
-void CSolidSetDefaults::SetDefaults( void *pData )
-{
- solid_t *pSolid = (solid_t *)pData;
- pSolid->params = g_PhysDefaultObjectParams;
- m_contentsMask = CONTENTS_SOLID;
-}
-
-CSolidSetDefaults g_SolidSetup;
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &mins -
-// &maxs -
-// Output : CPhysCollide
-//-----------------------------------------------------------------------------
-CPhysCollide *PhysCreateBbox( const Vector &minsIn, const Vector &maxsIn )
-{
- // UNDONE: Track down why this causes errors for the player controller and adjust/enable
- //float radius = 0.5 - DIST_EPSILON;
- Vector mins = minsIn;// + Vector(radius, radius, radius);
- Vector maxs = maxsIn;// - Vector(radius, radius, radius);
-
- // VPHYSICS caches/cleans up these
- CPhysCollide *pResult = physcollision->BBoxToCollide( mins, maxs );
-
- g_pPhysSaveRestoreManager->NoteBBox( mins, maxs, pResult );
-
- return pResult;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity -
-// &mins -
-// &maxs -
-// &origin -
-// isStatic -
-// Output : static IPhysicsObject
-//-----------------------------------------------------------------------------
-IPhysicsObject *PhysModelCreateBox( CBaseEntity *pEntity, const Vector &mins, const Vector &maxs, const Vector &origin, bool isStatic )
-{
- int modelIndex = pEntity->GetModelIndex();
- const char *pSurfaceProps = "flesh";
- solid_t solid;
- PhysGetDefaultAABBSolid( solid );
- Vector dims = maxs - mins;
- solid.params.volume = dims.x * dims.y * dims.z;
-
- if ( modelIndex )
- {
- const model_t *model = modelinfo->GetModel( modelIndex );
- if ( model )
- {
- CStudioHdr studioHdr( modelinfo->GetStudiomodel( model ), mdlcache );
- if ( studioHdr.IsValid() )
- {
- pSurfaceProps = Studio_GetDefaultSurfaceProps( &studioHdr );
- }
- }
- }
- Q_strncpy( solid.surfaceprop, pSurfaceProps, sizeof( solid.surfaceprop ) );
-
- CPhysCollide *pCollide = PhysCreateBbox( mins, maxs );
- if ( !pCollide )
- return NULL;
-
- return PhysModelCreateCustom( pEntity, pCollide, origin, vec3_angle, STRING(pEntity->GetModelName()), isStatic, &solid );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity -
-// &mins -
-// &maxs -
-// &origin -
-// isStatic -
-// Output : static IPhysicsObject
-//-----------------------------------------------------------------------------
-IPhysicsObject *PhysModelCreateOBB( CBaseEntity *pEntity, const Vector &mins, const Vector &maxs, const Vector &origin, const QAngle &angle, bool isStatic )
-{
- int modelIndex = pEntity->GetModelIndex();
- const char *pSurfaceProps = "flesh";
- solid_t solid;
- PhysGetDefaultAABBSolid( solid );
- Vector dims = maxs - mins;
- solid.params.volume = dims.x * dims.y * dims.z;
-
- if ( modelIndex )
- {
- const model_t *model = modelinfo->GetModel( modelIndex );
- if ( model )
- {
- CStudioHdr studioHdr( modelinfo->GetStudiomodel( model ), mdlcache );
- if (studioHdr.IsValid())
- {
- pSurfaceProps = Studio_GetDefaultSurfaceProps( &studioHdr );
- }
- }
- }
- Q_strncpy( solid.surfaceprop, pSurfaceProps, sizeof( solid.surfaceprop ) );
-
- CPhysCollide *pCollide = PhysCreateBbox( mins, maxs );
- if ( !pCollide )
- return NULL;
-
- return PhysModelCreateCustom( pEntity, pCollide, origin, angle, STRING(pEntity->GetModelName()), isStatic, &solid );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &solid -
-// *pEntity -
-// modelIndex -
-// solidIndex -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool PhysModelParseSolidByIndex( solid_t &solid, CBaseEntity *pEntity, int modelIndex, int solidIndex )
-{
- vcollide_t *pCollide = modelinfo->GetVCollide( modelIndex );
- if ( !pCollide )
- return false;
-
- bool parsed = false;
-
- memset( &solid, 0, sizeof(solid) );
- solid.params = g_PhysDefaultObjectParams;
-
- IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues );
- while ( !pParse->Finished() )
- {
- const char *pBlock = pParse->GetCurrentBlockName();
- if ( !strcmpi( pBlock, "solid" ) )
- {
- solid_t tmpSolid;
- memset( &tmpSolid, 0, sizeof(tmpSolid) );
- tmpSolid.params = g_PhysDefaultObjectParams;
-
- pParse->ParseSolid( &tmpSolid, &g_SolidSetup );
-
- if ( solidIndex < 0 || tmpSolid.index == solidIndex )
- {
- parsed = true;
- solid = tmpSolid;
- // just to be sure we aren't ever getting a non-zero solid by accident
- Assert( solidIndex >= 0 || solid.index == 0 );
- break;
- }
- }
- else
- {
- pParse->SkipBlock();
- }
- }
- physcollision->VPhysicsKeyParserDestroy( pParse );
-
- // collisions are off by default
- solid.params.enableCollisions = true;
-
- solid.params.pGameData = static_cast<void *>(pEntity);
- solid.params.pName = STRING(pEntity->GetModelName());
- return parsed;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &solid -
-// *pEntity -
-// modelIndex -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool PhysModelParseSolid( solid_t &solid, CBaseEntity *pEntity, int modelIndex )
-{
- return PhysModelParseSolidByIndex( solid, pEntity, modelIndex, -1 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &solid -
-// *pEntity -
-// *pCollide -
-// solidIndex -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool PhysModelParseSolidByIndex( solid_t &solid, CBaseEntity *pEntity, vcollide_t *pCollide, int solidIndex )
-{
- bool parsed = false;
-
- memset( &solid, 0, sizeof(solid) );
- solid.params = g_PhysDefaultObjectParams;
-
- IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues );
- while ( !pParse->Finished() )
- {
- const char *pBlock = pParse->GetCurrentBlockName();
- if ( !strcmpi( pBlock, "solid" ) )
- {
- solid_t tmpSolid;
- memset( &tmpSolid, 0, sizeof(tmpSolid) );
- tmpSolid.params = g_PhysDefaultObjectParams;
-
- pParse->ParseSolid( &tmpSolid, &g_SolidSetup );
-
- if ( solidIndex < 0 || tmpSolid.index == solidIndex )
- {
- parsed = true;
- solid = tmpSolid;
- // just to be sure we aren't ever getting a non-zero solid by accident
- Assert( solidIndex >= 0 || solid.index == 0 );
- break;
- }
- }
- else
- {
- pParse->SkipBlock();
- }
- }
- physcollision->VPhysicsKeyParserDestroy( pParse );
-
- // collisions are off by default
- solid.params.enableCollisions = true;
-
- solid.params.pGameData = static_cast<void *>(pEntity);
- solid.params.pName = STRING(pEntity->GetModelName());
- return parsed;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity -
-// modelIndex -
-// &origin -
-// &angles -
-// *pSolid -
-// Output : IPhysicsObject
-//-----------------------------------------------------------------------------
-IPhysicsObject *PhysModelCreate( CBaseEntity *pEntity, int modelIndex, const Vector &origin, const QAngle &angles, solid_t *pSolid )
-{
- if ( !physenv )
- return NULL;
-
- vcollide_t *pCollide = modelinfo->GetVCollide( modelIndex );
- if ( !pCollide || !pCollide->solidCount )
- return NULL;
-
- solid_t tmpSolid;
- if ( !pSolid )
- {
- pSolid = &tmpSolid;
- if ( !PhysModelParseSolidByIndex( tmpSolid, pEntity, pCollide, -1 ) )
- return NULL;
- }
-
- int surfaceProp = -1;
- if ( pSolid->surfaceprop[0] )
- {
- surfaceProp = physprops->GetSurfaceIndex( pSolid->surfaceprop );
- }
- IPhysicsObject *pObject = physenv->CreatePolyObject( pCollide->solids[pSolid->index], surfaceProp, origin, angles, &pSolid->params );
- //PhysCheckAdd( pObject, STRING(pEntity->m_iClassname) );
-
- if ( pObject )
- {
- if ( modelinfo->GetModelType(modelinfo->GetModel(modelIndex)) == mod_brush )
- {
- unsigned int contents = modelinfo->GetModelContents( modelIndex );
- Assert(contents!=0);
- // HACKHACK: contents is used to filter collisions
- // HACKHACK: So keep solid on for water brushes since they should pass collision rules (as triggers)
- if ( contents & MASK_WATER )
- {
- contents |= CONTENTS_SOLID;
- }
- if ( contents != pObject->GetContents() && contents != 0 )
- {
- pObject->SetContents( contents );
- pObject->RecheckCollisionFilter();
- }
- }
-
- g_pPhysSaveRestoreManager->AssociateModel( pObject, modelIndex);
- }
-
- return pObject;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity -
-// modelIndex -
-// &origin -
-// &angles -
-// Output : IPhysicsObject
-//-----------------------------------------------------------------------------
-IPhysicsObject *PhysModelCreateUnmoveable( CBaseEntity *pEntity, int modelIndex, const Vector &origin, const QAngle &angles )
-{
- if ( !physenv )
- return NULL;
-
- vcollide_t *pCollide = modelinfo->GetVCollide( modelIndex );
- if ( !pCollide || !pCollide->solidCount )
- return NULL;
-
- solid_t solid;
-
- if ( !PhysModelParseSolidByIndex( solid, pEntity, pCollide, -1 ) )
- return NULL;
-
- // collisions are off by default
- solid.params.enableCollisions = true;
- //solid.params.mass = 1.0;
- int surfaceProp = -1;
- if ( solid.surfaceprop[0] )
- {
- surfaceProp = physprops->GetSurfaceIndex( solid.surfaceprop );
- }
- solid.params.pGameData = static_cast<void *>(pEntity);
- solid.params.pName = STRING(pEntity->GetModelName());
- IPhysicsObject *pObject = physenv->CreatePolyObjectStatic( pCollide->solids[0], surfaceProp, origin, angles, &solid.params );
-
- //PhysCheckAdd( pObject, STRING(pEntity->m_iClassname) );
- if ( pObject )
- {
- if ( modelinfo->GetModelType(modelinfo->GetModel(modelIndex)) == mod_brush )
- {
- unsigned int contents = modelinfo->GetModelContents( modelIndex );
- Assert(contents!=0);
- if ( contents != pObject->GetContents() && contents != 0 )
- {
- pObject->SetContents( contents );
- pObject->RecheckCollisionFilter();
- }
- }
- g_pPhysSaveRestoreManager->AssociateModel( pObject, modelIndex);
- }
-
- return pObject;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Create a vphysics object based on an existing collision model
-// Input : *pEntity -
-// *pModel -
-// &origin -
-// &angles -
-// *pName -
-// isStatic -
-// *pSolid -
-// Output : IPhysicsObject
-//-----------------------------------------------------------------------------
-IPhysicsObject *PhysModelCreateCustom( CBaseEntity *pEntity, const CPhysCollide *pModel, const Vector &origin, const QAngle &angles, const char *pName, bool isStatic, solid_t *pSolid )
-{
- if ( !physenv )
- return NULL;
-
- solid_t tmpSolid;
- if ( !pSolid )
- {
- PhysGetDefaultAABBSolid( tmpSolid );
- pSolid = &tmpSolid;
- }
- int surfaceProp = physprops->GetSurfaceIndex( pSolid->surfaceprop );
- pSolid->params.pGameData = static_cast<void *>(pEntity);
- pSolid->params.pName = pName;
- IPhysicsObject *pObject = NULL;
- if ( isStatic )
- {
- pObject = physenv->CreatePolyObjectStatic( pModel, surfaceProp, origin, angles, &pSolid->params );
- }
- else
- {
- pObject = physenv->CreatePolyObject( pModel, surfaceProp, origin, angles, &pSolid->params );
- }
-
- if ( pObject )
- g_pPhysSaveRestoreManager->AssociateModel( pObject, pModel);
-
- return pObject;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity -
-// radius -
-// &origin -
-// &solid -
-// Output : IPhysicsObject
-//-----------------------------------------------------------------------------
-IPhysicsObject *PhysSphereCreate( CBaseEntity *pEntity, float radius, const Vector &origin, solid_t &solid )
-{
- if ( !physenv )
- return NULL;
-
- int surfaceProp = -1;
- if ( solid.surfaceprop[0] )
- {
- surfaceProp = physprops->GetSurfaceIndex( solid.surfaceprop );
- }
-
- solid.params.pGameData = static_cast<void *>(pEntity);
- IPhysicsObject *pObject = physenv->CreateSphereObject( radius, surfaceProp, origin, vec3_angle, &solid.params, false );
-
- return pObject;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void PhysGetDefaultAABBSolid( solid_t &solid )
-{
- solid.params = g_PhysDefaultObjectParams;
- solid.params.mass = 85.0f;
- solid.params.inertia = 1e24f;
- Q_strncpy( solid.surfaceprop, "default", sizeof( solid.surfaceprop ) );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Destroy a physics object
-// Input : *pObject -
-//-----------------------------------------------------------------------------
-void PhysDestroyObject( IPhysicsObject *pObject, CBaseEntity *pEntity )
-{
- g_pPhysSaveRestoreManager->ForgetModel( pObject );
-
-
- if ( pObject )
- pObject->SetGameData( NULL );
-
- g_EntityCollisionHash->RemoveAllPairsForObject( pObject );
- if ( pEntity && pEntity->IsMarkedForDeletion() )
- {
- g_EntityCollisionHash->RemoveAllPairsForObject( pEntity );
- }
-
- if ( physenv )
- {
- physenv->DestroyObject( pObject );
- }
-}
-
-void AddSurfacepropFile( const char *pFileName, IPhysicsSurfaceProps *pProps, IFileSystem *pFileSystem )
-{
- // Load file into memory
- FileHandle_t file = pFileSystem->Open( pFileName, "rb", "GAME" );
-
- if ( file )
- {
- int len = pFileSystem->Size( file );
-
- // read the file
- int nBufSize = len+1;
- if ( IsXbox() )
- {
- nBufSize = AlignValue( nBufSize , 512 );
- }
- char *buffer = (char *)stackalloc( nBufSize );
- pFileSystem->ReadEx( buffer, nBufSize, len, file );
- pFileSystem->Close( file );
- buffer[len] = 0;
- pProps->ParseSurfaceData( pFileName, buffer );
- // buffer is on the stack, no need to free
- }
- else
- {
- Error( "Unable to load surface prop file '%s' (referenced by manifest file '%s')\n", pFileName, SURFACEPROP_MANIFEST_FILE );
- }
-}
-
-void PhysParseSurfaceData( IPhysicsSurfaceProps *pProps, IFileSystem *pFileSystem )
-{
- KeyValues *manifest = new KeyValues( SURFACEPROP_MANIFEST_FILE );
- if ( manifest->LoadFromFile( pFileSystem, SURFACEPROP_MANIFEST_FILE, "GAME" ) )
- {
- for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
- {
- if ( !Q_stricmp( sub->GetName(), "file" ) )
- {
- // Add
- AddSurfacepropFile( sub->GetString(), pProps, pFileSystem );
- continue;
- }
-
- Warning( "surfaceprops::Init: Manifest '%s' with bogus file type '%s', expecting 'file'\n",
- SURFACEPROP_MANIFEST_FILE, sub->GetName() );
- }
- }
- else
- {
- Error( "Unable to load manifest file '%s'\n", SURFACEPROP_MANIFEST_FILE );
- }
-
- manifest->deleteThis();
-}
-
-void PhysCreateVirtualTerrain( CBaseEntity *pWorld, const objectparams_t &defaultParams )
-{
- if ( !physenv )
- return;
-
- char nameBuf[1024];
- for ( int i = 0; i < MAX_MAP_DISPINFO; i++ )
- {
- CPhysCollide *pCollide = modelinfo->GetCollideForVirtualTerrain( i );
- if ( pCollide )
- {
- solid_t solid;
- solid.params = defaultParams;
- solid.params.enableCollisions = true;
- solid.params.pGameData = static_cast<void *>(pWorld);
- Q_snprintf(nameBuf, sizeof(nameBuf), "vdisp_%04d", i );
- solid.params.pName = nameBuf;
- int surfaceData = physprops->GetSurfaceIndex( "default" );
- // create this as part of the world
- IPhysicsObject *pObject = physenv->CreatePolyObjectStatic( pCollide, surfaceData, vec3_origin, vec3_angle, &solid.params );
- pObject->SetCallbackFlags( pObject->GetCallbackFlags() | CALLBACK_NEVER_DELETED );
- }
- }
-}
-
-IPhysicsObject *PhysCreateWorld_Shared( CBaseEntity *pWorld, vcollide_t *pWorldCollide, const objectparams_t &defaultParams )
-{
- solid_t solid;
- fluid_t fluid;
-
- if ( !physenv )
- return NULL;
-
- int surfaceData = physprops->GetSurfaceIndex( "default" );
-
- objectparams_t params = defaultParams;
- params.pGameData = static_cast<void *>(pWorld);
- params.pName = "world";
-
- IPhysicsObject *pWorldPhysics = physenv->CreatePolyObjectStatic(
- pWorldCollide->solids[0], surfaceData, vec3_origin, vec3_angle, ¶ms );
-
- // hint - saves vphysics some work
- pWorldPhysics->SetCallbackFlags( pWorldPhysics->GetCallbackFlags() | CALLBACK_NEVER_DELETED );
-
- //PhysCheckAdd( world, "World" );
- // walk the world keys in case there are some fluid volumes to create
- IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pWorldCollide->pKeyValues );
-
- bool bCreateVirtualTerrain = false;
- while ( !pParse->Finished() )
- {
- const char *pBlock = pParse->GetCurrentBlockName();
-
- if ( !strcmpi( pBlock, "solid" ) || !strcmpi( pBlock, "staticsolid" ) )
- {
- solid.params = defaultParams;
- pParse->ParseSolid( &solid, &g_SolidSetup );
- solid.params.enableCollisions = true;
- solid.params.pGameData = static_cast<void *>(pWorld);
- solid.params.pName = "world";
- int surfaceData = physprops->GetSurfaceIndex( "default" );
-
- // already created world above
- if ( solid.index == 0 )
- continue;
-
- if ( !pWorldCollide->solids[solid.index] )
- {
- // this implies that the collision model is a mopp and the physics DLL doesn't support that.
- bCreateVirtualTerrain = true;
- continue;
- }
- // create this as part of the world
- IPhysicsObject *pObject = physenv->CreatePolyObjectStatic( pWorldCollide->solids[solid.index],
- surfaceData, vec3_origin, vec3_angle, &solid.params );
-
- // invalid collision model or can't create, ignore
- if (!pObject)
- continue;
-
- pObject->SetCallbackFlags( pObject->GetCallbackFlags() | CALLBACK_NEVER_DELETED );
- Assert( g_SolidSetup.GetContentsMask() != 0 );
- pObject->SetContents( g_SolidSetup.GetContentsMask() );
-
- if ( !pWorldPhysics )
- {
- pWorldPhysics = pObject;
- }
- }
- else if ( !strcmpi( pBlock, "fluid" ) )
- {
- pParse->ParseFluid( &fluid, NULL );
-
- // create a fluid for floating
- if ( fluid.index > 0 )
- {
- solid.params = defaultParams; // copy world's params
- solid.params.enableCollisions = true;
- solid.params.pName = "fluid";
- solid.params.pGameData = static_cast<void *>(pWorld);
- fluid.params.pGameData = static_cast<void *>(pWorld);
- int surfaceData = physprops->GetSurfaceIndex( fluid.surfaceprop );
- // create this as part of the world
- IPhysicsObject *pWater = physenv->CreatePolyObjectStatic( pWorldCollide->solids[fluid.index],
- surfaceData, vec3_origin, vec3_angle, &solid.params );
-
- pWater->SetCallbackFlags( pWater->GetCallbackFlags() | CALLBACK_NEVER_DELETED );
- physenv->CreateFluidController( pWater, &fluid.params );
- }
- }
- else if ( !strcmpi( pBlock, "materialtable" ) )
- {
- int surfaceTable[128];
- memset( surfaceTable, 0, sizeof(surfaceTable) );
-
- pParse->ParseSurfaceTable( surfaceTable, NULL );
- physprops->SetWorldMaterialIndexTable( surfaceTable, 128 );
- }
- else if ( !strcmpi(pBlock, "virtualterrain" ) )
- {
- bCreateVirtualTerrain = true;
- pParse->SkipBlock();
- }
- else
- {
- // unknown chunk???
- pParse->SkipBlock();
- }
- }
- physcollision->VPhysicsKeyParserDestroy( pParse );
-
- if ( bCreateVirtualTerrain && physcollision->SupportsVirtualMesh() )
- {
- PhysCreateVirtualTerrain( pWorld, defaultParams );
- }
- return pWorldPhysics;
-}
-
-
-//=============================================================================
-//
-// Physics Game Trace
-//
-class CPhysicsGameTrace : public IPhysicsGameTrace
-{
-public:
-
- void VehicleTraceRay( const Ray_t &ray, void *pVehicle, trace_t *pTrace );
- void VehicleTraceRayWithWater( const Ray_t &ray, void *pVehicle, trace_t *pTrace );
- bool VehiclePointInWater( const Vector &vecPoint );
-};
-
-CPhysicsGameTrace g_PhysGameTrace;
-IPhysicsGameTrace *physgametrace = &g_PhysGameTrace;
-
-//-----------------------------------------------------------------------------
-// Purpose: Game ray-traces in vphysics.
-//-----------------------------------------------------------------------------
-void CPhysicsGameTrace::VehicleTraceRay( const Ray_t &ray, void *pVehicle, trace_t *pTrace )
-{
- CBaseEntity *pBaseEntity = static_cast<CBaseEntity*>( pVehicle );
- UTIL_TraceRay( ray, MASK_SOLID, pBaseEntity, COLLISION_GROUP_NONE, pTrace );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Game ray-traces in vphysics.
-//-----------------------------------------------------------------------------
-void CPhysicsGameTrace::VehicleTraceRayWithWater( const Ray_t &ray, void *pVehicle, trace_t *pTrace )
-{
- CBaseEntity *pBaseEntity = static_cast<CBaseEntity*>( pVehicle );
- UTIL_TraceRay( ray, MASK_SOLID|MASK_WATER, pBaseEntity, COLLISION_GROUP_NONE, pTrace );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Test to see if a vehicle point is in water.
-//-----------------------------------------------------------------------------
-bool CPhysicsGameTrace::VehiclePointInWater( const Vector &vecPoint )
-{
- return ( ( UTIL_PointContents( vecPoint ) & MASK_WATER ) != 0 );
-}
-
-void PhysRecheckObjectPair( IPhysicsObject *pObject0, IPhysicsObject *pObject1 )
-{
- if ( !pObject0->IsStatic() )
- {
- pObject0->RecheckCollisionFilter();
- }
- if ( !pObject1->IsStatic() )
- {
- pObject1->RecheckCollisionFilter();
- }
-}
-
-void PhysEnableEntityCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 )
-{
- if ( !pObject0 || !pObject1 )
- return;
-
- g_EntityCollisionHash->RemoveObjectPair( pObject0->GetGameData(), pObject1->GetGameData() );
- PhysRecheckObjectPair( pObject0, pObject1 );
-}
-
-// disables collisions between entities (each entity may contain multiple objects)
-void PhysDisableEntityCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 )
-{
- if ( !pObject0 || !pObject1 )
- return;
-
- g_EntityCollisionHash->AddObjectPair( pObject0->GetGameData(), pObject1->GetGameData() );
- PhysRecheckObjectPair( pObject0, pObject1 );
-}
-
-void PhysDisableEntityCollisions( CBaseEntity *pEntity0, CBaseEntity *pEntity1 )
-{
- if ( !pEntity0 || !pEntity1 )
- return;
-
- g_EntityCollisionHash->AddObjectPair( pEntity0, pEntity1 );
-#ifndef CLIENT_DLL
- pEntity0->CollisionRulesChanged();
- pEntity1->CollisionRulesChanged();
-#endif
-}
-
-
-void PhysEnableEntityCollisions( CBaseEntity *pEntity0, CBaseEntity *pEntity1 )
-{
- if ( !pEntity0 || !pEntity1 )
- return;
-
- g_EntityCollisionHash->RemoveObjectPair( pEntity0, pEntity1 );
-#ifndef CLIENT_DLL
- pEntity0->CollisionRulesChanged();
- pEntity1->CollisionRulesChanged();
-#endif
-}
-
-bool PhysEntityCollisionsAreDisabled( CBaseEntity *pEntity0, CBaseEntity *pEntity1 )
-{
- return g_EntityCollisionHash->IsObjectPairInHash( pEntity0, pEntity1 );
-}
-
-void PhysEnableObjectCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 )
-{
- if ( !pObject0 || !pObject1 )
- return;
-
- g_EntityCollisionHash->RemoveObjectPair( pObject0, pObject1 );
- PhysRecheckObjectPair( pObject0, pObject1 );
-}
-
-// disables collisions between entities (each entity may contain multiple objects)
-void PhysDisableObjectCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 )
-{
- if ( !pObject0 || !pObject1 )
- return;
-
- g_EntityCollisionHash->AddObjectPair( pObject0, pObject1 );
- PhysRecheckObjectPair( pObject0, pObject1 );
-}
-
-void PhysComputeSlideDirection( IPhysicsObject *pPhysics, const Vector &inputVelocity, const AngularImpulse &inputAngularVelocity,
- Vector *pOutputVelocity, Vector *pOutputAngularVelocity, float minMass )
-{
- Vector velocity = inputVelocity;
- AngularImpulse angVel = inputAngularVelocity;
- Vector pos;
-
- IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot();
- while ( pSnapshot->IsValid() )
- {
- IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
- if ( !pOther->IsMoveable() || pOther->GetMass() > minMass )
- {
- Vector normal;
- pSnapshot->GetSurfaceNormal( normal );
-
- // BUGBUG: Figure out the correct rotation clipping equation
- if ( pOutputAngularVelocity )
- {
- angVel = normal * DotProduct( angVel, normal );
-#if 0
- pSnapshot->GetContactPoint( point );
- Vector point, dummy;
- AngularImpulse angularClip, clip2;
-
- pPhysics->CalculateVelocityOffset( normal, point, dummy, angularClip );
- VectorNormalize( angularClip );
- float proj = DotProduct( angVel, angularClip );
- if ( proj > 0 )
- {
- angVel -= angularClip * proj;
- }
- CrossProduct( angularClip, normal, clip2 );
- proj = DotProduct( angVel, clip2 );
- if ( proj > 0 )
- {
- angVel -= clip2 * proj;
- }
- //NDebugOverlay::Line( point, point - normal * 20, 255, 0, 0, true, 0.1 );
-#endif
- }
-
- // Determine how far along plane to slide based on incoming direction.
- // NOTE: Normal points away from this object
- float proj = DotProduct( velocity, normal );
- if ( proj > 0.0f )
- {
- velocity -= normal * proj;
- }
- }
- pSnapshot->NextFrictionData();
- }
- pPhysics->DestroyFrictionSnapshot( pSnapshot );
-
- //NDebugOverlay::Line( pos, pos + unitVel * 20, 0, 0, 255, true, 0.1 );
-
- if ( pOutputVelocity )
- {
- *pOutputVelocity = velocity;
- }
- if ( pOutputAngularVelocity )
- {
- *pOutputAngularVelocity = angVel;
- }
-}
-
-bool PhysHasContactWithOtherInDirection( IPhysicsObject *pPhysics, const Vector &dir )
-{
- bool hit = false;
- void *pGameData = pPhysics->GetGameData();
- IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot();
- while ( pSnapshot->IsValid() )
- {
- IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
- if ( pOther->GetGameData() != pGameData )
- {
- Vector normal;
- pSnapshot->GetSurfaceNormal( normal );
- if ( DotProduct(normal,dir) > 0 )
- {
- hit = true;
- break;
- }
- }
- pSnapshot->NextFrictionData();
- }
- pPhysics->DestroyFrictionSnapshot( pSnapshot );
-
- return hit;
-}
-
-
-void PhysForceClearVelocity( IPhysicsObject *pPhys )
-{
- IPhysicsFrictionSnapshot *pSnapshot = pPhys->CreateFrictionSnapshot();
- // clear the velocity of the rigid body
- Vector vel;
- AngularImpulse angVel;
- vel.Init();
- angVel.Init();
- pPhys->SetVelocity( &vel, &angVel );
- // now clear the "strain" stored in the contact points
- while ( pSnapshot->IsValid() )
- {
- pSnapshot->ClearFrictionForce();
- pSnapshot->RecomputeFriction();
- pSnapshot->NextFrictionData();
- }
- pPhys->DestroyFrictionSnapshot( pSnapshot );
-}
-
-
-void PhysFrictionEffect( Vector &vecPos, Vector vecVel, float energy, int surfaceProps, int surfacePropsHit )
-{
- Vector invVecVel = -vecVel;
- VectorNormalize( invVecVel );
-
- surfacedata_t *psurf = physprops->GetSurfaceData( surfaceProps );
- surfacedata_t *phit = physprops->GetSurfaceData( surfacePropsHit );
-
- switch ( phit->game.material )
- {
- case CHAR_TEX_DIRT:
-
- if ( energy < MASS10_SPEED2ENERGY(15) )
- break;
-
- g_pEffects->Dust( vecPos, invVecVel, 1, 16 );
- break;
-
- case CHAR_TEX_CONCRETE:
-
- if ( energy < MASS10_SPEED2ENERGY(28) )
- break;
-
- g_pEffects->Dust( vecPos, invVecVel, 1, 16 );
- break;
- }
-
- //Metal sparks
- if ( energy > MASS10_SPEED2ENERGY(50) )
- {
- // make sparks for metal/concrete scrapes with enough energy
- if ( psurf->game.material == CHAR_TEX_METAL || psurf->game.material == CHAR_TEX_GRATE )
- {
- switch ( phit->game.material )
- {
- case CHAR_TEX_CONCRETE:
- case CHAR_TEX_METAL:
-
- g_pEffects->MetalSparks( vecPos, invVecVel );
- break;
- }
- }
- }
-}
-
-void PhysFrictionSound( CBaseEntity *pEntity, IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit )
-{
- if ( !pEntity || energy < 75.0f || surfaceProps < 0 )
- return;
-
- // don't make noise for hidden/invisible/sky materials
- surfacedata_t *phit = physprops->GetSurfaceData( surfacePropsHit );
- surfacedata_t *psurf = physprops->GetSurfaceData( surfaceProps );
-
- if ( phit->game.material == 'X' || psurf->game.material == 'X' )
- return;
-
- // rescale the incoming energy
- energy *= ENERGY_VOLUME_SCALE;
-
- // volume of scrape is proportional to square of energy (steeper rolloff at low energies)
- float volume = energy * energy;
-
- unsigned short soundName = psurf->sounds.scrapeRough;
- short *soundHandle = &psurf->soundhandles.scrapeRough;
-
- if ( psurf->sounds.scrapeSmooth && phit->audio.roughnessFactor < psurf->audio.roughThreshold )
- {
- soundName = psurf->sounds.scrapeSmooth;
- soundHandle = &psurf->soundhandles.scrapeRough;
- }
-
- const char *pSoundName = physprops->GetString( soundName );
-
- PhysFrictionSound( pEntity, pObject, pSoundName, *soundHandle, volume );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Precaches a surfaceproperties string name if it's set.
-// Input : idx -
-// Output : static void
-//-----------------------------------------------------------------------------
-static HSOUNDSCRIPTHANDLE PrecachePhysicsSoundByStringIndex( int idx )
-{
- // Only precache if a value was set in the script file...
- if ( idx != 0 )
- {
- return CBaseEntity::PrecacheScriptSound( physprops->GetString( idx ) );
- }
-
- return SOUNDEMITTER_INVALID_HANDLE;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Iterates all surfacedata sounds and precaches them
-// Output : static void
-//-----------------------------------------------------------------------------
-void PrecachePhysicsSounds()
-{
- // precache the surface prop sounds
- for ( int i = 0; i < physprops->SurfacePropCount(); i++ )
- {
- surfacedata_t *pprop = physprops->GetSurfaceData( i );
- Assert( pprop );
-
- pprop->soundhandles.stepleft = PrecachePhysicsSoundByStringIndex( pprop->sounds.stepleft );
- pprop->soundhandles.stepright = PrecachePhysicsSoundByStringIndex( pprop->sounds.stepright );
- pprop->soundhandles.impactSoft = PrecachePhysicsSoundByStringIndex( pprop->sounds.impactSoft );
- pprop->soundhandles.impactHard = PrecachePhysicsSoundByStringIndex( pprop->sounds.impactHard );
- pprop->soundhandles.scrapeSmooth = PrecachePhysicsSoundByStringIndex( pprop->sounds.scrapeSmooth );
- pprop->soundhandles.scrapeRough = PrecachePhysicsSoundByStringIndex( pprop->sounds.scrapeRough );
- pprop->soundhandles.bulletImpact = PrecachePhysicsSoundByStringIndex( pprop->sounds.bulletImpact );
- pprop->soundhandles.rolling = PrecachePhysicsSoundByStringIndex( pprop->sounds.rolling );
- pprop->soundhandles.breakSound = PrecachePhysicsSoundByStringIndex( pprop->sounds.breakSound );
- pprop->soundhandles.strainSound = PrecachePhysicsSoundByStringIndex( pprop->sounds.strainSound );
- }
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Game & Client shared functions moved from physics.cpp +// +//=============================================================================// +#include "cbase.h" +#include "vcollide_parse.h" +#include "filesystem.h" +#include "movevars_shared.h" +#include "engine/ivmodelinfo.h" +#include "physics_shared.h" +#include "solidsetdefaults.h" +#include "model_types.h" +#include "bone_setup.h" +#include "vphysics/object_hash.h" +#include "vphysics/friction.h" +#include "coordsize.h" +#include <KeyValues.h> +#include "decals.h" +#include "IEffects.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" + +#include "physics_saverestore.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// +IPhysics *physics = NULL; +IPhysicsObject *g_PhysWorldObject = NULL; +IPhysicsCollision *physcollision = NULL; +IPhysicsEnvironment *physenv = NULL; +#ifdef PORTAL +IPhysicsEnvironment *physenv_main = NULL; +#endif +IPhysicsSurfaceProps *physprops = NULL; +// UNDONE: This hash holds both entity & IPhysicsObject pointer pairs +// UNDONE: Split into separate hashes? +IPhysicsObjectPairHash *g_EntityCollisionHash = NULL; + +const char *SURFACEPROP_MANIFEST_FILE = "scripts/surfaceproperties_manifest.txt"; + +const objectparams_t g_PhysDefaultObjectParams = +{ + NULL, + 1.0, //mass + 1.0, // inertia + 0.1f, // damping + 0.1f, // rotdamping + 0.05f, // rotIntertiaLimit + "DEFAULT", + NULL,// game data + 0.f, // volume (leave 0 if you don't have one or call physcollision->CollideVolume() to compute it) + 1.0f, // drag coefficient + true,// enable collisions? +}; + + +void CSolidSetDefaults::ParseKeyValue( void *pData, const char *pKey, const char *pValue ) +{ + if ( !Q_stricmp( pKey, "contents" ) ) + { + m_contentsMask = atoi( pValue ); + } +} + +void CSolidSetDefaults::SetDefaults( void *pData ) +{ + solid_t *pSolid = (solid_t *)pData; + pSolid->params = g_PhysDefaultObjectParams; + m_contentsMask = CONTENTS_SOLID; +} + +CSolidSetDefaults g_SolidSetup; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &mins - +// &maxs - +// Output : CPhysCollide +//----------------------------------------------------------------------------- +CPhysCollide *PhysCreateBbox( const Vector &minsIn, const Vector &maxsIn ) +{ + // UNDONE: Track down why this causes errors for the player controller and adjust/enable + //float radius = 0.5 - DIST_EPSILON; + Vector mins = minsIn;// + Vector(radius, radius, radius); + Vector maxs = maxsIn;// - Vector(radius, radius, radius); + + // VPHYSICS caches/cleans up these + CPhysCollide *pResult = physcollision->BBoxToCollide( mins, maxs ); + + g_pPhysSaveRestoreManager->NoteBBox( mins, maxs, pResult ); + + return pResult; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEntity - +// &mins - +// &maxs - +// &origin - +// isStatic - +// Output : static IPhysicsObject +//----------------------------------------------------------------------------- +IPhysicsObject *PhysModelCreateBox( CBaseEntity *pEntity, const Vector &mins, const Vector &maxs, const Vector &origin, bool isStatic ) +{ + int modelIndex = pEntity->GetModelIndex(); + const char *pSurfaceProps = "flesh"; + solid_t solid; + PhysGetDefaultAABBSolid( solid ); + Vector dims = maxs - mins; + solid.params.volume = dims.x * dims.y * dims.z; + + if ( modelIndex ) + { + const model_t *model = modelinfo->GetModel( modelIndex ); + if ( model ) + { + CStudioHdr studioHdr( modelinfo->GetStudiomodel( model ), mdlcache ); + if ( studioHdr.IsValid() ) + { + pSurfaceProps = Studio_GetDefaultSurfaceProps( &studioHdr ); + } + } + } + Q_strncpy( solid.surfaceprop, pSurfaceProps, sizeof( solid.surfaceprop ) ); + + CPhysCollide *pCollide = PhysCreateBbox( mins, maxs ); + if ( !pCollide ) + return NULL; + + return PhysModelCreateCustom( pEntity, pCollide, origin, vec3_angle, STRING(pEntity->GetModelName()), isStatic, &solid ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEntity - +// &mins - +// &maxs - +// &origin - +// isStatic - +// Output : static IPhysicsObject +//----------------------------------------------------------------------------- +IPhysicsObject *PhysModelCreateOBB( CBaseEntity *pEntity, const Vector &mins, const Vector &maxs, const Vector &origin, const QAngle &angle, bool isStatic ) +{ + int modelIndex = pEntity->GetModelIndex(); + const char *pSurfaceProps = "flesh"; + solid_t solid; + PhysGetDefaultAABBSolid( solid ); + Vector dims = maxs - mins; + solid.params.volume = dims.x * dims.y * dims.z; + + if ( modelIndex ) + { + const model_t *model = modelinfo->GetModel( modelIndex ); + if ( model ) + { + CStudioHdr studioHdr( modelinfo->GetStudiomodel( model ), mdlcache ); + if (studioHdr.IsValid()) + { + pSurfaceProps = Studio_GetDefaultSurfaceProps( &studioHdr ); + } + } + } + Q_strncpy( solid.surfaceprop, pSurfaceProps, sizeof( solid.surfaceprop ) ); + + CPhysCollide *pCollide = PhysCreateBbox( mins, maxs ); + if ( !pCollide ) + return NULL; + + return PhysModelCreateCustom( pEntity, pCollide, origin, angle, STRING(pEntity->GetModelName()), isStatic, &solid ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &solid - +// *pEntity - +// modelIndex - +// solidIndex - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool PhysModelParseSolidByIndex( solid_t &solid, CBaseEntity *pEntity, int modelIndex, int solidIndex ) +{ + vcollide_t *pCollide = modelinfo->GetVCollide( modelIndex ); + if ( !pCollide ) + return false; + + bool parsed = false; + + memset( &solid, 0, sizeof(solid) ); + solid.params = g_PhysDefaultObjectParams; + + IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues ); + while ( !pParse->Finished() ) + { + const char *pBlock = pParse->GetCurrentBlockName(); + if ( !strcmpi( pBlock, "solid" ) ) + { + solid_t tmpSolid; + memset( &tmpSolid, 0, sizeof(tmpSolid) ); + tmpSolid.params = g_PhysDefaultObjectParams; + + pParse->ParseSolid( &tmpSolid, &g_SolidSetup ); + + if ( solidIndex < 0 || tmpSolid.index == solidIndex ) + { + parsed = true; + solid = tmpSolid; + // just to be sure we aren't ever getting a non-zero solid by accident + Assert( solidIndex >= 0 || solid.index == 0 ); + break; + } + } + else + { + pParse->SkipBlock(); + } + } + physcollision->VPhysicsKeyParserDestroy( pParse ); + + // collisions are off by default + solid.params.enableCollisions = true; + + solid.params.pGameData = static_cast<void *>(pEntity); + solid.params.pName = STRING(pEntity->GetModelName()); + return parsed; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &solid - +// *pEntity - +// modelIndex - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool PhysModelParseSolid( solid_t &solid, CBaseEntity *pEntity, int modelIndex ) +{ + return PhysModelParseSolidByIndex( solid, pEntity, modelIndex, -1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &solid - +// *pEntity - +// *pCollide - +// solidIndex - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool PhysModelParseSolidByIndex( solid_t &solid, CBaseEntity *pEntity, vcollide_t *pCollide, int solidIndex ) +{ + bool parsed = false; + + memset( &solid, 0, sizeof(solid) ); + solid.params = g_PhysDefaultObjectParams; + + IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pCollide->pKeyValues ); + while ( !pParse->Finished() ) + { + const char *pBlock = pParse->GetCurrentBlockName(); + if ( !strcmpi( pBlock, "solid" ) ) + { + solid_t tmpSolid; + memset( &tmpSolid, 0, sizeof(tmpSolid) ); + tmpSolid.params = g_PhysDefaultObjectParams; + + pParse->ParseSolid( &tmpSolid, &g_SolidSetup ); + + if ( solidIndex < 0 || tmpSolid.index == solidIndex ) + { + parsed = true; + solid = tmpSolid; + // just to be sure we aren't ever getting a non-zero solid by accident + Assert( solidIndex >= 0 || solid.index == 0 ); + break; + } + } + else + { + pParse->SkipBlock(); + } + } + physcollision->VPhysicsKeyParserDestroy( pParse ); + + // collisions are off by default + solid.params.enableCollisions = true; + + solid.params.pGameData = static_cast<void *>(pEntity); + solid.params.pName = STRING(pEntity->GetModelName()); + return parsed; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEntity - +// modelIndex - +// &origin - +// &angles - +// *pSolid - +// Output : IPhysicsObject +//----------------------------------------------------------------------------- +IPhysicsObject *PhysModelCreate( CBaseEntity *pEntity, int modelIndex, const Vector &origin, const QAngle &angles, solid_t *pSolid ) +{ + if ( !physenv ) + return NULL; + + vcollide_t *pCollide = modelinfo->GetVCollide( modelIndex ); + if ( !pCollide || !pCollide->solidCount ) + return NULL; + + solid_t tmpSolid; + if ( !pSolid ) + { + pSolid = &tmpSolid; + if ( !PhysModelParseSolidByIndex( tmpSolid, pEntity, pCollide, -1 ) ) + return NULL; + } + + int surfaceProp = -1; + if ( pSolid->surfaceprop[0] ) + { + surfaceProp = physprops->GetSurfaceIndex( pSolid->surfaceprop ); + } + IPhysicsObject *pObject = physenv->CreatePolyObject( pCollide->solids[pSolid->index], surfaceProp, origin, angles, &pSolid->params ); + //PhysCheckAdd( pObject, STRING(pEntity->m_iClassname) ); + + if ( pObject ) + { + if ( modelinfo->GetModelType(modelinfo->GetModel(modelIndex)) == mod_brush ) + { + unsigned int contents = modelinfo->GetModelContents( modelIndex ); + Assert(contents!=0); + // HACKHACK: contents is used to filter collisions + // HACKHACK: So keep solid on for water brushes since they should pass collision rules (as triggers) + if ( contents & MASK_WATER ) + { + contents |= CONTENTS_SOLID; + } + if ( contents != pObject->GetContents() && contents != 0 ) + { + pObject->SetContents( contents ); + pObject->RecheckCollisionFilter(); + } + } + + g_pPhysSaveRestoreManager->AssociateModel( pObject, modelIndex); + } + + return pObject; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEntity - +// modelIndex - +// &origin - +// &angles - +// Output : IPhysicsObject +//----------------------------------------------------------------------------- +IPhysicsObject *PhysModelCreateUnmoveable( CBaseEntity *pEntity, int modelIndex, const Vector &origin, const QAngle &angles ) +{ + if ( !physenv ) + return NULL; + + vcollide_t *pCollide = modelinfo->GetVCollide( modelIndex ); + if ( !pCollide || !pCollide->solidCount ) + return NULL; + + solid_t solid; + + if ( !PhysModelParseSolidByIndex( solid, pEntity, pCollide, -1 ) ) + return NULL; + + // collisions are off by default + solid.params.enableCollisions = true; + //solid.params.mass = 1.0; + int surfaceProp = -1; + if ( solid.surfaceprop[0] ) + { + surfaceProp = physprops->GetSurfaceIndex( solid.surfaceprop ); + } + solid.params.pGameData = static_cast<void *>(pEntity); + solid.params.pName = STRING(pEntity->GetModelName()); + IPhysicsObject *pObject = physenv->CreatePolyObjectStatic( pCollide->solids[0], surfaceProp, origin, angles, &solid.params ); + + //PhysCheckAdd( pObject, STRING(pEntity->m_iClassname) ); + if ( pObject ) + { + if ( modelinfo->GetModelType(modelinfo->GetModel(modelIndex)) == mod_brush ) + { + unsigned int contents = modelinfo->GetModelContents( modelIndex ); + Assert(contents!=0); + if ( contents != pObject->GetContents() && contents != 0 ) + { + pObject->SetContents( contents ); + pObject->RecheckCollisionFilter(); + } + } + g_pPhysSaveRestoreManager->AssociateModel( pObject, modelIndex); + } + + return pObject; +} + + +//----------------------------------------------------------------------------- +// Purpose: Create a vphysics object based on an existing collision model +// Input : *pEntity - +// *pModel - +// &origin - +// &angles - +// *pName - +// isStatic - +// *pSolid - +// Output : IPhysicsObject +//----------------------------------------------------------------------------- +IPhysicsObject *PhysModelCreateCustom( CBaseEntity *pEntity, const CPhysCollide *pModel, const Vector &origin, const QAngle &angles, const char *pName, bool isStatic, solid_t *pSolid ) +{ + if ( !physenv ) + return NULL; + + solid_t tmpSolid; + if ( !pSolid ) + { + PhysGetDefaultAABBSolid( tmpSolid ); + pSolid = &tmpSolid; + } + int surfaceProp = physprops->GetSurfaceIndex( pSolid->surfaceprop ); + pSolid->params.pGameData = static_cast<void *>(pEntity); + pSolid->params.pName = pName; + IPhysicsObject *pObject = NULL; + if ( isStatic ) + { + pObject = physenv->CreatePolyObjectStatic( pModel, surfaceProp, origin, angles, &pSolid->params ); + } + else + { + pObject = physenv->CreatePolyObject( pModel, surfaceProp, origin, angles, &pSolid->params ); + } + + if ( pObject ) + g_pPhysSaveRestoreManager->AssociateModel( pObject, pModel); + + return pObject; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEntity - +// radius - +// &origin - +// &solid - +// Output : IPhysicsObject +//----------------------------------------------------------------------------- +IPhysicsObject *PhysSphereCreate( CBaseEntity *pEntity, float radius, const Vector &origin, solid_t &solid ) +{ + if ( !physenv ) + return NULL; + + int surfaceProp = -1; + if ( solid.surfaceprop[0] ) + { + surfaceProp = physprops->GetSurfaceIndex( solid.surfaceprop ); + } + + solid.params.pGameData = static_cast<void *>(pEntity); + IPhysicsObject *pObject = physenv->CreateSphereObject( radius, surfaceProp, origin, vec3_angle, &solid.params, false ); + + return pObject; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PhysGetDefaultAABBSolid( solid_t &solid ) +{ + solid.params = g_PhysDefaultObjectParams; + solid.params.mass = 85.0f; + solid.params.inertia = 1e24f; + Q_strncpy( solid.surfaceprop, "default", sizeof( solid.surfaceprop ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destroy a physics object +// Input : *pObject - +//----------------------------------------------------------------------------- +void PhysDestroyObject( IPhysicsObject *pObject, CBaseEntity *pEntity ) +{ + g_pPhysSaveRestoreManager->ForgetModel( pObject ); + + + if ( pObject ) + pObject->SetGameData( NULL ); + + g_EntityCollisionHash->RemoveAllPairsForObject( pObject ); + if ( pEntity && pEntity->IsMarkedForDeletion() ) + { + g_EntityCollisionHash->RemoveAllPairsForObject( pEntity ); + } + + if ( physenv ) + { + physenv->DestroyObject( pObject ); + } +} + +void AddSurfacepropFile( const char *pFileName, IPhysicsSurfaceProps *pProps, IFileSystem *pFileSystem ) +{ + // Load file into memory + FileHandle_t file = pFileSystem->Open( pFileName, "rb", "GAME" ); + + if ( file ) + { + int len = pFileSystem->Size( file ); + + // read the file + int nBufSize = len+1; + if ( IsXbox() ) + { + nBufSize = AlignValue( nBufSize , 512 ); + } + char *buffer = (char *)stackalloc( nBufSize ); + pFileSystem->ReadEx( buffer, nBufSize, len, file ); + pFileSystem->Close( file ); + buffer[len] = 0; + pProps->ParseSurfaceData( pFileName, buffer ); + // buffer is on the stack, no need to free + } + else + { + Error( "Unable to load surface prop file '%s' (referenced by manifest file '%s')\n", pFileName, SURFACEPROP_MANIFEST_FILE ); + } +} + +void PhysParseSurfaceData( IPhysicsSurfaceProps *pProps, IFileSystem *pFileSystem ) +{ + KeyValues *manifest = new KeyValues( SURFACEPROP_MANIFEST_FILE ); + if ( manifest->LoadFromFile( pFileSystem, SURFACEPROP_MANIFEST_FILE, "GAME" ) ) + { + for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() ) + { + if ( !Q_stricmp( sub->GetName(), "file" ) ) + { + // Add + AddSurfacepropFile( sub->GetString(), pProps, pFileSystem ); + continue; + } + + Warning( "surfaceprops::Init: Manifest '%s' with bogus file type '%s', expecting 'file'\n", + SURFACEPROP_MANIFEST_FILE, sub->GetName() ); + } + } + else + { + Error( "Unable to load manifest file '%s'\n", SURFACEPROP_MANIFEST_FILE ); + } + + manifest->deleteThis(); +} + +void PhysCreateVirtualTerrain( CBaseEntity *pWorld, const objectparams_t &defaultParams ) +{ + if ( !physenv ) + return; + + char nameBuf[1024]; + for ( int i = 0; i < MAX_MAP_DISPINFO; i++ ) + { + CPhysCollide *pCollide = modelinfo->GetCollideForVirtualTerrain( i ); + if ( pCollide ) + { + solid_t solid; + solid.params = defaultParams; + solid.params.enableCollisions = true; + solid.params.pGameData = static_cast<void *>(pWorld); + Q_snprintf(nameBuf, sizeof(nameBuf), "vdisp_%04d", i ); + solid.params.pName = nameBuf; + int surfaceData = physprops->GetSurfaceIndex( "default" ); + // create this as part of the world + IPhysicsObject *pObject = physenv->CreatePolyObjectStatic( pCollide, surfaceData, vec3_origin, vec3_angle, &solid.params ); + pObject->SetCallbackFlags( pObject->GetCallbackFlags() | CALLBACK_NEVER_DELETED ); + } + } +} + +IPhysicsObject *PhysCreateWorld_Shared( CBaseEntity *pWorld, vcollide_t *pWorldCollide, const objectparams_t &defaultParams ) +{ + solid_t solid; + fluid_t fluid; + + if ( !physenv ) + return NULL; + + int surfaceData = physprops->GetSurfaceIndex( "default" ); + + objectparams_t params = defaultParams; + params.pGameData = static_cast<void *>(pWorld); + params.pName = "world"; + + IPhysicsObject *pWorldPhysics = physenv->CreatePolyObjectStatic( + pWorldCollide->solids[0], surfaceData, vec3_origin, vec3_angle, ¶ms ); + + // hint - saves vphysics some work + pWorldPhysics->SetCallbackFlags( pWorldPhysics->GetCallbackFlags() | CALLBACK_NEVER_DELETED ); + + //PhysCheckAdd( world, "World" ); + // walk the world keys in case there are some fluid volumes to create + IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pWorldCollide->pKeyValues ); + + bool bCreateVirtualTerrain = false; + while ( !pParse->Finished() ) + { + const char *pBlock = pParse->GetCurrentBlockName(); + + if ( !strcmpi( pBlock, "solid" ) || !strcmpi( pBlock, "staticsolid" ) ) + { + solid.params = defaultParams; + pParse->ParseSolid( &solid, &g_SolidSetup ); + solid.params.enableCollisions = true; + solid.params.pGameData = static_cast<void *>(pWorld); + solid.params.pName = "world"; + int surfaceData = physprops->GetSurfaceIndex( "default" ); + + // already created world above + if ( solid.index == 0 ) + continue; + + if ( !pWorldCollide->solids[solid.index] ) + { + // this implies that the collision model is a mopp and the physics DLL doesn't support that. + bCreateVirtualTerrain = true; + continue; + } + // create this as part of the world + IPhysicsObject *pObject = physenv->CreatePolyObjectStatic( pWorldCollide->solids[solid.index], + surfaceData, vec3_origin, vec3_angle, &solid.params ); + + // invalid collision model or can't create, ignore + if (!pObject) + continue; + + pObject->SetCallbackFlags( pObject->GetCallbackFlags() | CALLBACK_NEVER_DELETED ); + Assert( g_SolidSetup.GetContentsMask() != 0 ); + pObject->SetContents( g_SolidSetup.GetContentsMask() ); + + if ( !pWorldPhysics ) + { + pWorldPhysics = pObject; + } + } + else if ( !strcmpi( pBlock, "fluid" ) ) + { + pParse->ParseFluid( &fluid, NULL ); + + // create a fluid for floating + if ( fluid.index > 0 ) + { + solid.params = defaultParams; // copy world's params + solid.params.enableCollisions = true; + solid.params.pName = "fluid"; + solid.params.pGameData = static_cast<void *>(pWorld); + fluid.params.pGameData = static_cast<void *>(pWorld); + int surfaceData = physprops->GetSurfaceIndex( fluid.surfaceprop ); + // create this as part of the world + IPhysicsObject *pWater = physenv->CreatePolyObjectStatic( pWorldCollide->solids[fluid.index], + surfaceData, vec3_origin, vec3_angle, &solid.params ); + + pWater->SetCallbackFlags( pWater->GetCallbackFlags() | CALLBACK_NEVER_DELETED ); + physenv->CreateFluidController( pWater, &fluid.params ); + } + } + else if ( !strcmpi( pBlock, "materialtable" ) ) + { + int surfaceTable[128]; + memset( surfaceTable, 0, sizeof(surfaceTable) ); + + pParse->ParseSurfaceTable( surfaceTable, NULL ); + physprops->SetWorldMaterialIndexTable( surfaceTable, 128 ); + } + else if ( !strcmpi(pBlock, "virtualterrain" ) ) + { + bCreateVirtualTerrain = true; + pParse->SkipBlock(); + } + else + { + // unknown chunk??? + pParse->SkipBlock(); + } + } + physcollision->VPhysicsKeyParserDestroy( pParse ); + + if ( bCreateVirtualTerrain && physcollision->SupportsVirtualMesh() ) + { + PhysCreateVirtualTerrain( pWorld, defaultParams ); + } + return pWorldPhysics; +} + + +//============================================================================= +// +// Physics Game Trace +// +class CPhysicsGameTrace : public IPhysicsGameTrace +{ +public: + + void VehicleTraceRay( const Ray_t &ray, void *pVehicle, trace_t *pTrace ); + void VehicleTraceRayWithWater( const Ray_t &ray, void *pVehicle, trace_t *pTrace ); + bool VehiclePointInWater( const Vector &vecPoint ); +}; + +CPhysicsGameTrace g_PhysGameTrace; +IPhysicsGameTrace *physgametrace = &g_PhysGameTrace; + +//----------------------------------------------------------------------------- +// Purpose: Game ray-traces in vphysics. +//----------------------------------------------------------------------------- +void CPhysicsGameTrace::VehicleTraceRay( const Ray_t &ray, void *pVehicle, trace_t *pTrace ) +{ + CBaseEntity *pBaseEntity = static_cast<CBaseEntity*>( pVehicle ); + UTIL_TraceRay( ray, MASK_SOLID, pBaseEntity, COLLISION_GROUP_NONE, pTrace ); +} + +//----------------------------------------------------------------------------- +// Purpose: Game ray-traces in vphysics. +//----------------------------------------------------------------------------- +void CPhysicsGameTrace::VehicleTraceRayWithWater( const Ray_t &ray, void *pVehicle, trace_t *pTrace ) +{ + CBaseEntity *pBaseEntity = static_cast<CBaseEntity*>( pVehicle ); + UTIL_TraceRay( ray, MASK_SOLID|MASK_WATER, pBaseEntity, COLLISION_GROUP_NONE, pTrace ); +} + +//----------------------------------------------------------------------------- +// Purpose: Test to see if a vehicle point is in water. +//----------------------------------------------------------------------------- +bool CPhysicsGameTrace::VehiclePointInWater( const Vector &vecPoint ) +{ + return ( ( UTIL_PointContents( vecPoint ) & MASK_WATER ) != 0 ); +} + +void PhysRecheckObjectPair( IPhysicsObject *pObject0, IPhysicsObject *pObject1 ) +{ + if ( !pObject0->IsStatic() ) + { + pObject0->RecheckCollisionFilter(); + } + if ( !pObject1->IsStatic() ) + { + pObject1->RecheckCollisionFilter(); + } +} + +void PhysEnableEntityCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 ) +{ + if ( !pObject0 || !pObject1 ) + return; + + g_EntityCollisionHash->RemoveObjectPair( pObject0->GetGameData(), pObject1->GetGameData() ); + PhysRecheckObjectPair( pObject0, pObject1 ); +} + +// disables collisions between entities (each entity may contain multiple objects) +void PhysDisableEntityCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 ) +{ + if ( !pObject0 || !pObject1 ) + return; + + g_EntityCollisionHash->AddObjectPair( pObject0->GetGameData(), pObject1->GetGameData() ); + PhysRecheckObjectPair( pObject0, pObject1 ); +} + +void PhysDisableEntityCollisions( CBaseEntity *pEntity0, CBaseEntity *pEntity1 ) +{ + if ( !pEntity0 || !pEntity1 ) + return; + + g_EntityCollisionHash->AddObjectPair( pEntity0, pEntity1 ); +#ifndef CLIENT_DLL + pEntity0->CollisionRulesChanged(); + pEntity1->CollisionRulesChanged(); +#endif +} + + +void PhysEnableEntityCollisions( CBaseEntity *pEntity0, CBaseEntity *pEntity1 ) +{ + if ( !pEntity0 || !pEntity1 ) + return; + + g_EntityCollisionHash->RemoveObjectPair( pEntity0, pEntity1 ); +#ifndef CLIENT_DLL + pEntity0->CollisionRulesChanged(); + pEntity1->CollisionRulesChanged(); +#endif +} + +bool PhysEntityCollisionsAreDisabled( CBaseEntity *pEntity0, CBaseEntity *pEntity1 ) +{ + return g_EntityCollisionHash->IsObjectPairInHash( pEntity0, pEntity1 ); +} + +void PhysEnableObjectCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 ) +{ + if ( !pObject0 || !pObject1 ) + return; + + g_EntityCollisionHash->RemoveObjectPair( pObject0, pObject1 ); + PhysRecheckObjectPair( pObject0, pObject1 ); +} + +// disables collisions between entities (each entity may contain multiple objects) +void PhysDisableObjectCollisions( IPhysicsObject *pObject0, IPhysicsObject *pObject1 ) +{ + if ( !pObject0 || !pObject1 ) + return; + + g_EntityCollisionHash->AddObjectPair( pObject0, pObject1 ); + PhysRecheckObjectPair( pObject0, pObject1 ); +} + +void PhysComputeSlideDirection( IPhysicsObject *pPhysics, const Vector &inputVelocity, const AngularImpulse &inputAngularVelocity, + Vector *pOutputVelocity, Vector *pOutputAngularVelocity, float minMass ) +{ + Vector velocity = inputVelocity; + AngularImpulse angVel = inputAngularVelocity; + Vector pos; + + IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot(); + while ( pSnapshot->IsValid() ) + { + IPhysicsObject *pOther = pSnapshot->GetObject( 1 ); + if ( !pOther->IsMoveable() || pOther->GetMass() > minMass ) + { + Vector normal; + pSnapshot->GetSurfaceNormal( normal ); + + // BUGBUG: Figure out the correct rotation clipping equation + if ( pOutputAngularVelocity ) + { + angVel = normal * DotProduct( angVel, normal ); +#if 0 + pSnapshot->GetContactPoint( point ); + Vector point, dummy; + AngularImpulse angularClip, clip2; + + pPhysics->CalculateVelocityOffset( normal, point, dummy, angularClip ); + VectorNormalize( angularClip ); + float proj = DotProduct( angVel, angularClip ); + if ( proj > 0 ) + { + angVel -= angularClip * proj; + } + CrossProduct( angularClip, normal, clip2 ); + proj = DotProduct( angVel, clip2 ); + if ( proj > 0 ) + { + angVel -= clip2 * proj; + } + //NDebugOverlay::Line( point, point - normal * 20, 255, 0, 0, true, 0.1 ); +#endif + } + + // Determine how far along plane to slide based on incoming direction. + // NOTE: Normal points away from this object + float proj = DotProduct( velocity, normal ); + if ( proj > 0.0f ) + { + velocity -= normal * proj; + } + } + pSnapshot->NextFrictionData(); + } + pPhysics->DestroyFrictionSnapshot( pSnapshot ); + + //NDebugOverlay::Line( pos, pos + unitVel * 20, 0, 0, 255, true, 0.1 ); + + if ( pOutputVelocity ) + { + *pOutputVelocity = velocity; + } + if ( pOutputAngularVelocity ) + { + *pOutputAngularVelocity = angVel; + } +} + +bool PhysHasContactWithOtherInDirection( IPhysicsObject *pPhysics, const Vector &dir ) +{ + bool hit = false; + void *pGameData = pPhysics->GetGameData(); + IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot(); + while ( pSnapshot->IsValid() ) + { + IPhysicsObject *pOther = pSnapshot->GetObject( 1 ); + if ( pOther->GetGameData() != pGameData ) + { + Vector normal; + pSnapshot->GetSurfaceNormal( normal ); + if ( DotProduct(normal,dir) > 0 ) + { + hit = true; + break; + } + } + pSnapshot->NextFrictionData(); + } + pPhysics->DestroyFrictionSnapshot( pSnapshot ); + + return hit; +} + + +void PhysForceClearVelocity( IPhysicsObject *pPhys ) +{ + IPhysicsFrictionSnapshot *pSnapshot = pPhys->CreateFrictionSnapshot(); + // clear the velocity of the rigid body + Vector vel; + AngularImpulse angVel; + vel.Init(); + angVel.Init(); + pPhys->SetVelocity( &vel, &angVel ); + // now clear the "strain" stored in the contact points + while ( pSnapshot->IsValid() ) + { + pSnapshot->ClearFrictionForce(); + pSnapshot->RecomputeFriction(); + pSnapshot->NextFrictionData(); + } + pPhys->DestroyFrictionSnapshot( pSnapshot ); +} + + +void PhysFrictionEffect( Vector &vecPos, Vector vecVel, float energy, int surfaceProps, int surfacePropsHit ) +{ + Vector invVecVel = -vecVel; + VectorNormalize( invVecVel ); + + surfacedata_t *psurf = physprops->GetSurfaceData( surfaceProps ); + surfacedata_t *phit = physprops->GetSurfaceData( surfacePropsHit ); + + switch ( phit->game.material ) + { + case CHAR_TEX_DIRT: + + if ( energy < MASS10_SPEED2ENERGY(15) ) + break; + + g_pEffects->Dust( vecPos, invVecVel, 1, 16 ); + break; + + case CHAR_TEX_CONCRETE: + + if ( energy < MASS10_SPEED2ENERGY(28) ) + break; + + g_pEffects->Dust( vecPos, invVecVel, 1, 16 ); + break; + } + + //Metal sparks + if ( energy > MASS10_SPEED2ENERGY(50) ) + { + // make sparks for metal/concrete scrapes with enough energy + if ( psurf->game.material == CHAR_TEX_METAL || psurf->game.material == CHAR_TEX_GRATE ) + { + switch ( phit->game.material ) + { + case CHAR_TEX_CONCRETE: + case CHAR_TEX_METAL: + + g_pEffects->MetalSparks( vecPos, invVecVel ); + break; + } + } + } +} + +void PhysFrictionSound( CBaseEntity *pEntity, IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit ) +{ + if ( !pEntity || energy < 75.0f || surfaceProps < 0 ) + return; + + // don't make noise for hidden/invisible/sky materials + surfacedata_t *phit = physprops->GetSurfaceData( surfacePropsHit ); + surfacedata_t *psurf = physprops->GetSurfaceData( surfaceProps ); + + if ( phit->game.material == 'X' || psurf->game.material == 'X' ) + return; + + // rescale the incoming energy + energy *= ENERGY_VOLUME_SCALE; + + // volume of scrape is proportional to square of energy (steeper rolloff at low energies) + float volume = energy * energy; + + unsigned short soundName = psurf->sounds.scrapeRough; + short *soundHandle = &psurf->soundhandles.scrapeRough; + + if ( psurf->sounds.scrapeSmooth && phit->audio.roughnessFactor < psurf->audio.roughThreshold ) + { + soundName = psurf->sounds.scrapeSmooth; + soundHandle = &psurf->soundhandles.scrapeRough; + } + + const char *pSoundName = physprops->GetString( soundName ); + + PhysFrictionSound( pEntity, pObject, pSoundName, *soundHandle, volume ); +} + +//----------------------------------------------------------------------------- +// Purpose: Precaches a surfaceproperties string name if it's set. +// Input : idx - +// Output : static void +//----------------------------------------------------------------------------- +static HSOUNDSCRIPTHANDLE PrecachePhysicsSoundByStringIndex( int idx ) +{ + // Only precache if a value was set in the script file... + if ( idx != 0 ) + { + return CBaseEntity::PrecacheScriptSound( physprops->GetString( idx ) ); + } + + return SOUNDEMITTER_INVALID_HANDLE; +} + +//----------------------------------------------------------------------------- +// Purpose: Iterates all surfacedata sounds and precaches them +// Output : static void +//----------------------------------------------------------------------------- +void PrecachePhysicsSounds() +{ + // precache the surface prop sounds + for ( int i = 0; i < physprops->SurfacePropCount(); i++ ) + { + surfacedata_t *pprop = physprops->GetSurfaceData( i ); + Assert( pprop ); + + pprop->soundhandles.stepleft = PrecachePhysicsSoundByStringIndex( pprop->sounds.stepleft ); + pprop->soundhandles.stepright = PrecachePhysicsSoundByStringIndex( pprop->sounds.stepright ); + pprop->soundhandles.impactSoft = PrecachePhysicsSoundByStringIndex( pprop->sounds.impactSoft ); + pprop->soundhandles.impactHard = PrecachePhysicsSoundByStringIndex( pprop->sounds.impactHard ); + pprop->soundhandles.scrapeSmooth = PrecachePhysicsSoundByStringIndex( pprop->sounds.scrapeSmooth ); + pprop->soundhandles.scrapeRough = PrecachePhysicsSoundByStringIndex( pprop->sounds.scrapeRough ); + pprop->soundhandles.bulletImpact = PrecachePhysicsSoundByStringIndex( pprop->sounds.bulletImpact ); + pprop->soundhandles.rolling = PrecachePhysicsSoundByStringIndex( pprop->sounds.rolling ); + pprop->soundhandles.breakSound = PrecachePhysicsSoundByStringIndex( pprop->sounds.breakSound ); + pprop->soundhandles.strainSound = PrecachePhysicsSoundByStringIndex( pprop->sounds.strainSound ); + } +} + + |