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.

2724 lines
74 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Builds physics collision models from studio model source
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. // NOTE: The term joint here is used to mean a bone, collision model, and a joint.
  10. // Each "joint" is the collision geometry at a named bone (or set of bones that have been merged)
  11. // and the joint (with constraints) between that set and its parent. The root "joint" has
  12. // no constraints.
  13. // I chose to refer to them as joints to avoid confusion. Yes they encompass bones and joints,
  14. // but they use the same names, and the data is actually linked.
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <sys/stat.h>
  18. #include <math.h>
  19. #include "vphysics/constraints.h"
  20. #include "collisionmodel.h"
  21. #include "cmdlib.h"
  22. #include "scriplib.h"
  23. #include "mathlib/mathlib.h"
  24. #include "studio.h"
  25. #include "studiomdl.h"
  26. #include "physdll.h"
  27. #include "phyfile.h"
  28. #include "utlvector.h"
  29. #include "vcollide_parse.h"
  30. #include "tier1/strtools.h"
  31. #include "tier2/tier2.h"
  32. #include "KeyValues.h"
  33. #include "tier1/smartptr.h"
  34. #include "tier2/p4helpers.h"
  35. // these functions just wrap atoi/atof and check for NULL
  36. static float Safe_atof( const char *pString );
  37. static int Safe_atoi( const char *pString );
  38. IPhysicsCollision *physcollision = NULL;
  39. IPhysicsSurfaceProps *physprops = NULL;
  40. float g_WeldVertEpsilon = 0.0f;
  41. float g_WeldNormalEpsilon = 0.999f;
  42. //-----------------------------------------------------------------------------
  43. // Purpose: Contains a single convex element of a physical collision system
  44. //-----------------------------------------------------------------------------
  45. class CPhysCollisionModel
  46. {
  47. public:
  48. CPhysCollisionModel( void )
  49. {
  50. memset( this, 0, sizeof(*this) );
  51. }
  52. const char *m_parent;
  53. const char *m_name;
  54. // physical properties stored on disk
  55. float m_mass;
  56. float m_volume;
  57. float m_surfaceArea;
  58. float m_damping;
  59. float m_rotdamping;
  60. float m_inertia;
  61. float m_dragCoefficient;
  62. // these tune the model building process, they don't go in the file
  63. float m_massBias;
  64. CPhysCollide *m_pCollisionData;
  65. CPhysCollisionModel *m_pNext;
  66. };
  67. enum jointlimit_t
  68. {
  69. JOINT_FREE = 0,
  70. JOINT_FIXED = 1,
  71. JOINT_LIMIT = 2,
  72. };
  73. // list of vertex indices that form a convex element
  74. struct convexlist_t
  75. {
  76. int firstVertIndex;
  77. int numVertIndex;
  78. };
  79. //-----------------------------------------------------------------------------
  80. // Purpose: element of a list of constraints for a jointed model
  81. //-----------------------------------------------------------------------------
  82. class CJointConstraint
  83. {
  84. public:
  85. CJointConstraint( void )
  86. {
  87. m_pJointName = NULL;
  88. }
  89. CJointConstraint( const char *pName, int axis, jointlimit_t type, float min, float max, float friction )
  90. : m_axis(axis), m_jointType(type), m_limitMin(min), m_limitMax(max), m_friction(friction)
  91. {
  92. m_pJointName = pName;
  93. }
  94. const char *m_pJointName;
  95. int m_axis;
  96. jointlimit_t m_jointType;
  97. float m_limitMin;
  98. float m_limitMax;
  99. float m_friction;
  100. CJointConstraint *m_pNext;
  101. };
  102. struct mergelist_t
  103. {
  104. char *pParent;
  105. char *pChild;
  106. };
  107. struct collisionpair_t
  108. {
  109. int obj0;
  110. int obj1;
  111. const char *pName0;
  112. const char *pName1;
  113. collisionpair_t *pNext;
  114. };
  115. //-----------------------------------------------------------------------------
  116. // Purpose: Search a source for a bone with a specified name
  117. // Input : *pSource -
  118. // *pName -
  119. // Output : int boneIndex, -1 if none
  120. //-----------------------------------------------------------------------------
  121. int FindLocalBoneNamed( const s_source_t *pSource, const char *pName )
  122. {
  123. if ( pName )
  124. {
  125. int i;
  126. for ( i = 0; i < pSource->numbones; i++ )
  127. {
  128. if ( !stricmp( pName, pSource->localBone[i].name ) )
  129. return i;
  130. }
  131. pName = RenameBone( pName );
  132. for ( i = 0; i < pSource->numbones; i++ )
  133. {
  134. if ( !stricmp( pName, pSource->localBone[i].name ) )
  135. return i;
  136. }
  137. }
  138. return -1;
  139. }
  140. // Returns the index to pName in g_bonetable
  141. int FindBoneInTable( const char *pName )
  142. {
  143. return findGlobalBone( pName );
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Purpose: Contains a complete physical joint system with constraint relationships
  147. //-----------------------------------------------------------------------------
  148. // This class is really just a namespace for a set of globals...
  149. class CJointedModel
  150. {
  151. public:
  152. s_source_t *m_pModel;
  153. int m_collisionCount;
  154. CPhysCollisionModel *m_pCollisionList;
  155. collisionpair_t *m_pCollisionPairs;
  156. float m_totalMass;
  157. int m_bonemap[MAXSTUDIOSRCBONES];
  158. CJointConstraint *m_pConstraintList;
  159. int m_constraintCount;
  160. int m_totalVerts;
  161. int m_maxConvex;
  162. char m_rootName[128];
  163. bool m_allowConcave;
  164. bool m_allowConcaveJoints;
  165. bool m_isMassCenterForced;
  166. bool m_noSelfCollisions;
  167. bool m_remove2d;
  168. Vector m_massCenterForced;
  169. float m_defaultDamping;
  170. float m_defaultRotdamping;
  171. float m_defaultInertia;
  172. float m_defaultDrag;
  173. CUtlVector<char> m_textCommands;
  174. CUtlVector<mergelist_t> m_mergeList;
  175. CJointedModel( void );
  176. void SetSource( s_source_t *pmodel );
  177. void InitBoneMap( void );
  178. void SkipBone( int boneIndex );
  179. void MergeBones( int parent, int child );
  180. void AddMergeCommand( char const *pParent, char const *pChild );
  181. bool ShouldProcessBone( int boneIndex );
  182. int BoneIndex( const char *pName );
  183. int RemapBone( int boneIndex ) const;
  184. void AppendCollisionModel( CPhysCollisionModel *pCollide );
  185. void UnlinkCollisionModel( CPhysCollisionModel *pCollide );
  186. CPhysCollisionModel *GetCollisionModel( const char *pName );
  187. void AppendCollisionPair( const char *pName0, const char *pName1 );
  188. void AddConstraint( const char *pJointName, int axis, jointlimit_t jointType, float limitMin, float limitMax, float friction );
  189. int CollisionIndex( const char *pName );
  190. void SortCollisionList( void );
  191. void ForceMassCenter( const Vector &centerOfMass );
  192. void AllowConcave( void ) { m_allowConcave = true; }
  193. void AllowConcaveJoints() { m_allowConcaveJoints = true; }
  194. void Remove2DConvex() { m_remove2d = true; }
  195. void SetMaxConvex( int newMax ) { m_maxConvex = newMax; }
  196. void Simplify();
  197. void DefaultDamping( float damping );
  198. void DefaultRotdamping( float rotdamping );
  199. void DefaultInertia( float inertia );
  200. void DefaultDrag( float drag );
  201. void SetTotalMass( float mass );
  202. void SetAutoMass( void );
  203. void SetNoSelfCollisions();
  204. void SetCollisionModelDefaults( CPhysCollisionModel *pModel );
  205. void JointDamping( const char *pJointName, float damping );
  206. void JointRotdamping( const char *pJointName, float rotdamping );
  207. void JointInertia( const char *pJointName, float inertia );
  208. void JointMassBias( const char *pJointName, float massBias );
  209. void AddText( const char *pText )
  210. {
  211. int len = strlen(pText);
  212. int count = m_textCommands.Size();
  213. m_textCommands.AddMultipleToTail( len );
  214. memcpy( m_textCommands.Base() + count, pText, len );
  215. }
  216. void ComputeMass( void );
  217. float m_flFrictionTimeIn;
  218. float m_flFrictionTimeOut;
  219. float m_flFrictionTimeHold;
  220. int m_iMinAnimatedFriction;
  221. int m_iMaxAnimatedFriction;
  222. bool m_bHasAnimatedFriction;
  223. bool m_bAssumeWorldspace; // assume the model is already declared in worldspace, regardless of bone names
  224. };
  225. CJointedModel g_JointedModel;
  226. bool g_bJointed = false;
  227. CJointedModel::CJointedModel( void )
  228. {
  229. m_pModel = NULL;
  230. m_collisionCount = 0;
  231. m_pCollisionList = NULL;
  232. m_pCollisionPairs = NULL;
  233. m_totalMass = 1.0;
  234. memset( m_bonemap, 0, sizeof(m_bonemap) );
  235. m_pConstraintList = NULL;
  236. m_constraintCount = 0;
  237. m_totalVerts = 0;
  238. // UNDONE: Move these defaults elsewhere? They are all overrideable by the QC/script
  239. m_defaultDamping = 0;
  240. m_defaultRotdamping = 0;
  241. m_defaultInertia = 1.0;
  242. m_defaultDrag = -1;
  243. m_allowConcave = false;
  244. m_allowConcaveJoints = false;
  245. m_remove2d = false;
  246. m_maxConvex = 40;
  247. m_isMassCenterForced = false;
  248. m_noSelfCollisions = false;
  249. m_massCenterForced.Init();
  250. m_flFrictionTimeIn = 0.0f;
  251. m_flFrictionTimeOut = 0.0f;
  252. m_iMinAnimatedFriction = 1.0f;
  253. m_iMaxAnimatedFriction = 1.0f;
  254. m_bHasAnimatedFriction = false;
  255. }
  256. void CJointedModel::SetSource( s_source_t *pmodel )
  257. {
  258. m_pModel = pmodel;
  259. InitBoneMap();
  260. m_totalVerts = pmodel->numvertices;
  261. }
  262. void CJointedModel::InitBoneMap( void )
  263. {
  264. for ( int i = 0; i < m_pModel->numbones; i++ )
  265. {
  266. m_bonemap[i] = i;
  267. }
  268. }
  269. void CJointedModel::SkipBone( int boneIndex )
  270. {
  271. if ( boneIndex >= 0 )
  272. m_bonemap[boneIndex] = -1;
  273. }
  274. void CJointedModel::AddMergeCommand( char const *pParent, char const *pChild )
  275. {
  276. int i = m_mergeList.AddToTail();
  277. m_mergeList[i].pParent = strdup(pParent);
  278. m_mergeList[i].pChild = strdup(pChild);
  279. }
  280. void CJointedModel::MergeBones( int parent, int child )
  281. {
  282. if ( parent < 0 || child < 0 )
  283. return;
  284. int map = parent;
  285. int safety = 0;
  286. while ( m_bonemap[map] != map )
  287. {
  288. map = m_bonemap[map];
  289. safety++;
  290. // infinite loop?
  291. if ( safety > m_pModel->numbones )
  292. break;
  293. if ( map < 0 )
  294. break;
  295. }
  296. m_bonemap[child] = map;
  297. }
  298. bool CJointedModel::ShouldProcessBone( int boneIndex )
  299. {
  300. if ( boneIndex >= 0 )
  301. {
  302. if ( m_bonemap[boneIndex] == boneIndex )
  303. return true;
  304. }
  305. return false;
  306. }
  307. int CJointedModel::BoneIndex( const char *pName )
  308. {
  309. pName = RenameBone( pName );
  310. for ( int boneIndex = 0; boneIndex < m_pModel->numbones; boneIndex++ )
  311. {
  312. if ( !stricmp( m_pModel->localBone[boneIndex].name, pName ) )
  313. return boneIndex;
  314. }
  315. return -1;
  316. }
  317. int CJointedModel::RemapBone( int boneIndex ) const
  318. {
  319. if ( boneIndex >= 0 )
  320. return m_bonemap[boneIndex];
  321. return boneIndex;
  322. }
  323. void CJointedModel::AppendCollisionModel( CPhysCollisionModel *pCollide )
  324. {
  325. if ( m_isMassCenterForced )
  326. {
  327. physcollision->CollideSetMassCenter( pCollide->m_pCollisionData, m_massCenterForced );
  328. }
  329. pCollide->m_pNext = m_pCollisionList;
  330. m_pCollisionList = pCollide;
  331. m_collisionCount++;
  332. }
  333. void CJointedModel::UnlinkCollisionModel( CPhysCollisionModel *pCollide )
  334. {
  335. CPhysCollisionModel **pList = &m_pCollisionList;
  336. if ( !pCollide )
  337. return;
  338. while ( *pList )
  339. {
  340. CPhysCollisionModel *pNode = *pList;
  341. if ( pNode == pCollide )
  342. {
  343. *pList = pCollide->m_pNext;
  344. m_collisionCount--;
  345. pCollide->m_pNext = NULL;
  346. return;
  347. }
  348. pList = &pNode->m_pNext;
  349. }
  350. }
  351. int CJointedModel::CollisionIndex( const char *pName )
  352. {
  353. CPhysCollisionModel *pList = m_pCollisionList;
  354. int index = 0;
  355. while ( pList )
  356. {
  357. if ( !stricmp( pName, pList->m_name ) )
  358. return index;
  359. pList = pList->m_pNext;
  360. index++;
  361. }
  362. return -1;
  363. }
  364. //-----------------------------------------------------------------------------
  365. // Purpose: Sort the list so that parents come before their children
  366. //-----------------------------------------------------------------------------
  367. void CJointedModel::SortCollisionList( void )
  368. {
  369. if ( !m_collisionCount )
  370. return;
  371. CPhysCollisionModel **pArray;
  372. pArray = new CPhysCollisionModel *[m_collisionCount];
  373. CPhysCollisionModel *pList = m_pCollisionList;
  374. // make an array to make sorting easier
  375. int i = 0;
  376. while ( pList )
  377. {
  378. pArray[i++] = pList;
  379. pList = pList->m_pNext;
  380. }
  381. // really stupid bubble sort!
  382. // this is really inefficient but it was easy to code and there are never
  383. // more than maxConvex elements.
  384. bool swapped = true;
  385. while ( swapped )
  386. {
  387. swapped = false;
  388. // loop over all solids and swap any parent/child pairs that are out of order
  389. for ( i = 0; i < m_collisionCount; i++ )
  390. {
  391. CPhysCollisionModel *pPhys = pArray[i];
  392. if ( !pPhys->m_parent )
  393. continue;
  394. // Don't try to move ones where the pPhys and its parent have the same name
  395. // otherwise an infinite loop results
  396. if ( !Q_stricmp( pPhys->m_name, pPhys->m_parent ) )
  397. continue;
  398. // find the parent
  399. int j;
  400. for ( j = 0; j < m_collisionCount; j++ )
  401. {
  402. if ( j == i )
  403. continue;
  404. if ( !stricmp( pPhys->m_parent, pArray[j]->m_name ) )
  405. break;
  406. }
  407. // if the child came before the parent, then swap the parent and child positions
  408. if ( j > i && j < m_collisionCount )
  409. {
  410. swapped = true;
  411. pArray[i] = pArray[j];
  412. pArray[j] = pPhys;
  413. }
  414. }
  415. }
  416. // link up the sorted list
  417. for ( i = 0; i < m_collisionCount-1; i++ )
  418. {
  419. pArray[i]->m_pNext = pArray[i+1];
  420. }
  421. // terminate
  422. pArray[i]->m_pNext = NULL;
  423. // point the list to first joint
  424. m_pCollisionList = pArray[0];
  425. // delete the working array
  426. delete[] pArray;
  427. }
  428. void CJointedModel::AppendCollisionPair( const char *pName0, const char *pName1 )
  429. {
  430. collisionpair_t *pPair = new collisionpair_t;
  431. pPair->obj0 = -1;
  432. pPair->obj1 = -1;
  433. int jointIndex0 = FindLocalBoneNamed( m_pModel, pName0 );
  434. pPair->pName0 = (jointIndex0 >= 0) ? m_pModel->localBone[jointIndex0].name : NULL;
  435. int jointIndex1 = FindLocalBoneNamed( m_pModel, pName1 );
  436. pPair->pName1 = (jointIndex1 >= 0) ? m_pModel->localBone[jointIndex1].name : NULL;
  437. pPair->pNext = m_pCollisionPairs;
  438. m_pCollisionPairs = pPair;
  439. }
  440. void CJointedModel::ForceMassCenter( const Vector &centerOfMass )
  441. {
  442. m_isMassCenterForced = true;
  443. m_massCenterForced = centerOfMass;
  444. }
  445. // called before processing, after the model has been simplified.
  446. // Update internal state due to simplification
  447. void CJointedModel::Simplify()
  448. {
  449. for ( int i = 0; i < m_pModel->numbones; i++ )
  450. {
  451. if ( m_pModel->boneLocalToGlobal[i] < 0 )
  452. {
  453. SkipBone(i);
  454. }
  455. }
  456. extern int g_rootIndex;
  457. const char *pAnimationRootBone = g_bonetable[g_rootIndex].name;
  458. // merge this root bone with the root of animation
  459. MergeBones( FindLocalBoneNamed( m_pModel, pAnimationRootBone ), FindLocalBoneNamed( m_pModel, m_rootName ) );
  460. }
  461. CPhysCollisionModel *CJointedModel::GetCollisionModel( const char *pName )
  462. {
  463. CPhysCollisionModel *pList = m_pCollisionList;
  464. while ( pList )
  465. {
  466. if ( !stricmp( pName, pList->m_name ) )
  467. return pList;
  468. pList = pList->m_pNext;
  469. }
  470. return NULL;
  471. }
  472. void CJointedModel::AddConstraint( const char *pJointName, int axis, jointlimit_t jointType, float limitMin, float limitMax, float friction )
  473. {
  474. // In the editor/qc friction values are shown as 5X so 1.0 can be the default.
  475. CJointConstraint *pConstraint = new CJointConstraint( pJointName, axis, jointType, limitMin, limitMax, friction * (1.0f/5.0f) );
  476. // link it in
  477. pConstraint->m_pNext = m_pConstraintList;
  478. m_pConstraintList = pConstraint;
  479. m_constraintCount++;
  480. }
  481. void CJointedModel::DefaultDamping( float damping )
  482. {
  483. m_defaultDamping = damping;
  484. }
  485. void CJointedModel::DefaultRotdamping( float rotdamping )
  486. {
  487. m_defaultRotdamping = rotdamping;
  488. }
  489. void CJointedModel::DefaultInertia( float inertia )
  490. {
  491. m_defaultInertia = inertia;
  492. }
  493. void CJointedModel::SetTotalMass( float mass )
  494. {
  495. m_totalMass = mass;
  496. }
  497. void CJointedModel::SetAutoMass( void )
  498. {
  499. m_totalMass = -1;
  500. }
  501. void CJointedModel::SetNoSelfCollisions()
  502. {
  503. m_noSelfCollisions = true;
  504. }
  505. void CJointedModel::SetCollisionModelDefaults( CPhysCollisionModel *pModel )
  506. {
  507. pModel->m_damping = m_defaultDamping;
  508. pModel->m_inertia = m_defaultInertia;
  509. pModel->m_rotdamping = m_defaultRotdamping;
  510. pModel->m_massBias = 1.0;
  511. // not written unless modified
  512. pModel->m_dragCoefficient = m_defaultDrag;
  513. }
  514. void CJointedModel::ComputeMass( void )
  515. {
  516. // already set
  517. if ( m_totalMass >= 0 )
  518. return;
  519. CPhysCollisionModel *pList = m_pCollisionList;
  520. m_totalMass = 0;
  521. while ( pList )
  522. {
  523. char* pSurfaceProps = GetSurfaceProp( pList->m_name );
  524. int index = physprops->GetSurfaceIndex( pSurfaceProps );
  525. float density, thickness;
  526. physprops->GetPhysicsProperties( index, &density, &thickness, NULL, NULL );
  527. if ( thickness > 0 )
  528. {
  529. m_totalMass += pList->m_surfaceArea * thickness * CUBIC_METERS_PER_CUBIC_INCH * density;
  530. }
  531. else
  532. {
  533. // density is in kg/m^3, volume is in in^3
  534. m_totalMass += pList->m_volume * CUBIC_METERS_PER_CUBIC_INCH * density;
  535. }
  536. pList = pList->m_pNext;
  537. }
  538. if( !g_quiet )
  539. {
  540. printf("Computed Mass: %.2f kg\n", m_totalMass );
  541. }
  542. }
  543. //-----------------------------------------------------------------------------
  544. // Purpose: Creates a collision object using the defaults in joints
  545. // Input : &joints - joint system to create the model in
  546. // *pJointName - name to give this model
  547. // Output : static CPhysCollisionModel
  548. //-----------------------------------------------------------------------------
  549. static CPhysCollisionModel *InitCollisionModel( CJointedModel &joints, const char *pJointName )
  550. {
  551. CPhysCollisionModel *pModel = joints.GetCollisionModel( pJointName );
  552. if ( !pModel )
  553. {
  554. int boneIndex = joints.BoneIndex( pJointName );
  555. if ( boneIndex < 0 )
  556. return NULL;
  557. pModel = new CPhysCollisionModel;
  558. // this name is the same as pJointName, but guaranteed to be non-volatile (we'd have to copy pJointName)
  559. pModel->m_name = joints.m_pModel->localBone[boneIndex].name;
  560. if ( joints.m_pModel->localBone[boneIndex].parent >= 0 )
  561. {
  562. pModel->m_parent = joints.m_pModel->localBone[joints.m_pModel->localBone[boneIndex].parent].name;
  563. }
  564. else
  565. {
  566. pModel->m_parent = NULL;
  567. }
  568. joints.SetCollisionModelDefaults( pModel );
  569. joints.AppendCollisionModel( pModel );
  570. }
  571. return pModel;
  572. }
  573. void CJointedModel::JointDamping( const char *pJointName, float damping )
  574. {
  575. CPhysCollisionModel *pModel = InitCollisionModel( *this, pJointName );
  576. if ( pModel )
  577. {
  578. pModel->m_damping = damping;
  579. }
  580. }
  581. void CJointedModel::JointRotdamping( const char *pJointName, float rotdamping )
  582. {
  583. CPhysCollisionModel *pModel = InitCollisionModel( *this, pJointName );
  584. if ( pModel )
  585. {
  586. pModel->m_rotdamping = rotdamping;
  587. }
  588. }
  589. void CJointedModel::JointMassBias( const char *pJointName, float massBias )
  590. {
  591. CPhysCollisionModel *pModel = InitCollisionModel( *this, pJointName );
  592. if ( pModel )
  593. {
  594. pModel->m_massBias = massBias;
  595. }
  596. }
  597. void CJointedModel::JointInertia( const char *pJointName, float inertia )
  598. {
  599. CPhysCollisionModel *pModel = InitCollisionModel( *this, pJointName );
  600. if ( pModel )
  601. {
  602. pModel->m_inertia = inertia;
  603. }
  604. }
  605. void CJointedModel::DefaultDrag( float drag )
  606. {
  607. m_defaultDrag = drag;
  608. }
  609. // ----------------------------------------------------------
  610. //-----------------------------------------------------------------------------
  611. // Purpose: Transforms the source's verts into "world" space
  612. // Input : *psource -
  613. // *worldVerts -
  614. //-----------------------------------------------------------------------------
  615. void ConvertToWorldSpace( CJointedModel &joints, s_source_t *psource, CUtlVector<Vector> &worldVerts )
  616. {
  617. int i, n;
  618. if (!joints.m_bAssumeWorldspace)
  619. {
  620. matrix3x4_t boneToWorld[MAXSTUDIOSRCBONES]; // bone transformation matrix
  621. CalcBoneTransforms( g_panimation[0], 0, boneToWorld );
  622. for (i = 0; i < psource->numvertices; i++)
  623. {
  624. Vector tmp,tmp2;
  625. worldVerts[i].Init( 0, 0, 0 );
  626. int nBoneCount = psource->vertex[i].boneweight.numbones;
  627. for (n = 0; n < nBoneCount; n++)
  628. {
  629. // convert to Half-Life world space
  630. // convert vertex into original models' bone local space
  631. int localBone = psource->vertex[i].boneweight.bone[n];
  632. int globalBone = psource->boneLocalToGlobal[localBone];
  633. Assert( localBone >= 0 );
  634. Assert( globalBone >= 0 );
  635. matrix3x4_t boneToPose;
  636. ConcatTransforms( psource->boneToPose[localBone], g_bonetable[globalBone].srcRealign, boneToPose );
  637. VectorITransform( psource->vertex[i].position, boneToPose, tmp2 );
  638. // now transform to that bone's world-space position in this animation
  639. VectorTransform(tmp2, boneToWorld[globalBone], tmp );
  640. VectorMA( worldVerts[i], psource->vertex[i].boneweight.weight[n], tmp, worldVerts[i] );
  641. }
  642. }
  643. }
  644. else
  645. {
  646. matrix3x4_t srcBoneToWorld[MAXSTUDIOSRCBONES]; // bone transformation matrix
  647. BuildRawTransforms( psource, "BindPose", 0, psource->scale, psource->adjust, psource->rotation, 0, srcBoneToWorld );
  648. for (i = 0; i < psource->numvertices; i++)
  649. {
  650. Vector tmp;
  651. worldVerts[i].Init( 0, 0, 0 );
  652. int nBoneCount = psource->vertex[i].boneweight.numbones;
  653. for (n = 0; n < nBoneCount; n++)
  654. {
  655. int localBone = psource->vertex[i].boneweight.bone[n];
  656. Assert( localBone >= 0 );
  657. // convert vertex into world space
  658. VectorTransform( psource->vertex[i].position, srcBoneToWorld[localBone], tmp );
  659. // just assume the model is in identity space
  660. // FIXME: shouldn't this do an inverse xform of the default boneToWorld?
  661. VectorMA( worldVerts[i], psource->vertex[i].boneweight.weight[n], tmp, worldVerts[i] );
  662. }
  663. }
  664. }
  665. }
  666. //-----------------------------------------------------------------------------
  667. // Purpose: Transforms the set of verts into the space of a particular bone
  668. // Input : *psource -
  669. // boneIndex -
  670. // *boneVerts -
  671. //-----------------------------------------------------------------------------
  672. void ConvertToBoneSpace( s_source_t *psource, int boneIndex, CUtlVector<Vector> &boneVerts )
  673. {
  674. int i;
  675. int remapIndex = psource->boneLocalToGlobal[boneIndex];
  676. matrix3x4_t boneToPose;
  677. if ( remapIndex < 0 )
  678. {
  679. MdlWarning("Error! physics for unused bone %s\n", psource->localBone[boneIndex].name );
  680. MatrixCopy( psource->boneToPose[boneIndex], boneToPose );
  681. }
  682. else
  683. {
  684. ConcatTransforms( psource->boneToPose[boneIndex], g_bonetable[remapIndex].srcRealign, boneToPose );
  685. }
  686. for (i = 0; i < psource->numvertices; i++)
  687. {
  688. VectorITransform(psource->vertex[i].position, boneToPose, boneVerts[i] );
  689. }
  690. }
  691. //-----------------------------------------------------------------------------
  692. // Purpose: Test this face to see if any of its verts are assigned to a particular bone
  693. // Input : &joints -
  694. // *pmodel -
  695. // *face -
  696. // boneIndex -
  697. // Output : Returns true if this face has a vert assigned to boneIndex
  698. //-----------------------------------------------------------------------------
  699. bool FaceHasVertOnBone( const CJointedModel &joints, s_source_t *pSource, s_face_t *face, int boneIndex )
  700. {
  701. if ( boneIndex < 0 )
  702. return true;
  703. int j;
  704. s_boneweight_t *pweight;
  705. pweight = &pSource->vertex[ face->a ].boneweight;
  706. for ( j = 0; j < pweight->numbones; j++ )
  707. {
  708. // assigned to boneIndex?
  709. if ( joints.RemapBone( pweight->bone[j] ) == boneIndex )
  710. return true;
  711. }
  712. pweight = &pSource->vertex[ face->b ].boneweight;
  713. for ( j = 0; j < pweight->numbones; j++ )
  714. {
  715. // assigned to boneIndex?
  716. if ( joints.RemapBone( pweight->bone[j] ) == boneIndex )
  717. return true;
  718. }
  719. pweight = &pSource->vertex[ face->c ].boneweight;
  720. for ( j = 0; j < pweight->numbones; j++ )
  721. {
  722. // assigned to boneIndex?
  723. if ( joints.RemapBone( pweight->bone[j] ) == boneIndex )
  724. return true;
  725. }
  726. return false;
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Purpose: Fixup the pointers in this face to reference the mesh globally (source relative)
  730. // (faces are mesh relative, each source has several meshes)
  731. // Input : *pout -
  732. // *pmesh -
  733. // *pin -
  734. //-----------------------------------------------------------------------------
  735. void GlobalFace( s_face_t *pout, s_mesh_t *pmesh, s_face_t *pin )
  736. {
  737. pout->a = pmesh->vertexoffset + pin->a;
  738. pout->b = pmesh->vertexoffset + pin->b;
  739. pout->c = pmesh->vertexoffset + pin->c;
  740. }
  741. //-----------------------------------------------------------------------------
  742. // Purpose: Copy all verts assigned to this bone.
  743. // NOTE: Leaves gaps in the model around joints
  744. // Input : **verts -
  745. // *worldVerts -
  746. // &joints -
  747. // boneIndex -
  748. // Output : int vertCount
  749. //-----------------------------------------------------------------------------
  750. int CopyVertsByBone( Vector **verts, Vector *worldVerts, const CJointedModel &joints, int boneIndex )
  751. {
  752. int vertCount = 0;
  753. s_source_t *pmodel = joints.m_pModel;
  754. // loop through each vert to find those assigned to this bone
  755. for ( int i = 0; i < pmodel->numvertices; i++ )
  756. {
  757. s_boneweight_t *pweight = &pmodel->vertex[ i ].boneweight;
  758. // look at each assignment for this vert
  759. for ( int j = 0; j < pweight->numbones; j++ )
  760. {
  761. // Discover the local bone index for this bone
  762. int localBone = pweight->bone[j];
  763. // assigned to boneIndex?
  764. if ( joints.RemapBone( localBone ) == boneIndex )
  765. {
  766. // add this vert to model
  767. verts[vertCount++] = &worldVerts[i];
  768. }
  769. }
  770. }
  771. return vertCount;
  772. }
  773. //-----------------------------------------------------------------------------
  774. // Purpose: Copy all verts that are referenced by a face which has a vert assigned
  775. // to this bone.
  776. // NOTE: convex hulls of each bone will overlap at the joints
  777. // Input : **verts -
  778. // *worldVerts -
  779. // &joints -
  780. // boneIndex -
  781. // Output : int
  782. //-----------------------------------------------------------------------------
  783. int CopyFaceVertsByBone( Vector **verts, Vector *worldVerts, const CJointedModel &joints, int boneIndex )
  784. {
  785. int vertCount = 0;
  786. s_source_t *pmodel = joints.m_pModel;
  787. int *vertChecked = new int[pmodel->numvertices];
  788. for ( int b = 0; b < pmodel->numvertices; b++ )
  789. {
  790. vertChecked[b] = 0;
  791. }
  792. for ( int i = 0; i < pmodel->nummeshes; i++ )
  793. {
  794. s_mesh_t *pmesh = pmodel->mesh + pmodel->meshindex[i];
  795. for ( int j = 0; j < pmesh->numfaces; j++ )
  796. {
  797. s_face_t *face = pmodel->face + pmesh->faceoffset + j;
  798. s_face_t globalFace;
  799. GlobalFace( &globalFace, pmesh, face );
  800. if ( FaceHasVertOnBone( joints, pmodel, &globalFace, boneIndex ) )
  801. {
  802. if ( !vertChecked[globalFace.a] )
  803. {
  804. // add this vert to model
  805. verts[vertCount++] = &worldVerts[globalFace.a];
  806. }
  807. if ( !vertChecked[globalFace.b] )
  808. {
  809. // add this vert to model
  810. verts[vertCount++] = &worldVerts[globalFace.b];
  811. }
  812. if ( !vertChecked[globalFace.c] )
  813. {
  814. // add this vert to model
  815. verts[vertCount++] = &worldVerts[globalFace.c];
  816. }
  817. // mark these verts so you only add them once
  818. vertChecked[globalFace.a] = 1;
  819. vertChecked[globalFace.b] = 1;
  820. vertChecked[globalFace.c] = 1;
  821. }
  822. }
  823. }
  824. delete[] vertChecked;
  825. return vertCount;
  826. }
  827. //-----------------------------------------------------------------------------
  828. // Purpose: Find all verts that differ only by texture coordinates - this allows
  829. // us to ignore texture coordinates on collision models
  830. // Input : *weldTable - output table
  831. // *pmodel - input model
  832. //-----------------------------------------------------------------------------
  833. void BuildVertWeldTable( int *weldTable, s_source_t *pmodel )
  834. {
  835. for ( int i = 0; i < pmodel->numvertices; i++ )
  836. {
  837. bool found = false;
  838. for ( int j = 0; j < i; j++ )
  839. {
  840. float dist = (pmodel->vertex[j].position - pmodel->vertex[i].position).Length();
  841. float normalDist = DotProduct( pmodel->vertex[j].normal, pmodel->vertex[i].normal );
  842. if ( dist <= g_WeldVertEpsilon && normalDist > g_WeldNormalEpsilon )
  843. {
  844. found = true;
  845. weldTable[i] = j;
  846. break;
  847. }
  848. }
  849. if ( !found )
  850. {
  851. weldTable[i] = i;
  852. }
  853. }
  854. }
  855. //-----------------------------------------------------------------------------
  856. // Purpose: marks all verts with a unique ID. Each set of connected verts has
  857. // the same ID. IDs are the index of the lowest numbered face on the
  858. // mesh
  859. // Input : *vertID - array that holds IDs
  860. // *pmodel - model to process
  861. //-----------------------------------------------------------------------------
  862. void MarkConnectedMeshes( int *vertID, s_source_t *pmodel, int *vertMap )
  863. {
  864. int i;
  865. // mark all verts as max faceid + 1
  866. for ( i = 0; i < pmodel->numvertices; i++ )
  867. {
  868. // If these verts have been welded to a lower-index vert, mark them
  869. // as already processed to avoid making additional convex objects out of them.
  870. if ( vertMap[i] != i )
  871. {
  872. vertID[i] = -1;
  873. }
  874. else
  875. {
  876. vertID[i] = pmodel->numfaces+1;
  877. }
  878. }
  879. int marked = 0;
  880. int faceid = 0;
  881. // iterate the face list, minimizing the vertID at each vert
  882. // until we have an iteration where no vertIDs are changed
  883. do
  884. {
  885. marked = 0;
  886. faceid = 0;
  887. for ( i = 0; i < pmodel->nummeshes; i++ )
  888. {
  889. s_mesh_t *pmesh = pmodel->mesh + pmodel->meshindex[i];
  890. for ( int j = 0; j < pmesh->numfaces; j++ )
  891. {
  892. s_face_t *face = pmodel->face + pmesh->faceoffset + j;
  893. s_face_t globalFace;
  894. GlobalFace( &globalFace, pmesh, face );
  895. // account for welding
  896. globalFace.a = vertMap[globalFace.a];
  897. globalFace.b = vertMap[globalFace.b];
  898. globalFace.c = vertMap[globalFace.c];
  899. // find min(faceid, vertID[a], vertID[b], vertID[c]);
  900. int newid = min(faceid, vertID[globalFace.a]);
  901. newid = min( newid, vertID[globalFace.b]);
  902. newid = min( newid, vertID[globalFace.c]);
  903. // mark all verts with the minimum, count the number we had to mark
  904. if ( vertID[globalFace.a] != newid )
  905. {
  906. vertID[globalFace.a] = newid;
  907. marked++;
  908. }
  909. if ( vertID[globalFace.b] != newid )
  910. {
  911. vertID[globalFace.b] = newid;
  912. marked++;
  913. }
  914. if ( vertID[globalFace.c] != newid )
  915. {
  916. vertID[globalFace.c] = newid;
  917. marked++;
  918. }
  919. faceid++;
  920. }
  921. }
  922. } while ( marked != 0 );
  923. }
  924. //-----------------------------------------------------------------------------
  925. // Purpose: Finds a CPhysCollisionModel in a linked list of models.
  926. // Input : *pHead -
  927. // *pName -
  928. // Output : CPhysCollisionModel
  929. //-----------------------------------------------------------------------------
  930. CPhysCollisionModel *FindObjectInList( CPhysCollisionModel *pHead, const char *pName )
  931. {
  932. while ( pHead )
  933. {
  934. if ( !stricmp( pName, pHead->m_name ) )
  935. break;
  936. pHead = pHead->m_pNext;
  937. }
  938. return pHead;
  939. }
  940. //-----------------------------------------------------------------------------
  941. // Purpose: Fix all bones to reference the remapped/collapsed bone structure
  942. // Input : *pSource -
  943. // *pList -
  944. //-----------------------------------------------------------------------------
  945. void FixBoneList( int *boneMap, const s_source_t *pSource, CPhysCollisionModel *pList )
  946. {
  947. if ( !g_bJointed )
  948. return;
  949. CPhysCollisionModel *pmodel = pList;
  950. while ( pmodel )
  951. {
  952. int nodeIndex = FindLocalBoneNamed( pSource, pmodel->m_name );
  953. if ( nodeIndex < 0 )
  954. {
  955. MdlWarning("Physics for unknown bone %s\n", pmodel->m_name );
  956. }
  957. else
  958. {
  959. int count = 0;
  960. // remove simplified bones
  961. while ( pSource->boneLocalToGlobal[nodeIndex] < 0 )
  962. {
  963. if ( count++ > MAXSTUDIOSRCBONES )
  964. break;
  965. // simplified out, move up to the parent
  966. nodeIndex = pSource->localBone[nodeIndex].parent;
  967. }
  968. if ( nodeIndex >= 0 )
  969. {
  970. // bone collapse may have changed parent hierarchy, and the root name.
  971. // The vertices are converted to the new reference by ConvertToWorldSpace(), as well as RemapVerticesToGlobalBones()
  972. pmodel->m_name = g_bonetable[ pSource->boneLocalToGlobal[nodeIndex] ].name;
  973. pmodel->m_parent = NULL;
  974. int parentIndex = pSource->localBone[nodeIndex].parent;
  975. if ( parentIndex >= 0 && parentIndex != nodeIndex )
  976. {
  977. parentIndex = boneMap[parentIndex];
  978. if (pSource->boneLocalToGlobal[parentIndex] < 0)
  979. {
  980. pmodel->m_parent = pSource->localBone[parentIndex].name;
  981. }
  982. else
  983. {
  984. pmodel->m_parent = g_bonetable[ pSource->boneLocalToGlobal[parentIndex] ].name;
  985. }
  986. }
  987. }
  988. else
  989. {
  990. MdlWarning("Physics for unknown bone %s\n", pmodel->m_name );
  991. }
  992. }
  993. pmodel = pmodel->m_pNext;
  994. }
  995. }
  996. //-----------------------------------------------------------------------------
  997. // Purpose: Fixup all references to parents by walking up on models whose parents
  998. // have no collision geometry. Bones without geometry cannot be physically
  999. // simulated, so they must be removed.
  1000. // NOTE: This is broken. It won't work for tree structures with an empty parent
  1001. // (i.e. 2 children attached to a parent bone that has no physics geometry - thus empty)
  1002. // It will not convert that parent into a constraint between 2 children
  1003. // Input : *pList -
  1004. // *pSource -
  1005. // *pParentName -
  1006. // Output : const char
  1007. //-----------------------------------------------------------------------------
  1008. const char *FixParent( CPhysCollisionModel *pList, s_source_t *pSource, const char *pParentName )
  1009. {
  1010. while ( pParentName )
  1011. {
  1012. if ( FindObjectInList( pList, pParentName ) )
  1013. {
  1014. return pParentName;
  1015. }
  1016. int nodeIndex = FindLocalBoneNamed( pSource, pParentName );
  1017. if ( nodeIndex < 0 )
  1018. return NULL;
  1019. int parentIndex = pSource->localBone[nodeIndex].parent;
  1020. if ( parentIndex < 0 )
  1021. {
  1022. break;
  1023. }
  1024. pParentName = pSource->localBone[parentIndex].name;
  1025. }
  1026. return NULL;
  1027. }
  1028. struct boundingvolume_t
  1029. {
  1030. Vector mins;
  1031. Vector maxs;
  1032. };
  1033. void CreateCollide( CPhysCollisionModel *pBase, CPhysConvex **pElements, int elementCount, const boundingvolume_t &bv )
  1034. {
  1035. int i;
  1036. if ( !pBase )
  1037. return;
  1038. // NOTE: Must do this before building collide
  1039. pBase->m_volume = 0;
  1040. pBase->m_surfaceArea = 0;
  1041. for ( i = 0; i < elementCount; i++ )
  1042. {
  1043. pBase->m_volume += physcollision->ConvexVolume( pElements[i] );
  1044. pBase->m_surfaceArea += physcollision->ConvexSurfaceArea( pElements[i] );
  1045. }
  1046. convertconvexparams_t params;
  1047. params.Defaults();
  1048. params.buildOuterConvexHull = true;
  1049. params.buildDragAxisAreas = true;
  1050. Vector size = bv.maxs - bv.mins;
  1051. int largest = 0;
  1052. float minSurfaceArea = -1.0f;
  1053. for ( i = 0; i < 3; i++ )
  1054. {
  1055. if ( size[i] > size[largest] )
  1056. {
  1057. largest = i;
  1058. }
  1059. int other = (i+1)%3;
  1060. int cross = (i+2)%3;
  1061. float surfaceArea = size[other] * size[cross];
  1062. if ( minSurfaceArea < 0 || surfaceArea < minSurfaceArea )
  1063. {
  1064. minSurfaceArea = surfaceArea;
  1065. }
  1066. }
  1067. // this can be really slow with super-large models and a low error tolerance
  1068. // Basically you get a ray cast through each square of epsilon surface area on each OBB side
  1069. // So compute it for 0.01% error (on the smallest side, less on larger sides)
  1070. params.dragAreaEpsilon = clamp( minSurfaceArea * 1e-4f, 0.25f, 128.0f );
  1071. Vector tmp = size;
  1072. tmp[largest] = 0;
  1073. float len = tmp.Length();
  1074. if ( len > 0 )
  1075. {
  1076. float sizeRatio = size[largest] / len;
  1077. // HACKHACK: Hardcoded size ratio to induce damping
  1078. // This prevents long skinny objects from rolling endlessly
  1079. if ( sizeRatio > 9 )
  1080. {
  1081. pBase->m_rotdamping = 1.0f;
  1082. }
  1083. }
  1084. // THIS DESTROYS pConvex!!
  1085. pBase->m_pCollisionData = physcollision->ConvertConvexToCollideParams( pElements, elementCount, params );
  1086. // debug output for the drag area calculations
  1087. #if 0
  1088. Msg("Drag epsilon is %.3f\n", params.dragAreaEpsilon );
  1089. Vector areas = physcollision->CollideGetOrthographicAreas( pBase->m_pCollisionData );
  1090. Msg("Drag fractions are %.3f %.3f %.3f\n", areas.x, areas.y, areas.z );
  1091. #endif
  1092. }
  1093. // is this list of verts contained in a slab of epsilon width? If so, it's probably
  1094. // an error of some kind - we shouldn't be authoring flat or 2d collision models
  1095. bool IsApproximatelyPlanar( Vector **verts, int vertCount, float epsilon )
  1096. {
  1097. if ( vertCount < 4 )
  1098. return true;
  1099. // If we're using an un-welded model, then this may generate a degenerate normal
  1100. // loop to search for an actual plane
  1101. int v0 = 1, v1 = 2;
  1102. Vector normal;
  1103. while ( v0 < vertCount && v1 < vertCount )
  1104. {
  1105. Vector edge0 = *verts[v0] - *verts[0];
  1106. Vector edge1 = *verts[v1] - *verts[0];
  1107. normal = CrossProduct( edge0, edge1 );
  1108. float len = VectorNormalize( normal );
  1109. if ( len > 0.001 )
  1110. break;
  1111. if ( edge0.Length() < 0.001 )
  1112. {
  1113. // verts[0] and v0 are coincident, try new verts
  1114. v0++;
  1115. v1++;
  1116. }
  1117. else
  1118. {
  1119. // v0 seems fine, try a new v1 -- it's probably coincident with v0
  1120. v1++;
  1121. }
  1122. }
  1123. // form the plane and project all of the verts into it
  1124. float minDist = DotProduct( normal, *verts[0] );
  1125. float maxDist = minDist;
  1126. for ( int i = 0; i < vertCount; i++ )
  1127. {
  1128. float d = DotProduct( *verts[i], normal );
  1129. if ( d < minDist )
  1130. {
  1131. minDist = d;
  1132. }
  1133. else if ( d > maxDist )
  1134. {
  1135. maxDist = d;
  1136. }
  1137. // at least one vert out of the plane, we've got something 3 dimensional
  1138. if ( fabsf(maxDist-minDist) > epsilon )
  1139. return false;
  1140. }
  1141. return true;
  1142. }
  1143. void BuildConvexListByVertID( s_source_t *pmodel, CUtlVector<convexlist_t> &convexList, CUtlVector<int> &vertList, CUtlVector<int> &vertID )
  1144. {
  1145. // loop through each island of verts and append it to the convex list
  1146. convexlist_t current;
  1147. for ( int i = 0; i < pmodel->numvertices; i++ )
  1148. {
  1149. // already processed this group
  1150. if ( vertID[i] < 0 || vertID[i] > pmodel->numfaces )
  1151. continue;
  1152. current.firstVertIndex = vertList.Count();
  1153. current.numVertIndex = 0;
  1154. int id = vertID[i];
  1155. for ( int j = i; j < pmodel->numvertices; j++ )
  1156. {
  1157. if ( vertID[j] == id )
  1158. {
  1159. vertList.AddToTail(j);
  1160. current.numVertIndex++;
  1161. // don't reuse this vert
  1162. vertID[j] = -1;
  1163. }
  1164. }
  1165. convexList.AddToTail(current);
  1166. }
  1167. }
  1168. // build a list of vertex indices for each connected sub-piece
  1169. void BuildSingleConvexForFaceList( s_source_t *pmodel, CUtlVector<convexlist_t> &convexList, CUtlVector<int> &vertList, const CUtlVector<s_face_t> &faceList )
  1170. {
  1171. CUtlVector<int> vertID;
  1172. vertID.SetCount(pmodel->numvertices);
  1173. int i;
  1174. for ( i = 0; i < pmodel->numvertices; i++ )
  1175. {
  1176. vertID[i] = -1;
  1177. }
  1178. for ( i = 0; i < faceList.Count(); i++ )
  1179. {
  1180. const s_face_t &globalFace = faceList[i];
  1181. vertID[globalFace.a] = 1;
  1182. vertID[globalFace.b] = 1;
  1183. vertID[globalFace.c] = 1;
  1184. }
  1185. BuildConvexListByVertID( pmodel, convexList, vertList, vertID );
  1186. }
  1187. void BuildConvexListForFaceList( s_source_t *pmodel, CUtlVector<convexlist_t> &convexList, CUtlVector<int> &vertList, const CUtlVector<s_face_t> &faceList )
  1188. {
  1189. CUtlVector<int> weldTable;
  1190. weldTable.SetCount(pmodel->numvertices);
  1191. BuildVertWeldTable( weldTable.Base(), pmodel );
  1192. int i;
  1193. CUtlVector<int> vertID;
  1194. vertID.SetCount(pmodel->numvertices);
  1195. // mark all verts as max faceid + 1
  1196. for ( i = 0; i < pmodel->numvertices; i++ )
  1197. {
  1198. // If these verts have been welded to a lower-index vert, mark them
  1199. // as already processed to avoid making additional convex objects out of them.
  1200. if ( weldTable[i] != i )
  1201. {
  1202. vertID[i] = -1;
  1203. }
  1204. else
  1205. {
  1206. vertID[i] = pmodel->numfaces+1;
  1207. }
  1208. }
  1209. Assert(convexList.Count()==0);
  1210. Assert(vertList.Count()==0);
  1211. int marked = 0;
  1212. int faceid = 0;
  1213. // iterate the face list, minimizing the vertID at each vert
  1214. // until we have an iteration where no vertIDs are changed
  1215. do
  1216. {
  1217. marked = 0;
  1218. faceid = 0;
  1219. // basically this flood fills ids out to the verts until each island of connected
  1220. // verts shares a single id (so new verts got marked)
  1221. for ( i = 0; i < faceList.Count(); i++ )
  1222. {
  1223. s_face_t globalFace = faceList[i];
  1224. // account for welding
  1225. globalFace.a = weldTable[globalFace.a];
  1226. globalFace.b = weldTable[globalFace.b];
  1227. globalFace.c = weldTable[globalFace.c];
  1228. int newid = min(i, vertID[globalFace.a]);
  1229. newid = min( newid, vertID[globalFace.b]);
  1230. newid = min( newid, vertID[globalFace.c]);
  1231. // mark all verts with the minimum, count the number we had to mark
  1232. if ( vertID[globalFace.a] != newid )
  1233. {
  1234. vertID[globalFace.a] = newid;
  1235. marked++;
  1236. }
  1237. if ( vertID[globalFace.b] != newid )
  1238. {
  1239. vertID[globalFace.b] = newid;
  1240. marked++;
  1241. }
  1242. if ( vertID[globalFace.c] != newid )
  1243. {
  1244. vertID[globalFace.c] = newid;
  1245. marked++;
  1246. }
  1247. }
  1248. } while ( marked != 0 );
  1249. BuildConvexListByVertID( pmodel, convexList, vertList, vertID );
  1250. }
  1251. // take a list of convex elements (lists of vert indices into master vert list) and build CPhysConvex out of them
  1252. // return true if there are no errors detected
  1253. bool BuildConvexesForLists( CUtlVector<CPhysConvex *> &convexOut, const CUtlVector<convexlist_t> &convexList, const CUtlVector<int> &vertList, const CUtlVector<Vector> &worldspaceVerts, bool bRemove2d )
  1254. {
  1255. bool bValid = true;
  1256. CUtlVector<Vector *> vertsThisConvex;
  1257. for ( int i = 0; i < convexList.Count(); i++ )
  1258. {
  1259. const convexlist_t &elem = convexList[i];
  1260. vertsThisConvex.RemoveAll();
  1261. for ( int j = 0; j < elem.numVertIndex; j++ )
  1262. {
  1263. // this is ok because physcollision won't modify these, but wants non-const
  1264. Vector *pVert = const_cast<Vector *>(&worldspaceVerts[vertList[j + elem.firstVertIndex]]);
  1265. vertsThisConvex.AddToTail( pVert );
  1266. }
  1267. // need at least 3 verts to build a CPhysConvex
  1268. if ( vertsThisConvex.Count() > 2 )
  1269. {
  1270. const float g_epsilon_2d = 0.5f;
  1271. // HACKHACK: A heuristic to detect models without smoothing groups set
  1272. // UNDONE: Do a BSP to decompose arbitrary models to convex?
  1273. if ( IsApproximatelyPlanar( vertsThisConvex.Base(), vertsThisConvex.Count(), g_epsilon_2d ) )
  1274. {
  1275. if ( bRemove2d )
  1276. continue;
  1277. MdlWarning("Model has 2-dimensional geometry (less than %.3f inches thick on any axis)!!!\n", g_epsilon_2d );
  1278. bValid = false;
  1279. }
  1280. // go ahead and build it out
  1281. CPhysConvex *pConvex = physcollision->ConvexFromVerts( vertsThisConvex.Base(), vertsThisConvex.Count() );
  1282. if ( pConvex )
  1283. {
  1284. // Got something valid, attach this convex data to the root model
  1285. physcollision->SetConvexGameData( pConvex, 0 );
  1286. convexOut.AddToTail(pConvex);
  1287. }
  1288. }
  1289. }
  1290. return bValid;
  1291. }
  1292. //-----------------------------------------------------------------------------
  1293. // Purpose: Build a jointed collision model with constraints
  1294. // Input : &joints -
  1295. // Output : int
  1296. //-----------------------------------------------------------------------------
  1297. int ProcessJointedModel( CJointedModel &joints )
  1298. {
  1299. if( !g_quiet )
  1300. {
  1301. printf("Processing jointed collision model\n" );
  1302. }
  1303. s_source_t *pmodel = joints.m_pModel;
  1304. // loop through each bone and form a collision model
  1305. for ( int boneIndex = 0; boneIndex < joints.m_pModel->numbones; boneIndex++ )
  1306. {
  1307. if ( !joints.ShouldProcessBone( boneIndex ) )
  1308. continue;
  1309. CUtlVector<Vector> bonespaceVerts;
  1310. bonespaceVerts.SetCount(pmodel->numvertices);
  1311. ConvertToBoneSpace( joints.m_pModel, boneIndex, bonespaceVerts );
  1312. CUtlVector<s_face_t> faceList;
  1313. CUtlVector<convexlist_t> convexList;
  1314. CUtlVector<int> vertList;
  1315. CUtlVector<CPhysConvex *> convexOut;
  1316. bool bValid = false;
  1317. for ( int i = 0; i < pmodel->nummeshes; i++ )
  1318. {
  1319. s_mesh_t *pmesh = pmodel->mesh + pmodel->meshindex[i];
  1320. for ( int j = 0; j < pmesh->numfaces; j++ )
  1321. {
  1322. s_face_t *face = pmodel->face + pmesh->faceoffset + j;
  1323. s_face_t globalFace;
  1324. GlobalFace( &globalFace, pmesh, face );
  1325. if ( FaceHasVertOnBone( joints, pmodel, &globalFace, boneIndex ) )
  1326. {
  1327. faceList.AddToTail( globalFace );
  1328. }
  1329. }
  1330. if ( joints.m_allowConcaveJoints )
  1331. {
  1332. BuildConvexListForFaceList( pmodel, convexList, vertList, faceList );
  1333. }
  1334. else
  1335. {
  1336. BuildSingleConvexForFaceList( pmodel, convexList, vertList, faceList );
  1337. }
  1338. bValid = BuildConvexesForLists( convexOut, convexList, vertList, bonespaceVerts, joints.m_remove2d );
  1339. }
  1340. if ( convexOut.Count() > joints.m_maxConvex )
  1341. {
  1342. MdlWarning("COSTLY COLLISION MODEL!!!! (%d parts - %d allowed)\n", convexOut.Count(), joints.m_maxConvex );
  1343. bValid = false;
  1344. }
  1345. if ( !bValid && convexOut.Count() )
  1346. {
  1347. MdlWarning("Error with convex elements of %s, building single convex!!!!\n", pmodel->filename );
  1348. for ( int i = 0; i < convexOut.Count(); i++ )
  1349. {
  1350. physcollision->ConvexFree( convexOut[i] );
  1351. }
  1352. convexOut.Purge();
  1353. }
  1354. if ( convexOut.Count() )
  1355. {
  1356. int i;
  1357. CPhysCollisionModel *pPhys = InitCollisionModel( joints, pmodel->localBone[boneIndex].name );
  1358. pPhys->m_mass = 1.0;
  1359. pPhys->m_name = joints.m_pModel->localBone[boneIndex].name;
  1360. if ( joints.m_pModel->localBone[boneIndex].parent >= 0 )
  1361. {
  1362. pPhys->m_parent = joints.m_pModel->localBone[joints.m_pModel->localBone[boneIndex].parent].name;
  1363. }
  1364. else
  1365. {
  1366. pPhys->m_parent = NULL;
  1367. }
  1368. boundingvolume_t bv;
  1369. ClearBounds( bv.mins, bv.maxs );
  1370. int vertCount = 0;
  1371. for ( i = 0; i < convexList.Count(); i++ )
  1372. {
  1373. const convexlist_t &elem = convexList[i];
  1374. for ( int j = 0; j < elem.numVertIndex; j++ )
  1375. {
  1376. AddPointToBounds( bonespaceVerts[vertList[elem.firstVertIndex+j]], bv.mins, bv.maxs );
  1377. vertCount++;
  1378. }
  1379. }
  1380. for ( i = 0; i < convexOut.Count(); i++ )
  1381. {
  1382. // Attach this convex data to this particular bone
  1383. int globalBoneIndex = joints.m_pModel->boneLocalToGlobal[boneIndex];
  1384. physcollision->SetConvexGameData( convexOut[i], globalBoneIndex + 1 );
  1385. }
  1386. CreateCollide( pPhys, convexOut.Base(), convexOut.Count(), bv );
  1387. if( !g_quiet )
  1388. {
  1389. printf("%-24s (%3d verts, %d convex elements) volume: %4.2f\n", pPhys->m_name, vertCount, convexOut.Count(), pPhys->m_volume );
  1390. }
  1391. joints.UnlinkCollisionModel( pPhys );
  1392. joints.AppendCollisionModel( pPhys );
  1393. }
  1394. }
  1395. // remove any non-physical joints at this point
  1396. CPhysCollisionModel *pPhys = joints.m_pCollisionList;
  1397. while (pPhys)
  1398. {
  1399. CPhysCollisionModel *pNext = pPhys->m_pNext;
  1400. if ( !pPhys->m_pCollisionData )
  1401. {
  1402. joints.UnlinkCollisionModel(pPhys);
  1403. delete pPhys;
  1404. }
  1405. pPhys = pNext;
  1406. }
  1407. return 1;
  1408. }
  1409. #if 0
  1410. // debug visualization code - use this to dump out intermediate geometry files for visualization in glview.exe
  1411. void DumpToGLView( char const *pName, s_source_t *pmodel, Vector *worldVerts, int *used )
  1412. {
  1413. int i;
  1414. for ( i = 0; i < pmodel->numvertices; i++ )
  1415. used[i] = -1;
  1416. FILE *fp = fopen( pName, "w" );
  1417. // dump the model to a glview file
  1418. for ( i = 0; i < pmodel->nummeshes; i++ )
  1419. {
  1420. s_mesh_t *pmesh = pmodel->mesh + pmodel->meshindex[i];
  1421. for ( int j = 0; j < pmesh->numfaces; j++ )
  1422. {
  1423. s_face_t *face = pmodel->face + pmesh->faceoffset + j;
  1424. s_face_t globalFace;
  1425. GlobalFace( &globalFace, pmesh, face );
  1426. fprintf( fp, "3\n" );
  1427. fprintf( fp, "%6.3f %6.3f %6.3f 0 1 0\n", worldVerts[globalFace.b].x, worldVerts[globalFace.b].y, worldVerts[globalFace.b].z );
  1428. fprintf( fp, "%6.3f %6.3f %6.3f 1 0 0\n", worldVerts[globalFace.a].x, worldVerts[globalFace.a].y, worldVerts[globalFace.a].z );
  1429. fprintf( fp, "%6.3f %6.3f %6.3f 0 0 1\n", worldVerts[globalFace.c].x, worldVerts[globalFace.c].y, worldVerts[globalFace.c].z );
  1430. used[globalFace.a] = 0;
  1431. used[globalFace.b] = 0;
  1432. used[globalFace.c] = 0;
  1433. }
  1434. }
  1435. // dump a triangle expanded around each vert to the file (to show degenerate tris' verts).
  1436. for ( i = 0; i < pmodel->numvertices; i++ )
  1437. {
  1438. if ( used[i] < 0 )
  1439. continue;
  1440. fprintf( fp, "3\n" );
  1441. Vector vert;
  1442. vert = worldVerts[i] + Vector(0,0,5);
  1443. fprintf( fp, "%6.3f %6.3f %6.3f 1 0 0\n", vert.x, vert.y, vert.z );
  1444. vert = worldVerts[i] + Vector(5,0,-5);
  1445. fprintf( fp, "%6.3f %6.3f %6.3f 0 1 0\n", vert.x, vert.y, vert.z );
  1446. vert = worldVerts[i] + Vector(-5,0,-5);
  1447. fprintf( fp, "%6.3f %6.3f %6.3f 0 0 1\n", vert.x, vert.y, vert.z );
  1448. }
  1449. fclose( fp );
  1450. }
  1451. #endif
  1452. int ProcessSingleBody( CJointedModel &joints )
  1453. {
  1454. s_source_t *pmodel = joints.m_pModel;
  1455. // THIS CODE IS ONLY EXECUTED ON PROPS - i.e. NON-JOINTED MODELS
  1456. CUtlVector<Vector> worldspaceVerts;
  1457. worldspaceVerts.SetCount(pmodel->numvertices);
  1458. ConvertToWorldSpace( joints, pmodel, worldspaceVerts );
  1459. CUtlVector<s_face_t> faceList;
  1460. CUtlVector<convexlist_t> convexList;
  1461. CUtlVector<int> vertList;
  1462. CUtlVector<CPhysConvex *> convexOut;
  1463. bool bValid = false;
  1464. if ( joints.m_allowConcave )
  1465. {
  1466. for ( int i = 0; i < pmodel->nummeshes; i++ )
  1467. {
  1468. s_mesh_t *pmesh = pmodel->mesh + pmodel->meshindex[i];
  1469. for ( int j = 0; j < pmesh->numfaces; j++ )
  1470. {
  1471. s_face_t *face = pmodel->face + pmesh->faceoffset + j;
  1472. s_face_t globalFace;
  1473. GlobalFace( &globalFace, pmesh, face );
  1474. faceList.AddToTail( globalFace );
  1475. }
  1476. }
  1477. BuildConvexListForFaceList( pmodel, convexList, vertList, faceList );
  1478. bValid = BuildConvexesForLists( convexOut, convexList, vertList, worldspaceVerts, joints.m_remove2d );
  1479. }
  1480. if ( convexOut.Count() > joints.m_maxConvex )
  1481. {
  1482. MdlWarning("COSTLY COLLISION MODEL!!!! (%d parts - %d allowed)\n", convexOut.Count(), joints.m_maxConvex );
  1483. bValid = false;
  1484. }
  1485. if ( !bValid && convexOut.Count() )
  1486. {
  1487. MdlWarning("Error with convex elements of %s, building single convex!!!!\n", pmodel->filename );
  1488. for ( int i = 0; i < convexOut.Count(); i++ )
  1489. {
  1490. physcollision->ConvexFree( convexOut[i] );
  1491. }
  1492. convexOut.Purge();
  1493. }
  1494. // either we don't want concave, or there was an error building it
  1495. if ( !convexOut.Count() )
  1496. {
  1497. convexlist_t elem;
  1498. elem.firstVertIndex = 0;
  1499. elem.numVertIndex = pmodel->numvertices;
  1500. convexList.AddToTail(elem);
  1501. for ( int i = 0; i < pmodel->numvertices; i++ )
  1502. {
  1503. vertList.AddToTail(i);
  1504. }
  1505. BuildConvexesForLists( convexOut, convexList, vertList, worldspaceVerts, true );
  1506. }
  1507. if ( convexOut.Count() )
  1508. {
  1509. if( !g_quiet )
  1510. {
  1511. printf("Model has %d convex sub-parts\n", convexOut.Count() );
  1512. }
  1513. CPhysCollisionModel *pPhys = new CPhysCollisionModel;
  1514. joints.SetCollisionModelDefaults( pPhys );
  1515. boundingvolume_t bv;
  1516. ClearBounds( bv.mins, bv.maxs );
  1517. for ( int i = worldspaceVerts.Count()-1; --i >= 0; )
  1518. {
  1519. AddPointToBounds( worldspaceVerts[i], bv.mins, bv.maxs );
  1520. }
  1521. CreateCollide( pPhys, convexOut.Base(), convexOut.Count(), bv );
  1522. // Init mass, write routine will distribute the total mass
  1523. pPhys->m_mass = 1.0;
  1524. char tmp[512];
  1525. Q_FileBase( pmodel->filename, tmp, sizeof( tmp ) );
  1526. // UNDONE: Memory leak
  1527. char *out = new char[strlen(tmp)+1];
  1528. strcpy( out, tmp );
  1529. pPhys->m_name = out;
  1530. pPhys->m_parent = NULL;
  1531. joints.AppendCollisionModel( pPhys );
  1532. }
  1533. return 1;
  1534. }
  1535. #define MAX_ARGS 16
  1536. #define ARG_SIZE 256
  1537. //-----------------------------------------------------------------------------
  1538. // Purpose: HACKETY HACK - get the args into a buffer.
  1539. // This checks for overflow, but it's not very robust - shouldn't be necessary though
  1540. // Input : pArgs[][ARG_SIZE] -
  1541. // maxCount - array size of pargs
  1542. // Output : int - count actually used
  1543. //-----------------------------------------------------------------------------
  1544. int ReadArgs( char pArgs[][ARG_SIZE], int maxCount )
  1545. {
  1546. int argCount = 0;
  1547. while ( argCount < maxCount && TokenAvailable() )
  1548. {
  1549. GetToken(false);
  1550. strncpy( pArgs[argCount], token, ARG_SIZE );
  1551. argCount++;
  1552. }
  1553. return argCount;
  1554. }
  1555. //-----------------------------------------------------------------------------
  1556. // Purpose: Simple atof wrapper to keep from crashing on bad user input
  1557. // Input : *pString -
  1558. // Output : float
  1559. //-----------------------------------------------------------------------------
  1560. float Safe_atof( const char *pString )
  1561. {
  1562. if ( !pString )
  1563. return 0;
  1564. return atof(pString);
  1565. }
  1566. //-----------------------------------------------------------------------------
  1567. // Purpose: Simple atoi wrapper to avoid crashing on bad user input
  1568. // Input : *pString -
  1569. // Output : int
  1570. //-----------------------------------------------------------------------------
  1571. int Safe_atoi( const char *pString )
  1572. {
  1573. if ( !pString )
  1574. return 0;
  1575. return atoi(pString);
  1576. }
  1577. //-----------------------------------------------------------------------------
  1578. // Purpose: Add a constraint to our joint system
  1579. // Input : &joints -
  1580. // *pJointName -
  1581. // *pJointAxis -
  1582. // *pJointType -
  1583. // *pLimitMin -
  1584. // *pLimitMax -
  1585. //-----------------------------------------------------------------------------
  1586. void CCmd_JointConstrain( CJointedModel &joints, const char *pJointName, const char *pJointAxis, const char *pJointType, const char *pLimitMin, const char *pLimitMax, const char *pFriction )
  1587. {
  1588. float limitMin = Safe_atof(pLimitMin);
  1589. float limitMax = Safe_atof(pLimitMax);
  1590. float friction = Safe_atof(pFriction);
  1591. int axis = -1;
  1592. int jointIndex = FindLocalBoneNamed( joints.m_pModel, pJointName );
  1593. if ( !g_bCreateMakefile && jointIndex < 0 )
  1594. {
  1595. MdlWarning("Can't find joint %s\n", pJointName );
  1596. return;
  1597. }
  1598. pJointName = joints.m_pModel->localBone[jointIndex].name;
  1599. if ( pJointAxis )
  1600. {
  1601. axis = tolower(pJointAxis[0]) - 'x';
  1602. }
  1603. if ( axis < 0 || axis > 2 || limitMin > limitMax )
  1604. {
  1605. MdlError("Invalid joint constraint for %s\nCan't build ragdoll!\n", pJointName );
  1606. return;
  1607. }
  1608. jointlimit_t jointType = JOINT_FREE;
  1609. if ( !stricmp( pJointType, "free" ) )
  1610. {
  1611. jointType = JOINT_FREE;
  1612. }
  1613. else if ( !stricmp( pJointType, "fixed" ) )
  1614. {
  1615. jointType = JOINT_FIXED;
  1616. }
  1617. else if ( !stricmp( pJointType, "limit" ) )
  1618. {
  1619. jointType = JOINT_LIMIT;
  1620. }
  1621. else
  1622. {
  1623. MdlWarning("Unknown joint type %s (must be free, fixed, or limit)\n", pJointType );
  1624. return;
  1625. }
  1626. joints.AddConstraint( pJointName, axis, jointType, limitMin, limitMax, friction );
  1627. }
  1628. //-----------------------------------------------------------------------------
  1629. // Purpose: Remove a joint from the system (don't create physical geometry for it)
  1630. // Input : &joints -
  1631. // args[][ARG_SIZE] -
  1632. // argCount -
  1633. //-----------------------------------------------------------------------------
  1634. // UNDONE: Automatically skip joints that will have mass that is too low?
  1635. void CCmd_JointSkip( CJointedModel &joints, const char *pName )
  1636. {
  1637. int boneIndex = FindLocalBoneNamed( joints.m_pModel, pName );
  1638. if ( boneIndex < 0 )
  1639. {
  1640. MdlWarning("Can't skip joint %s, not found\n", pName );
  1641. }
  1642. else
  1643. {
  1644. // printf("skipping joint %s\n", pName );
  1645. joints.SkipBone( boneIndex );
  1646. }
  1647. }
  1648. //-----------------------------------------------------------------------------
  1649. // Purpose: Sets the object's mass. The code will distribute this mass to each
  1650. // part based on the collision model's volume
  1651. // Input : &joints -
  1652. // *pMass -
  1653. //-----------------------------------------------------------------------------
  1654. void CCmd_TotalMass( CJointedModel &joints, const char *pMass )
  1655. {
  1656. joints.SetTotalMass( Safe_atof(pMass) );
  1657. }
  1658. //-----------------------------------------------------------------------------
  1659. // Purpose: verts from the bone named pChild are added to the collision model of pParent
  1660. // Input : *pmodel - source model
  1661. // *pParent - destination bone name
  1662. // *pChild - source bone name
  1663. //-----------------------------------------------------------------------------
  1664. void CCmd_JointMerge( CJointedModel &joints, const char *pParent, const char *pChild )
  1665. {
  1666. joints.AddMergeCommand( pParent, pChild );
  1667. joints.MergeBones( FindLocalBoneNamed( joints.m_pModel, pParent ), FindLocalBoneNamed( joints.m_pModel, pChild ) );
  1668. }
  1669. void CCmd_JointRoot( CJointedModel &joints, const char *pBone )
  1670. {
  1671. // save the root bone name
  1672. strcpy( joints.m_rootName, pBone );
  1673. }
  1674. void CCmd_JoinAnimatedFriction( CJointedModel &joints, const char *pMinFriction, const char *pMaxFriction, const char *pTimeIn, const char *pTimeHold, const char *pTimeOut )
  1675. {
  1676. joints.m_flFrictionTimeIn = Safe_atof( pTimeIn );
  1677. joints.m_flFrictionTimeOut = Safe_atof( pTimeOut );
  1678. joints.m_flFrictionTimeHold = Safe_atof( pTimeHold );
  1679. joints.m_iMinAnimatedFriction = Safe_atoi( pMinFriction );
  1680. joints.m_iMaxAnimatedFriction = Safe_atoi( pMaxFriction );
  1681. joints.m_bHasAnimatedFriction = true;
  1682. }
  1683. //-----------------------------------------------------------------------------
  1684. // Purpose: Parses all legal commands inside the $collisionjoints {} block
  1685. // Input : &joints -
  1686. //-----------------------------------------------------------------------------
  1687. void ParseCollisionCommands( CJointedModel &joints )
  1688. {
  1689. char command[512];
  1690. char args[MAX_ARGS][ARG_SIZE];
  1691. int argCount;
  1692. while( GetToken( true ) )
  1693. {
  1694. if ( !strcmp( token, "}" ) )
  1695. return;
  1696. strcpy( command, token );
  1697. if ( !stricmp( command, "$mass" ) )
  1698. {
  1699. argCount = ReadArgs( args, 1 );
  1700. CCmd_TotalMass( joints, args[0] );
  1701. }
  1702. // default properties
  1703. else if ( !stricmp( command, "$automass" ) )
  1704. {
  1705. joints.SetAutoMass();
  1706. }
  1707. else if ( !stricmp( command, "$inertia" ) )
  1708. {
  1709. argCount = ReadArgs( args, 1 );
  1710. joints.DefaultInertia( Safe_atof( args[0] ) );
  1711. }
  1712. else if ( !stricmp( command, "$damping" ) )
  1713. {
  1714. argCount = ReadArgs( args, 1 );
  1715. joints.DefaultDamping( Safe_atof( args[0] ) );
  1716. }
  1717. else if ( !stricmp( command, "$rotdamping" ) )
  1718. {
  1719. argCount = ReadArgs( args, 1 );
  1720. joints.DefaultRotdamping( Safe_atof( args[0] ) );
  1721. }
  1722. else if ( !stricmp( command, "$drag" ) )
  1723. {
  1724. argCount = ReadArgs( args, 1 );
  1725. joints.DefaultDrag( Safe_atof( args[0] ) );
  1726. }
  1727. else if ( !stricmp( command, "$rollingDrag" ) )
  1728. {
  1729. argCount = ReadArgs( args, 1 );
  1730. // JAY: Removed this in favor of heuristic/tuning approach
  1731. //joints.DefaultRollingDrag( Safe_atof( args[0] ) );
  1732. }
  1733. else if ( !stricmp( command, "$maxconvexpieces") )
  1734. {
  1735. argCount = ReadArgs( args, 1 );
  1736. joints.SetMaxConvex( Safe_atoi(args[0]) );
  1737. }
  1738. else if ( !stricmp( command, "$remove2d") )
  1739. {
  1740. joints.Remove2DConvex();
  1741. }
  1742. else if ( !stricmp( command, "$concaveperjoint") )
  1743. {
  1744. joints.AllowConcaveJoints();
  1745. }
  1746. else if ( !stricmp( command, "$weldposition") )
  1747. {
  1748. argCount = ReadArgs(args,1);
  1749. g_WeldVertEpsilon = Safe_atof( args[0] );
  1750. }
  1751. else if ( !stricmp( command, "$weldnormal") )
  1752. {
  1753. argCount = ReadArgs(args,1);
  1754. g_WeldNormalEpsilon = Safe_atof( args[0] );
  1755. }
  1756. else if ( !stricmp( command, "$concave" ) )
  1757. {
  1758. joints.AllowConcave();
  1759. }
  1760. else if ( !stricmp( command, "$masscenter" ) )
  1761. {
  1762. argCount = ReadArgs( args, 3 );
  1763. Vector center;
  1764. center.Init( Safe_atof(args[0]), Safe_atof(args[1]), Safe_atof(args[2]) );
  1765. joints.ForceMassCenter( center );
  1766. }
  1767. // joint commands
  1768. else if ( !stricmp( command, "$jointskip" ) )
  1769. {
  1770. argCount = ReadArgs( args, 1 );
  1771. CCmd_JointSkip( joints, args[0] );
  1772. }
  1773. else if ( !stricmp( command, "$jointmerge" ) )
  1774. {
  1775. argCount = ReadArgs( args, 2 );
  1776. CCmd_JointMerge( joints, args[0], args[1] );
  1777. }
  1778. else if ( !stricmp( command, "$rootbone" ) )
  1779. {
  1780. argCount = ReadArgs( args, 1 );
  1781. CCmd_JointRoot( joints, args[0] );
  1782. }
  1783. else if ( !stricmp( command, "$jointconstrain" ) )
  1784. {
  1785. argCount = ReadArgs( args, 6 );
  1786. char *pFriction = args[5];
  1787. if ( argCount < 6 )
  1788. {
  1789. pFriction = "1.0";
  1790. }
  1791. CCmd_JointConstrain( joints, args[0], args[1], args[2], args[3], args[4], pFriction );
  1792. }
  1793. // joint properties
  1794. else if ( !stricmp( command, "$jointinertia" ) )
  1795. {
  1796. argCount = ReadArgs( args, 2 );
  1797. joints.JointInertia( args[0], Safe_atof( args[1] ) );
  1798. }
  1799. else if ( !stricmp( command, "$jointdamping" ) )
  1800. {
  1801. argCount = ReadArgs( args, 2 );
  1802. joints.JointDamping( args[0], Safe_atof( args[1] ) );
  1803. }
  1804. else if ( !stricmp( command, "$jointrotdamping" ) )
  1805. {
  1806. argCount = ReadArgs( args, 2 );
  1807. joints.JointRotdamping( args[0], Safe_atof( args[1] ) );
  1808. }
  1809. else if ( !stricmp( command, "$jointmassbias" ) )
  1810. {
  1811. argCount = ReadArgs( args, 2 );
  1812. joints.JointMassBias( args[0], Safe_atof( args[1] ) );
  1813. }
  1814. else if ( !stricmp( command, "$noselfcollisions" ) )
  1815. {
  1816. joints.SetNoSelfCollisions();
  1817. }
  1818. else if ( !stricmp( command, "$jointcollide" ) )
  1819. {
  1820. argCount = ReadArgs( args, 2 );
  1821. joints.AppendCollisionPair( args[0], args[1] );
  1822. }
  1823. else if ( !stricmp( command, "$animatedfriction" ) )
  1824. {
  1825. argCount = ReadArgs( args, 5 );
  1826. if ( argCount == 5 )
  1827. {
  1828. CCmd_JoinAnimatedFriction( joints, args[0], args[1], args[2], args[3], args[4] );
  1829. }
  1830. }
  1831. else if ( !stricmp( command, "$assumeworldspace") )
  1832. {
  1833. joints.m_bAssumeWorldspace = true;
  1834. }
  1835. else
  1836. {
  1837. MdlWarning("Unknown command %s in collision series\n", command );
  1838. }
  1839. }
  1840. }
  1841. void Cmd_CollisionText( void )
  1842. {
  1843. int level = 1;
  1844. if ( !GetToken( true ) )
  1845. return;
  1846. if ( token[0] != '{' )
  1847. return;
  1848. while ( GetToken(true) )
  1849. {
  1850. if ( !strcmp( token, "}" ) )
  1851. {
  1852. level--;
  1853. if ( level <= 0 )
  1854. break;
  1855. g_JointedModel.AddText( " }\n" );
  1856. }
  1857. else if ( !strcmp( token, "{" ) )
  1858. {
  1859. g_JointedModel.AddText( "{" );
  1860. level++;
  1861. }
  1862. else
  1863. {
  1864. // tokens inside braces are quoted
  1865. if ( level > 1 )
  1866. {
  1867. g_JointedModel.AddText( "\"" );
  1868. g_JointedModel.AddText( token );
  1869. g_JointedModel.AddText( "\" " );
  1870. }
  1871. else
  1872. {
  1873. g_JointedModel.AddText( token );
  1874. g_JointedModel.AddText( " " );
  1875. }
  1876. }
  1877. }
  1878. }
  1879. static bool LoadSurfaceProps( const char *pMaterialFilename )
  1880. {
  1881. if ( !physprops )
  1882. return false;
  1883. FileHandle_t fp = g_pFileSystem->Open( pMaterialFilename, "rb", TOOLS_READ_PATH_ID );
  1884. if ( fp == FILESYSTEM_INVALID_HANDLE )
  1885. return false;
  1886. int len = g_pFileSystem->Size( fp );
  1887. char *pText = new char[len+1];
  1888. g_pFileSystem->Read( pText, len, fp );
  1889. g_pFileSystem->Close( fp );
  1890. pText[len]=0;
  1891. physprops->ParseSurfaceData( pMaterialFilename, pText );
  1892. delete[] pText;
  1893. return true;
  1894. }
  1895. void LoadSurfacePropsAll()
  1896. {
  1897. // already loaded
  1898. if ( physprops->SurfacePropCount() )
  1899. return;
  1900. const char *SURFACEPROP_MANIFEST_FILE = "scripts/surfaceproperties_manifest.txt";
  1901. KeyValues *manifest = new KeyValues( SURFACEPROP_MANIFEST_FILE );
  1902. if ( manifest->LoadFromFile( g_pFileSystem, SURFACEPROP_MANIFEST_FILE, "GAME" ) )
  1903. {
  1904. for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
  1905. {
  1906. if ( !Q_stricmp( sub->GetName(), "file" ) )
  1907. {
  1908. // Add
  1909. LoadSurfaceProps( sub->GetString() );
  1910. continue;
  1911. }
  1912. }
  1913. }
  1914. manifest->deleteThis();
  1915. }
  1916. //-----------------------------------------------------------------------------
  1917. // Purpose: Entry point for script processing. Delegate to necessary subroutines.
  1918. // Parse the collisionmodel {} and collisionjoints {} chunks
  1919. // Input : separateJoints - whether this has a constraint system or not (true if it does)
  1920. // Output : int
  1921. //-----------------------------------------------------------------------------
  1922. int DoCollisionModel( bool separateJoints )
  1923. {
  1924. char name[512];
  1925. s_source_t *pmodel;
  1926. // name
  1927. if (!GetToken(false)) return 0;
  1928. V_strcpy_safe( name, token );
  1929. PhysicsDLLPath( "VPHYSICS.DLL" );
  1930. // CreateInterfaceFn physicsFactory = GetPhysicsFactory();
  1931. CreateInterfaceFn physicsFactory = Sys_GetFactory(Sys_LoadModule( "vphysics.dll" ));
  1932. if ( !physicsFactory )
  1933. return 0;
  1934. physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
  1935. physprops = (IPhysicsSurfaceProps *)physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL );
  1936. LoadSurfacePropsAll();
  1937. int nummaterials = g_nummaterials;
  1938. int numtextures = g_numtextures;
  1939. pmodel = Load_Source( name, "SMD" );
  1940. if ( !pmodel )
  1941. return 0;
  1942. // auto-remove any new materials/textures
  1943. if (nummaterials && numtextures && (numtextures != g_numtextures || nummaterials != g_nummaterials))
  1944. {
  1945. g_numtextures = numtextures;
  1946. g_nummaterials = nummaterials;
  1947. pmodel->texmap[0] = 0;
  1948. }
  1949. // all bones map to themselves by default
  1950. g_JointedModel.SetSource( pmodel );
  1951. bool parseCommands = false;
  1952. // If the next token is a { that means a data block for the collision model
  1953. if (GetToken(true))
  1954. {
  1955. if ( !strcmp( token, "{" ) )
  1956. {
  1957. parseCommands = true;
  1958. }
  1959. else
  1960. {
  1961. UnGetToken();
  1962. }
  1963. }
  1964. if ( parseCommands )
  1965. {
  1966. ParseCollisionCommands( g_JointedModel );
  1967. }
  1968. g_bJointed = separateJoints;
  1969. // collision script is stored in g_JointedModel for later processing
  1970. return 1;
  1971. }
  1972. //-----------------------------------------------------------------------------
  1973. // Purpose: Walk the list of models, add up the volume
  1974. // Input : *pList -
  1975. // Output : float
  1976. //-----------------------------------------------------------------------------
  1977. float TotalVolume( CPhysCollisionModel *pList )
  1978. {
  1979. float volume = 0;
  1980. while ( pList )
  1981. {
  1982. volume += pList->m_volume * pList->m_massBias;
  1983. pList = pList->m_pNext;
  1984. }
  1985. return volume;
  1986. }
  1987. //-----------------------------------------------------------------------------
  1988. // Purpose: Write key/value pairs out to a file
  1989. // Input : *fp - output file
  1990. // *pKeyName - key name
  1991. // outputData - type specific output data
  1992. //-----------------------------------------------------------------------------
  1993. void KeyWriteInt( FILE *fp, const char *pKeyName, int outputData )
  1994. {
  1995. fprintf( fp, "\"%s\" \"%d\"\n", pKeyName, outputData );
  1996. }
  1997. void KeyWriteIntPair( FILE *fp, const char *pKeyName, int outputData0, int outputData1 )
  1998. {
  1999. fprintf( fp, "\"%s\" \"%d,%d\"\n", pKeyName, outputData0, outputData1 );
  2000. }
  2001. void KeyWriteString( FILE *fp, const char *pKeyName, const char *outputData )
  2002. {
  2003. fprintf( fp, "\"%s\" \"%s\"\n", pKeyName, outputData );
  2004. }
  2005. void KeyWriteVector3( FILE *fp, const char *pKeyName, const Vector& outputData )
  2006. {
  2007. fprintf( fp, "\"%s\" \"%f %f %f\"\n", pKeyName, outputData[0], outputData[1], outputData[2] );
  2008. }
  2009. void KeyWriteQAngle( FILE *fp, const char *pKeyName, const QAngle& outputData )
  2010. {
  2011. fprintf( fp, "\"%s\" \"%f %f %f\"\n", pKeyName, outputData[0], outputData[1], outputData[2] );
  2012. }
  2013. void KeyWriteFloat( FILE *fp, const char *pKeyName, float outputData )
  2014. {
  2015. fprintf( fp, "\"%s\" \"%f\"\n", pKeyName, outputData );
  2016. }
  2017. void FixCollisionHierarchy( CJointedModel &joints )
  2018. {
  2019. if ( joints.m_pCollisionList )
  2020. {
  2021. CPhysCollisionModel *pPhys = joints.m_pCollisionList;
  2022. FixBoneList( joints.m_bonemap, joints.m_pModel, joints.m_pCollisionList );
  2023. // Point parents at joints that are actually in the model
  2024. for ( ;pPhys; pPhys = pPhys->m_pNext )
  2025. {
  2026. pPhys->m_parent = FixParent( joints.m_pCollisionList, joints.m_pModel, pPhys->m_parent );
  2027. }
  2028. // sort the list so parents come before children
  2029. joints.SortCollisionList();
  2030. // Now remap the constraints to bones to
  2031. // Now that bones are in order, set physics indices in main bone structure
  2032. CJointConstraint *pList = g_JointedModel.m_pConstraintList;
  2033. while ( pList )
  2034. {
  2035. pList->m_pJointName = FixParent( joints.m_pCollisionList, joints.m_pModel, pList->m_pJointName );
  2036. pList = pList->m_pNext;
  2037. }
  2038. pPhys = joints.m_pCollisionList;
  2039. int i;
  2040. for ( i = 0; i < g_numbones; i++ )
  2041. {
  2042. g_bonetable[i].physicsBoneIndex = -1;
  2043. }
  2044. int index = 0;
  2045. while ( pPhys )
  2046. {
  2047. int boneIndex = FindBoneInTable( pPhys->m_name );
  2048. if ( boneIndex >= 0 )
  2049. {
  2050. g_bonetable[boneIndex].physicsBoneIndex = index;
  2051. }
  2052. pPhys = pPhys->m_pNext;
  2053. index ++;
  2054. }
  2055. for ( i = 0; i < g_numbones; i++ )
  2056. {
  2057. // if no bone was set, set to parent bone
  2058. if ( g_bonetable[i].physicsBoneIndex < 0 )
  2059. {
  2060. int index = g_bonetable[i].parent;
  2061. int bone = -1;
  2062. while ( index >= 0 )
  2063. {
  2064. bone = g_bonetable[index].physicsBoneIndex;
  2065. if ( bone >= 0 )
  2066. break;
  2067. index = g_bonetable[index].parent;
  2068. }
  2069. // found one?
  2070. if ( bone >= 0 )
  2071. {
  2072. g_bonetable[i].physicsBoneIndex = bone;
  2073. }
  2074. else
  2075. {
  2076. // just set physics to affect root
  2077. g_bonetable[i].physicsBoneIndex = 0;
  2078. }
  2079. }
  2080. }
  2081. }
  2082. }
  2083. //-----------------------------------------------------------------------------
  2084. // Purpose: Builds the physics/collision model.
  2085. // This must execute after the model has been simplified!!
  2086. //-----------------------------------------------------------------------------
  2087. void CollisionModel_Build( void )
  2088. {
  2089. // no collision model referenced
  2090. if ( !g_JointedModel.m_pModel )
  2091. return;
  2092. g_JointedModel.Simplify();
  2093. if ( g_bJointed )
  2094. {
  2095. ProcessJointedModel( g_JointedModel );
  2096. }
  2097. else
  2098. {
  2099. ProcessSingleBody( g_JointedModel );
  2100. }
  2101. FixCollisionHierarchy( g_JointedModel );
  2102. if( !g_quiet )
  2103. {
  2104. printf("Collision model completed.\n" );
  2105. }
  2106. g_JointedModel.ComputeMass();
  2107. }
  2108. void BuildRagdollConstraint( CPhysCollisionModel *pPhys, constraint_ragdollparams_t &ragdoll )
  2109. {
  2110. memset( &ragdoll, 0, sizeof(ragdoll) );
  2111. ragdoll.parentIndex = g_JointedModel.CollisionIndex(pPhys->m_parent);
  2112. ragdoll.childIndex = g_JointedModel.CollisionIndex(pPhys->m_name);
  2113. if ( ragdoll.parentIndex < 0 || ragdoll.childIndex < 0 )
  2114. {
  2115. MdlWarning("Constraint between bone %s and %s\n", pPhys->m_name, pPhys->m_parent );
  2116. if ( ragdoll.childIndex < 0 )
  2117. MdlWarning("\"%s\" does not appear in collision model!!!\n", pPhys->m_name );
  2118. if ( ragdoll.parentIndex < 0 )
  2119. MdlWarning("\"%s\" does not appear in collision model!!!\n", pPhys->m_parent );
  2120. MdlError("Bad constraint in ragdoll\n");
  2121. }
  2122. CJointConstraint *pList = g_JointedModel.m_pConstraintList;
  2123. while ( pList )
  2124. {
  2125. int index = g_JointedModel.CollisionIndex(pList->m_pJointName);
  2126. CPhysCollisionModel *pListModel = g_JointedModel.GetCollisionModel(pList->m_pJointName);
  2127. if ( index < 0 )
  2128. {
  2129. MdlError("Rotation constraint on bone \"%s\" which does not appear in collision model!!!\n", pList->m_pJointName );
  2130. }
  2131. else if ( (!pListModel->m_parent || g_JointedModel.CollisionIndex(pListModel->m_parent) < 0) && stricmp( pList->m_pJointName, g_JointedModel.m_rootName ) )
  2132. {
  2133. MdlError("Rotation constraint on bone \"%s\" which has no parent!!!\n", pList->m_pJointName );
  2134. }
  2135. else if ( index == ragdoll.childIndex )
  2136. {
  2137. switch ( pList->m_jointType )
  2138. {
  2139. case JOINT_LIMIT:
  2140. ragdoll.axes[pList->m_axis].SetAxisFriction( pList->m_limitMin, pList->m_limitMax, pList->m_friction );
  2141. break;
  2142. case JOINT_FIXED:
  2143. ragdoll.axes[pList->m_axis].SetAxisFriction( 0,0,0 );
  2144. break;
  2145. case JOINT_FREE:
  2146. ragdoll.axes[pList->m_axis].SetAxisFriction( -360, 360, pList->m_friction );
  2147. break;
  2148. }
  2149. }
  2150. pList = pList->m_pNext;
  2151. }
  2152. }
  2153. float GetCollisionModelMass()
  2154. {
  2155. return g_JointedModel.m_totalMass;
  2156. }
  2157. void CollisionModel_ExpandBBox( Vector &mins, Vector &maxs )
  2158. {
  2159. // don't do fixup for ragdolls
  2160. if ( g_bJointed )
  2161. return;
  2162. if ( g_JointedModel.m_pCollisionList )
  2163. {
  2164. Vector collideMins, collideMaxs;
  2165. physcollision->CollideGetAABB( &collideMins, &collideMaxs, g_JointedModel.m_pCollisionList->m_pCollisionData, vec3_origin, vec3_angle );
  2166. // add the 0.25 inch collision separation as well
  2167. const float radius = 0.25;
  2168. collideMins -= Vector(radius,radius,radius);
  2169. collideMaxs += Vector(radius,radius,radius);
  2170. AddPointToBounds( collideMins, mins, maxs );
  2171. AddPointToBounds( collideMaxs, mins, maxs );
  2172. }
  2173. }
  2174. //-----------------------------------------------------------------------------
  2175. // Purpose: Write out any data that's been saved in the globals
  2176. //-----------------------------------------------------------------------------
  2177. void CollisionModel_Write( long checkSum )
  2178. {
  2179. if ( g_JointedModel.m_pCollisionList )
  2180. {
  2181. CPhysCollisionModel *pPhys = g_JointedModel.m_pCollisionList;
  2182. char filename[MAX_PATH];
  2183. V_strcpy_safe( filename, gamedir );
  2184. // if( *g_pPlatformName )
  2185. // {
  2186. // strcat( filename, "platform_" );
  2187. // strcat( filename, g_pPlatformName );
  2188. // strcat( filename, "/" );
  2189. // }
  2190. V_strcat_safe( filename, "models/" );
  2191. V_strcat_safe( filename, outname );
  2192. float volume = TotalVolume( pPhys );
  2193. if ( volume <= 0 )
  2194. volume = 1;
  2195. if( !g_quiet )
  2196. {
  2197. printf("Collision model volume %.2f in^3\n", volume );
  2198. }
  2199. Q_SetExtension( filename, ".phy", sizeof( filename ) );
  2200. CPlainAutoPtr< CP4File > spFile( g_p4factory->AccessFile( filename ) );
  2201. spFile->Edit();
  2202. FILE *fp = fopen( filename, "wb" );
  2203. if ( fp )
  2204. {
  2205. // write out the collision header (size is version)
  2206. phyheader_t header;
  2207. header.size = sizeof(header);
  2208. header.id = 0;
  2209. header.checkSum = checkSum;
  2210. header.solidCount = 0;
  2211. pPhys = g_JointedModel.m_pCollisionList;
  2212. while ( pPhys )
  2213. {
  2214. header.solidCount++;
  2215. pPhys = pPhys->m_pNext;
  2216. }
  2217. fwrite( &header, sizeof(header), 1, fp );
  2218. // Write out the binary physics collision data
  2219. pPhys = g_JointedModel.m_pCollisionList;
  2220. while ( pPhys )
  2221. {
  2222. int size = physcollision->CollideSize( pPhys->m_pCollisionData );
  2223. fwrite( &size, sizeof(int), 1, fp );
  2224. char *buf = (char *)stackalloc( size );
  2225. physcollision->CollideWrite( buf, pPhys->m_pCollisionData );
  2226. fwrite( buf, size, 1, fp );
  2227. pPhys = pPhys->m_pNext;
  2228. }
  2229. // write out the properties of each solid
  2230. int solidIndex = 0;
  2231. pPhys = g_JointedModel.m_pCollisionList;
  2232. while ( pPhys )
  2233. {
  2234. pPhys->m_mass = ((pPhys->m_volume * pPhys->m_massBias) / volume) * g_JointedModel.m_totalMass;
  2235. if ( pPhys->m_mass < 1.0 )
  2236. pPhys->m_mass = 1.0;
  2237. fprintf( fp, "solid {\n" );
  2238. KeyWriteInt( fp, "index", solidIndex );
  2239. KeyWriteString( fp, "name", pPhys->m_name );
  2240. if ( pPhys->m_parent )
  2241. {
  2242. KeyWriteString( fp, "parent", pPhys->m_parent );
  2243. }
  2244. KeyWriteFloat( fp, "mass", pPhys->m_mass );
  2245. //KeyWriteFloat( fp, "volume", pPhys->m_volume );
  2246. char* pSurfaceProps = GetSurfaceProp( pPhys->m_name );
  2247. KeyWriteString( fp, "surfaceprop", pSurfaceProps );
  2248. KeyWriteFloat( fp, "damping", pPhys->m_damping );
  2249. KeyWriteFloat( fp, "rotdamping", pPhys->m_rotdamping );
  2250. if ( pPhys->m_dragCoefficient != -1 )
  2251. {
  2252. KeyWriteFloat( fp, "drag", pPhys->m_dragCoefficient );
  2253. }
  2254. KeyWriteFloat( fp, "inertia", pPhys->m_inertia );
  2255. KeyWriteFloat( fp, "volume", pPhys->m_volume );
  2256. if ( pPhys->m_massBias != 1.0f )
  2257. {
  2258. KeyWriteFloat( fp, "massbias", pPhys->m_massBias );
  2259. }
  2260. fprintf( fp, "}\n" );
  2261. pPhys = pPhys->m_pNext;
  2262. solidIndex++;
  2263. }
  2264. // by default, write constraints from each limb to its parent
  2265. pPhys = g_JointedModel.m_pCollisionList;
  2266. while ( pPhys )
  2267. {
  2268. // check to see if bone collapse/remap has left this with parent pointing at itself
  2269. if ( pPhys->m_parent )
  2270. {
  2271. constraint_ragdollparams_t ragdoll;
  2272. BuildRagdollConstraint( pPhys, ragdoll );
  2273. if ( ragdoll.parentIndex != ragdoll.childIndex )
  2274. {
  2275. fprintf( fp, "ragdollconstraint {\n" );
  2276. KeyWriteInt( fp, "parent", ragdoll.parentIndex );
  2277. KeyWriteInt( fp, "child", ragdoll.childIndex );
  2278. KeyWriteFloat( fp, "xmin", ragdoll.axes[0].minRotation );
  2279. KeyWriteFloat( fp, "xmax", ragdoll.axes[0].maxRotation );
  2280. KeyWriteFloat( fp, "xfriction", ragdoll.axes[0].torque );
  2281. KeyWriteFloat( fp, "ymin", ragdoll.axes[1].minRotation );
  2282. KeyWriteFloat( fp, "ymax", ragdoll.axes[1].maxRotation );
  2283. KeyWriteFloat( fp, "yfriction", ragdoll.axes[1].torque );
  2284. KeyWriteFloat( fp, "zmin", ragdoll.axes[2].minRotation );
  2285. KeyWriteFloat( fp, "zmax", ragdoll.axes[2].maxRotation );
  2286. KeyWriteFloat( fp, "zfriction", ragdoll.axes[2].torque );
  2287. fprintf( fp, "}\n" );
  2288. }
  2289. }
  2290. pPhys = pPhys->m_pNext;
  2291. }
  2292. if ( g_JointedModel.m_noSelfCollisions )
  2293. {
  2294. fprintf(fp, "collisionrules {\n" );
  2295. KeyWriteInt( fp, "selfcollisions", 0 );
  2296. fprintf(fp, "}\n");
  2297. }
  2298. else if ( g_JointedModel.m_pCollisionPairs )
  2299. {
  2300. fprintf(fp, "collisionrules {\n" );
  2301. collisionpair_t *pPair = g_JointedModel.m_pCollisionPairs;
  2302. while ( pPair )
  2303. {
  2304. pPair->obj0 = g_JointedModel.CollisionIndex( pPair->pName0 );
  2305. pPair->obj1 = g_JointedModel.CollisionIndex( pPair->pName1 );
  2306. if ( pPair->obj0 >= 0 && pPair->obj1 >= 0 && pPair->obj0 != pPair->obj1 )
  2307. {
  2308. KeyWriteIntPair( fp, "collisionpair", pPair->obj0, pPair->obj1 );
  2309. }
  2310. else
  2311. {
  2312. MdlWarning("Invalid collision pair (%s, %s)\n", pPair->pName0, pPair->pName1 );
  2313. }
  2314. pPair = pPair->pNext;
  2315. }
  2316. fprintf(fp, "}\n");
  2317. }
  2318. if ( g_JointedModel.m_bHasAnimatedFriction == true )
  2319. {
  2320. fprintf( fp, "animatedfriction {\n" );
  2321. KeyWriteFloat( fp, "animfrictionmin", g_JointedModel.m_iMinAnimatedFriction );
  2322. KeyWriteFloat( fp, "animfrictionmax", g_JointedModel.m_iMaxAnimatedFriction );
  2323. KeyWriteFloat( fp, "animfrictiontimein", g_JointedModel.m_flFrictionTimeIn );
  2324. KeyWriteFloat( fp, "animfrictiontimeout", g_JointedModel.m_flFrictionTimeOut );
  2325. KeyWriteFloat( fp, "animfrictiontimehold", g_JointedModel.m_flFrictionTimeHold );
  2326. fprintf( fp, "}\n" );
  2327. }
  2328. // block that is only parsed by the editor
  2329. fprintf( fp, "editparams {\n" );
  2330. KeyWriteString( fp, "rootname", g_JointedModel.m_rootName );
  2331. KeyWriteFloat( fp, "totalmass", g_JointedModel.m_totalMass );
  2332. if ( g_JointedModel.m_allowConcave )
  2333. {
  2334. KeyWriteInt( fp, "concave", 1 );
  2335. }
  2336. for ( int k = 0; k < g_JointedModel.m_mergeList.Count(); k++ )
  2337. {
  2338. char buf[512];
  2339. Q_snprintf( buf, sizeof(buf), "%s,%s", g_JointedModel.m_mergeList[k].pParent, g_JointedModel.m_mergeList[k].pChild );
  2340. KeyWriteString( fp, "jointmerge", buf );
  2341. }
  2342. fprintf( fp, "}\n" );
  2343. char terminator = 0;
  2344. if ( g_JointedModel.m_textCommands.Size() )
  2345. {
  2346. fwrite( g_JointedModel.m_textCommands.Base(), g_JointedModel.m_textCommands.Size(), 1, fp );
  2347. }
  2348. fwrite( &terminator, sizeof(terminator), 1, fp );
  2349. fclose( fp );
  2350. spFile->Add();
  2351. }
  2352. else
  2353. {
  2354. MdlWarning("Error writing %s!!!\n", filename );
  2355. }
  2356. }
  2357. }