Counter Strike : Global Offensive Source Code
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.

645 lines
16 KiB

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