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.

4003 lines
138 KiB

  1. //========= Copyright � Valve Corporation, All rights reserved. ============//
  2. #include "softbody.h"
  3. #include "vstdlib/jobthread.h"
  4. #include "tier1/fmtstr.h"
  5. #include "tier1/utlbuffer.h"
  6. #include "mathlib/femodel.h"
  7. #include "mathlib/femodel.inl"
  8. #include "tier1/fmtstr.h"
  9. #include "modellib/clothhelpers.h"
  10. #include "mathlib/femodeldesc.h"
  11. #include "mathlib/softbodyenvironment.h"
  12. #include "tier0/miniprofiler.h"
  13. #include "bitvec.h"
  14. #include "filesystem.h"
  15. #include "mathlib/dynamictree.h"
  16. #include "engine/ivdebugoverlay.h"
  17. #include "mathlib/vertexcolor.h"
  18. #include "rubikon/param_types.h"
  19. #include "materialsystem/imesh.h"
  20. #include "mathlib/softbody.inl"
  21. // #include "rnthread.h"
  22. // #include "rnsimd.h"
  23. // #include "modellib/model.h"
  24. // #include "broadphase.h"
  25. // #include "dynamictree.h"
  26. // #include "sphereshape.h"
  27. // #include "capsuleshape.h"
  28. // #include "hullshape.h"
  29. // #include "meshshape.h"
  30. extern const CPUInformation &cpuInfo;
  31. DECLARE_LOGGING_CHANNEL( LOG_PHYSICS );
  32. enum ClothDebugFlagEnum_t
  33. {
  34. CLOTH_DEBUG_SIM_ANIM_POS = 1 << 0, // GetParticleTransforms() returns sim pos = anim pos
  35. CLOTH_DEBUG_SIM_ANIM_ROT = 1 << 1,
  36. CLOTH_DEBUG_SNAP_TO_ANIM = 1 << 2, // copy positions from GetAnim() in Post()
  37. CLOTH_SKIP_FILTER_TRANSFORMS = 1 << 3,
  38. CLOTH_FORCE_INTERPOLATION_1 = 1 << 4
  39. };
  40. const int g_nClothDebug = 0;
  41. // the verlet integrator has some root differences from the explicit Euler that the old cloth system employs.
  42. // The factor of 2 here reflects one of those (at^2/2 doesn't work the same for the implicit integrator)
  43. const float g_flClothAttrVel = 2; // the 2nd substep in Source1 (SysIterateOverTime) is made with 1/2 timestep and the force computed from animation force attraction is double of the intended...
  44. const float g_flClothAttrPos = 1;
  45. const float g_flClothDampingMultiplier = 1;
  46. float g_flClothNodeVelocityLimit = 1000000;
  47. float g_flClothGroundPlaneThickness = 3;
  48. static const float g_flStickyDist = 2.0f;
  49. static const float g_flRopeSize = 20.0f;
  50. static const float g_flTeleportDeltaSq = ( 200.0f * 200.0f );
  51. static const float g_flNoTeleportDeltaSq = ( 100.0f * 100.0f );
  52. MPROF_NODE( SoftbodyFilterTransforms, "Softbody:FilterTransforms", "Physics" );
  53. MPROF_NODE( SoftbodyDraw, "Softbody:Draw", "Physics" );
  54. MPROF_NODE( SoftbodyStep, "Softbody:Step", "Physics" );
  55. MPROF_NODE( SoftbodyStepIntegrate, "Softbody:Step/Integrate", "Physics" )
  56. MPROF_NODE( SoftbodyStepPredict, "Softbody:Step/Predict", "Physics" )
  57. MPROF_NODE( SoftbodyStepCollide, "Softbody:Step/Collide", "Physics" )
  58. MPROF_NODE( SoftbodyStepIterate, "Softbody:Step/Iterate", "Physics" )
  59. MPROF_NODE( SoftbodyStepPost, "Softbody:Step/Post", "Physics" )
  60. MPROF_NODE( FeRelaxRods, "Softbody:FeRelaxRods", "Physics" );
  61. MPROF_NODE( FeRelaxQuads, "Softbody:FeRelaxQuads", "Physics" );
  62. MPROF_NODE( SoftbodyStepCollideCompute, "Softbody:Step/Collide/Compute", "Physics" );
  63. MPROF_NODE( SoftbodyStepCollideComputeOuter, "Softbody:Step/Collide/ComputeOuter", "Physics" );
  64. //MPROF_NODE( SoftbodyTreeBounds, "Softbody:ComputeTreeBounds", "Physics" );
  65. float g_flClothGuardThreshold = 1000;
  66. int g_nClothWatch = 1;
  67. class CRnSoftbodyChangeGuard
  68. {
  69. const CSoftbody *m_pSoftbody;
  70. const char *m_pName;
  71. AABB_t m_Box0, m_Box1;
  72. public:
  73. static float Difference( const AABB_t &a, const AABB_t &b )
  74. {
  75. return ( a.m_vMinBounds - b.m_vMinBounds ).Length() + ( a.m_vMaxBounds - b.m_vMaxBounds ).Length();
  76. }
  77. CRnSoftbodyChangeGuard( const CSoftbody *pSoftbody, const char *pName )
  78. {
  79. m_pSoftbody = pSoftbody;
  80. m_pName = pName;
  81. m_Box0 = GetAabb( pSoftbody->GetNodePositions( 0 ), pSoftbody->GetNodeCount() );
  82. m_Box1 = GetAabb( pSoftbody->GetNodePositions( 1 ), pSoftbody->GetNodeCount() );
  83. }
  84. ~CRnSoftbodyChangeGuard()
  85. {
  86. AABB_t box0 = GetAabb( m_pSoftbody->GetNodePositions( 0 ), m_pSoftbody->GetNodeCount() );
  87. AABB_t box1 = GetAabb( m_pSoftbody->GetNodePositions( 1 ), m_pSoftbody->GetNodeCount() );
  88. float flMove = Difference( m_Box0, box0 ) + Difference( m_Box1, box1 );
  89. if ( flMove > g_flClothGuardThreshold )
  90. {
  91. if ( m_pSoftbody->GetIndexInWorld() == g_nClothWatch )
  92. {
  93. Log_Msg( LOG_PHYSICS, "Cloth %d %s in %s changed %g: from {%.0f,%.0f}\xB1{%.0f,%.0f} to {%.0f,%.0f}\xB1{%.0f,%.0f}\n", m_pSoftbody->m_nIndexInWorld, m_pSoftbody->m_DebugName.GetSafe(), m_pName, flMove,
  94. box0.GetCenter().x, box0.GetCenter().y, box0.GetSize().x, box0.GetSize().y,
  95. box1.GetCenter().x, box1.GetCenter().y, box1.GetSize().x, box1.GetSize().y
  96. );
  97. }
  98. }
  99. }
  100. };
  101. #if defined(_DEBUG)
  102. #define CHANGE_GUARD() //CRnSoftbodyChangeGuard changeGuard( this, __FUNCTION__)
  103. #else
  104. #define CHANGE_GUARD()
  105. #endif
  106. CSoftbody::CSoftbody( void )
  107. {
  108. // this constructor prepares softbody for a snoop or deserialization
  109. m_pEnvironment = NULL;
  110. m_pPos0 = m_pPos1 = NULL;
  111. m_pParticles = NULL;
  112. m_flThreadStretch = 0;
  113. m_flSurfaceStretch = 0;
  114. InitDefaults( );
  115. }
  116. CSoftbody::CSoftbody( CSoftbodyEnvironment *pWorld, const CFeModel *pFeModel )
  117. {
  118. Init( pWorld, pFeModel , 0 );
  119. }
  120. void CSoftbody::Init( CSoftbodyEnvironment *pWorld, const CFeModel *pFeModel, int numModelBones )
  121. {
  122. m_pEnvironment = pWorld;
  123. m_pFeModel = const_cast< CFeModel* >( pFeModel );
  124. Init( numModelBones );
  125. m_pEnvironment->Register( this );
  126. }
  127. CSoftbody::~CSoftbody()
  128. {
  129. Shutdown( );
  130. }
  131. void AddOrigin( matrix3x4a_t &tm, const Vector &vDelta )
  132. {
  133. tm.SetOrigin( tm.GetOrigin() + vDelta );
  134. }
  135. void CSoftbody::ReplaceFeModel( CFeModelReplaceContext &context )
  136. {
  137. uint8 *pOldBuffer = ( uint8* )m_pParticles;
  138. matrix3x4a_t *pOldAnim = GetAnimatedTransforms(), *pOldSim = GetSimulatedTransforms();
  139. VectorAligned *pOldPos0 = m_pPos0, *pOldPos1 = m_pPos1;
  140. m_StickyBuffer.Clear();
  141. m_pFeModel = const_cast< CFeModel* >( context.GetNew() );
  142. InitFeModel();
  143. Vector vSimOrigin = m_nAnimSpace == SOFTBODY_ANIM_SPACE_LOCAL ? vec3_origin : m_vSimOrigin;
  144. // heuristics: sim origin is probably where the user expects to find new pieces of their new cloth
  145. for ( int nNewNode = 0; nNewNode < ( int )m_nNodeCount; ++nNewNode )
  146. {
  147. // remap what little we can, shift what we can't
  148. int nOldNode = context.NewToOldNode( nNewNode );
  149. if ( nOldNode < 0 )
  150. {
  151. m_pPos0[ nNewNode ] += vSimOrigin;
  152. m_pPos1[ nNewNode ] += vSimOrigin;
  153. }
  154. else
  155. {
  156. m_pPos0[ nNewNode ] = pOldPos0[ nOldNode ];
  157. m_pPos1[ nNewNode ] = pOldPos1[ nOldNode ];
  158. }
  159. }
  160. // and map the animations, too
  161. for ( int nNewCtrl = 0; nNewCtrl < ( int )m_nParticleCount; ++nNewCtrl )
  162. {
  163. int nOldCtrl = context.NewToOldCtrl( nNewCtrl );
  164. if ( nOldCtrl < 0 )
  165. {
  166. AddOrigin( GetAnim( nNewCtrl ), vSimOrigin );
  167. AddOrigin( GetSim( nNewCtrl ), vSimOrigin );
  168. }
  169. else
  170. {
  171. GetAnim( nNewCtrl ) = pOldAnim[ nOldCtrl ];
  172. GetSim( nNewCtrl ) = pOldSim[ nOldCtrl ];
  173. }
  174. }
  175. GoWakeup(); // we could copy the old positions here, but I doubt anyone needs it
  176. MemAlloc_FreeAligned( pOldBuffer );
  177. }
  178. uint Align4( uint a )
  179. {
  180. return ( a + 3 ) & ~3;
  181. }
  182. void CSoftbody::InitDefaults( )
  183. {
  184. m_nDebugSelection = 0;
  185. m_nDebugDrawTreeEndLevel = 0;
  186. m_nDebugDrawTreeBeginLevel = 0;
  187. m_nDebugDrawTreeFlags = 0;
  188. m_flVelAirDrag = m_flExpAirDrag = 0.0f;
  189. m_flVelQuadAirDrag = m_flExpQuadAirDrag = 0.0f;
  190. m_flVelRodAirDrag = m_flExpRodAirDrag = 0.0f;
  191. m_flQuadVelocitySmoothRate = 0.0f;
  192. m_flRodVelocitySmoothRate = 0.0f;
  193. m_flVolumetricSolveAmount = 0.0f;
  194. m_nRodVelocitySmoothIterations = 0;
  195. m_nQuadVelocitySmoothIterations = 0;
  196. m_nSimFlags = 0;
  197. m_nAnimSpace = SOFTBODY_ANIM_SPACE_WORLD;
  198. m_flLastTimestep = 1.0f / 60.0f;
  199. m_flOverPredict = 0;
  200. m_bAnimTransformChanged = false;
  201. m_bSimTransformsOutdated = false;
  202. m_bGravityDisabled = false;
  203. m_bEnableAnimationAttraction = true;
  204. m_bEnableFollowNodes = true;
  205. m_bEnableInclusiveCollisionSpheres = true;
  206. m_bEnableExclusiveCollisionSpheres = true;
  207. m_bEnableCollisionPlanes = true;
  208. m_bEnableGroundCollision = true;
  209. m_bEnableGroundTrace = false;
  210. m_bEnableFtlPass = false;
  211. m_bEnableSprings = true;
  212. m_bFrozen = false;
  213. m_bDebugDraw = true;
  214. m_bEnableSimd = GetCPUInformation().m_bSSE2;
  215. m_nEnableWorldShapeCollision = 0;
  216. m_nStepsSimulated = 0;
  217. m_flGravityScale = 1.0f;
  218. m_flModelScale = 1.0f;
  219. m_flClothScale = 1.0f;
  220. m_flVelocityDamping = 0.0f;
  221. m_flStepUnderRelax = 0.0f;
  222. m_flDampingMultiplier = 1.0f;
  223. m_vGround = vec3_origin;
  224. m_vSimOrigin = vec3_origin;
  225. m_vSimAngles.Init( );
  226. m_vRopeOffset = Vector( 0, -1, 0 ) * g_flRopeSize; // this is the "right" (?!) vector in dota heroes
  227. m_nActivityState = STATE_DORMANT;
  228. //m_nDebugNode = 0xFFFFFFFF;
  229. /*
  230. m_bTeleportOnNextSetAbsOrigin = true;
  231. m_bTeleportOnNextSetAbsAngles = true;
  232. */
  233. m_nStateCounter = 0;
  234. }
  235. void CSoftbody::SetInstanceSettings( void *pSettings )
  236. {
  237. if ( pSettings )
  238. {
  239. m_flClothScale = *( const float* ) pSettings;
  240. }
  241. }
  242. void CSoftbody::Init( int numModelBones )
  243. {
  244. InitDefaults( );
  245. V_memset( m_pUserData, 0, sizeof( m_pUserData ) );
  246. InitFeModel( numModelBones );
  247. if ( IsDebug() ) Post();
  248. Validate();
  249. }
  250. void CSoftbody::InitFeModel( int numModelBones )
  251. {
  252. m_nNodeCount = m_pFeModel->m_nNodeCount;
  253. m_nParticleCount = m_pFeModel->m_nCtrlCount;
  254. m_flThreadStretch = m_pFeModel->m_flDefaultThreadStretch;
  255. m_flSurfaceStretch = m_pFeModel->m_flDefaultSurfaceStretch;
  256. m_flGravityScale = m_pFeModel->m_flDefaultGravityScale;
  257. if ( m_flGravityScale == 0.0f )
  258. {
  259. Warning( "Graivty Scale 0, probably invalid default. Changing to 1\n" );
  260. m_flGravityScale = 1.0f;
  261. }
  262. m_flVelAirDrag = m_pFeModel->m_flDefaultVelAirDrag;
  263. m_flExpAirDrag = m_pFeModel->m_flDefaultExpAirDrag;
  264. m_flVelQuadAirDrag = m_pFeModel->m_flDefaultVelQuadAirDrag;
  265. m_flExpQuadAirDrag = m_pFeModel->m_flDefaultExpQuadAirDrag;
  266. m_flVelRodAirDrag = m_pFeModel->m_flDefaultVelRodAirDrag;
  267. m_flExpRodAirDrag = m_pFeModel->m_flDefaultExpRodAirDrag;
  268. m_flQuadVelocitySmoothRate = m_pFeModel->m_flQuadVelocitySmoothRate;
  269. m_flRodVelocitySmoothRate = m_pFeModel->m_flRodVelocitySmoothRate;
  270. m_flVolumetricSolveAmount = m_pFeModel->m_flDefaultVolumetricSolveAmount;
  271. m_nRodVelocitySmoothIterations = m_pFeModel->m_nRodVelocitySmoothIterations;
  272. m_nQuadVelocitySmoothIterations = m_pFeModel->m_nQuadVelocitySmoothIterations;
  273. uint bTreeCollisionCount = ( m_pFeModel->m_nTaperedCapsuleStretchCount | m_pFeModel->m_nTaperedCapsuleRigidCount | m_pFeModel->m_nSphereRigidCount | ( m_pFeModel->m_nDynamicNodeFlags & FE_FLAG_ENABLE_WORLD_SHAPE_COLLISION_MASK ) ) ;
  274. uint bAnyCollisionCount = bTreeCollisionCount | m_pFeModel->m_nCollisionPlanes | m_pFeModel->m_nCollisionSpheres[ 0 ];
  275. uint nAabbs = bTreeCollisionCount ? m_pFeModel->GetDynamicNodeCount() - 1 : 0;
  276. uint nParticleGlue = bAnyCollisionCount ? m_pFeModel->GetDynamicNodeCount() : 0;
  277. uint nMemSize = GetParticleArrayCount() * sizeof( matrix3x4a_t ) + m_nNodeCount * 2 * sizeof( VectorAligned ) + nAabbs * sizeof( FeAabb_t ) + nParticleGlue * sizeof( CParticleGlue );
  278. if ( numModelBones )
  279. {
  280. nMemSize += numModelBones * sizeof( *m_pModelBoneToCtrl ) + m_pFeModel->m_nCtrlCount * sizeof( *m_pCtrlToModelBone );
  281. }
  282. CBufferStrider buffer( MemAlloc_AllocAligned( nMemSize, 16 ) );
  283. V_memset( buffer.Get( ), 0, nMemSize );
  284. uint8 *pMemEnd = buffer.Get( ) + nMemSize; NOTE_UNUSED( pMemEnd );
  285. m_pParticles = buffer.Stride< matrix3x4a_t >( GetParticleArrayCount( ) );
  286. m_pPos0 = buffer.Stride< VectorAligned >( m_nNodeCount );
  287. m_pPos1 = buffer.Stride< VectorAligned >( m_nNodeCount );
  288. m_pAabb = nAabbs ? buffer.Stride< FeAabb_t >( nAabbs ) : NULL;
  289. if ( nParticleGlue )
  290. {
  291. m_StickyBuffer.EnsureBitExists( nParticleGlue - 1 );
  292. m_pParticleGlue = buffer.Stride< CParticleGlue >( nParticleGlue );
  293. }
  294. else
  295. {
  296. m_pParticleGlue = NULL;
  297. }
  298. if ( numModelBones )
  299. {
  300. m_pModelBoneToCtrl = buffer.Stride< int16 >( numModelBones );
  301. for ( int i = 0; i < numModelBones; ++i )
  302. {
  303. m_pModelBoneToCtrl[ i ] = -1;
  304. }
  305. m_pCtrlToModelBone = buffer.Stride< int16 >( m_pFeModel->m_nCtrlCount );
  306. for ( uint i = 0; i < m_pFeModel->m_nCtrlCount; ++i )
  307. {
  308. m_pCtrlToModelBone[ i ] = -1;
  309. }
  310. }
  311. else
  312. {
  313. m_pModelBoneToCtrl = NULL;
  314. m_pCtrlToModelBone = NULL;
  315. }
  316. m_bEnableFtlPass = ( m_pFeModel->m_nDynamicNodeFlags & FE_FLAG_ENABLE_FTL ) != 0;
  317. m_nEnableWorldShapeCollision = ( m_pFeModel->m_nDynamicNodeFlags >> FE_FLAG_ENABLE_WORLD_SHAPE_COLLISION_SHIFT ) & ( ( 1 << SHAPE_COUNT ) - 1 );
  318. if ( !m_pFeModel->m_pNodeToCtrl && m_pFeModel->m_pInitPose )
  319. {
  320. AssertDbg( m_pFeModel->m_nCtrlCount == m_pFeModel->m_nNodeCount );
  321. for ( int nNode = 0; nNode < m_nNodeCount; ++nNode )
  322. {
  323. m_pPos0[ nNode ] = m_pPos1[ nNode ] = m_pFeModel->m_pInitPose[ nNode ].GetOrigin( );
  324. GetAnim( nNode ) = GetSim( nNode ) = TransformMatrix( m_pFeModel->m_pInitPose[ nNode ] );
  325. }
  326. }
  327. else
  328. {
  329. // simple initialization
  330. V_memset( m_pPos0, 0, m_nNodeCount * sizeof( *m_pPos0 ) );
  331. V_memset( m_pPos1, 0, m_nNodeCount * sizeof( *m_pPos1 ) );
  332. for ( int nCtrl = 0; nCtrl < GetParticleArrayCount( ); ++nCtrl )
  333. {
  334. m_pParticles[ nCtrl ] = g_MatrixIdentity;
  335. }
  336. }
  337. AssertDbg( pMemEnd == buffer.Get( ) );
  338. m_nSimFlags = m_pEnvironment->GetSoftbodySimulationFlags();
  339. }
  340. void CSoftbody::BindModelBoneToCtrl( int nModelBone, int nCtrl )
  341. {
  342. if ( nCtrl >= 0 )
  343. {
  344. m_pCtrlToModelBone[ nCtrl ] = nModelBone;
  345. }
  346. if ( nModelBone >= 0 )
  347. {
  348. m_pModelBoneToCtrl[ nModelBone ] = nCtrl;
  349. }
  350. }
  351. void CSoftbody::SetSimFlags( uint nNewSimFlags )
  352. {
  353. uint nDiffFlags = m_nSimFlags ^ nNewSimFlags;
  354. if ( nDiffFlags )
  355. {
  356. // reset?
  357. m_nSimFlags = nNewSimFlags;
  358. }
  359. }
  360. void CSoftbody::DebugDump( )
  361. {
  362. if ( IsDebug() && g_nClothDebug && m_nStepsSimulated < 7 )
  363. {
  364. CUtlString line;
  365. AppendDebugInfo( line );
  366. Msg( "%s\n", line.Get( ) );
  367. }
  368. }
  369. void CSoftbody::AppendDebugInfo( CUtlString &line )
  370. {
  371. AABB_t bbox = BuildBounds( );
  372. Vector c = bbox.GetCenter( ), e = bbox.GetSize( );
  373. CUtlString desc;
  374. const char *pActivityState = "Undefined";
  375. switch ( m_nActivityState )
  376. {
  377. case STATE_ACTIVE:
  378. pActivityState = "active";
  379. break;
  380. case STATE_DORMANT:
  381. pActivityState = "dormant";
  382. break;
  383. case STATE_WAKEUP:
  384. pActivityState = "wakeup";
  385. break;
  386. }
  387. desc.Format( "Softbody #%d %s.%d %s (%.4g,%.4g)\xb1(%.4g,%.4g)", m_nIndexInWorld, pActivityState, m_nStateCounter, m_DebugName.GetSafe( ), c.x, c.y, e.x, e.y );
  388. line += desc;
  389. }
  390. void CSoftbody::Validate()
  391. {
  392. if ( IsDebug( ) )
  393. {
  394. const matrix3x4a_t *pSim = m_pParticles + m_nParticleCount; NOTE_UNUSED( pSim );
  395. for ( int nCtrl = 0; nCtrl < m_nParticleCount; ++nCtrl )
  396. {
  397. uint nNode = m_pFeModel->CtrlToNode( nCtrl ); NOTE_UNUSED( nNode );
  398. bool bIsSimulated = ( nNode < m_nParticleCount && nNode > m_pFeModel->m_nStaticNodes ); NOTE_UNUSED( bIsSimulated );
  399. AssertDbg( IsGoodWorldTransform( pSim[ nCtrl ] ) );
  400. AssertDbg( IsGoodWorldTransform( Descale( m_pParticles[ nCtrl ] ) ) );
  401. }
  402. }
  403. }
  404. void CSoftbody::SetPose( const CTransform *pPose )
  405. {
  406. CHANGE_GUARD();
  407. for ( int nParticle = 0; nParticle < m_nParticleCount; ++nParticle )
  408. {
  409. SetCtrl( nParticle, pPose[ nParticle ] );
  410. }
  411. Validate( );
  412. m_bAnimTransformChanged = true;
  413. m_bSimTransformsOutdated = false;
  414. }
  415. void CSoftbody::SetPose( const CTransform &tm, const CTransform *pPose )
  416. {
  417. CHANGE_GUARD();
  418. AssertDbg( FloatsAreEqual( QuaternionLength( tm.m_orientation ), 1.0f, 1e-5f ) );
  419. for ( int nParticle = 0; nParticle < m_nParticleCount; ++nParticle )
  420. {
  421. CTransform pose = ConcatTransforms( tm, pPose[ nParticle ] );
  422. SetCtrl( nParticle, pose );
  423. }
  424. Validate( );
  425. m_bAnimTransformChanged = true;
  426. m_bSimTransformsOutdated = false;
  427. }
  428. void CSoftbody::SetCtrl( int nParticle, const CTransform &pose )
  429. {
  430. CHANGE_GUARD();
  431. matrix3x4a_t tmPose = TransformMatrix( pose );
  432. GetAnim( nParticle ) = tmPose;
  433. uint nNode = m_pFeModel->CtrlToNode( nParticle );
  434. if ( nNode < m_nNodeCount )
  435. {
  436. if ( m_pFeModel->m_pNodeIntegrator )
  437. {
  438. float flDamping = m_flLastTimestep * ( m_pFeModel->m_pNodeIntegrator[ nNode ].flPointDamping * m_flDampingMultiplier * g_flClothDampingMultiplier );
  439. if ( flDamping < 1.0f )
  440. {
  441. GetSim( nParticle ).SetOrigin(
  442. m_pPos0[ nNode ] = m_pPos1[ nNode ] =
  443. pose.GetOrigin( ) * ( 1 - flDamping ) + m_pPos1[ nNode ] * flDamping
  444. );
  445. }
  446. }
  447. else
  448. {
  449. m_pPos0[ nNode ] = m_pPos1[ nNode ] = pose.GetOrigin( );
  450. }
  451. }
  452. else
  453. {
  454. GetSim( nParticle ) = tmPose;
  455. }
  456. }
  457. void CSoftbody::SetPoseFromBones( const int16 *pCtrlToBone, const matrix3x4a_t *pBones, float flScale )
  458. {
  459. CHANGE_GUARD();
  460. if ( m_pFeModel->m_pCtrlToNode )
  461. {
  462. for ( int nParticle = 0; nParticle < m_nParticleCount; ++nParticle )
  463. {
  464. if ( pCtrlToBone[ nParticle ] < 0 )
  465. continue;
  466. const matrix3x4a_t &pose = pBones[ pCtrlToBone[ nParticle ] ];
  467. AssertDbg( IsGoodWorldTransform( pose, flScale ) );
  468. GetSim( nParticle ) = GetAnim( nParticle ) = ScaleMatrix3x3( pose, flScale ) ;
  469. uint nNode = m_pFeModel->m_pCtrlToNode[ nParticle ];
  470. if ( nNode < m_nNodeCount )
  471. {
  472. m_pPos0[ nNode ] = m_pPos1[ nNode ] = pose.GetOrigin( );
  473. }
  474. }
  475. }
  476. else
  477. {
  478. for ( int nParticle = 0; nParticle < m_nParticleCount; ++nParticle )
  479. {
  480. if ( pCtrlToBone[ nParticle ] < 0 )
  481. continue;
  482. const matrix3x4a_t &pose = pBones[ pCtrlToBone[ nParticle ] ];
  483. const matrix3x4a_t poseNoScale = ScaleMatrix3x3( pose, flScale );
  484. AssertDbg( IsGoodWorldTransform( poseNoScale, 1.0f ) );
  485. GetSim( nParticle ) = GetAnim( nParticle ) = poseNoScale;
  486. uint nNode = nParticle;
  487. m_pPos0[ nNode ] = m_pPos1[ nNode ] = pose.GetOrigin( );
  488. }
  489. }
  490. m_bAnimTransformChanged = true;
  491. m_bSimTransformsOutdated = false;
  492. Validate( );
  493. }
  494. uint CSoftbody::Step( float flTimeStep )
  495. {
  496. return Step( m_pEnvironment->GetSoftbodyIterations(), flTimeStep );
  497. }
  498. const float g_flClothStep = 1.0f;
  499. const int g_nClothCompatibility = 1;
  500. void CSoftbody::DebugPreStep(float flTimeStep )
  501. {
  502. #ifdef _DEBUG
  503. static bool s_bRead = false;
  504. CUtlString buf; NOTE_UNUSED( buf );
  505. if ( g_nClothDebug )
  506. {
  507. buf = PrintParticleState();
  508. }
  509. if ( s_bRead )
  510. {
  511. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  512. if ( g_pFullFileSystem->ReadFile( "cloth.txt", NULL, buf ) )
  513. {
  514. ParseParticleState( buf, flTimeStep );
  515. }
  516. s_bRead = false;
  517. }
  518. #endif
  519. }
  520. uint CSoftbody::Step( int nIterations, float flTimeStep )
  521. {
  522. if ( m_bFrozen )
  523. return 0;
  524. CHANGE_GUARD();
  525. MPROF_AUTO_FAST( SoftbodyStep );
  526. flTimeStep *= g_flClothStep;
  527. if ( m_nActivityState == STATE_ACTIVE && flTimeStep > 1e-5f )
  528. {
  529. Validate(); // DebugPreStep(flTimeStep);
  530. RawSimulate( nIterations, flTimeStep );
  531. m_bSimTransformsOutdated = true;
  532. m_nStepsSimulated++;
  533. Post( );
  534. Validate( ); // DebugDump( );
  535. return 1;
  536. }
  537. else
  538. {
  539. return 0;
  540. }
  541. }
  542. uint CSoftbody::GetStateHash()const
  543. {
  544. CRC32_t hash;
  545. CRC32_Init( &hash );
  546. CRC32_ProcessBuffer( &hash, m_pPos0, sizeof( *m_pPos0 ) * m_pFeModel->m_nNodeCount );
  547. CRC32_ProcessBuffer( &hash, m_pPos1, sizeof( *m_pPos1 ) * m_pFeModel->m_nNodeCount );
  548. CRC32_Final( &hash );
  549. return hash;
  550. }
  551. bool IsEqual( const CUtlVector< uint > &a, const CUtlVector< uint > & b )
  552. {
  553. if ( a.Count() != b.Count() ) return false;
  554. for ( int nIndex = 0; nIndex < a.Count(); ++nIndex )
  555. if ( a[ nIndex ] != b[ nIndex ] )
  556. return false;
  557. return true;
  558. }
  559. // check that RawSimulate is deterministic
  560. void CSoftbody::ValidatingSimulate( int nIterations, float flTimeStep )
  561. {
  562. // remember the initial state
  563. CUtlVector< VectorAligned > pos0, pos1;
  564. int nNodes = m_pFeModel->m_nNodeCount;
  565. pos0.CopyArray( m_pPos0, nNodes );
  566. pos1.CopyArray( m_pPos1, nNodes );
  567. bool bWasAnimTransformChanged = m_bAnimTransformChanged;
  568. bool bRepeat = false;
  569. for ( ;; )
  570. {
  571. RawSimulate( nIterations, flTimeStep );
  572. CUtlVector< VectorAligned > res0, res1; // initial results
  573. res0.CopyArray( m_pPos0, nNodes );
  574. res1.CopyArray( m_pPos1, nNodes );
  575. CUtlVector< VectorAligned > rep0, rep1; // repeat input state
  576. rep0.CopyArray( pos0.Base(), nNodes );
  577. rep1.CopyArray( pos1.Base(), nNodes );
  578. VectorAligned *pSavePos0 = m_pPos0, *pSavePos1 = m_pPos1; // save the previous pointers
  579. m_pPos0 = rep0.Base();
  580. m_pPos1 = rep1.Base();
  581. m_bAnimTransformChanged = bWasAnimTransformChanged;
  582. RawSimulate( nIterations, flTimeStep ); // repeat simulation with the repeat input state
  583. if ( m_pPos0 == rep1.Base() ) { rep0.Swap( rep1 ); } // m_Pos were swapped, swap rep
  584. m_pPos0 = pSavePos0; // recover buffer pointers
  585. m_pPos1 = pSavePos1;
  586. // check that the initial results equal to repeat results
  587. for ( int n = 0; n < nNodes; ++n )
  588. {
  589. Assert( res0[ n ] == rep0[ n ] );
  590. Assert( res1[ n ] == rep1[ n ] );
  591. }
  592. if ( !bRepeat )
  593. break;
  594. m_bAnimTransformChanged = bWasAnimTransformChanged;
  595. // start all over from the copied initial state
  596. V_memcpy( m_pPos0, pos0.Base(), sizeof( VectorAligned ) * nNodes );
  597. V_memcpy( m_pPos1, pos1.Base(), sizeof( VectorAligned ) * nNodes );
  598. }
  599. }
  600. void CSoftbody::RawSimulate( int nIterations, float flTimeStep )
  601. {
  602. if ( g_nClothCompatibility >= 2 && ( m_pFeModel->m_nDynamicNodeFlags & FE_FLAG_UNINERTIAL_CONSTRAINTS ) )
  603. {
  604. Integrate_S1( flTimeStep );
  605. ResolveStretch_S1( flTimeStep ); // this folds in the constraint iterator loop
  606. ResolveAnimAttraction_S1( flTimeStep );
  607. Collide();
  608. }
  609. else
  610. {
  611. Integrate( flTimeStep );
  612. Predict( flTimeStep );
  613. AddAnimationAttraction( flTimeStep );
  614. Collide();
  615. {
  616. CConstraintIterator iterator( this );
  617. iterator.Iterate( nIterations );
  618. }
  619. }
  620. }
  621. void CSoftbody::ParseParticleState( CUtlBuffer &buf, float flTimeStep )
  622. {
  623. char szName[ 300 ];
  624. Vector vOrigin, vAnimTarget, vVelocity;
  625. uint nTotalFound = 0; NOTE_UNUSED( nTotalFound );
  626. while ( 10 == buf.Scanf( "%s %f %f %f %f %f %f %f %f %f\n", szName, &vOrigin.x, &vOrigin.y, &vOrigin.z, &vAnimTarget.x, &vAnimTarget.y, &vAnimTarget.z, &vVelocity.x, &vVelocity.y, &vVelocity.z ) )
  627. {
  628. // find this bone
  629. bool bFound = false;
  630. for ( uint nCtrl = 0; nCtrl < m_pFeModel->m_nCtrlCount; ++nCtrl )
  631. {
  632. if ( !V_stricmp( m_pFeModel->m_pCtrlName[ nCtrl ], szName ) )
  633. {
  634. uint nNode = m_pFeModel->CtrlToNode( nCtrl );
  635. bFound = true;
  636. m_pPos1[ nNode ] = vOrigin;
  637. m_pPos0[ nNode ] = vOrigin - flTimeStep * vVelocity;
  638. GetAnim( nCtrl ).SetOrigin( vAnimTarget );
  639. break;
  640. }
  641. }
  642. if ( bFound )
  643. {
  644. nTotalFound++;
  645. }
  646. else
  647. {
  648. Msg( "Not found: %s\n", szName );
  649. }
  650. }
  651. Msg( "%d bones found and initialized\n", nTotalFound );
  652. }
  653. CUtlString CSoftbody::PrintParticleState( )const
  654. {
  655. CUtlString buf;
  656. for ( uint nCtrl = 0; nCtrl < m_pFeModel->m_nCtrlCount ; ++nCtrl )
  657. {
  658. CUtlString line;
  659. uint nNode = m_pFeModel->CtrlToNode( nCtrl );
  660. const VectorAligned &vOrigin = m_pPos1[ nNode ];
  661. Vector vAnimTarget = GetAnim( nCtrl ).GetOrigin(), vVelocity = ( m_pPos1[ nNode ] - m_pPos0[ nNode ] ) / m_flLastTimestep;
  662. line.Format( "%s %g %g %g %g %g %g %g %g %g\n", m_pFeModel->m_pCtrlName[ nCtrl ],
  663. vOrigin.x, vOrigin.y, vOrigin.z, vAnimTarget.x, vAnimTarget.y, vAnimTarget.z, vVelocity.x, vVelocity.y, vVelocity.z
  664. );
  665. buf += line;
  666. }
  667. return buf;
  668. }
  669. void CSoftbody::Integrate_S1( float flTimeStep )
  670. {
  671. const uint nStaticNodes = m_pFeModel->m_nStaticNodes;
  672. const FeNodeIntegrator_t *pNodeIntegrator = m_pFeModel->m_pNodeIntegrator;
  673. float flTickRate = 1.0f / flTimeStep, flPrevTickRate = 1.0f / m_flLastTimestep;
  674. VectorAligned *pPos0 = m_pPos0, *pPos1 = m_pPos1;
  675. if ( m_bAnimTransformChanged )
  676. {
  677. // <sergiy> Note that the animation is copied into static nodes in Source1 in CClothModelPiece::SetupBone(), cloth_system.cpp#36:3397
  678. // it's probably a good idea to continue doing that regardless of the slight bug in source1 that overshot static bones slightly, unless artists unwittingly relied on that bug in some cases
  679. {
  680. for ( uint nStaticNode = 0; nStaticNode < nStaticNodes; ++nStaticNode )
  681. {
  682. uint nCtrl = m_pFeModel->NodeToCtrl( nStaticNode );
  683. Assert( nCtrl < m_nParticleCount );
  684. pPos0[ nStaticNode ] = pPos1[ nStaticNode ];
  685. pPos1[ nStaticNode ] = GetAnim( nCtrl ).GetOrigin();
  686. }
  687. }
  688. m_bAnimTransformChanged = false;
  689. if ( m_bEnableFollowNodes )
  690. {
  691. for ( uint nFlwr = 0, nFollowers = m_pFeModel->m_nFollowNodeCount; nFlwr < nFollowers; ++nFlwr )
  692. {
  693. const FeFollowNode_t fn = m_pFeModel->m_pFollowNodes[ nFlwr ];
  694. Vector vDelta = ( pPos1[ fn.nParentNode ] - pPos0[ fn.nParentNode ] ) * fn.flWeight;
  695. // why pos0-=, and not pos1+=? because we're computing pos2=pos1 + ( pos1 - pos0 ) later, and advancing pos1 will have the effect of doubling the velocity of the child
  696. pPos1[ fn.nChildNode ] += vDelta;
  697. pPos0[ fn.nChildNode ] += vDelta;
  698. }
  699. }
  700. }
  701. else
  702. {
  703. // since we're double-buffering the positions, we need to copy the positions for at least one frame
  704. // after animation stopped updating them. We can add another flag here to avoid memcpy if it ever becomes noticeable CPU drain
  705. V_memcpy( m_pPos0, m_pPos1, sizeof( *m_pPos0 ) * nStaticNodes );
  706. }
  707. Vector vGravity( 0, 0, -( m_bGravityDisabled ? 0 : m_flGravityScale ) );
  708. AssertDbg( !m_pFeModel->m_pCtrlToNode ); // it should be safe to assume we have no node-to-ctrl mapping on Source1-imported content
  709. for ( uint nDynNode = nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  710. {
  711. // CClothParticleState::InitialForces()
  712. VectorAligned &pos1 = pPos1[ nDynNode ], &pos0 = pPos0[ nDynNode ];
  713. Vector vOrigin = pos1, vVelocity = ( pos1 - pos0 ) * flPrevTickRate;
  714. float flInvMass = m_pFeModel->m_pNodeInvMasses[ nDynNode ];
  715. Vector vForce;
  716. // animation attraction
  717. if ( pNodeIntegrator )
  718. {
  719. const FeNodeIntegrator_t &integrator = pNodeIntegrator[ nDynNode ];
  720. vForce = vGravity * integrator.flGravity / flInvMass;
  721. Vector vAnimPos = GetAnim( nDynNode ).GetOrigin();
  722. Vector vDelta = vAnimPos - vOrigin;
  723. float flAnimationForceAttraction = integrator.flAnimationForceAttraction / flInvMass; // this is predivided by mass in CAuthClothParser::ParseLegacyDotaNodeGrid(), line 1894
  724. vForce += vDelta * ( flAnimationForceAttraction * g_flClothAttrVel * flTickRate );
  725. vForce -= ( pNodeIntegrator[ nDynNode ].flPointDamping / flInvMass ) * vVelocity; // it's safe to assume S1-imported cloth has Unitless Damping = false, so all the damping values are premultiplied by mass
  726. }
  727. else
  728. {
  729. vForce = vGravity * 360 / flInvMass; // 360 is the default gravity, if there's no integrator, 360 is the assumed gravity per node
  730. }
  731. // CClothParticleState::Integrate()
  732. float flDeltaTimeMass = flTimeStep * flInvMass;
  733. vVelocity += vForce * flDeltaTimeMass;
  734. pos1 = vOrigin + flTimeStep * vVelocity;
  735. pos0 = vVelocity; // pos0 will temporarily store velocity!
  736. }
  737. m_flLastTimestep = flTimeStep;
  738. }
  739. void CSoftbody::ResolveStretch_S1( float flTimeStep )
  740. {
  741. VectorAligned *pVel = m_pPos0, *pPos = m_pPos1;
  742. float flConstraintScale = m_flModelScale * m_flClothScale;
  743. uint nStaticNodes = m_pFeModel->m_nStaticNodes;
  744. const float *pStretchForce = m_pFeModel->m_pLegacyStretchForce;
  745. for ( uint nRod = 0; nRod < m_pFeModel->m_nRodCount; ++nRod )
  746. {
  747. const FeRodConstraint_t &rod = m_pFeModel->m_pRods[ nRod ];
  748. VectorAligned &vOrigin1 = pPos[ rod.nNode[ 0 ] ], &vOrigin2 = pPos[ rod.nNode[ 1 ] ];
  749. VectorAligned &vVelocity1 = pVel[ rod.nNode[ 0 ] ], &vVelocity2 = pVel[ rod.nNode[ 1 ] ];
  750. //
  751. // CClothSpring::ResolveStretch()
  752. //
  753. Vector vDeltaOrigin = vOrigin1 - vOrigin2;
  754. float flDistance = vDeltaOrigin.Length();
  755. float flRestLength = rod.flMaxDist;
  756. if ( flDistance > flRestLength )
  757. {
  758. // Note: CClothSpring::m_flStretchiness = 1 - SpringStretchiness from keyvalue file.
  759. float flSpringStretchiness = rod.flRelaxationFactor;
  760. vDeltaOrigin /= flDistance;
  761. flDistance -= flRestLength;
  762. flDistance *= flSpringStretchiness;
  763. Vector vDeltaDistance = vDeltaOrigin * flDistance;
  764. if ( ( rod.nNode[ 0 ] <= rod.nNode[ 1 ] || rod.nNode[ 0 ] < nStaticNodes ) && rod.nNode[1] >= nStaticNodes )
  765. {
  766. vOrigin2 += vDeltaDistance;
  767. if ( pStretchForce )
  768. {
  769. Vector vDeltaVelocity = vDeltaDistance * pStretchForce[ rod.nNode[ 1 ] ];
  770. vVelocity2 += vDeltaVelocity;
  771. }
  772. }
  773. else if ( rod.nNode[ 0 ] >= nStaticNodes )
  774. {
  775. vOrigin1 -= vDeltaDistance;
  776. if ( pStretchForce )
  777. {
  778. Vector vDeltaVelocity = vDeltaDistance * pStretchForce[ rod.nNode[ 0 ] ];
  779. vVelocity1 -= vDeltaVelocity;
  780. }
  781. }
  782. }
  783. }
  784. // the rest is forward compatibility stuff
  785. float flRodStiffness = expf( -m_flThreadStretch );
  786. float flSurfaceStiffness = expf( -m_flSurfaceStretch );
  787. m_pFeModel->RelaxBend( pPos, flRodStiffness );
  788. {
  789. MPROF_AUTO_FAST( FeRelaxQuads );
  790. if ( m_bEnableSimd )
  791. {
  792. m_pFeModel->RelaxSimdQuads( pPos, flSurfaceStiffness, flConstraintScale, m_nSimFlags & SOFTBODY_SIM_EXPERIMENTAL_1 );
  793. m_pFeModel->RelaxSimdTris( pPos, flSurfaceStiffness, flConstraintScale );
  794. }
  795. else
  796. {
  797. m_pFeModel->RelaxQuads( pPos, flSurfaceStiffness, flConstraintScale, m_nSimFlags & SOFTBODY_SIM_EXPERIMENTAL_1 );
  798. m_pFeModel->RelaxTris( pPos, flSurfaceStiffness, flConstraintScale );
  799. }
  800. }
  801. }
  802. void CSoftbody::ResolveAnimAttraction_S1( float flTimeStep )
  803. {
  804. VectorAligned *pPos = m_pPos1;
  805. AssertDbg( !m_pFeModel->m_pNodeToCtrl );
  806. if ( const FeNodeIntegrator_t *pIntegrator = m_pFeModel->m_pNodeIntegrator )
  807. {
  808. for ( uint nDynNode = m_pFeModel->m_nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  809. {
  810. VectorAligned &refParticleOrigin = pPos[ nDynNode ];
  811. Vector vAnimPos = GetAnim( nDynNode ).GetOrigin();
  812. Vector vDelta = vAnimPos - refParticleOrigin;
  813. vDelta *= pIntegrator[ nDynNode ].flAnimationVertexAttraction * flTimeStep;
  814. refParticleOrigin += vDelta;
  815. }
  816. }
  817. // convert velocities back to positions for possible verlet integration on the next step
  818. for ( uint nDynNode = m_pFeModel->m_nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  819. {
  820. m_pPos0[ nDynNode ] = pPos[ nDynNode ] - m_pPos0[ nDynNode ] * flTimeStep;
  821. }
  822. }
  823. //-------------------------------------------------------------------------------------------------
  824. FORCEINLINE AABB_t AsBounds3( const FeAabb_t &bbox )
  825. {
  826. AABB_t out;
  827. out.m_vMinBounds = AsVector( bbox.m_vMinBounds );
  828. out.m_vMaxBounds = AsVector( bbox.m_vMaxBounds );
  829. return out;
  830. }
  831. void CSoftbody::Collide()
  832. {
  833. if ( !m_pAabb )
  834. return; // no memory preallocated to compute AABBs, presumably because we don't have anything to collide with
  835. CHANGE_GUARD();
  836. //if ( !( ( m_nEnableWorldShapeCollision && m_pEnvironment ) | m_pFeModel->m_nTaperedCapsuleStretchCount | m_pFeModel->m_nTaperedCapsuleRigidCount | m_pFeModel->m_nSphereRigidCount ) )
  837. // return; // nothing to collide with, let's not recompute AABBs
  838. MPROF_AUTO_FAST( SoftbodyStepCollide );
  839. m_pFeModel->ComputeCollisionTreeBounds( m_pPos1 + m_pFeModel->m_nStaticNodes, m_pAabb );
  840. CollideWithRigidsInternal();
  841. CollideWithWorldInternal();
  842. }
  843. void ProjectParticleOutOfSphere( VectorAligned &refParticle, const Vector &vSphereCenter, float flSumRadius )
  844. {
  845. Vector vDist = refParticle - vSphereCenter;
  846. float flDistSqr = vDist.LengthSqr();
  847. if ( flDistSqr < 0.01f )
  848. {
  849. // unstable; TODO: go along the normal?
  850. refParticle.z = vSphereCenter.z + flSumRadius;
  851. }
  852. else
  853. {
  854. if ( flDistSqr < Sqr( flSumRadius ) )
  855. {
  856. Vector vRenormalized = vDist * flSumRadius / sqrtf( flDistSqr );
  857. refParticle = vSphereCenter + vRenormalized;
  858. }
  859. }
  860. }
  861. float ProjectParticleOutOfSphere_Sticky( VectorAligned &refParticle, const Vector &vSphereCenter, float flSumRadius, float flStickiness )
  862. {
  863. Vector vDist = refParticle - vSphereCenter;
  864. float flDistSqr = vDist.LengthSqr();
  865. if ( flDistSqr < 0.01f )
  866. {
  867. // unstable; TODO: go along the normal?
  868. refParticle.z = vSphereCenter.z + flSumRadius;
  869. return flStickiness; // full stickiness
  870. }
  871. else if ( flDistSqr < Sqr( flSumRadius ) )
  872. {
  873. Vector vRenormalized = vDist * flSumRadius / sqrtf( flDistSqr );
  874. refParticle = vSphereCenter + vRenormalized;
  875. return flStickiness; // full stickiness
  876. }
  877. else if ( flDistSqr < Sqr( flSumRadius + g_flStickyDist ) )
  878. {
  879. return flStickiness * ( flSumRadius + g_flStickyDist - sqrtf( flDistSqr ) );
  880. }
  881. return 0.0f;//too far for any stickiness
  882. }
  883. class CTaperedCapsuleColliderFunctor_Slidy : public CTaperedCapsuleColliderFunctor
  884. {
  885. public:
  886. void operator()( uint nDynNode ) const
  887. {
  888. VectorAligned &refParticle = m_pDynPos1[ nDynNode ];
  889. Vector d = refParticle - m_vSphereCenter0;
  890. float flNodeRadius = m_pNodeCollisionRadii ? m_pNodeCollisionRadii[ nDynNode ] : 0;
  891. float flNodeCoreRadius = flNodeRadius * 0.5f;
  892. float proj = DotProduct( d, m_vAxisX );
  893. Vector vOrtho = d - m_vAxisX * proj;
  894. float flLineDist = vOrtho.Length();
  895. float flAffineProj = proj + flLineDist * m_flSlope;
  896. if ( flAffineProj <= 0 )
  897. {
  898. // projection is onto Sphere0
  899. ProjectParticleOutOfSphere( refParticle, m_vSphereCenter0, flNodeCoreRadius + m_vSphereCenter0.w );
  900. }
  901. else if ( flAffineProj > m_flDist )
  902. {
  903. // projection is onto Sphere1
  904. ProjectParticleOutOfSphere( refParticle, m_vSphereCenter1, flNodeRadius + m_vSphereCenter1.w );
  905. }
  906. else
  907. {
  908. // projection is onto the interior conical surface of the tapered capsule
  909. float flBlend = flAffineProj / m_flDist;
  910. ProjectParticleOutOfSphere( refParticle, m_vSphereCenter0 * ( 1 - flBlend ) + m_vSphereCenter1 * flBlend, flNodeRadius + m_vSphereCenter0.w * ( 1 - flBlend ) + m_vSphereCenter1.w * flBlend );
  911. }
  912. }
  913. };
  914. class CTaperedCapsuleColliderFunctor_Sticky : public CTaperedCapsuleColliderFunctor
  915. {
  916. public:
  917. void operator()( uint nDynNode ) const
  918. {
  919. VectorAligned &refParticle = m_pDynPos1[ nDynNode ];
  920. Vector d = refParticle - m_vSphereCenter0;
  921. float flNodeRadius = m_pNodeCollisionRadii ? m_pNodeCollisionRadii[ nDynNode ] : 0;
  922. float flNodeCoreRadius = flNodeRadius * 0.5f;
  923. float proj = DotProduct( d, m_vAxisX );
  924. Vector vOrtho = d - m_vAxisX * proj;
  925. float flLineDist = vOrtho.Length();
  926. float flAffineProj = proj + flLineDist * m_flSlope;
  927. if ( flAffineProj <= 0 )
  928. {
  929. // projection is onto Sphere0
  930. float flNewStickiness = ProjectParticleOutOfSphere_Sticky( refParticle, m_vSphereCenter0, flNodeCoreRadius + m_vSphereCenter0.w, m_flStickiness );
  931. if ( flNewStickiness > 0.01f )
  932. {
  933. m_pSoftbody->GlueNode( nDynNode, m_nParentNode[ 0 ], flNewStickiness );
  934. }
  935. }
  936. else if ( flAffineProj > m_flDist )
  937. {
  938. // projection is onto Sphere1
  939. float flNewStickiness = ProjectParticleOutOfSphere_Sticky( refParticle, m_vSphereCenter1, flNodeRadius + m_vSphereCenter1.w, m_flStickiness );
  940. if ( flNewStickiness > 0.01f )
  941. {
  942. m_pSoftbody->GlueNode( nDynNode, m_nParentNode[ 1 ], flNewStickiness );
  943. }
  944. }
  945. else
  946. {
  947. // projection is onto the interior conical surface of the tapered capsule
  948. float flBlend = flAffineProj / m_flDist;
  949. float flNewStickiness = ProjectParticleOutOfSphere_Sticky( refParticle, m_vSphereCenter0 * ( 1 - flBlend ) + m_vSphereCenter1 * flBlend, flNodeRadius + m_vSphereCenter0.w * ( 1 - flBlend ) + m_vSphereCenter1.w * flBlend, m_flStickiness );
  950. if ( flNewStickiness > 0.01f )
  951. {
  952. m_pSoftbody->GlueNode( nDynNode, m_nParentNode[ 0 ], m_nParentNode[ 1 ], flNewStickiness, flBlend );
  953. }
  954. }
  955. }
  956. };
  957. class CSphereColliderFunctor_Slidy : public CSphereColliderFunctor
  958. {
  959. public:
  960. CSphereColliderFunctor_Slidy( CSoftbody *pSoftbody, const Vector &vSphereCenter, float flSphereRadius, float flStickiness, uint16 nParentNode ) :
  961. CSphereColliderFunctor( pSoftbody, vSphereCenter, flSphereRadius, flStickiness, nParentNode )
  962. {
  963. }
  964. void operator () ( uint nDynNode )const
  965. {
  966. VectorAligned &refParticle = m_pDynPos1[ nDynNode ];
  967. float flSumRadius = m_Sphere.w;
  968. if ( m_pNodeCollisionRadii )
  969. {
  970. flSumRadius += m_pNodeCollisionRadii[ nDynNode ];
  971. }
  972. ProjectParticleOutOfSphere( refParticle, m_Sphere, flSumRadius );
  973. }
  974. };
  975. class CSphereColliderFunctor_Sticky : public CSphereColliderFunctor
  976. {
  977. public:
  978. void operator () ( uint nDynNode )const
  979. {
  980. VectorAligned &refParticle = m_pDynPos1[ nDynNode ];
  981. float flSumRadius = m_Sphere.w;
  982. if ( m_pNodeCollisionRadii )
  983. {
  984. flSumRadius += m_pNodeCollisionRadii[ nDynNode ];
  985. }
  986. float flNewStickiness = ProjectParticleOutOfSphere_Sticky( refParticle, m_Sphere, flSumRadius, m_flStickiness );
  987. if ( flNewStickiness > 0.01f )
  988. {
  989. m_pSoftbody->GlueNode( nDynNode, m_nParentNode, flNewStickiness );
  990. }
  991. }
  992. };
  993. void CSphereColliderFunctor::Collide( uint16 nCollisionMask )
  994. {
  995. FeAabb_t aabb;
  996. fltx4 f4CenterAndRadius = LoadAlignedSIMD( m_Sphere.Base() ), f4SphereRadius = SplatWSIMD( f4CenterAndRadius );
  997. aabb.m_vMinBounds = f4CenterAndRadius - f4SphereRadius;
  998. aabb.m_vMaxBounds = f4CenterAndRadius + f4SphereRadius;
  999. const CFeModel *pFeModel = m_pSoftbody->GetFeModel();
  1000. if ( m_flStickiness <= 0.01f )
  1001. {
  1002. pFeModel->CastBox( nCollisionMask, aabb, m_pSoftbody->m_pAabb, static_cast< CSphereColliderFunctor_Slidy& >( *this ) );
  1003. }
  1004. else
  1005. {
  1006. aabb.m_vMinBounds -= ReplicateX4( g_flStickyDist );
  1007. aabb.m_vMaxBounds += ReplicateX4( g_flStickyDist );
  1008. pFeModel->CastBox( nCollisionMask, aabb, m_pSoftbody->m_pAabb, static_cast< CSphereColliderFunctor_Sticky& >( *this ) );
  1009. }
  1010. }
  1011. void CTaperedCapsuleColliderFunctor::Collide( uint16 nCollisionMask )
  1012. {
  1013. Vector vAxis = m_vSphereCenter1 - m_vSphereCenter0;
  1014. float flDist = vAxis.Length();
  1015. float flStickOut = flDist - ( m_vSphereCenter1.w - m_vSphereCenter0.w ); // how much the small sphere sticks out of the large sphere
  1016. if ( flStickOut > /*tc.flRadius[ 0 ] **/ 0.5f ) // only collide with full capsule if sticks out enough
  1017. {
  1018. FeAabb_t aabb;
  1019. fltx4 f4Center0 = LoadAlignedSIMD( m_vSphereCenter0.Base() ), f4Center1 = LoadAlignedSIMD( m_vSphereCenter1.Base() ), f4SphereRadius0 = SplatWSIMD( f4Center0 ), f4SphereRadius1 = SplatWSIMD( f4Center1 );
  1020. aabb.m_vMinBounds = MinSIMD( f4Center0 - f4SphereRadius0, f4Center1 - f4SphereRadius1 );
  1021. aabb.m_vMaxBounds = MaxSIMD( f4Center0 + f4SphereRadius0, f4Center1 + f4SphereRadius1 );
  1022. const CFeModel *pFeModel = m_pSoftbody->GetFeModel();
  1023. if ( m_flStickiness <= 0.01f )
  1024. {
  1025. pFeModel->CastBox( nCollisionMask, aabb, m_pSoftbody->m_pAabb, static_cast< CTaperedCapsuleColliderFunctor_Slidy& >( *this ) );
  1026. }
  1027. else
  1028. {
  1029. aabb.m_vMinBounds -= ReplicateX4( g_flStickyDist );
  1030. aabb.m_vMinBounds += ReplicateX4( g_flStickyDist );
  1031. pFeModel->CastBox( nCollisionMask, aabb, m_pSoftbody->m_pAabb, static_cast< CTaperedCapsuleColliderFunctor_Sticky& >( *this ) );
  1032. }
  1033. }
  1034. else
  1035. {
  1036. CSphereColliderFunctor sphere;
  1037. sphere.m_Sphere = m_vSphereCenter1;
  1038. sphere.m_pDynPos1 = m_pDynPos1;
  1039. sphere.m_pNodeCollisionRadii = m_pNodeCollisionRadii;
  1040. sphere.m_pSoftbody = m_pSoftbody;
  1041. sphere.m_flStickiness = m_flStickiness;
  1042. sphere.m_nParentNode = m_nParentNode[ 1 ];
  1043. sphere.Collide( nCollisionMask ); // just do the big sphere, it almost encloses the small sphere
  1044. }
  1045. }
  1046. class CRnSphereColliderFunctor
  1047. {
  1048. VectorAligned m_Sphere;
  1049. VectorAligned *m_pDynPos1;
  1050. const float *m_pNodeCollisionRadii;
  1051. float m_flRadiusScale;
  1052. public:
  1053. CRnSphereColliderFunctor( const Vector &vCenter, float flRadius, VectorAligned *pDynPos1, const float *pNodeCollisionRadii, float flRadiusScale )
  1054. {
  1055. m_Sphere = vCenter;
  1056. m_Sphere.w = flRadius;
  1057. m_pDynPos1 = pDynPos1;
  1058. m_pNodeCollisionRadii = pNodeCollisionRadii;
  1059. m_flRadiusScale = flRadiusScale;
  1060. }
  1061. void operator () ( uint nDynNode )const
  1062. {
  1063. VectorAligned &refParticle = m_pDynPos1[ nDynNode ];
  1064. float flSumNodeRadius = m_pNodeCollisionRadii ? m_pNodeCollisionRadii[ nDynNode ] * m_flRadiusScale + m_Sphere.w : m_Sphere.w;
  1065. ProjectParticleOutOfSphere( refParticle, m_Sphere, flSumNodeRadius );
  1066. }
  1067. };
  1068. class CRnCapsuleColliderFunctor
  1069. {
  1070. Vector m_vCenter0;
  1071. Vector m_vCenter1;
  1072. Vector m_vAxisX;
  1073. float m_flRadius;
  1074. float m_flDist;
  1075. VectorAligned *m_pDynPos1;
  1076. const float *m_pNodeCollisionRadii;
  1077. float m_flRadiusScale;
  1078. public:
  1079. CRnCapsuleColliderFunctor( const Vector &vCenter0, const Vector &vCenter1, float flRadius, VectorAligned *pDynPos1, const float *pNodeCollisionRadii, float flRadiusScale )
  1080. {
  1081. m_vCenter0 = vCenter0;
  1082. m_vCenter1 = vCenter1;
  1083. m_flRadius = flRadius;
  1084. m_vAxisX = vCenter1 - vCenter0;
  1085. m_flDist = m_vAxisX.Length();
  1086. m_vAxisX /= m_flDist;
  1087. m_pDynPos1 = pDynPos1;
  1088. m_pNodeCollisionRadii = pNodeCollisionRadii;
  1089. m_flRadiusScale = flRadiusScale;
  1090. }
  1091. void operator()( uint nDynNode ) const
  1092. {
  1093. VectorAligned &refParticle = m_pDynPos1[ nDynNode ];
  1094. Vector d = refParticle - m_vCenter0;
  1095. float flSumNodeRadius = m_pNodeCollisionRadii ? m_pNodeCollisionRadii[ nDynNode ] * m_flRadiusScale + m_flRadius : m_flRadius;
  1096. float proj = DotProduct( d, m_vAxisX );
  1097. if ( proj <= 0 )
  1098. {
  1099. // projection is onto Sphere0
  1100. ProjectParticleOutOfSphere( refParticle, m_vCenter0, flSumNodeRadius );
  1101. }
  1102. else if ( proj > m_flDist )
  1103. {
  1104. // projection is onto Sphere1
  1105. ProjectParticleOutOfSphere( refParticle, m_vCenter1, flSumNodeRadius );
  1106. }
  1107. else
  1108. {
  1109. Vector vOrtho = d - m_vAxisX * proj;
  1110. float flLineDist = vOrtho.Length();
  1111. float flPushOutBy = flSumNodeRadius - flLineDist;
  1112. if ( flPushOutBy > 0 )
  1113. {
  1114. // project outward
  1115. if ( flLineDist > 0.0001f )
  1116. {
  1117. refParticle += ( flPushOutBy / flLineDist ) * vOrtho;
  1118. }
  1119. else
  1120. {
  1121. refParticle += VectorPerpendicularToVector( m_vAxisX ) * flSumNodeRadius;
  1122. }
  1123. }
  1124. }
  1125. }
  1126. };
  1127. #ifdef SOURCE2_SUPPORT
  1128. bool SoftbodyGjk( Vector &refOut, const Vector &vParticle, const CGJKHull& proxy2, int nIterationCount = 16 )
  1129. {
  1130. // Build initial simplex
  1131. CRnSimplex simplex, last_simplex;
  1132. simplex.Init( vParticle, proxy2.GetVertex( 0 ) );
  1133. // Compute initial distance
  1134. float d2 = FLT_MAX;
  1135. bool bOverlap = false; NOTE_UNUSED( bOverlap );
  1136. // Run GJK
  1137. for ( int nIteration = 0; nIteration < nIterationCount; ++nIteration )
  1138. {
  1139. // Solve simplex and check if associated tetrahedron contains the origin
  1140. simplex.Solve();
  1141. if ( simplex.VertexCount() == MAX_SIMPLEX_VERTICES )
  1142. {
  1143. // Overlap
  1144. bOverlap = true;
  1145. break;
  1146. }
  1147. // Test distance progression
  1148. float d2_old = d2;
  1149. Vector cp = simplex.GetClosestPoint();
  1150. d2 = Dot( cp, cp );
  1151. if ( d2 >= d2_old )
  1152. {
  1153. // Reconstruct last simplex
  1154. AssertDbg( !last_simplex.IsEmpty() );
  1155. simplex = last_simplex;
  1156. break;
  1157. }
  1158. // Build a new tentative support vertex
  1159. Vector v = simplex.GetSearchDirection();
  1160. if ( LengthSq( v ) < 1000.0f * FLT_MIN )
  1161. {
  1162. // The origin is probably contained by a line segment
  1163. // or triangle. Thus the shapes are overlapped.
  1164. // We should return zero here because there may be overlap.
  1165. // In case the simplex is a point, segment, or triangle it is difficult
  1166. // to determine if the origin is contained in the CSO or very close to it.
  1167. // We'll use SAT outside to resolve this - when Distance is exactly 0.0, we should run SAT as it may mean negative distance (penetration) in reality, and GJK doesn not compute penetration distance.
  1168. bOverlap = true;
  1169. break;
  1170. }
  1171. const Vector &w1 = vParticle;
  1172. int nIndex2 = proxy2.GetSupport( v );
  1173. Vector w2 = proxy2.GetVertex( nIndex2 );
  1174. last_simplex = simplex;
  1175. if ( !simplex.AddVertex( 0, w1, nIndex2, w2 ) )
  1176. {
  1177. // cache hit; we may start infinitely cycling - break the cycle
  1178. break;
  1179. }
  1180. }
  1181. Vector vPoint1, vPoint2;
  1182. simplex.BuildWitnessPoints( vPoint1, vPoint2 );
  1183. Assert( !bOverlap || Distance( vPoint1, vPoint2 ) < FLT_EPSILON );
  1184. refOut = vPoint2;
  1185. return !bOverlap;
  1186. }
  1187. class CRnHullColliderFunctor : public CGJKHull
  1188. {
  1189. const Transform &m_Xform;
  1190. VectorAligned *m_pDynPos1;
  1191. const float *m_pNodeCollisionRadii;
  1192. float m_flRadiusScale;
  1193. float m_flAddRadius;
  1194. public:
  1195. CRnHullColliderFunctor( const RnHull_t *pHull, float flHullScale, const Transform &xform, float flAddRadius, VectorAligned *pDynPos1, const float *pNodeCollisionRadii, float flRadiusScale ) :
  1196. CGJKHull( pHull, flHullScale ),
  1197. m_Xform( xform ),
  1198. m_flAddRadius( flAddRadius ),
  1199. m_flRadiusScale( flRadiusScale ),
  1200. m_pDynPos1( pDynPos1 ),
  1201. m_pNodeCollisionRadii( pNodeCollisionRadii )
  1202. {
  1203. }
  1204. static float Distance( const RnPlane_t &plane, float flScale, const Vector &vParticle )
  1205. {
  1206. return DotProduct( plane.m_vNormal, vParticle ) - flScale * plane.m_flOffset;
  1207. }
  1208. AABB_t GetAabb()
  1209. {
  1210. AABB_t aabb;
  1211. aabb.MakeInvalid();
  1212. for ( int nVertex = 0; nVertex < m_pHull->GetVertexCount(); ++nVertex )
  1213. {
  1214. aabb |= Mul( m_Xform, GetVertex( nVertex ) ); // GetVertex takes the scale into account
  1215. }
  1216. aabb.Expand( m_flAddRadius );
  1217. return aabb;
  1218. }
  1219. void operator()( uint nDynNode ) const
  1220. {
  1221. VectorAligned &refParticle = m_pDynPos1[ nDynNode ];
  1222. Vector vLocalParticle = TMul( m_Xform, refParticle );
  1223. RnDistanceQueryResult_t result;
  1224. Vector vPointOnHull;
  1225. float flSumNodeRadius = m_pNodeCollisionRadii ? m_pNodeCollisionRadii[ nDynNode ] * m_flRadiusScale + m_flAddRadius : m_flAddRadius;
  1226. if ( SoftbodyGjk( vPointOnHull, vLocalParticle, *this ) )
  1227. {
  1228. // GJK succeeded
  1229. Vector vNormal = vLocalParticle - vPointOnHull;
  1230. float flDistance = vNormal.Length();
  1231. if ( flDistance < flSumNodeRadius )
  1232. {
  1233. AssertDbg( flDistance > FLT_EPSILON );
  1234. refParticle += Mul( m_Xform.R, vNormal ) * ( flSumNodeRadius / flDistance - 1 );
  1235. }
  1236. }
  1237. else
  1238. {
  1239. // GJK failed; run SAT
  1240. const RnPlane_t *pPlanes = m_pHull->m_Planes.Base();
  1241. int nPlaneCount = m_pHull->GetPlaneCount();
  1242. float flScale = m_flScale;
  1243. int nBestPlane = 0;
  1244. float flMaxDist = Distance( pPlanes[0], flScale, vLocalParticle );
  1245. for ( int nPlane = 1; nPlane < nPlaneCount; ++nPlane )
  1246. {
  1247. const RnPlane_t &plane = pPlanes[ nPlane ];
  1248. float flDistance = Distance( plane, flScale, vLocalParticle );
  1249. if ( flDistance > flMaxDist )
  1250. {
  1251. flMaxDist = flDistance;
  1252. nBestPlane = nPlane;
  1253. }
  1254. }
  1255. AssertDbg( flMaxDist < FLT_EPSILON );
  1256. if ( flMaxDist < flSumNodeRadius )
  1257. {
  1258. refParticle += ( flSumNodeRadius - flMaxDist ) * Mul( m_Xform.R, pPlanes[ nBestPlane ].m_vNormal );
  1259. }
  1260. }
  1261. }
  1262. };
  1263. class CRnTriColliderFunctor
  1264. {
  1265. CFeModel::Aabb_t m_Aabb;
  1266. VectorAligned *m_pDynPos1;
  1267. const float *m_pNodeCollisionRadii;
  1268. float m_flRadiusScale;
  1269. float m_flAddRadius;
  1270. public:
  1271. Vector m_v0;
  1272. Vector m_vNormal;
  1273. Vector m_d1;
  1274. Vector m_d2;
  1275. float m_flD1x; // the edge 0-1 goes from (0,0) to ( D1x, 0)
  1276. Vector2D m_D2; // the edge 0-2 goes from (0,0) to ( D2x, D2y )
  1277. Vector2D m_n12;// 2D normal to edge 1-2, pointing inside triangle, is -D2y, D2x-D1x
  1278. Vector2D m_n20;// 2D normal to edge 2-0, pointing inside triangle, is D2y, -D2x
  1279. mutable int m_nProbedNodes;
  1280. // mutable int m_nMovedNodes;
  1281. public:
  1282. CRnTriColliderFunctor( float flAddRadius, VectorAligned *pDynPos1, const float *pNodeCollisionRadii, float flRadiusScale ) :
  1283. m_flAddRadius( flAddRadius ),
  1284. m_flRadiusScale( flRadiusScale ),
  1285. m_pDynPos1( pDynPos1 ),
  1286. m_pNodeCollisionRadii( pNodeCollisionRadii )
  1287. {
  1288. m_nProbedNodes = 0;
  1289. // m_nMovedNodes = 0;
  1290. }
  1291. void PrepareTriangle( CRnMeshShape *pMesh, uint nTri, const Transform &bodyTransform )
  1292. {
  1293. VectorAligned v0, v1, v2;
  1294. pMesh->GetTriangle( nTri, v0, v1, v2 );
  1295. m_Aabb.m_vMaxBounds = m_Aabb.m_vMinBounds = LoadAlignedSIMD( &v0 );
  1296. m_Aabb |= LoadAlignedSIMD( &v1 );
  1297. m_Aabb |= LoadAlignedSIMD( &v2 );
  1298. m_v0 = bodyTransform * v0;
  1299. Vector d1 = bodyTransform.R * ( v1 - v0 );
  1300. m_flD1x = d1.Length();
  1301. m_d1 = d1 / m_flD1x;
  1302. Vector d2 = bodyTransform.R * ( v2 - v0 );
  1303. m_D2.x = DotProduct( m_d1, d2 );
  1304. m_d2 = d2 - m_d1 * m_D2.x;
  1305. m_D2.y = m_d2.Length();
  1306. m_d2 /= m_D2.y;
  1307. m_n12 = Vector2D( -m_D2.y, m_D2.x - m_flD1x );
  1308. m_n12.NormalizeInPlace();
  1309. m_n20 = Vector2D( m_D2.y, -m_D2.x );
  1310. m_n20.NormalizeInPlace();
  1311. m_vNormal = CrossProduct( m_d1, m_d2 );
  1312. AssertDbg( fabsf( m_vNormal.Length() - 1 ) < 0.001f );
  1313. }
  1314. const CFeModel::Aabb_t &GetAabb()const
  1315. {
  1316. return m_Aabb;
  1317. }
  1318. void operator()( uint nDynNode )const
  1319. {
  1320. m_nProbedNodes++;
  1321. VectorAligned &refParticle = m_pDynPos1[ nDynNode ];
  1322. float flSumNodeRadius = m_pNodeCollisionRadii ? m_pNodeCollisionRadii[ nDynNode ] * m_flRadiusScale + m_flAddRadius : m_flAddRadius;
  1323. Vector d = refParticle - m_v0;
  1324. float flDistNormal = DotProduct( m_vNormal, d );
  1325. if ( fabsf( flDistNormal ) > flSumNodeRadius )
  1326. return;
  1327. float x = DotProduct( m_d1, d ), y = DotProduct( m_d2, d ); // x, y of the projection of the point onto the plane
  1328. // 2D SAT: go through all edges, find the best separating axis (min edge penetration )
  1329. float flPenetration01 = y;
  1330. float flPenetration12 = m_n12.x * ( x - m_flD1x ) + m_n12.y * y;
  1331. float flPenetration20 = m_n20.x * x + m_n20.y * y;
  1332. float flEdgePenetration = Min( flPenetration01, Min( flPenetration12, flPenetration20 ) );
  1333. if ( flEdgePenetration >= 0 )
  1334. {
  1335. // the point projects inside the triangle
  1336. refParticle += ( flSumNodeRadius - flDistNormal ) * m_vNormal;
  1337. //++m_nMovedNodes;
  1338. }
  1339. else if ( flDistNormal > 0 )
  1340. {
  1341. // the point is outside the triangle. Find the closest point and see if it's farther than the edge
  1342. float flDistSqr0 = Sqr( x ) + Sqr( y );
  1343. float flDistSqr1 = Sqr( x - m_flD1x ) + Sqr( y );
  1344. float flDistSqr2 = Sqr( x - m_D2.x ) + Sqr( y - m_D2.y );
  1345. float flVertDistSqr = Min( flDistSqr0 , Min( flDistSqr1, flDistSqr2 ) ); // closest-vertex distance sqr
  1346. float flDistEdgeSqr = flEdgePenetration * flEdgePenetration; // note: edge penetration = -distance; square removes the sign
  1347. float flDistXYSqr = Max( flVertDistSqr, flDistEdgeSqr );
  1348. float flDist = sqrtf( flDistXYSqr + Sqr( flDistNormal ) );
  1349. if ( flDist < flSumNodeRadius )
  1350. {
  1351. refParticle += ( flSumNodeRadius - flDist ) * m_vNormal;
  1352. //++m_nMovedNodes;
  1353. }
  1354. }
  1355. else
  1356. {
  1357. // from behind the triangle, we'll just ignore the particle
  1358. }
  1359. }
  1360. };
  1361. #endif
  1362. void CSoftbody::CollideWithRigidsInternal()
  1363. {
  1364. for ( uint nTapCap = m_pFeModel->m_nTaperedCapsuleStretchCount; nTapCap-- > 0; )
  1365. {
  1366. const FeTaperedCapsuleStretch_t &tc = m_pFeModel->m_pTaperedCapsuleStretches[ nTapCap ];
  1367. const VectorAligned &vCenter1 = m_pPos1[ tc.nNode[ 1 ] ];
  1368. AssertDbg( tc.flRadius[ 0 ] <= tc.flRadius[ 1 ] );
  1369. if ( tc.nNode[ 0 ] == tc.nNode[ 1 ] )
  1370. {
  1371. CSphereColliderFunctor sphere( this, vCenter1, tc.flRadius[ 1 ] * m_flModelScale, tc.flStickiness, tc.nNode[ 1 ] );
  1372. sphere.Collide( tc.nCollisionMask ); // this collision is a sphere
  1373. }
  1374. else
  1375. {
  1376. const VectorAligned &vCenter0 = m_pPos1[ tc.nNode[ 0 ] ];
  1377. CTaperedCapsuleColliderFunctor capsule( this, vCenter0, tc.flRadius[ 0 ] * m_flModelScale, vCenter1, tc.flRadius[ 1 ] * m_flModelScale, tc.flStickiness, tc.nNode[ 0 ], tc.nNode[ 1 ] );
  1378. capsule.Collide( tc.nCollisionMask );
  1379. }
  1380. }
  1381. for ( uint nTapCap = m_pFeModel->m_nTaperedCapsuleRigidCount; nTapCap-- > 0; )
  1382. {
  1383. const FeTaperedCapsuleRigid_t &tc = m_pFeModel->m_pTaperedCapsuleRigids[ nTapCap ];
  1384. const matrix3x4a_t &tm = GetAnim( m_pFeModel->NodeToCtrl( tc.nNode ) );
  1385. CTaperedCapsuleColliderFunctor capsule( this, tm.TransformVector( tc.vCenter[ 0 ] * m_flModelScale ), tc.flRadius[ 0 ] * m_flModelScale, tm.TransformVector( tc.vCenter[ 1 ] * m_flModelScale ), tc.flRadius[ 1 ] * m_flModelScale, tc.flStickiness, tc.nNode, tc.nNode );
  1386. capsule.Collide( tc.nCollisionMask );
  1387. }
  1388. for ( uint nSphere = m_pFeModel->m_nSphereRigidCount; nSphere-- > 0; )
  1389. {
  1390. const FeSphereRigid_t &sr = m_pFeModel->m_pSphereRigids[ nSphere ];
  1391. const matrix3x4a_t &tm = GetAnim( m_pFeModel->NodeToCtrl( sr.nNode ) );
  1392. CSphereColliderFunctor sphere( this, tm.TransformVector( sr.vCenter * m_flModelScale ), sr.flRadius* m_flModelScale, sr.flStickiness, sr.nNode );
  1393. sphere.Collide( sr.nCollisionMask );
  1394. }
  1395. }
  1396. void CSoftbody::SetCollisionAttributes( const RnCollisionAttr_t &attr )
  1397. {
  1398. m_CollisionAttributes = attr;
  1399. }
  1400. //MPROF_NODE( NodeMeshProbes, "Softbody:Node-Tris", "Physics" );
  1401. void CSoftbody::CollideWithWorldInternal()
  1402. {
  1403. uint8 nEnableWorldShapeCollision = m_nEnableWorldShapeCollision;
  1404. if ( !( nEnableWorldShapeCollision && m_pEnvironment ) )
  1405. return;
  1406. // probe the broadphase
  1407. const FeAabb_t &bbox = m_pAabb[ m_pFeModel->GetTreeRootAabbIndex() ]; //
  1408. CProxyVector proxies;
  1409. CDynamicTree *pTree = m_pEnvironment->GetBroadphaseTree();
  1410. if ( !pTree )
  1411. return;
  1412. pTree->Query( proxies, AsBounds3( bbox ) );
  1413. const CFeModel *pFeModel = GetFeModel();
  1414. for ( int ndx = 0; ndx < proxies.Count(); ++ndx )
  1415. {
  1416. int32 nProxy = proxies[ ndx ];
  1417. CSoftbodyCollisionShape *pShape = ( CSoftbodyCollisionShape* )pTree->GetUserData( nProxy );
  1418. uint16 nPairFlags = m_pEnvironment->m_Filter.TestSimulation( pShape->GetCollisionAttributes(), GetCollisionAttributes() );
  1419. if ( !( nPairFlags & INTERSECTION_PAIR_RESOLVE_CONTACTS ) )
  1420. {
  1421. continue; // We don't need to resolve contacts. And we don't really do anything else in cloth right now.
  1422. }
  1423. PhysicsShapeType_t nShapeType = pShape->GetType();
  1424. if ( nEnableWorldShapeCollision & ( 1 << nShapeType ) )
  1425. {
  1426. switch ( nShapeType )
  1427. {
  1428. case SHAPE_SPHERE:
  1429. {
  1430. CSoftbodyCollisionSphere *pSphere = static_cast< CSoftbodyCollisionSphere * >( pShape );
  1431. Vector vCenter = pSphere->GetCenter();
  1432. float flRadius = pSphere->GetRadius() + pFeModel->m_flAddWorldCollisionRadius * m_flModelScale;
  1433. fltx4 v4Center = LoadUnaligned3SIMD( &vCenter ), f4Radius = ReplicateX4( flRadius );
  1434. CRnSphereColliderFunctor collider( vCenter, flRadius, m_pPos1 + pFeModel->m_nStaticNodes, pFeModel->m_pNodeCollisionRadii, m_flModelScale );
  1435. FeAabb_t shapeAabb;
  1436. shapeAabb.m_vMinBounds = v4Center - f4Radius;
  1437. shapeAabb.m_vMaxBounds = v4Center + f4Radius;
  1438. pFeModel->CastBox<CRnSphereColliderFunctor, true>( 0, shapeAabb, m_pAabb, collider );
  1439. }
  1440. break;
  1441. case SHAPE_CAPSULE:
  1442. {
  1443. CSoftbodyCollisionCapsule *pCapsule = static_cast< CSoftbodyCollisionCapsule* >( pShape );
  1444. const Vector &vCenter0 = pCapsule->GetCenter( 0 ), &vCenter1 = pCapsule->GetCenter( 1 );
  1445. float flRadius = pCapsule->GetRadius() + pFeModel->m_flAddWorldCollisionRadius * m_flModelScale;
  1446. fltx4 v4Center0 = LoadUnaligned3SIMD( &vCenter0 ), v4Center1 = LoadUnaligned3SIMD( &vCenter1 ), f4Radius = ReplicateX4( flRadius );
  1447. CRnCapsuleColliderFunctor collider( vCenter0, vCenter1, flRadius, m_pPos1 + pFeModel->m_nStaticNodes, pFeModel->m_pNodeCollisionRadii, m_flModelScale );
  1448. FeAabb_t shapeAabb;
  1449. shapeAabb.m_vMinBounds = MinSIMD( v4Center0, v4Center1 ) - f4Radius;
  1450. shapeAabb.m_vMaxBounds = MaxSIMD( v4Center0, v4Center1 ) + f4Radius;
  1451. pFeModel->CastBox< CRnCapsuleColliderFunctor, true >( 0, shapeAabb, m_pAabb, collider );
  1452. }
  1453. break;
  1454. #ifdef SOURCE2
  1455. case SHAPE_HULL:
  1456. {
  1457. CRnHullShape *pHull = static_cast< CRnHullShape* >( pShape );
  1458. CRnHullColliderFunctor collider( pHull->GetHull(), pHull->GetScale(), pBody->GetTransform(), pFeModel->m_flAddWorldCollisionRadius * m_flModelScale, m_pPos1 + pFeModel->m_nStaticNodes, pFeModel->m_pNodeCollisionRadii, m_flModelScale );
  1459. pFeModel->CastBox< CRnHullColliderFunctor, true >( 0, CFeModel::Aabb_t( collider.GetAabb() ), m_pAabb, collider );
  1460. }
  1461. break;
  1462. case SHAPE_MESH:
  1463. {
  1464. CRnMeshShape *pMesh = static_cast< CRnMeshShape* >( pShape );
  1465. // cheap test version
  1466. CTriangleVector tris;
  1467. float flAddRadius = pFeModel->m_flAddWorldCollisionRadius * m_flModelScale;
  1468. CRnTriColliderFunctor collider( flAddRadius, m_pPos1 + pFeModel->m_nStaticNodes, pFeModel->m_pNodeCollisionRadii, m_flModelScale );
  1469. Transform bodyTransform = pBody->GetTransform();
  1470. FeAabb_t bboxClothForMesh = bbox; // blow up the cloth for mesh query
  1471. fltx4 f4AddWorldCollisionRadius = ReplicateX4( flAddRadius );
  1472. bboxClothForMesh.AddExtents( f4AddWorldCollisionRadius );
  1473. pMesh->Query( tris, TMul( bodyTransform, AsBounds3( bboxClothForMesh ) ) );
  1474. //MPROF_AUTO_FAST( NodeMeshProbes );
  1475. for ( uint32 nTri : tris )// for ( int nTri = 0; nTri < pMesh->GetMesh()->m_Triangles.Count(); ++nTri )
  1476. {
  1477. // float flTransientError = pFeModel->ComputeCollisionTreeBoundsError( m_pPos1 + pFeModel->m_nStaticNodes, m_pAabb ); // we move nodes around, so they'll be slightly outside of their respective parents' bboxes, but hopefully that's ok. Updating bboxes is possible, but adds a factor of logN to the complexity, or an extra linear search at the end of the overlap query
  1478. collider.PrepareTriangle( pMesh, nTri, bodyTransform );
  1479. CFeModel::Aabb_t bboxCollider = collider.GetAabb();
  1480. bboxCollider.AddExtents( f4AddWorldCollisionRadius ); // triangle is thin, particles are thin, we need to add some thickness to one or the other. Can't add it to the particle tree without increasing complexity of the query, so I will have to add it to triangle
  1481. pFeModel->CastBox< CRnTriColliderFunctor, true >( 0, bboxCollider, m_pAabb, collider ); // for ( uint nDynNode = 0; nDynNode < pFeModel->GetDynamicNodeCount(); ++nDynNode ) collider( nDynNode );
  1482. }
  1483. //MPROF_SET_COUNT( NodeMeshProbes, collider.m_nProbedNodes );
  1484. }
  1485. break;
  1486. #endif
  1487. }
  1488. }
  1489. }
  1490. }
  1491. bool CSoftbody::AdvanceSleepCounter()
  1492. {
  1493. if ( m_nStateCounter++ > FRAMES_INVISIBLE_BEFORE_DORMANT )
  1494. {
  1495. GoDormant();
  1496. return true;
  1497. }
  1498. else
  1499. {
  1500. return false;
  1501. }
  1502. }
  1503. CSoftbody::CConstraintIterator::CConstraintIterator( CSoftbody *pSoftbody )
  1504. {
  1505. m_pSoftbody = pSoftbody;
  1506. m_pEnvironment = pSoftbody->m_pEnvironment;
  1507. if ( pSoftbody->m_pFeModel->m_pLegacyStretchForce || ( pSoftbody->m_pFeModel->m_nDynamicNodeFlags & FE_FLAG_UNINERTIAL_CONSTRAINTS ) )
  1508. {
  1509. m_PosBeforeCorrect.CopyArray( pSoftbody->m_pPos1, pSoftbody->m_nNodeCount );
  1510. }
  1511. }
  1512. const float g_flClothLegacyStretchForce = 0.95f;// this only matters for Dota legacy content
  1513. CSoftbody::CConstraintIterator::~CConstraintIterator()
  1514. {
  1515. if ( !m_PosBeforeCorrect.IsEmpty( ) )
  1516. {
  1517. //CRnSoftbodyChangeGuard cg( m_pSoftbody, "~CConstraintIterator" );
  1518. const CFeModel *pFeModel = m_pSoftbody->m_pFeModel;
  1519. VectorAligned *pPos0 = m_pSoftbody->m_pPos0, *pPos1 = m_pSoftbody->m_pPos1;
  1520. // In Source1, static particle velocities are always reset to 0, see CClothParticleState::Integrate()
  1521. uint nStaticNodes = pFeModel->m_nStaticNodes;
  1522. for ( uint nNode = 0; nNode < nStaticNodes; ++nNode )
  1523. {
  1524. pPos0[ nNode ] = pPos1[ nNode ];
  1525. }
  1526. if ( const float *pLegacyStretchForce = m_pSoftbody->m_pFeModel->m_pLegacyStretchForce )
  1527. {
  1528. AssertDbg( pFeModel->m_nDynamicNodeFlags & FE_FLAG_UNINERTIAL_CONSTRAINTS );
  1529. float dt = m_pSoftbody->m_flLastTimestep;
  1530. for ( uint nNode = pFeModel->m_nStaticNodes, nNodeCount = pFeModel->m_nNodeCount; nNode < nNodeCount; ++nNode )
  1531. {
  1532. float flLegacyStretchForce = pLegacyStretchForce[ nNode ] * dt; // 0.0 means no velocity at all will be created by solving any constraints; 1.0 means full velocity will be applied
  1533. pPos0[ nNode ] += ( pPos1[ nNode ] - m_PosBeforeCorrect[ nNode ] ) * clamp( 1.0f - flLegacyStretchForce, 0.0f, g_flClothLegacyStretchForce );
  1534. }
  1535. }
  1536. else
  1537. {
  1538. AssertDbg( pFeModel->m_nDynamicNodeFlags & FE_FLAG_UNINERTIAL_CONSTRAINTS ); // the only other useful case
  1539. for ( uint nNode = pFeModel->m_nStaticNodes, nNodeCount = pFeModel->m_nNodeCount; nNode < nNodeCount; ++nNode )
  1540. {
  1541. pPos0[ nNode ] += ( pPos1[ nNode ] - m_PosBeforeCorrect[ nNode ] );
  1542. }
  1543. }
  1544. }
  1545. }
  1546. void CSoftbody::CConstraintIterator::Iterate( int nIterations )
  1547. {
  1548. VectorAligned /*pPos0 = m_pSoftbody->m_pPos0, */*pPos1 = m_pSoftbody->m_pPos1;
  1549. const CFeModel *pFeModel = m_pSoftbody->m_pFeModel;
  1550. float flRodStiffness = expf( -m_pSoftbody->m_flThreadStretch / nIterations );
  1551. float flSurfaceStiffness = expf( -m_pSoftbody->m_flSurfaceStretch / nIterations );
  1552. float flConstraintScale = m_pSoftbody->m_flModelScale * m_pSoftbody->m_flClothScale;
  1553. bool bEnableSimd = m_pSoftbody->m_bEnableSimd;
  1554. for ( int nIteration = 0; nIteration < nIterations; ++nIteration )
  1555. {
  1556. if ( flRodStiffness > 0.01f )
  1557. {
  1558. MPROF_AUTO_FAST( FeRelaxRods );
  1559. if ( m_pSoftbody->m_bEnableFtlPass && nIteration + 1 == nIterations )
  1560. {
  1561. if ( bEnableSimd )
  1562. {
  1563. pFeModel->RelaxSimdRodsFtl( ( fltx4* ) pPos1, flRodStiffness, flConstraintScale );
  1564. }
  1565. else
  1566. {
  1567. pFeModel->RelaxRods2Ftl( pPos1, flRodStiffness, flConstraintScale );
  1568. }
  1569. }
  1570. else
  1571. {
  1572. if ( bEnableSimd )
  1573. {
  1574. pFeModel->RelaxSimdRods( ( fltx4* ) pPos1, flRodStiffness, flConstraintScale );
  1575. }
  1576. else
  1577. {
  1578. pFeModel->RelaxRods2( pPos1, flRodStiffness, flConstraintScale );
  1579. }
  1580. }
  1581. }
  1582. pFeModel->RelaxBend( pPos1, flRodStiffness );
  1583. {
  1584. MPROF_AUTO_FAST_COUNT( FeRelaxQuads, bEnableSimd ? pFeModel->m_nSimdQuadCount[ 0 ] * 4 : pFeModel->m_nQuadCount[ 0 ] );
  1585. if ( bEnableSimd )
  1586. {
  1587. pFeModel->RelaxSimdQuads( pPos1, flSurfaceStiffness, flConstraintScale, m_pSoftbody->m_nSimFlags & SOFTBODY_SIM_EXPERIMENTAL_1 );
  1588. pFeModel->RelaxSimdTris( pPos1, flSurfaceStiffness, flConstraintScale );
  1589. }
  1590. else
  1591. {
  1592. pFeModel->RelaxQuads( pPos1, flSurfaceStiffness, flConstraintScale, m_pSoftbody->m_nSimFlags & SOFTBODY_SIM_EXPERIMENTAL_1 );
  1593. pFeModel->RelaxTris( pPos1, flSurfaceStiffness, flConstraintScale );
  1594. }
  1595. }
  1596. }
  1597. }
  1598. AABB_t CSoftbody::BuildBounds( )const
  1599. {
  1600. fltx4 f4MinBounds, f4MaxBounds;
  1601. if ( m_pAabb )
  1602. {
  1603. const FeAabb_t &bbox = m_pAabb[ m_pFeModel->GetTreeRootAabbIndex() ];
  1604. f4MinBounds = bbox.m_vMinBounds;
  1605. f4MaxBounds = bbox.m_vMaxBounds;
  1606. }
  1607. else
  1608. {
  1609. f4MinBounds = Four_FLT_MAX;
  1610. f4MaxBounds = Four_Negative_FLT_MAX;
  1611. for ( uint nNode = 0; nNode < m_nNodeCount; nNode++ )
  1612. {
  1613. fltx4 pos = LoadAlignedSIMD( m_pPos1[ nNode ].Base() );
  1614. f4MinBounds = MinSIMD( f4MinBounds, pos );
  1615. f4MaxBounds = MaxSIMD( f4MaxBounds, pos );
  1616. }
  1617. }
  1618. AABB_t result;
  1619. result.m_vMinBounds = AsVector( f4MinBounds );
  1620. result.m_vMaxBounds = AsVector( f4MaxBounds );
  1621. return result;
  1622. }
  1623. Quaternion PartialRotation( const Quaternion &q, float f )
  1624. {
  1625. Assert( q.w >= 0 );
  1626. Quaternion p = q;
  1627. p.x *= f;
  1628. p.y *= f;
  1629. p.z *= f;
  1630. p.w = sqrtf( Max( 0.0f, 1.0f - Sqr( q.x ) + Sqr( q.y ) + Sqr( q.z ) ) );
  1631. return p;
  1632. }
  1633. void CSoftbody::SetAbsAngles( const QAngle &vNewAngles, bool bTeleport )
  1634. {
  1635. if ( m_vSimAngles == vNewAngles )
  1636. return;
  1637. CHANGE_GUARD();
  1638. float flLocalSpaceCloth = ( m_nAnimSpace == SOFTBODY_ANIM_SPACE_LOCAL ) ? 1.0f : 0.0f;
  1639. if ( m_pFeModel->m_pLocalRotation && !bTeleport )
  1640. {
  1641. Quaternion simTarget = AngleQuaternion( vNewAngles );
  1642. Quaternion qDelta( simTarget * Conjugate( AngleQuaternion( m_vSimAngles ) ) );
  1643. // qDelta is small in most cases; we could compute the approximate exp( flLocalRotation * lo( qDelta ) ) here
  1644. // since x,y,z of a quaternion is sin( theta/2 ) * axis, we can just scale the axis
  1645. if ( qDelta.w < 0 )
  1646. qDelta = -qDelta;
  1647. Vector vSimOrigin = m_vSimOrigin;
  1648. for ( int nNode = m_pFeModel->m_nStaticNodes; nNode < m_nNodeCount; ++nNode )
  1649. {
  1650. float flLocalRotation = m_pFeModel->m_pLocalRotation[ nNode - m_pFeModel->m_nStaticNodes ] - flLocalSpaceCloth;
  1651. if ( flLocalRotation > 0 )
  1652. {
  1653. m_pPos0[ nNode ] = VectorRotate( m_pPos0[ nNode ] - vSimOrigin, PartialRotation( qDelta, flLocalRotation ) ) + vSimOrigin;
  1654. m_pPos1[ nNode ] = VectorRotate( m_pPos1[ nNode ] - vSimOrigin, PartialRotation( qDelta, flLocalRotation ) ) + vSimOrigin;
  1655. }
  1656. }
  1657. m_bSimTransformsOutdated = true;
  1658. }
  1659. else
  1660. {
  1661. float flLocalRotation = m_pFeModel->m_flLocalRotation - flLocalSpaceCloth;
  1662. if ( flLocalRotation != 0.0f || bTeleport )
  1663. {
  1664. Quaternion simTarget = AngleQuaternion( vNewAngles );
  1665. Quaternion qDelta( simTarget * Conjugate( AngleQuaternion( m_vSimAngles ) ) );
  1666. // qDelta is small in most cases; we could compute the approximate exp( flLocalRotation * lo( qDelta ) ) here
  1667. // since x,y,z of a quaternion is sin( theta/2 ) * axis, we can just scale the axis
  1668. if ( qDelta.w < 0 )
  1669. qDelta = -qDelta;
  1670. matrix3x4a_t transform = QuaternionMatrix( bTeleport ? qDelta : PartialRotation( qDelta, flLocalRotation ) );
  1671. if ( m_nAnimSpace != SOFTBODY_ANIM_SPACE_LOCAL )
  1672. {
  1673. transform.SetOrigin( m_vSimOrigin - VectorRotate( m_vSimOrigin, transform ) );
  1674. }
  1675. for ( int nNode = 0; nNode < m_nNodeCount; ++nNode )
  1676. {
  1677. m_pPos0[ nNode ] = VectorTransform( m_pPos0[ nNode ], transform ); // equivalent to : VectorRotate( m_pPos0[ nNode ] - m_vSimOrigin, transform ) + m_vSimOrigin;
  1678. m_pPos1[ nNode ] = VectorTransform( m_pPos1[ nNode ], transform ); // equivalent to : VectorRotate( m_pPos1[ nNode ] - m_vSimOrigin, transform ) + m_vSimOrigin;
  1679. }
  1680. m_bSimTransformsOutdated = true;
  1681. }
  1682. }
  1683. m_vSimAngles = vNewAngles;
  1684. if ( flLocalSpaceCloth == 1.0f )
  1685. {
  1686. m_vRopeOffset = Vector( 0, -1, 0 );
  1687. }
  1688. else
  1689. {
  1690. AngleVectors( vNewAngles, NULL, &m_vRopeOffset, NULL );
  1691. m_vRopeOffset *= g_flRopeSize;
  1692. }
  1693. }
  1694. matrix3x4_t CSoftbody::GetDifferenceTransform( const Vector &vAltOrigin, const QAngle &vAltAngles )
  1695. {
  1696. Quaternion simTarget = AngleQuaternion( vAltAngles );
  1697. Quaternion qDelta( simTarget * Conjugate( AngleQuaternion( m_vSimAngles ) ) );
  1698. matrix3x4a_t transform = QuaternionMatrix( qDelta );
  1699. transform.SetOrigin( vAltOrigin - VectorRotate( m_vSimOrigin, transform ) );
  1700. return transform;
  1701. }
  1702. void CSoftbody::ComputeInterpolatedNodePositions( float flFactor, VectorAligned *pPosOut )
  1703. {
  1704. for ( uint nNode = 0; nNode < m_nNodeCount; ++nNode )
  1705. {
  1706. pPosOut[ nNode ] = m_pPos1[ nNode ] * flFactor + m_pPos0[ nNode ] * ( 1 - flFactor );
  1707. }
  1708. }
  1709. void CSoftbody::DebugTraceMove( const char *pMsg )
  1710. {
  1711. Vector d = m_pPos1[ 0 ] - m_vSimOrigin;
  1712. Msg( "%s a1 :{r=%.1f,a=%.1f}\n", pMsg, sqrtf( d.x * d.x + d.y * d.y ), RAD2DEG( atan2f( d.y, d.x ) ) - m_vSimAngles.y );
  1713. //Msg( "%s d1 {%.1f,%.1f}\n", pMsg, d.x, d.y );
  1714. }
  1715. void CSoftbody::SetAbsOrigin( const Vector &vNewOrigin, bool bTeleport )
  1716. {
  1717. /*
  1718. if ( m_bTeleportOnNextSetAbsOrigin )
  1719. {
  1720. if ( m_nActivityState == STATE_ACTIVE )
  1721. {
  1722. m_bTeleportOnNextSetAbsOrigin = false;
  1723. }
  1724. bTeleport = true;
  1725. }
  1726. */
  1727. if ( m_vSimOrigin == vNewOrigin )
  1728. return;
  1729. CHANGE_GUARD();
  1730. Vector vDelta = vNewOrigin - m_vSimOrigin;
  1731. float flLocalSpaceCloth = ( m_nAnimSpace == SOFTBODY_ANIM_SPACE_LOCAL ) ? 1.0f : 0.0f;
  1732. float flFractionTeleport = -flLocalSpaceCloth, flIsFullTeleport = 0.0f;
  1733. // m_pFeModel->m_flLocalForce == 0 means that moving absOrigin should not affect the m_Pos arrays
  1734. if ( bTeleport || m_pFeModel->m_flLocalForce <= 0.0f )
  1735. {
  1736. // if we're told to teleport - either by explicit flag, or by "LocalForce" == 0, then
  1737. // teleport, not matter how close the new position is
  1738. flIsFullTeleport = flFractionTeleport = 1.0f - flLocalSpaceCloth;
  1739. }
  1740. else
  1741. {
  1742. float flDeltaLenSq = vDelta.LengthSqr( );
  1743. // which fraction of this delta should we move the particles? If it's 200 inches, source1 assumes teleportation
  1744. AssertDbg( g_flNoTeleportDeltaSq < g_flTeleportDeltaSq );
  1745. flIsFullTeleport = ( flDeltaLenSq - g_flNoTeleportDeltaSq ) * ( 1.0f / ( g_flTeleportDeltaSq - g_flNoTeleportDeltaSq ) );
  1746. flFractionTeleport = clamp( flIsFullTeleport, 1.0f - m_pFeModel->m_flLocalForce, 1 ) - flLocalSpaceCloth;
  1747. }
  1748. // WARNING flIsFullTeleport goes from -Big to +Big, but it's logicaly clamped to 0,1
  1749. if ( flFractionTeleport != 0 )
  1750. {
  1751. // Note: when we move a local-space entity (such as in Hammer), we still simulate and animate in entity space, not world space. This means entity's cloth moves in the opposite direction to moving the entity itself.
  1752. Vector vPartialDelta = vDelta * flFractionTeleport;
  1753. if ( m_pFeModel->m_pLocalForce && flFractionTeleport < 1.0f )
  1754. {
  1755. AssertDbg( m_pFeModel->m_flLocalForce > 0.0f );
  1756. uint nStaticNodes = m_pFeModel->m_nStaticNodes;
  1757. for ( int nNode = 0; nNode < nStaticNodes; ++nNode )
  1758. {
  1759. m_pPos0[ nNode ] += vPartialDelta;
  1760. m_pPos1[ nNode ] += vPartialDelta;
  1761. }
  1762. for ( int nNode = nStaticNodes; nNode < m_nNodeCount; ++nNode )
  1763. {
  1764. Vector vPartialDelta = vDelta * ( clamp( flIsFullTeleport, 1.0f - m_pFeModel->m_pLocalForce[ nNode - nStaticNodes ] - flLocalSpaceCloth, 1.0f ) );
  1765. m_pPos0[ nNode ] += vPartialDelta;
  1766. m_pPos1[ nNode ] += vPartialDelta;
  1767. }
  1768. }
  1769. else
  1770. {
  1771. for ( int nNode = 0; nNode < m_nNodeCount; ++nNode )
  1772. {
  1773. m_pPos0[ nNode ] += vPartialDelta;
  1774. m_pPos1[ nNode ] += vPartialDelta;
  1775. }
  1776. }
  1777. m_bSimTransformsOutdated = true;
  1778. }
  1779. m_vSimOrigin = vNewOrigin;
  1780. if ( m_bEnableGroundTrace //
  1781. && m_pFeModel->m_nWorldCollisionNodeCount // no need to trace the ground plane if we don't have any world-collision nodes
  1782. && Sqr( m_vGround.x - vNewOrigin.x ) + Sqr( m_vGround.y - vNewOrigin.y ) > 16 * 16 // no need to retrace the ground plane until we move away at least 1 ft
  1783. )
  1784. {
  1785. #ifdef SOURCE2_SUPPORT
  1786. // we have world collision nodes, let's update the ground plane
  1787. PhysicsTrace_t trace;
  1788. Vector vRayStart = vNewOrigin + Vector( 0,0, m_pEnvironment->GetSoftbodyGroundTraceRaise() ), vRayDelta( 0, 0, -200 );
  1789. RnQueryAttr_t attr;
  1790. m_pEnvironment->CastRaySingle( trace, vRayStart, vRayDelta, SELECT_STATIC, attr );
  1791. if ( trace.DidHit( ) )
  1792. {
  1793. m_vGround.z = trace.m_vHitPoint.z + g_flClothGroundPlaneThickness.GetFloat();
  1794. }
  1795. else
  1796. {
  1797. m_vGround.z = vRayStart.z + vRayDelta.z;
  1798. }
  1799. m_vGround.x = m_vSimOrigin.x;
  1800. m_vGround.y = m_vSimOrigin.y;
  1801. #endif
  1802. }
  1803. DebugDump();
  1804. }
  1805. static float s_flTransformErrorTolerance = 0.001f;
  1806. /*
  1807. bool CSoftbody::SetupCtrl( uint nCtrl, matrix3x4a_t &writeBone )
  1808. {
  1809. if ( !BeforeFilterTransforms() )
  1810. return false;
  1811. MPROF_AUTO_FAST( SoftbodyFilterTransforms );
  1812. GetAnim( nCtrl ) = writeBone;
  1813. // just a marker to let the next simulation step know we need to update static particles
  1814. m_bAnimTransformChanged = true;
  1815. float flMatrixScale = m_flModelScale;
  1816. //float flInvScale = 1.0f / flMatrixScale;
  1817. // GetSim( nCtrl ) = ScaleMatrix3x3( writeBone, flInvScale ); ?????
  1818. ///////
  1819. // recompute Sim particle rotations if needed (if we simulated particles since the last Filter was called)
  1820. matrix3x4a_t *pSim = GetParticleTransforms( );
  1821. uint nNode = m_pFeModel->m_pCtrlToNode[ nCtrl ];
  1822. ///////
  1823. // copy all the dynamic sim transforms (computed in GetParticleTransforms()) into outWorld
  1824. // we cannot do that in the same loop with the parent transforms because the order of controls (sim order) is independent of the animation bone order, so parent-to-child ordering is not preserved
  1825. if( nNode >= m_pFeModel->m_nRotLockStaticNodes )
  1826. {
  1827. AssertDbg( / *nNode > nNodeCount - m_pFeModel->m_nFitMatrixCount || * /IsGoodWorldTransform( pSim[ nCtrl ] ) ); // the last N nodes are FitMatrices, which may have non-uniform scale at some point
  1828. writeBone = ScaleMatrix3x3( pSim[ nCtrl ], flMatrixScale );
  1829. return true;
  1830. }
  1831. else
  1832. {
  1833. return false;
  1834. }
  1835. ///////
  1836. // now that we have copied all the world transforms back and forth between internal and external caches,
  1837. // compute the corresponding parent transforms.
  1838. /// outWorldBone = ScaleMatrix3x3( pSim[ nCtrl ], flMatrixScale ); ????????
  1839. }
  1840. */
  1841. void CSoftbody::FilterTransforms( const FilterTransformsParams_t &params )
  1842. {
  1843. // if ( IsDebug() && m_nIndexInWorld == g_nClothWatch)
  1844. // {
  1845. // AABB_t box0 = GetAabb( GetNodePositions(), m_nNodeCount );
  1846. // char * states[] = { "Active", "Dormant", "Waking up" };
  1847. // Log_Msg( LOG_PHYSICS, "Cloth %d %s FilterTransforms({%.0f,%.0f}\xB1{%.0f,%.0f})\n", m_nIndexInWorld, states[ m_nActivityState ], box0.GetCenter().x, box0.GetCenter().y, box0.GetSize().x, box0.GetSize().y );
  1848. // }
  1849. if ( !BeforeFilterTransforms() )
  1850. return;
  1851. MPROF_AUTO_FAST( SoftbodyFilterTransforms );
  1852. ///////
  1853. // just a marker to let the next simulation step know we need to update static particles
  1854. m_bAnimTransformChanged = true;
  1855. float flMatrixScale = ( params.flMatrixScale == 0.0f ? m_flModelScale : params.flMatrixScale );
  1856. if ( ( m_bSimTransformsOutdated || params.pNodePos ) && params.pCtrlToBone )
  1857. {
  1858. float flInvScale = 1.0f / flMatrixScale;
  1859. for ( uint nNode = 0; nNode < m_pFeModel->m_nStaticNodes; ++nNode )
  1860. {
  1861. uint nCtrl = m_pFeModel->NodeToCtrl( nNode );
  1862. int nBone = params.pCtrlToBone[ nCtrl ];
  1863. if ( nBone >= 0 )
  1864. {
  1865. AssertDbg( !params.pValidTransforms || BitVec_IsBitSet( params.pValidTransforms, nBone ) );
  1866. GetSim( nCtrl ) = ScaleMatrix3x3( params.pOutputWorldTransforms[ nBone ], flInvScale );
  1867. }
  1868. }
  1869. }
  1870. const VectorAligned *pNodePos = params.pNodePos;
  1871. ///////
  1872. // recompute Sim particle rotations if needed (if we simulated particles since the last Filter was called)
  1873. matrix3x4a_t *pSim = GetParticleTransforms( pNodePos );
  1874. uint nNodeCount = m_pFeModel->m_nNodeCount;
  1875. ///////
  1876. // copy all the dynamic sim transforms (computed in GetParticleTransforms()) into outWorld
  1877. // we cannot do that in the same loop with the parent transforms because the order of controls (sim order) is independent of the animation bone order, so parent-to-child ordering is not preserved
  1878. for ( uint nNode = m_pFeModel->m_nRotLockStaticNodes; nNode < nNodeCount; ++nNode )
  1879. {
  1880. uint nCtrl = m_pFeModel->NodeToCtrl( nNode ); AssertDbg( nCtrl < m_nParticleCount );
  1881. int nBone = params.pCtrlToBone[ nCtrl ];
  1882. if ( nBone >= 0 )
  1883. {
  1884. matrix3x4_t &refWorldBone = params.pOutputWorldTransforms[ nBone ];
  1885. AssertDbg( /*nNode > nNodeCount - m_pFeModel->m_nFitMatrixCount || */IsGoodWorldTransform( pSim[ nCtrl ] ) ); // the last N nodes are FitMatrices, which may have non-uniform scale at some point
  1886. refWorldBone = ScaleMatrix3x3( pSim[ nCtrl ], flMatrixScale );
  1887. }
  1888. }
  1889. ///////
  1890. // now that we have copied all the world transforms back and forth between internal and external caches,
  1891. // compute the corresponding parent transforms.
  1892. for ( uint nNode = m_pFeModel->m_nRotLockStaticNodes; nNode < nNodeCount; ++nNode )
  1893. {
  1894. uint nCtrl = m_pFeModel->NodeToCtrl( nNode );
  1895. int nBone = params.pCtrlToBone[ nCtrl ]; // Note: if we don't want to overwrite static (rotate-locked) bones, we can just put -1 into the corresponding entries of this map
  1896. if ( nBone < 0 )
  1897. continue; // this is a virtual node (does not have a corresponding bone)
  1898. AssertDbg( m_pFeModel->CtrlToNode( nCtrl ) < m_nNodeCount );
  1899. matrix3x4a_t &outWorldBone = params.pOutputWorldTransforms[ nBone ];
  1900. outWorldBone = ScaleMatrix3x3( pSim[ nCtrl ], flMatrixScale );
  1901. }
  1902. }
  1903. void CSoftbody::FilterTransforms( matrix3x4a_t *pModelBones )
  1904. {
  1905. if ( !BeforeFilterTransforms() )
  1906. return;
  1907. MPROF_AUTO_FAST( SoftbodyFilterTransforms );
  1908. ///////
  1909. // just a marker to let the next simulation step know we need to update static particles
  1910. m_bAnimTransformChanged = true;
  1911. float flMatrixScale = m_flModelScale ;
  1912. if ( m_bSimTransformsOutdated )
  1913. {
  1914. float flInvScale = 1.0f / flMatrixScale;
  1915. for ( uint nNode = 0; nNode < m_pFeModel->m_nStaticNodes; ++nNode )
  1916. {
  1917. uint nCtrl = m_pFeModel->NodeToCtrl( nNode );
  1918. int nBone = m_pCtrlToModelBone[ nCtrl ];
  1919. if ( nBone >= 0 )
  1920. {
  1921. GetSim( nCtrl ) = ScaleMatrix3x3( pModelBones[ nBone ], flInvScale );
  1922. }
  1923. }
  1924. }
  1925. const VectorAligned *pNodePos = NULL;
  1926. ///////
  1927. // recompute Sim particle rotations if needed (if we simulated particles since the last Filter was called)
  1928. matrix3x4a_t *pSim = GetParticleTransforms( pNodePos );
  1929. uint nNodeCount = m_pFeModel->m_nNodeCount;
  1930. ///////
  1931. // copy all the dynamic sim transforms (computed in GetParticleTransforms()) into outWorld
  1932. // we cannot do that in the same loop with the parent transforms because the order of controls (sim order) is independent of the animation bone order, so parent-to-child ordering is not preserved
  1933. for ( uint nNode = m_pFeModel->m_nRotLockStaticNodes; nNode < nNodeCount; ++nNode )
  1934. {
  1935. uint nCtrl = m_pFeModel->NodeToCtrl( nNode ); AssertDbg( nCtrl < m_nParticleCount );
  1936. int nBone = m_pCtrlToModelBone[ nCtrl ];
  1937. if ( nBone >= 0 )
  1938. {
  1939. matrix3x4_t &refWorldBone = pModelBones[ nBone ];
  1940. AssertDbg( /*nNode > nNodeCount - m_pFeModel->m_nFitMatrixCount || */IsGoodWorldTransform( pSim[ nCtrl ] ) ); // the last N nodes are FitMatrices, which may have non-uniform scale at some point
  1941. refWorldBone = ScaleMatrix3x3( pSim[ nCtrl ], flMatrixScale );
  1942. }
  1943. }
  1944. }
  1945. bool CSoftbody::IsDormant() const
  1946. {
  1947. return m_nActivityState == STATE_DORMANT;
  1948. }
  1949. bool CSoftbody::IsActive() const
  1950. {
  1951. return m_nActivityState == STATE_ACTIVE;
  1952. }
  1953. void CSoftbody::GoDormant( )
  1954. {
  1955. if ( m_nActivityState == STATE_ACTIVE )
  1956. {
  1957. // if ( IsDebug() && m_nIndexInWorld == g_nClothWatch )
  1958. // {
  1959. // Log_Msg( LOG_PHYSICS, "Cloth %d goes dormant\n", m_nIndexInWorld );
  1960. // }
  1961. m_nActivityState = STATE_DORMANT;
  1962. }
  1963. /*
  1964. m_bTeleportOnNextSetAbsOrigin = true;
  1965. m_bTeleportOnNextSetAbsAngles = true;
  1966. */
  1967. //Log_Detailed( LOG_PHYSICS, "Softbody::GoDormant(%s, skel %p)\n", GetDebugName( ), GetUserData( 0 ) );
  1968. }
  1969. void CSoftbody::GoWakeup( )
  1970. {
  1971. if ( IsDormant( ) )
  1972. {
  1973. // if ( IsDebug() && m_nIndexInWorld == g_nClothWatch )
  1974. // {
  1975. // Log_Msg( LOG_PHYSICS, "Cloth %d wakes up\n", m_nIndexInWorld );
  1976. // }
  1977. m_nActivityState = STATE_ACTIVE;
  1978. m_nStateCounter = 0;
  1979. /*
  1980. m_bTeleportOnNextSetAbsOrigin = true;
  1981. m_bTeleportOnNextSetAbsAngles = true;
  1982. */
  1983. }
  1984. //Log_Detailed( LOG_PHYSICS, "Softbody::GoWakeup(%s, skel %p)\n", GetDebugName( ), GetUserData( 0 ) );
  1985. }
  1986. bool CSoftbody::BeforeFilterTransforms( )
  1987. {
  1988. switch ( m_nActivityState )
  1989. {
  1990. case STATE_ACTIVE:
  1991. m_nStateCounter = 0; // we've simulated 0 frames since last FilterTransforms
  1992. return true; // yes, we need to FilterTranforms
  1993. case STATE_WAKEUP:
  1994. // we do not need to Filter Transforms yet, but will shortly
  1995. // StateCounter == 0 : we definitely do NOT want to filter transforms, we have the wrong transforms and they will stretch the cloth visually
  1996. // StateCounter > 0 : we MAY filter transforms, but they are not simulating yet. Maybe we need to do it anyway to account for discrepancies pre- and post-filter
  1997. return m_nStateCounter > 0;
  1998. default:
  1999. GoWakeup( );
  2000. return false;
  2001. }
  2002. }
  2003. ConVar cloth_wind( "cloth_wind", "0" );
  2004. ConVar cloth_windage_multiplier( "cloth_windage_multiplier", "1", FCVAR_CHEAT );
  2005. ConVar cloth_wind_pitch( "cloth_wind_pitch", "0" );
  2006. void CSoftbody::IntegrateWind( VectorAligned *pPos, float flTimeStep )
  2007. {
  2008. {
  2009. const Vector4DAligned &vWindDesc = m_pEnvironment->GetWindDesc();
  2010. float flNormalPressure = m_pFeModel->m_flWindage * cloth_windage_multiplier.GetFloat() * flTimeStep * vWindDesc.w;
  2011. if ( flNormalPressure != 0 )
  2012. {
  2013. m_pFeModel->ApplyQuadWind( pPos, vWindDesc.AsVector3D() * flNormalPressure, m_pFeModel->m_flWindDrag );
  2014. }
  2015. }
  2016. {
  2017. float flDebugWind = cloth_wind.GetFloat();
  2018. if ( flDebugWind != 0 )
  2019. {
  2020. Vector vDebugWindVector;
  2021. QAngle vecWindAngle( 0, cloth_wind_pitch.GetFloat(), 0 );
  2022. AngleVectors( vecWindAngle, &vDebugWindVector );
  2023. float flDebugWindPressure = m_pFeModel->m_flWindage * flTimeStep * flDebugWind;
  2024. if ( flDebugWindPressure != 0 )
  2025. {
  2026. m_pFeModel->ApplyQuadWind( pPos, vDebugWindVector * flDebugWindPressure, m_pFeModel->m_flWindDrag );
  2027. }
  2028. }
  2029. }
  2030. }
  2031. // add acceleration components
  2032. void CSoftbody::Integrate( float flTimeStep )
  2033. {
  2034. if ( m_bEnableSprings )
  2035. {
  2036. MPROF_AUTO_FAST( SoftbodyStepIntegrate );
  2037. m_pFeModel->IntegrateSprings( m_pPos0, m_pPos1, flTimeStep, m_flModelScale );
  2038. }
  2039. IntegrateWind( m_pPos1, flTimeStep );
  2040. AssertDbg( m_flExpAirDrag >= 0 && m_flVelAirDrag >= 0 );
  2041. if ( m_flExpAirDrag + m_flVelAirDrag != 0 )
  2042. {
  2043. m_pFeModel->ApplyAirDrag( m_pPos0, m_pPos1, 1.0f - expf( -m_flExpAirDrag * flTimeStep ), m_flVelAirDrag );
  2044. }
  2045. AssertDbg( m_flExpQuadAirDrag >= 0 && m_flVelQuadAirDrag >= 0 );
  2046. if ( m_flExpQuadAirDrag + m_flVelQuadAirDrag != 0 )
  2047. {
  2048. m_pFeModel->ApplyQuadAirDrag( ( fltx4* )m_pPos0, ( const fltx4* )m_pPos1, 1.0f - expf( -m_flExpQuadAirDrag * flTimeStep ), m_flVelQuadAirDrag );
  2049. }
  2050. AssertDbg( m_flExpRodAirDrag >= 0 && m_flVelRodAirDrag >= 0 );
  2051. if ( m_flExpRodAirDrag + m_flVelRodAirDrag != 0 )
  2052. {
  2053. m_pFeModel->ApplyRodAirDrag( ( fltx4* )m_pPos0, ( const fltx4* )m_pPos1, 1.0f - expf( -m_flExpRodAirDrag * flTimeStep ), m_flVelRodAirDrag );
  2054. }
  2055. float flQuadVelocitySmoothRate = m_flQuadVelocitySmoothRate;
  2056. if ( flQuadVelocitySmoothRate != 0 )
  2057. {
  2058. float flMul = 1.0f - clamp( flQuadVelocitySmoothRate, 0, 1 );
  2059. for ( int nIt = ( int )m_nQuadVelocitySmoothIterations; nIt-- > 0; )
  2060. {
  2061. m_pFeModel->SmoothQuadVelocityField( ( fltx4* )m_pPos0, ( const fltx4* )m_pPos1, flMul );
  2062. }
  2063. }
  2064. float flRodVelocitySmoothRate = m_flRodVelocitySmoothRate;
  2065. if ( flRodVelocitySmoothRate != 0 )
  2066. {
  2067. float flMul = 1.0f - clamp( flRodVelocitySmoothRate, 0, 1 );
  2068. for ( int nIt = ( int )m_nRodVelocitySmoothIterations; nIt-- > 0; )
  2069. {
  2070. m_pFeModel->SmoothRodVelocityField( ( fltx4* )m_pPos0, ( const fltx4* )m_pPos1, flMul );
  2071. }
  2072. }
  2073. }
  2074. inline fltx4 LoadOriginAligned( const matrix3x4a_t &tm )
  2075. {
  2076. const fltx4 &x = tm.SIMDRow( 0 ), &y = tm.SIMDRow( 1 ), &z = tm.SIMDRow( 2 );
  2077. fltx4 nnxy = _mm_unpackhi_ps( x, y ), xyzz = _mm_shuffle_ps( nnxy, z, MM_SHUFFLE_REV( 2, 3, 3, 3 ) );
  2078. return xyzz;
  2079. }
  2080. // Note: animation attraction (neither positional nor velocity) should NOT be affected immediately by the damping. Source1 crazy explicit-Euler-ish integration scheme
  2081. // bypasses damping when computing animation attraction, folding it into the "Force" that affects velocity, that will affect force again, but not until the next frame.
  2082. // This scheme is unstable in multiple subtle ways, but artists learned to work around it by tweaking numbers.
  2083. void CSoftbody::AddAnimationAttraction( float flTimeStep )
  2084. {
  2085. const uint nStaticNodes = m_pFeModel->m_nStaticNodes;
  2086. const FeNodeIntegrator_t *pNodeIntegrator = m_pFeModel->m_pNodeIntegrator;
  2087. // <sergiy> this is source1's AnimationVertexAttraction simulation. The values are always very small in Dota, so it's not really necessary
  2088. if ( m_bEnableAnimationAttraction && pNodeIntegrator && ( m_pFeModel->m_nDynamicNodeFlags & ( FE_FLAG_HAS_ANIMATION_VERTEX_ATTRACTION | FE_FLAG_HAS_ANIMATION_FORCE_ATTRACTION ) ) )
  2089. {
  2090. if ( m_bEnableSimd )
  2091. {
  2092. //fltx4 f4TimeStep = ReplicateX4( flTimeStep );
  2093. for ( uint nDynNode = nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  2094. {
  2095. const FeNodeIntegrator_t &integrator = pNodeIntegrator[ nDynNode ];
  2096. fltx4 &pos0 = ( fltx4& )m_pPos0[ nDynNode ], &pos1 = ( fltx4& )m_pPos1[ nDynNode ];
  2097. uint nCtrl = m_pFeModel->NodeToCtrl( nDynNode );
  2098. fltx4 vAnimationTarget = LoadOriginAligned( GetAnim( nCtrl ) ), vAnimDelta = vAnimationTarget - pos1;
  2099. // the position attraction does not involve velocity and is inertia-less. That's why we add the delta to both previous and current time stp
  2100. fltx4 vPositionAttraction = vAnimDelta * ReplicateX4( Min( 1.0f, integrator.flAnimationVertexAttraction * flTimeStep * g_flClothAttrPos ) );
  2101. fltx4 vVelocityAttraction = vAnimDelta * ReplicateX4( integrator.flAnimationForceAttraction * flTimeStep * g_flClothAttrVel );
  2102. pos0 += vPositionAttraction;
  2103. pos1 += vPositionAttraction + vVelocityAttraction;
  2104. }
  2105. }
  2106. else
  2107. {
  2108. for ( uint nDynNode = nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  2109. {
  2110. const FeNodeIntegrator_t &integrator = pNodeIntegrator[ nDynNode ];
  2111. VectorAligned &pos0 = m_pPos0[ nDynNode ], &pos1 = m_pPos1[ nDynNode ];
  2112. uint nCtrl = m_pFeModel->NodeToCtrl( nDynNode );
  2113. VectorAligned vAnimationTarget( GetAnim( nCtrl ).GetOrigin( ) ), vAnimDelta( vAnimationTarget - pos1 );
  2114. // the position attraction does not involve velocity and is inertia-less. That's why we add the delta to both previous and current time stp
  2115. Vector vPositionAttraction( vAnimDelta * Min( 1.0f, integrator.flAnimationVertexAttraction * flTimeStep * g_flClothAttrPos ) );
  2116. Vector vVelocityAttraction = vAnimDelta * ( integrator.flAnimationForceAttraction * flTimeStep * g_flClothAttrVel );
  2117. pos0 += vPositionAttraction;
  2118. pos1 += vPositionAttraction + vVelocityAttraction;
  2119. }
  2120. }
  2121. /*if ( m_nDebugNode < m_nNodeCount )
  2122. {
  2123. Vector vAnimTarget = GetAnim( m_pFeModel->NodeToCtrl( m_nDebugNode ) ).GetOrigin(), vPos0 = m_pPos0[ m_nDebugNode ], vPos1 = m_pPos1[ m_nDebugNode ];
  2124. Vector vVel1 = ( vPos1 - vPos0 ) / m_flLastTimestep;
  2125. Msg( "\t%.2f %.2f %.2f\t%.2f %.2f %.2f\t%.2f %.2f %.2f\n",
  2126. ( vAnimTarget - vPos0 ).x, ( vAnimTarget - vPos0 ).y, ( vAnimTarget - vPos0 ).z,
  2127. vVel1.x, vVel1.y, vVel1.z,
  2128. vPos1.x - vPos0.x, vPos1.y - vPos0.y, vPos1.z - vPos0.z
  2129. );
  2130. }*/
  2131. }
  2132. }
  2133. class CGluePredictFunctor
  2134. {
  2135. const CParticleGlue *m_pGlue;
  2136. fltx4 *m_pPos0;
  2137. fltx4 *m_pPos1;
  2138. float m_flTimestepScale;
  2139. uint m_nStaticNodes;
  2140. public:
  2141. CGluePredictFunctor( const CParticleGlue *pGlue, fltx4 *pPos0, fltx4 *pPos1, float flTimestepScale, uint nStaticNodes )
  2142. : m_pGlue( pGlue )
  2143. , m_pPos0( pPos0 )
  2144. , m_pPos1( pPos1 )
  2145. , m_flTimestepScale( flTimestepScale )
  2146. , m_nStaticNodes( nStaticNodes )
  2147. {
  2148. }
  2149. void operator() ( uint nDynNode )const
  2150. {
  2151. const CParticleGlue &glue = m_pGlue[ nDynNode ];
  2152. fltx4 vDelta = m_pPos1[ glue.m_nParentNode[ 0 ] ] - m_pPos0[ glue.m_nParentNode[ 0 ] ];
  2153. if ( glue.m_flWeight1 > 0.0f )
  2154. {
  2155. AssertDbg( glue.m_flWeight1 <= 1.0f );
  2156. fltx4 vDelta1 = m_pPos1[ glue.m_nParentNode[ 1 ] ] - m_pPos0[ glue.m_nParentNode[ 1 ] ];
  2157. fltx4 f4Weight1 = ReplicateX4( glue.m_flWeight1 );
  2158. vDelta = vDelta * ( Four_Ones - f4Weight1 ) + vDelta1 * f4Weight1;
  2159. }
  2160. vDelta *= ReplicateX4( m_flTimestepScale * glue.m_flStickiness );
  2161. m_pPos1[ m_nStaticNodes + nDynNode ] += vDelta;
  2162. m_pPos0[ m_nStaticNodes + nDynNode ] += vDelta;
  2163. }
  2164. };
  2165. void CSoftbody::Predict( float flTimeStep )
  2166. {
  2167. MPROF_AUTO_FAST( SoftbodyStepPredict );
  2168. const uint nStaticNodes = m_pFeModel->m_nStaticNodes;
  2169. const FeNodeIntegrator_t *pNodeIntegrator = m_pFeModel->m_pNodeIntegrator;
  2170. float flTimestepScale = ( 1 - m_flVelocityDamping ) * ( 1 + m_flOverPredict ) * flTimeStep / Max( 0.25f * flTimeStep, m_flLastTimestep );
  2171. fltx4 *pPos0 = ( fltx4* ) m_pPos0, *pPos1 = ( fltx4* ) m_pPos1;
  2172. float flVelLim = g_flClothNodeVelocityLimit;
  2173. if ( flVelLim < 1e5 ) // experimental
  2174. {
  2175. float flVelLimSqr = Sqr( flVelLim );
  2176. for ( int nNode = 0; nNode < m_nNodeCount; ++nNode )
  2177. {
  2178. Vector vDelta = m_pPos1[ nNode ] - m_pPos0[ nNode ];
  2179. float flLenSqr = vDelta.LengthSqr();
  2180. if ( flLenSqr > flVelLimSqr )
  2181. {
  2182. m_pPos0[ nNode ] += ( 1.0f - sqrtf( flVelLimSqr / flLenSqr ) ) * vDelta;
  2183. }
  2184. }
  2185. }
  2186. if ( m_bAnimTransformChanged /*|| ( pNodeIntegrator && ( m_pFeModel->m_nStaticNodeFlags & FE_FLAG_HAS_ANIMATION_VERTEX_ATTRACTION ) )*/ )
  2187. {
  2188. /*
  2189. float flTimeStepAdjusted = flTimeStep * g_flClothAttrPos;
  2190. if ( pNodeIntegrator )
  2191. {
  2192. for ( uint nStaticNode = 0; nStaticNode < nStaticNodes; ++nStaticNode )
  2193. {
  2194. fltx4 vTarget = LoadOriginAligned( GetAnim( m_pFeModel->NodeToCtrl( nStaticNode ) ) );
  2195. float flAttract = pNodeIntegrator[ nStaticNode ].flAnimationVertexAttraction * flTimeStepAdjusted;
  2196. fltx4 f4Attract = MinSIMD( ReplicateX4( flAttract ), Four_Ones );
  2197. pPos0[ nStaticNode ] = f4Attract * vTarget + ( Four_Ones - f4Attract ) * pPos1[ nStaticNode ];
  2198. }
  2199. }
  2200. else
  2201. */
  2202. // <sergiy> Note that the animation is copied into static nodes in Source1 in CClothModelPiece::SetupBone(), cloth_system.cpp#36:3397
  2203. // it's probably a good idea to continue doing that regardless of the slight bug in source1 that overshot static bones slightly, unless artists unwittingly relied on that bug in some cases
  2204. {
  2205. for ( uint nStaticNode = 0; nStaticNode < nStaticNodes; ++nStaticNode )
  2206. {
  2207. uint nCtrl = m_pFeModel->NodeToCtrl( nStaticNode );
  2208. Assert( nCtrl < m_nParticleCount );
  2209. fltx4 vTarget = LoadOriginAligned( GetAnim( nCtrl ) );
  2210. pPos0[ nStaticNode ] = vTarget;
  2211. }
  2212. }
  2213. m_bAnimTransformChanged = false;
  2214. }
  2215. else
  2216. {
  2217. // since we're double-buffering the positions, we need to copy the positions for at least one frame
  2218. // after animation stopped updating them. We can add another flag here to avoid memcpy if it ever becomes noticeable CPU drain
  2219. V_memcpy( m_pPos0, m_pPos1, sizeof( *m_pPos0 ) * nStaticNodes );
  2220. }
  2221. if ( m_bEnableFollowNodes )
  2222. {
  2223. for ( uint nFlwr = 0, nFollowers = m_pFeModel->m_nFollowNodeCount; nFlwr < nFollowers; ++nFlwr )
  2224. {
  2225. const FeFollowNode_t fn = m_pFeModel->m_pFollowNodes[ nFlwr ];
  2226. fltx4 vDelta = ( pPos1[ fn.nParentNode ] - pPos0[ fn.nParentNode ] ) * ReplicateX4( flTimestepScale * fn.flWeight );
  2227. // why pos0-=, and not pos1+=? because we're computing pos2=pos1 + ( pos1 - pos0 ) later, and advancing pos1 will have the effect of doubling the velocity of the child
  2228. pPos1[ fn.nChildNode ] += vDelta;
  2229. pPos0[ fn.nChildNode ] += vDelta;
  2230. }
  2231. }
  2232. if ( m_pParticleGlue )
  2233. {
  2234. CGluePredictFunctor functor( m_pParticleGlue, pPos0, pPos1, flTimestepScale, m_pFeModel->m_nStaticNodes );
  2235. m_StickyBuffer.ScanBits( functor );
  2236. m_StickyBuffer.Clear();
  2237. }
  2238. float flGravityStepScale = -GetEffectiveGravityScale( ) * flTimeStep * flTimeStep;
  2239. fltx4 vGravityStepScaled = { 0, 0, flGravityStepScale, 0 };
  2240. fltx4 f4TimestepScale = ReplicateX4( flTimestepScale );
  2241. // verlet integration
  2242. if ( pNodeIntegrator )
  2243. {
  2244. if ( m_flDampingMultiplier > 0 && ( m_pFeModel->m_nDynamicNodeFlags & FE_FLAG_HAS_NODE_DAMPING ) )
  2245. {
  2246. float flTimeStep_with_DampingMultiplier = flTimeStep * m_flDampingMultiplier * g_flClothDampingMultiplier;
  2247. for ( uint nDynNode = nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  2248. {
  2249. const FeNodeIntegrator_t &integrator = pNodeIntegrator[ nDynNode ];
  2250. float flDamping = integrator.flPointDamping * flTimeStep_with_DampingMultiplier ;
  2251. fltx4 f4DampingMul = MaxSIMD( Four_Zeros, Four_Ones - ReplicateX4( flDamping ) );
  2252. fltx4 &pos0 = pPos0[ nDynNode ], &pos1 = pPos1[ nDynNode ];
  2253. pos0 = pos1 + ( pos1 - pos0 ) * f4TimestepScale * f4DampingMul + ReplicateX4( integrator.flGravity ) * vGravityStepScaled;
  2254. }
  2255. }
  2256. else
  2257. {
  2258. // we still have custom gravity in node integrators..
  2259. for ( uint nDynNode = nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  2260. {
  2261. const FeNodeIntegrator_t &integrator = pNodeIntegrator[ nDynNode ];
  2262. fltx4 &pos0 = pPos0[ nDynNode ], &pos1 = pPos1[ nDynNode ];
  2263. pos0 = pos1 + ( ( pos1 - pos0 ) * f4TimestepScale ) + ReplicateX4( integrator.flGravity ) * vGravityStepScaled;
  2264. }
  2265. }
  2266. }
  2267. else
  2268. {
  2269. fltx4 vGravityStep = -ReplicateX4( flGravityStepScale ) * LoadAlignedSIMD( m_pEnvironment->GetGravity( ).Base() );
  2270. for ( uint nDynNode = nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  2271. {
  2272. pPos0[ nDynNode ] = pPos1[ nDynNode ] + ( pPos1[ nDynNode ] - pPos0[ nDynNode ] ) * f4TimestepScale + vGravityStep;
  2273. }
  2274. }
  2275. Swap( m_pPos0, m_pPos1 );
  2276. m_flLastTimestep = flTimeStep;
  2277. }
  2278. Vector CSoftbody::GetEffectiveGravity( void ) const
  2279. {
  2280. return m_pEnvironment->GetGravity( ) * GetEffectiveGravityScale( );
  2281. }
  2282. float CSoftbody::GetEffectiveGravityScale( void ) const
  2283. {
  2284. return m_bGravityDisabled ? 0 : m_flGravityScale * ( 1 + m_flOverPredict );
  2285. }
  2286. inline float EllipsoidIsoParm( const Vector &vLocalPos, const Vector &vInvRadius )
  2287. {
  2288. return ScaleVector( vLocalPos, vInvRadius ).LengthSqr( );
  2289. }
  2290. void CSoftbody::Post( )
  2291. {
  2292. MPROF_AUTO_FAST( SoftbodyStepPost );
  2293. if ( IsDebug( ) )
  2294. {
  2295. for ( uint nNode = 0; nNode < m_nNodeCount; ++nNode )
  2296. {
  2297. AssertDbg( m_pPos1[ nNode ].IsValid( ) );
  2298. }
  2299. }
  2300. // modify pos1
  2301. if ( m_flOverPredict != 0 || m_flStepUnderRelax != 0 )
  2302. {
  2303. float w1 = expf( -m_flStepUnderRelax ) / ( 1 + m_flOverPredict );
  2304. fltx4 f4Weight1 = ReplicateX4( w1 ); // 0: just take pos0; 1: just leave pos1
  2305. fltx4 f4Weight0 = Four_Ones - f4Weight1;
  2306. for ( uint nDynNode = m_pFeModel->m_nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  2307. {
  2308. fltx4 f4BlendedPos = LoadAlignedSIMD( m_pPos0[ nDynNode ].Base() ) * f4Weight0 + LoadAlignedSIMD( m_pPos1[ nDynNode ].Base() ) * f4Weight1;
  2309. StoreAlignedSIMD( &m_pPos1[ nDynNode ].x, f4BlendedPos );
  2310. }
  2311. }
  2312. if ( g_nClothDebug & CLOTH_DEBUG_SNAP_TO_ANIM )
  2313. {
  2314. for ( uint nNode = 0; nNode < m_nNodeCount; ++nNode )
  2315. {
  2316. m_pPos1[ nNode ] = GetAnim( m_pFeModel->NodeToCtrl( nNode ) ).GetOrigin( );
  2317. }
  2318. }
  2319. // apply constraints now
  2320. // inclusive collision ellipsoids
  2321. if ( m_bEnableInclusiveCollisionSpheres )
  2322. {
  2323. for ( uint nSphere = 0; nSphere < m_pFeModel->m_nCollisionSpheres[ 1 ]; ++nSphere )
  2324. {
  2325. const FeCollisionSphere_t &fce = m_pFeModel->m_pCollisionSpheres[ nSphere ];
  2326. const matrix3x4a_t &anim = GetAnim( fce.nCtrlParent );
  2327. VectorAligned &pos = m_pPos1[ fce.nChildNode ];
  2328. Vector vLocalPos = VectorITransform( pos, anim ) - fce.m_vOrigin;
  2329. float flIsoParm = vLocalPos.LengthSqr( ) * fce.m_flRFactor; // r-factor is 1/radius^2
  2330. if ( flIsoParm > 1.0f )
  2331. {
  2332. //<sergiy> This is not a true projection onto ellipsoid, although it is if the ellipsoid is a sphere.
  2333. // I hope this clamping will be enough for our purposes, and it's much cheaper than true projection
  2334. pos = VectorTransform( vLocalPos / sqrtf( flIsoParm ) + fce.m_vOrigin, anim );
  2335. }
  2336. }
  2337. }
  2338. // exclusive collision ellipsoids
  2339. if ( m_bEnableExclusiveCollisionSpheres )
  2340. {
  2341. for ( uint nSphere = m_pFeModel->m_nCollisionSpheres[ 1 ]; nSphere < m_pFeModel->m_nCollisionSpheres[ 0 ]; ++nSphere )
  2342. {
  2343. const FeCollisionSphere_t &fce = m_pFeModel->m_pCollisionSpheres[ nSphere ];
  2344. const matrix3x4a_t &anim = GetAnim( fce.nCtrlParent );
  2345. VectorAligned &pos = m_pPos1[ fce.nChildNode ];
  2346. Vector vLocalPos = VectorITransform( pos, anim ) - fce.m_vOrigin;
  2347. float flDistSqr = vLocalPos.LengthSqr( );
  2348. if ( flDistSqr < Sqr( fce.m_flRFactor ) ) // r-factor is radius
  2349. {
  2350. //<sergiy> This is not a true projection onto ellipsoid, although it is if the ellipsoid is a sphere.
  2351. // I hope this clamping will be enough for our purposes, and it's much cheaper than true projection
  2352. Vector vSurface;
  2353. if ( flDistSqr < 0.0001f )
  2354. {
  2355. vSurface = Vector( 0, 0, fce.m_flRFactor );
  2356. }
  2357. else
  2358. {
  2359. vSurface = vLocalPos * ( fce.m_flRFactor / sqrtf( flDistSqr ) );
  2360. }
  2361. pos = VectorTransform( vSurface + fce.m_vOrigin, anim );
  2362. }
  2363. }
  2364. }
  2365. if ( m_bEnableCollisionPlanes )
  2366. {
  2367. for ( uint nPlane = 0; nPlane < m_pFeModel->m_nCollisionPlanes; ++nPlane )
  2368. {
  2369. const FeCollisionPlane_t &shape = m_pFeModel->m_pCollisionPlanes[ nPlane ];
  2370. const matrix3x4a_t &anim = GetAnim( shape.nCtrlParent );
  2371. VectorAligned &pos = m_pPos1[ shape.nChildNode ];
  2372. Vector vPlaneNormalWorld = VectorRotate( shape.m_Plane.m_vNormal, anim );
  2373. float flDist = DotProduct( pos - anim.GetOrigin( ), vPlaneNormalWorld ) - shape.m_Plane.m_flOffset;
  2374. if ( flDist < 0 )
  2375. {
  2376. pos -= flDist * vPlaneNormalWorld;
  2377. }
  2378. }
  2379. }
  2380. if ( m_bEnableGroundCollision )
  2381. {
  2382. const uint16 *pNodes = m_pFeModel->m_pWorldCollisionNodes;
  2383. for ( uint nParm = 0; nParm < m_pFeModel->m_nWorldCollisionParamCount; ++nParm )
  2384. {
  2385. const FeWorldCollisionParams_t &parm = m_pFeModel->m_pWorldCollisionParams[ nParm ];
  2386. for ( uint n = parm.nListBegin; n < parm.nListEnd; ++n )
  2387. {
  2388. uint nNode = pNodes[ n ];
  2389. VectorAligned &pos1 = m_pPos1[ nNode ];
  2390. float h = pos1.z - m_vGround.z;
  2391. const float flContactSlop = 0.0f;
  2392. if ( h >= flContactSlop )
  2393. continue; // no contact
  2394. float flGoZ = parm.flWorldFriction, flStopZ = 1 - flGoZ;
  2395. /*if ( h > 0 )
  2396. {
  2397. float flTuneOut = 1.0f - h * ( 1.0f / flContactSlop ); // lerp between ( stay, attract ) and ( 1, 0 )
  2398. flAttract *= flTuneOut;
  2399. flStay = 1.0f - flAttract;
  2400. }
  2401. else*/
  2402. {
  2403. pos1.z = m_vGround.z;
  2404. }
  2405. float flGoXY = ( 1 - parm.flGroundFriction ) * flGoZ, flStopXY = 1 - flGoXY; // ground friction 1.0 means full attract
  2406. VectorAligned &pos0 = m_pPos0[ nNode ];
  2407. pos0.x = pos0.x * flGoXY + pos1.x * flStopXY;
  2408. pos0.y = pos0.y * flGoXY + pos1.y * flStopXY;
  2409. pos0.z = pos0.z * flGoZ + pos1.z * flStopZ ;
  2410. }
  2411. }
  2412. }
  2413. m_pFeModel->FitCenters( ( fltx4* )m_pPos1 );
  2414. if ( m_flVolumetricSolveAmount > 0.0f )
  2415. {
  2416. m_pFeModel->FeedbackFitTransforms( m_pPos1, m_flVolumetricSolveAmount );
  2417. }
  2418. }
  2419. void CSoftbody::ResetVelocities( )
  2420. {
  2421. V_memcpy( m_pPos0, m_pPos1, sizeof( *m_pPos0 ) * m_nNodeCount );
  2422. }
  2423. MPROF_NODE( SoftbodyParticleTransforms_Bases, "Softbody:ParticleTransforms:Bases", "Softbody" );
  2424. matrix3x4a_t* CSoftbody::GetParticleTransforms( const VectorAligned *pInputNodePos, uint nFlags )
  2425. {
  2426. AssertDbg( !IsDormant( ) ); // sanity check: if the cloth is dormant, why is someone asking for the cloth transforms?
  2427. matrix3x4a_t *pSim = m_pParticles + m_nParticleCount;
  2428. Validate( );
  2429. Assert( !pInputNodePos ); // I don't need this for interpolation anymore
  2430. VectorAligned *pNodePos = /*pInputNodePos ? pInputNodePos : */m_pPos1;
  2431. if ( m_bSimTransformsOutdated )
  2432. {
  2433. // all the dynamic node sim transforms are generated in this routine from pNodePos, and orientations from GetAnim(), so there's no need to copy the positions or orientations, they may be trash
  2434. // it may however be necessary to have all the static transforms copied
  2435. /*for ( uint nDynNode = m_pFeModel->m_nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  2436. {
  2437. uint nCtrl = m_pFeModel->NodeToCtrl( nDynNode );
  2438. GetSim( nCtrl ).SetOrigin( pNodePos[ nDynNode ] );
  2439. }*/
  2440. uint nStaticNodeCount = m_pFeModel->m_nStaticNodes, nRotLockNodeCount = m_pFeModel->m_nRotLockStaticNodes; NOTE_UNUSED( nStaticNodeCount ); NOTE_UNUSED( nRotLockNodeCount );
  2441. if ( nFlags & SOFTBODY_SIM_TRANSFORMS_INCLUDE_STATIC )
  2442. {
  2443. // the most conservative approach to returning sim particle transforms is to take all static particles from animation at the start, so that if we forget to compute some, we'll have a purely animation-driven transform
  2444. for ( uint nNode = 0; nNode < nRotLockNodeCount; ++nNode )
  2445. {
  2446. uint nCtrl = m_pFeModel->NodeToCtrl( nNode );
  2447. pSim[ nCtrl ] = GetAnim( nCtrl );
  2448. }
  2449. }
  2450. for ( uint nNode = nRotLockNodeCount; nNode < nStaticNodeCount; ++nNode )
  2451. {
  2452. uint nCtrl = m_pFeModel->NodeToCtrl( nNode );
  2453. matrix3x4_t tm = GetAnim( nCtrl );
  2454. tm.SetOrigin( pNodePos[ nNode ] ); // we're about to overwrite the orientation of this transform with bases; and position with (sometimes interpolated) pNodePos
  2455. pSim[ nCtrl ] = tm;
  2456. }
  2457. {
  2458. MPROF_AUTO_FAST_COUNT( SoftbodyParticleTransforms_Bases, m_pFeModel->m_nNodeBaseCount );
  2459. for ( uint nBase = 0; nBase < m_pFeModel->m_nNodeBaseCount; ++nBase )
  2460. {
  2461. const FeNodeBase_t &basis = m_pFeModel->m_pNodeBases[ nBase ];
  2462. #if 0
  2463. Vector vAxisX = ( pNodePos[ basis.nNodeX1 ] - pNodePos[ basis.nNodeX0 ] ).NormalizedSafe( Vector( 0, 0, -1 ) );
  2464. Vector vAxisY = ( pNodePos[ basis.nNodeY1 ] - pNodePos[ basis.nNodeY0 ] );
  2465. vAxisY = ( vAxisY - DotProduct( vAxisY, vAxisX ) * vAxisX );
  2466. float flAxisYlen = vAxisY.Length();
  2467. if ( flAxisYlen > 0.001f )
  2468. {
  2469. vAxisY /= flAxisYlen;
  2470. }
  2471. else
  2472. {
  2473. // there's no useful direction for Y, just pick an orthogonal direction. Best if it's stable
  2474. VectorPerpendicularToVector( vAxisX, &vAxisY );
  2475. }
  2476. #else
  2477. // this version is more consistent with Source1 bone normal/matrix computation
  2478. Vector vAxisX = ( pNodePos[ basis.nNodeX1 ] - pNodePos[ basis.nNodeX0 ] );
  2479. Vector vAxisY = ( pNodePos[ basis.nNodeY1 ] - pNodePos[ basis.nNodeY0 ] ).NormalizedSafe( Vector( 0, 0, -1 ) );
  2480. vAxisX = ( vAxisX - DotProduct( vAxisY, vAxisX ) * vAxisY );
  2481. float flAxisXlen = vAxisX.Length();
  2482. if ( flAxisXlen > 0.05f )
  2483. {
  2484. vAxisX /= flAxisXlen;
  2485. }
  2486. else
  2487. {
  2488. // there's no useful direction for Y, just pick an orthogonal direction. Best if it's stable
  2489. VectorPerpendicularToVector( vAxisY, &vAxisX );
  2490. }
  2491. #endif
  2492. matrix3x4a_t tmPredicted;
  2493. tmPredicted.InitXYZ( vAxisX, vAxisY, CrossProduct( vAxisX, vAxisY ), pNodePos[ basis.nNode ] ); // in source1 it's down(-up), forward, right. Forward computed first and the others are going from it
  2494. AssertDbg( IsGoodWorldTransform( tmPredicted, 1, 0.01f ) );
  2495. matrix3x4a_t &tmNode = GetSim( m_pFeModel->NodeToCtrl( basis.nNode ) );
  2496. if ( basis.qAdjust.w == 1.0f )
  2497. {
  2498. tmNode = tmPredicted;
  2499. }
  2500. else
  2501. {
  2502. tmNode = ConcatTransforms( tmPredicted, QuaternionMatrix( basis.qAdjust ) );
  2503. }
  2504. AssertDbg( IsGoodWorldTransform( tmNode, 1, 0.01f ) );
  2505. AssertDbg( tmNode.GetOrigin() == pNodePos[ basis.nNode ] );
  2506. }
  2507. }
  2508. const uint16 *pRopes = m_pFeModel->m_pRopes;
  2509. uint nRopeBegin = m_pFeModel->m_nRopeCount;
  2510. for ( uint nRopeIndex = 0; nRopeIndex < m_pFeModel->m_nRopeCount; ++nRopeIndex )
  2511. {
  2512. uint nRopeEnd = m_pFeModel->m_pRopes[ nRopeIndex ], nEndNode = pRopes[ nRopeEnd - 1 ], nEndCtrl = m_pFeModel->NodeToCtrl( nEndNode );
  2513. // take care of the terminator of the chain
  2514. if ( nRopeEnd - nRopeBegin == 2 )
  2515. {
  2516. uint nNode = pRopes[ nRopeEnd - 1 ], nPrevNode = pRopes[ nRopeEnd - 2 ];
  2517. GetSim( nEndCtrl ) = AlignX( GetAnim( nEndCtrl ), pNodePos[ nNode ] - pNodePos[ nPrevNode ], pNodePos[ nNode ] );
  2518. }
  2519. else
  2520. {
  2521. AssertDbg( nRopeEnd - nRopeBegin > 2 );
  2522. for ( uint nNextLinkIndex = nRopeBegin + 2; ; )
  2523. {
  2524. uint nNode = pRopes[ nNextLinkIndex - 1 ], nCtrl = m_pFeModel->NodeToCtrl( nNode ), nNextNode = pRopes[ nNextLinkIndex ];
  2525. AssertDbg( IsGoodWorldTransform( GetAnim( nCtrl ) ) );
  2526. matrix3x4a_t tmAligned = AlignX( GetAnim( nCtrl ), pNodePos[ nNextNode ] - pNodePos[ nNode ], pNodePos[ nNode ] );
  2527. AssertDbg( IsGoodWorldTransform( tmAligned, 1.0f, 0.0005f ) );
  2528. GetSim( nCtrl ) = tmAligned;
  2529. ++nNextLinkIndex;
  2530. if ( nNextLinkIndex >= nRopeEnd )
  2531. {
  2532. Assert( nNextLinkIndex == nRopeEnd );
  2533. Set3x3( GetSim( nEndCtrl ), tmAligned, pNodePos[ nEndNode ] );
  2534. break;
  2535. }
  2536. }
  2537. }
  2538. nRopeBegin = nRopeEnd;
  2539. }
  2540. for ( uint nFreeNodeIndex = m_pFeModel->m_nFreeNodeCount; nFreeNodeIndex-- > 0; )
  2541. {
  2542. uint nNode = m_pFeModel->m_pFreeNodes[ nFreeNodeIndex ];
  2543. uint nCtrl = m_pFeModel->NodeToCtrl( nNode );
  2544. Set3x3( GetSim( nCtrl ), GetAnim( nCtrl ), pNodePos[ nNode ] );
  2545. }
  2546. // fix up the bones that need to be oriented according to some polygon because they don't have enough influences otherwise
  2547. for ( uint nRevOffsetIndex = m_pFeModel->m_nReverseOffsetCount; nRevOffsetIndex-- > 0; )
  2548. {
  2549. const FeNodeReverseOffset_t &revOffset = m_pFeModel->m_pReverseOffsets[ nRevOffsetIndex ];
  2550. matrix3x4a_t &tmBone = pSim[ revOffset.nBoneCtrl ];
  2551. Vector vOffsetWs = VectorRotate( revOffset.vOffset, tmBone );
  2552. Vector vTargetCenter = pNodePos[ revOffset.nTargetNode ] - vOffsetWs;
  2553. tmBone.SetOrigin( vTargetCenter );
  2554. uint nBoneNode = m_pFeModel->CtrlToNode( revOffset.nBoneCtrl );
  2555. m_pPos1[ nBoneNode ] = m_pPos0[ nBoneNode ] = vTargetCenter; // we can only compute this efficiently here, where we have all the matrices
  2556. }
  2557. m_pFeModel->FitTransforms( pNodePos, pSim );
  2558. if ( int nClothDebug = g_nClothDebug )
  2559. {
  2560. for ( int nCtrl = 0; nCtrl < m_nParticleCount; ++nCtrl )
  2561. {
  2562. if ( nClothDebug & CLOTH_DEBUG_SIM_ANIM_POS )
  2563. {
  2564. GetSim( nCtrl ).SetOrigin( GetAnim( nCtrl ).GetOrigin( ) );
  2565. }
  2566. else if ( nClothDebug & CLOTH_DEBUG_SIM_ANIM_ROT )
  2567. {
  2568. Set3x3( GetSim( nCtrl ), GetAnim( nCtrl ) );
  2569. }
  2570. }
  2571. }
  2572. AssertDbg( nRopeBegin == m_pFeModel->m_nRopeIndexCount );
  2573. m_bSimTransformsOutdated = false;
  2574. Validate();
  2575. }
  2576. return pSim;
  2577. }
  2578. void CSoftbody::SetAnimatedTransform( int nParticle, const matrix3x4a_t &transform )
  2579. {
  2580. GetAnim( nParticle ) = transform;
  2581. m_bAnimTransformChanged = true;
  2582. }
  2583. void CSoftbody::TouchAnimatedTransforms()
  2584. {
  2585. m_bAnimTransformChanged = true;
  2586. }
  2587. bool IsBoneMapUnique( const int16 *pBones, uint nCount )
  2588. {
  2589. CUtlHashtable< int16 > used;
  2590. for ( uint nBone = 0; nBone < nCount; ++nBone )
  2591. {
  2592. if ( pBones[ nBone ] >= 0 )
  2593. {
  2594. if ( used.HasElement( pBones[ nBone ] ) )
  2595. return false;
  2596. used.Insert( pBones[ nBone ] );
  2597. }
  2598. }
  2599. return true;
  2600. }
  2601. void CSoftbody::SetAnimatedTransforms( const matrix3x4a_t *pSimulationWorldTransforms )
  2602. {
  2603. float flInvScale = 1.0f / m_flModelScale;
  2604. AssertDbg( IsBoneMapUnique( m_pCtrlToModelBone, m_nParticleCount ) );
  2605. for( int nParticle = 0; nParticle < m_nParticleCount; ++nParticle )
  2606. {
  2607. int nBone = m_pCtrlToModelBone[ nParticle ];
  2608. if ( nBone >= 0 )
  2609. {
  2610. const matrix3x4a_t &tm = pSimulationWorldTransforms[ nBone ];
  2611. // AssertDbg( ( tm.GetOrigin() - m_vSimOrigin ).LengthSqr() < 500 * 500 ); // we shouldn't set bones far away from sim origin, or we won't be able to detect teleportation and adjust in time
  2612. matrix3x4a_t tmUnscaled = ScaleMatrix3x3( tm, flInvScale );
  2613. AssertDbg( IsGoodWorldTransform( tmUnscaled ) );
  2614. GetAnim( nParticle ) = tmUnscaled;
  2615. }
  2616. }
  2617. UpdateCtrlOffsets( false );
  2618. m_bAnimTransformChanged = true;
  2619. }
  2620. void CSoftbody::SetAnimatedTransformsNoScale( const matrix3x4a_t *pSimulationWorldTransforms )
  2621. {
  2622. AssertDbg( IsBoneMapUnique( m_pCtrlToModelBone, m_nParticleCount ) );
  2623. for ( int nParticle = 0; nParticle < m_nParticleCount; ++nParticle )
  2624. {
  2625. int nBone = m_pCtrlToModelBone[ nParticle ];
  2626. if ( nBone >= 0 )
  2627. {
  2628. const matrix3x4a_t &tm = pSimulationWorldTransforms[ nBone ];
  2629. AssertDbg( IsGoodWorldTransform( tm ) );
  2630. GetAnim( nParticle ) = tm;
  2631. }
  2632. }
  2633. UpdateCtrlOffsets( false );
  2634. m_bAnimTransformChanged = true;
  2635. }
  2636. void CSoftbody::UpdateCtrlOffsets( bool bOverridePose )
  2637. {
  2638. float flConstraintScale = m_flModelScale * m_flClothScale;
  2639. for ( int nOffset = 0; nOffset < m_pFeModel->m_nCtrlOffsets; ++nOffset )
  2640. {
  2641. const FeCtrlOffset_t &offset = m_pFeModel->m_pCtrlOffsets[ nOffset ];
  2642. matrix3x4a_t tm = GetAnim( offset.nCtrlParent );
  2643. tm.SetOrigin( VectorTransform( offset.vOffset * flConstraintScale, tm ) );
  2644. GetAnim( offset.nCtrlChild ) = tm;
  2645. if ( bOverridePose )
  2646. {
  2647. GetSim( offset.nCtrlChild ) = tm;
  2648. uint nNodeChild = m_pFeModel->CtrlToNode( offset.nCtrlChild );
  2649. m_pPos0[ nNodeChild ] = m_pPos1[ nNodeChild ] = tm.GetOrigin( );
  2650. }
  2651. }
  2652. for ( int nOsOffset = 0; nOsOffset < m_pFeModel->m_nCtrlOsOffsets; ++nOsOffset )
  2653. {
  2654. const FeCtrlOsOffset_t &offset = m_pFeModel->m_pCtrlOsOffsets[ nOsOffset ];
  2655. matrix3x4a_t tm = GetAnim( offset.nCtrlParent );
  2656. tm.SetOrigin( m_vRopeOffset + tm.GetOrigin() );
  2657. GetAnim( offset.nCtrlChild ) = tm;
  2658. if ( bOverridePose )
  2659. {
  2660. GetSim( offset.nCtrlChild ) = tm;
  2661. uint nNodeChild = m_pFeModel->CtrlToNode( offset.nCtrlChild );
  2662. m_pPos0[ nNodeChild ] = m_pPos1[ nNodeChild ] = tm.GetOrigin( );
  2663. }
  2664. }
  2665. }
  2666. void CSoftbody::InitializeTransforms( const int16 *pCtrlToBone, const matrix3x4a_t *pSimulationWorldTransforms )
  2667. {
  2668. float flInvScale = 1.0f / m_flModelScale; NOTE_UNUSED( flInvScale );
  2669. for ( uint nParticle = 0; nParticle < m_nParticleCount; ++nParticle )
  2670. {
  2671. int nBone = pCtrlToBone[ nParticle ];
  2672. if ( nBone >= 0 )
  2673. {
  2674. matrix3x4a_t tm = pSimulationWorldTransforms[ nBone ];
  2675. MatrixNormalize( tm, tm );
  2676. //AssertDbg( IsGoodWorldTransform( tm, m_flModelScale ) );
  2677. //tm.ScaleUpper3x3Matrix( flInvScale );
  2678. GetAnim( nParticle ) = GetSim( nParticle ) = tm;
  2679. AssertDbg( IsGoodWorldTransform( tm ) );
  2680. uint nNode = m_pFeModel->CtrlToNode( nParticle );
  2681. m_pPos0[ nNode ] = m_pPos1[ nNode ] = tm.GetOrigin( );
  2682. }
  2683. else
  2684. {
  2685. //GetAnim( nParticle ) = GetSim( nParticle ) = g_MatrixIdentity;
  2686. //m_pPos0[ nNode ] = m_pPos1[ nNode ] = vec3_origin;
  2687. // this means some nodes will not be set by animation, which will cause trouble esp. when they're static nodes that need to be animated
  2688. AssertDbg( !g_nClothDebug || m_pFeModel->FindCtrlOffsetByChild( nParticle ) || m_pFeModel->FindCtrlOsOffsetByChild( nParticle ) );
  2689. }
  2690. }
  2691. //
  2692. // also force all simulated particles to the new animated position
  2693. //
  2694. UpdateCtrlOffsets( true );
  2695. m_bAnimTransformChanged = false;
  2696. }
  2697. void DrawTaperedCapsule( IVDebugOverlay* pDebugOverlay, Vector v0, Vector v1, float r0, float r1 )
  2698. {
  2699. if ( r0 > r1 )
  2700. {
  2701. Swap( r0, r1 );
  2702. Swap( v0, v1 );
  2703. }
  2704. Vector vAxis = v1 - v0;
  2705. float flAxisLen = vAxis.Length();
  2706. float flSlope = 0;
  2707. int nStacks0, nStacks = 8;
  2708. if ( flAxisLen <= r1 - r0 + 0.001f )
  2709. {
  2710. // just draw a sphere
  2711. v0 = v1;
  2712. r0 = r1;
  2713. vAxis = Vector( 1, 0, 0 );
  2714. nStacks0 = nStacks / 2;
  2715. }
  2716. else
  2717. {
  2718. flSlope = asinf( ( r1 - r0 ) / flAxisLen );
  2719. vAxis /= flAxisLen;
  2720. nStacks0 = Min( nStacks - 1, Max( 1, int( ceil( ( M_PI / 2 - flSlope ) / ( M_PI / nStacks ) + .5f) ) ) );
  2721. }
  2722. int nSegments = 8;
  2723. CUtlVector< Vector > verts;
  2724. verts.SetCount( nStacks * nSegments + 2 );
  2725. verts[ 0 ] = v0 - vAxis * r0;
  2726. Vector vNormal0 = VectorPerpendicularToVector( vAxis );
  2727. Vector vNormal1 = CrossProduct( vAxis, vNormal0 );
  2728. for ( int nStack = 0; nStack < nStacks0; ++nStack )
  2729. {
  2730. float flStackAngle = ( nStack + 1 ) * ( M_PI / 2 - flSlope ) / nStacks0;
  2731. float csr0 = cosf( flStackAngle ) * r0, ssr0 = sinf( flStackAngle ) * r0;
  2732. for ( int nSeg = 0; nSeg < nSegments; ++nSeg )
  2733. {
  2734. float flPsi = 2 * nSeg * M_PI / nSegments;
  2735. verts[ 1 + nStack * nSegments + nSeg ] = v0 - csr0 * vAxis + cosf( flPsi ) * ssr0 * vNormal0 + sinf( flPsi ) * ssr0 * vNormal1;
  2736. }
  2737. }
  2738. for ( int nStack = nStacks0; nStack < nStacks; ++nStack )
  2739. {
  2740. float flStackAngle = ( M_PI / 2 - flSlope ) + ( nStack - nStacks0 ) * ( M_PI / 2 + flSlope ) / ( nStacks - nStacks0 );
  2741. float csr1 = cosf( flStackAngle ) * r1, ssr1 = sinf( flStackAngle ) * r1;
  2742. for ( int nSeg = 0; nSeg < nSegments; ++nSeg )
  2743. {
  2744. float flPsi = 2 * nSeg * M_PI / nSegments;
  2745. verts[ 1 + nStack * nSegments + nSeg ] = v1 - csr1 * vAxis + cosf( flPsi ) * ssr1 * vNormal0 + sinf( flPsi ) * ssr1 * vNormal1;
  2746. }
  2747. }
  2748. verts.Tail() = v1 + vAxis * r1;
  2749. int r = 128, g = 128, b = 128, a = 32;
  2750. for ( int nSeg = 0; nSeg < nSegments; ++nSeg )
  2751. {
  2752. int nNextSeg = ( nSeg + 1 ) % nSegments;
  2753. pDebugOverlay->AddTriangleOverlay( verts[ 0 ], verts[ 1 + nSeg ], verts[ 1 + nNextSeg ], r, g, b, a, false, 0 );
  2754. for ( int nStack = 1; nStack < nStacks; ++nStack )
  2755. {
  2756. int nPrevStack = nStack - 1;
  2757. pDebugOverlay->AddTriangleOverlay( verts[ 1 + nPrevStack * nSegments + nSeg ], verts[ 1 + nPrevStack * nSegments + nNextSeg ], verts[ 1 + nStack * nSegments + nNextSeg ], r, g, b, a, false, 0 );
  2758. pDebugOverlay->AddTriangleOverlay( verts[ 1 + nStack * nSegments + nNextSeg ], verts[ 1 + nStack * nSegments + nSeg ], verts[ 1 + nPrevStack * nSegments + nSeg ], r, g, b, a, false, 0 );
  2759. }
  2760. pDebugOverlay->AddTriangleOverlay( verts.Tail(), verts[ 1 + ( nStacks - 1 ) * nSegments + nNextSeg ], verts[ 1 + ( nStacks - 1 ) * nSegments + nSeg ], r, g, b, a, false, 0 );
  2761. }
  2762. for ( int nSeg = 0; nSeg < nSegments; ++nSeg )
  2763. {
  2764. for ( int nStack = nStacks0 - 1; nStack <= nStacks0; ++nStack )
  2765. {
  2766. int nNextSeg = ( nSeg + 1 ) % nSegments;
  2767. pDebugOverlay->AddLineOverlay( verts[ 1 + nStack * nSegments + nSeg ], verts[ 1 + nStack * nSegments + nNextSeg ], 200, 200, 200, 200, false, 0 );
  2768. }
  2769. }
  2770. }
  2771. void AddLineOverlay( CMeshBuilder &meshBuilder, const matrix3x4a_t &tmViewModel, const Vector &v0, const Vector &v1, unsigned char r, unsigned char g, unsigned char b, unsigned char a )
  2772. {
  2773. Vector v0v = VectorTransform( v0, tmViewModel );
  2774. Vector v1v = VectorTransform( v1, tmViewModel );
  2775. meshBuilder.Position3fv( v0v.Base() );
  2776. meshBuilder.Color4ub( r, g, b, a );
  2777. meshBuilder.AdvanceVertex();
  2778. meshBuilder.Position3fv( v1v.Base() );
  2779. meshBuilder.Color4ub( r, g, b, a );
  2780. meshBuilder.AdvanceVertex();
  2781. }
  2782. // HLMV: Called from StudioModel::DrawModel->DrawSoftbody, in studio_render.cpp
  2783. // Client: Called from CSoftbodyProcess::PostRender, in physics_softbody.cpp
  2784. void CSoftbody::Draw( const RnDebugDrawOptions_t &options, IMesh *pDynamicMesh )
  2785. {
  2786. if ( options.m_nLayers == 0 )
  2787. return;
  2788. VectorAligned *pPos0 = m_pPos0, *pPos1 = m_pPos1;
  2789. CUtlVectorAligned< VectorAligned > posBuffer;
  2790. matrix3x4a_t tmViewModel = g_MatrixIdentity;
  2791. if ( m_nAnimSpace == SOFTBODY_ANIM_SPACE_LOCAL )
  2792. {
  2793. posBuffer.SetCount( m_nNodeCount * 2 );
  2794. pPos0 = &posBuffer[ 0 ];
  2795. pPos1 = &posBuffer[ m_nNodeCount ];
  2796. AngleMatrix( m_vSimAngles, m_vSimOrigin, tmViewModel );
  2797. for ( uint nNode = 0; nNode < m_nNodeCount; ++nNode )
  2798. {
  2799. pPos0[ nNode ] = VectorTransform( m_pPos0[ nNode ], tmViewModel );
  2800. pPos1[ nNode ] = VectorTransform( m_pPos1[ nNode ], tmViewModel );
  2801. }
  2802. }
  2803. if ( options.m_nLayers & RN_SOFTBODY_DRAW_WIND )
  2804. {
  2805. CMeshBuilder meshBuilder;
  2806. meshBuilder.Begin( pDynamicMesh, MATERIAL_LINES, m_nNodeCount );
  2807. CUtlVector< VectorAligned > pos2;
  2808. pos2.CopyArray( m_pPos1, m_nNodeCount );
  2809. IntegrateWind( pos2.Base(), 0.05f );
  2810. float flMaxLenInv = 100.0f;
  2811. for ( uint n = 0; n < m_nNodeCount; ++n )
  2812. {
  2813. float flLen = ( pos2[ n ] - m_pPos1[ n ] ).Length();
  2814. if ( flLen * flMaxLenInv > 1.0f )
  2815. {
  2816. flMaxLenInv = 1.0f / flLen;
  2817. }
  2818. }
  2819. for ( uint n = 0; n < m_nNodeCount; ++n )
  2820. {
  2821. float flLen = ( pos2[ n ] - m_pPos1[ n ] ).Length();
  2822. if ( flLen < 0.001f )
  2823. continue;
  2824. meshBuilder.Position3fv( m_pPos1[ n].Base() );
  2825. meshBuilder.Color4ub( 255, 255, 255, 255 );
  2826. meshBuilder.AdvanceVertex();
  2827. meshBuilder.Position3fv( pos2[ n ].Base() );
  2828. meshBuilder.Color4ub( 255, 255 - 128 * flLen * flMaxLenInv, 255, 255 );
  2829. meshBuilder.AdvanceVertex();
  2830. }
  2831. meshBuilder.End();
  2832. }
  2833. // find virtual nodes
  2834. CVarBitVec virtualCtrls( m_nParticleCount );
  2835. uint nVirtualCtrls = ComputeVirtualCtrls( virtualCtrls );
  2836. // the positions of nodes are not correct in dormant mode
  2837. const matrix3x4a_t *pSim = IsDormant() ? GetAnimatedTransforms() : GetParticleTransforms( NULL, SOFTBODY_SIM_TRANSFORMS_INCLUDE_STATIC ); // make the softbody compute the sim bones
  2838. if ( m_pFeModel->m_nSimdTriCount[ 0 ] && (options.m_nLayers & RN_SOFTBODY_DRAW_POLYGONS))
  2839. {
  2840. CMeshBuilder meshBuilder;
  2841. meshBuilder.Begin( pDynamicMesh, MATERIAL_TRIANGLES, m_pFeModel->m_nSimdTriCount[ 0 ] * 4 );
  2842. uint32 fourColors[ 4 ] = { 0xBBAAAAAA, 0xCCBBBBBB, 0xDDCCCCCC, 0xEEDDDDDD };
  2843. for ( int nSimdTri = 0; nSimdTri < m_pFeModel->m_nSimdTriCount[ 0 ]; ++nSimdTri )
  2844. {
  2845. const FeSimdTri_t &tri = m_pFeModel->m_pSimdTris[ nSimdTri ];
  2846. for ( int q = 0; q < 4; ++q )
  2847. {
  2848. for ( int j = 0; j < 3; ++j )
  2849. {
  2850. meshBuilder.Position3fv( pPos1[ tri.nNode[ j ][ q ] ].Base() );
  2851. meshBuilder.Color4ubv( ( const unsigned char* )&fourColors[ nSimdTri & 3 ] );
  2852. meshBuilder.AdvanceVertex();
  2853. }
  2854. }
  2855. }
  2856. meshBuilder.End();
  2857. }
  2858. if ( m_pFeModel->m_nSimdQuadCount[ 0 ] && ( options.m_nLayers & RN_SOFTBODY_DRAW_POLYGONS ) )
  2859. {
  2860. CMeshBuilder meshBuilder;
  2861. int nBaseIndex = meshBuilder.IndexCount();
  2862. meshBuilder.Begin( pDynamicMesh, MATERIAL_LINES, m_nNodeCount, m_pFeModel->m_nSimdQuadCount[ 0 ] * 4 * 8 );
  2863. uint32 fourColors[ 4 ] = { 0xAABBAAAA, 0xBBCCBBBB, 0xCCDDCCCC, 0xDDEEDDDD };
  2864. for ( uint n = 0; n < m_nNodeCount; ++n )
  2865. {
  2866. meshBuilder.Position3fv( pPos1[ n ].Base() );
  2867. meshBuilder.Color4ubv( ( const unsigned char* )&fourColors[ n < m_pFeModel->m_nStaticNodes ? 1 : 0 ] );
  2868. meshBuilder.AdvanceVertex();
  2869. }
  2870. CUtlHashtable< uint32 > linesDrawn;
  2871. for ( int nSimdQuad = 0; nSimdQuad < m_pFeModel->m_nSimdQuadCount[ 0 ]; nSimdQuad ++)
  2872. {
  2873. const FeSimdQuad_t &quad = m_pFeModel->m_pSimdQuads[ nSimdQuad ];
  2874. for ( int q = 0; q < 4; ++q )
  2875. {
  2876. for ( int j = 0; j < 4; ++j )
  2877. {
  2878. uint jIdxs = ( 0x16BC >> ( j * 4 ) ) & 15; // 0-1, 1-2, 2-3, 3-0
  2879. // 0x17E8 >> ... : 0-1, 1-3, 3-2, 2-0
  2880. uint nIndex0 = quad.nNode[ jIdxs >> 2 ][ q ], nIndex1 = quad.nNode[ jIdxs & 3 ][ q ], nIndexPair = ( nIndex0 << 16 ) | nIndex1;
  2881. if ( !linesDrawn.HasElement( nIndexPair ) )
  2882. {
  2883. linesDrawn.Insert( nIndexPair );
  2884. meshBuilder.Index( nIndex0 + nBaseIndex );
  2885. meshBuilder.AdvanceIndex();
  2886. meshBuilder.Index( nIndex1 + nBaseIndex );
  2887. meshBuilder.AdvanceIndex();
  2888. }
  2889. }
  2890. }
  2891. }
  2892. meshBuilder.End();
  2893. }
  2894. if ( m_pFeModel->m_nSimdRodCount || nVirtualCtrls )
  2895. {
  2896. CMeshBuilder meshBuilder;
  2897. meshBuilder.Begin( pDynamicMesh, MATERIAL_LINES, m_pFeModel->m_nSimdRodCount * 4 + nVirtualCtrls * 6 );
  2898. if ( options.m_nLayers & RN_SOFTBODY_DRAW_EDGES )
  2899. {
  2900. uint32 fourColors[ 4 ] = { 0xBBBBAAAA, 0xCCCCBBBB, 0xDDDDCCCC, 0xEEEEDDDD };
  2901. for ( int nSimdRod = 0; nSimdRod < m_pFeModel->m_nSimdRodCount; ++nSimdRod )
  2902. {
  2903. const FeSimdRodConstraint_t &rod = m_pFeModel->m_pSimdRods[ nSimdRod ];
  2904. for ( int q = 0; q < 4; ++q )
  2905. {
  2906. for ( int j = 0; j < 2; ++j )
  2907. {
  2908. meshBuilder.Position3fv( pPos1[ rod.nNode[ j ][ q ] ].Base() );
  2909. meshBuilder.Color4ubv( ( const unsigned char* )&fourColors[ nSimdRod & 3 ] );
  2910. meshBuilder.AdvanceVertex();
  2911. }
  2912. }
  2913. }
  2914. }
  2915. if ( options.m_nLayers & RN_SOFTBODY_DRAW_INDICES )
  2916. {
  2917. }
  2918. if ( options.m_nLayers & RN_SOFTBODY_DRAW_BASES )
  2919. {
  2920. for ( int nCtrl = 0; nCtrl < m_nParticleCount; ++nCtrl )
  2921. {
  2922. uint nNode = m_pFeModel->CtrlToNode( nCtrl );
  2923. if ( nNode < m_nNodeCount && !virtualCtrls.IsBitSet( nNode ) )
  2924. {
  2925. float flAxisScale = ( nNode < m_pFeModel->m_nRotLockStaticNodes ? 2.0f : 5.0f );
  2926. // this is a dynamic node
  2927. const matrix3x4a_t &node = pSim[ nCtrl ];
  2928. AddLineOverlay( meshBuilder, tmViewModel, node.GetOrigin(), node.GetOrigin() + node.GetColumn( X_AXIS ) * flAxisScale * 2, 255, 180, 180, 255 );
  2929. AddLineOverlay( meshBuilder, tmViewModel, node.GetOrigin(), node.GetOrigin() + node.GetColumn( Y_AXIS ) * flAxisScale, 180, 255, 180, 255 );
  2930. AddLineOverlay( meshBuilder, tmViewModel, node.GetOrigin(), node.GetOrigin() + node.GetColumn( Z_AXIS ) * flAxisScale, 180, 180, 255, 255 );
  2931. const matrix3x4a_t &anim = GetAnim( nCtrl );
  2932. float flAnimAxisScale = 0.5f * flAxisScale;
  2933. AddLineOverlay( meshBuilder, tmViewModel, anim.GetOrigin(), anim.GetOrigin() + anim.GetColumn( X_AXIS ) * flAnimAxisScale, 200, 128, 128, 255 );
  2934. AddLineOverlay( meshBuilder, tmViewModel, anim.GetOrigin(), anim.GetOrigin() + anim.GetColumn( Y_AXIS ) * flAnimAxisScale, 128, 200, 128, 255 );
  2935. AddLineOverlay( meshBuilder, tmViewModel, anim.GetOrigin(), anim.GetOrigin() + anim.GetColumn( Z_AXIS ) * flAnimAxisScale, 128, 128, 200, 255 );
  2936. }
  2937. }
  2938. }
  2939. meshBuilder.End();
  2940. }
  2941. }
  2942. void CSoftbody::Draw( const RnDebugDrawOptions_t &options, IVDebugOverlay* pDebugOverlay )
  2943. {
  2944. if ( !m_bDebugDraw )
  2945. return;
  2946. uint nDebugLayers = options.m_nLayers;
  2947. MPROF_AUTO_FAST( SoftbodyDraw );
  2948. VectorAligned *pPos0 = m_pPos0, *pPos1 = m_pPos1;
  2949. CUtlVector< VectorAligned > posBuffer;
  2950. if ( m_nAnimSpace == SOFTBODY_ANIM_SPACE_LOCAL )
  2951. {
  2952. posBuffer.SetCount( m_nNodeCount * 2 );
  2953. pPos0 = &posBuffer[ 0 ];
  2954. pPos1 = &posBuffer[ m_nNodeCount ];
  2955. matrix3x4a_t tmEntity;
  2956. AngleMatrix( m_vSimAngles, m_vSimOrigin, tmEntity );
  2957. for ( uint nNode = 0; nNode < m_nNodeCount; ++nNode )
  2958. {
  2959. pPos0[ nNode ] = VectorTransform( m_pPos0[ nNode ], tmEntity );
  2960. pPos1[ nNode ] = VectorTransform( m_pPos1[ nNode ], tmEntity );
  2961. }
  2962. }
  2963. // find virtual nodes
  2964. CVarBitVec virtualCtrls( m_nParticleCount );
  2965. ComputeVirtualCtrls( virtualCtrls );
  2966. // the positions of nodes are not correct in dormant mode
  2967. const matrix3x4a_t *pSim = IsDormant() ? GetAnimatedTransforms() : GetParticleTransforms( NULL, SOFTBODY_SIM_TRANSFORMS_INCLUDE_STATIC ); // make the softbody compute the sim bones
  2968. VertexColor_t colorNeg( 255, 255, 200, 255 ), colorPos( 255, 200, 200, 255 ), colorNeutral( 200, 200, 200, 255 );
  2969. for ( uint nTapCap = m_pFeModel->m_nTaperedCapsuleStretchCount; nTapCap-- > 0; )
  2970. {
  2971. const FeTaperedCapsuleStretch_t &tc = m_pFeModel->m_pTaperedCapsuleStretches[ nTapCap ];
  2972. DrawTaperedCapsule( pDebugOverlay, pPos1[ tc.nNode[ 0 ] ], pPos1[ tc.nNode[ 1 ] ], tc.flRadius[ 0 ] * m_flModelScale, tc.flRadius[ 1 ] * m_flModelScale );
  2973. }
  2974. for ( uint nTapCap = m_pFeModel->m_nTaperedCapsuleRigidCount; nTapCap-- > 0; )
  2975. {
  2976. const FeTaperedCapsuleRigid_t &tc = m_pFeModel->m_pTaperedCapsuleRigids[ nTapCap ];
  2977. const matrix3x4a_t &tm = GetAnim( tc.nNode );
  2978. DrawTaperedCapsule( pDebugOverlay, tm.TransformVector( tc.vCenter[ 0 ] * m_flModelScale ), tm.TransformVector( tc.vCenter[ 1 ] * m_flModelScale ), tc.flRadius[ 0 ] * m_flModelScale, tc.flRadius[ 1 ] * m_flModelScale );
  2979. }
  2980. for ( uint nSphere = m_pFeModel->m_nSphereRigidCount; nSphere-- > 0; )
  2981. {
  2982. const FeSphereRigid_t &tc = m_pFeModel->m_pSphereRigids[ nSphere ];
  2983. const matrix3x4a_t &tm = GetAnim( tc.nNode );
  2984. DrawTaperedCapsule( pDebugOverlay, tm.TransformVector( tc.vCenter * m_flModelScale ), tm.TransformVector( tc.vCenter * m_flModelScale ), tc.flRadius* m_flModelScale, tc.flRadius * m_flModelScale );
  2985. }
  2986. float flConstraintScale = m_flModelScale * m_flClothScale;
  2987. if ( m_nDebugDrawTreeFlags && m_pFeModel->m_pTreeParents && m_nDebugDrawTreeBeginLevel != m_nDebugDrawTreeEndLevel )
  2988. {
  2989. CUtlVector< FeAabb_t > boxes;
  2990. const uint nDynCount = m_pFeModel->GetDynamicNodeCount();
  2991. boxes.SetCount( nDynCount - 1 );
  2992. m_pFeModel->ComputeCollisionTreeBounds( pPos1 + m_pFeModel->m_nStaticNodes, boxes.Base() );
  2993. CUtlVector< uint16 > levels;
  2994. levels.SetCount( 2 * nDynCount );
  2995. levels.FillWithValue( 0 );
  2996. levels[ 2 * nDynCount - 1 ] = 0xABCD;
  2997. if ( m_nDebugDrawTreeFlags & SOFTBODY_DEBUG_DRAW_TREE_BOTTOM_UP )
  2998. {
  2999. m_pFeModel->ComputeCollisionTreeHeightBottomUp( levels.Base() + nDynCount );
  3000. }
  3001. else if ( m_nDebugDrawTreeFlags & SOFTBODY_DEBUG_DRAW_TREE_TOP_DOWN )
  3002. {
  3003. m_pFeModel->ComputeCollisionTreeDepthTopDown( levels.Base() );
  3004. }
  3005. else
  3006. {
  3007. for ( int nDynNode = 0; nDynNode < nDynCount - 1; ++nDynNode )
  3008. {
  3009. levels[ nDynCount + nDynNode ] = nDynNode + 1;
  3010. }
  3011. }
  3012. Assert( levels[ 2 * nDynCount - 1 ] == 0xABCD );
  3013. //int nStep = m_nDebugDrawTreeBeginLevel < m_nDebugDrawTreeEndLevel ? 1 : -1;
  3014. //int nColorStep = ( nStep * 200 ) / ( m_nDebugDrawTreeEndLevel - m_nDebugDrawTreeBeginLevel );
  3015. //int nColor = 255;
  3016. //for ( int nLevel = m_nDebugDrawTreeBeginLevel; nLevel != m_nDebugDrawTreeEndLevel; nLevel += nStep, nColor += nColorStep )
  3017. for ( uint nDynNode = 0; nDynNode < nDynCount - 1; ++nDynNode )
  3018. {
  3019. int nLevel = levels[ nDynCount + nDynNode ];
  3020. if ( ( nLevel >= m_nDebugDrawTreeBeginLevel && nLevel < m_nDebugDrawTreeEndLevel ) || ( nLevel <= m_nDebugDrawTreeBeginLevel && nLevel > m_nDebugDrawTreeEndLevel ) )
  3021. {
  3022. FeAabb_t &box = boxes[ nDynNode ];
  3023. NOTE_UNUSED( box );
  3024. int nColor = 180 - ( 80 * ( nLevel - m_nDebugDrawTreeBeginLevel ) ) / ( m_nDebugDrawTreeEndLevel - m_nDebugDrawTreeBeginLevel );
  3025. NOTE_UNUSED( nColor );
  3026. //pDebugOverlay->AddBoxOverlay( AsVector( box.m_vMinBounds ), AsVector( box.m_vMaxBounds ), nColor, nColor, nColor, nColor );
  3027. // pDebugOverlay->AddLineOverlay( Vector( a.x, a.y, a.z ), Vector( b.x, a.y, a.z ), nColor, nColor, nColor, nColor );
  3028. // pDebugOverlay->AddLineOverlay( Vector( a.x, a.y, a.z ), Vector( a.x, b.y, a.z ), nColor, nColor, nColor, nColor );
  3029. // pDebugOverlay->AddLineOverlay( Vector( a.x, a.y, a.z ), Vector( a.x, a.y, b.z ), nColor, nColor, nColor, nColor );
  3030. // pDebugOverlay->AddLineOverlay( Vector( a.x, b.y, b.z ), Vector( b.x, b.y, b.z ), nColor, nColor, nColor, nColor );
  3031. // pDebugOverlay->AddLineOverlay( Vector( b.x, a.y, b.z ), Vector( b.x, b.y, b.z ), nColor, nColor, nColor, nColor );
  3032. // pDebugOverlay->AddLineOverlay( Vector( b.x, b.y, a.z ), Vector( b.x, b.y, b.z ), nColor, nColor, nColor, nColor );
  3033. //
  3034. // pDebugOverlay->AddLineOverlay( Vector( b.x, a.y, a.z ), Vector( b.x, b.y, a.z ), nColor, nColor, nColor, nColor );
  3035. // pDebugOverlay->AddLineOverlay( Vector( a.x, b.y, a.z ), Vector( a.x, b.y, b.z ), nColor, nColor, nColor, nColor );
  3036. // pDebugOverlay->AddLineOverlay( Vector( a.x, a.y, b.z ), Vector( b.x, a.y, b.z ), nColor, nColor, nColor, nColor );
  3037. // pDebugOverlay->AddLineOverlay( Vector( b.x, b.y, a.z ), Vector( a.x, b.y, b.z ), nColor, nColor, nColor, nColor );
  3038. // pDebugOverlay->AddLineOverlay( Vector( a.x, b.y, b.z ), Vector( b.x, a.y, b.z ), nColor, nColor, nColor, nColor );
  3039. // pDebugOverlay->AddLineOverlay( Vector( b.x, a.y, b.z ), Vector( b.x, b.y, a.z ), nColor, nColor, nColor, nColor );
  3040. }
  3041. }
  3042. if ( m_nDebugDrawTreeFlags & ( 1 << 4 ) )
  3043. return;
  3044. }
  3045. for ( uint nQuad = 0; nQuad < m_pFeModel->m_nQuadCount[2]; ++nQuad )
  3046. {
  3047. const FeQuad_t &quad = m_pFeModel->m_pQuads[ nQuad ];
  3048. VectorAligned p0 = pPos1[ quad.nNode[ 0 ] ], p1 = pPos1[ quad.nNode[ 1 ] ], p2 = pPos1[ quad.nNode[ 2 ] ], p3 = pPos1[ quad.nNode[ 3 ] ];
  3049. float flError = m_pFeModel->RelaxQuad2( 1.0f, flConstraintScale, quad, p0, p1, p2, p3 ); NOTE_UNUSED( flError );
  3050. int nRelError = int ( 64 * Clamp< float >( flError / Sqr( .125f * Perimeter( quad ) ), 0, 1 ) );
  3051. pDebugOverlay->AddTriangleOverlay( p0, p1, p2, 192 + nRelError, 255 - nRelError, 255, 64, false, 0 );
  3052. pDebugOverlay->AddTriangleOverlay( p0, p2, p3, 192 + nRelError, 255 - nRelError, 245, 64, false, 0 );
  3053. }
  3054. for ( uint nQuad = m_pFeModel->m_nQuadCount[ 2 ]; nQuad < m_pFeModel->m_nQuadCount[ 1 ]; ++nQuad )
  3055. {
  3056. const FeQuad_t &quad = m_pFeModel->m_pQuads[ nQuad ];
  3057. VectorAligned p0 = pPos1[ quad.nNode[ 0 ] ], p1 = pPos1[ quad.nNode[ 1 ] ], p2 = pPos1[ quad.nNode[ 2 ] ], p3 = pPos1[ quad.nNode[ 3 ] ];
  3058. float flError = m_pFeModel->RelaxQuad1( 1.0f, flConstraintScale, quad, p0, p1, p2, p3 ); NOTE_UNUSED( flError );
  3059. int nRelError = int( 64 * Clamp< float >( flError / Sqr( .125f * Perimeter( quad ) ), 0, 1 ) );
  3060. pDebugOverlay->AddTriangleOverlay( p0, p1, p2, 192 + nRelError, 255 - nRelError, 255, 64, false, 0 );
  3061. pDebugOverlay->AddTriangleOverlay( p0, p2, p3, 192 + nRelError, 255 - nRelError, 245, 64, false, 0 );
  3062. }
  3063. for ( uint nQuad = m_pFeModel->m_nQuadCount[ 1 ]; nQuad < m_pFeModel->m_nQuadCount[ 0 ]; ++nQuad )
  3064. {
  3065. const FeQuad_t &quad = m_pFeModel->m_pQuads[ nQuad ];
  3066. VectorAligned p0 = pPos1[ quad.nNode[ 0 ] ], p1 = pPos1[ quad.nNode[ 1 ] ], p2 = pPos1[ quad.nNode[ 2 ] ], p3 = pPos1[ quad.nNode[ 3 ] ];
  3067. float flError = m_pFeModel->RelaxQuad0( 1.0f, flConstraintScale, quad, p0, p1, p2, p3 ); NOTE_UNUSED( flError );
  3068. int nRelError = int( 64 * Clamp< float >( flError / Sqr( .125f * Perimeter( quad ) ), 0, 1 ) );
  3069. pDebugOverlay->AddTriangleOverlay( p0, p1, p2, 192 + nRelError, 255 - nRelError, 255, 64, false, 0 );
  3070. pDebugOverlay->AddTriangleOverlay( p0, p2, p3, 192 + nRelError, 255 - nRelError, 245, 64, false, 0 );
  3071. }
  3072. for ( uint nTri = 0; nTri < m_pFeModel->m_nTriCount[ 2 ]; ++nTri )
  3073. {
  3074. const FeTri_t &tri = m_pFeModel->m_pTris[ nTri ];
  3075. VectorAligned p0 = pPos1[ tri.nNode[ 0 ] ], p1 = pPos1[ tri.nNode[ 1 ] ], p2 = pPos1[ tri.nNode[ 2 ] ];
  3076. float flError = m_pFeModel->RelaxTri2( 1.0f, flConstraintScale, tri, p0, p1, p2 ); NOTE_UNUSED( flError );
  3077. int nRelError = int( 64 * Clamp< float >( flError / Sqr( .125f * Perimeter( tri ) ), 0, 1 ) );
  3078. pDebugOverlay->AddTriangleOverlay( p0, p1, p2, 192 + nRelError, 255 - nRelError, 255, 64, false, 0 );
  3079. }
  3080. for ( uint nTri = m_pFeModel->m_nTriCount[ 2 ]; nTri < m_pFeModel->m_nTriCount[ 1 ]; ++nTri )
  3081. {
  3082. const FeTri_t &tri = m_pFeModel->m_pTris[ nTri ];
  3083. VectorAligned p0 = pPos1[ tri.nNode[ 0 ] ], p1 = pPos1[ tri.nNode[ 1 ] ], p2 = pPos1[ tri.nNode[ 2 ] ];
  3084. float flError = m_pFeModel->RelaxTri1( 1.0f, flConstraintScale, tri, p0, p1, p2 ); NOTE_UNUSED( flError );
  3085. int nRelError = int( 64 * Clamp< float >( flError / Sqr( .125f * Perimeter( tri ) ), 0, 1 ) );
  3086. pDebugOverlay->AddTriangleOverlay( p0, p1, p2, 192 + nRelError, 255 - nRelError, 255, 64, false, 0 );
  3087. }
  3088. for ( uint nTri = m_pFeModel->m_nTriCount[ 1 ]; nTri < m_pFeModel->m_nTriCount[ 0 ]; ++nTri )
  3089. {
  3090. const FeTri_t &tri = m_pFeModel->m_pTris[ nTri ];
  3091. VectorAligned p0 = pPos1[ tri.nNode[ 0 ] ], p1 = pPos1[ tri.nNode[ 1 ] ], p2 = pPos1[ tri.nNode[ 2 ] ];
  3092. float flError = m_pFeModel->RelaxTri0( 1.0f, flConstraintScale, tri, p0, p1, p2 ); NOTE_UNUSED( flError );
  3093. int nRelError = int( 64 * Clamp< float >( flError / Sqr( .125f * Perimeter( tri ) ), 0, 1 ) );
  3094. pDebugOverlay->AddTriangleOverlay( p0, p1, p2, 192 + nRelError, 255 - nRelError, 255, 64, false, 0 );
  3095. }
  3096. CUtlHashtable< uint32 > drawnLines;
  3097. for ( uint nQuad = 0; nQuad < m_pFeModel->m_nQuadCount[ 0 ]; ++nQuad )
  3098. {
  3099. const FeQuad_t &quad = m_pFeModel->m_pQuads[ nQuad ];
  3100. VectorAligned vPos[ 4 ] = { pPos1[ quad.nNode[ 0 ] ], pPos1[ quad.nNode[ 1 ] ], pPos1[ quad.nNode[ 2 ] ], pPos1[ quad.nNode[ 3 ] ] };
  3101. for ( int j = 0; j < 4; ++j )
  3102. {
  3103. int j1 = ( j + 1 ) % 4;
  3104. bool bStatic = j == 0 && nQuad < m_pFeModel->m_nQuadCount[ 2 ];
  3105. uint id1 = quad.nNode[ j ] | ( quad.nNode[ j1 ] << 16 ), id2 = quad.nNode[ j1 ] | ( quad.nNode[ j ] << 16 );
  3106. if ( drawnLines.Find( id1 ) == drawnLines.InvalidHandle() )
  3107. {
  3108. drawnLines.Insert( id1 );
  3109. drawnLines.Insert( id2 );
  3110. pDebugOverlay->AddLineOverlay( vPos[ j ], vPos[ j1 ], bStatic ? 200 : 255, bStatic ? 100 : 255, 200, 64, false, 0 );
  3111. }
  3112. }
  3113. }
  3114. if ( !m_bEnableSimd ) // ? m_pFeModel->m_nTriCount[ 0 ] : m_pFeModel->m_nSimdTriCount[ 0 ]
  3115. {
  3116. for ( uint nTri = 0; nTri < m_pFeModel->m_nTriCount[ 0 ]; ++nTri )
  3117. {
  3118. const FeTri_t &tri = m_pFeModel->m_pTris[ nTri ];
  3119. VectorAligned vPos[ 3 ] = { pPos1[ tri.nNode[ 0 ] ], pPos1[ tri.nNode[ 1 ] ], pPos1[ tri.nNode[ 2 ] ] };
  3120. for ( int j = 0; j < 3; ++j )
  3121. {
  3122. int j1 = ( j + 1 ) % 3;
  3123. bool bStatic = j == 0 && nTri < m_pFeModel->m_nTriCount[ 2 ];
  3124. uint id1 = tri.nNode[ j ] | ( tri.nNode[ j1 ] << 16 ), id2 = tri.nNode[ j1 ] | ( tri.nNode[ j ] << 16 );
  3125. if ( drawnLines.Find( id1 ) == drawnLines.InvalidHandle( ) )
  3126. {
  3127. drawnLines.Insert( id1 );
  3128. drawnLines.Insert( id2 );
  3129. pDebugOverlay->AddLineOverlay( vPos[ j ], vPos[ j1 ], bStatic ? 200 : 255, bStatic ? 100 : 255, 200, 64, false, 0 );
  3130. }
  3131. }
  3132. }
  3133. }
  3134. else
  3135. {
  3136. for ( int nSimdTri = 0; nSimdTri < m_pFeModel->m_nSimdTriCount[ 0 ]; ++nSimdTri )
  3137. {
  3138. const FeSimdTri_t &tri = m_pFeModel->m_pSimdTris[ nSimdTri ];
  3139. for ( int q = 0; q < 4; ++q )
  3140. {
  3141. VectorAligned vPos[ 3 ] = { pPos1[ tri.nNode[ 0 ][ q ] ], pPos1[ tri.nNode[ 1 ][ q ] ], pPos1[ tri.nNode[ 2 ][ q ] ] };
  3142. for ( int j = 0; j < 3; ++j )
  3143. {
  3144. int j1 = ( j + 1 ) % 3;
  3145. bool bStatic = j == 0 && nSimdTri < m_pFeModel->m_nSimdTriCount[ 2 ];
  3146. uint id1 = tri.nNode[ j ][ q ] | ( tri.nNode[ j1 ][ q ] << 16 ), id2 = tri.nNode[ j1 ][ q ] | ( tri.nNode[ j ][ q ] << 16 );
  3147. if ( drawnLines.Find( id1 ) == drawnLines.InvalidHandle( ) )
  3148. {
  3149. drawnLines.Insert( id1 );
  3150. drawnLines.Insert( id2 );
  3151. pDebugOverlay->AddLineOverlay( vPos[ j ], vPos[ j1 ], bStatic ? 200 : 255, bStatic ? 100 : 255, 200, 64, false, 0 );
  3152. }
  3153. }
  3154. }
  3155. }
  3156. }
  3157. for ( int nSimdRod = 0; nSimdRod < m_pFeModel->m_nSimdRodCount; ++nSimdRod )
  3158. {
  3159. const FeSimdRodConstraint_t &rod = m_pFeModel->m_pSimdRods[ nSimdRod ];
  3160. for ( int q = 0; q < 4; ++q )
  3161. {
  3162. int v0 = rod.nNode[ 0 ][ q ], v1 = rod.nNode[ 1 ][ q ];
  3163. VectorAligned vPos[ 2 ] = { pPos1[ v0 ], pPos1[ v1 ] };
  3164. bool bStatic = SubFloat( rod.f4Weight0, q ) == 0.0f || SubFloat( rod.f4Weight0, q ) == 1.0f ;
  3165. bool bVirtual = virtualCtrls[ m_pFeModel->NodeToCtrl( v0 ) ] || virtualCtrls[ m_pFeModel->NodeToCtrl( v1 ) ];
  3166. uint id1 = v0 | ( v1 << 16 ), id2 = v1 | ( v0 << 16 );
  3167. if ( drawnLines.Find( id1 ) == drawnLines.InvalidHandle( ) )
  3168. {
  3169. drawnLines.Insert( id1 );
  3170. drawnLines.Insert( id2 );
  3171. int r = 200, g = 200, b = 255;
  3172. if ( bStatic )
  3173. {
  3174. r = 150; b = 100;
  3175. }
  3176. if ( bVirtual )
  3177. {
  3178. r /= 2; g /= 2; b /= 2;
  3179. }
  3180. pDebugOverlay->AddLineOverlay( vPos[ 0 ], vPos[ 1 ], r, g, b, 64, false, 0 );
  3181. }
  3182. }
  3183. }
  3184. if ( nDebugLayers & RN_DRAW_SOFTBODY_BASES )
  3185. {
  3186. for ( int nCtrl = 0; nCtrl < m_nParticleCount; ++nCtrl )
  3187. {
  3188. uint nNode = m_pFeModel->CtrlToNode( nCtrl );
  3189. if ( nNode < m_nNodeCount && !virtualCtrls.IsBitSet( nNode ) )
  3190. {
  3191. float flAxisScale = ( nNode < m_pFeModel->m_nRotLockStaticNodes ? 2.0f : 5.0f );
  3192. // this is a dynamic node
  3193. const matrix3x4a_t &node = pSim[ nCtrl ];
  3194. pDebugOverlay->AddLineOverlay( node.GetOrigin(), node.GetOrigin() + node.GetColumn( X_AXIS ) * flAxisScale * 2, 255, 180, 180, 255, false, 0 );
  3195. pDebugOverlay->AddLineOverlay( node.GetOrigin(), node.GetOrigin() + node.GetColumn( Y_AXIS ) * flAxisScale, 180, 255, 180, 255, false, 0 );
  3196. pDebugOverlay->AddLineOverlay( node.GetOrigin(), node.GetOrigin() + node.GetColumn( Z_AXIS ) * flAxisScale, 180, 180, 255, 255, false, 0 );
  3197. const matrix3x4a_t &anim = GetAnim( nCtrl );
  3198. float flAnimAxisScale = 0.5f * flAxisScale;
  3199. pDebugOverlay->AddLineOverlay( anim.GetOrigin( ), anim.GetOrigin( ) + anim.GetColumn( X_AXIS ) * flAnimAxisScale, 200, 128, 128, 255, false, 0 );
  3200. pDebugOverlay->AddLineOverlay( anim.GetOrigin( ), anim.GetOrigin( ) + anim.GetColumn( Y_AXIS ) * flAnimAxisScale, 128, 200, 128, 255, false, 0 );
  3201. pDebugOverlay->AddLineOverlay( anim.GetOrigin( ), anim.GetOrigin( ) + anim.GetColumn( Z_AXIS ) * flAnimAxisScale, 128, 128, 200, 255, false, 0 );
  3202. }
  3203. }
  3204. }
  3205. float flMaxErr = 0, flMaxVel = 0;
  3206. Vector vMaxErr = vec3_origin, vMaxVel = vec3_origin;
  3207. if ( nDebugLayers & RN_DRAW_SOFTBODY_FIELDS )
  3208. {
  3209. for ( uint nNode = 0; nNode < m_nNodeCount; ++nNode )
  3210. {
  3211. Vector p1 = pPos1[ nNode ], p0 = pPos0[ nNode ];
  3212. pDebugOverlay->AddLineOverlay( p1, p0, ( p1 - p0 ).Length() > 70.0f ? 255 : 150, 150, 150, 255, false, 0 );
  3213. uint nCtrl = m_pFeModel->NodeToCtrl( nNode );
  3214. //if ( *m_pFeModel->GetCtrlName( nCtrl ) )
  3215. if ( !virtualCtrls[ nCtrl ]
  3216. && m_pFeModel->m_pNodeIntegrator
  3217. && ( m_pFeModel->m_pNodeIntegrator[ nNode ].flAnimationForceAttraction != 0 || m_pFeModel->m_pNodeIntegrator[ nNode ].flAnimationVertexAttraction != 0 )
  3218. )
  3219. {
  3220. // only draw anim-sim lines for real nodes. Virtual nodes have no corresponding bones, so we don't care about them too much
  3221. Vector vAnim = GetAnim( nCtrl ).GetOrigin( ), vSim = pSim[ nCtrl ].GetOrigin( );
  3222. pDebugOverlay->AddLineOverlay( pPos1[ nNode ], vAnim, 120, 120, 200, 255, false, 0 );
  3223. if ( nNode >= m_pFeModel->m_nStaticNodes )
  3224. {
  3225. pDebugOverlay->AddLineOverlay( vSim, vAnim, 120, 120, 120, 255, false, 0 );
  3226. }
  3227. float flErr = ( vSim - vAnim ).Length();
  3228. if ( flErr > flMaxErr )
  3229. {
  3230. flMaxErr = flErr;
  3231. vMaxErr = vAnim;
  3232. }
  3233. if ( nNode >= m_pFeModel->m_nStaticNodes )
  3234. {
  3235. float flVel = ( pPos1[ nNode ] - pPos0[ nNode ] ).Length( ) / m_flLastTimestep;
  3236. if ( flVel > flMaxVel )
  3237. {
  3238. flMaxVel = flVel;
  3239. vMaxVel = pPos1[ nNode ];
  3240. }
  3241. }
  3242. }
  3243. }
  3244. }
  3245. if ( flMaxErr > 1 )
  3246. {
  3247. pDebugOverlay->AddTextOverlay( vMaxErr, 0, 0, "%.2f", flMaxErr );
  3248. }
  3249. if ( flMaxVel > 1 )
  3250. {
  3251. pDebugOverlay->AddTextOverlay( vMaxVel, 1, 0.0f, "v=%.1f", flMaxVel );
  3252. }
  3253. if ( nDebugLayers & RN_DRAW_SOFTBODY_INDICES )
  3254. {
  3255. for ( uint nNode = 0; nNode < m_nNodeCount; ++nNode )
  3256. {
  3257. pDebugOverlay->AddTextOverlay( pPos1[ nNode ], 0, ( nNode < m_pFeModel->m_nStaticNodes ? "*%d %s" : "%d %s" ), nNode, m_pFeModel->GetNodeName( nNode ) );
  3258. }
  3259. }
  3260. {
  3261. if ( m_bEnableSimd )
  3262. {
  3263. struct CLabel
  3264. {
  3265. CLabel() : m_flHighlight( 1 ){}
  3266. CUtlString m_Text;
  3267. Vector m_vPos0, m_vPos1;
  3268. uint m_nNode0, m_nNode1;
  3269. float m_flHighlight;
  3270. Vector GetPos()const
  3271. {
  3272. return ( m_vPos1 + m_vPos0 ) * .5f;
  3273. }
  3274. void Init( uint nNode0, uint nNode1, const Vector &vPos0, const Vector &vPos1, float flExpectedLength )
  3275. {
  3276. m_nNode1 = nNode1;
  3277. m_nNode0 = nNode0;
  3278. m_vPos0 = vPos0;
  3279. m_vPos1 = vPos1;
  3280. float flErr = ( vPos0 - vPos1 ).Length() / flExpectedLength - 1.0f;
  3281. //if ( fabsf( flErr ) > 0.1f )
  3282. {
  3283. m_Text = CFmtStr( "%+.2f*%.1f in ", flErr, flExpectedLength ).Get();
  3284. }
  3285. }
  3286. };
  3287. static CUtlVector< CLabel > labelPool;
  3288. labelPool.EnsureCapacity( m_pFeModel->m_nSimdRodCount );
  3289. labelPool.RemoveAll();
  3290. CUtlHashtable< uint32, int > labels;
  3291. for ( uint nSimdRod = 0; nSimdRod < m_pFeModel->m_nSimdRodCount; ++nSimdRod )
  3292. {
  3293. const FeSimdRodConstraint_t &simdRod = m_pFeModel->m_pSimdRods[ nSimdRod ];
  3294. for ( int nLane = 0; nLane < 4; ++nLane )
  3295. {
  3296. uint n0 = simdRod.nNode[ 0 ][ nLane ], n1 = simdRod.nNode[ 1 ][ nLane ];
  3297. Vector vPos = ( pPos1[ n0 ] + pPos1[ n1 ] ) * 0.5f;
  3298. float flHighlight = m_pEnvironment ? m_pEnvironment->GetDebugHighlightCone().Highlight( vPos ) : 1.0f;
  3299. if ( flHighlight <= 0.0f )
  3300. continue;
  3301. if ( n0 > n1 )
  3302. Swap( n0, n1 );
  3303. uint nNodeKey = n0 | ( n1 << 16 );
  3304. int nLabel = labels.Get( nNodeKey, -1 );
  3305. CLabel *pLabel ;
  3306. if ( nLabel < 0 )
  3307. {
  3308. nLabel = labelPool.AddToTail();
  3309. pLabel = &labelPool[ nLabel ];
  3310. pLabel->Init( n0, n1, pPos1[ n0 ], pPos1[ n1 ], SubFloat( simdRod.f4MaxDist, nLane ) );
  3311. pLabel->m_flHighlight = flHighlight;
  3312. labels.Insert( nNodeKey, nLabel );
  3313. }
  3314. else
  3315. {
  3316. pLabel = &labelPool[ nLabel ];
  3317. pLabel->m_Text.Append( ", " );
  3318. }
  3319. pLabel->m_Text.Append( CFmtStrN< 64 >( "%u.%u", nSimdRod, nLane ) );
  3320. }
  3321. }
  3322. for ( UtlHashHandle_t iter = labels.FirstHandle(); iter != labels.InvalidHandle(); iter = labels.NextHandle( iter ) )
  3323. {
  3324. //uint nNodeKey = labels.Key( iter );
  3325. CLabel *pLabel = &labelPool[ labels.Element( iter ) ];
  3326. pDebugOverlay->AddTextOverlay( pLabel->GetPos(), 0.0f, pLabel->m_flHighlight, "%s", pLabel->m_Text.Get() );
  3327. }
  3328. }
  3329. else
  3330. {
  3331. for ( uint nRod = 0; nRod < m_pFeModel->m_nRodCount; ++nRod )
  3332. {
  3333. const FeRodConstraint_t &rod = m_pFeModel->m_pRods[ nRod ];
  3334. pDebugOverlay->AddTextOverlay( ( pPos1[ rod.nNode[ 0 ] ] + pPos1[ rod.nNode[ 1 ] ] ) * 0.5f, 0.0f, .5f, "rod%u", nRod );
  3335. }
  3336. }
  3337. }
  3338. }
  3339. uint CSoftbody::ComputeVirtualCtrls( CVarBitVec &virtualCtrls )
  3340. {
  3341. for ( uint nOffset = 0; nOffset < m_pFeModel->m_nCtrlOsOffsets; ++nOffset )
  3342. {
  3343. virtualCtrls.Set( m_pFeModel->m_pCtrlOsOffsets[ nOffset ].nCtrlChild );
  3344. }
  3345. for ( uint nOffset = 0; nOffset < m_pFeModel->m_nCtrlOffsets; ++nOffset )
  3346. {
  3347. virtualCtrls.Set( m_pFeModel->m_pCtrlOffsets[ nOffset ].nCtrlChild );
  3348. }
  3349. return m_pFeModel->m_nCtrlOsOffsets + m_pFeModel->m_nCtrlOffsets;
  3350. }
  3351. void CSoftbody::SetDebugSelection( int nSel )
  3352. {
  3353. m_nDebugSelection = nSel;
  3354. }
  3355. int CSoftbody::CastCone( const Vector &vStart, const Vector &vDir, float flConePitch )
  3356. {
  3357. float flClosest = FLT_MAX, flPrecision = FLT_MAX;
  3358. int nClosest = 0;
  3359. float flDir = vDir.LengthSqr();
  3360. Vector vNormalizedDir = vDir / flDir;
  3361. for ( int nNode = 0; nNode < m_nNodeCount; ++nNode )
  3362. {
  3363. Vector vPos = m_pPos1[ nNode ] - vStart;
  3364. float flDist = DotProduct( vPos, vNormalizedDir );
  3365. Vector vOrthoDist = vPos - vNormalizedDir * flDist;
  3366. float flOrthoDistSqr = vOrthoDist.LengthSqr( );
  3367. float flThresholdSqr = Sqr( flConePitch * flDist );
  3368. if ( flDist > 0 && flDist < flClosest && flOrthoDistSqr < flThresholdSqr )
  3369. {
  3370. flClosest = flDist;
  3371. nClosest = nNode;
  3372. flPrecision = flOrthoDistSqr;
  3373. }
  3374. }
  3375. return nClosest;
  3376. }
  3377. void CSoftbody::SetUserData( uint nIndex, void *pData )
  3378. {
  3379. if ( nIndex < ARRAYSIZE( m_pUserData ) )
  3380. {
  3381. m_pUserData[ nIndex ] = ( uintp ) pData;
  3382. }
  3383. }
  3384. void* CSoftbody::GetUserData( uint nIndex )
  3385. {
  3386. return ( nIndex < ARRAYSIZE( m_pUserData ) ) ? ( void* )( m_pUserData[ nIndex ] ): NULL;
  3387. }
  3388. float CSoftbody::GetEnergy( PhysicsSoftbodyEnergyTypeEnum_t nEnergy )const
  3389. {
  3390. switch ( nEnergy )
  3391. {
  3392. case SOFTBODY_ENERGY_ELASTIC:
  3393. return GetElasticEnergy( );
  3394. case SOFTBODY_ENERGY_KINEMATIC:
  3395. return GetKinematicEnergy( );
  3396. case SOFTBODY_ENERGY_POTENTIAL:
  3397. return GetPotentialEnergy( );
  3398. default:
  3399. return GetElasticEnergy( ) + GetKinematicEnergy( ) + GetPotentialEnergy( );
  3400. }
  3401. }
  3402. float CSoftbody::GetElasticEnergy( )const
  3403. {
  3404. float dtInv = 1.0f / m_flLastTimestep, dtInvSqr = Sqr( dtInv );
  3405. float flConstraintScale = m_flModelScale * m_flClothScale;
  3406. float flRodEnergy = m_pFeModel->ComputeElasticEnergyRods( m_pPos1, flConstraintScale ) * dtInvSqr, flQuadEnergy = m_pFeModel->ComputeElasticEnergyQuads( m_pPos1, flConstraintScale ) * dtInvSqr;
  3407. float flSpringEnergy = m_pFeModel->ComputeElasticEnergySprings( m_pPos0, m_pPos1, m_flLastTimestep, flConstraintScale );
  3408. return flRodEnergy + flQuadEnergy + flSpringEnergy;
  3409. }
  3410. float CSoftbody::GetPotentialEnergy( )const
  3411. {
  3412. const FeNodeIntegrator_t *pNodeIntegrator = m_pFeModel->m_nDynamicNodeFlags & ( FE_FLAG_HAS_NODE_DAMPING | FE_FLAG_HAS_ANIMATION_FORCE_ATTRACTION ) ? m_pFeModel->m_pNodeIntegrator : NULL;
  3413. float flTimeStep = m_flLastTimestep;
  3414. float flPotentialEnergy = 0;
  3415. for ( uint nDynNode = m_pFeModel->m_nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  3416. {
  3417. float flDamping = 0.0f;
  3418. float flGravity = GetEffectiveGravityScale( );
  3419. if ( pNodeIntegrator )
  3420. {
  3421. const FeNodeIntegrator_t &integrator = pNodeIntegrator[ nDynNode ];
  3422. flDamping = integrator.flPointDamping * flTimeStep * m_flDampingMultiplier;
  3423. flGravity *= integrator.flGravity;
  3424. }
  3425. const VectorAligned &pos1 = m_pPos1[ nDynNode ];
  3426. flPotentialEnergy += ( pos1.z * Max( 0.0f, 1.0f - flDamping ) * flGravity ) / m_pFeModel->m_pNodeInvMasses[ nDynNode ]; // mgh=hgm
  3427. }
  3428. return flPotentialEnergy;
  3429. }
  3430. float CSoftbody::GetKinematicEnergy( )const
  3431. {
  3432. const FeNodeIntegrator_t *pNodeIntegrator = m_pFeModel->m_nDynamicNodeFlags & ( FE_FLAG_HAS_NODE_DAMPING | FE_FLAG_HAS_ANIMATION_FORCE_ATTRACTION ) ? m_pFeModel->m_pNodeIntegrator : NULL;
  3433. float flTimeStep = m_flLastTimestep;
  3434. float flKinematicEnergy = 0;
  3435. for ( uint nDynNode = m_pFeModel->m_nStaticNodes; nDynNode < m_nNodeCount; ++nDynNode )
  3436. {
  3437. float flDamping = 0.0f;
  3438. if ( pNodeIntegrator )
  3439. {
  3440. const FeNodeIntegrator_t &integrator = pNodeIntegrator[ nDynNode ];
  3441. flDamping = integrator.flPointDamping * flTimeStep * m_flDampingMultiplier;
  3442. }
  3443. const VectorAligned &pos0 = m_pPos0[ nDynNode ], &pos1 = m_pPos1[ nDynNode ];
  3444. flKinematicEnergy += ( ( pos1 - pos0 ) * Max( 0.0f, 1.0f - flDamping ) / m_flLastTimestep ).LengthSqr() * 0.5f / m_pFeModel->m_pNodeInvMasses[ nDynNode ];
  3445. }
  3446. return flKinematicEnergy;
  3447. }
  3448. Vector CSoftbody::GetCtrlVelocity( uint nCtrl ) const
  3449. {
  3450. return GetNodeVelocity( m_pFeModel->CtrlToNode( nCtrl ) );
  3451. }
  3452. //--------------------------------------------------------------------------------------------------
  3453. void CSoftbody::EnableDebugRendering( bool bEnable )
  3454. {
  3455. m_bDebugDraw = bEnable;
  3456. }
  3457. void CSoftbody::SetNodePositions( SetNodePositionsParams_t &params )
  3458. {
  3459. CHANGE_GUARD();
  3460. SetAbsOrigin( params.vAbsOrigin, true );
  3461. SetAbsAngles( params.vAbsAngles, true );
  3462. V_memcpy( GetNodePositions( params.nFrame ), params.pPos, sizeof( *params.pPos ) * Min<uint>( params.nCount, m_nNodeCount ) );
  3463. m_bSimTransformsOutdated = true;
  3464. }
  3465. void CSoftbody::SetNodePositions( const Vector *pPos, int nCount, int nFrame )
  3466. {
  3467. CHANGE_GUARD();
  3468. VectorAligned *pInternalPos = GetNodePositions( nFrame );
  3469. uint nUsefulNodeCount = Min<uint>( nCount, m_nNodeCount );
  3470. for ( uint nNode = 0; nNode < nUsefulNodeCount; ++nNode )
  3471. {
  3472. pInternalPos[ nNode ] = pPos[ nNode ];
  3473. }
  3474. m_bSimTransformsOutdated = true;
  3475. }