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.

619 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. /***
  9. *
  10. * Copyright (c) 1998, Valve LLC. All rights reserved.
  11. *
  12. * This product contains software technology licensed from Id
  13. * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
  14. * All Rights Reserved.
  15. *
  16. ****/
  17. #include "filesystem.h"
  18. #include "vphysics/constraints.h"
  19. #include "phyfile.h"
  20. #include "physdll.h"
  21. #include "physmesh.h"
  22. #include "mathlib/mathlib.h"
  23. #include <stddef.h>
  24. #include "utlvector.h"
  25. #include "commonmacros.h"
  26. #include "studiomodel.h"
  27. #include "tier1/strtools.h"
  28. #include "bone_setup.h"
  29. #include "fmtstr.h"
  30. #include "vcollide_parse.h"
  31. int FindPhysprop( const char *pPropname );
  32. bool LoadPhysicsProperties( void );
  33. extern int FindBoneIndex( CStudioHdr *pstudiohdr, const char *pName );
  34. struct collisionpair_t
  35. {
  36. int object0;
  37. int object1;
  38. collisionpair_t *pNext;
  39. };
  40. class CStudioPhysics : public IStudioPhysics
  41. {
  42. public:
  43. CStudioPhysics( void )
  44. {
  45. m_pList = NULL;
  46. m_listCount = 0;
  47. m_mass = 0;
  48. m_noselfCollisions = false;
  49. m_pCollisionPairs = NULL;
  50. memset( &m_edit, 0, sizeof(editparams_t) );
  51. }
  52. ~CStudioPhysics( void )
  53. {
  54. if ( physcollision )
  55. {
  56. for ( int i = 0; i < m_listCount; i++ )
  57. {
  58. physcollision->DestroyDebugMesh( m_pList[i].m_vertCount, m_pList[i].m_pVerts );
  59. physcollision->DestroyQueryModel( m_pList[i].m_pCollisionModel );
  60. }
  61. }
  62. delete[] m_pList;
  63. }
  64. int Count( void )
  65. {
  66. return m_listCount;
  67. }
  68. CPhysmesh *GetMesh( int index )
  69. {
  70. if ( index < m_listCount )
  71. return m_pList + index;
  72. return NULL;
  73. }
  74. float GetMass( void ) { return m_mass; }
  75. void AddCollisionPair( int index0, int index1 )
  76. {
  77. collisionpair_t *pPair = new collisionpair_t;
  78. pPair->object0 = index0;
  79. pPair->object1 = index1;
  80. pPair->pNext = m_pCollisionPairs;
  81. m_pCollisionPairs = pPair;
  82. }
  83. void Load( MDLHandle_t handle );
  84. char *DumpQC( void );
  85. void ParseKeydata( void );
  86. vcollide_t *GetVCollide()
  87. {
  88. return g_pMDLCache->GetVCollide( m_MDLHandle );
  89. }
  90. CPhysmesh *m_pList;
  91. MDLHandle_t m_MDLHandle;
  92. int m_listCount;
  93. float m_mass;
  94. editparams_t m_edit;
  95. bool m_noselfCollisions;
  96. collisionpair_t *m_pCollisionPairs;
  97. };
  98. void CPhysmesh::Clear( void )
  99. {
  100. memset( this, 0, sizeof(*this) );
  101. memset( &m_constraint, 0, sizeof(m_constraint) );
  102. m_constraint.parentIndex = -1;
  103. m_constraint.childIndex = -1;
  104. }
  105. IStudioPhysics *LoadPhysics( MDLHandle_t mdlHandle )
  106. {
  107. CStudioPhysics *pPhysics = new CStudioPhysics;
  108. pPhysics->Load( mdlHandle );
  109. return pPhysics;
  110. }
  111. void DestroyPhysics( IStudioPhysics *pStudioPhysics )
  112. {
  113. CStudioPhysics *pPhysics = static_cast<CStudioPhysics*>( pStudioPhysics );
  114. if ( pPhysics )
  115. {
  116. delete pPhysics;
  117. }
  118. }
  119. void CStudioPhysics::Load( MDLHandle_t mdlHandle )
  120. {
  121. m_MDLHandle = mdlHandle;
  122. LoadPhysicsProperties();
  123. vcollide_t *pVCollide = GetVCollide( );
  124. if ( !pVCollide )
  125. {
  126. m_pList = NULL;
  127. m_listCount = 0;
  128. return;
  129. }
  130. m_pList = new CPhysmesh[pVCollide->solidCount];
  131. m_listCount = pVCollide->solidCount;
  132. int i;
  133. for ( i = 0; i < pVCollide->solidCount; i++ )
  134. {
  135. m_pList[i].Clear();
  136. m_pList[i].m_vertCount = physcollision->CreateDebugMesh( pVCollide->solids[i], &m_pList[i].m_pVerts );
  137. m_pList[i].m_pCollisionModel = physcollision->CreateQueryModel( pVCollide->solids[i] );
  138. }
  139. ParseKeydata();
  140. CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( mdlHandle ), g_pMDLCache );
  141. for ( i = 0; i < pVCollide->solidCount; i++ )
  142. {
  143. CPhysmesh *pmesh = m_pList + i;
  144. int boneIndex = FindBoneIndex( &studioHdr, pmesh->m_boneName );
  145. if ( boneIndex < 0 )
  146. continue;
  147. if ( pmesh->m_constraint.parentIndex >= 0 )
  148. {
  149. CPhysmesh *pparent = m_pList + pmesh->m_constraint.parentIndex;
  150. int parentIndex = FindBoneIndex( &studioHdr, pparent->m_boneName );
  151. Studio_CalcBoneToBoneTransform( &studioHdr, boneIndex, parentIndex, pmesh->m_matrix );
  152. }
  153. else
  154. {
  155. MatrixInvert( studioHdr.pBone(boneIndex)->poseToBone, pmesh->m_matrix );
  156. }
  157. }
  158. // doesn't have a root bone? Make it the first bone
  159. if ( !m_edit.rootName[0] )
  160. {
  161. strcpy( m_edit.rootName, m_pList[0].m_boneName );
  162. }
  163. }
  164. class CEditParse : public IVPhysicsKeyHandler
  165. {
  166. public:
  167. virtual void ParseKeyValue( void *pCustom, const char *pKey, const char *pValue )
  168. {
  169. editparams_t *pEdit = (editparams_t *)pCustom;
  170. if ( !strcmpi( pKey, "rootname" ) )
  171. {
  172. strncpy( pEdit->rootName, pValue, sizeof(pEdit->rootName) );
  173. }
  174. else if ( !strcmpi( pKey, "totalmass" ) )
  175. {
  176. pEdit->totalMass = atof( pValue );
  177. }
  178. else if ( !strcmpi( pKey, "concave" ) )
  179. {
  180. pEdit->concave = atoi( pValue );
  181. }
  182. else if ( !strcmpi( pKey, "jointmerge" ) )
  183. {
  184. char tmp[1024];
  185. char parentName[512], childName[512];
  186. Q_strncpy( tmp, pValue, 1024 );
  187. char *pWord = strtok( tmp, "," );
  188. Q_strncpy( parentName, pWord, sizeof(parentName) );
  189. pWord = strtok( NULL, "," );
  190. Q_strncpy( childName, pWord, sizeof(childName) );
  191. if ( pEdit->mergeCount < ARRAYSIZE(pEdit->mergeList) )
  192. {
  193. merge_t *pMerge = &pEdit->mergeList[pEdit->mergeCount];
  194. pEdit->mergeCount++;
  195. pMerge->parent = g_pStudioModel->FindBone(parentName);
  196. pMerge->child = g_pStudioModel->FindBone(childName);
  197. }
  198. }
  199. }
  200. virtual void SetDefaults( void *pCustom )
  201. {
  202. editparams_t *pEdit = (editparams_t *)pCustom;
  203. memset( pEdit, 0, sizeof(*pEdit) );
  204. }
  205. };
  206. class CRagdollCollisionRulesParse : public IVPhysicsKeyHandler
  207. {
  208. public:
  209. CRagdollCollisionRulesParse( CStudioPhysics *pStudio ) : m_pStudio(pStudio)
  210. {
  211. pStudio->m_noselfCollisions = false;
  212. }
  213. virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue )
  214. {
  215. if ( !strcmpi( pKey, "selfcollisions" ) )
  216. {
  217. // keys disabled by default
  218. Assert( atoi(pValue) == 0 );
  219. m_pStudio->m_noselfCollisions = true;
  220. }
  221. else if ( !strcmpi( pKey, "collisionpair" ) )
  222. {
  223. if ( !m_pStudio->m_noselfCollisions )
  224. {
  225. char tmp[1024];
  226. Q_strncpy( tmp, pValue, 1024 );
  227. char *pWord = strtok( tmp, "," );
  228. int index0 = atoi(pWord);
  229. pWord = strtok( NULL, "," );
  230. int index1 = atoi(pWord);
  231. m_pStudio->AddCollisionPair( index0, index1 );
  232. }
  233. else
  234. {
  235. Assert(0);
  236. }
  237. }
  238. }
  239. virtual void SetDefaults( void *pData ) {}
  240. private:
  241. CStudioPhysics *m_pStudio;
  242. };
  243. class CSolidParse : public IVPhysicsKeyHandler
  244. {
  245. public:
  246. virtual void ParseKeyValue( void *pCustom, const char *pKey, const char *pValue )
  247. {
  248. hlmvsolid_t *pSolid = (hlmvsolid_t *)pCustom;
  249. if ( !strcmpi( pKey, "massbias" ) )
  250. {
  251. pSolid->massBias = atof( pValue );
  252. }
  253. else
  254. {
  255. printf("Bad key %s!!\n", pKey);
  256. }
  257. }
  258. virtual void SetDefaults( void *pCustom )
  259. {
  260. hlmvsolid_t *pSolid = (hlmvsolid_t *)pCustom;
  261. pSolid->massBias = 1.0;
  262. }
  263. };
  264. void CStudioPhysics::ParseKeydata( void )
  265. {
  266. IVPhysicsKeyParser *pParser = physcollision->VPhysicsKeyParserCreate( GetVCollide()->pKeyValues );
  267. while ( !pParser->Finished() )
  268. {
  269. const char *pBlock = pParser->GetCurrentBlockName();
  270. if ( !stricmp( pBlock, "solid" ) )
  271. {
  272. hlmvsolid_t solid;
  273. CSolidParse solidParse;
  274. pParser->ParseSolid( &solid, &solidParse );
  275. solid.surfacePropIndex = FindPhysprop( solid.surfaceprop );
  276. if ( solid.index >= 0 && solid.index < m_listCount )
  277. {
  278. strcpy( m_pList[solid.index].m_boneName, solid.name );
  279. memcpy( &m_pList[solid.index].m_solid, &solid, sizeof(solid) );
  280. }
  281. }
  282. else if ( !stricmp( pBlock, "ragdollconstraint" ) )
  283. {
  284. constraint_ragdollparams_t constraint;
  285. pParser->ParseRagdollConstraint( &constraint, NULL );
  286. if ( constraint.childIndex >= 0 && constraint.childIndex < m_listCount )
  287. {
  288. // In the editor / qc these show up as 5X so that 1.0 is the default
  289. constraint.axes[0].torque *= 5;
  290. constraint.axes[1].torque *= 5;
  291. constraint.axes[2].torque *= 5;
  292. m_pList[constraint.childIndex].m_constraint = constraint;
  293. }
  294. }
  295. else if ( !stricmp( pBlock, "editparams" ) )
  296. {
  297. CEditParse editParse;
  298. pParser->ParseCustom( &m_edit, &editParse );
  299. m_mass = m_edit.totalMass;
  300. }
  301. else if ( !strcmpi( pBlock, "collisionrules" ) )
  302. {
  303. CRagdollCollisionRulesParse rules(this);
  304. pParser->ParseCustom( NULL, &rules );
  305. }
  306. else
  307. {
  308. pParser->SkipBlock();
  309. }
  310. }
  311. physcollision->VPhysicsKeyParserDestroy( pParser );
  312. }
  313. int FindPhysprop( const char *pPropname )
  314. {
  315. if ( physprop )
  316. {
  317. int count = physprop->SurfacePropCount();
  318. for ( int i = 0; i < count; i++ )
  319. {
  320. if ( !strcmpi( pPropname, physprop->GetPropName(i) ) )
  321. return i;
  322. }
  323. }
  324. return 0;
  325. }
  326. class CTextBuffer
  327. {
  328. public:
  329. CTextBuffer( void ) {}
  330. ~CTextBuffer( void ) {}
  331. inline int GetSize( void ) { return m_buffer.Size(); }
  332. inline char *GetData( void ) { return m_buffer.Base(); }
  333. void WriteText( const char *pText )
  334. {
  335. int len = strlen( pText );
  336. CopyData( pText, len );
  337. }
  338. void Terminate( void ) { CopyData( "\0", 1 ); }
  339. void CopyData( const char *pData, int len )
  340. {
  341. int offset = m_buffer.AddMultipleToTail( len );
  342. memcpy( m_buffer.Base() + offset, pData, len );
  343. }
  344. private:
  345. CUtlVector<char> m_buffer;
  346. };
  347. struct physdefaults_t
  348. {
  349. int surfacePropIndex;
  350. float inertia;
  351. float damping;
  352. float rotdamping;
  353. };
  354. //-----------------------------------------------------------------------------
  355. // Purpose: Nasty little routine (that was easy to code) to find the most common
  356. // value in an array of structs containing that as a member
  357. // Input : *pStructArray - pointer to head of struct array
  358. // arrayCount - number of elements in the array
  359. // structSize - size of each element
  360. // fieldOffset - offset to the float we're finding
  361. // Output : static T - most common value
  362. //-----------------------------------------------------------------------------
  363. template< class T >
  364. static T FindCommonValue( void *pStructArray, int arrayCount, int structSize, int fieldOffset )
  365. {
  366. int maxCount = 0;
  367. T maxVal = 0;
  368. // BUGBUG: This is O(n^2), but n is really small
  369. for ( int i = 0; i < arrayCount; i++ )
  370. {
  371. // current = struct[i].offset
  372. T current = *(T *)((char *)pStructArray + (i*structSize) + fieldOffset);
  373. int currentCount = 0;
  374. // if everything is set to the default, this is almost O(n)
  375. if ( current == maxVal )
  376. continue;
  377. for ( int j = 0; j < arrayCount; j++ )
  378. {
  379. // value = struct[j].offset
  380. T value = *(T *)((char *)pStructArray + (j*structSize) + fieldOffset);
  381. if ( value == current )
  382. currentCount++;
  383. }
  384. if ( currentCount > maxCount )
  385. {
  386. maxVal = current;
  387. maxCount = currentCount;
  388. }
  389. }
  390. return maxVal;
  391. }
  392. static void CalcDefaultProperties( CPhysmesh *pList, int listCount, physdefaults_t &defs )
  393. {
  394. defs.surfacePropIndex = FindCommonValue<int>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.surfacePropIndex) );
  395. defs.inertia = FindCommonValue<float>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.params.inertia) );
  396. defs.damping = FindCommonValue<float>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.params.damping) );
  397. defs.rotdamping = FindCommonValue<float>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.params.rotdamping) );
  398. }
  399. static void DumpModelProperties( CTextBuffer &out, float mass, physdefaults_t &defs )
  400. {
  401. char tmpbuf[1024];
  402. sprintf( tmpbuf, "\t$mass %.1f\r\n", mass );
  403. out.WriteText( tmpbuf );
  404. sprintf( tmpbuf, "\t$inertia %.2f\r\n", defs.inertia );
  405. out.WriteText( tmpbuf );
  406. sprintf( tmpbuf, "\t$damping %.2f\r\n", defs.damping );
  407. out.WriteText( tmpbuf );
  408. sprintf( tmpbuf, "\t$rotdamping %.2f\r\n", defs.rotdamping );
  409. out.WriteText( tmpbuf );
  410. }
  411. char *CStudioPhysics::DumpQC( void )
  412. {
  413. if ( !m_listCount )
  414. return NULL;
  415. CTextBuffer out;
  416. physdefaults_t defs;
  417. CalcDefaultProperties( m_pList, m_listCount, defs );
  418. if ( m_listCount == 1 )
  419. {
  420. out.WriteText( "$collisionmodel ragdoll {\r\n\r\n" );
  421. if ( m_edit.concave )
  422. {
  423. out.WriteText( "\t$concave\r\n" );
  424. }
  425. DumpModelProperties( out, m_mass, defs );
  426. }
  427. else
  428. {
  429. int i;
  430. out.WriteText( "$collisionjoints ragdoll {\r\n\r\n" );
  431. DumpModelProperties( out, m_mass, defs );
  432. // write out the root bone
  433. if ( m_edit.rootName[0] )
  434. {
  435. char tmp[128];
  436. sprintf( tmp, "\t$rootbone \"%s\"\r\n", m_edit.rootName );
  437. out.WriteText( tmp );
  438. }
  439. for ( i = 0; i < m_edit.mergeCount; i++ )
  440. {
  441. char tmp[1024];
  442. if ( m_edit.mergeList[i].parent >= 0 && m_edit.mergeList[i].child >= 0 )
  443. {
  444. char const *pParentName = g_pStudioModel->GetStudioHdr()->pBone(m_edit.mergeList[i].parent)->pszName();
  445. char const *pChildName = g_pStudioModel->GetStudioHdr()->pBone(m_edit.mergeList[i].child)->pszName();
  446. Q_snprintf( tmp, sizeof(tmp), "\t$jointmerge \"%s\" \"%s\"\r\n", pParentName, pChildName );
  447. out.WriteText( tmp );
  448. }
  449. }
  450. char tmpbuf[1024];
  451. for ( i = 0; i < m_listCount; i++ )
  452. {
  453. CPhysmesh *pmesh = m_pList + i;
  454. char jointname[256];
  455. sprintf( jointname, "\"%s\"", pmesh->m_boneName );
  456. if ( pmesh->m_solid.massBias != 1.0 )
  457. {
  458. sprintf( tmpbuf, "\t$jointmassbias %s %.2f\r\n", jointname, pmesh->m_solid.massBias );
  459. out.WriteText( tmpbuf );
  460. }
  461. if ( pmesh->m_solid.params.inertia != defs.inertia )
  462. {
  463. sprintf( tmpbuf, "\t$jointinertia %s %.2f\r\n", jointname, pmesh->m_solid.params.inertia );
  464. out.WriteText( tmpbuf );
  465. }
  466. if ( pmesh->m_solid.params.damping != defs.damping )
  467. {
  468. sprintf( tmpbuf, "\t$jointdamping %s %.2f\r\n", jointname, pmesh->m_solid.params.damping );
  469. out.WriteText( tmpbuf );
  470. }
  471. if ( pmesh->m_solid.params.rotdamping != defs.rotdamping )
  472. {
  473. sprintf( tmpbuf, "\t$jointrotdamping %s %.2f\r\n", jointname, pmesh->m_solid.params.rotdamping );
  474. out.WriteText( tmpbuf );
  475. }
  476. if ( pmesh->m_constraint.parentIndex >= 0 )
  477. {
  478. for ( int j = 0; j < 3; j++ )
  479. {
  480. char *pAxis[] = { "x", "y", "z" };
  481. sprintf( tmpbuf, "\t$jointconstrain %s %s limit %.2f %.2f %.2f\r\n", jointname, pAxis[j], pmesh->m_constraint.axes[j].minRotation, pmesh->m_constraint.axes[j].maxRotation, pmesh->m_constraint.axes[j].torque );
  482. out.WriteText( tmpbuf );
  483. }
  484. }
  485. if ( i != m_listCount-1 )
  486. {
  487. out.WriteText( "\r\n" );
  488. }
  489. }
  490. }
  491. if ( m_noselfCollisions )
  492. {
  493. out.WriteText( "\t$noselfcollisions\r\n" );
  494. }
  495. else if ( m_pCollisionPairs )
  496. {
  497. collisionpair_t *pPair = m_pCollisionPairs;
  498. out.WriteText("\r\n");
  499. while ( pPair )
  500. {
  501. out.WriteText( CFmtStr( "\t$jointcollide %s %s\r\n", m_pList[pPair->object0].m_boneName, m_pList[pPair->object1].m_boneName ) );
  502. pPair = pPair->pNext;
  503. }
  504. }
  505. out.WriteText( "}\r\n" );
  506. // only need the pose for ragdolls
  507. if ( m_listCount != 1 )
  508. {
  509. out.WriteText( "$sequence ragdoll \t\t\"ragdoll_pose\" \t\tFPS 30 \t\tactivity ACT_DIERAGDOLL 1\r\n" );
  510. }
  511. out.Terminate();
  512. if ( out.GetSize() )
  513. {
  514. char *pOutput = new char[out.GetSize()];
  515. memcpy( pOutput, out.GetData(), out.GetSize() );
  516. return pOutput;
  517. }
  518. return NULL;
  519. }
  520. static const char *pMaterialFilename = "scripts/surfaceproperties.txt";
  521. bool LoadPhysicsProperties( void )
  522. {
  523. // already loaded
  524. if ( physprop->SurfacePropCount() )
  525. return false;
  526. FileHandle_t fp = g_pFileSystem->Open( pMaterialFilename, "rb" );
  527. if ( fp == FILESYSTEM_INVALID_HANDLE )
  528. return false;
  529. int len = g_pFileSystem->Size( fp );
  530. char *pText = new char[len+1];
  531. g_pFileSystem->Read( pText, len, fp );
  532. g_pFileSystem->Close( fp );
  533. pText[len]=0;
  534. physprop->ParseSurfaceData( pMaterialFilename, pText );
  535. delete[] pText;
  536. return true;
  537. }