diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/client/ragdoll.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/client/ragdoll.cpp')
| -rw-r--r-- | mp/src/game/client/ragdoll.cpp | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/mp/src/game/client/ragdoll.cpp b/mp/src/game/client/ragdoll.cpp new file mode 100644 index 00000000..1bf83ce3 --- /dev/null +++ b/mp/src/game/client/ragdoll.cpp @@ -0,0 +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 );
+}
+
|