diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/client/ragdoll.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/client/ragdoll.cpp')
| -rw-r--r-- | mp/src/game/client/ragdoll.cpp | 1734 |
1 files changed, 867 insertions, 867 deletions
diff --git a/mp/src/game/client/ragdoll.cpp b/mp/src/game/client/ragdoll.cpp index 1bf83ce3..3457b054 100644 --- a/mp/src/game/client/ragdoll.cpp +++ b/mp/src/game/client/ragdoll.cpp @@ -1,867 +1,867 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//===========================================================================//
-
-#include "cbase.h"
-#include "mathlib/vmatrix.h"
-#include "ragdoll_shared.h"
-#include "bone_setup.h"
-#include "materialsystem/imesh.h"
-#include "engine/ivmodelinfo.h"
-#include "iviewrender.h"
-#include "tier0/vprof.h"
-#include "view.h"
-#include "physics_saverestore.h"
-#include "vphysics/constraints.h"
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#ifdef _DEBUG
-extern ConVar r_FadeProps;
-#endif
-
-CRagdoll::CRagdoll()
-{
- m_ragdoll.listCount = 0;
- m_vecLastOrigin.Init();
- m_flLastOriginChangeTime = - 1.0f;
-
- m_lastUpdate = -FLT_MAX;
-}
-
-#define DEFINE_RAGDOLL_ELEMENT( i ) \
- DEFINE_FIELD( m_ragdoll.list[i].originParentSpace, FIELD_VECTOR ), \
- DEFINE_PHYSPTR( m_ragdoll.list[i].pObject ), \
- DEFINE_PHYSPTR( m_ragdoll.list[i].pConstraint ), \
- DEFINE_FIELD( m_ragdoll.list[i].parentIndex, FIELD_INTEGER )
-
-BEGIN_SIMPLE_DATADESC( CRagdoll )
-
- DEFINE_AUTO_ARRAY( m_ragdoll.boneIndex, FIELD_INTEGER ),
- DEFINE_FIELD( m_ragdoll.listCount, FIELD_INTEGER ),
- DEFINE_FIELD( m_ragdoll.allowStretch, FIELD_BOOLEAN ),
- DEFINE_PHYSPTR( m_ragdoll.pGroup ),
-
- DEFINE_RAGDOLL_ELEMENT( 0 ),
- DEFINE_RAGDOLL_ELEMENT( 1 ),
- DEFINE_RAGDOLL_ELEMENT( 2 ),
- DEFINE_RAGDOLL_ELEMENT( 3 ),
- DEFINE_RAGDOLL_ELEMENT( 4 ),
- DEFINE_RAGDOLL_ELEMENT( 5 ),
- DEFINE_RAGDOLL_ELEMENT( 6 ),
- DEFINE_RAGDOLL_ELEMENT( 7 ),
- DEFINE_RAGDOLL_ELEMENT( 8 ),
- DEFINE_RAGDOLL_ELEMENT( 9 ),
- DEFINE_RAGDOLL_ELEMENT( 10 ),
- DEFINE_RAGDOLL_ELEMENT( 11 ),
- DEFINE_RAGDOLL_ELEMENT( 12 ),
- DEFINE_RAGDOLL_ELEMENT( 13 ),
- DEFINE_RAGDOLL_ELEMENT( 14 ),
- DEFINE_RAGDOLL_ELEMENT( 15 ),
- DEFINE_RAGDOLL_ELEMENT( 16 ),
- DEFINE_RAGDOLL_ELEMENT( 17 ),
- DEFINE_RAGDOLL_ELEMENT( 18 ),
- DEFINE_RAGDOLL_ELEMENT( 19 ),
- DEFINE_RAGDOLL_ELEMENT( 20 ),
- DEFINE_RAGDOLL_ELEMENT( 21 ),
- DEFINE_RAGDOLL_ELEMENT( 22 ),
- DEFINE_RAGDOLL_ELEMENT( 23 ),
-
-END_DATADESC()
-
-IPhysicsObject *CRagdoll::GetElement( int elementNum )
-{
- return m_ragdoll.list[elementNum].pObject;
-}
-
-void CRagdoll::BuildRagdollBounds( C_BaseEntity *ent )
-{
- Vector mins, maxs, size;
- modelinfo->GetModelBounds( ent->GetModel(), mins, maxs );
- size = (maxs - mins) * 0.5;
- m_radius = size.Length();
-
- m_mins.Init(-m_radius,-m_radius,-m_radius);
- m_maxs.Init(m_radius,m_radius,m_radius);
-}
-
-void CRagdoll::Init(
- C_BaseEntity *ent,
- CStudioHdr *pstudiohdr,
- const Vector &forceVector,
- int forceBone,
- const matrix3x4_t *pDeltaBones0,
- const matrix3x4_t *pDeltaBones1,
- const matrix3x4_t *pCurrentBonePosition,
- float dt,
- bool bFixedConstraints )
-{
- ragdollparams_t params;
- params.pGameData = static_cast<void *>( ent );
- params.modelIndex = ent->GetModelIndex();
- params.pCollide = modelinfo->GetVCollide( params.modelIndex );
- params.pStudioHdr = pstudiohdr;
- params.forceVector = forceVector;
- params.forceBoneIndex = forceBone;
- params.forcePosition.Init();
- params.pCurrentBones = pCurrentBonePosition;
- params.jointFrictionScale = 1.0;
- params.allowStretch = false;
- params.fixedConstraints = bFixedConstraints;
- RagdollCreate( m_ragdoll, params, physenv );
- ent->VPhysicsSetObject( NULL );
- ent->VPhysicsSetObject( m_ragdoll.list[0].pObject );
- // Mark the ragdoll as debris.
- ent->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
-
- RagdollApplyAnimationAsVelocity( m_ragdoll, pDeltaBones0, pDeltaBones1, dt );
- RagdollActivate( m_ragdoll, params.pCollide, ent->GetModelIndex() );
-
- // It's moving now...
- m_flLastOriginChangeTime = gpGlobals->curtime;
-
- // So traces hit it.
- ent->AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
-
- if ( !m_ragdoll.listCount )
- return;
-
- BuildRagdollBounds( ent );
-
- for ( int i = 0; i < m_ragdoll.listCount; i++ )
- {
- g_pPhysSaveRestoreManager->AssociateModel( m_ragdoll.list[i].pObject, ent->GetModelIndex() );
- }
-
-#if RAGDOLL_VISUALIZE
- memcpy( m_savedBone1, &pDeltaBones0[0], sizeof(matrix3x4_t) * pstudiohdr->numbones() );
- memcpy( m_savedBone2, &pDeltaBones1[0], sizeof(matrix3x4_t) * pstudiohdr->numbones() );
- memcpy( m_savedBone3, &pCurrentBonePosition[0], sizeof(matrix3x4_t) * pstudiohdr->numbones() );
-#endif
-}
-
-CRagdoll::~CRagdoll( void )
-{
- for ( int i = 0; i < m_ragdoll.listCount; i++ )
- {
- IPhysicsObject *pObject = m_ragdoll.list[i].pObject;
- if ( pObject )
- {
- g_pPhysSaveRestoreManager->ForgetModel( m_ragdoll.list[i].pObject );
- // Disable collision on all ragdoll parts before calling RagdollDestroy
- // (which might cause touch callbacks on the ragdoll otherwise, which is
- // very bad for a half deleted ragdoll).
- pObject->EnableCollisions( false );
- }
- }
-
- RagdollDestroy( m_ragdoll );
-}
-
-
-void CRagdoll::RagdollBone( C_BaseEntity *ent, mstudiobone_t *pbones, int boneCount, bool *boneSimulated, CBoneAccessor &pBoneToWorld )
-{
- for ( int i = 0; i < m_ragdoll.listCount; i++ )
- {
- if ( RagdollGetBoneMatrix( m_ragdoll, pBoneToWorld, i ) )
- {
- boneSimulated[m_ragdoll.boneIndex[i]] = true;
- }
- }
-}
-
-const Vector& CRagdoll::GetRagdollOrigin( )
-{
- m_ragdoll.list[0].pObject->GetPosition( &m_origin, 0 );
- return m_origin;
-}
-
-void CRagdoll::GetRagdollBounds( Vector &theMins, Vector &theMaxs )
-{
- theMins = m_mins;
- theMaxs = m_maxs;
-}
-
-void CRagdoll::VPhysicsUpdate( IPhysicsObject *pPhysics )
-{
- if ( m_lastUpdate == gpGlobals->curtime )
- return;
- m_lastUpdate = gpGlobals->curtime;
- m_allAsleep = RagdollIsAsleep( m_ragdoll );
- if ( m_allAsleep )
- {
- // NOTE: This is the bbox of the ragdoll's physics
- // It's not always correct to use for culling, but it sure beats
- // using the radius box!
- Vector origin = GetRagdollOrigin();
- RagdollComputeExactBbox( m_ragdoll, origin, m_mins, m_maxs );
- m_mins -= origin;
- m_maxs -= origin;
- }
- else
- {
- m_mins.Init(-m_radius,-m_radius,-m_radius);
- m_maxs.Init(m_radius,m_radius,m_radius);
-
- if ( m_ragdoll.pGroup->IsInErrorState() )
- {
- C_BaseEntity *pEntity = static_cast<C_BaseEntity *>(m_ragdoll.list[0].pObject->GetGameData());
- RagdollSolveSeparation( m_ragdoll, pEntity );
- }
- }
-
- // See if we should go to sleep...
- CheckSettleStationaryRagdoll();
-}
-
-//=============================================================================
-// HPE_BEGIN:
-// [menglish] Transforms a vector from the given bone's space to world space
-//=============================================================================
-
-bool CRagdoll::TransformVectorToWorld(int iBoneIndex, const Vector *vPosition, Vector *vOut)
-{
- int listIndex = -1;
- if( iBoneIndex >= 0 && iBoneIndex < m_ragdoll.listCount)
- {
- for ( int i = 0; i < m_ragdoll.listCount; ++i )
- {
- if(m_ragdoll.boneIndex[i] == iBoneIndex)
- listIndex = i;
- }
- if(listIndex != -1)
- {
- m_ragdoll.list[listIndex].pObject->LocalToWorld(vOut, *vPosition);
- return true;
- }
- }
- return false;
-}
-
-//=============================================================================
-// HPE_END
-//=============================================================================
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : -
-//-----------------------------------------------------------------------------
-void CRagdoll::PhysForceRagdollToSleep()
-{
- for ( int i = 0; i < m_ragdoll.listCount; i++ )
- {
- if ( m_ragdoll.list[i].pObject )
- {
- PhysForceClearVelocity( m_ragdoll.list[i].pObject );
- m_ragdoll.list[i].pObject->Sleep();
- }
- }
-}
-
-#define RAGDOLL_SLEEP_TOLERANCE 1.0f
-static ConVar ragdoll_sleepaftertime( "ragdoll_sleepaftertime", "5.0f", 0, "After this many seconds of being basically stationary, the ragdoll will go to sleep." );
-
-void CRagdoll::CheckSettleStationaryRagdoll()
-{
- Vector delta = GetRagdollOrigin() - m_vecLastOrigin;
- m_vecLastOrigin = GetRagdollOrigin();
- for ( int i = 0; i < 3; ++i )
- {
- // It's still moving...
- if ( fabs( delta[ i ] ) > RAGDOLL_SLEEP_TOLERANCE )
- {
- m_flLastOriginChangeTime = gpGlobals->curtime;
- // Msg( "%d [%p] Still moving\n", gpGlobals->tickcount, this );
- return;
- }
- }
-
- // It's totally asleep, don't worry about forcing it to settle
- if ( m_allAsleep )
- return;
-
- // Msg( "%d [%p] Settling\n", gpGlobals->tickcount, this );
-
- // It has stopped moving, see if it
- float dt = gpGlobals->curtime - m_flLastOriginChangeTime;
- if ( dt < ragdoll_sleepaftertime.GetFloat() )
- return;
-
- // Msg( "%d [%p] FORCE SLEEP\n",gpGlobals->tickcount, this );
-
- // Force it to go to sleep
- PhysForceRagdollToSleep();
-}
-
-void CRagdoll::ResetRagdollSleepAfterTime( void )
-{
- m_flLastOriginChangeTime = gpGlobals->curtime;
-}
-
-void CRagdoll::DrawWireframe()
-{
- IMaterial *pWireframe = materials->FindMaterial("shadertest/wireframevertexcolor", TEXTURE_GROUP_OTHER);
-
- int i;
- matrix3x4_t matrix;
- for ( i = 0; i < m_ragdoll.listCount; i++ )
- {
- static color32 debugColor = {0,255,255,0};
-
- // draw the actual physics positions, not the cleaned up animation position
- m_ragdoll.list[i].pObject->GetPositionMatrix( &matrix );
- const CPhysCollide *pCollide = m_ragdoll.list[i].pObject->GetCollide();
- engine->DebugDrawPhysCollide( pCollide, pWireframe, matrix, debugColor );
- }
-
-#if RAGDOLL_VISUALIZE
- for ( i = 0; i < m_ragdoll.listCount; i++ )
- {
- static color32 debugColor = {255,0,0,0};
-
- const CPhysCollide *pCollide = m_ragdoll.list[i].pObject->GetCollide();
- engine->DebugDrawPhysCollide( pCollide, pWireframe, m_savedBone1[m_ragdoll.boneIndex[i]], debugColor );
- }
- for ( i = 0; i < m_ragdoll.listCount; i++ )
- {
- static color32 debugColor = {0,255,0,0};
-
- const CPhysCollide *pCollide = m_ragdoll.list[i].pObject->GetCollide();
- engine->DebugDrawPhysCollide( pCollide, pWireframe, m_savedBone2[m_ragdoll.boneIndex[i]], debugColor );
- }
-
- for ( i = 0; i < m_ragdoll.listCount; i++ )
- {
- static color32 debugColor = {0,0,255,0};
-
- const CPhysCollide *pCollide = m_ragdoll.list[i].pObject->GetCollide();
- engine->DebugDrawPhysCollide( pCollide, pWireframe, m_savedBone3[m_ragdoll.boneIndex[i]], debugColor );
- }
-#endif
-}
-
-
-CRagdoll *CreateRagdoll(
- C_BaseEntity *ent,
- CStudioHdr *pstudiohdr,
- const Vector &forceVector,
- int forceBone,
- const matrix3x4_t *pDeltaBones0,
- const matrix3x4_t *pDeltaBones1,
- const matrix3x4_t *pCurrentBonePosition,
- float dt,
- bool bFixedConstraints )
-{
- CRagdoll *pRagdoll = new CRagdoll;
- pRagdoll->Init( ent, pstudiohdr, forceVector, forceBone, pDeltaBones0, pDeltaBones1, pCurrentBonePosition, dt, bFixedConstraints );
-
- if ( !pRagdoll->IsValid() )
- {
- Msg("Bad ragdoll for %s\n", pstudiohdr->pszName() );
- delete pRagdoll;
- pRagdoll = NULL;
- }
- return pRagdoll;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-class C_ServerRagdoll : public C_BaseAnimating
-{
-public:
- DECLARE_CLASS( C_ServerRagdoll, C_BaseAnimating );
- DECLARE_CLIENTCLASS();
- DECLARE_INTERPOLATION();
-
- C_ServerRagdoll( void );
-
- virtual void PostDataUpdate( DataUpdateType_t updateType );
-
- virtual int InternalDrawModel( int flags );
- virtual CStudioHdr *OnNewModel( void );
- virtual unsigned char GetClientSideFade();
- virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights );
-
- void GetRenderBounds( Vector& theMins, Vector& theMaxs );
- virtual void AddEntity( void );
- virtual void AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Quaternion q[], float currentTime );
- virtual void BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t &cameraTransform, int boneMask, CBoneBitList &boneComputed );
- IPhysicsObject *GetElement( int elementNum );
- virtual void UpdateOnRemove();
- virtual float LastBoneChangedTime();
-
- // Incoming from network
- Vector m_ragPos[RAGDOLL_MAX_ELEMENTS];
- QAngle m_ragAngles[RAGDOLL_MAX_ELEMENTS];
-
- CInterpolatedVarArray< Vector, RAGDOLL_MAX_ELEMENTS > m_iv_ragPos;
- CInterpolatedVarArray< QAngle, RAGDOLL_MAX_ELEMENTS > m_iv_ragAngles;
-
- int m_elementCount;
- int m_boneIndex[RAGDOLL_MAX_ELEMENTS];
-
-private:
- C_ServerRagdoll( const C_ServerRagdoll &src );
-
- typedef CHandle<C_BaseAnimating> CBaseAnimatingHandle;
- CNetworkVar( CBaseAnimatingHandle, m_hUnragdoll );
- CNetworkVar( float, m_flBlendWeight );
- float m_flBlendWeightCurrent;
- CNetworkVar( int, m_nOverlaySequence );
- float m_flLastBoneChangeTime;
-};
-
-
-EXTERN_RECV_TABLE(DT_Ragdoll);
-IMPLEMENT_CLIENTCLASS_DT(C_ServerRagdoll, DT_Ragdoll, CRagdollProp)
- RecvPropArray(RecvPropQAngles(RECVINFO(m_ragAngles[0])), m_ragAngles),
- RecvPropArray(RecvPropVector(RECVINFO(m_ragPos[0])), m_ragPos),
- RecvPropEHandle(RECVINFO(m_hUnragdoll)),
- RecvPropFloat(RECVINFO(m_flBlendWeight)),
- RecvPropInt(RECVINFO(m_nOverlaySequence)),
-END_RECV_TABLE()
-
-
-C_ServerRagdoll::C_ServerRagdoll( void ) :
- m_iv_ragPos("C_ServerRagdoll::m_iv_ragPos"),
- m_iv_ragAngles("C_ServerRagdoll::m_iv_ragAngles")
-{
- m_elementCount = 0;
- m_flLastBoneChangeTime = -FLT_MAX;
-
- AddVar( m_ragPos, &m_iv_ragPos, LATCH_SIMULATION_VAR );
- AddVar( m_ragAngles, &m_iv_ragAngles, LATCH_SIMULATION_VAR );
-
- m_flBlendWeight = 0.0f;
- m_flBlendWeightCurrent = 0.0f;
- m_nOverlaySequence = -1;
- m_flFadeScale = 1;
-}
-
-void C_ServerRagdoll::PostDataUpdate( DataUpdateType_t updateType )
-{
- BaseClass::PostDataUpdate( updateType );
-
- m_iv_ragPos.NoteChanged( gpGlobals->curtime, true );
- m_iv_ragAngles.NoteChanged( gpGlobals->curtime, true );
- // this is the local client time at which this update becomes stale
- m_flLastBoneChangeTime = gpGlobals->curtime + GetInterpolationAmount(m_iv_ragPos.GetType());
-}
-
-float C_ServerRagdoll::LastBoneChangedTime()
-{
- return m_flLastBoneChangeTime;
-}
-
-int C_ServerRagdoll::InternalDrawModel( int flags )
-{
- int ret = BaseClass::InternalDrawModel( flags );
- if ( vcollide_wireframe.GetBool() )
- {
- vcollide_t *pCollide = modelinfo->GetVCollide( GetModelIndex() );
- IMaterial *pWireframe = materials->FindMaterial("shadertest/wireframevertexcolor", TEXTURE_GROUP_OTHER);
-
- matrix3x4_t matrix;
- for ( int i = 0; i < m_elementCount; i++ )
- {
- static color32 debugColor = {0,255,255,0};
-
- AngleMatrix( m_ragAngles[i], m_ragPos[i], matrix );
- engine->DebugDrawPhysCollide( pCollide->solids[i], pWireframe, matrix, debugColor );
- }
- }
- return ret;
-}
-
-
-CStudioHdr *C_ServerRagdoll::OnNewModel( void )
-{
- CStudioHdr *hdr = BaseClass::OnNewModel();
-
- if ( !m_elementCount )
- {
- vcollide_t *pCollide = modelinfo->GetVCollide( GetModelIndex() );
- if ( !pCollide )
- {
- const char *pszName = modelinfo->GetModelName( modelinfo->GetModel( GetModelIndex() ) );
- Msg( "*** ERROR: C_ServerRagdoll::InitModel: %s missing vcollide data ***\n", (pszName) ? pszName : "<null>" );
- m_elementCount = 0;
- }
- else
- {
- m_elementCount = RagdollExtractBoneIndices( m_boneIndex, hdr, pCollide );
- }
- m_iv_ragPos.SetMaxCount( m_elementCount );
- m_iv_ragAngles.SetMaxCount( m_elementCount );
- }
-
- return hdr;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: clear out any face/eye values stored in the material system
-//-----------------------------------------------------------------------------
-void C_ServerRagdoll::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights )
-{
- BaseClass::SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights );
-
- CStudioHdr *hdr = GetModelPtr();
- if ( !hdr )
- return;
-
- int nFlexDescCount = hdr->numflexdesc();
- if ( nFlexDescCount )
- {
- Assert( !pFlexDelayedWeights );
- memset( pFlexWeights, 0, nFlexWeightCount * sizeof(float) );
- }
-
- if ( m_iEyeAttachment > 0 )
- {
- matrix3x4_t attToWorld;
- if (GetAttachment( m_iEyeAttachment, attToWorld ))
- {
- Vector local, tmp;
- local.Init( 1000.0f, 0.0f, 0.0f );
- VectorTransform( local, attToWorld, tmp );
- modelrender->SetViewTarget( GetModelPtr(), GetBody(), tmp );
- }
- }
-}
-
-
-void C_ServerRagdoll::GetRenderBounds( Vector& theMins, Vector& theMaxs )
-{
- if( !CollisionProp()->IsBoundsDefinedInEntitySpace() )
- {
- IRotateAABB( EntityToWorldTransform(), CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), theMins, theMaxs );
- }
- else
- {
- theMins = CollisionProp()->OBBMins();
- theMaxs = CollisionProp()->OBBMaxs();
- }
-}
-
-void C_ServerRagdoll::AddEntity( void )
-{
- BaseClass::AddEntity();
-
- // Move blend weight toward target over 0.2 seconds
- m_flBlendWeightCurrent = Approach( m_flBlendWeight, m_flBlendWeightCurrent, gpGlobals->frametime * 5.0f );
-}
-
-void C_ServerRagdoll::AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Quaternion q[], float currentTime )
-{
- BaseClass::AccumulateLayers( boneSetup, pos, q, currentTime );
-
- if ( m_nOverlaySequence >= 0 && m_nOverlaySequence < boneSetup.GetStudioHdr()->GetNumSeq() )
- {
- boneSetup.AccumulatePose( pos, q, m_nOverlaySequence, GetCycle(), m_flBlendWeightCurrent, currentTime, m_pIk );
- }
-}
-
-void C_ServerRagdoll::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t &cameraTransform, int boneMask, CBoneBitList &boneComputed )
-{
- if ( !hdr )
- return;
- matrix3x4_t bonematrix;
- bool boneSimulated[MAXSTUDIOBONES];
-
- // no bones have been simulated
- memset( boneSimulated, 0, sizeof(boneSimulated) );
- mstudiobone_t *pbones = hdr->pBone( 0 );
-
- mstudioseqdesc_t *pSeqDesc = NULL;
- if ( m_nOverlaySequence >= 0 && m_nOverlaySequence < hdr->GetNumSeq() )
- {
- pSeqDesc = &hdr->pSeqdesc( m_nOverlaySequence );
- }
-
- int i;
- for ( i = 0; i < m_elementCount; i++ )
- {
- int index = m_boneIndex[i];
- if ( index >= 0 )
- {
- if ( hdr->boneFlags(index) & boneMask )
- {
- boneSimulated[index] = true;
- matrix3x4_t &matrix = GetBoneForWrite( index );
-
- if ( m_flBlendWeightCurrent != 0.0f && pSeqDesc &&
- // FIXME: this bone access is illegal
- pSeqDesc->weight( index ) != 0.0f )
- {
- // Use the animated bone position instead
- boneSimulated[index] = false;
- }
- else
- {
- AngleMatrix( m_ragAngles[i], m_ragPos[i], matrix );
- }
- }
- }
- }
-
- for ( i = 0; i < hdr->numbones(); i++ )
- {
- if ( !( hdr->boneFlags( i ) & boneMask ) )
- continue;
-
- // BUGBUG: Merge this code with the code in c_baseanimating somehow!!!
- // animate all non-simulated bones
- if ( boneSimulated[i] ||
- CalcProceduralBone( hdr, i, m_BoneAccessor ) )
- {
- continue;
- }
- else
- {
- QuaternionMatrix( q[i], pos[i], bonematrix );
-
- if (pbones[i].parent == -1)
- {
- ConcatTransforms( cameraTransform, bonematrix, GetBoneForWrite( i ) );
- }
- else
- {
- ConcatTransforms( GetBone( pbones[i].parent ), bonematrix, GetBoneForWrite( i ) );
- }
- }
-
- if ( pbones[i].parent == -1 )
- {
- // Apply client-side effects to the transformation matrix
- // ApplyBoneMatrixTransform( GetBoneForWrite( i ) );
- }
- }
-}
-
-IPhysicsObject *C_ServerRagdoll::GetElement( int elementNum )
-{
- return NULL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : virtual void
-//-----------------------------------------------------------------------------
-void C_ServerRagdoll::UpdateOnRemove()
-{
- C_BaseAnimating *anim = m_hUnragdoll.Get();
- if ( NULL != anim &&
- anim->GetModel() &&
- ( anim->GetModel() == GetModel() ) )
- {
- // Need to tell C_BaseAnimating to blend out of the ragdoll data that we received last
- C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false );
- anim->CreateUnragdollInfo( this );
- }
-
- // Do last to mimic destrictor order
- BaseClass::UpdateOnRemove();
-}
-
-//-----------------------------------------------------------------------------
-// Fade out
-//-----------------------------------------------------------------------------
-unsigned char C_ServerRagdoll::GetClientSideFade()
-{
- return UTIL_ComputeEntityFade( this, m_fadeMinDist, m_fadeMaxDist, m_flFadeScale );
-}
-
-static int GetHighestBit( int flags )
-{
- for ( int i = 31; i >= 0; --i )
- {
- if ( flags & (1<<i) )
- return (1<<i);
- }
-
- return 0;
-}
-
-#define ATTACH_INTERP_TIME 0.2
-class C_ServerRagdollAttached : public C_ServerRagdoll
-{
- DECLARE_CLASS( C_ServerRagdollAttached, C_ServerRagdoll );
-public:
- C_ServerRagdollAttached( void )
- {
- m_bHasParent = false;
- m_vecOffset.Init();
- }
- DECLARE_CLIENTCLASS();
- bool SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
- {
- if ( GetMoveParent() )
- {
- // HACKHACK: Force the attached bone to be set up
- int index = m_boneIndex[m_ragdollAttachedObjectIndex];
- int boneFlags = GetModelPtr()->boneFlags(index);
- if ( !(boneFlags & boneMask) )
- {
- // BUGBUG: The attached bone is required and this call is going to skip it, so force it
- // HACKHACK: Assume the highest bit numbered bone flag is the minimum bone set
- boneMask |= GetHighestBit( boneFlags );
- }
- }
- return BaseClass::SetupBones( pBoneToWorldOut, nMaxBones, boneMask, currentTime );
- }
-
- virtual void BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
- {
- VPROF_BUDGET( "C_ServerRagdollAttached::SetupBones", VPROF_BUDGETGROUP_CLIENT_ANIMATION );
-
- if ( !hdr )
- return;
-
- float frac = RemapVal( gpGlobals->curtime, m_parentTime, m_parentTime+ATTACH_INTERP_TIME, 0, 1 );
- frac = clamp( frac, 0.f, 1.f );
- // interpolate offset over some time
- Vector offset = m_vecOffset * (1-frac);
-
- C_BaseAnimating *parent = assert_cast< C_BaseAnimating* >( GetMoveParent() );
- Vector worldOrigin;
- worldOrigin.Init();
-
-
- if ( parent )
- {
- Assert( parent != this );
- parent->SetupBones( NULL, -1, BONE_USED_BY_ANYTHING, gpGlobals->curtime );
-
- matrix3x4_t boneToWorld;
- parent->GetCachedBoneMatrix( m_boneIndexAttached, boneToWorld );
- VectorTransform( m_attachmentPointBoneSpace, boneToWorld, worldOrigin );
- }
- BaseClass::BuildTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed );
-
- if ( parent )
- {
- int index = m_boneIndex[m_ragdollAttachedObjectIndex];
- const matrix3x4_t &matrix = GetBone( index );
- Vector ragOrigin;
- VectorTransform( m_attachmentPointRagdollSpace, matrix, ragOrigin );
- offset = worldOrigin - ragOrigin;
- // fixes culling
- SetAbsOrigin( worldOrigin );
- m_vecOffset = offset;
- }
-
- for ( int i = 0; i < hdr->numbones(); i++ )
- {
- if ( !( hdr->boneFlags( i ) & boneMask ) )
- continue;
-
- Vector pos;
- matrix3x4_t &matrix = GetBoneForWrite( i );
- MatrixGetColumn( matrix, 3, pos );
- pos += offset;
- MatrixSetColumn( pos, 3, matrix );
- }
- }
- void OnDataChanged( DataUpdateType_t updateType );
- virtual float LastBoneChangedTime() { return FLT_MAX; }
-
- Vector m_attachmentPointBoneSpace;
- Vector m_vecOffset;
- Vector m_attachmentPointRagdollSpace;
- int m_ragdollAttachedObjectIndex;
- int m_boneIndexAttached;
- float m_parentTime;
- bool m_bHasParent;
-private:
- C_ServerRagdollAttached( const C_ServerRagdollAttached & );
-};
-
-EXTERN_RECV_TABLE(DT_Ragdoll_Attached);
-IMPLEMENT_CLIENTCLASS_DT(C_ServerRagdollAttached, DT_Ragdoll_Attached, CRagdollPropAttached)
- RecvPropInt( RECVINFO( m_boneIndexAttached ) ),
- RecvPropInt( RECVINFO( m_ragdollAttachedObjectIndex ) ),
- RecvPropVector(RECVINFO(m_attachmentPointBoneSpace) ),
- RecvPropVector(RECVINFO(m_attachmentPointRagdollSpace) ),
-END_RECV_TABLE()
-
-void C_ServerRagdollAttached::OnDataChanged( DataUpdateType_t updateType )
-{
- BaseClass::OnDataChanged( updateType );
-
- bool bParentNow = GetMoveParent() ? true : false;
- if ( m_bHasParent != bParentNow )
- {
- if ( m_bHasParent )
- {
- m_parentTime = gpGlobals->curtime;
- }
- m_bHasParent = bParentNow;
- }
-}
-
-struct ragdoll_remember_t
-{
- C_BaseEntity *ragdoll;
- int tickCount;
-};
-
-struct ragdoll_memory_list_t
-{
- CUtlVector<ragdoll_remember_t> list;
-
- int tickCount;
-
- void Update()
- {
- if ( tickCount > gpGlobals->tickcount )
- {
- list.RemoveAll();
- return;
- }
-
- for ( int i = list.Count()-1; i >= 0; --i )
- {
- if ( list[i].tickCount != gpGlobals->tickcount )
- {
- list.FastRemove(i);
- }
- }
- }
-
- bool IsInList( C_BaseEntity *pRagdoll )
- {
- for ( int i = list.Count()-1; i >= 0; --i )
- {
- if ( list[i].ragdoll == pRagdoll )
- return true;
- }
-
- return false;
- }
- void AddToList( C_BaseEntity *pRagdoll )
- {
- Update();
- int index = list.AddToTail();
- list[index].ragdoll = pRagdoll;
- list[index].tickCount = gpGlobals->tickcount;
- }
-};
-
-static ragdoll_memory_list_t gRagdolls;
-
-void NoteRagdollCreationTick( C_BaseEntity *pRagdoll )
-{
- gRagdolls.AddToList( pRagdoll );
-}
-
-// returns true if the ragdoll was created on this tick
-bool WasRagdollCreatedOnCurrentTick( C_BaseEntity *pRagdoll )
-{
- gRagdolls.Update();
- return gRagdolls.IsInList( pRagdoll );
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "cbase.h" +#include "mathlib/vmatrix.h" +#include "ragdoll_shared.h" +#include "bone_setup.h" +#include "materialsystem/imesh.h" +#include "engine/ivmodelinfo.h" +#include "iviewrender.h" +#include "tier0/vprof.h" +#include "view.h" +#include "physics_saverestore.h" +#include "vphysics/constraints.h" +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef _DEBUG +extern ConVar r_FadeProps; +#endif + +CRagdoll::CRagdoll() +{ + m_ragdoll.listCount = 0; + m_vecLastOrigin.Init(); + m_flLastOriginChangeTime = - 1.0f; + + m_lastUpdate = -FLT_MAX; +} + +#define DEFINE_RAGDOLL_ELEMENT( i ) \ + DEFINE_FIELD( m_ragdoll.list[i].originParentSpace, FIELD_VECTOR ), \ + DEFINE_PHYSPTR( m_ragdoll.list[i].pObject ), \ + DEFINE_PHYSPTR( m_ragdoll.list[i].pConstraint ), \ + DEFINE_FIELD( m_ragdoll.list[i].parentIndex, FIELD_INTEGER ) + +BEGIN_SIMPLE_DATADESC( CRagdoll ) + + DEFINE_AUTO_ARRAY( m_ragdoll.boneIndex, FIELD_INTEGER ), + DEFINE_FIELD( m_ragdoll.listCount, FIELD_INTEGER ), + DEFINE_FIELD( m_ragdoll.allowStretch, FIELD_BOOLEAN ), + DEFINE_PHYSPTR( m_ragdoll.pGroup ), + + DEFINE_RAGDOLL_ELEMENT( 0 ), + DEFINE_RAGDOLL_ELEMENT( 1 ), + DEFINE_RAGDOLL_ELEMENT( 2 ), + DEFINE_RAGDOLL_ELEMENT( 3 ), + DEFINE_RAGDOLL_ELEMENT( 4 ), + DEFINE_RAGDOLL_ELEMENT( 5 ), + DEFINE_RAGDOLL_ELEMENT( 6 ), + DEFINE_RAGDOLL_ELEMENT( 7 ), + DEFINE_RAGDOLL_ELEMENT( 8 ), + DEFINE_RAGDOLL_ELEMENT( 9 ), + DEFINE_RAGDOLL_ELEMENT( 10 ), + DEFINE_RAGDOLL_ELEMENT( 11 ), + DEFINE_RAGDOLL_ELEMENT( 12 ), + DEFINE_RAGDOLL_ELEMENT( 13 ), + DEFINE_RAGDOLL_ELEMENT( 14 ), + DEFINE_RAGDOLL_ELEMENT( 15 ), + DEFINE_RAGDOLL_ELEMENT( 16 ), + DEFINE_RAGDOLL_ELEMENT( 17 ), + DEFINE_RAGDOLL_ELEMENT( 18 ), + DEFINE_RAGDOLL_ELEMENT( 19 ), + DEFINE_RAGDOLL_ELEMENT( 20 ), + DEFINE_RAGDOLL_ELEMENT( 21 ), + DEFINE_RAGDOLL_ELEMENT( 22 ), + DEFINE_RAGDOLL_ELEMENT( 23 ), + +END_DATADESC() + +IPhysicsObject *CRagdoll::GetElement( int elementNum ) +{ + return m_ragdoll.list[elementNum].pObject; +} + +void CRagdoll::BuildRagdollBounds( C_BaseEntity *ent ) +{ + Vector mins, maxs, size; + modelinfo->GetModelBounds( ent->GetModel(), mins, maxs ); + size = (maxs - mins) * 0.5; + m_radius = size.Length(); + + m_mins.Init(-m_radius,-m_radius,-m_radius); + m_maxs.Init(m_radius,m_radius,m_radius); +} + +void CRagdoll::Init( + C_BaseEntity *ent, + CStudioHdr *pstudiohdr, + const Vector &forceVector, + int forceBone, + const matrix3x4_t *pDeltaBones0, + const matrix3x4_t *pDeltaBones1, + const matrix3x4_t *pCurrentBonePosition, + float dt, + bool bFixedConstraints ) +{ + ragdollparams_t params; + params.pGameData = static_cast<void *>( ent ); + params.modelIndex = ent->GetModelIndex(); + params.pCollide = modelinfo->GetVCollide( params.modelIndex ); + params.pStudioHdr = pstudiohdr; + params.forceVector = forceVector; + params.forceBoneIndex = forceBone; + params.forcePosition.Init(); + params.pCurrentBones = pCurrentBonePosition; + params.jointFrictionScale = 1.0; + params.allowStretch = false; + params.fixedConstraints = bFixedConstraints; + RagdollCreate( m_ragdoll, params, physenv ); + ent->VPhysicsSetObject( NULL ); + ent->VPhysicsSetObject( m_ragdoll.list[0].pObject ); + // Mark the ragdoll as debris. + ent->SetCollisionGroup( COLLISION_GROUP_DEBRIS ); + + RagdollApplyAnimationAsVelocity( m_ragdoll, pDeltaBones0, pDeltaBones1, dt ); + RagdollActivate( m_ragdoll, params.pCollide, ent->GetModelIndex() ); + + // It's moving now... + m_flLastOriginChangeTime = gpGlobals->curtime; + + // So traces hit it. + ent->AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); + + if ( !m_ragdoll.listCount ) + return; + + BuildRagdollBounds( ent ); + + for ( int i = 0; i < m_ragdoll.listCount; i++ ) + { + g_pPhysSaveRestoreManager->AssociateModel( m_ragdoll.list[i].pObject, ent->GetModelIndex() ); + } + +#if RAGDOLL_VISUALIZE + memcpy( m_savedBone1, &pDeltaBones0[0], sizeof(matrix3x4_t) * pstudiohdr->numbones() ); + memcpy( m_savedBone2, &pDeltaBones1[0], sizeof(matrix3x4_t) * pstudiohdr->numbones() ); + memcpy( m_savedBone3, &pCurrentBonePosition[0], sizeof(matrix3x4_t) * pstudiohdr->numbones() ); +#endif +} + +CRagdoll::~CRagdoll( void ) +{ + for ( int i = 0; i < m_ragdoll.listCount; i++ ) + { + IPhysicsObject *pObject = m_ragdoll.list[i].pObject; + if ( pObject ) + { + g_pPhysSaveRestoreManager->ForgetModel( m_ragdoll.list[i].pObject ); + // Disable collision on all ragdoll parts before calling RagdollDestroy + // (which might cause touch callbacks on the ragdoll otherwise, which is + // very bad for a half deleted ragdoll). + pObject->EnableCollisions( false ); + } + } + + RagdollDestroy( m_ragdoll ); +} + + +void CRagdoll::RagdollBone( C_BaseEntity *ent, mstudiobone_t *pbones, int boneCount, bool *boneSimulated, CBoneAccessor &pBoneToWorld ) +{ + for ( int i = 0; i < m_ragdoll.listCount; i++ ) + { + if ( RagdollGetBoneMatrix( m_ragdoll, pBoneToWorld, i ) ) + { + boneSimulated[m_ragdoll.boneIndex[i]] = true; + } + } +} + +const Vector& CRagdoll::GetRagdollOrigin( ) +{ + m_ragdoll.list[0].pObject->GetPosition( &m_origin, 0 ); + return m_origin; +} + +void CRagdoll::GetRagdollBounds( Vector &theMins, Vector &theMaxs ) +{ + theMins = m_mins; + theMaxs = m_maxs; +} + +void CRagdoll::VPhysicsUpdate( IPhysicsObject *pPhysics ) +{ + if ( m_lastUpdate == gpGlobals->curtime ) + return; + m_lastUpdate = gpGlobals->curtime; + m_allAsleep = RagdollIsAsleep( m_ragdoll ); + if ( m_allAsleep ) + { + // NOTE: This is the bbox of the ragdoll's physics + // It's not always correct to use for culling, but it sure beats + // using the radius box! + Vector origin = GetRagdollOrigin(); + RagdollComputeExactBbox( m_ragdoll, origin, m_mins, m_maxs ); + m_mins -= origin; + m_maxs -= origin; + } + else + { + m_mins.Init(-m_radius,-m_radius,-m_radius); + m_maxs.Init(m_radius,m_radius,m_radius); + + if ( m_ragdoll.pGroup->IsInErrorState() ) + { + C_BaseEntity *pEntity = static_cast<C_BaseEntity *>(m_ragdoll.list[0].pObject->GetGameData()); + RagdollSolveSeparation( m_ragdoll, pEntity ); + } + } + + // See if we should go to sleep... + CheckSettleStationaryRagdoll(); +} + +//============================================================================= +// HPE_BEGIN: +// [menglish] Transforms a vector from the given bone's space to world space +//============================================================================= + +bool CRagdoll::TransformVectorToWorld(int iBoneIndex, const Vector *vPosition, Vector *vOut) +{ + int listIndex = -1; + if( iBoneIndex >= 0 && iBoneIndex < m_ragdoll.listCount) + { + for ( int i = 0; i < m_ragdoll.listCount; ++i ) + { + if(m_ragdoll.boneIndex[i] == iBoneIndex) + listIndex = i; + } + if(listIndex != -1) + { + m_ragdoll.list[listIndex].pObject->LocalToWorld(vOut, *vPosition); + return true; + } + } + return false; +} + +//============================================================================= +// HPE_END +//============================================================================= + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void CRagdoll::PhysForceRagdollToSleep() +{ + for ( int i = 0; i < m_ragdoll.listCount; i++ ) + { + if ( m_ragdoll.list[i].pObject ) + { + PhysForceClearVelocity( m_ragdoll.list[i].pObject ); + m_ragdoll.list[i].pObject->Sleep(); + } + } +} + +#define RAGDOLL_SLEEP_TOLERANCE 1.0f +static ConVar ragdoll_sleepaftertime( "ragdoll_sleepaftertime", "5.0f", 0, "After this many seconds of being basically stationary, the ragdoll will go to sleep." ); + +void CRagdoll::CheckSettleStationaryRagdoll() +{ + Vector delta = GetRagdollOrigin() - m_vecLastOrigin; + m_vecLastOrigin = GetRagdollOrigin(); + for ( int i = 0; i < 3; ++i ) + { + // It's still moving... + if ( fabs( delta[ i ] ) > RAGDOLL_SLEEP_TOLERANCE ) + { + m_flLastOriginChangeTime = gpGlobals->curtime; + // Msg( "%d [%p] Still moving\n", gpGlobals->tickcount, this ); + return; + } + } + + // It's totally asleep, don't worry about forcing it to settle + if ( m_allAsleep ) + return; + + // Msg( "%d [%p] Settling\n", gpGlobals->tickcount, this ); + + // It has stopped moving, see if it + float dt = gpGlobals->curtime - m_flLastOriginChangeTime; + if ( dt < ragdoll_sleepaftertime.GetFloat() ) + return; + + // Msg( "%d [%p] FORCE SLEEP\n",gpGlobals->tickcount, this ); + + // Force it to go to sleep + PhysForceRagdollToSleep(); +} + +void CRagdoll::ResetRagdollSleepAfterTime( void ) +{ + m_flLastOriginChangeTime = gpGlobals->curtime; +} + +void CRagdoll::DrawWireframe() +{ + IMaterial *pWireframe = materials->FindMaterial("shadertest/wireframevertexcolor", TEXTURE_GROUP_OTHER); + + int i; + matrix3x4_t matrix; + for ( i = 0; i < m_ragdoll.listCount; i++ ) + { + static color32 debugColor = {0,255,255,0}; + + // draw the actual physics positions, not the cleaned up animation position + m_ragdoll.list[i].pObject->GetPositionMatrix( &matrix ); + const CPhysCollide *pCollide = m_ragdoll.list[i].pObject->GetCollide(); + engine->DebugDrawPhysCollide( pCollide, pWireframe, matrix, debugColor ); + } + +#if RAGDOLL_VISUALIZE + for ( i = 0; i < m_ragdoll.listCount; i++ ) + { + static color32 debugColor = {255,0,0,0}; + + const CPhysCollide *pCollide = m_ragdoll.list[i].pObject->GetCollide(); + engine->DebugDrawPhysCollide( pCollide, pWireframe, m_savedBone1[m_ragdoll.boneIndex[i]], debugColor ); + } + for ( i = 0; i < m_ragdoll.listCount; i++ ) + { + static color32 debugColor = {0,255,0,0}; + + const CPhysCollide *pCollide = m_ragdoll.list[i].pObject->GetCollide(); + engine->DebugDrawPhysCollide( pCollide, pWireframe, m_savedBone2[m_ragdoll.boneIndex[i]], debugColor ); + } + + for ( i = 0; i < m_ragdoll.listCount; i++ ) + { + static color32 debugColor = {0,0,255,0}; + + const CPhysCollide *pCollide = m_ragdoll.list[i].pObject->GetCollide(); + engine->DebugDrawPhysCollide( pCollide, pWireframe, m_savedBone3[m_ragdoll.boneIndex[i]], debugColor ); + } +#endif +} + + +CRagdoll *CreateRagdoll( + C_BaseEntity *ent, + CStudioHdr *pstudiohdr, + const Vector &forceVector, + int forceBone, + const matrix3x4_t *pDeltaBones0, + const matrix3x4_t *pDeltaBones1, + const matrix3x4_t *pCurrentBonePosition, + float dt, + bool bFixedConstraints ) +{ + CRagdoll *pRagdoll = new CRagdoll; + pRagdoll->Init( ent, pstudiohdr, forceVector, forceBone, pDeltaBones0, pDeltaBones1, pCurrentBonePosition, dt, bFixedConstraints ); + + if ( !pRagdoll->IsValid() ) + { + Msg("Bad ragdoll for %s\n", pstudiohdr->pszName() ); + delete pRagdoll; + pRagdoll = NULL; + } + return pRagdoll; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class C_ServerRagdoll : public C_BaseAnimating +{ +public: + DECLARE_CLASS( C_ServerRagdoll, C_BaseAnimating ); + DECLARE_CLIENTCLASS(); + DECLARE_INTERPOLATION(); + + C_ServerRagdoll( void ); + + virtual void PostDataUpdate( DataUpdateType_t updateType ); + + virtual int InternalDrawModel( int flags ); + virtual CStudioHdr *OnNewModel( void ); + virtual unsigned char GetClientSideFade(); + virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights ); + + void GetRenderBounds( Vector& theMins, Vector& theMaxs ); + virtual void AddEntity( void ); + virtual void AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Quaternion q[], float currentTime ); + virtual void BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t &cameraTransform, int boneMask, CBoneBitList &boneComputed ); + IPhysicsObject *GetElement( int elementNum ); + virtual void UpdateOnRemove(); + virtual float LastBoneChangedTime(); + + // Incoming from network + Vector m_ragPos[RAGDOLL_MAX_ELEMENTS]; + QAngle m_ragAngles[RAGDOLL_MAX_ELEMENTS]; + + CInterpolatedVarArray< Vector, RAGDOLL_MAX_ELEMENTS > m_iv_ragPos; + CInterpolatedVarArray< QAngle, RAGDOLL_MAX_ELEMENTS > m_iv_ragAngles; + + int m_elementCount; + int m_boneIndex[RAGDOLL_MAX_ELEMENTS]; + +private: + C_ServerRagdoll( const C_ServerRagdoll &src ); + + typedef CHandle<C_BaseAnimating> CBaseAnimatingHandle; + CNetworkVar( CBaseAnimatingHandle, m_hUnragdoll ); + CNetworkVar( float, m_flBlendWeight ); + float m_flBlendWeightCurrent; + CNetworkVar( int, m_nOverlaySequence ); + float m_flLastBoneChangeTime; +}; + + +EXTERN_RECV_TABLE(DT_Ragdoll); +IMPLEMENT_CLIENTCLASS_DT(C_ServerRagdoll, DT_Ragdoll, CRagdollProp) + RecvPropArray(RecvPropQAngles(RECVINFO(m_ragAngles[0])), m_ragAngles), + RecvPropArray(RecvPropVector(RECVINFO(m_ragPos[0])), m_ragPos), + RecvPropEHandle(RECVINFO(m_hUnragdoll)), + RecvPropFloat(RECVINFO(m_flBlendWeight)), + RecvPropInt(RECVINFO(m_nOverlaySequence)), +END_RECV_TABLE() + + +C_ServerRagdoll::C_ServerRagdoll( void ) : + m_iv_ragPos("C_ServerRagdoll::m_iv_ragPos"), + m_iv_ragAngles("C_ServerRagdoll::m_iv_ragAngles") +{ + m_elementCount = 0; + m_flLastBoneChangeTime = -FLT_MAX; + + AddVar( m_ragPos, &m_iv_ragPos, LATCH_SIMULATION_VAR ); + AddVar( m_ragAngles, &m_iv_ragAngles, LATCH_SIMULATION_VAR ); + + m_flBlendWeight = 0.0f; + m_flBlendWeightCurrent = 0.0f; + m_nOverlaySequence = -1; + m_flFadeScale = 1; +} + +void C_ServerRagdoll::PostDataUpdate( DataUpdateType_t updateType ) +{ + BaseClass::PostDataUpdate( updateType ); + + m_iv_ragPos.NoteChanged( gpGlobals->curtime, true ); + m_iv_ragAngles.NoteChanged( gpGlobals->curtime, true ); + // this is the local client time at which this update becomes stale + m_flLastBoneChangeTime = gpGlobals->curtime + GetInterpolationAmount(m_iv_ragPos.GetType()); +} + +float C_ServerRagdoll::LastBoneChangedTime() +{ + return m_flLastBoneChangeTime; +} + +int C_ServerRagdoll::InternalDrawModel( int flags ) +{ + int ret = BaseClass::InternalDrawModel( flags ); + if ( vcollide_wireframe.GetBool() ) + { + vcollide_t *pCollide = modelinfo->GetVCollide( GetModelIndex() ); + IMaterial *pWireframe = materials->FindMaterial("shadertest/wireframevertexcolor", TEXTURE_GROUP_OTHER); + + matrix3x4_t matrix; + for ( int i = 0; i < m_elementCount; i++ ) + { + static color32 debugColor = {0,255,255,0}; + + AngleMatrix( m_ragAngles[i], m_ragPos[i], matrix ); + engine->DebugDrawPhysCollide( pCollide->solids[i], pWireframe, matrix, debugColor ); + } + } + return ret; +} + + +CStudioHdr *C_ServerRagdoll::OnNewModel( void ) +{ + CStudioHdr *hdr = BaseClass::OnNewModel(); + + if ( !m_elementCount ) + { + vcollide_t *pCollide = modelinfo->GetVCollide( GetModelIndex() ); + if ( !pCollide ) + { + const char *pszName = modelinfo->GetModelName( modelinfo->GetModel( GetModelIndex() ) ); + Msg( "*** ERROR: C_ServerRagdoll::InitModel: %s missing vcollide data ***\n", (pszName) ? pszName : "<null>" ); + m_elementCount = 0; + } + else + { + m_elementCount = RagdollExtractBoneIndices( m_boneIndex, hdr, pCollide ); + } + m_iv_ragPos.SetMaxCount( m_elementCount ); + m_iv_ragAngles.SetMaxCount( m_elementCount ); + } + + return hdr; +} + +//----------------------------------------------------------------------------- +// Purpose: clear out any face/eye values stored in the material system +//----------------------------------------------------------------------------- +void C_ServerRagdoll::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights ) +{ + BaseClass::SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights ); + + CStudioHdr *hdr = GetModelPtr(); + if ( !hdr ) + return; + + int nFlexDescCount = hdr->numflexdesc(); + if ( nFlexDescCount ) + { + Assert( !pFlexDelayedWeights ); + memset( pFlexWeights, 0, nFlexWeightCount * sizeof(float) ); + } + + if ( m_iEyeAttachment > 0 ) + { + matrix3x4_t attToWorld; + if (GetAttachment( m_iEyeAttachment, attToWorld )) + { + Vector local, tmp; + local.Init( 1000.0f, 0.0f, 0.0f ); + VectorTransform( local, attToWorld, tmp ); + modelrender->SetViewTarget( GetModelPtr(), GetBody(), tmp ); + } + } +} + + +void C_ServerRagdoll::GetRenderBounds( Vector& theMins, Vector& theMaxs ) +{ + if( !CollisionProp()->IsBoundsDefinedInEntitySpace() ) + { + IRotateAABB( EntityToWorldTransform(), CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), theMins, theMaxs ); + } + else + { + theMins = CollisionProp()->OBBMins(); + theMaxs = CollisionProp()->OBBMaxs(); + } +} + +void C_ServerRagdoll::AddEntity( void ) +{ + BaseClass::AddEntity(); + + // Move blend weight toward target over 0.2 seconds + m_flBlendWeightCurrent = Approach( m_flBlendWeight, m_flBlendWeightCurrent, gpGlobals->frametime * 5.0f ); +} + +void C_ServerRagdoll::AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Quaternion q[], float currentTime ) +{ + BaseClass::AccumulateLayers( boneSetup, pos, q, currentTime ); + + if ( m_nOverlaySequence >= 0 && m_nOverlaySequence < boneSetup.GetStudioHdr()->GetNumSeq() ) + { + boneSetup.AccumulatePose( pos, q, m_nOverlaySequence, GetCycle(), m_flBlendWeightCurrent, currentTime, m_pIk ); + } +} + +void C_ServerRagdoll::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t &cameraTransform, int boneMask, CBoneBitList &boneComputed ) +{ + if ( !hdr ) + return; + matrix3x4_t bonematrix; + bool boneSimulated[MAXSTUDIOBONES]; + + // no bones have been simulated + memset( boneSimulated, 0, sizeof(boneSimulated) ); + mstudiobone_t *pbones = hdr->pBone( 0 ); + + mstudioseqdesc_t *pSeqDesc = NULL; + if ( m_nOverlaySequence >= 0 && m_nOverlaySequence < hdr->GetNumSeq() ) + { + pSeqDesc = &hdr->pSeqdesc( m_nOverlaySequence ); + } + + int i; + for ( i = 0; i < m_elementCount; i++ ) + { + int index = m_boneIndex[i]; + if ( index >= 0 ) + { + if ( hdr->boneFlags(index) & boneMask ) + { + boneSimulated[index] = true; + matrix3x4_t &matrix = GetBoneForWrite( index ); + + if ( m_flBlendWeightCurrent != 0.0f && pSeqDesc && + // FIXME: this bone access is illegal + pSeqDesc->weight( index ) != 0.0f ) + { + // Use the animated bone position instead + boneSimulated[index] = false; + } + else + { + AngleMatrix( m_ragAngles[i], m_ragPos[i], matrix ); + } + } + } + } + + for ( i = 0; i < hdr->numbones(); i++ ) + { + if ( !( hdr->boneFlags( i ) & boneMask ) ) + continue; + + // BUGBUG: Merge this code with the code in c_baseanimating somehow!!! + // animate all non-simulated bones + if ( boneSimulated[i] || + CalcProceduralBone( hdr, i, m_BoneAccessor ) ) + { + continue; + } + else + { + QuaternionMatrix( q[i], pos[i], bonematrix ); + + if (pbones[i].parent == -1) + { + ConcatTransforms( cameraTransform, bonematrix, GetBoneForWrite( i ) ); + } + else + { + ConcatTransforms( GetBone( pbones[i].parent ), bonematrix, GetBoneForWrite( i ) ); + } + } + + if ( pbones[i].parent == -1 ) + { + // Apply client-side effects to the transformation matrix + // ApplyBoneMatrixTransform( GetBoneForWrite( i ) ); + } + } +} + +IPhysicsObject *C_ServerRagdoll::GetElement( int elementNum ) +{ + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : virtual void +//----------------------------------------------------------------------------- +void C_ServerRagdoll::UpdateOnRemove() +{ + C_BaseAnimating *anim = m_hUnragdoll.Get(); + if ( NULL != anim && + anim->GetModel() && + ( anim->GetModel() == GetModel() ) ) + { + // Need to tell C_BaseAnimating to blend out of the ragdoll data that we received last + C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false ); + anim->CreateUnragdollInfo( this ); + } + + // Do last to mimic destrictor order + BaseClass::UpdateOnRemove(); +} + +//----------------------------------------------------------------------------- +// Fade out +//----------------------------------------------------------------------------- +unsigned char C_ServerRagdoll::GetClientSideFade() +{ + return UTIL_ComputeEntityFade( this, m_fadeMinDist, m_fadeMaxDist, m_flFadeScale ); +} + +static int GetHighestBit( int flags ) +{ + for ( int i = 31; i >= 0; --i ) + { + if ( flags & (1<<i) ) + return (1<<i); + } + + return 0; +} + +#define ATTACH_INTERP_TIME 0.2 +class C_ServerRagdollAttached : public C_ServerRagdoll +{ + DECLARE_CLASS( C_ServerRagdollAttached, C_ServerRagdoll ); +public: + C_ServerRagdollAttached( void ) + { + m_bHasParent = false; + m_vecOffset.Init(); + } + DECLARE_CLIENTCLASS(); + bool SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime ) + { + if ( GetMoveParent() ) + { + // HACKHACK: Force the attached bone to be set up + int index = m_boneIndex[m_ragdollAttachedObjectIndex]; + int boneFlags = GetModelPtr()->boneFlags(index); + if ( !(boneFlags & boneMask) ) + { + // BUGBUG: The attached bone is required and this call is going to skip it, so force it + // HACKHACK: Assume the highest bit numbered bone flag is the minimum bone set + boneMask |= GetHighestBit( boneFlags ); + } + } + return BaseClass::SetupBones( pBoneToWorldOut, nMaxBones, boneMask, currentTime ); + } + + virtual void BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed ) + { + VPROF_BUDGET( "C_ServerRagdollAttached::SetupBones", VPROF_BUDGETGROUP_CLIENT_ANIMATION ); + + if ( !hdr ) + return; + + float frac = RemapVal( gpGlobals->curtime, m_parentTime, m_parentTime+ATTACH_INTERP_TIME, 0, 1 ); + frac = clamp( frac, 0.f, 1.f ); + // interpolate offset over some time + Vector offset = m_vecOffset * (1-frac); + + C_BaseAnimating *parent = assert_cast< C_BaseAnimating* >( GetMoveParent() ); + Vector worldOrigin; + worldOrigin.Init(); + + + if ( parent ) + { + Assert( parent != this ); + parent->SetupBones( NULL, -1, BONE_USED_BY_ANYTHING, gpGlobals->curtime ); + + matrix3x4_t boneToWorld; + parent->GetCachedBoneMatrix( m_boneIndexAttached, boneToWorld ); + VectorTransform( m_attachmentPointBoneSpace, boneToWorld, worldOrigin ); + } + BaseClass::BuildTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed ); + + if ( parent ) + { + int index = m_boneIndex[m_ragdollAttachedObjectIndex]; + const matrix3x4_t &matrix = GetBone( index ); + Vector ragOrigin; + VectorTransform( m_attachmentPointRagdollSpace, matrix, ragOrigin ); + offset = worldOrigin - ragOrigin; + // fixes culling + SetAbsOrigin( worldOrigin ); + m_vecOffset = offset; + } + + for ( int i = 0; i < hdr->numbones(); i++ ) + { + if ( !( hdr->boneFlags( i ) & boneMask ) ) + continue; + + Vector pos; + matrix3x4_t &matrix = GetBoneForWrite( i ); + MatrixGetColumn( matrix, 3, pos ); + pos += offset; + MatrixSetColumn( pos, 3, matrix ); + } + } + void OnDataChanged( DataUpdateType_t updateType ); + virtual float LastBoneChangedTime() { return FLT_MAX; } + + Vector m_attachmentPointBoneSpace; + Vector m_vecOffset; + Vector m_attachmentPointRagdollSpace; + int m_ragdollAttachedObjectIndex; + int m_boneIndexAttached; + float m_parentTime; + bool m_bHasParent; +private: + C_ServerRagdollAttached( const C_ServerRagdollAttached & ); +}; + +EXTERN_RECV_TABLE(DT_Ragdoll_Attached); +IMPLEMENT_CLIENTCLASS_DT(C_ServerRagdollAttached, DT_Ragdoll_Attached, CRagdollPropAttached) + RecvPropInt( RECVINFO( m_boneIndexAttached ) ), + RecvPropInt( RECVINFO( m_ragdollAttachedObjectIndex ) ), + RecvPropVector(RECVINFO(m_attachmentPointBoneSpace) ), + RecvPropVector(RECVINFO(m_attachmentPointRagdollSpace) ), +END_RECV_TABLE() + +void C_ServerRagdollAttached::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + bool bParentNow = GetMoveParent() ? true : false; + if ( m_bHasParent != bParentNow ) + { + if ( m_bHasParent ) + { + m_parentTime = gpGlobals->curtime; + } + m_bHasParent = bParentNow; + } +} + +struct ragdoll_remember_t +{ + C_BaseEntity *ragdoll; + int tickCount; +}; + +struct ragdoll_memory_list_t +{ + CUtlVector<ragdoll_remember_t> list; + + int tickCount; + + void Update() + { + if ( tickCount > gpGlobals->tickcount ) + { + list.RemoveAll(); + return; + } + + for ( int i = list.Count()-1; i >= 0; --i ) + { + if ( list[i].tickCount != gpGlobals->tickcount ) + { + list.FastRemove(i); + } + } + } + + bool IsInList( C_BaseEntity *pRagdoll ) + { + for ( int i = list.Count()-1; i >= 0; --i ) + { + if ( list[i].ragdoll == pRagdoll ) + return true; + } + + return false; + } + void AddToList( C_BaseEntity *pRagdoll ) + { + Update(); + int index = list.AddToTail(); + list[index].ragdoll = pRagdoll; + list[index].tickCount = gpGlobals->tickcount; + } +}; + +static ragdoll_memory_list_t gRagdolls; + +void NoteRagdollCreationTick( C_BaseEntity *pRagdoll ) +{ + gRagdolls.AddToList( pRagdoll ); +} + +// returns true if the ragdoll was created on this tick +bool WasRagdollCreatedOnCurrentTick( C_BaseEntity *pRagdoll ) +{ + gRagdolls.Update(); + return gRagdolls.IsInList( pRagdoll ); +} + |