|
|
//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "mathlib/vmatrix.h"
#include "ragdoll_shared.h"
#include "bone_setup.h"
#if defined( _PS3 )
#include "bone_setup_PS3.h"
#endif
#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"
#include "clientalphaproperty.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#ifdef _DEBUG
extern ConVar r_FadeProps; #endif
extern ConVar r_sequence_debug;
CRagdoll::CRagdoll() { m_ragdoll.listCount = 0; m_vecLastOrigin.Init(); m_flLastOriginChangeTime = - 1.0f; m_flAwakeTime = -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 ), DEFINE_RAGDOLL_ELEMENT( 24 ), DEFINE_RAGDOLL_ELEMENT( 25 ), DEFINE_RAGDOLL_ELEMENT( 26 ), DEFINE_RAGDOLL_ELEMENT( 27 ), DEFINE_RAGDOLL_ELEMENT( 28 ), DEFINE_RAGDOLL_ELEMENT( 29 ), DEFINE_RAGDOLL_ELEMENT( 30 ), DEFINE_RAGDOLL_ELEMENT( 31 ),
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; float radius = size.Length();
m_mins.Init(-radius,-radius,-radius); m_maxs.Init(radius,radius,radius); }
static ConVar cl_ragdoll_self_collision( "cl_ragdoll_self_collision", "1", FCVAR_DEVELOPMENTONLY ); extern ConVar cl_ragdoll_collide;
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, bool bBleedOutOnSleep ) { ragdollparams_t params; params.pGameData = static_cast<void *>( ent ); params.modelIndex = ent->GetModelIndex(); params.pCollide = modelinfo->GetVCollide( params.modelIndex );
if ( !params.pCollide ) { return; }
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 ); // forcibly disable ragdoll self collisions on the client in L4D / x360 to save perf
for ( int i = 0; i < m_ragdoll.listCount; i++ ) { m_ragdoll.list[i].pObject->SetUseAlternateGravity(true); if ( !cl_ragdoll_self_collision.GetBool() && !cl_ragdoll_collide.GetBool() ) { PhysSetGameFlags(m_ragdoll.list[i].pObject, FVPHYSICS_NO_SELF_COLLISIONS); m_ragdoll.list[i].pObject->SetCollisionHints(COLLISION_HINT_DEBRIS); } }
RagdollApplyAnimationAsVelocity( m_ragdoll, pDeltaBones0, pDeltaBones1, dt ); RagdollActivate( m_ragdoll, params.pCollide, ent->GetModelIndex() ); m_ragdoll.list[0].pObject->GetPosition( &m_origin, 0 );
// It's moving now...
m_flLastOriginChangeTime = physenv->GetSimulationTime(); m_flAwakeTime = physenv->GetSimulationTime(); m_doBleedOut = bBleedOutOnSleep;
// 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, const 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( ) { 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_ragdoll.list[0].pObject->GetPosition( &m_origin, 0 ); m_lastUpdate = gpGlobals->curtime;
Vector origin = GetRagdollOrigin(); RagdollComputeApproximateBbox( m_ragdoll, origin, m_mins, m_maxs ); m_mins -= origin; m_maxs -= origin; m_allAsleep = RagdollIsAsleep( m_ragdoll );
if ( !m_allAsleep ) { 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(); } else if ( m_doBleedOut ) { // Stopped moving, now create the blood pool
CreateBloodPool(); } }
//-----------------------------------------------------------------------------
// Purpose: 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; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : -
//-----------------------------------------------------------------------------
void CRagdoll::PhysForceRagdollToSleep() { IPhysicsObject *pList[32]; Vector vel; AngularImpulse angVel; vel.Init(); angVel.Init(); for ( int i = 0; i < m_ragdoll.listCount; i++ ) { if ( m_ragdoll.list[i].pObject ) { m_ragdoll.list[i].pObject->SetVelocity( &vel, &angVel ); pList[i] = m_ragdoll.list[i].pObject; } } physenv->ForceObjectsToSleep(pList, m_ragdoll.listCount); }
#define RAGDOLL_SLEEP_TOLERANCE 1.0f
static ConVar ragdoll_sleepaftertime( "ragdoll_sleepaftertime", "4", 0, "After this many seconds of being basically stationary, the ragdoll will go to sleep." );
void CRagdoll::CheckSettleStationaryRagdoll() { float dt = physenv->GetSimulationTime() - m_flAwakeTime; float tolerance = clamp( (RAGDOLL_SLEEP_TOLERANCE * dt), RAGDOLL_SLEEP_TOLERANCE, (RAGDOLL_SLEEP_TOLERANCE * 10) );
Vector delta = GetRagdollOrigin() - m_vecLastOrigin; for ( int i = 0; i < 3; ++i ) { // It's still moving...
if ( fabs( delta[ i ] ) > tolerance ) { m_flLastOriginChangeTime = physenv->GetSimulationTime(); m_vecLastOrigin = GetRagdollOrigin(); //Msg( "%d [%p] Still moving (tolerance is %f, dt is %f)\n", gpGlobals->tickcount, this, tolerance, dt );
return; } }
//Msg( "%d [%p] Settling - tolerance is %f, delta is %f %f %f\n", gpGlobals->tickcount, this, tolerance, delta.x, delta.y, delta.z );
// It has stopped moving, see if it
dt = physenv->GetSimulationTime() - m_flLastOriginChangeTime; if ( dt < ragdoll_sleepaftertime.GetFloat() ) return;
//Msg( "%d [%p] FORCE SLEEP\n",gpGlobals->tickcount, this );
// Force it to go to sleep
PhysForceRagdollToSleep();
// Now that it stopped, create the blood pool
CreateBloodPool(); }
void CRagdoll::ResetRagdollSleepAfterTime( void ) { m_flLastOriginChangeTime = physenv->GetSimulationTime(); m_flAwakeTime = physenv->GetSimulationTime(); }
#define ALLOW_BLOOD_POOL 0
void CRagdoll::CreateBloodPool( void ) {
#if ALLOW_BLOOD_POOL
if ( m_doBleedOut ) { DispatchParticleEffect( "blood_pool", GetRagdollOrigin(), QAngle( 0, 0, 0 ) ); m_doBleedOut = false; }
#endif
}
void CRagdoll::DrawWireframe() { IMaterial *pWireframe = materials->FindMaterial("debug/debugwireframevertexcolor", 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, bool bBleedOutOnSleep ) { CRagdoll *pRagdoll = new CRagdoll; pRagdoll->Init( ent, pstudiohdr, forceVector, forceBone, pDeltaBones0, pDeltaBones1, pCurrentBonePosition, dt, bFixedConstraints, bBleedOutOnSleep );
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 );
// Inherited from IClientUnknown
public: virtual IClientModelRenderable* GetClientModelRenderable();
virtual void PostDataUpdate( DataUpdateType_t updateType );
virtual int InternalDrawModel( int flags, const RenderableInstance_t &instance ); virtual CStudioHdr *OnNewModel( void ); virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights );
void GetRenderBounds( Vector& theMins, Vector& theMaxs ); virtual bool Simulate( void ); virtual void AccumulateLayers( IBoneSetup &boneSetup, BoneVector pos[], BoneQuaternion q[], float currentTime ); virtual void BuildTransformations( CStudioHdr *pStudioHdr, BoneVector *pos, BoneQuaternion q[], const matrix3x4_t &cameraTransform, int boneMask, CBoneBitList &boneComputed ); IPhysicsObject *GetElement( int elementNum ); virtual void UpdateOnRemove(); virtual float LastBoneChangedTime();
#if defined( _PS3 )
virtual void AccumulateLayers_AddPoseCalls( IBoneSetup_PS3 &boneSetup, BoneVector pos[], BoneQuaternion q[], float currentTime ); #endif
// 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; }
void C_ServerRagdoll::PostDataUpdate( DataUpdateType_t updateType ) { BaseClass::PostDataUpdate( updateType );
m_iv_ragPos.NoteChanged( gpGlobals->curtime, gpGlobals->curtime, true ); m_iv_ragAngles.NoteChanged( gpGlobals->curtime, gpGlobals->curtime, true ); // this is the local client time at which this update becomes stale
m_flLastBoneChangeTime = gpGlobals->curtime + GetInterpolationAmount(m_iv_ragPos.GetType()); if ( m_flBlendWeightCurrent != m_flBlendWeight ) { AddToEntityList( ENTITY_LIST_SIMULATE ); } }
float C_ServerRagdoll::LastBoneChangedTime() { return m_flLastBoneChangeTime; }
int C_ServerRagdoll::InternalDrawModel( int flags, const RenderableInstance_t &instance ) { int ret = BaseClass::InternalDrawModel( flags, instance ); if ( vcollide_wireframe.GetBool() ) { vcollide_t *pCollide = modelinfo->GetVCollide( GetModelIndex() ); IMaterial *pWireframe = materials->FindMaterial("debug/debugwireframevertexcolor", 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; 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( gpGlobals->curtime, m_elementCount ); m_iv_ragAngles.SetMaxCount( gpGlobals->curtime, m_elementCount ); }
return hdr; }
//-----------------------------------------------------------------------------
// Should we participate in the model fast path?
//-----------------------------------------------------------------------------
IClientModelRenderable* C_ServerRagdoll::GetClientModelRenderable() { // FIXME: Once we get modelrender->SetViewTarget working in the model fast
// path, we can eliminate the check for eye attachment
if ( m_iEyeAttachment > 0 ) return NULL;
return BaseClass::GetClientModelRenderable(); }
//-----------------------------------------------------------------------------
// 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 ) { memset( pFlexWeights, 0, nFlexWeightCount * sizeof(float) ); if ( pFlexDelayedWeights ) { memset( pFlexDelayedWeights, 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(); } }
bool C_ServerRagdoll::Simulate( void ) { bool bRet = BaseClass::Simulate();
// Move blend weight toward target over 0.2 seconds
m_flBlendWeightCurrent = Approach( m_flBlendWeight, m_flBlendWeightCurrent, gpGlobals->frametime * 5.0f ); if ( m_flBlendWeightCurrent != m_flBlendWeight ) bRet = true; return bRet; }
void C_ServerRagdoll::AccumulateLayers( IBoneSetup &boneSetup, BoneVector pos[], BoneQuaternion q[], float currentTime ) { BaseClass::AccumulateLayers( boneSetup, pos, q, currentTime );
if ( m_nOverlaySequence >= 0 && m_nOverlaySequence < boneSetup.GetStudioHdr()->GetNumSeq() ) { if (r_sequence_debug.GetInt() == entindex()) { DevMsgRT( "%8.4f : %30s : %5.3f : %4.2f\n", currentTime, boneSetup.GetStudioHdr()->pSeqdesc( m_nOverlaySequence ).pszLabel(), GetCycle(), m_flBlendWeightCurrent ); } boneSetup.AccumulatePose( pos, q, m_nOverlaySequence, GetCycle(), m_flBlendWeightCurrent, currentTime, m_pIk ); } }
#if defined( _PS3 )
void C_ServerRagdoll::AccumulateLayers_AddPoseCalls( IBoneSetup_PS3 &boneSetup, BoneVector pos[], BoneQuaternion q[], float currentTime ) { BaseClass::AccumulateLayers_AddPoseCalls( boneSetup, pos, q, currentTime );
if ( m_nOverlaySequence >= 0 && m_nOverlaySequence < boneSetup.GetStudioHdr()->GetNumSeq() ) { if (r_sequence_debug.GetInt() == entindex()) { DevMsgRT( "%8.4f : %30s : %5.3f : %4.2f\n", currentTime, boneSetup.GetStudioHdr()->pSeqdesc( m_nOverlaySequence ).pszLabel(), GetCycle(), m_flBlendWeightCurrent ); } // boneSetup.AccumulatePose( pos, q, m_nOverlaySequence, GetCycle(), m_flBlendWeightCurrent, currentTime, m_pIk );
boneSetup.AccumulatePose_AddToBoneJob( boneSetup.GetBoneJobSPU(), m_nOverlaySequence, GetCycle(), m_flBlendWeightCurrent, m_pIk, 0 ); } } #endif
void C_ServerRagdoll::BuildTransformations( CStudioHdr *hdr, BoneVector *pos, BoneQuaternion q[], const matrix3x4_t &cameraTransform, int boneMask, CBoneBitList &boneComputed ) { if ( !hdr ) return; matrix3x4a_t bonematrix; bool boneSimulated[MAXSTUDIOBONES];
// no bones have been simulated
memset( boneSimulated, 0, sizeof(boneSimulated) ); const 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_Aligned( 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(); }
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( matrix3x4a_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, BoneVector *pos, BoneQuaternion 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, 1 ); // 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 ); }
|