//========= Copyright © Valve Corporation, All rights reserved. ============// // // A port of CRnSoftbody // Note: mathlib is tentative place for this code. We will probably move it to a separate lib or dll or vphysics.dll // #ifndef MATHLIB_SOFTBODY_HDR #define MATHLIB_SOFTBODY_HDR #include "rubikon/param_types.h" #include "rubikon/debugname.h" #include "rubikon/intersection.h" // #include "rnmath.h" // #include "geometry.h" // #include "mass.h" // #include "graphedge.h" // #include "collisionfilter.h" // #include "rnserialize.h" // #include "legacyobject.h" #include "tier1/utlbuffer.h" #include "mathlib/femodel.h" #include "tier1/hierarchicalbitvec.h" #include "rubikon/serializehelpers.h" class CRnSoftbodyDesc; class CSoftbodyEnvironment; class CRnBody; class CRnJoint; struct PhysSoftbodyDesc_t; class CJob ; class CSoftbody; class CFeModel; struct FilterTransformsParams_t; struct SetNodePositionsParams_t; class CFeModelReplaceContext; class IVDebugOverlay; class IMesh; struct FilterTransformsParams_t { matrix3x4a_t *pOutputWorldTransforms; const int16 *pCtrlToBone; const uint32 *pValidTransforms; VectorAligned *pNodePos; bool flMatrixScale; // Scale to convey in matrices; 1.0 will not scale anything (appropriate for Hammer conventions). 0.0 means use the model scale (appropriate for game conventions) }; struct SetNodePositionsParams_t { SetNodePositionsParams_t() { nFrame = 1; } const VectorAligned *pPos; int nCount; Vector vAbsOrigin; QAngle vAbsAngles; int nFrame; }; class CParticleGlue { public: float m_flStickiness; float m_flWeight1; // 0: only use parent [0], >0 : blend from [0] to [1] uint16 m_nParentNode[ 2 ]; // this is actual node index; we're mostly parented to static nodes public: CParticleGlue(){} CParticleGlue( uint nParentNode, float flStickiness ) { m_flStickiness = flStickiness; m_flWeight1 = 0; m_nParentNode[ 0 ] = m_nParentNode[ 1 ] = nParentNode; } }; class ALIGN16 CSoftbody { public: CSoftbody( void ); CSoftbody( CSoftbodyEnvironment *pWorld, const CFeModel *pFeModel ); ~CSoftbody(); void Shutdown( ); void Init( int numModelBones = 0); void Init( CSoftbodyEnvironment *pWorld, const CFeModel *pFeModel, int numModelBones ); void InitFeModel( int numModelBones = 0 ); void InitDefaults( ); void Integrate( float flTimeStep ); void IntegrateWind( VectorAligned *pPos, float flTimeStep ); void RawSimulate( int nIterations, float flTimeStep ); void ValidatingSimulate( int nIterations, float flTimeStep ); void AddAnimationAttraction( float flTimeStep ); void Predict( float flTimeStep ); void Post( ); void Collide(); void CollideWithWorldInternal(); void CollideWithRigidsInternal(); // void CollideTaperedCapsule( uint16 nCollisionMask, const VectorAligned &vCenter0, float flRadius0, const VectorAligned &vCenter1, float flRadius1, const Vector &vAxis, float flDist ); // void CollideTaperedCapsule( uint16 nCollisionMask, const VectorAligned &vCenter0, float flRadius0, const VectorAligned &vCenter1, float flRadius1 ); // @begin_publish uint Step( int nIterations, float flTimeStep ); uint Step( float flTimeStep ); uint GetStateHash()const; void TouchAnimatedTransforms(); void SetAnimatedTransform( int nParticle, const matrix3x4a_t &transform ); void SetAnimatedTransforms( const matrix3x4a_t *pSimulationWorldTransforms ); void SetAnimatedTransformsNoScale( const matrix3x4a_t *pSimulationWorldTransforms ); matrix3x4a_t* GetParticleTransforms( const VectorAligned *pInputNodePos = NULL, uint nFlags = 0 ); const VectorAligned* GetNodePositions( int nFrame = 1 ) const { return nFrame ? m_pPos1 : m_pPos0; } VectorAligned* GetNodePositions( int nFrame = 1 ) { return nFrame ? m_pPos1 : m_pPos0; } void SetNodePositions( const Vector *pPos, int nCount, int nFrame = 1 ); void SetNodePositions( SetNodePositionsParams_t ¶ms ); uint GetNodeCount() const { return m_nNodeCount; } uint GetCtrlCount() const { return m_nParticleCount; } Vector GetNodeVelocity( uint nNode ) const { return ( m_pPos1[ nNode ] - m_pPos0[ nNode ] ) / m_flLastTimestep; } Vector GetCtrlVelocity( uint nCtrl ) const; matrix3x4a_t* GetAnimatedTransforms( ) { return m_pParticles; } const matrix3x4a_t* GetAnimatedTransforms() const { return m_pParticles; } matrix3x4a_t* GetSimulatedTransforms() { return m_pParticles + m_nParticleCount; } const matrix3x4a_t* GetSimulatedTransforms() const { return m_pParticles + m_nParticleCount; } const CFeModel *GetFeModel()const { return m_pFeModel; } CFeModel *GetFeModel( ){ return m_pFeModel; } CSoftbodyEnvironment *GetEnvironment( ) const { return m_pEnvironment; } int GetParticleCount( ) const { return m_nParticleCount; } void SetDebugNameV( const char* pNameFormat, va_list args ) { m_DebugName.SetV( pNameFormat, args );} const char *GetDebugName() const { return m_DebugName.Get(); } void AppendDebugInfo( CUtlString &line ); void Draw( const RnDebugDrawOptions_t &options, IVDebugOverlay* pDebugOverlay ); void Draw( const RnDebugDrawOptions_t &options, IMesh *pDynamicMesh ); void SetPose() { SetPose( m_pFeModel->m_pInitPose ); } void SetPose( const CTransform *pPose ); void SetPose( const CTransform &tm ) { SetPose( tm, m_pFeModel->m_pInitPose ); } void SetPose( const CTransform &tm, const CTransform *pPose ); void SetPoseFromBones( const int16 *pCtrlToBone, const matrix3x4a_t *pBones, float flScale = 1.0f ); void SetCtrl( int nParticle, const CTransform &tm ); void SetDebugSelection( int nSelection ); void SetSimFlags( uint nNewSimFlags ); void AddSimFlags( uint nAddSimFlags ) { SetSimFlags( m_nSimFlags | nAddSimFlags ); } void ClearSimFlags( uint nClearSimFlags ) { SetSimFlags( m_nSimFlags & ~nClearSimFlags ); } void SetAnimSpace( PhysicsSoftbodyAnimSpace_t nAnimSpace ) { m_nAnimSpace = nAnimSpace; } PhysicsSoftbodyAnimSpace_t GetAnimSpace()const { return m_nAnimSpace; } uint GetSimFlags()const { return m_nSimFlags; } int CastCone( const Vector &vStart, const Vector &vDir, float flConePitch ); uint GetContactCount( )const { return 0; } void SetOverPredict( float flOverPredict ) { m_flOverPredict = flOverPredict; } float GetOverPredict( ) const { return m_flOverPredict; } void ResetVelocities( ); void SetThreadStretch( float flBendUnderRelax ) { m_flThreadStretch = flBendUnderRelax; } float GetThreadStretch( )const { return m_flThreadStretch; } void SetSurfaceStretch( float flStretchUnderRelax ) { m_flSurfaceStretch = flStretchUnderRelax; } float GetSurfaceStretch( )const { return m_flSurfaceStretch; } void SetStepUnderRelax( float flStepUnderRelax ) { m_flStepUnderRelax = flStepUnderRelax; } float GetStepUnderRelax( void ) const { return m_flStepUnderRelax; } AABB_t BuildBounds( )const; void FilterTransforms( const FilterTransformsParams_t ¶ms ); void FilterTransforms( matrix3x4a_t *pModelBones ); void SetGravityScale( float flScale ) { m_flGravityScale = flScale; } void EnableGravity( bool bEnableGravity ) { m_bGravityDisabled = !bEnableGravity; } void EnableGravity( ) { m_bGravityDisabled = false; } void DisableGravity( ) { m_bGravityDisabled = true; } bool IsGravityEnabled( ) const { return !m_bGravityDisabled; } bool IsGravityDisabled( ) const { return m_bGravityDisabled; } bool IsFtlPassEnabled( ) const { return m_bEnableFtlPass; } void EnableFtlPass( bool bEnable ) { m_bEnableFtlPass = bEnable; } float GetGravityScale( void ) const{ return m_flGravityScale; } Vector GetEffectiveGravity( void ) const; float GetEffectiveGravityScale( void ) const; void EnableAnimationAttraction( bool bEnable ) { m_bEnableAnimationAttraction = bEnable; } bool IsAnimationAttractionEnabled( )const { return m_bEnableAnimationAttraction; } void EnableFollowNode( bool bEnable ) { m_bEnableFollowNodes = bEnable; } bool IsFollowNodeEnabled( ) const { return m_bEnableFollowNodes; } void EnableSprings( bool bEnable ){ m_bEnableSprings = bEnable; } bool AreSpringsEnabled( )const { return m_bEnableSprings; } void EnableInclusiveCollisionSpheres( bool bEnable ) { m_bEnableInclusiveCollisionSpheres = bEnable; } bool AreInclusiveCollisionSpheresEnabled( ) const { return m_bEnableInclusiveCollisionSpheres; } void EnableExclusiveCollisionSpheres( bool bEnable ) { m_bEnableExclusiveCollisionSpheres = bEnable; } bool AreExclusiveCollisionSpheresEnabled( ) const { return m_bEnableExclusiveCollisionSpheres; } void EnableCollisionPlanes( bool bEnable ) { m_bEnableCollisionPlanes = bEnable; } bool AreCollisionPlanesEnabled( ) const { return m_bEnableCollisionPlanes; } void EnableGroundCollision( bool bEnable ) { m_bEnableGroundCollision = bEnable; } bool IsGroundCollisionEnabled( ) const { return m_bEnableGroundCollision; } void EnableGroundTrace( bool bEnable ) { m_bEnableGroundTrace = bEnable; } bool IsGroundTraceEnabled( ) const { return m_bEnableGroundTrace; } float GetTimeStep( void )const { return m_flLastTimestep; } void SetModelScale( float flModelScale ) { m_flModelScale = flModelScale; } float GetModelScale( )const { return m_flModelScale; } void SetVelocityDamping( float flDamping ) { m_flVelocityDamping = flDamping; } // 1.0f - full damping; 0.0 - no damping (default) float GetVelocityDamping( )const { return m_flVelocityDamping; } void SetUserData( uint nIndex, void *pData ); void* GetUserData( uint nIndex ); //void SetOrigin( const Vector &vAbsOrigin ); void InitializeTransforms( const int16 *pCtrlToBone, const matrix3x4a_t *pSimulationWorldTransforms ); void SetAbsAngles( const QAngle &vNewAngles, bool bTeleport ); const QAngle GetAbsAngles() const { return m_vSimAngles; } void SetAbsOrigin( const Vector &vNewOrigin, bool bTeleport ); const Vector GetAbsOrigin()const { return m_vSimOrigin; } float GetEnergy( PhysicsSoftbodyEnergyTypeEnum_t nEnergy )const; float GetElasticEnergy( )const; float GetPotentialEnergy( )const; float GetKinematicEnergy( )const; void SetDampingMultiplier( float flMul ) { m_flDampingMultiplier = flMul; } float GetDampingMultiplier( )const { return m_flDampingMultiplier; } void SetGroundZ( float flGroundZ ) { m_vGround.Init( m_vSimOrigin.x, m_vSimOrigin.y, flGroundZ ); } float GetGroundZ( )const { return m_vGround.z; } bool IsDormant( )const; void GoDormant( ); bool AdvanceSleepCounter(); void GoWakeup( ); bool IsActive()const; bool BeforeFilterTransforms( ); void SetDebugDrawTreeBeginLevel( int nLevel ) { m_nDebugDrawTreeBeginLevel = nLevel; } int GetDebugDrawTreeBeginLevel() { return m_nDebugDrawTreeBeginLevel; } void SetDebugDrawTreeEndLevel( int nLevel ) { m_nDebugDrawTreeEndLevel = nLevel; } int GetDebugDrawTreeEndLevel() { return m_nDebugDrawTreeEndLevel; } void SetDebugDrawTreeFlags( uint nFlags ) { m_nDebugDrawTreeFlags = nFlags; } uint GetDebugDrawTreeFlags(){ return m_nDebugDrawTreeFlags; } void EnableDebugDraw( bool bEnable ){ m_bDebugDraw = bEnable; } void EnableDebugRendering( bool bEnable ); float GetVelAirDrag() const { return m_flVelAirDrag; } void SetVelAirDrag( float flVelAirDrag ){ m_flVelAirDrag = flVelAirDrag; } float GetExpAirDrag() const { return m_flExpAirDrag; } void SetExpAirDrag( float flExpAirDrag ){ m_flExpAirDrag = flExpAirDrag; } float GetVelQuadAirDrag() const { return m_flVelQuadAirDrag; } void SetVelQuadAirDrag( float flVelQuadAirDrag ){ m_flVelQuadAirDrag = flVelQuadAirDrag; } float GetExpQuadAirDrag() const { return m_flExpQuadAirDrag; } void SetExpQuadAirDrag( float flExpQuadAirDrag ){ m_flExpQuadAirDrag = flExpQuadAirDrag; } float GetVelRodAirDrag() const { return m_flVelRodAirDrag; } void SetVelRodAirDrag( float flVelRodAirDrag ){ m_flVelRodAirDrag = flVelRodAirDrag; } float GetExpRodAirDrag() const { return m_flExpRodAirDrag; } void SetExpRodAirDrag( float flExpRodAirDrag ){ m_flExpRodAirDrag = flExpRodAirDrag; } float GetQuadVelocitySmoothRate()const { return m_flQuadVelocitySmoothRate; } void SetQuadVelocitySmoothRate( float flRate ){ m_flQuadVelocitySmoothRate = flRate; } float GetRodVelocitySmoothRate()const { return m_flRodVelocitySmoothRate; } void SetRodVelocitySmoothRate( float flRate ){ m_flRodVelocitySmoothRate = flRate; } uint16 GetQuadVelocitySmoothIterations()const { return m_nQuadVelocitySmoothIterations; } void SetQuadVelocitySmoothIterations( uint16 nIterations ){ m_nQuadVelocitySmoothIterations = nIterations; } uint16 GetRodVelocitySmoothIterations()const { return m_nRodVelocitySmoothIterations; } void SetRodVelocitySmoothIterations( uint16 nIterations ){ m_nRodVelocitySmoothIterations = nIterations; } const RnCollisionAttr_t &GetCollisionAttributes() const { return m_CollisionAttributes; } void SetCollisionAttributes( const RnCollisionAttr_t &attr ); int GetIndexInWorld() const { return m_nIndexInWorld; } void ReplaceFeModel( CFeModelReplaceContext &context ); matrix3x4_t GetDifferenceTransform( const Vector &vAltOrigin, const QAngle &vAltAngles ); void ComputeInterpolatedNodePositions( float flFactor, VectorAligned *pPosOut ); void SetInstanceSettings( void *pSettings ); void SetFrozen( bool bFrozen ) { m_bFrozen = true; } bool IsFrozen()const { return m_bFrozen; } void SetVolumetricSolveAmount( float flVolumetricSolveAmount ) { m_flVolumetricSolveAmount = flVolumetricSolveAmount; } float GetVolumetricSolveAmount()const { return m_flVolumetricSolveAmount; } void ParseParticleState( CUtlBuffer &buf, float flTimeStep ); CUtlString PrintParticleState( )const; int16 *GetModelBoneToCtrl() { return m_pModelBoneToCtrl; } void BindModelBoneToCtrl( int nModelBone, int nCtrl ); //bool SetupCtrl( uint nCtrl, matrix3x4a_t &writeBone ); // @end_publish void DebugDump( ); void UpdateCtrlOffsets( bool bOverridePose ); uint ComputeVirtualCtrls( CVarBitVec &virtualNodes ); matrix3x4a_t &GetAnim( int i ) { return GetAnimatedTransforms()[ i ]; } const matrix3x4a_t &GetAnim( int i ) const { return GetAnimatedTransforms()[ i ]; } matrix3x4a_t &GetSim( int i ) { return GetSimulatedTransforms()[ i ]; } const matrix3x4a_t &GetSim( int i ) const { return GetSimulatedTransforms()[ i ]; } uint ShouldUsePreconditioner()const { return m_nSimFlags & ( SOFTBODY_SIM_DIAGONAL_PRECONDITIONER | SOFTBODY_SIM_TRIDIAGONAL_PRECONDITIONER | SOFTBODY_SIM_RELAXATION_PRECONDITIONER ); } class CWorldIndexPred { public: static int GetIndex( const CSoftbody *pBody ) { return pBody->m_nIndexInWorld; } static void SetIndex( CSoftbody *pBody, int nIndex ) { pBody->m_nIndexInWorld = nIndex; } }; friend class CWorldIndexPred; uint GetParticleArrayCount( ) const { return m_nParticleCount * 2; } void Validate(); void DebugPreStep( float flTimeStep ); void DebugPostStep(); class CConstraintIterator { public: CConstraintIterator( CSoftbody *pSoftbody ); ~CConstraintIterator( ); void Iterate( int nIterations ); protected: CSoftbody *m_pSoftbody; CSoftbodyEnvironment *m_pEnvironment; CUtlVectorFixedGrowable< VectorAligned, 128 > m_PosBeforeCorrect; // the biggest hero in source1 is Medusa, with 104 nodes (52 useful, 52 virtual) }; friend class CConstraintIterator; void GlueNode( uint nDynNode, uint nParentNode, float flStickiness ); void GlueNode( uint nDynNode, uint nParentNode0, uint nParentNode1, float flStickiness, float flWeight1 ); void GlueNode( uint nDynNode, const CParticleGlue &glue, float flReplacementStickiness ); void DebugTraceMove( const char *pMsg ); protected: void Integrate_S1( float flTimeStep ); void ResolveStretch_S1( float flTimeStep ); void ResolveAnimAttraction_S1( float flTimeStep ); protected: friend class CRnSoftbodyChangeGuard; CRnDebugName m_DebugName; CSoftbodyEnvironment *m_pEnvironment; RnCollisionAttr_t m_CollisionAttributes; CFeModel *m_pFeModel; // Finite Element Model float m_flThreadStretch; // positive: underrelax; negative: overrelax float m_flSurfaceStretch; float m_flStepUnderRelax; float m_flOverPredict; // 0 : normal integration; positive: overpredict, correct, step back float m_flGravityScale; uint m_nNodeCount; // actual simulated node count (includes static and dynamic nodes: even though static nodes are not simulated, we need their coordinates to simulate the other nodes connected to them) uint m_nParticleCount; // Ctrl count float m_flModelScale; float m_flVelocityDamping; float m_flDampingMultiplier; float m_flClothScale; Vector m_vGround; Vector m_vSimOrigin; QAngle m_vSimAngles; matrix3x4a_t *m_pParticles SERIALIZE_ARRAY_SIZE( GetParticleArrayCount() ); VectorAligned *m_pPos0 SERIALIZE_ARRAY_SIZE( m_nNodeCount ); VectorAligned *m_pPos1 SERIALIZE_ARRAY_SIZE( m_nNodeCount ); FeAabb_t *m_pAabb SERIALIZE_ARRAY_SIZE( m_pFeModel->GetDynamicNodeCount() - 1 ); int16 *m_pModelBoneToCtrl; int16 *m_pCtrlToModelBone; CHierarchicalBitVector m_StickyBuffer; // sticky particles CParticleGlue *m_pParticleGlue SERIALIZE_ARRAY_SIZE( m_pFeModel->GetDynamicNodeCount() ); enum StateEnum_t { STATE_ACTIVE, // actively simulating, taking in transforms, filtering out transforms STATE_DORMANT,// not simulating, not taking in transforms, not filtering anything STATE_WAKEUP, // StateCounter == 0 : not simulating, readying to take in transforms, not filtering anything, not copying transforms // StateCounter > 0 : not simulating, taking in transforms, not filtering anything, copying transforms STEPS_INVISIBLE_BEFORE_DORMANT = 12, FRAMES_INVISIBLE_BEFORE_DORMANT = 3 }; uint32 m_nSimFlags; uint32 m_nStepsSimulated; int8 m_nDebugDrawTreeEndLevel; int8 m_nDebugDrawTreeBeginLevel; uint8 m_nDebugDrawTreeFlags; // STATE_ACTIVE: how many steps we've taken without having FilterTransforms called once // STATE_DORMANT: doesn't matter // STATE_WAKEUP: how many times we've set animated transforms (need 2 to switch to ACTIVE state) uint8 m_nStateCounter; float m_flLastTimestep; float m_flVelAirDrag; float m_flExpAirDrag; float m_flVelQuadAirDrag; float m_flExpQuadAirDrag; float m_flVelRodAirDrag; float m_flExpRodAirDrag; float m_flQuadVelocitySmoothRate; float m_flRodVelocitySmoothRate; float m_flVolumetricSolveAmount; uint16 m_nQuadVelocitySmoothIterations; uint16 m_nRodVelocitySmoothIterations; int m_nIndexInWorld; int m_nDebugSelection; Vector m_vRopeOffset; // a horrible S1 cloth rope hack I'm faithfully replicating so that dota cloth looks exactly the same uintp m_pUserData[ 2 ] ; StateEnum_t m_nActivityState; //uint m_nDebugNode; uint8 m_nEnableWorldShapeCollision : 4; PhysicsSoftbodyAnimSpace_t m_nAnimSpace : 2; bool m_bAnimTransformChanged : 1; // True means that the animation transforms, that the game sets from outside, have changed and need to be propagated into the simulation bool m_bSimTransformsOutdated : 1; // True means that the sim transforms, that the game queries from outside, are out of date and need to be copied from the simulation (and their rotations computed) bool m_bGravityDisabled : 1; bool m_bEnableAnimationAttraction : 1; bool m_bEnableFollowNodes : 1; bool m_bEnableSprings : 1; bool m_bEnableInclusiveCollisionSpheres : 1; bool m_bEnableExclusiveCollisionSpheres : 1; bool m_bEnableCollisionPlanes : 1; bool m_bEnableGroundCollision : 1; bool m_bEnableGroundTrace : 1; bool m_bEnableFtlPass : 1; bool m_bFrozen : 1; bool m_bDebugDraw : 1; bool m_bEnableSimd : 1; /* bool m_bTeleportOnNextSetAbsOrigin : 1; bool m_bTeleportOnNextSetAbsAngles : 1; */ friend class CTaperedCapsuleColliderFunctor; friend class CGluePredictFunctor; friend class CSphereColliderFunctor; } ALIGN16_POST; inline void CSoftbody::GlueNode( uint nDynNode, uint nParentNode, float flStickiness ) { Assert( nDynNode < m_pFeModel->GetDynamicNodeCount() ); m_StickyBuffer.Set( nDynNode ); CParticleGlue &glue = m_pParticleGlue[ nDynNode ]; glue.m_flStickiness = flStickiness; glue.m_flWeight1 = 0; glue.m_nParentNode[ 0 ] = nParentNode; glue.m_nParentNode[ 1 ] = nParentNode; } inline void CSoftbody::GlueNode( uint nDynNode, uint nParentNode0, uint nParentNode1, float flStickiness, float flWeight1 ) { Assert( nDynNode < m_pFeModel->GetDynamicNodeCount() ); m_StickyBuffer.Set( nDynNode ); CParticleGlue &glue = m_pParticleGlue[ nDynNode ]; glue.m_flStickiness = flStickiness; glue.m_flWeight1 = flWeight1; glue.m_nParentNode[ 0 ] = nParentNode0; glue.m_nParentNode[ 1 ] = nParentNode1; } inline void CSoftbody::GlueNode( uint nDynNode, const CParticleGlue &glueBase, float flReplacementStickiness ) { m_StickyBuffer.Set( nDynNode ); CParticleGlue &glueNode = m_pParticleGlue[ nDynNode ]; glueNode.m_flStickiness = flReplacementStickiness; glueNode.m_flWeight1 = glueBase.m_flWeight1; // 0: only use parent [0], >0 : blend from [0] to [1] glueNode.m_nParentNode[ 0 ] = glueBase.m_nParentNode[ 0 ]; glueNode.m_nParentNode[ 1 ] = glueBase.m_nParentNode[ 1 ]; } class CSphereColliderFunctor { public: VectorAligned m_Sphere; VectorAligned *m_pDynPos1; const float *m_pNodeCollisionRadii; CSoftbody *m_pSoftbody; float m_flStickiness; uint16 m_nParentNode; // needed for gluing particle to this node public: CSphereColliderFunctor(){} CSphereColliderFunctor( CSoftbody *pSoftbody, const Vector &vSphereCenter, float flSphereRadius, float flStickiness, uint16 nParentNode ) { m_flStickiness = flStickiness; m_nParentNode = nParentNode; m_pSoftbody = pSoftbody; m_Sphere = vSphereCenter; m_Sphere.w = flSphereRadius; const CFeModel *pFeModel = pSoftbody->GetFeModel(); m_pDynPos1 = pSoftbody->m_pPos1 + pFeModel->m_nStaticNodes; m_pNodeCollisionRadii = pFeModel->m_pNodeCollisionRadii; } void Collide( uint16 nCollisionMask ); }; class CTaperedCapsuleColliderFunctor { public: VectorAligned m_vSphereCenter0; VectorAligned m_vSphereCenter1; VectorAligned *m_pDynPos1; const float *m_pNodeCollisionRadii; CSoftbody *m_pSoftbody; float m_flStickiness; uint16 m_nParentNode[ 2 ]; float m_flSlope; Vector m_vAxisX; float m_flDist; public: CTaperedCapsuleColliderFunctor(){} CTaperedCapsuleColliderFunctor( CSoftbody *pSoftbody, const Vector &vSphereCenter0, float flSphereRadius0, const Vector &vSphereCenter1, float flSphereRadius1, float flStickiness, uint16 nParentNodes0, uint16 nParentNodes1 ) { m_pSoftbody = pSoftbody; m_vSphereCenter0 = vSphereCenter0; m_vSphereCenter0.w = flSphereRadius0; m_vSphereCenter1 = vSphereCenter1; m_vSphereCenter1.w = flSphereRadius1; m_flStickiness = flStickiness; m_nParentNode[ 0 ] = nParentNodes0; m_nParentNode[ 1 ] = nParentNodes1; const CFeModel *pFeModel = pSoftbody->GetFeModel(); m_pDynPos1 = pSoftbody->m_pPos1 + pFeModel->m_nStaticNodes; m_pNodeCollisionRadii = pFeModel->m_pNodeCollisionRadii; m_vAxisX = ( vSphereCenter1 - vSphereCenter0 ) ; m_flDist = m_vAxisX.Length(); m_vAxisX /= m_flDist; m_flSlope = ( flSphereRadius1 - flSphereRadius0 ) / m_flDist; } void Collide( uint16 nCollisionMask ); }; #endif