//========= 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 ) ;
}