From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- sp/src/game/client/ragdoll.cpp | 867 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 867 insertions(+) create mode 100644 sp/src/game/client/ragdoll.cpp (limited to 'sp/src/game/client/ragdoll.cpp') diff --git a/sp/src/game/client/ragdoll.cpp b/sp/src/game/client/ragdoll.cpp new file mode 100644 index 00000000..1bf83ce3 --- /dev/null +++ b/sp/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( 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(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 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 : "" ); + 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<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 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 ); +} + -- cgit v1.2.3