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.

2059 lines
59 KiB

  1. //========== Copyright (c) Valve Corporation. All Rights Reserved. ============
  2. #include "mdlobjects/authphysfx.h"
  3. #include "resourcefile/resourcestream.h"
  4. #include "mathlib/femodeldesc.h"
  5. #include "rubikon/param_types.h"
  6. #include "tier1/utlstringtoken.h"
  7. #include "tier1/keyvalues.h"
  8. #include "tier2/fileutils.h"
  9. #include "tier2/p4helpers.h"
  10. #include "mathlib/disjoint_set_forest.h"
  11. //#include "mdlobjects/physmodelsource.h"
  12. #include "meshutils/mesh.h"
  13. // #include "meshutils/meshdisjointsetpartition.h"
  14. // #include "meshutils/meshconvexitydetector.h"
  15. // #include "mdlobjects/vpropbreakablelist.h"
  16. #include "mdlobjects/physmodelsource.h"
  17. #include "mathlib/femodelbuilder.h"
  18. #include "tier1/heapsort.h"
  19. #include "tier1/fmtstr.h"
  20. #include "mathlib/disjoint_set_forest.h"
  21. #include "datamodel/dmelement.h"
  22. #include "datamodel/dmelementfactoryhelper.h"
  23. #include "datamodel/idatamodel.h"
  24. #include "movieobjects/dmedag.h"
  25. #include "movieobjects/dmemesh.h"
  26. #include "movieobjects/dmejoint.h"
  27. #include "movieobjects/dmemodel.h"
  28. #include "mdlobjects/clothproxymesh.h"
  29. #include "dmeutils/dmmeshutils.h"
  30. // #ifdef _DEBUG
  31. // #define TNT_BOUNDS_CHECK
  32. // #else
  33. // //#define TNT_UNROLL_LOOPS
  34. // #endif
  35. //
  36. // #include "tnt.h"
  37. // #include "tnt_linalg.h"
  38. DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_AUTH_PHYS, "AuthPhys" );
  39. const char *g_pTokenSeparators[] = { " ", "\t", "\n", "\r", ",", ";", "|" };
  40. //-----------------------------------------------------------------------------
  41. static CDmeModel* LoadModelFromDMX( const char* pDMXFile )
  42. {
  43. CDmElement* pRoot;
  44. if ( g_pDataModel->RestoreFromFile( pDMXFile, "CONTENT", NULL, &pRoot ) == DMFILEID_INVALID )
  45. {
  46. return false;
  47. }
  48. // If this isn't a DME Model
  49. CDmeModel* pModel = NULL;
  50. if ( pRoot->IsA< CDmeModel >() )
  51. {
  52. pModel = dynamic_cast<CDmeModel*>( pRoot );
  53. }
  54. // Try to find a CDmeModel
  55. if ( !pModel )
  56. {
  57. pModel = pRoot->GetValueElement< CDmeModel >( "model" );
  58. }
  59. if ( !pModel )
  60. {
  61. pModel = pRoot->GetValueElement< CDmeModel >( "skeleton" );
  62. }
  63. return pModel;
  64. }
  65. //-----------------------------------------------------------------------------
  66. int CompareCaseInsensitive( const CUtlString *pLeft, const CUtlString *pRight )
  67. {
  68. return V_stricmp( pLeft->Get(), pRight->Get() );
  69. }
  70. //-----------------------------------------------------------------------------
  71. inline const Vector GetAxisX( const Quaternion &q )
  72. {
  73. return q.GetForward( );
  74. }
  75. int CAuthPhysFx::FindNodeIndex( const char *pName )
  76. {
  77. for ( int i = 0; i < m_Nodes.Count(); ++i )
  78. {
  79. if ( !V_stricmp( pName, m_Nodes[ i ].m_Name.Get() ) )
  80. return i;
  81. }
  82. return -1;
  83. }
  84. CAuthPhysFx::CBone *CAuthPhysFx::GetOrCreateBone( const char *pName )
  85. {
  86. int nNode = FindNodeIndex( pName );
  87. if ( nNode >= 0 )
  88. {
  89. return &m_Nodes[ nNode ];
  90. }
  91. CBone *pNewBone = &m_Nodes[ m_Nodes.AddToTail() ];
  92. pNewBone->m_Name = pName;
  93. return pNewBone;
  94. }
  95. int DescSort( const int* a, const int* b )
  96. {
  97. return *b - *a;
  98. }
  99. Vector Sqr( const Vector &v )
  100. {
  101. return Vector( v.x * v.x, v.y * v.y, v.z * v.z );
  102. }
  103. Vector SafeSqrt( const Vector &v )
  104. {
  105. return Vector( sqrtf( MAX( 0, v.x ) ), sqrtf( MAX( 0, v.y ) ), sqrtf( MAX( 0, v.z ) ) );
  106. }
  107. void CAuthPhysFx::AddRod( const CUtlVector< CBone > &nodes, uint nNode0, uint nNode1, float flRelaxationFactor )
  108. {
  109. if ( nodes[ nNode0 ].m_bSimulated || nodes[ nNode1 ].m_bSimulated )
  110. {
  111. CRod &rod = m_Rods[ m_Rods.AddToTail( ) ];
  112. rod.m_nNodes[ 0 ] = nNode0;
  113. rod.m_nNodes[ 1 ] = nNode1;
  114. rod.m_flRelaxationFactor = flRelaxationFactor;
  115. }
  116. }
  117. static float GetClothFloat( KeyValues *pKeyValues, const char *keyName = NULL, float defaultValue = 0.0f )
  118. {
  119. if ( keyName )
  120. {
  121. CUtlString altKeyName( "s2:" );
  122. altKeyName += keyName;
  123. if ( KeyValues *pKey = pKeyValues->FindKey( altKeyName ) )
  124. {
  125. return pKey->GetFloat( );
  126. }
  127. }
  128. return pKeyValues->GetFloat( keyName, defaultValue );
  129. };
  130. static bool FindKey( KeyValues *pParent, const char *pSubkey, float *pFloatOut )
  131. {
  132. if ( KeyValues *pChild = pParent->FindKey( pSubkey ) )
  133. {
  134. *pFloatOut = pChild->GetFloat( ( const char * )NULL, *pFloatOut );
  135. return true;
  136. }
  137. return false;
  138. }
  139. static int GetClothInt( KeyValues *pKeyValues, const char *keyName = NULL, int defaultValue = 0 )
  140. {
  141. if ( keyName )
  142. {
  143. CUtlString altKeyName( "s2:" );
  144. altKeyName += keyName;
  145. if ( KeyValues *pKey = pKeyValues->FindKey( altKeyName ) )
  146. {
  147. return pKey->GetInt( );
  148. }
  149. }
  150. return pKeyValues->GetInt( keyName, defaultValue );
  151. };
  152. static const char *GetClothString( KeyValues *pKeyValues, const char *keyName = NULL, const char *defaultValue = "" )
  153. {
  154. if ( keyName )
  155. {
  156. CUtlString altKeyName( "s2:" );
  157. altKeyName += keyName;
  158. if ( KeyValues *pKey = pKeyValues->FindKey( altKeyName ) )
  159. {
  160. return pKey->GetString( );
  161. }
  162. }
  163. return pKeyValues->GetString( keyName, defaultValue );
  164. };
  165. void CreateGridNodeBases( const CNodeIdx &nodeIdx, int nRows, int nColumns, const CUtlVector< CAuthPhysFx::CBone > &nodes, CUtlVector< FeNodeBase_t > &nodeBases )
  166. {
  167. //float sin45 = sqrtf( 0.5f );
  168. //const Quaternion qAdjust = Quaternion( 0, sin45, 0, sin45 ) * Quaternion( 0, 0, sin45, sin45 );
  169. const Quaternion qAdjust = Quaternion( .5, .5, .5, .5 ) * Quaternion( 1, 0, 0, 0 );
  170. matrix3x4a_t tmTest;
  171. QuaternionMatrix( qAdjust, tmTest );
  172. for ( int nRow = 0; nRow < nRows; ++nRow )
  173. {
  174. for ( int nColumn = 0; nColumn < nColumns; ++nColumn )
  175. {
  176. uint nNode = nodeIdx( nRow, nColumn );
  177. if ( nodes[ nNode ].m_bVirtual || !nodes[ nNode ].m_bSimulated )
  178. {
  179. continue; // don't care about virtual or static nodes
  180. }
  181. int nUp = Max( 0, nRow - 1 ), nDown = Min( nRows - 1, nRow + 1 );
  182. int nLeft = Max( 0, nColumn - 1 ), nRight = Min( nColumns - 1, nColumn + 1 );
  183. FeNodeBase_t &nb = nodeBases[ nodeBases.AddToTail( ) ];
  184. V_memset( &nb, 0, sizeof( nb ) );
  185. nb.nNode = nNode;
  186. nb.nNodeX0 = nodeIdx( nRow, nLeft );
  187. nb.nNodeX1 = nodeIdx( nRow, nRight );
  188. nb.nNodeY0 = nodeIdx( nUp, nColumn );
  189. nb.nNodeY1 = nodeIdx( nDown, nColumn );
  190. // nodeX -> right -> particle Z; this direction is gram-schmidt-orthogonalized (in Source1 using double-cross-product, in Source2 I just GS-orthogonalize it directly)
  191. // nodeY -> Down(-Up) -> particle X; this direction is unchanged
  192. nb.qAdjust = qAdjust;
  193. }
  194. }
  195. }
  196. void CAuthPhysFx::Load( const CFeModel *pFeModel )
  197. {
  198. #ifdef _DEBUG // recreate the CFeModel
  199. CResourceStreamVM stream;
  200. const PhysFeModelDesc_t *pFeDesc = Compile( &stream );
  201. CFeModel *pFeModel2 = stream.Allocate< CFeModel >();
  202. Clone( pFeDesc, 0, stream.Allocate< char * >( pFeModel2->m_nCtrlCount ), pFeModel2 );
  203. NOTE_UNUSED( pFeModel2 );
  204. #endif
  205. }
  206. bool CAuthPhysFx::ImportDotaCloth( const char *pFileName, CPhysModelSource &physicsModel/*CUtlStringMap< int, CUtlSymbolTable > *pBoneToIndex*/ )
  207. {
  208. const char *pContentPath = V_IsAbsolutePath( pFileName ) ? NULL : "CONTENT";
  209. if ( !g_pFullFileSystem->FileExists( pFileName, pContentPath ) )
  210. {
  211. return false;
  212. }
  213. KeyValues *kv = new KeyValues( "Cloth" );
  214. if ( !kv->LoadFromFile( g_pFullFileSystem, pFileName, pContentPath ) )
  215. {
  216. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse %s\n", pFileName );
  217. kv->deleteThis();
  218. return false;
  219. }
  220. CAuthClothParser parser;
  221. parser.SetBones( physicsModel );
  222. bool bParsedOk = parser.Parse( kv );
  223. kv->deleteThis();
  224. if ( bParsedOk )
  225. {
  226. Swap( parser );
  227. }
  228. m_Constraints.Purge( );
  229. m_Capsules.Purge( );
  230. m_bFollowTheLead = true; // this is a bad default, necessary for compatibility with old Dota2 Source1 cloth
  231. for ( uint n = 0; n < ( uint )m_Nodes.Count(); ++n )
  232. {
  233. if ( m_Nodes[ n ].m_flLocalForce != m_flLocalForce || m_Nodes[ n ].m_flLocalRotation != m_flLocalRotation )
  234. {
  235. m_bUsePerNodeLocalForceAndRotation = true; // Dota2 Source1 uses different local rotation and force per cloth piece
  236. break;
  237. }
  238. }
  239. return true;
  240. }
  241. void CAuthClothParser::SetBones( CPhysModelSource &physicsModel )
  242. {
  243. m_BoneToParent.SetCount( physicsModel.GetBoneCount( ) );
  244. for ( int nBone = 0; nBone < physicsModel.GetBoneCount( ); ++nBone )
  245. {
  246. m_BoneToIndex.Insert( physicsModel.GetBoneNameByIndex( nBone ), nBone );
  247. m_BoneToParent[ nBone ] = physicsModel.GetParentJoint( nBone );
  248. }
  249. m_ModelBoneToNode.SetCount( physicsModel.GetBoneCount( ) );
  250. m_ModelBoneToNode.FillWithValue( -1 );
  251. // we'll use the "bindpose" or similar animation, first frame, if we can
  252. /*
  253. const char * idleAnimations[] = { "bindpose", "idle", "run" };
  254. for ( int i = 0; i < ARRAYSIZE( idleAnimations ); ++i )
  255. {
  256. if ( physicsModel.GetAnimFrame( idleAnimations[ i ], 0, &transforms ) )
  257. {
  258. break; // we'll use this
  259. }
  260. }
  261. if( m_BoneTransforms.Count() < physicsModel.GetBoneCount() )
  262. {
  263. Log_Msg( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " m_BoneTransforms.Count() %d < physicsModel.GetBoneCount() %d\n", m_BoneTransforms.Count(), physicsModel.GetBoneCount() );
  264. Log_Msg( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Could not find bindpose animation, using actual bind pose which dota s1 does NOT use when building cloth. The resulting cloth will be different from Dota Source1 cloth\n" );
  265. // couldn't get the first frame of one of ethalon poses
  266. physicsModel.GetBindPoseWorldTransforms( m_BoneTransforms );
  267. AdjustLegacyDotaOrientation( m_BoneTransforms );
  268. } */
  269. physicsModel.GetBindPoseWorldTransforms( m_BoneTransforms );
  270. AdjustLegacyDotaOrientation( m_BoneTransforms );
  271. }
  272. int CAuthClothParser::FindNodeByName( const char *pName )
  273. {
  274. // fill out the node map
  275. for ( ; m_nNodeNameMapNodes < m_Nodes.Count( ); m_nNodeNameMapNodes++ )
  276. {
  277. m_NodeNameMap[ m_Nodes[ m_nNodeNameMapNodes ].m_Name ] = m_nNodeNameMapNodes;
  278. }
  279. UtlSymId_t hFind = m_NodeNameMap.Find( pName );
  280. if ( hFind != UTL_INVAL_SYMBOL )
  281. {
  282. return m_NodeNameMap[ hFind ];
  283. }
  284. else
  285. {
  286. return -1;
  287. }
  288. }
  289. bool CAuthClothParser::Parse( KeyValues *kv )
  290. {
  291. m_nDefaultCompatibilityMode = GetClothInt( kv, "compatibilityMode", 3 );
  292. m_bFollowTheLead = kv->GetBool( "followTheLead", true );
  293. m_nNodeNameMapNodes = 0;
  294. for ( KeyValues *pSubKey = kv->GetFirstSubKey( ); pSubKey != NULL; pSubKey = pSubKey->GetNextKey( ) )
  295. {
  296. const char *pSubkeyName = pSubKey->GetName( );
  297. if ( !V_stricmp( pSubkeyName, "Defaults" ) )
  298. {
  299. if ( !ParseDefaults( pSubKey ) )
  300. return false;
  301. }
  302. else if ( !V_stricmp( pSubKey->GetName( ), "Cloth" ) )
  303. {
  304. if ( !ParsePiece( pSubKey ) )
  305. return false;
  306. }
  307. }
  308. ReconstructHierarchy( );
  309. return true;
  310. }
  311. void CAuthClothParser::ReconstructHierarchy()
  312. {
  313. // reconstruct the hierarchy. We could just assume that the hierarchy goes along the columns to previous row, but this pass
  314. // will allow any arbitrary topology of the cloth bones
  315. for ( int nModelBone = 0; nModelBone < m_ModelBoneToNode.Count( ); ++nModelBone )
  316. {
  317. int nChildNode = m_ModelBoneToNode[ nModelBone ];
  318. if ( nChildNode >= 0 )
  319. {
  320. int nModelBoneParent = m_BoneToParent[ nModelBone ];
  321. if ( nModelBoneParent >= 0 )
  322. {
  323. int nParentNode = m_ModelBoneToNode[ nModelBoneParent ];
  324. if ( nParentNode >= 0 )
  325. {
  326. int &refParent = m_Nodes[ nChildNode ].m_nParent;
  327. if ( refParent >= 0 && refParent != nParentNode )
  328. {
  329. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Node %s has deducted parent %s, but skeleton parent is %s. Resolving conflict in favor of the skeleton hierarchy.\n", m_Nodes[ nChildNode ].m_Name.Get( ), m_Nodes[ refParent ].m_Name.Get( ), m_Nodes[ nParentNode ].m_Name.Get( ) );
  330. }
  331. refParent = nParentNode;
  332. }
  333. }
  334. }
  335. }
  336. }
  337. bool CAuthClothParser::ParseDefaults( KeyValues *pSubKey )
  338. {
  339. m_flDefaultSurfaceStretch = GetClothFloat( pSubKey, "SurfaceStretch", 0.0f );
  340. m_flDefaultThreadStretch = GetClothFloat( pSubKey, "ThreadStretch", 0.0f );
  341. return true;
  342. }
  343. CBoneParseParams::CBoneParseParams( KeyValues *pSubKey, int nCompatibilityMode )
  344. {
  345. m_pBonePrefix = GetClothString( pSubKey, "BonePrefix" );
  346. m_nNominalColumnCount = GetClothInt( pSubKey, "columns" );
  347. m_nRowCount = GetClothInt( pSubKey, "rows" );
  348. m_flWorldFriction = GetClothFloat( pSubKey, "WorldFriction", 0.35f );
  349. m_flAnimationForceAttraction = GetClothFloat( pSubKey, "AnimationForceAttraction" );
  350. m_flAnimationVertexAttraction = GetClothFloat( pSubKey, "AnimationVertexAttraction" );
  351. m_flDamping = GetClothFloat( pSubKey, "damping" ); // this is velocity-acceleration damping: ( damping force ) = -flDamping * ( velocity ); velocity *= 1 - flDamping * dt / m
  352. m_flFixedPointDamping = GetClothFloat( pSubKey, "FixedPointDamping", 0.0f );
  353. m_flFollowRootEnd = m_flFollowRootBegin = m_flFixedPointDamping;
  354. m_flSpringStretchiness = GetClothFloat( pSubKey, "SpringStretchiness", 0.0f );
  355. // <sergiy> stretchiness meant opposite things as constructor parameter and internal member variable. This is how Dota Source1 implementation goes. I'll rename the internal parameter to something else in S2 to distinguish.
  356. // Also: CClothSpring::m_flStretchiness = 1 - SpringStretchiness from keyvalue file... yeah..
  357. m_flRelaxationFactor = clamp( 1.0f - m_flSpringStretchiness, 0.0f, 1.0f );
  358. // the stretch force is computed after applying SpringStretchiness (flRelaxationFactor) in Source1 cloth, so we'll follow the same pattern to be true to the original: we'll premultiply it when creating CString
  359. // in the new versions, we shouldn't support stretch force. We should use damping instead.
  360. m_flStretchForce = GetClothFloat( pSubKey, "StretchForce", nCompatibilityMode >= 2 ? 1.0f : 0.0f );
  361. m_flStructSpringConstant = GetClothFloat( pSubKey, "StructSpringConstant", 4.0f );
  362. m_flStructSpringDamping = GetClothFloat( pSubKey, "StructSpringDamping", 0.6f );
  363. float flDefaultGravity = nCompatibilityMode >= 1 ? 20 : 380;
  364. m_flGravityScale = GetClothFloat( pSubKey, "gravity_scale", 0.0f );
  365. if ( const char *pGravityString = GetClothString( pSubKey, "gravity", NULL ) )
  366. {
  367. Vector vGravityDirection = Vector( 0, 0, -flDefaultGravity );
  368. if ( 3 == sscanf( pGravityString, "%g %g %g", &vGravityDirection.x, &vGravityDirection.y, &vGravityDirection.z ) )
  369. {
  370. if ( m_flGravityScale == 0.0f )
  371. {
  372. m_flGravityScale = vGravityDirection.NormalizeInPlace();
  373. }
  374. else
  375. {
  376. m_flGravityScale *= vGravityDirection.NormalizeInPlace();
  377. }
  378. if ( vGravityDirection.z > 0 )
  379. {
  380. // Everything further assumes negative gravity direction (towards -Z)
  381. vGravityDirection = -vGravityDirection;
  382. m_flGravityScale = -m_flGravityScale;
  383. }
  384. }
  385. }
  386. else
  387. {
  388. m_flGravityScale = flDefaultGravity;
  389. }
  390. if ( const char *pStabilizeAnim = GetClothString( pSubKey, "stabilizeAnim", NULL ) )
  391. {
  392. switch ( sscanf( pStabilizeAnim, "%g %g", &m_flFollowRootBegin, &m_flFollowRootEnd ) )
  393. {
  394. case 2:
  395. break; // ok, we parsed both
  396. case 0:
  397. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse stabilizeAnim extended keyword parameters, leaving stabilization at the default. Fixed point damping = %g\n", m_flFixedPointDamping );
  398. break;
  399. case 1:
  400. m_flFollowRootEnd = m_flFollowRootBegin;
  401. break;
  402. }
  403. }
  404. m_bIsRopeS1 = nCompatibilityMode >= 3 && m_nNominalColumnCount == 1;
  405. m_nVirtColumnCount = m_nNominalColumnCount;
  406. if ( m_bIsRopeS1 )
  407. {
  408. m_nVirtColumnCount++; // add an extra column, like in Source1 rope simulation
  409. }
  410. }
  411. void CBoneParseParams::ApplyDefaultParams( CBone &node )
  412. {
  413. // apply node settings that are not per node
  414. node.m_Integrator.flPointDamping = m_flDamping;
  415. // <sergiy> Important fix: the springs (particularly animation force attraction) in Source1 were tuned to the "mass" of cloth particles (see CClothParticleState::Integrate() , flDeltaTimeMass = flFrameTime * pClothParticle->GetInverseMass() )
  416. // Source2 computes accelerations directly, so we need to pre-divide by mass here. I forgot to do that in CL 2322760, fixed in 2532503 in femodelbuilder.cpp:2220
  417. node.m_Integrator.flAnimationForceAttraction = m_flAnimationForceAttraction;
  418. node.m_Integrator.flAnimationVertexAttraction = m_flAnimationVertexAttraction;
  419. node.m_Integrator.flGravity = m_flGravityScale;
  420. node.m_flWorldFriction = m_flWorldFriction;
  421. node.m_flLegacyStretchForce = m_flStretchForce;
  422. }
  423. bool CAuthClothParser::ParsePiece( KeyValues *pSubKey )
  424. {
  425. m_nCompatibilityMode = GetClothInt( pSubKey, "compatibilityMode", m_nDefaultCompatibilityMode );
  426. if ( m_nCompatibilityMode >= 2 )
  427. {
  428. m_bUninertialRods = true;
  429. }
  430. if ( m_nCompatibilityMode >= 1 )
  431. {
  432. m_bExplicitMasses = true;
  433. m_bUnitlessDamping = false; // in Dota2 Source1 damping was a coefficient for computing force, not acceleration. So the same damping would affect nodes with differing masses differently
  434. }
  435. CBoneParseParams parseParm( pSubKey, m_nCompatibilityMode );
  436. m_flLocalForce = GetClothFloat( pSubKey, "LocalForce", m_flLocalForce );
  437. m_flLocalRotation = 1.0f - GetClothFloat( pSubKey, "LocalRotation", 1.0f - m_flLocalRotation );
  438. bool bAnonymousPiece = !parseParm.m_pBonePrefix || !*parseParm.m_pBonePrefix || parseParm.m_nNominalColumnCount <= 0 || parseParm.m_nRowCount <= 0; NOTE_UNUSED( bAnonymousPiece );
  439. parseParm.m_pCollisionSpheres = &m_CollisionSpheres;
  440. parseParm.m_pCollisionPlanes = &m_CollisionPlanes;
  441. if ( !bAnonymousPiece )
  442. {
  443. int nNodeBaseIndex = m_Nodes.AddMultipleToTail( parseParm.m_nRowCount * parseParm.m_nVirtColumnCount );
  444. for ( int nNode = nNodeBaseIndex; nNode < m_Nodes.Count(); ++nNode )
  445. {
  446. m_Nodes[ nNode ].m_flLocalForce = m_flLocalForce;
  447. m_Nodes[ nNode ].m_flLocalRotation = m_flLocalRotation;
  448. }
  449. CNodeIdx nodeIdx( nNodeBaseIndex, parseParm.m_nRowCount, parseParm.m_nVirtColumnCount );
  450. if ( !ParseLegacyDotaNodeGrid( pSubKey, parseParm, nodeIdx ) )
  451. return false;
  452. if ( !CreateLegacyDotaRodGrid( parseParm, nodeIdx, m_nCompatibilityMode ) )
  453. return false;
  454. }
  455. if ( m_nCompatibilityMode <= 0 )
  456. {
  457. ParseExplicitDefinitions( pSubKey, parseParm );
  458. }
  459. return true;
  460. }
  461. bool Preparse( KeyValues *kv, CAuthPhysFx::CCollisionSphere &sphere )
  462. {
  463. sphere.m_bInclusive = 0 == V_stricmp( kv->GetName(), "in_sphere" );
  464. if ( kv->GetBool( "inside" ) || kv->GetBool( "inclusive" ) )
  465. sphere.m_bInclusive = true;
  466. if ( kv->GetBool( "outside" ) || kv->GetBool( "exclusive" ) )
  467. sphere.m_bInclusive = false;
  468. FindKey( kv, "radius", &sphere.m_flRadius );
  469. const char *pCenter = kv->GetString( "center", NULL );
  470. if ( !pCenter )
  471. {
  472. pCenter = kv->GetString( "offset", NULL );
  473. }
  474. if ( pCenter )
  475. {
  476. if ( 3 != sscanf( pCenter, "%g %g %g", &sphere.m_vOrigin.x, &sphere.m_vOrigin.y, &sphere.m_vOrigin.z ) )
  477. {
  478. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse sphere center %s\n", pCenter );
  479. }
  480. }
  481. if ( sphere.m_flRadius < 1e-5f && !sphere.m_bInclusive )
  482. {
  483. return false; // no collision with this sphere
  484. }
  485. return true;
  486. }
  487. bool Preparse( KeyValues *kv, CAuthPhysFx::CCollisionPlane &plane )
  488. {
  489. FindKey( kv, "offset", &plane.m_Plane.m_flOffset );
  490. if ( const char *pNormal = kv->GetString( "normal", NULL ) )
  491. {
  492. if ( 3 != sscanf( pNormal, "%g %g %g", &plane.m_Plane.m_vNormal.x, &plane.m_Plane.m_vNormal.y, &plane.m_Plane.m_vNormal.z ) )
  493. {
  494. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse plane normal %s\n", pNormal );
  495. }
  496. else
  497. {
  498. plane.m_Plane.m_vNormal.NormalizedSafe( Vector( 1, 0, 0 ) );
  499. }
  500. }
  501. return true;
  502. }
  503. template <typename T >
  504. void CAuthClothParser::ParseExplicitColl( KeyValues *kv, CBoneParseParams &parseParm, CUtlVector< T > &collArray )
  505. {
  506. T coll;
  507. if ( !Preparse( kv, coll ) )
  508. return;
  509. if ( const char *pParent = kv->GetString( "parent", NULL ) )
  510. {
  511. coll.m_nParentBone = FindNodeByName( pParent );
  512. if ( coll.m_nParentBone < 0 )
  513. {
  514. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot find coll parent bone %s\n", pParent );
  515. return;
  516. }
  517. }
  518. if ( const char *pChild = kv->GetString( "child", NULL ) )
  519. {
  520. coll.m_nChildBone = FindNodeByName( pChild );
  521. if ( coll.m_nChildBone < 0 )
  522. {
  523. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot find coll child bone %s\n", pChild );
  524. }
  525. }
  526. if ( coll.m_nChildBone < 0 )
  527. coll.m_nChildBone = coll.m_nParentBone;
  528. if ( coll.m_nParentBone < 0 )
  529. coll.m_nParentBone = coll.m_nChildBone;
  530. if ( coll.m_nParentBone < 0 )
  531. {
  532. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Collision Sphere has no parent bone\n" );
  533. return;
  534. }
  535. if ( const char *pChildren = kv->GetString( "children", NULL ) )
  536. {
  537. CUtlStringList tokens( pChildren, g_pTokenSeparators, ARRAYSIZE( g_pTokenSeparators ) );
  538. for ( int i = 0; i < tokens.Count( ); ++i )
  539. {
  540. const char *pChildName = tokens[ i ];
  541. coll.m_nChildBone = FindNodeByName( pChildName );
  542. if ( coll.m_nChildBone >= 0 )
  543. {
  544. collArray.AddToTail( coll );
  545. }
  546. else
  547. {
  548. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot find child %s\n", pChildName );
  549. }
  550. }
  551. }
  552. else
  553. {
  554. Assert( coll.m_nChildBone >= 0 );
  555. collArray.AddToTail( coll );
  556. }
  557. }
  558. void CAuthClothParser::ParseExplicitDefinitions( KeyValues *pCloth, CBoneParseParams &parseParm )
  559. {
  560. for ( KeyValues *pSubKey = pCloth->GetFirstSubKey( ); pSubKey != NULL; pSubKey = pSubKey->GetNextKey( ) )
  561. {
  562. const char *pSubkeyName = pSubKey->GetName( );
  563. if ( !V_stricmp( pSubkeyName, "node" ) )
  564. {
  565. ParseExplicitNode( pSubKey, parseParm );
  566. }
  567. else if ( !V_stricmp( pSubkeyName, "tri" ) || !V_stricmp( pSubkeyName, "quad" ) || !V_stricmp( pSubkeyName, "elem" ) )
  568. {
  569. ParseExplicitElem( pSubKey, parseParm );
  570. }
  571. else if ( !V_stricmp( pSubkeyName, "sphere" ) || !V_stricmp( pSubkeyName, "in_sphere" ) || !V_stricmp( pSubkeyName, "ex_sphere" ) )
  572. {
  573. ParseExplicitColl( pSubKey, parseParm, m_CollisionSpheres );
  574. }
  575. else if ( !V_stricmp( pSubkeyName, "plane" ) )
  576. {
  577. ParseExplicitColl( pSubKey, parseParm, m_CollisionPlanes );
  578. }
  579. }
  580. }
  581. void CAuthClothParser::ParseExplicitNode( KeyValues *kv, CBoneParseParams &parseParm )
  582. {
  583. CBone bone;
  584. bone.m_Name = kv->GetString( "name" );
  585. parseParm.ApplyDefaultParams( bone );
  586. FindKey( kv, "mass", &bone.m_flMass );
  587. FindKey( kv, "gravity", &bone.m_Integrator.flGravity );
  588. FindKey( kv, "damping", &bone.m_Integrator.flPointDamping );
  589. if ( const char *pParent = kv->GetString( "parent", NULL ) )
  590. {
  591. bone.m_nParent = FindNodeByName( pParent );
  592. if ( bone.m_nParent < 0 )
  593. {
  594. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Explicit Node %s - cannot find parent %s\n", bone.m_Name.Get( ), pParent );
  595. }
  596. }
  597. if ( const char *pFollowParent = kv->GetString( "followParent", NULL ) )
  598. {
  599. bone.m_nFollowParent = FindNodeByName( pFollowParent );
  600. if ( bone.m_nFollowParent < 0 )
  601. {
  602. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Explicit Node %s - cannot find followParent %s\n", bone.m_Name.Get( ), pFollowParent );
  603. }
  604. }
  605. if ( FindKey( kv, "followWeight", &bone.m_flFollowWeight ) && bone.m_flFollowWeight > 0 && bone.m_nParent >= 0 && bone.m_nFollowParent < 0 )
  606. {
  607. // if there's parent, followWeight, but no followParent, we can assume followParent to be = parent
  608. bone.m_nFollowParent = bone.m_nParent;
  609. }
  610. if ( const char *pOffset = kv->GetString( "offset", NULL ) )
  611. {
  612. Vector vOffset;
  613. if ( sscanf( pOffset, "%g %g %g", &vOffset.x, &vOffset.y, &vOffset.z ) != 3 )
  614. {
  615. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Explicit node %s - cannot parse offset %s\n", bone.m_Name.Get( ), pOffset );
  616. }
  617. else if ( bone.m_nParent < 0 )
  618. {
  619. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Explicit node %s has offset %s but no parent\n", bone.m_Name.Get( ), pOffset );
  620. }
  621. else
  622. {
  623. const CTransform &parentXform = m_Nodes[ bone.m_nParent ].m_Transform;
  624. bone.m_Transform.m_orientation = parentXform.m_orientation;
  625. bone.m_Transform.m_vPosition = TransformPoint( parentXform, vOffset );
  626. }
  627. }
  628. if ( const char *pFlags = kv->GetString( "flags", NULL ) )
  629. {
  630. parseParm.ApplyDotaFlags( bone, pFlags );
  631. }
  632. if ( kv->GetBool( "static" ) )
  633. {
  634. bone.m_bSimulated = false;
  635. bone.m_bFreeRotation = false;
  636. }
  637. if ( const char *pLock = kv->GetString( "lock", NULL ) )
  638. {
  639. CUtlStringList tokens( pLock, g_pTokenSeparators, ARRAYSIZE( g_pTokenSeparators ) );
  640. for ( int i = 0; i < tokens.Count( ); ++i )
  641. {
  642. if ( !V_stricmp( tokens[ i ], "position" ) )
  643. {
  644. bone.m_bSimulated = false;
  645. }
  646. else if ( !V_stricmp( tokens[ i ], "rotation" ) )
  647. {
  648. bone.m_bFreeRotation = false;
  649. }
  650. else
  651. {
  652. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Invalid lock parameter %s in bone %s, ignoring\n", tokens[ i ], bone.m_Name.Get() );
  653. }
  654. }
  655. }
  656. if ( !bone.m_bFreeRotation && bone.m_bSimulated )
  657. {
  658. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Bone %s rotation is locked, but the bone is simulated, which is not useful\n", bone.m_Name.Get( ) );
  659. }
  660. int nNewNode = m_Nodes.AddToTail( bone );
  661. TieModelToNewNode( nNewNode );
  662. }
  663. void CAuthClothParser::ParseExplicitElem( KeyValues *kv, CBoneParseParams &parseParm )
  664. {
  665. const char *pNodes = kv->GetString( "nodes", NULL );
  666. if ( !pNodes )
  667. {
  668. pNodes = kv->GetString( );
  669. }
  670. if ( pNodes )
  671. {
  672. CUtlStringList tokens( pNodes, g_pTokenSeparators, ARRAYSIZE( g_pTokenSeparators ) );
  673. CQuad quad;
  674. int nNodeCount = 0;
  675. for ( int i = 0; i < tokens.Count( ); ++i )
  676. {
  677. int nNode = FindNodeByName( tokens[ i ] );
  678. if ( nNode >= 0 )
  679. {
  680. quad.m_nNodes[ nNodeCount ] = nNode;
  681. ++nNodeCount;
  682. }
  683. else
  684. {
  685. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cloth cannot find element node '%s', ignoring!\n", tokens[ i ] );
  686. }
  687. }
  688. if ( nNodeCount >= 3 )
  689. {
  690. for ( int i = nNodeCount; i < 4; ++i )
  691. quad.m_nNodes[ i ] = quad.m_nNodes[ nNodeCount - 1 ];
  692. m_Quads.AddToTail( quad );
  693. }
  694. else
  695. {
  696. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cloth element must have 3 or 4 nodes: %s\n", pNodes );
  697. }
  698. }
  699. }
  700. // Return: true if the node was successfully tied to an existing bone
  701. bool CAuthClothParser::TieModelToNewNode( int nNewNode )
  702. {
  703. CBone &node = m_Nodes[ nNewNode ];
  704. UtlSymId_t nFindBone = m_BoneToIndex.Find( node.m_Name.Get( ) );
  705. if ( nFindBone != UTL_INVAL_SYMBOL )
  706. {
  707. int nBoneIndex = m_BoneToIndex[ nFindBone ];
  708. node.m_Transform = m_BoneTransforms[ nBoneIndex ];
  709. // this bone hasn't been mapped to an earlier node, it's original bone-node, map it now
  710. m_ModelBoneToNode[ nBoneIndex ] = nNewNode;
  711. return true;
  712. }
  713. else
  714. {
  715. // we'll figure the parent out later
  716. node.m_bVirtual = true; // this node is virtual. it has no bone name.
  717. return false;
  718. }
  719. }
  720. bool CAuthClothParser::ParseLegacyDotaNodeGrid( KeyValues *pSubKey, CBoneParseParams &parseParm, const CNodeIdx &nodeIdx )
  721. {
  722. CVarBitVec nodesInitialized( m_Nodes.Count( ) );
  723. // add the grid (or one strand) of Nodes
  724. for ( int nColumn = 0; nColumn < parseParm.m_nVirtColumnCount; ++nColumn )
  725. {
  726. for ( int nRow = 0; nRow < parseParm.m_nRowCount; ++nRow )
  727. {
  728. const char *pRowFlags = GetClothString( pSubKey, CFmtStr( "r%d", nRow ).Get( ) );
  729. const char *pColumnFlags = GetClothString( pSubKey, CFmtStr( "c%d", nColumn ).Get( ) );
  730. const char *pNodeFlags = GetClothString( pSubKey, CFmtStr( "r%dc%d", nRow, nColumn ).Get( ) );
  731. int nNewNode = nodeIdx( nRow, nColumn );
  732. CBone &node = m_Nodes[ nNewNode ];
  733. // There is no possibility to create free-rotating static nodes in dota2 source1
  734. node.m_bFreeRotation = false;
  735. // then, apply sequentially settings: per column; per individual node
  736. parseParm.m_nBoneIndex = nNewNode;
  737. if ( parseParm.m_bIsRopeS1 && nColumn )
  738. {
  739. parseParm.m_bPrevColumnParent = true; // for the ropes, we parent virtual column #1 to the previous column #0
  740. parseParm.m_vOffset = Vector( 0, -20, 0 ); // NOTE: OS offset!
  741. node.m_bOsOffset = true;
  742. node.m_bVirtual = true;
  743. }
  744. else
  745. {
  746. parseParm.m_bPrevColumnParent = false;
  747. parseParm.m_vOffset = vec3_origin;
  748. }
  749. node.m_Name.Format( "%sr%dc%d", parseParm.m_pBonePrefix, nRow, nColumn );
  750. if ( nRow > 0 && node.m_bSimulated )
  751. {
  752. // note: following happensin Dota2 Source1 in CClothModelPiece::SetupBone() in cloth_system.cpp#36:3412, the whole column follows the static parent
  753. // this happens for ropes and cloths.
  754. node.m_nFollowParent = nodeIdx( 0, parseParm.m_bIsRopeS1 ? 0 : nColumn );
  755. node.m_flFollowWeight = nRow * ( parseParm.m_flFollowRootEnd - parseParm.m_flFollowRootBegin ) / ( parseParm.m_nRowCount - 1 ) + parseParm.m_flFollowRootBegin;
  756. }
  757. if ( !TieModelToNewNode( nNewNode ) )
  758. {
  759. //node.m_Name += "(virtual)"; // the name doesn't matter, except for debugging, so marking it clearly as a virtual bone name
  760. }
  761. parseParm.ApplyDefaultParams( node );
  762. parseParm.ApplyDotaFlags( node, pRowFlags );
  763. parseParm.ApplyDotaFlags( node, pColumnFlags );
  764. parseParm.ApplyDotaFlags( node, pNodeFlags );
  765. if ( !node.m_bSimulated )
  766. {
  767. node.m_Integrator.flPointDamping = parseParm.m_flFixedPointDamping * 60; // Dota cloth didn't take variability of time into account, we will
  768. }
  769. // pre-divide by nominal mass
  770. if ( node.m_flMass > 0.0333f )
  771. {
  772. node.m_Integrator.flAnimationForceAttraction /= node.m_flMass;
  773. if ( node.m_bSimulated )
  774. {
  775. // NOTE: the damping gets divided by mass later in the FE model builder
  776. // node.m_Integrator.flPointDamping /= node.m_flMass;
  777. }
  778. }
  779. if ( parseParm.m_bPrevColumnParent )
  780. {
  781. if ( nColumn > 0 )
  782. {
  783. node.m_nParent = nodeIdx( nRow, nColumn - 1 );
  784. }
  785. }
  786. else
  787. {
  788. if ( nRow > 0 )
  789. {
  790. node.m_nParent = nodeIdx( nRow - 1, nColumn );
  791. }
  792. }
  793. if ( node.m_bVirtual )
  794. {
  795. if ( node.m_nParent < 0 )
  796. {
  797. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Virtual particle %s has no parent to latch onto\n", node.m_Name.Get( ) );
  798. return false; // we tried to find a parent bone , but there was none. We can't have a virtual particle without a clear parent bone to at least define its relaxed position
  799. }
  800. if ( !nodesInitialized.IsBitSet( node.m_nParent ) )
  801. {
  802. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Virtual particle %s has parent that hasn't been initialized yet, ordering problem\n", node.m_Name.Get( ) );
  803. return false;
  804. }
  805. if ( parseParm.m_vOffset.Length( ) < 1e-6f )
  806. {
  807. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Virtual particle %s has no offset to its parent %s, it's useless, please just reuse %s\n", node.m_Name.Get( ), m_Nodes[ node.m_nParent ].m_Name.Get( ), node.m_Name.Get( ) );
  808. }
  809. const CTransform &parentXform = m_Nodes[ node.m_nParent ].m_Transform;
  810. node.m_Transform.m_orientation = parentXform.m_orientation;
  811. if ( node.m_bOsOffset )
  812. {
  813. node.m_Transform.m_vPosition = parentXform.m_vPosition + parseParm.m_vOffset;
  814. }
  815. else
  816. {
  817. node.m_Transform.m_vPosition = TransformPoint( parentXform, parseParm.m_vOffset );
  818. }
  819. }
  820. nodesInitialized.Set( nNewNode );
  821. }
  822. }
  823. return true;
  824. }
  825. void CAuthClothParser::AddDotaRod( uint nNode0, uint nNode1, CBoneParseParams &parseParm )
  826. {
  827. AddRod( m_Nodes, nNode0, nNode1, parseParm.m_flRelaxationFactor );
  828. // springs do not evaluate in Source1: the only call to Evaluate is after a return;
  829. // springs do ResolveStretch in Source1, but the effect is simply applying velocity of vDir * ( ( flDistance - flRestLength ) * flStretchiness * flStretchForce) in the direction vDir of fixing up the rod.
  830. // This can be accounted for in the node damping: since all nodes in a cloth piece have the same flStretchiness * flStretchForce, and verlet integrator will by default cause the delta velocity = 60 * ( flDistance - flRestLength )
  831. //m_Springs.AddToTail( CSpring( nNode0, nNode1, parseParm.m_flStructSpringConstant, parseParm.m_flStructSpringDamping, parseParm.m_flStretchForce, parseParm.m_flRelaxationFactor ) );
  832. }
  833. bool CAuthClothParser::CreateLegacyDotaRodGrid( CBoneParseParams &parseParm, const CNodeIdx &nodeIdx, int nCompatibilityMode )
  834. {
  835. // add finite elements (either rods or quads) that will simulate the cloth
  836. if ( parseParm.m_nVirtColumnCount == 1 )
  837. {
  838. // special case, single-strand cloth (rope)
  839. for ( int nRow = 1; nRow < parseParm.m_nRowCount; ++nRow )
  840. {
  841. uint nNode0 = nodeIdx( nRow - 1, 0 ), nNode1 = nodeIdx( nRow, 0 );
  842. AddDotaRod( nNode0, nNode1, parseParm );
  843. if ( nRow >= 2 && /*bUseBendSprings*/false )
  844. {
  845. uint nNodePrev = nodeIdx( nRow - 2, 0 );
  846. AddDotaRod( nNodePrev, nNode1, parseParm );
  847. }
  848. }
  849. }
  850. else
  851. {
  852. for ( int nRow = 1; nRow < parseParm.m_nRowCount; ++nRow )
  853. {
  854. for ( int nColumn = 1; nColumn < parseParm.m_nVirtColumnCount; ++nColumn )
  855. {
  856. uint nNode00 = nodeIdx( nRow - 1, nColumn - 1 );
  857. uint nNode01 = nodeIdx( nRow - 1, nColumn );
  858. uint nNode11 = nodeIdx( nRow, nColumn );
  859. uint nNode10 = nodeIdx( nRow, nColumn - 1 );
  860. if ( m_nCompatibilityMode == 0 || ( m_nCompatibilityMode == 1 && nRow >= 2 ) )
  861. {
  862. CQuad &quad = m_Quads[ m_Quads.AddToTail( ) ];
  863. quad.m_nNodes[ 0 ] = nNode00;
  864. quad.m_nNodes[ 1 ] = nNode01;
  865. quad.m_nNodes[ 2 ] = nNode11;
  866. quad.m_nNodes[ 3 ] = nNode10;
  867. // in compat 1, we skip the first row of quads; in compat 2+, we skip all quads
  868. }
  869. else
  870. {
  871. // for compatibility with previous version of the solver, simplify to rods
  872. //
  873. // "Structural springs", in source1 cloth lingo
  874. //
  875. AddDotaRod( nNode00, nNode01, parseParm );
  876. AddDotaRod( nNode00, nNode10, parseParm );
  877. if ( nColumn + 1 == parseParm.m_nVirtColumnCount )
  878. {
  879. // last column
  880. AddDotaRod( nNode01, nNode11, parseParm );
  881. }
  882. if ( nRow + 1 == parseParm.m_nRowCount )
  883. {
  884. // last row
  885. AddDotaRod( nNode10, nNode11, parseParm );
  886. }
  887. //
  888. // "Shear springs"
  889. //
  890. if ( false )
  891. {
  892. AddDotaRod( nNode00, nNode11, parseParm );
  893. AddDotaRod( nNode01, nNode10, parseParm );
  894. }
  895. }
  896. }
  897. }
  898. CreateGridNodeBases( nodeIdx, parseParm.m_nRowCount, parseParm.m_nVirtColumnCount, m_Nodes, m_PresetNodeBases );
  899. }
  900. return true;
  901. }
  902. static const char *s_pszTokenDelimiter = " ,|\t\r\n";
  903. bool FloatToken( float &x )
  904. {
  905. const char *pToken = strtok( NULL, s_pszTokenDelimiter );
  906. if ( !pToken )
  907. return false;
  908. x = atof( pToken );
  909. return true;
  910. }
  911. bool VectorToken( Vector &v )
  912. {
  913. return FloatToken( v.x ) && FloatToken( v.y ) && FloatToken( v.z );
  914. }
  915. bool KeywordToken( const char *pKeyword )
  916. {
  917. const char *pToken = strtok( NULL, s_pszTokenDelimiter );
  918. if ( !pToken )
  919. return false;
  920. return V_stricmp( pToken, pKeyword ) == 0;
  921. }
  922. bool CBoneParseParams::ApplyDotaFlags( CAuthPhysFx::CBone &bone, const char *pszParms )
  923. {
  924. if ( pszParms[ 0 ] == 0 )
  925. {
  926. return true;
  927. }
  928. int nParamLength = V_strlen( pszParms );
  929. if ( nParamLength > 1024 )
  930. {
  931. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Keyvalue parameter too long in %s\n", bone.m_Name.Get( ) );
  932. return false;
  933. }
  934. char *pszParmsCopy = ( char * ) stackalloc( nParamLength + 1 );
  935. if ( pszParmsCopy != NULL )
  936. {
  937. V_strcpy( pszParmsCopy, pszParms );
  938. char *pszToken = strtok( pszParmsCopy, s_pszTokenDelimiter );
  939. while ( pszToken != NULL )
  940. {
  941. if ( V_stricmp( pszToken, "fixed" ) == 0 )
  942. {
  943. bone.m_bSimulated = false;
  944. }
  945. else if ( V_stricmp( pszToken, "world" ) == 0 )
  946. {
  947. bone.m_bNeedsWorldCollision = true;
  948. }
  949. else if ( V_stricmp( pszToken, "mass" ) == 0 )
  950. {
  951. float flValue;
  952. if( FloatToken( flValue ) )
  953. {
  954. if ( flValue > 0.0f )
  955. {
  956. bone.m_flMass = flValue;
  957. bone.m_bHasMassOverride = true;
  958. bone.m_bSimulated = true;
  959. }
  960. else
  961. {
  962. bone.m_flMass = 1.0f; // take default mass; todo: find out what "mass 0" is actually supposed to mean
  963. bone.m_bHasMassOverride = false;
  964. bone.m_bSimulated = true;
  965. }
  966. }
  967. else
  968. {
  969. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse mass in %s\n", bone.m_Name.Get() );
  970. return false;
  971. }
  972. }
  973. else if ( V_stricmp( pszToken, "damping" ) == 0 )
  974. {
  975. if ( !FloatToken( bone.m_Integrator.flPointDamping ) )
  976. {
  977. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse damping in %s\n", bone.m_Name.Get( ) );
  978. return false;
  979. }
  980. }
  981. else if ( V_stricmp( pszToken, "gravity" ) == 0 )
  982. {
  983. if ( !FloatToken( bone.m_Integrator.flGravity ) )
  984. {
  985. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse gravity in %s\n", bone.m_Name.Get( ) );
  986. return false;
  987. }
  988. }
  989. else if ( V_stricmp( pszToken, "offsetx" ) == 0 )
  990. {
  991. if ( !FloatToken( m_vOffset.x ) )
  992. {
  993. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse offsetx in %s\n", bone.m_Name.Get( ) );
  994. return false;
  995. }
  996. }
  997. else if ( V_stricmp( pszToken, "offsety" ) == 0 )
  998. {
  999. if ( !FloatToken( m_vOffset.y ) )
  1000. {
  1001. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse offsety in %s\n", bone.m_Name.Get( ) );
  1002. return false;
  1003. }
  1004. }
  1005. else if ( V_stricmp( pszToken, "offsetz" ) == 0 )
  1006. {
  1007. if ( !FloatToken( m_vOffset.z ) )
  1008. {
  1009. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse offsetz in %s\n", bone.m_Name.Get( ) );
  1010. return false;
  1011. }
  1012. }
  1013. else if ( V_stricmp( pszToken, "prev_col_parent" ) == 0 )
  1014. {
  1015. m_bPrevColumnParent = true;
  1016. }
  1017. else if ( V_stricmp( pszToken, "prev_row_parent" ) == 0 )
  1018. {
  1019. m_bPrevColumnParent = false;
  1020. }
  1021. else if ( V_stricmp( pszToken, "stabilizeAnim" ) == 0 )
  1022. {
  1023. if ( !FloatToken( bone.m_flFollowWeight ) )
  1024. {
  1025. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse stabilizeAnim in %s\n", bone.m_Name.Get( ) );
  1026. return false;
  1027. }
  1028. }
  1029. else if ( V_stricmp( pszToken, "in_sphere" ) == 0 || V_stricmp( pszToken, "ex_sphere" ) == 0 )
  1030. {
  1031. CCollisionSphere ce;
  1032. if ( !VectorToken( ce.m_vOrigin ) || !KeywordToken( "r" ) || !FloatToken( ce.m_flRadius ) )
  1033. {
  1034. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse %s in %s\n", pszToken, bone.m_Name.Get( ) );
  1035. return false;
  1036. }
  1037. ce.m_bInclusive = V_stricmp( pszToken, "in_sphere" ) == 0;
  1038. ce.m_nChildBone = ce.m_nParentBone = m_nBoneIndex;
  1039. m_pCollisionSpheres->AddToTail( ce );
  1040. }
  1041. else if ( V_stricmp( pszToken, "plane" ) == 0 )
  1042. {
  1043. CCollisionPlane cp;
  1044. if ( !VectorToken( cp.m_Plane.m_vNormal ) || !KeywordToken( "d" ) || !FloatToken( cp.m_Plane.m_flOffset ) )
  1045. {
  1046. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse %s in %s\n", pszToken, bone.m_Name.Get( ) );
  1047. return false;
  1048. }
  1049. cp.m_nChildBone = cp.m_nParentBone = m_nBoneIndex;
  1050. m_pCollisionPlanes->AddToTail( cp );
  1051. }
  1052. /*else if ( V_stricmp( pszToken, "0" ) == 0 )
  1053. {
  1054. bone.m_flMass = 1.0f; // take default mass; it seems impossible now to find out what "mass 0" was actually supposed to mean originally
  1055. bone.m_bHasMassOverride = false;
  1056. bone.m_bSimulated = true;
  1057. }*/
  1058. else
  1059. {
  1060. float flValue = V_atof( pszToken );
  1061. if ( flValue != 0.0f )
  1062. {
  1063. // <sergiy> just a number is ignored in source1; Some artists (e.g. JO) thought it should means mass, but it doesn't mean anything in source1 cloth, so for compatibility I'll ignore it here, too
  1064. //bone.m_flMass = flValue;
  1065. //bone.m_bHasMassOverride = true;
  1066. //bone.m_bSimulated = true;
  1067. }
  1068. else
  1069. {
  1070. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING " Cannot parse %s in %s\n", pszToken, bone.m_Name.Get( ) );
  1071. return false;
  1072. }
  1073. }
  1074. if ( pszToken )
  1075. {
  1076. pszToken = strtok( NULL, s_pszTokenDelimiter );
  1077. }
  1078. }
  1079. }
  1080. return true;
  1081. }
  1082. int CAuthPhysFx::GetSimParticleCount()const
  1083. {
  1084. int nCount = 0;
  1085. for( int nNode = 0; nNode < m_Nodes.Count(); ++nNode )
  1086. {
  1087. if( m_Nodes[nNode].m_bSimulated )
  1088. {
  1089. nCount++;
  1090. }
  1091. }
  1092. return nCount;
  1093. }
  1094. float CAuthPhysFx::GetRodLength( int nRod )const
  1095. {
  1096. const CRod & rod = m_Rods[ nRod ];
  1097. if ( rod.m_bExplicitLength )
  1098. {
  1099. return rod.m_flLength;
  1100. }
  1101. else
  1102. {
  1103. return ( m_Nodes[ rod.m_nNodes[ 0 ] ].m_Transform.m_vPosition - m_Nodes[ rod.m_nNodes[ 1 ] ].m_Transform.m_vPosition ).Length(); // implicit length
  1104. }
  1105. }
  1106. bool CAuthPhysFx::IsSimilarTo( const CFeModel *pFeModel )const
  1107. {
  1108. if ( !pFeModel )
  1109. {
  1110. return IsEmpty();
  1111. }
  1112. if ( int( pFeModel->m_nNodeCount ) != m_Nodes.Count() )
  1113. return false;
  1114. if ( int( pFeModel->m_nQuadCount ) != m_Quads.Count() )
  1115. return false;
  1116. if ( int( pFeModel->m_nRodCount ) != m_Rods.Count() )
  1117. return false;
  1118. if ( int( pFeModel->m_nCtrlOffsets ) != m_CtrlOffsets.Count() )
  1119. return false;
  1120. for ( int i = 0; i < m_Nodes.Count(); ++i )
  1121. {
  1122. if ( m_Nodes[ i ].m_Name != pFeModel->GetNodeName( i ) )
  1123. return false;
  1124. }
  1125. return true;
  1126. }
  1127. CFeModelBuilder::BuildNode_t CAuthPhysFx::CBone::AsBuildNode()const
  1128. {
  1129. CFeModelBuilder::BuildNode_t node;
  1130. const CBone &bone = *this;
  1131. node.pName = bone.m_Name.Get();
  1132. node.transform = bone.m_Transform;
  1133. node.nParent = bone.m_nParent;
  1134. node.nFollowParent = bone.m_nFollowParent;
  1135. node.flFollowWeight = bone.m_flFollowWeight;
  1136. if ( bone.m_bSimulated && bone.m_flMass > 0 )
  1137. {
  1138. node.flMassMultiplier = bone.m_flMass;
  1139. node.invMass = 1.0f / bone.m_flMass;
  1140. node.bSimulated = true;
  1141. node.bForceSimulated = bone.m_bForceSimulated;
  1142. }
  1143. else
  1144. {
  1145. node.flMassMultiplier = 0;
  1146. node.invMass = 0;
  1147. node.bSimulated = false;
  1148. }
  1149. node.flMassBias = bone.m_flMassBias;
  1150. node.bFreeRotation = bone.m_bFreeRotation;
  1151. node.bAnimRotation = bone.m_bAnimRotation;
  1152. node.bVirtual = bone.m_bVirtual;
  1153. node.bNeedNodeBase = bone.m_bNeedNodeBase;
  1154. node.bOsOffset = bone.m_bOsOffset;
  1155. node.bMassMultiplierGlobal = bone.m_bHasMassOverride;
  1156. node.flSlack = 0;
  1157. node.integrator = bone.m_Integrator;
  1158. node.flLegacyStretchForce = bone.m_flLegacyStretchForce;
  1159. node.bWorldCollision = bone.m_bNeedsWorldCollision;
  1160. node.flWorldFriction = bone.m_flWorldFriction;
  1161. node.flGroundFriction = bone.m_flGroundFriction;
  1162. node.flCollisionRadius = bone.m_flCollisionRadius;
  1163. node.flLocalRotation = bone.m_flLocalRotation;
  1164. node.flLocalForce = bone.m_flLocalForce;
  1165. node.nCollisionMask = bone.m_nCollisionMask;
  1166. return node;
  1167. }
  1168. CFeModelBuilder::BuildElem_t CAuthPhysFx::CQuad::AsBuildElem() const
  1169. {
  1170. CFeModelBuilder::BuildElem_t elem;
  1171. const CAuthPhysFx::CQuad &quad = *this;
  1172. elem.nNode[ 0 ] = quad.m_nNodes[ 0 ];
  1173. elem.nNode[ 1 ] = quad.m_nNodes[ 1 ];
  1174. elem.nNode[ 2 ] = quad.m_nNodes[ 2 ];
  1175. elem.nNode[ 3 ] = quad.m_nNodes[ 3 ];
  1176. elem.nStaticNodes = 0;
  1177. elem.nRank = 0;
  1178. elem.flSlack = 0;
  1179. return elem;
  1180. }
  1181. CLockedResource< PhysFeModelDesc_t > CAuthPhysFx::Compile( CResourceStream *pStream, const CVClothProxyMeshOptions *pOptions )const
  1182. {
  1183. CFeModelBuilder builder;
  1184. if ( pOptions )
  1185. {
  1186. builder.m_bNeedBacksolvedBasesOnly = pOptions->m_bDriveMeshesWithBacksolvedJointsOnly;
  1187. }
  1188. // Note: for now I'm skipping (pre-applying) the mapping from ctrl to node;
  1189. // that mapping will be set to NULL. So, the indices and count of nodes in builder.m_Nodes and m_NOdes is different
  1190. builder.EnableIdentityCtrlOrder( );
  1191. const int nParentBodyCount = /*pParent->GetBodyCount()*/ 0;
  1192. builder.m_Nodes.EnsureCapacity( m_Nodes.Count() + nParentBodyCount );
  1193. builder.m_Nodes.SetCount( m_Nodes.Count() );
  1194. builder.m_Elems.EnsureCapacity( m_Quads.Count( ) );
  1195. builder.m_Springs.EnsureCapacity( m_Springs.Count( ) );
  1196. builder.m_Rods.EnsureCapacity( m_Rods.Count( ) );
  1197. builder.m_PresetNodeBases.CopyArray( m_PresetNodeBases.Base( ), m_PresetNodeBases.Count() );
  1198. builder.m_TaperedCapsuleStretches.CopyArray( m_TaperedCapsuleStretches.Base(), m_TaperedCapsuleStretches.Count() );
  1199. builder.m_TaperedCapsuleRigids.CopyArray( m_TaperedCapsuleRigids.Base(), m_TaperedCapsuleRigids.Count() );
  1200. builder.m_SphereRigids.CopyArray( m_SphereRigids.Base(), m_SphereRigids.Count() );
  1201. builder.m_CtrlOffsets.SetCount( m_CtrlOffsets.Count() );
  1202. for ( int nCtrlOffset = 0; nCtrlOffset < m_CtrlOffsets.Count(); ++nCtrlOffset )
  1203. {
  1204. builder.m_CtrlOffsets[ nCtrlOffset ] = m_CtrlOffsets[ nCtrlOffset ];
  1205. }
  1206. builder.m_FitInfluences.CopyArray( m_FitInfluences.Base(), m_FitInfluences.Count() );
  1207. for ( int nNode = 0; nNode < m_Nodes.Count(); ++nNode )
  1208. {
  1209. builder.m_Nodes[ nNode ] = m_Nodes[ nNode ].AsBuildNode();
  1210. }
  1211. for ( int nQuad = 0; nQuad < m_Quads.Count(); ++nQuad )
  1212. {
  1213. const CQuad &quad = m_Quads[ nQuad ];
  1214. CFeModelBuilder::BuildElem_t elem = quad.AsBuildElem();
  1215. uint numNodes = elem.NumNodes();
  1216. BubbleSort( elem.nNode, numNodes, [&builder] ( uint left, uint right ){
  1217. return builder.m_Nodes[ left ].invMass < builder.m_Nodes[ right ].invMass;
  1218. } );
  1219. elem.nNode[ 3 ] = elem.nNode[ numNodes - 1 ];
  1220. for ( elem.nStaticNodes = 0; elem.nStaticNodes < numNodes && builder.m_Nodes[ elem.nNode[ elem.nStaticNodes ] ].invMass == 0; ++elem.nStaticNodes )
  1221. continue;
  1222. if ( quad.m_bUseRods )
  1223. {
  1224. // convert this to rods?
  1225. }
  1226. builder.m_Elems.AddToTail( elem ); // if elem has 3 or 4 static nodes, the builder will skip it; builder will also sort everything as needed
  1227. }
  1228. for ( int nRod = 0; nRod < m_Rods.Count( ); ++nRod )
  1229. {
  1230. const CRod &source = m_Rods[ nRod ];
  1231. FeRodConstraint_t rod;
  1232. rod.nNode[ 0 ] = source.m_nNodes[ 0 ];
  1233. rod.nNode[ 1 ] = source.m_nNodes[ 1 ];
  1234. rod.flRelaxationFactor = source.m_flRelaxationFactor;
  1235. CFeModelBuilder::BuildNode_t &n0 = builder.m_Nodes[ rod.nNode[ 0 ] ], &n1 = builder.m_Nodes[ rod.nNode[ 1 ] ];
  1236. rod.flMaxDist = source.m_bExplicitLength ? source.m_flLength : ( n0.transform.m_vPosition - n1.transform.m_vPosition ).Length( );
  1237. rod.flMinDist = source.m_flContractionFactor * rod.flMaxDist; // Trying to match source1, where min rod distance is 0, but also improve on it a bit, we don't really want cloth to ever collapse
  1238. float sumInvMass = source.m_flMotionBias[ 0 ] * n0.invMass + source.m_flMotionBias[ 1 ] * n1.invMass;
  1239. Assert( rod.flRelaxationFactor >= 0 && rod.flRelaxationFactor <= 1.0f );
  1240. if ( sumInvMass > 1e-6f && rod.flRelaxationFactor >= 0.0033f ) // we need to have non-zero relaxation factor for this rod to make any sense to compute; otherwise it has no effect. And we shouldn't have any relaxation facotrs outside (0,1) interval, ever
  1241. {
  1242. rod.flWeight0 = source.m_flMotionBias[ 0 ] * n0.invMass / sumInvMass;
  1243. builder.m_Rods.AddToTail( rod );
  1244. }
  1245. }
  1246. for ( int nSpring = 0; nSpring < m_Springs.Count( ); ++nSpring )
  1247. {
  1248. CFeModelBuilder::BuildSpring_t out;
  1249. const CSpring &in = m_Springs[ nSpring ];
  1250. out.nNode[ 0 ] = in.m_nNodes[ 0 ];
  1251. out.nNode[ 1 ] = in.m_nNodes[ 1 ];
  1252. out.flSpringConstant = in.m_flSpringConstant;
  1253. out.flSpringDamping = in.m_flSpringDamping;
  1254. // note: Stretchiness is not implemented!
  1255. out.flStretchiness = in.m_flStretchiness;
  1256. builder.m_Springs.AddToTail( out );
  1257. }
  1258. builder.m_CollisionSpheres.SetCount( m_CollisionSpheres.Count( ) );
  1259. for ( int nCollSphere = 0; nCollSphere < m_CollisionSpheres.Count( ); ++nCollSphere )
  1260. {
  1261. const CCollisionSphere &source = m_CollisionSpheres[ nCollSphere ];
  1262. CFeModelBuilder::BuildCollisionSphere_t &sphere = builder.m_CollisionSpheres[ nCollSphere ];
  1263. sphere.m_bInclusive = source.m_bInclusive;
  1264. sphere.m_nChild = source.m_nChildBone;
  1265. sphere.m_nParent = source.m_nParentBone;
  1266. sphere.m_vOrigin = source.m_vOrigin;
  1267. sphere.m_flRadius = source.m_flRadius;
  1268. sphere.m_flStickiness = source.m_flStickiness;
  1269. }
  1270. builder.m_CollisionPlanes.SetCount( m_CollisionPlanes.Count( ) );
  1271. for ( int nCollisionPlane = 0; nCollisionPlane < m_CollisionPlanes.Count( ); ++nCollisionPlane )
  1272. {
  1273. const CCollisionPlane &source = m_CollisionPlanes[ nCollisionPlane ];
  1274. CFeModelBuilder::BuildCollisionPlane_t &plane = builder.m_CollisionPlanes[ nCollisionPlane ];
  1275. plane.m_nChild = source.m_nChildBone;
  1276. plane.m_nParent = source.m_nParentBone;
  1277. plane.m_Plane = source.m_Plane;
  1278. plane.m_flStickiness = source.m_flStickiness;
  1279. AssertDbg( uint( plane.m_nChild ) < uint( m_Nodes.Count() ) && uint( plane.m_nParent ) < uint( m_Nodes.Count() ) );
  1280. }
  1281. if ( m_bForceWorldCollisionOnAllNodes )
  1282. {
  1283. for ( CFeModelBuilder::BuildNode_t &node: builder.m_Nodes )
  1284. {
  1285. if ( !node.bWorldCollision )
  1286. {
  1287. node.bWorldCollision = true;
  1288. node.flWorldFriction = m_flDefaultWorldCollisionPenetration;
  1289. node.flGroundFriction = m_flDefaultGroundFriction;
  1290. }
  1291. }
  1292. }
  1293. builder.m_flDefaultSurfaceStretch = m_flDefaultSurfaceStretch;
  1294. builder.m_flDefaultThreadStretch = m_flDefaultThreadStretch;
  1295. builder.m_flDefaultGravityScale = m_flDefaultGravityScale;
  1296. builder.m_flDefaultVelAirDrag = m_flDefaultVelAirDrag;
  1297. builder.m_flDefaultExpAirDrag = m_flDefaultExpAirDrag;
  1298. builder.m_flDefaultVelQuadAirDrag = m_flDefaultVelQuadAirDrag;
  1299. builder.m_flDefaultExpQuadAirDrag = m_flDefaultExpQuadAirDrag;
  1300. builder.m_flDefaultVelRodAirDrag = m_flDefaultVelRodAirDrag;
  1301. builder.m_flDefaultExpRodAirDrag = m_flDefaultExpRodAirDrag;
  1302. builder.m_flQuadVelocitySmoothRate = m_flQuadVelocitySmoothRate;
  1303. builder.m_flRodVelocitySmoothRate = m_flRodVelocitySmoothRate;
  1304. builder.m_flWindage = m_flWindage;
  1305. builder.m_flWindDrag = m_flWindDrag;
  1306. builder.m_nQuadVelocitySmoothIterations = Max( 0, m_nQuadVelocitySmoothIterations );
  1307. builder.m_nRodVelocitySmoothIterations = Max( 0, m_nRodVelocitySmoothIterations );
  1308. builder.m_flAddWorldCollisionRadius = m_flAddWorldCollisionRadius;
  1309. builder.m_bAddStiffnessRods = m_bAddStiffnessRods;
  1310. builder.m_bRigidEdgeHinges = m_bRigidEdgeHinges;
  1311. builder.m_bUsePerNodeLocalForceAndRotation = m_bUsePerNodeLocalForceAndRotation;
  1312. builder.m_flLocalForce = m_flLocalForce;
  1313. builder.m_flLocalRotation = m_flLocalRotation;
  1314. builder.m_flDefaultVolumetricSolveAmount = m_flVolumetricSolveAmount;
  1315. builder.SetQuadBendTolerance( m_flQuadBendTolerance );
  1316. builder.EnableExplicitNodeMasses( m_bExplicitMasses );
  1317. builder.EnableUnitlessDamping( m_bUnitlessDamping );
  1318. if ( m_bFollowTheLead )
  1319. {
  1320. builder.m_nDynamicNodeFlags |= FE_FLAG_ENABLE_FTL;
  1321. }
  1322. if ( m_bCanCollideWithWorldCapsulesAndSpheres )
  1323. {
  1324. builder.m_nDynamicNodeFlags |= FE_FLAG_ENABLE_WORLD_SPHERE_COLLISION | FE_FLAG_ENABLE_WORLD_CAPSULE_COLLISION;
  1325. }
  1326. if( m_bCanCollideWithWorldMeshes )
  1327. {
  1328. builder.m_nDynamicNodeFlags |= FE_FLAG_ENABLE_WORLD_MESH_COLLISION;
  1329. }
  1330. if ( m_bCanCollideWithWorldHulls )
  1331. {
  1332. builder.m_nDynamicNodeFlags |= FE_FLAG_ENABLE_WORLD_HULL_COLLISION;
  1333. }
  1334. if ( m_bUninertialRods )
  1335. {
  1336. builder.m_nDynamicNodeFlags |= FE_FLAG_UNINERTIAL_CONSTRAINTS;
  1337. }
  1338. builder.Finish( false, m_flAddCurvature, 0 );
  1339. if ( builder.m_pLegacyStretchForce )
  1340. {
  1341. Assert( m_bUninertialRods );
  1342. }
  1343. CLockedResource< PhysFeModelDesc_t > pDesc;
  1344. if ( builder.m_nNodeCount > builder.m_nStaticNodes )
  1345. {
  1346. pDesc = Clone( &builder, pStream );
  1347. }
  1348. else if ( builder.m_nNodeCount )
  1349. {
  1350. Log_Warning( LOG_AUTH_PHYS, FUNCTION_LINE_STRING "Degenerate softbody with %u static nodes - ignoring, because there are no dynamic nodes to simulate\n", builder.m_nStaticNodes );
  1351. }
  1352. return pDesc;
  1353. }
  1354. struct VectorHash_t
  1355. {
  1356. uint operator()( const Vector &v ) const
  1357. {
  1358. uint32 *p = (uint32*)&v;
  1359. return p[0] ^ RotateBitsLeft32( p[1], 5 ) ^ RotateBitsLeft32( p[2], 10 );
  1360. }
  1361. };
  1362. struct VectorEqual_t
  1363. {
  1364. bool operator() ( const Vector &a, const Vector &b )const
  1365. {
  1366. return a.DistToSqr( b ) < 1e-12f;
  1367. }
  1368. };
  1369. class VertexDict
  1370. {
  1371. public:
  1372. VertexDict()
  1373. {
  1374. m_nVerts = 0;
  1375. }
  1376. int Add( const Vector &v )
  1377. {
  1378. UtlHashHandle_t h = m_Dict.Find( v );
  1379. if( h == m_Dict.InvalidHandle() )
  1380. {
  1381. m_Dict.Insert( v, m_nVerts );
  1382. return m_nVerts++;
  1383. }
  1384. else
  1385. {
  1386. return m_Dict.Element( h );
  1387. }
  1388. }
  1389. uint Count()
  1390. {
  1391. return m_nVerts;
  1392. }
  1393. protected:
  1394. CUtlHashtable< Vector, uint, VectorHash_t, VectorEqual_t > m_Dict;
  1395. uint m_nVerts;
  1396. };
  1397. const Quaternion GetRelativeRotationFromTwistAngles( const Vector &vTwist )
  1398. {
  1399. float tx = tanf( vTwist.x * 0.5f ), ty = tanf( vTwist.y * 0.5f ), tz = tanf( vTwist.z * 0.5f ), f = ( 1 + ty * ty ) * ( 1 + tz * tz );
  1400. Quaternion q;
  1401. q.w = 1 / sqrtf( ( 1 + tx * tx ) * f ); // there are 2 solutions: x,y,z,w and x,y,-z,-w where w>0
  1402. q.x = tx * q.w;
  1403. q.y = ty / sqrtf( 1 + ty * ty );
  1404. q.z = tz / sqrtf( f );
  1405. return q;
  1406. }
  1407. bool IsIn( const char *pName, const CUtlVector<CUtlString,CUtlMemory< CUtlString, int > > &choice )
  1408. {
  1409. for( int i = 0; i < choice.Count(); ++i )
  1410. {
  1411. if( !V_stricmp( choice[i], pName ) )
  1412. {
  1413. return true;
  1414. }
  1415. }
  1416. return false;
  1417. }
  1418. void AppendTo( CUtlVector< CUtlString> &appendTo, const CUtlVector< CUtlString > &appendFrom )
  1419. {
  1420. for( int i = 0; i < appendFrom.Count(); ++i )
  1421. {
  1422. if( appendTo.Find( appendFrom[i] ) < 0 )
  1423. {
  1424. appendTo.AddToTail( appendFrom[i] );
  1425. }
  1426. }
  1427. }
  1428. void CAuthPhysCollisionAttributes::ApplyOverride( const CAuthPhysCollisionAttributesOverride &collOverride )
  1429. {
  1430. switch( collOverride.m_nMode )
  1431. {
  1432. case AUTH_PHYS_COLL_ATTR_OVERRIDE:
  1433. {
  1434. if( !collOverride.m_CollisionGroup.IsEmpty() )
  1435. {
  1436. m_CollisionGroup = collOverride.m_CollisionGroup;
  1437. }
  1438. m_InteractAs = collOverride.m_InteractAs;
  1439. m_InteractWith = collOverride.m_InteractWith;
  1440. }
  1441. break;
  1442. case AUTH_PHYS_COLL_ATTR_APPEND:
  1443. {
  1444. if( !collOverride.m_CollisionGroup.IsEmpty() )
  1445. {
  1446. m_CollisionGroup = collOverride.m_CollisionGroup;
  1447. }
  1448. AppendTo( m_InteractAs, collOverride.m_InteractAs );
  1449. AppendTo( m_InteractWith, collOverride.m_InteractWith );
  1450. }
  1451. break;
  1452. }
  1453. }
  1454. int CAuthPhysCompileContext::ResolveCollisionAttributesIndex()
  1455. {
  1456. for( int i = 0; i < m_CollAttrPalette.Count(); ++i )
  1457. {
  1458. if( m_CollAttrPalette[i] == m_DefaultCollisionAttributes )
  1459. {
  1460. return i;
  1461. }
  1462. }
  1463. int nAdded = m_CollAttrPalette.AddToTail() ;
  1464. m_CollAttrPalette[nAdded] = m_DefaultCollisionAttributes;
  1465. return nAdded;
  1466. }
  1467. CLockedResource<char> CAuthPhysCompileContext::WriteString( const char *pString, uint32 *pHashOut )
  1468. {
  1469. if ( pHashOut )
  1470. {
  1471. uint32 nHash = MakeStringToken( pString ).GetHashCode( );
  1472. *pHashOut = nHash;
  1473. }
  1474. return FindOrWrite( pString, V_strlen( pString ) + 1 );
  1475. }
  1476. int CAuthPhysCompileContext::ResolveSurfacePropertyIndex()
  1477. {
  1478. for( int i = 0; i < m_SurfacePropPalette.Count(); ++i )
  1479. {
  1480. if( m_DefaultSurfaceProperty == m_SurfacePropPalette[i] )
  1481. return i;
  1482. }
  1483. int nNewEntry = m_SurfacePropPalette.AddToTail();
  1484. m_SurfacePropPalette[nNewEntry ] = m_DefaultSurfaceProperty;
  1485. return nNewEntry;
  1486. }
  1487. static bool IsIn( const CUtlVector< CUtlString > &left, const CUtlVector< CUtlString > &right )
  1488. {
  1489. for( int i = 0; i < left.Count(); ++i )
  1490. {
  1491. bool bFound = false;
  1492. for( int j = 0; j < right.Count(); ++j )
  1493. {
  1494. if( !V_stricmp( left[i], right[j] ) )
  1495. {
  1496. bFound = true;
  1497. break;
  1498. }
  1499. }
  1500. if( !bFound )
  1501. {
  1502. return false;
  1503. }
  1504. }
  1505. return true;
  1506. }
  1507. static bool IsIn( const CUtlHashtable< uint32 >& leftHashes, const CUtlHashtable< uint32 >& rightHashes )
  1508. {
  1509. FOR_EACH_HASHTABLE( leftHashes, it )
  1510. {
  1511. if( rightHashes.Find( leftHashes.Key( it ) ) == rightHashes.InvalidHandle() )
  1512. {
  1513. return false;
  1514. }
  1515. }
  1516. return true;
  1517. }
  1518. bool EqualCaseInsensitive( const CUtlVector< CUtlString > &left, const CUtlVector< CUtlString > &right )
  1519. {
  1520. if( left.Count() * right.Count() < 50 )
  1521. {
  1522. return IsIn( left, right ) && IsIn( right, left );
  1523. }
  1524. else
  1525. {
  1526. // scalable version
  1527. CUtlHashtable< uint32 >leftHashes, rightHashes;
  1528. for( int nLeftIndex = 0; nLeftIndex < left.Count(); ++nLeftIndex )
  1529. {
  1530. leftHashes.Insert( MakeStringToken( left[nLeftIndex] ).GetHashCode() );
  1531. }
  1532. for( int nRightIndex = 0; nRightIndex < right.Count(); ++nRightIndex )
  1533. {
  1534. rightHashes.Insert( MakeStringToken( right[nRightIndex] ).GetHashCode() );
  1535. }
  1536. return IsIn( leftHashes, rightHashes ) && IsIn( rightHashes, leftHashes );
  1537. }
  1538. }
  1539. bool CAuthPhysCollisionAttributes::operator == ( const CAuthPhysCollisionAttributes & other ) const
  1540. {
  1541. return !V_stricmp( m_CollisionGroup, other.m_CollisionGroup ) && EqualCaseInsensitive( m_InteractAs, other.m_InteractAs ) && EqualCaseInsensitive( m_InteractWith, other.m_InteractWith );
  1542. }
  1543. bool CAuthPhysFx::IsNewSpringAllowed( int nBone0, int nBone1 )
  1544. {
  1545. if( nBone0 == nBone1 )
  1546. {
  1547. // no constraining a bone to itself
  1548. return false;
  1549. }
  1550. if( !m_Nodes[ nBone0 ].m_bSimulated && !m_Nodes[nBone1].m_bSimulated )
  1551. {
  1552. // no constraining non-simulating bones
  1553. return false;
  1554. }
  1555. // also, no constraining bones that are already constrained
  1556. // note: linear search, will get slow if we get insanely complex softbodies, but we're not likely to get there anytime soon, if ever
  1557. for( int i = 0; i < m_Constraints.Count(); ++i )
  1558. {
  1559. if( m_Constraints[i].Equals( nBone0, nBone1 ) )
  1560. {
  1561. return false;
  1562. }
  1563. }
  1564. return true;
  1565. }
  1566. bool CAuthPhysFx::IsNewRodAllowed( int nBone0, int nBone1 )
  1567. {
  1568. if ( nBone0 == nBone1 )
  1569. {
  1570. // no constraining a bone to itself
  1571. return false;
  1572. }
  1573. if ( !m_Nodes[ nBone0 ].m_bSimulated && !m_Nodes[ nBone1 ].m_bSimulated )
  1574. {
  1575. // no constraining non-simulating bones
  1576. return false;
  1577. }
  1578. // also, no constraining bones that are already constrained
  1579. // note: linear search, will get slow if we get insanely complex softbodies, but we're not likely to get there anytime soon, if ever
  1580. for ( int i = 0; i < m_Rods.Count(); ++i )
  1581. {
  1582. if ( m_Rods[ i ].Equals( nBone0, nBone1 ) )
  1583. {
  1584. return false;
  1585. }
  1586. }
  1587. return true;
  1588. }
  1589. void CAuthPhysFx::SetBones( const CUtlVector< CBone > &bones )
  1590. {
  1591. m_Nodes.CopyArray( bones.Base(), bones.Count() );
  1592. }
  1593. int CAuthPhysFx::AddConstraint( int nBone0, int nBone1 )
  1594. {
  1595. Assert( uint( nBone0 ) < uint( m_Nodes.Count() ) && uint( nBone1 ) < uint( m_Nodes.Count() ) );
  1596. return m_Constraints.AddToTail( CConstraint( nBone0, nBone1 ) );
  1597. }
  1598. bool CAuthPhysFx::IsConstraintSimulated( int nConstraint ) const
  1599. {
  1600. const CConstraint &constraint = m_Constraints[ nConstraint ];
  1601. const CBone &bone0 = m_Nodes[ constraint.m_nBones[0] ], &bone1 = m_Nodes[ constraint.m_nBones[1] ];
  1602. return bone0.m_bSimulated || bone1.m_bSimulated;
  1603. }
  1604. bool CAuthPhysFx::IsSpringSimulated( int nSpring ) const
  1605. {
  1606. const CSpring &Spring = m_Springs[ nSpring ];
  1607. const CBone &bone0 = m_Nodes[ Spring.m_nNodes[ 0 ] ], &bone1 = m_Nodes[ Spring.m_nNodes[ 1 ] ];
  1608. return bone0.m_bSimulated || bone1.m_bSimulated;
  1609. }
  1610. void CAuthPhysFx::SortAndRemoveDuplicates( )
  1611. {
  1612. HeapSort( m_Quads, [] ( const CAuthPhysFx::CQuad& left, const CAuthPhysFx::CQuad& right ) {
  1613. for ( int c = 0; c < 4; ++c )
  1614. {
  1615. if ( left.m_nNodes[ c ] != right.m_nNodes[ c ] )
  1616. {
  1617. return left.m_nNodes[ c ] < right.m_nNodes[ c ];
  1618. }
  1619. }
  1620. return false;
  1621. } );
  1622. RemoveDuplicates( m_Quads );
  1623. HeapSort( m_Rods, [] ( const CAuthPhysFx::CRod &left, const CAuthPhysFx::CRod &right ) {
  1624. return left < right;
  1625. } );
  1626. RemoveDuplicates( m_Rods );
  1627. }
  1628. static bool IsIn( int nNode, const CVarBitVec &nodes )
  1629. {
  1630. return ( nNode >= 0 && nNode < nodes.GetNumBits( ) && nodes.IsBitSet( nNode ) );
  1631. }
  1632. void CAuthPhysFx::RemoveRodsConnecting( const CVarBitVec &nodes )
  1633. {
  1634. for ( int nRod = 0; nRod < m_Rods.Count( ); )
  1635. {
  1636. const CRod &rod = m_Rods[ nRod ];
  1637. if ( IsIn( rod.m_nNodes[ 0 ], nodes ) && IsIn( rod.m_nNodes[ 1 ], nodes ) )
  1638. {
  1639. m_Rods.FastRemove( nRod );
  1640. }
  1641. else
  1642. {
  1643. ++nRod;
  1644. }
  1645. }
  1646. }
  1647. void CAuthPhysFx::RemoveQuadsConnecting( const CVarBitVec &nodes )
  1648. {
  1649. for ( int nQuad = 0; nQuad < m_Quads.Count( ); )
  1650. {
  1651. const CQuad &quad = m_Quads[ nQuad ];
  1652. if ( IsIn( quad.m_nNodes[ 0 ], nodes ) && IsIn( quad.m_nNodes[ 1 ], nodes ) && IsIn( quad.m_nNodes[ 2 ], nodes ) && IsIn( quad.m_nNodes[ 3 ], nodes ) )
  1653. {
  1654. m_Quads.FastRemove( nQuad );
  1655. }
  1656. else
  1657. {
  1658. ++nQuad;
  1659. }
  1660. }
  1661. }
  1662. // Find connected nodes, assign each island an index, assign each node an island
  1663. int CAuthPhysFx::BuildIslandMap( CUtlVector< int > &nodeToIsland ) const
  1664. {
  1665. int nNodeCount = m_Nodes.Count();
  1666. CDisjointSetForest forest( nNodeCount );
  1667. for ( const CQuad &quad : m_Quads )
  1668. {
  1669. forest.Union( quad.m_nNodes[ 0 ], quad.m_nNodes[ 1 ] );
  1670. forest.Union( quad.m_nNodes[ 0 ], quad.m_nNodes[ 2 ] );
  1671. forest.Union( quad.m_nNodes[ 1 ], quad.m_nNodes[ 2 ] );
  1672. if ( quad.m_nNodes[ 3 ] != quad.m_nNodes[ 2 ] )
  1673. {
  1674. forest.Union( quad.m_nNodes[ 0 ], quad.m_nNodes[ 3 ] );
  1675. forest.Union( quad.m_nNodes[ 1 ], quad.m_nNodes[ 3 ] );
  1676. forest.Union( quad.m_nNodes[ 2 ], quad.m_nNodes[ 3 ] );
  1677. }
  1678. }
  1679. for ( const CRod &rod : m_Rods )
  1680. {
  1681. forest.Union( rod.m_nNodes[ 0 ], rod.m_nNodes[ 1 ] );
  1682. }
  1683. nodeToIsland.SetCount( nNodeCount );
  1684. nodeToIsland.FillWithValue( -1 );
  1685. int nIslandCount = 0;
  1686. for ( int n = 0; n < nNodeCount; ++n )
  1687. {
  1688. int nIslandRootNode = forest.Find( n );
  1689. int &refIsland = nodeToIsland[ nIslandRootNode ];
  1690. if ( refIsland < 0 )
  1691. {
  1692. refIsland = nIslandCount++;
  1693. }
  1694. nodeToIsland[ n ] = refIsland;
  1695. }
  1696. return nIslandCount;
  1697. }
  1698. template <typename Array>
  1699. static int Cleanup( Array &arr, int nNodeCount )
  1700. {
  1701. int nRemoved = 0;
  1702. for ( int i = arr.Count(); i-- > 0; )
  1703. {
  1704. if ( !arr[ i ].IsValid( nNodeCount ) )
  1705. {
  1706. arr.FastRemove( i );
  1707. ++nRemoved;
  1708. }
  1709. }
  1710. return nRemoved;
  1711. }
  1712. int CAuthPhysFx::Cleanup()
  1713. {
  1714. int nRemoved = ::Cleanup( m_SphereRigids, m_Nodes.Count() );
  1715. nRemoved += ::Cleanup( m_TaperedCapsuleRigids, m_Nodes.Count() );
  1716. nRemoved += ::Cleanup( m_TaperedCapsuleStretches, m_Nodes.Count() );
  1717. nRemoved += ::Cleanup( m_CtrlOffsets, m_Nodes.Count() );
  1718. nRemoved += ::Cleanup( m_Quads, m_Nodes.Count() );
  1719. nRemoved += ::Cleanup( m_Rods, m_Nodes.Count() );
  1720. return nRemoved;
  1721. }