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.

3173 lines
86 KiB

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