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.

643 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "physics_vehicle.h"
  10. #include "ivp_material.hxx"
  11. #include <ctype.h>
  12. #include "utlsymbol.h"
  13. #include "tier1/strtools.h"
  14. #include "vcollide_parse_private.h"
  15. #include "ctype.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. //-----------------------------------------------------------------------------
  19. // Purpose: This is the data stored for each material/surface propery list
  20. //-----------------------------------------------------------------------------
  21. class CSurface : public IVP_Material
  22. {
  23. public:
  24. // IVP_Material
  25. virtual IVP_DOUBLE get_friction_factor()
  26. {
  27. return data.physics.friction;
  28. }
  29. virtual IVP_DOUBLE get_elasticity()
  30. {
  31. return data.physics.elasticity;
  32. }
  33. virtual const char *get_name();
  34. // UNDONE: not implemented here.
  35. virtual IVP_DOUBLE get_second_friction_factor()
  36. {
  37. return 0;
  38. }
  39. virtual IVP_DOUBLE get_adhesion()
  40. {
  41. return 0;
  42. }
  43. virtual IVP_DOUBLE get_damping()
  44. {
  45. return data.physics.dampening;
  46. }
  47. // strings
  48. CUtlSymbol m_name;
  49. unsigned short m_pad;
  50. // physics properties
  51. surfacedata_t data;
  52. };
  53. class CPhysicsSurfaceProps;
  54. class CIVPMaterialManager : public IVP_Material_Manager
  55. {
  56. typedef IVP_Material_Manager BaseClass;
  57. public:
  58. CIVPMaterialManager( void );
  59. void Init( CPhysicsSurfaceProps *pProps ) { m_props = pProps; }
  60. void SetPropMap( int *map, int mapSize );
  61. int RemapIVPMaterialIndex( int ivpMaterialIndex ) const;
  62. // IVP_Material_Manager
  63. virtual IVP_Material *get_material_by_index(IVP_Real_Object *pObject, const IVP_U_Point *world_position, int index);
  64. 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
  65. {
  66. // vehicle wheels get no friction with stuff that isn't ground
  67. // helps keep control of the car
  68. // traction on less than 60 degree slopes.
  69. float wheelFriction = 1.0f;
  70. if ( ShouldOverrideWheelContactFriction( &wheelFriction, situation->objects[0], situation->objects[1], &situation->surf_normal ) )
  71. {
  72. return wheelFriction;
  73. }
  74. IVP_DOUBLE factor = BaseClass::get_friction_factor( situation );
  75. factor = clamp(factor,0.0,1.0);
  76. return factor;
  77. }
  78. virtual IVP_DOUBLE get_elasticity(IVP_Contact_Situation *situation) // range [0, 1.0f[, the relative speed after a collision compared to the speed before
  79. {
  80. IVP_DOUBLE flElasticity = BaseClass::get_elasticity( situation );
  81. if ( flElasticity > 1.0f )
  82. {
  83. flElasticity = 1.0f;
  84. }
  85. else if ( flElasticity < 0 )
  86. {
  87. flElasticity = 0;
  88. }
  89. return flElasticity;
  90. }
  91. private:
  92. CPhysicsSurfaceProps *m_props;
  93. unsigned short m_propMap[128];
  94. };
  95. //-----------------------------------------------------------------------------
  96. // Purpose: This is the main database of materials
  97. //-----------------------------------------------------------------------------
  98. class CPhysicsSurfaceProps : public IPhysicsSurfacePropsInternal
  99. {
  100. public:
  101. CPhysicsSurfaceProps( void );
  102. ~CPhysicsSurfaceProps( void );
  103. virtual int ParseSurfaceData( const char *pFilename, const char *pTextfile );
  104. virtual int SurfacePropCount( void ) const;
  105. virtual int GetSurfaceIndex( const char *pPropertyName ) const;
  106. virtual void GetPhysicsProperties( int surfaceDataIndex, float *density, float *thickness, float *friction, float *elasticity ) const;
  107. virtual void GetPhysicsParameters( int surfaceDataIndex, surfacephysicsparams_t *pParamsOut ) const;
  108. virtual surfacedata_t *GetSurfaceData( int surfaceDataIndex );
  109. virtual const char *GetString( unsigned short stringTableIndex ) const;
  110. virtual const char *GetPropName( int surfaceDataIndex ) const;
  111. virtual void SetWorldMaterialIndexTable( int *pMapArray, int mapSize );
  112. virtual int RemapIVPMaterialIndex( int ivpMaterialIndex ) const
  113. {
  114. return m_ivpManager.RemapIVPMaterialIndex( ivpMaterialIndex );
  115. }
  116. bool IsReservedMaterialIndex( int materialIndex ) const;
  117. virtual const char *GetReservedMaterialName( int materialIndex ) const;
  118. int GetReservedFallBack( int materialIndex ) const;
  119. int GetReservedSurfaceIndex( const char *pPropertyName ) const;
  120. // The database is derived from the IVP material class
  121. const IVP_Material *GetIVPMaterial( int materialIndex ) const;
  122. IVP_Material *GetIVPMaterial( int materialIndex );
  123. virtual int GetIVPMaterialIndex( const IVP_Material *pIVP ) const;
  124. IVP_Material_Manager *GetIVPManager( void ) { return &m_ivpManager; }
  125. const char *GetNameString( CUtlSymbol name ) const
  126. {
  127. return m_strings.String(name);
  128. }
  129. private:
  130. const CSurface *GetInternalSurface( int materialIndex ) const;
  131. CSurface *GetInternalSurface( int materialIndex );
  132. void CopyPhysicsProperties( CSurface *pOut, int baseIndex );
  133. bool AddFileToDatabase( const char *pFilename );
  134. private:
  135. CUtlSymbolTableMT m_strings;
  136. CUtlVector<CSurface> m_props;
  137. CUtlVector<CUtlSymbol> m_fileList;
  138. CIVPMaterialManager m_ivpManager;
  139. bool m_init;
  140. int m_shadowFallback;
  141. };
  142. // Singleton database object
  143. CPhysicsSurfaceProps g_SurfaceDatabase;
  144. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CPhysicsSurfaceProps, IPhysicsSurfaceProps, VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, g_SurfaceDatabase);
  145. // Global pointer to singleton for VPHYSICS.DLL internal access
  146. IPhysicsSurfacePropsInternal *physprops = &g_SurfaceDatabase;
  147. const char *CSurface::get_name()
  148. {
  149. return g_SurfaceDatabase.GetNameString( m_name );
  150. }
  151. CPhysicsSurfaceProps::CPhysicsSurfaceProps( void ) : m_fileList(8,8), m_strings( 0, 32, true )
  152. {
  153. m_ivpManager.Init( this );
  154. // Force index 0 to be the empty string. Allows game code to check for zero, but
  155. // still resolve to a string
  156. m_strings.AddString("");
  157. m_init = false;
  158. m_shadowFallback = 0;
  159. }
  160. CPhysicsSurfaceProps::~CPhysicsSurfaceProps( void )
  161. {
  162. }
  163. int CPhysicsSurfaceProps::SurfacePropCount( void ) const
  164. {
  165. return m_props.Size();
  166. }
  167. // Add the filename to a list to make sure each file is only processed once
  168. bool CPhysicsSurfaceProps::AddFileToDatabase( const char *pFilename )
  169. {
  170. CUtlSymbol id = m_strings.AddString( pFilename );
  171. for ( int i = 0; i < m_fileList.Size(); i++ )
  172. {
  173. if ( m_fileList[i] == id )
  174. return false;
  175. }
  176. m_fileList.AddToTail( id );
  177. return true;
  178. }
  179. int CPhysicsSurfaceProps::GetSurfaceIndex( const char *pPropertyName ) const
  180. {
  181. if ( pPropertyName[0] == '$' )
  182. {
  183. int index = GetReservedSurfaceIndex( pPropertyName );
  184. if ( index >= 0 )
  185. return index;
  186. }
  187. CUtlSymbol id = m_strings.Find( pPropertyName );
  188. if ( id.IsValid() )
  189. {
  190. // BUGBUG: Linear search is slow!!!
  191. for ( int i = 0; i < m_props.Size(); i++ )
  192. {
  193. // NOTE: Just comparing strings by index is pretty fast though
  194. if ( m_props[i].m_name == id )
  195. return i;
  196. }
  197. }
  198. return -1;
  199. }
  200. const char *CPhysicsSurfaceProps::GetPropName( int surfaceDataIndex ) const
  201. {
  202. const CSurface *pSurface = GetInternalSurface( surfaceDataIndex );
  203. if ( pSurface )
  204. {
  205. return GetNameString( pSurface->m_name );
  206. }
  207. return NULL;
  208. }
  209. // UNDONE: move reserved materials into this table, or into a parallel table
  210. // that gets hooked out here.
  211. CSurface *CPhysicsSurfaceProps::GetInternalSurface( int materialIndex )
  212. {
  213. if ( IsReservedMaterialIndex( materialIndex ) )
  214. {
  215. materialIndex = GetReservedFallBack( materialIndex );
  216. }
  217. if ( materialIndex < 0 || materialIndex > m_props.Size()-1 )
  218. {
  219. return NULL;
  220. }
  221. return &m_props[materialIndex];
  222. }
  223. // this function is actually const except for the return type, so this is safe
  224. const CSurface *CPhysicsSurfaceProps::GetInternalSurface( int materialIndex ) const
  225. {
  226. return const_cast<CPhysicsSurfaceProps *>(this)->GetInternalSurface(materialIndex);
  227. }
  228. void CPhysicsSurfaceProps::GetPhysicsProperties( int materialIndex, float *density, float *thickness, float *friction, float *elasticity ) const
  229. {
  230. const CSurface *pSurface = GetInternalSurface( materialIndex );
  231. if ( !pSurface )
  232. {
  233. pSurface = GetInternalSurface( GetSurfaceIndex( "default" ) );
  234. Assert ( pSurface );
  235. }
  236. if ( pSurface )
  237. {
  238. if ( friction )
  239. {
  240. *friction = (float)pSurface->data.physics.friction;
  241. }
  242. if ( elasticity )
  243. {
  244. *elasticity = (float)pSurface->data.physics.elasticity;
  245. }
  246. if ( density )
  247. {
  248. *density = pSurface->data.physics.density;
  249. }
  250. if ( thickness )
  251. {
  252. *thickness = pSurface->data.physics.thickness;
  253. }
  254. }
  255. }
  256. void CPhysicsSurfaceProps::GetPhysicsParameters( int surfaceDataIndex, surfacephysicsparams_t *pParamsOut ) const
  257. {
  258. if ( !pParamsOut )
  259. return;
  260. const CSurface *pSurface = GetInternalSurface( surfaceDataIndex );
  261. if ( pSurface )
  262. {
  263. *pParamsOut = pSurface->data.physics;
  264. }
  265. }
  266. surfacedata_t *CPhysicsSurfaceProps::GetSurfaceData( int materialIndex )
  267. {
  268. CSurface *pSurface = GetInternalSurface( materialIndex );
  269. if (!pSurface)
  270. pSurface = GetInternalSurface( 0 ); // Zero is always the "default" property
  271. Assert ( pSurface );
  272. return &pSurface->data;
  273. }
  274. const char *CPhysicsSurfaceProps::GetString( unsigned short stringTableIndex ) const
  275. {
  276. return m_strings.String( stringTableIndex );
  277. }
  278. bool CPhysicsSurfaceProps::IsReservedMaterialIndex( int materialIndex ) const
  279. {
  280. return (materialIndex > 127) ? true : false;
  281. }
  282. const char *CPhysicsSurfaceProps::GetReservedMaterialName( int materialIndex ) const
  283. {
  284. // NOTE: All of these must start with '$'
  285. switch( materialIndex )
  286. {
  287. case MATERIAL_INDEX_SHADOW:
  288. return "$MATERIAL_INDEX_SHADOW";
  289. }
  290. return NULL;
  291. }
  292. int CPhysicsSurfaceProps::GetReservedSurfaceIndex( const char *pPropertyName ) const
  293. {
  294. if ( !Q_stricmp( pPropertyName, "$MATERIAL_INDEX_SHADOW" ) )
  295. {
  296. return MATERIAL_INDEX_SHADOW;
  297. }
  298. return -1;
  299. }
  300. const IVP_Material *CPhysicsSurfaceProps::GetIVPMaterial( int materialIndex ) const
  301. {
  302. return GetInternalSurface(materialIndex);
  303. }
  304. IVP_Material *CPhysicsSurfaceProps::GetIVPMaterial( int materialIndex )
  305. {
  306. return GetInternalSurface(materialIndex);
  307. }
  308. int CPhysicsSurfaceProps::GetReservedFallBack( int materialIndex ) const
  309. {
  310. switch( materialIndex )
  311. {
  312. case MATERIAL_INDEX_SHADOW:
  313. return m_shadowFallback;
  314. }
  315. return 0;
  316. }
  317. int CPhysicsSurfaceProps::GetIVPMaterialIndex( const IVP_Material *pIVP ) const
  318. {
  319. int index = (const CSurface *)pIVP - m_props.Base();
  320. if ( index >= 0 && index < m_props.Size() )
  321. return index;
  322. return -1;
  323. }
  324. void CPhysicsSurfaceProps::CopyPhysicsProperties( CSurface *pOut, int baseIndex )
  325. {
  326. const CSurface *pSurface = GetInternalSurface( baseIndex );
  327. if ( pSurface )
  328. {
  329. pOut->data = pSurface->data;
  330. }
  331. }
  332. int CPhysicsSurfaceProps::ParseSurfaceData( const char *pFileName, const char *pTextfile )
  333. {
  334. if ( !AddFileToDatabase( pFileName ) )
  335. {
  336. return 0;
  337. }
  338. const char *pText = pTextfile;
  339. do
  340. {
  341. char key[MAX_KEYVALUE], value[MAX_KEYVALUE];
  342. pText = ParseKeyvalue( pText, key, value );
  343. if ( !strcmp(value, "{") )
  344. {
  345. CSurface prop;
  346. memset( &prop.data, 0, sizeof(prop.data) );
  347. prop.m_name = m_strings.AddString( key );
  348. int baseMaterial = GetSurfaceIndex( key );
  349. if ( baseMaterial < 0 )
  350. {
  351. baseMaterial = GetSurfaceIndex( "default" );
  352. }
  353. CopyPhysicsProperties( &prop, baseMaterial );
  354. do
  355. {
  356. pText = ParseKeyvalue( pText, key, value );
  357. if ( !strcmpi( key, "}" ) )
  358. {
  359. // already in the database, don't add again, override values instead
  360. const char *pOverride = m_strings.String(prop.m_name);
  361. int propIndex = GetSurfaceIndex( pOverride );
  362. if ( propIndex >= 0 )
  363. {
  364. CSurface *pSurface = GetInternalSurface( propIndex );
  365. pSurface->data = prop.data;
  366. break;
  367. }
  368. m_props.AddToTail( prop );
  369. break;
  370. }
  371. else if ( !strcmpi( key, "base" ) )
  372. {
  373. baseMaterial = GetSurfaceIndex( value );
  374. CopyPhysicsProperties( &prop, baseMaterial );
  375. }
  376. else if ( !strcmpi( key, "thickness" ) )
  377. {
  378. prop.data.physics.thickness = atof(value);
  379. }
  380. else if ( !strcmpi( key, "density" ) )
  381. {
  382. prop.data.physics.density = atof(value);
  383. }
  384. else if ( !strcmpi( key, "elasticity" ) )
  385. {
  386. prop.data.physics.elasticity = atof(value);
  387. }
  388. else if ( !strcmpi( key, "friction" ) )
  389. {
  390. prop.data.physics.friction = atof(value);
  391. }
  392. else if ( !strcmpi( key, "maxspeedfactor" ) )
  393. {
  394. prop.data.game.maxSpeedFactor = atof(value);
  395. }
  396. else if ( !strcmpi( key, "jumpfactor" ) )
  397. {
  398. prop.data.game.jumpFactor = atof(value);
  399. }
  400. else if ( !strcmpi( key, "climbable" ) )
  401. {
  402. prop.data.game.climbable = atoi(value);
  403. }
  404. // audio parameters
  405. else if ( !strcmpi( key, "audioReflectivity" ) )
  406. {
  407. prop.data.audio.reflectivity = atof(value);
  408. }
  409. else if ( !strcmpi( key, "audioHardnessFactor" ) )
  410. {
  411. prop.data.audio.hardnessFactor = atof(value);
  412. }
  413. else if ( !strcmpi( key, "audioHardMinVelocity" ) )
  414. {
  415. prop.data.audio.hardVelocityThreshold = atof(value);
  416. }
  417. else if ( !strcmpi( key, "audioRoughnessFactor" ) )
  418. {
  419. prop.data.audio.roughnessFactor = atof(value);
  420. }
  421. else if ( !strcmpi( key, "scrapeRoughThreshold" ) )
  422. {
  423. prop.data.audio.roughThreshold = atof(value);
  424. }
  425. else if ( !strcmpi( key, "impactHardThreshold" ) )
  426. {
  427. prop.data.audio.hardThreshold = atof(value);
  428. }
  429. // sound names
  430. else if ( !strcmpi( key, "stepleft" ) )
  431. {
  432. prop.data.sounds.stepleft = m_strings.AddString( value );
  433. }
  434. else if ( !strcmpi( key, "stepright" ) )
  435. {
  436. prop.data.sounds.stepright = m_strings.AddString( value );
  437. }
  438. else if ( !strcmpi( key, "impactsoft" ) )
  439. {
  440. prop.data.sounds.impactSoft = m_strings.AddString( value );
  441. }
  442. else if ( !strcmpi( key, "impacthard" ) )
  443. {
  444. prop.data.sounds.impactHard = m_strings.AddString( value );
  445. }
  446. else if ( !strcmpi( key, "scrapesmooth" ) )
  447. {
  448. prop.data.sounds.scrapeSmooth = m_strings.AddString( value );
  449. }
  450. else if ( !strcmpi( key, "scraperough" ) )
  451. {
  452. prop.data.sounds.scrapeRough = m_strings.AddString( value );
  453. }
  454. else if ( !strcmpi( key, "bulletimpact" ) )
  455. {
  456. prop.data.sounds.bulletImpact = m_strings.AddString( value );
  457. }
  458. else if ( !strcmpi( key, "break" ) )
  459. {
  460. prop.data.sounds.breakSound = m_strings.AddString( value );
  461. }
  462. else if ( !strcmpi( key, "strain" ) )
  463. {
  464. prop.data.sounds.strainSound = m_strings.AddString( value );
  465. }
  466. else if ( !strcmpi( key, "rolling" ) )
  467. {
  468. prop.data.sounds.rolling = m_strings.AddString( value );
  469. }
  470. else if ( !strcmpi( key, "gamematerial" ) )
  471. {
  472. if ( strlen(value) == 1 && !V_isdigit( value[0]) )
  473. {
  474. prop.data.game.material = toupper(value[0]);
  475. }
  476. else
  477. {
  478. prop.data.game.material = atoi(value);
  479. }
  480. }
  481. else if ( !strcmpi( key, "dampening" ) )
  482. {
  483. prop.data.physics.dampening = atof(value);
  484. }
  485. else
  486. {
  487. // force a breakpoint
  488. AssertMsg2( 0, "Bad surfaceprop key %s (%s)\n", key, value );
  489. }
  490. } while (pText);
  491. }
  492. } while (pText);
  493. if ( !m_init )
  494. {
  495. m_init = true;
  496. //AddReservedMaterials
  497. CSurface prop;
  498. int baseMaterial = GetSurfaceIndex( "default" );
  499. memset( &prop.data, 0, sizeof(prop.data) );
  500. prop.m_name = m_strings.AddString( GetReservedMaterialName(MATERIAL_INDEX_SHADOW) );
  501. CopyPhysicsProperties( &prop, baseMaterial );
  502. prop.data.physics.elasticity = 1e-3f;
  503. prop.data.physics.friction = 0.8f;
  504. m_shadowFallback = m_props.AddToTail( prop );
  505. }
  506. return m_props.Size();
  507. }
  508. void CPhysicsSurfaceProps::SetWorldMaterialIndexTable( int *pMapArray, int mapSize )
  509. {
  510. m_ivpManager.SetPropMap( pMapArray, mapSize );
  511. }
  512. CIVPMaterialManager::CIVPMaterialManager( void ) : IVP_Material_Manager( IVP_FALSE )
  513. {
  514. // by default every index maps to itself (NULL translation)
  515. for ( int i = 0; i < ARRAYSIZE(m_propMap); i++ )
  516. {
  517. m_propMap[i] = i;
  518. }
  519. }
  520. int CIVPMaterialManager::RemapIVPMaterialIndex( int ivpMaterialIndex ) const
  521. {
  522. if ( ivpMaterialIndex > 127 )
  523. return ivpMaterialIndex;
  524. return m_propMap[ivpMaterialIndex];
  525. }
  526. // remap the incoming (from IVP) index and get the appropriate material
  527. // note that ivp will only supply indices between 1 and 127
  528. IVP_Material *CIVPMaterialManager::get_material_by_index(IVP_Real_Object *pObject, const IVP_U_Point *world_position, int index)
  529. {
  530. IVP_Material *tmp = m_props->GetIVPMaterial( RemapIVPMaterialIndex(index) );
  531. Assert(tmp);
  532. if ( tmp )
  533. {
  534. return tmp;
  535. }
  536. else
  537. {
  538. return m_props->GetIVPMaterial( m_props->GetSurfaceIndex( "default" ) );
  539. }
  540. }
  541. // Installs a LUT for remapping IVP material indices to physprop indices
  542. // A table of the names of the materials in index order is stored with the
  543. // compiled bsp file. This is then remapped dynamically without touching the
  544. // per-triangle indices on load. If we wanted to support multiple LUTs, it would
  545. // be better to preprocess/remap the triangles in the collision models at load time
  546. void CIVPMaterialManager::SetPropMap( int *map, int mapSize )
  547. {
  548. // ??? just ignore any extra bits
  549. if ( mapSize > 128 )
  550. {
  551. mapSize = 128;
  552. }
  553. for ( int i = 0; i < mapSize; i++ )
  554. {
  555. m_propMap[i] = (unsigned short)map[i];
  556. }
  557. }