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.
643 lines
17 KiB
643 lines
17 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
#include "cbase.h" |
|
#include "physics_vehicle.h" |
|
|
|
#include "ivp_material.hxx" |
|
#include <ctype.h> |
|
#include "utlsymbol.h" |
|
#include "tier1/strtools.h" |
|
#include "vcollide_parse_private.h" |
|
#include "ctype.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: This is the data stored for each material/surface propery list |
|
//----------------------------------------------------------------------------- |
|
class CSurface : public IVP_Material |
|
{ |
|
public: |
|
|
|
// IVP_Material |
|
virtual IVP_DOUBLE get_friction_factor() |
|
{ |
|
return data.physics.friction; |
|
} |
|
|
|
virtual IVP_DOUBLE get_elasticity() |
|
{ |
|
return data.physics.elasticity; |
|
} |
|
virtual const char *get_name(); |
|
// UNDONE: not implemented here. |
|
virtual IVP_DOUBLE get_second_friction_factor() |
|
{ |
|
return 0; |
|
} |
|
virtual IVP_DOUBLE get_adhesion() |
|
{ |
|
return 0; |
|
} |
|
|
|
virtual IVP_DOUBLE get_damping() |
|
{ |
|
return data.physics.dampening; |
|
} |
|
|
|
// strings |
|
CUtlSymbol m_name; |
|
unsigned short m_pad; |
|
|
|
// physics properties |
|
surfacedata_t data; |
|
}; |
|
|
|
|
|
class CPhysicsSurfaceProps; |
|
|
|
class CIVPMaterialManager : public IVP_Material_Manager |
|
{ |
|
typedef IVP_Material_Manager BaseClass; |
|
public: |
|
CIVPMaterialManager( void ); |
|
void Init( CPhysicsSurfaceProps *pProps ) { m_props = pProps; } |
|
void SetPropMap( int *map, int mapSize ); |
|
int RemapIVPMaterialIndex( int ivpMaterialIndex ) const; |
|
|
|
// IVP_Material_Manager |
|
virtual IVP_Material *get_material_by_index(IVP_Real_Object *pObject, const IVP_U_Point *world_position, int index); |
|
|
|
virtual IVP_DOUBLE get_friction_factor(IVP_Contact_Situation *situation) // returns values >0, value of 1.0f means object stands on a 45 degres hill |
|
{ |
|
// vehicle wheels get no friction with stuff that isn't ground |
|
// helps keep control of the car |
|
// traction on less than 60 degree slopes. |
|
float wheelFriction = 1.0f; |
|
if ( ShouldOverrideWheelContactFriction( &wheelFriction, situation->objects[0], situation->objects[1], &situation->surf_normal ) ) |
|
{ |
|
return wheelFriction; |
|
} |
|
|
|
IVP_DOUBLE factor = BaseClass::get_friction_factor( situation ); |
|
factor = clamp(factor,0.0,1.0); |
|
|
|
return factor; |
|
} |
|
|
|
virtual IVP_DOUBLE get_elasticity(IVP_Contact_Situation *situation) // range [0, 1.0f[, the relative speed after a collision compared to the speed before |
|
{ |
|
IVP_DOUBLE flElasticity = BaseClass::get_elasticity( situation ); |
|
if ( flElasticity > 1.0f ) |
|
{ |
|
flElasticity = 1.0f; |
|
} |
|
else if ( flElasticity < 0 ) |
|
{ |
|
flElasticity = 0; |
|
} |
|
return flElasticity; |
|
} |
|
|
|
private: |
|
CPhysicsSurfaceProps *m_props; |
|
unsigned short m_propMap[128]; |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: This is the main database of materials |
|
//----------------------------------------------------------------------------- |
|
class CPhysicsSurfaceProps : public IPhysicsSurfacePropsInternal |
|
{ |
|
public: |
|
CPhysicsSurfaceProps( void ); |
|
~CPhysicsSurfaceProps( void ); |
|
|
|
virtual int ParseSurfaceData( const char *pFilename, const char *pTextfile ); |
|
virtual int SurfacePropCount( void ) const; |
|
virtual int GetSurfaceIndex( const char *pPropertyName ) const; |
|
virtual void GetPhysicsProperties( int surfaceDataIndex, float *density, float *thickness, float *friction, float *elasticity ) const; |
|
virtual void GetPhysicsParameters( int surfaceDataIndex, surfacephysicsparams_t *pParamsOut ) const; |
|
virtual surfacedata_t *GetSurfaceData( int surfaceDataIndex ); |
|
virtual const char *GetString( unsigned short stringTableIndex ) const; |
|
virtual const char *GetPropName( int surfaceDataIndex ) const; |
|
virtual void SetWorldMaterialIndexTable( int *pMapArray, int mapSize ); |
|
virtual int RemapIVPMaterialIndex( int ivpMaterialIndex ) const |
|
{ |
|
return m_ivpManager.RemapIVPMaterialIndex( ivpMaterialIndex ); |
|
} |
|
bool IsReservedMaterialIndex( int materialIndex ) const; |
|
virtual const char *GetReservedMaterialName( int materialIndex ) const; |
|
int GetReservedFallBack( int materialIndex ) const; |
|
|
|
int GetReservedSurfaceIndex( const char *pPropertyName ) const; |
|
|
|
// The database is derived from the IVP material class |
|
const IVP_Material *GetIVPMaterial( int materialIndex ) const; |
|
IVP_Material *GetIVPMaterial( int materialIndex ); |
|
virtual int GetIVPMaterialIndex( const IVP_Material *pIVP ) const; |
|
IVP_Material_Manager *GetIVPManager( void ) { return &m_ivpManager; } |
|
|
|
const char *GetNameString( CUtlSymbol name ) const |
|
{ |
|
return m_strings.String(name); |
|
} |
|
|
|
private: |
|
const CSurface *GetInternalSurface( int materialIndex ) const; |
|
CSurface *GetInternalSurface( int materialIndex ); |
|
|
|
void CopyPhysicsProperties( CSurface *pOut, int baseIndex ); |
|
bool AddFileToDatabase( const char *pFilename ); |
|
|
|
private: |
|
CUtlSymbolTableMT m_strings; |
|
CUtlVector<CSurface> m_props; |
|
CUtlVector<CUtlSymbol> m_fileList; |
|
CIVPMaterialManager m_ivpManager; |
|
bool m_init; |
|
int m_shadowFallback; |
|
}; |
|
|
|
|
|
// Singleton database object |
|
CPhysicsSurfaceProps g_SurfaceDatabase; |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CPhysicsSurfaceProps, IPhysicsSurfaceProps, VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, g_SurfaceDatabase); |
|
|
|
|
|
// Global pointer to singleton for VPHYSICS.DLL internal access |
|
IPhysicsSurfacePropsInternal *physprops = &g_SurfaceDatabase; |
|
|
|
|
|
const char *CSurface::get_name() |
|
{ |
|
return g_SurfaceDatabase.GetNameString( m_name ); |
|
} |
|
|
|
CPhysicsSurfaceProps::CPhysicsSurfaceProps( void ) : m_fileList(8,8), m_strings( 0, 32, true ) |
|
{ |
|
m_ivpManager.Init( this ); |
|
// Force index 0 to be the empty string. Allows game code to check for zero, but |
|
// still resolve to a string |
|
m_strings.AddString(""); |
|
m_init = false; |
|
m_shadowFallback = 0; |
|
} |
|
|
|
|
|
CPhysicsSurfaceProps::~CPhysicsSurfaceProps( void ) |
|
{ |
|
} |
|
|
|
int CPhysicsSurfaceProps::SurfacePropCount( void ) const |
|
{ |
|
return m_props.Size(); |
|
} |
|
|
|
// Add the filename to a list to make sure each file is only processed once |
|
bool CPhysicsSurfaceProps::AddFileToDatabase( const char *pFilename ) |
|
{ |
|
CUtlSymbol id = m_strings.AddString( pFilename ); |
|
|
|
for ( int i = 0; i < m_fileList.Size(); i++ ) |
|
{ |
|
if ( m_fileList[i] == id ) |
|
return false; |
|
} |
|
|
|
m_fileList.AddToTail( id ); |
|
return true; |
|
} |
|
|
|
int CPhysicsSurfaceProps::GetSurfaceIndex( const char *pPropertyName ) const |
|
{ |
|
if ( pPropertyName[0] == '$' ) |
|
{ |
|
int index = GetReservedSurfaceIndex( pPropertyName ); |
|
if ( index >= 0 ) |
|
return index; |
|
} |
|
|
|
CUtlSymbol id = m_strings.Find( pPropertyName ); |
|
if ( id.IsValid() ) |
|
{ |
|
// BUGBUG: Linear search is slow!!! |
|
for ( int i = 0; i < m_props.Size(); i++ ) |
|
{ |
|
// NOTE: Just comparing strings by index is pretty fast though |
|
if ( m_props[i].m_name == id ) |
|
return i; |
|
} |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
|
|
const char *CPhysicsSurfaceProps::GetPropName( int surfaceDataIndex ) const |
|
{ |
|
const CSurface *pSurface = GetInternalSurface( surfaceDataIndex ); |
|
if ( pSurface ) |
|
{ |
|
return GetNameString( pSurface->m_name ); |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
// UNDONE: move reserved materials into this table, or into a parallel table |
|
// that gets hooked out here. |
|
CSurface *CPhysicsSurfaceProps::GetInternalSurface( int materialIndex ) |
|
{ |
|
if ( IsReservedMaterialIndex( materialIndex ) ) |
|
{ |
|
materialIndex = GetReservedFallBack( materialIndex ); |
|
} |
|
if ( materialIndex < 0 || materialIndex > m_props.Size()-1 ) |
|
{ |
|
return NULL; |
|
} |
|
return &m_props[materialIndex]; |
|
} |
|
|
|
// this function is actually const except for the return type, so this is safe |
|
const CSurface *CPhysicsSurfaceProps::GetInternalSurface( int materialIndex ) const |
|
{ |
|
return const_cast<CPhysicsSurfaceProps *>(this)->GetInternalSurface(materialIndex); |
|
} |
|
|
|
void CPhysicsSurfaceProps::GetPhysicsProperties( int materialIndex, float *density, float *thickness, float *friction, float *elasticity ) const |
|
{ |
|
const CSurface *pSurface = GetInternalSurface( materialIndex ); |
|
if ( !pSurface ) |
|
{ |
|
pSurface = GetInternalSurface( GetSurfaceIndex( "default" ) ); |
|
Assert ( pSurface ); |
|
} |
|
if ( pSurface ) |
|
{ |
|
if ( friction ) |
|
{ |
|
*friction = (float)pSurface->data.physics.friction; |
|
} |
|
if ( elasticity ) |
|
{ |
|
*elasticity = (float)pSurface->data.physics.elasticity; |
|
} |
|
if ( density ) |
|
{ |
|
*density = pSurface->data.physics.density; |
|
} |
|
if ( thickness ) |
|
{ |
|
*thickness = pSurface->data.physics.thickness; |
|
} |
|
} |
|
} |
|
|
|
void CPhysicsSurfaceProps::GetPhysicsParameters( int surfaceDataIndex, surfacephysicsparams_t *pParamsOut ) const |
|
{ |
|
if ( !pParamsOut ) |
|
return; |
|
|
|
const CSurface *pSurface = GetInternalSurface( surfaceDataIndex ); |
|
if ( pSurface ) |
|
{ |
|
*pParamsOut = pSurface->data.physics; |
|
} |
|
} |
|
|
|
surfacedata_t *CPhysicsSurfaceProps::GetSurfaceData( int materialIndex ) |
|
{ |
|
CSurface *pSurface = GetInternalSurface( materialIndex ); |
|
if (!pSurface) |
|
pSurface = GetInternalSurface( 0 ); // Zero is always the "default" property |
|
|
|
Assert ( pSurface ); |
|
return &pSurface->data; |
|
} |
|
|
|
const char *CPhysicsSurfaceProps::GetString( unsigned short stringTableIndex ) const |
|
{ |
|
return m_strings.String( stringTableIndex ); |
|
} |
|
|
|
|
|
bool CPhysicsSurfaceProps::IsReservedMaterialIndex( int materialIndex ) const |
|
{ |
|
return (materialIndex > 127) ? true : false; |
|
} |
|
|
|
const char *CPhysicsSurfaceProps::GetReservedMaterialName( int materialIndex ) const |
|
{ |
|
// NOTE: All of these must start with '$' |
|
switch( materialIndex ) |
|
{ |
|
case MATERIAL_INDEX_SHADOW: |
|
return "$MATERIAL_INDEX_SHADOW"; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
int CPhysicsSurfaceProps::GetReservedSurfaceIndex( const char *pPropertyName ) const |
|
{ |
|
if ( !Q_stricmp( pPropertyName, "$MATERIAL_INDEX_SHADOW" ) ) |
|
{ |
|
return MATERIAL_INDEX_SHADOW; |
|
} |
|
return -1; |
|
} |
|
|
|
const IVP_Material *CPhysicsSurfaceProps::GetIVPMaterial( int materialIndex ) const |
|
{ |
|
return GetInternalSurface(materialIndex); |
|
} |
|
|
|
IVP_Material *CPhysicsSurfaceProps::GetIVPMaterial( int materialIndex ) |
|
{ |
|
return GetInternalSurface(materialIndex); |
|
} |
|
|
|
|
|
int CPhysicsSurfaceProps::GetReservedFallBack( int materialIndex ) const |
|
{ |
|
switch( materialIndex ) |
|
{ |
|
case MATERIAL_INDEX_SHADOW: |
|
return m_shadowFallback; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
int CPhysicsSurfaceProps::GetIVPMaterialIndex( const IVP_Material *pIVP ) const |
|
{ |
|
int index = (const CSurface *)pIVP - m_props.Base(); |
|
if ( index >= 0 && index < m_props.Size() ) |
|
return index; |
|
|
|
return -1; |
|
} |
|
|
|
|
|
void CPhysicsSurfaceProps::CopyPhysicsProperties( CSurface *pOut, int baseIndex ) |
|
{ |
|
const CSurface *pSurface = GetInternalSurface( baseIndex ); |
|
if ( pSurface ) |
|
{ |
|
pOut->data = pSurface->data; |
|
} |
|
} |
|
|
|
|
|
int CPhysicsSurfaceProps::ParseSurfaceData( const char *pFileName, const char *pTextfile ) |
|
{ |
|
if ( !AddFileToDatabase( pFileName ) ) |
|
{ |
|
return 0; |
|
} |
|
|
|
const char *pText = pTextfile; |
|
|
|
do |
|
{ |
|
char key[MAX_KEYVALUE], value[MAX_KEYVALUE]; |
|
|
|
pText = ParseKeyvalue( pText, key, value ); |
|
if ( !strcmp(value, "{") ) |
|
{ |
|
CSurface prop; |
|
memset( &prop.data, 0, sizeof(prop.data) ); |
|
prop.m_name = m_strings.AddString( key ); |
|
int baseMaterial = GetSurfaceIndex( key ); |
|
if ( baseMaterial < 0 ) |
|
{ |
|
baseMaterial = GetSurfaceIndex( "default" ); |
|
} |
|
|
|
CopyPhysicsProperties( &prop, baseMaterial ); |
|
|
|
do |
|
{ |
|
pText = ParseKeyvalue( pText, key, value ); |
|
if ( !strcmpi( key, "}" ) ) |
|
{ |
|
// already in the database, don't add again, override values instead |
|
const char *pOverride = m_strings.String(prop.m_name); |
|
int propIndex = GetSurfaceIndex( pOverride ); |
|
if ( propIndex >= 0 ) |
|
{ |
|
CSurface *pSurface = GetInternalSurface( propIndex ); |
|
pSurface->data = prop.data; |
|
break; |
|
} |
|
|
|
m_props.AddToTail( prop ); |
|
break; |
|
} |
|
else if ( !strcmpi( key, "base" ) ) |
|
{ |
|
baseMaterial = GetSurfaceIndex( value ); |
|
CopyPhysicsProperties( &prop, baseMaterial ); |
|
} |
|
else if ( !strcmpi( key, "thickness" ) ) |
|
{ |
|
prop.data.physics.thickness = atof(value); |
|
} |
|
else if ( !strcmpi( key, "density" ) ) |
|
{ |
|
prop.data.physics.density = atof(value); |
|
} |
|
else if ( !strcmpi( key, "elasticity" ) ) |
|
{ |
|
prop.data.physics.elasticity = atof(value); |
|
} |
|
else if ( !strcmpi( key, "friction" ) ) |
|
{ |
|
prop.data.physics.friction = atof(value); |
|
} |
|
else if ( !strcmpi( key, "maxspeedfactor" ) ) |
|
{ |
|
prop.data.game.maxSpeedFactor = atof(value); |
|
} |
|
else if ( !strcmpi( key, "jumpfactor" ) ) |
|
{ |
|
prop.data.game.jumpFactor = atof(value); |
|
} |
|
else if ( !strcmpi( key, "climbable" ) ) |
|
{ |
|
prop.data.game.climbable = atoi(value); |
|
} |
|
// audio parameters |
|
else if ( !strcmpi( key, "audioReflectivity" ) ) |
|
{ |
|
prop.data.audio.reflectivity = atof(value); |
|
} |
|
else if ( !strcmpi( key, "audioHardnessFactor" ) ) |
|
{ |
|
prop.data.audio.hardnessFactor = atof(value); |
|
} |
|
else if ( !strcmpi( key, "audioHardMinVelocity" ) ) |
|
{ |
|
prop.data.audio.hardVelocityThreshold = atof(value); |
|
} |
|
else if ( !strcmpi( key, "audioRoughnessFactor" ) ) |
|
{ |
|
prop.data.audio.roughnessFactor = atof(value); |
|
} |
|
else if ( !strcmpi( key, "scrapeRoughThreshold" ) ) |
|
{ |
|
prop.data.audio.roughThreshold = atof(value); |
|
} |
|
else if ( !strcmpi( key, "impactHardThreshold" ) ) |
|
{ |
|
prop.data.audio.hardThreshold = atof(value); |
|
} |
|
// sound names |
|
else if ( !strcmpi( key, "stepleft" ) ) |
|
{ |
|
prop.data.sounds.stepleft = m_strings.AddString( value ); |
|
} |
|
else if ( !strcmpi( key, "stepright" ) ) |
|
{ |
|
prop.data.sounds.stepright = m_strings.AddString( value ); |
|
} |
|
else if ( !strcmpi( key, "impactsoft" ) ) |
|
{ |
|
prop.data.sounds.impactSoft = m_strings.AddString( value ); |
|
} |
|
else if ( !strcmpi( key, "impacthard" ) ) |
|
{ |
|
prop.data.sounds.impactHard = m_strings.AddString( value ); |
|
} |
|
else if ( !strcmpi( key, "scrapesmooth" ) ) |
|
{ |
|
prop.data.sounds.scrapeSmooth = m_strings.AddString( value ); |
|
} |
|
else if ( !strcmpi( key, "scraperough" ) ) |
|
{ |
|
prop.data.sounds.scrapeRough = m_strings.AddString( value ); |
|
} |
|
else if ( !strcmpi( key, "bulletimpact" ) ) |
|
{ |
|
prop.data.sounds.bulletImpact = m_strings.AddString( value ); |
|
} |
|
else if ( !strcmpi( key, "break" ) ) |
|
{ |
|
prop.data.sounds.breakSound = m_strings.AddString( value ); |
|
} |
|
else if ( !strcmpi( key, "strain" ) ) |
|
{ |
|
prop.data.sounds.strainSound = m_strings.AddString( value ); |
|
} |
|
else if ( !strcmpi( key, "rolling" ) ) |
|
{ |
|
prop.data.sounds.rolling = m_strings.AddString( value ); |
|
} |
|
else if ( !strcmpi( key, "gamematerial" ) ) |
|
{ |
|
if ( strlen(value) == 1 && !V_isdigit( value[0]) ) |
|
{ |
|
prop.data.game.material = toupper(value[0]); |
|
} |
|
else |
|
{ |
|
prop.data.game.material = atoi(value); |
|
} |
|
} |
|
else if ( !strcmpi( key, "dampening" ) ) |
|
{ |
|
prop.data.physics.dampening = atof(value); |
|
} |
|
else |
|
{ |
|
// force a breakpoint |
|
AssertMsg2( 0, "Bad surfaceprop key %s (%s)\n", key, value ); |
|
} |
|
} while (pText); |
|
} |
|
} while (pText); |
|
|
|
if ( !m_init ) |
|
{ |
|
m_init = true; |
|
//AddReservedMaterials |
|
CSurface prop; |
|
|
|
int baseMaterial = GetSurfaceIndex( "default" ); |
|
memset( &prop.data, 0, sizeof(prop.data) ); |
|
prop.m_name = m_strings.AddString( GetReservedMaterialName(MATERIAL_INDEX_SHADOW) ); |
|
CopyPhysicsProperties( &prop, baseMaterial ); |
|
prop.data.physics.elasticity = 1e-3f; |
|
prop.data.physics.friction = 0.8f; |
|
m_shadowFallback = m_props.AddToTail( prop ); |
|
} |
|
return m_props.Size(); |
|
} |
|
|
|
|
|
void CPhysicsSurfaceProps::SetWorldMaterialIndexTable( int *pMapArray, int mapSize ) |
|
{ |
|
m_ivpManager.SetPropMap( pMapArray, mapSize ); |
|
} |
|
|
|
CIVPMaterialManager::CIVPMaterialManager( void ) : IVP_Material_Manager( IVP_FALSE ) |
|
{ |
|
// by default every index maps to itself (NULL translation) |
|
for ( int i = 0; i < ARRAYSIZE(m_propMap); i++ ) |
|
{ |
|
m_propMap[i] = i; |
|
} |
|
} |
|
|
|
int CIVPMaterialManager::RemapIVPMaterialIndex( int ivpMaterialIndex ) const |
|
{ |
|
if ( ivpMaterialIndex > 127 ) |
|
return ivpMaterialIndex; |
|
|
|
return m_propMap[ivpMaterialIndex]; |
|
} |
|
|
|
// remap the incoming (from IVP) index and get the appropriate material |
|
// note that ivp will only supply indices between 1 and 127 |
|
IVP_Material *CIVPMaterialManager::get_material_by_index(IVP_Real_Object *pObject, const IVP_U_Point *world_position, int index) |
|
{ |
|
IVP_Material *tmp = m_props->GetIVPMaterial( RemapIVPMaterialIndex(index) ); |
|
Assert(tmp); |
|
if ( tmp ) |
|
{ |
|
return tmp; |
|
} |
|
else |
|
{ |
|
return m_props->GetIVPMaterial( m_props->GetSurfaceIndex( "default" ) ); |
|
} |
|
} |
|
|
|
// Installs a LUT for remapping IVP material indices to physprop indices |
|
// A table of the names of the materials in index order is stored with the |
|
// compiled bsp file. This is then remapped dynamically without touching the |
|
// per-triangle indices on load. If we wanted to support multiple LUTs, it would |
|
// be better to preprocess/remap the triangles in the collision models at load time |
|
void CIVPMaterialManager::SetPropMap( int *map, int mapSize ) |
|
{ |
|
// ??? just ignore any extra bits |
|
if ( mapSize > 128 ) |
|
{ |
|
mapSize = 128; |
|
} |
|
|
|
for ( int i = 0; i < mapSize; i++ ) |
|
{ |
|
m_propMap[i] = (unsigned short)map[i]; |
|
} |
|
}
|
|
|