//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
# include "cbase.h"
# include "c_baseanimating.h"
# include "c_sprite.h"
# include "model_types.h"
# include "bone_setup.h"
# include "ivrenderview.h"
# include "r_efx.h"
# include "dlight.h"
# include "beamdraw.h"
# include "cl_animevent.h"
# include "engine/IEngineSound.h"
# include "c_te_legacytempents.h"
# include "activitylist.h"
# include "animation.h"
# include "tier0/vprof.h"
# include "clienteffectprecachesystem.h"
# include "IEffects.h"
# include "engine/ivmodelinfo.h"
# include "engine/ivdebugoverlay.h"
# include "c_te_effect_dispatch.h"
# include <KeyValues.h>
# include "c_rope.h"
# include "isaverestore.h"
# include "datacache/imdlcache.h"
# include "eventlist.h"
# include "saverestore.h"
# include "physics_saverestore.h"
# include "vphysics/constraints.h"
# include "ragdoll_shared.h"
# include "view.h"
# include "c_ai_basenpc.h"
# include "c_entitydissolve.h"
# include "saverestoretypes.h"
# include "c_fire_smoke.h"
# include "input.h"
# include "soundinfo.h"
# include "tools/bonelist.h"
# include "toolframework/itoolframework.h"
# include "datacache/idatacache.h"
# include "gamestringpool.h"
# include "jigglebones.h"
# include "toolframework_client.h"
# include "vstdlib/jobthread.h"
# include "bonetoworldarray.h"
# include "posedebugger.h"
# include "tier0/icommandline.h"
# include "prediction.h"
# include "replay/replay_ragdoll.h"
# include "studio_stats.h"
# include "tier1/callqueue.h"
# ifdef TF_CLIENT_DLL
# include "c_tf_player.h"
# include "c_baseobject.h"
# endif
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
static ConVar cl_SetupAllBones ( " cl_SetupAllBones " , " 0 " ) ;
ConVar r_sequence_debug ( " r_sequence_debug " , " " ) ;
// If an NPC is moving faster than this, he should play the running footstep sound
const float RUN_SPEED_ESTIMATE_SQR = 150.0f * 150.0f ;
// Removed macro used by shared code stuff
# if defined( CBaseAnimating )
# undef CBaseAnimating
# endif
# ifdef DEBUG
static ConVar dbganimmodel ( " dbganimmodel " , " " ) ;
# endif
mstudioevent_t * GetEventIndexForSequence ( mstudioseqdesc_t & seqdesc ) ;
C_EntityDissolve * DissolveEffect ( C_BaseEntity * pTarget , float flTime ) ;
C_EntityFlame * FireEffect ( C_BaseAnimating * pTarget , C_BaseEntity * pServerFire , float * flScaleEnd , float * flTimeStart , float * flTimeEnd ) ;
bool NPC_IsImportantNPC ( C_BaseAnimating * pAnimating ) ;
void VCollideWireframe_ChangeCallback ( IConVar * pConVar , char const * pOldString , float flOldValue ) ;
ConVar vcollide_wireframe ( " vcollide_wireframe " , " 0 " , FCVAR_CHEAT , " Render physics collision models in wireframe " , VCollideWireframe_ChangeCallback ) ;
bool C_AnimationLayer : : IsActive ( void )
{
return ( m_nOrder ! = C_BaseAnimatingOverlay : : MAX_OVERLAYS ) ;
}
//-----------------------------------------------------------------------------
// Relative lighting entity
//-----------------------------------------------------------------------------
class C_InfoLightingRelative : public C_BaseEntity
{
public :
DECLARE_CLASS ( C_InfoLightingRelative , C_BaseEntity ) ;
DECLARE_CLIENTCLASS ( ) ;
void GetLightingOffset ( matrix3x4_t & offset ) ;
private :
EHANDLE m_hLightingLandmark ;
} ;
IMPLEMENT_CLIENTCLASS_DT ( C_InfoLightingRelative , DT_InfoLightingRelative , CInfoLightingRelative )
RecvPropEHandle ( RECVINFO ( m_hLightingLandmark ) ) ,
END_RECV_TABLE ( )
//-----------------------------------------------------------------------------
// Relative lighting entity
//-----------------------------------------------------------------------------
void C_InfoLightingRelative : : GetLightingOffset ( matrix3x4_t & offset )
{
if ( m_hLightingLandmark . Get ( ) )
{
matrix3x4_t matWorldToLandmark ;
MatrixInvert ( m_hLightingLandmark - > EntityToWorldTransform ( ) , matWorldToLandmark ) ;
ConcatTransforms ( EntityToWorldTransform ( ) , matWorldToLandmark , offset ) ;
}
else
{
SetIdentityMatrix ( offset ) ;
}
}
//-----------------------------------------------------------------------------
// Base Animating
//-----------------------------------------------------------------------------
struct clientanimating_t
{
C_BaseAnimating * pAnimating ;
unsigned int flags ;
clientanimating_t ( C_BaseAnimating * _pAnim , unsigned int _flags ) : pAnimating ( _pAnim ) , flags ( _flags ) { }
} ;
const unsigned int FCLIENTANIM_SEQUENCE_CYCLE = 0x00000001 ;
static CUtlVector < clientanimating_t > g_ClientSideAnimationList ;
BEGIN_RECV_TABLE_NOBASE ( C_BaseAnimating , DT_ServerAnimationData )
RecvPropFloat ( RECVINFO ( m_flCycle ) ) ,
END_RECV_TABLE ( )
void RecvProxy_Sequence ( const CRecvProxyData * pData , void * pStruct , void * pOut )
{
// Have the regular proxy store the data.
RecvProxy_Int32ToInt32 ( pData , pStruct , pOut ) ;
C_BaseAnimating * pAnimating = ( C_BaseAnimating * ) pStruct ;
if ( ! pAnimating )
return ;
pAnimating - > SetReceivedSequence ( ) ;
// render bounds may have changed
pAnimating - > UpdateVisibility ( ) ;
}
IMPLEMENT_CLIENTCLASS_DT ( C_BaseAnimating , DT_BaseAnimating , CBaseAnimating )
RecvPropInt ( RECVINFO ( m_nSequence ) , 0 , RecvProxy_Sequence ) ,
RecvPropInt ( RECVINFO ( m_nForceBone ) ) ,
RecvPropVector ( RECVINFO ( m_vecForce ) ) ,
RecvPropInt ( RECVINFO ( m_nSkin ) ) ,
RecvPropInt ( RECVINFO ( m_nBody ) ) ,
RecvPropInt ( RECVINFO ( m_nHitboxSet ) ) ,
RecvPropFloat ( RECVINFO ( m_flModelScale ) ) ,
RecvPropFloat ( RECVINFO_NAME ( m_flModelScale , m_flModelWidthScale ) ) , // for demo compatibility only
// RecvPropArray(RecvPropFloat(RECVINFO(m_flPoseParameter[0])), m_flPoseParameter),
RecvPropArray3 ( RECVINFO_ARRAY ( m_flPoseParameter ) , RecvPropFloat ( RECVINFO ( m_flPoseParameter [ 0 ] ) ) ) ,
RecvPropFloat ( RECVINFO ( m_flPlaybackRate ) ) ,
RecvPropArray3 ( RECVINFO_ARRAY ( m_flEncodedController ) , RecvPropFloat ( RECVINFO ( m_flEncodedController [ 0 ] ) ) ) ,
RecvPropInt ( RECVINFO ( m_bClientSideAnimation ) ) ,
RecvPropInt ( RECVINFO ( m_bClientSideFrameReset ) ) ,
RecvPropInt ( RECVINFO ( m_nNewSequenceParity ) ) ,
RecvPropInt ( RECVINFO ( m_nResetEventsParity ) ) ,
RecvPropInt ( RECVINFO ( m_nMuzzleFlashParity ) ) ,
RecvPropEHandle ( RECVINFO ( m_hLightingOrigin ) ) ,
RecvPropEHandle ( RECVINFO ( m_hLightingOriginRelative ) ) ,
RecvPropDataTable ( " serveranimdata " , 0 , 0 , & REFERENCE_RECV_TABLE ( DT_ServerAnimationData ) ) ,
RecvPropFloat ( RECVINFO ( m_fadeMinDist ) ) ,
RecvPropFloat ( RECVINFO ( m_fadeMaxDist ) ) ,
RecvPropFloat ( RECVINFO ( m_flFadeScale ) ) ,
END_RECV_TABLE ( )
BEGIN_PREDICTION_DATA ( C_BaseAnimating )
DEFINE_PRED_FIELD ( m_nSkin , FIELD_INTEGER , FTYPEDESC_INSENDTABLE ) ,
DEFINE_PRED_FIELD ( m_nBody , FIELD_INTEGER , FTYPEDESC_INSENDTABLE ) ,
// DEFINE_PRED_FIELD( m_nHitboxSet, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
// DEFINE_PRED_FIELD( m_flModelScale, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD ( m_nSequence , FIELD_INTEGER , FTYPEDESC_INSENDTABLE | FTYPEDESC_NOERRORCHECK ) ,
DEFINE_PRED_FIELD ( m_flPlaybackRate , FIELD_FLOAT , FTYPEDESC_INSENDTABLE | FTYPEDESC_NOERRORCHECK ) ,
DEFINE_PRED_FIELD ( m_flCycle , FIELD_FLOAT , FTYPEDESC_INSENDTABLE | FTYPEDESC_NOERRORCHECK ) ,
// DEFINE_PRED_ARRAY( m_flPoseParameter, FIELD_FLOAT, MAXSTUDIOPOSEPARAM, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_ARRAY_TOL ( m_flEncodedController , FIELD_FLOAT , MAXSTUDIOBONECTRLS , FTYPEDESC_INSENDTABLE , 0.02f ) ,
DEFINE_FIELD ( m_nPrevSequence , FIELD_INTEGER ) ,
//DEFINE_FIELD( m_flPrevEventCycle, FIELD_FLOAT ),
//DEFINE_FIELD( m_flEventCycle, FIELD_FLOAT ),
//DEFINE_FIELD( m_nEventSequence, FIELD_INTEGER ),
DEFINE_PRED_FIELD ( m_nNewSequenceParity , FIELD_INTEGER , FTYPEDESC_INSENDTABLE | FTYPEDESC_NOERRORCHECK ) ,
DEFINE_PRED_FIELD ( m_nResetEventsParity , FIELD_INTEGER , FTYPEDESC_INSENDTABLE | FTYPEDESC_NOERRORCHECK ) ,
// DEFINE_PRED_FIELD( m_nPrevResetEventsParity, FIELD_INTEGER, 0 ),
DEFINE_PRED_FIELD ( m_nMuzzleFlashParity , FIELD_CHARACTER , FTYPEDESC_INSENDTABLE ) ,
//DEFINE_FIELD( m_nOldMuzzleFlashParity, FIELD_CHARACTER ),
//DEFINE_FIELD( m_nPrevNewSequenceParity, FIELD_INTEGER ),
//DEFINE_FIELD( m_nPrevResetEventsParity, FIELD_INTEGER ),
// DEFINE_PRED_FIELD( m_vecForce, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
// DEFINE_PRED_FIELD( m_nForceBone, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
// DEFINE_PRED_FIELD( m_bClientSideAnimation, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
// DEFINE_PRED_FIELD( m_bClientSideFrameReset, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
// DEFINE_FIELD( m_pRagdollInfo, RagdollInfo_t ),
// DEFINE_FIELD( m_CachedBones, CUtlVector < CBoneCacheEntry > ),
// DEFINE_FIELD( m_pActualAttachmentAngles, FIELD_VECTOR ),
// DEFINE_FIELD( m_pActualAttachmentOrigin, FIELD_VECTOR ),
// DEFINE_FIELD( m_animationQueue, CUtlVector < C_AnimationLayer > ),
// DEFINE_FIELD( m_pIk, CIKContext ),
// DEFINE_FIELD( m_bLastClientSideFrameReset, FIELD_BOOLEAN ),
// DEFINE_FIELD( hdr, studiohdr_t ),
// DEFINE_FIELD( m_pRagdoll, IRagdoll ),
// DEFINE_FIELD( m_bStoreRagdollInfo, FIELD_BOOLEAN ),
// DEFINE_FIELD( C_BaseFlex, m_iEyeAttachment, FIELD_INTEGER ),
END_PREDICTION_DATA ( )
LINK_ENTITY_TO_CLASS ( client_ragdoll , C_ClientRagdoll ) ;
BEGIN_DATADESC ( C_ClientRagdoll )
DEFINE_FIELD ( m_bFadeOut , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_bImportant , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_iCurrentFriction , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_iMinFriction , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_iMaxFriction , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_flFrictionModTime , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_flFrictionTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_iFrictionAnimState , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_bReleaseRagdoll , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_nBody , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_nSkin , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_nRenderFX , FIELD_CHARACTER ) ,
DEFINE_FIELD ( m_nRenderMode , FIELD_CHARACTER ) ,
DEFINE_FIELD ( m_clrRender , FIELD_COLOR32 ) ,
DEFINE_FIELD ( m_flEffectTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_bFadingOut , FIELD_BOOLEAN ) ,
DEFINE_AUTO_ARRAY ( m_flScaleEnd , FIELD_FLOAT ) ,
DEFINE_AUTO_ARRAY ( m_flScaleTimeStart , FIELD_FLOAT ) ,
DEFINE_AUTO_ARRAY ( m_flScaleTimeEnd , FIELD_FLOAT ) ,
DEFINE_EMBEDDEDBYREF ( m_pRagdoll ) ,
END_DATADESC ( )
C_ClientRagdoll : : C_ClientRagdoll ( bool bRestoring )
{
m_iCurrentFriction = 0 ;
m_iFrictionAnimState = RAGDOLL_FRICTION_NONE ;
m_bReleaseRagdoll = false ;
m_bFadeOut = false ;
m_bFadingOut = false ;
m_bImportant = false ;
m_bNoModelParticles = false ;
SetClassname ( " client_ragdoll " ) ;
if ( bRestoring = = true )
{
m_pRagdoll = new CRagdoll ;
}
}
void C_ClientRagdoll : : OnSave ( void )
{
}
void C_ClientRagdoll : : OnRestore ( void )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( hdr = = NULL )
{
const char * pModelName = STRING ( GetModelName ( ) ) ;
SetModel ( pModelName ) ;
hdr = GetModelPtr ( ) ;
if ( hdr = = NULL )
return ;
}
if ( m_pRagdoll = = NULL )
return ;
ragdoll_t * pRagdollT = m_pRagdoll - > GetRagdoll ( ) ;
if ( pRagdollT = = NULL | | pRagdollT - > list [ 0 ] . pObject = = NULL )
{
m_bReleaseRagdoll = true ;
m_pRagdoll = NULL ;
Assert ( ! " Attempted to restore a ragdoll without physobjects! " ) ;
return ;
}
if ( GetFlags ( ) & FL_DISSOLVING )
{
DissolveEffect ( this , m_flEffectTime ) ;
}
else if ( GetFlags ( ) & FL_ONFIRE )
{
C_EntityFlame * pFireChild = dynamic_cast < C_EntityFlame * > ( GetEffectEntity ( ) ) ;
C_EntityFlame * pNewFireChild = FireEffect ( this , pFireChild , m_flScaleEnd , m_flScaleTimeStart , m_flScaleTimeEnd ) ;
//Set the new fire child as the new effect entity.
SetEffectEntity ( pNewFireChild ) ;
}
VPhysicsSetObject ( NULL ) ;
VPhysicsSetObject ( pRagdollT - > list [ 0 ] . pObject ) ;
SetupBones ( NULL , - 1 , BONE_USED_BY_ANYTHING , gpGlobals - > curtime ) ;
pRagdollT - > list [ 0 ] . parentIndex = - 1 ;
pRagdollT - > list [ 0 ] . originParentSpace . Init ( ) ;
RagdollActivate ( * pRagdollT , modelinfo - > GetVCollide ( GetModelIndex ( ) ) , GetModelIndex ( ) , true ) ;
RagdollSetupAnimatedFriction ( physenv , pRagdollT , GetModelIndex ( ) ) ;
m_pRagdoll - > BuildRagdollBounds ( this ) ;
// UNDONE: The shadow & leaf system cleanup should probably be in C_BaseEntity::OnRestore()
// this must be recomputed because the model was NULL when this was set up
RemoveFromLeafSystem ( ) ;
AddToLeafSystem ( RENDER_GROUP_OPAQUE_ENTITY ) ;
DestroyShadow ( ) ;
CreateShadow ( ) ;
SetNextClientThink ( CLIENT_THINK_ALWAYS ) ;
if ( m_bFadeOut = = true )
{
s_RagdollLRU . MoveToTopOfLRU ( this , m_bImportant ) ;
}
NoteRagdollCreationTick ( this ) ;
BaseClass : : OnRestore ( ) ;
RagdollMoved ( ) ;
}
void C_ClientRagdoll : : ImpactTrace ( trace_t * pTrace , int iDamageType , const char * pCustomImpactName )
{
VPROF ( " C_ClientRagdoll::ImpactTrace " ) ;
IPhysicsObject * pPhysicsObject = VPhysicsGetObject ( ) ;
if ( ! pPhysicsObject )
return ;
Vector dir = pTrace - > endpos - pTrace - > startpos ;
if ( iDamageType = = DMG_BLAST )
{
dir * = 500 ; // adjust impact strenght
// apply force at object mass center
pPhysicsObject - > ApplyForceCenter ( dir ) ;
}
else
{
Vector hitpos ;
VectorMA ( pTrace - > startpos , pTrace - > fraction , dir , hitpos ) ;
VectorNormalize ( dir ) ;
dir * = 4000 ; // adjust impact strenght
// apply force where we hit it
pPhysicsObject - > ApplyForceOffset ( dir , hitpos ) ;
}
m_pRagdoll - > ResetRagdollSleepAfterTime ( ) ;
}
ConVar g_debug_ragdoll_visualize ( " g_debug_ragdoll_visualize " , " 0 " , FCVAR_CHEAT ) ;
void C_ClientRagdoll : : HandleAnimatedFriction ( void )
{
if ( m_iFrictionAnimState = = RAGDOLL_FRICTION_OFF )
return ;
ragdoll_t * pRagdollT = NULL ;
int iBoneCount = 0 ;
if ( m_pRagdoll )
{
pRagdollT = m_pRagdoll - > GetRagdoll ( ) ;
iBoneCount = m_pRagdoll - > RagdollBoneCount ( ) ;
}
if ( pRagdollT = = NULL )
return ;
switch ( m_iFrictionAnimState )
{
case RAGDOLL_FRICTION_NONE :
{
m_iMinFriction = pRagdollT - > animfriction . iMinAnimatedFriction ;
m_iMaxFriction = pRagdollT - > animfriction . iMaxAnimatedFriction ;
if ( m_iMinFriction ! = 0 | | m_iMaxFriction ! = 0 )
{
m_iFrictionAnimState = RAGDOLL_FRICTION_IN ;
m_flFrictionModTime = pRagdollT - > animfriction . flFrictionTimeIn ;
m_flFrictionTime = gpGlobals - > curtime + m_flFrictionModTime ;
m_iCurrentFriction = m_iMinFriction ;
}
else
{
m_iFrictionAnimState = RAGDOLL_FRICTION_OFF ;
}
break ;
}
case RAGDOLL_FRICTION_IN :
{
float flDeltaTime = ( m_flFrictionTime - gpGlobals - > curtime ) ;
m_iCurrentFriction = RemapValClamped ( flDeltaTime , m_flFrictionModTime , 0 , m_iMinFriction , m_iMaxFriction ) ;
if ( flDeltaTime < = 0.0f )
{
m_flFrictionModTime = pRagdollT - > animfriction . flFrictionTimeHold ;
m_flFrictionTime = gpGlobals - > curtime + m_flFrictionModTime ;
m_iFrictionAnimState = RAGDOLL_FRICTION_HOLD ;
}
break ;
}
case RAGDOLL_FRICTION_HOLD :
{
if ( m_flFrictionTime < gpGlobals - > curtime )
{
m_flFrictionModTime = pRagdollT - > animfriction . flFrictionTimeOut ;
m_flFrictionTime = gpGlobals - > curtime + m_flFrictionModTime ;
m_iFrictionAnimState = RAGDOLL_FRICTION_OUT ;
}
break ;
}
case RAGDOLL_FRICTION_OUT :
{
float flDeltaTime = ( m_flFrictionTime - gpGlobals - > curtime ) ;
m_iCurrentFriction = RemapValClamped ( flDeltaTime , 0 , m_flFrictionModTime , m_iMinFriction , m_iMaxFriction ) ;
if ( flDeltaTime < = 0.0f )
{
m_iFrictionAnimState = RAGDOLL_FRICTION_OFF ;
}
break ;
}
}
for ( int i = 0 ; i < iBoneCount ; i + + )
{
if ( pRagdollT - > list [ i ] . pConstraint )
pRagdollT - > list [ i ] . pConstraint - > SetAngularMotor ( 0 , m_iCurrentFriction ) ;
}
IPhysicsObject * pPhysicsObject = VPhysicsGetObject ( ) ;
if ( pPhysicsObject )
{
pPhysicsObject - > Wake ( ) ;
}
}
ConVar g_ragdoll_fadespeed ( " g_ragdoll_fadespeed " , " 600 " ) ;
ConVar g_ragdoll_lvfadespeed ( " g_ragdoll_lvfadespeed " , " 100 " ) ;
void C_ClientRagdoll : : OnPVSStatusChanged ( bool bInPVS )
{
if ( bInPVS )
{
CreateShadow ( ) ;
}
else
{
DestroyShadow ( ) ;
}
}
void C_ClientRagdoll : : FadeOut ( void )
{
if ( m_bFadingOut = = false )
{
return ;
}
int iAlpha = GetRenderColor ( ) . a ;
int iFadeSpeed = ( g_RagdollLVManager . IsLowViolence ( ) ) ? g_ragdoll_lvfadespeed . GetInt ( ) : g_ragdoll_fadespeed . GetInt ( ) ;
iAlpha = MAX ( iAlpha - ( iFadeSpeed * gpGlobals - > frametime ) , 0 ) ;
SetRenderMode ( kRenderTransAlpha ) ;
SetRenderColorA ( iAlpha ) ;
if ( iAlpha = = 0 )
{
m_bReleaseRagdoll = true ;
}
}
void C_ClientRagdoll : : SUB_Remove ( void )
{
m_bFadingOut = true ;
SetNextClientThink ( CLIENT_THINK_ALWAYS ) ;
}
void C_ClientRagdoll : : ClientThink ( void )
{
if ( m_bReleaseRagdoll = = true )
{
DestroyBoneAttachments ( ) ;
Release ( ) ;
return ;
}
if ( g_debug_ragdoll_visualize . GetBool ( ) )
{
Vector vMins , vMaxs ;
Vector origin = m_pRagdoll - > GetRagdollOrigin ( ) ;
m_pRagdoll - > GetRagdollBounds ( vMins , vMaxs ) ;
debugoverlay - > AddBoxOverlay ( origin , vMins , vMaxs , QAngle ( 0 , 0 , 0 ) , 0 , 255 , 0 , 16 , 0 ) ;
}
HandleAnimatedFriction ( ) ;
FadeOut ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: clear out any face/eye values stored in the material system
//-----------------------------------------------------------------------------
float C_ClientRagdoll : : LastBoneChangedTime ( )
{
// When did this last change?
return m_pRagdoll ? m_pRagdoll - > GetLastVPhysicsUpdateTime ( ) : - FLT_MAX ;
}
//-----------------------------------------------------------------------------
// Purpose: clear out any face/eye values stored in the material system
//-----------------------------------------------------------------------------
void C_ClientRagdoll : : 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_ClientRagdoll : : Release ( void )
{
C_BaseEntity * pChild = GetEffectEntity ( ) ;
if ( pChild & & pChild - > IsMarkedForDeletion ( ) = = false )
{
pChild - > Release ( ) ;
}
if ( GetThinkHandle ( ) ! = INVALID_THINK_HANDLE )
{
ClientThinkList ( ) - > RemoveThinkable ( GetClientHandle ( ) ) ;
}
ClientEntityList ( ) . RemoveEntity ( GetClientHandle ( ) ) ;
partition - > Remove ( PARTITION_CLIENT_SOLID_EDICTS | PARTITION_CLIENT_RESPONSIVE_EDICTS | PARTITION_CLIENT_NON_STATIC_EDICTS , CollisionProp ( ) - > GetPartitionHandle ( ) ) ;
RemoveFromLeafSystem ( ) ;
BaseClass : : Release ( ) ;
}
//-----------------------------------------------------------------------------
// Incremented each frame in InvalidateModelBones. Models compare this value to what it
// was last time they setup their bones to determine if they need to re-setup their bones.
static unsigned long g_iModelBoneCounter = 0 ;
CUtlVector < C_BaseAnimating * > g_PreviousBoneSetups ;
static unsigned long g_iPreviousBoneCounter = ( unsigned ) - 1 ;
class C_BaseAnimatingGameSystem : public CAutoGameSystem
{
void LevelShutdownPostEntity ( )
{
g_iPreviousBoneCounter = ( unsigned ) - 1 ;
if ( g_PreviousBoneSetups . Count ( ) ! = 0 )
{
Msg ( " %d entities in bone setup array. Should have been cleaned up by now \n " , g_PreviousBoneSetups . Count ( ) ) ;
g_PreviousBoneSetups . RemoveAll ( ) ;
}
}
} g_BaseAnimatingGameSystem ;
//-----------------------------------------------------------------------------
// Purpose: convert axis rotations to a quaternion
//-----------------------------------------------------------------------------
C_BaseAnimating : : C_BaseAnimating ( ) :
m_iv_flCycle ( " C_BaseAnimating::m_iv_flCycle " ) ,
m_iv_flPoseParameter ( " C_BaseAnimating::m_iv_flPoseParameter " ) ,
m_iv_flEncodedController ( " C_BaseAnimating::m_iv_flEncodedController " )
{
m_vecForce . Init ( ) ;
m_nForceBone = - 1 ;
m_ClientSideAnimationListHandle = INVALID_CLIENTSIDEANIMATION_LIST_HANDLE ;
m_nPrevSequence = - 1 ;
m_nRestoreSequence = - 1 ;
m_pRagdoll = NULL ;
m_builtRagdoll = false ;
m_hitboxBoneCacheHandle = 0 ;
m_nHitboxSet = 0 ;
int i ;
for ( i = 0 ; i < ARRAYSIZE ( m_flEncodedController ) ; i + + )
{
m_flEncodedController [ i ] = 0.0f ;
}
AddBaseAnimatingInterpolatedVars ( ) ;
m_iMostRecentModelBoneCounter = 0xFFFFFFFF ;
m_iMostRecentBoneSetupRequest = g_iPreviousBoneCounter - 1 ;
m_flLastBoneSetupTime = - FLT_MAX ;
m_vecPreRagdollMins = vec3_origin ;
m_vecPreRagdollMaxs = vec3_origin ;
m_bStoreRagdollInfo = false ;
m_pRagdollInfo = NULL ;
m_pJiggleBones = NULL ;
m_pBoneMergeCache = NULL ;
m_flPlaybackRate = 1.0f ;
m_nEventSequence = - 1 ;
m_pIk = NULL ;
// Assume false. Derived classes might fill in a receive table entry
// and in that case this would show up as true
m_bClientSideAnimation = false ;
m_nPrevNewSequenceParity = - 1 ;
m_nPrevResetEventsParity = - 1 ;
m_nOldMuzzleFlashParity = 0 ;
m_nMuzzleFlashParity = 0 ;
m_flModelScale = 1.0f ;
m_iEyeAttachment = 0 ;
# ifdef _XBOX
m_iAccumulatedBoneMask = 0 ;
# endif
m_pStudioHdr = NULL ;
m_hStudioHdr = MDLHANDLE_INVALID ;
m_bReceivedSequence = false ;
m_boneIndexAttached = - 1 ;
m_flOldModelScale = 0.0f ;
m_pAttachedTo = NULL ;
m_bDynamicModelAllowed = false ;
m_bDynamicModelPending = false ;
m_bResetSequenceInfoOnLoad = false ;
Q_memset ( & m_mouth , 0 , sizeof ( m_mouth ) ) ;
m_flCycle = 0 ;
m_flOldCycle = 0 ;
}
//-----------------------------------------------------------------------------
// Purpose: cleanup
//-----------------------------------------------------------------------------
C_BaseAnimating : : ~ C_BaseAnimating ( )
{
int i = g_PreviousBoneSetups . Find ( this ) ;
if ( i ! = - 1 )
g_PreviousBoneSetups . FastRemove ( i ) ;
RemoveFromClientSideAnimationList ( ) ;
TermRopes ( ) ;
delete m_pRagdollInfo ;
Assert ( ! m_pRagdoll ) ;
delete m_pIk ;
delete m_pBoneMergeCache ;
Studio_DestroyBoneCache ( m_hitboxBoneCacheHandle ) ;
delete m_pJiggleBones ;
InvalidateMdlCache ( ) ;
// Kill off anything bone attached to us.
DestroyBoneAttachments ( ) ;
// If we are bone attached to something, remove us from the list.
if ( m_pAttachedTo )
{
m_pAttachedTo - > RemoveBoneAttachment ( this ) ;
m_pAttachedTo = NULL ;
}
}
bool C_BaseAnimating : : UsesPowerOfTwoFrameBufferTexture ( void )
{
return modelinfo - > IsUsingFBTexture ( GetModel ( ) , GetSkin ( ) , GetBody ( ) , GetClientRenderable ( ) ) ;
}
//-----------------------------------------------------------------------------
// VPhysics object
//-----------------------------------------------------------------------------
int C_BaseAnimating : : VPhysicsGetObjectList ( IPhysicsObject * * pList , int listMax )
{
if ( IsRagdoll ( ) )
{
int i ;
for ( i = 0 ; i < m_pRagdoll - > RagdollBoneCount ( ) ; + + i )
{
if ( i > = listMax )
break ;
pList [ i ] = m_pRagdoll - > GetElement ( i ) ;
}
return i ;
}
return BaseClass : : VPhysicsGetObjectList ( pList , listMax ) ;
}
//-----------------------------------------------------------------------------
// Should this object cast render-to-texture shadows?
//-----------------------------------------------------------------------------
ShadowType_t C_BaseAnimating : : ShadowCastType ( )
{
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( ! pStudioHdr | | ! pStudioHdr - > SequencesAvailable ( ) )
return SHADOWS_NONE ;
if ( IsEffectActive ( EF_NODRAW | EF_NOSHADOW ) )
return SHADOWS_NONE ;
if ( pStudioHdr - > GetNumSeq ( ) = = 0 )
return SHADOWS_RENDER_TO_TEXTURE ;
if ( ! IsRagdoll ( ) )
{
// If we have pose parameters, always update
if ( pStudioHdr - > GetNumPoseParameters ( ) > 0 )
return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC ;
// If we have bone controllers, always update
if ( pStudioHdr - > numbonecontrollers ( ) > 0 )
return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC ;
// If we use IK, always update
if ( pStudioHdr - > numikchains ( ) > 0 )
return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC ;
}
// FIXME: Do something to check to see how many frames the current animation has
// If we do this, we have to be able to handle the case of changing ShadowCastTypes
// at the moment, they are assumed to be constant.
return SHADOWS_RENDER_TO_TEXTURE ;
}
//-----------------------------------------------------------------------------
// Purpose: convert axis rotations to a quaternion
//-----------------------------------------------------------------------------
void C_BaseAnimating : : SetPredictable ( bool state )
{
BaseClass : : SetPredictable ( state ) ;
UpdateRelevantInterpolatedVars ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: sets client side animation
//-----------------------------------------------------------------------------
void C_BaseAnimating : : UseClientSideAnimation ( )
{
m_bClientSideAnimation = true ;
}
void C_BaseAnimating : : UpdateRelevantInterpolatedVars ( )
{
MDLCACHE_CRITICAL_SECTION ( ) ;
// Remove any interpolated vars that need to be removed.
if ( ! GetPredictable ( ) & & ! IsClientCreated ( ) & & GetModelPtr ( ) & & GetModelPtr ( ) - > SequencesAvailable ( ) )
{
AddBaseAnimatingInterpolatedVars ( ) ;
}
else
{
RemoveBaseAnimatingInterpolatedVars ( ) ;
}
}
void C_BaseAnimating : : AddBaseAnimatingInterpolatedVars ( )
{
AddVar ( m_flEncodedController , & m_iv_flEncodedController , LATCH_ANIMATION_VAR , true ) ;
AddVar ( m_flPoseParameter , & m_iv_flPoseParameter , LATCH_ANIMATION_VAR , true ) ;
int flags = LATCH_ANIMATION_VAR ;
if ( m_bClientSideAnimation )
flags | = EXCLUDE_AUTO_INTERPOLATE ;
AddVar ( & m_flCycle , & m_iv_flCycle , flags , true ) ;
}
void C_BaseAnimating : : RemoveBaseAnimatingInterpolatedVars ( )
{
RemoveVar ( m_flEncodedController , false ) ;
RemoveVar ( m_flPoseParameter , false ) ;
# ifdef HL2MP
// HACK: Don't want to remove interpolation for predictables in hl2dm, though
// The animation state stuff sets the pose parameters -- so they should interp
// but m_flCycle is not touched, so it's only set during prediction (which occurs on tick boundaries)
// and so needs to continue to be interpolated for smooth rendering of the lower body of the local player in third person, etc.
if ( ! GetPredictable ( ) )
# endif
{
RemoveVar ( & m_flCycle , false ) ;
}
}
void C_BaseAnimating : : LockStudioHdr ( )
{
Assert ( m_hStudioHdr = = MDLHANDLE_INVALID & & m_pStudioHdr = = NULL ) ;
AUTO_LOCK ( m_StudioHdrInitLock ) ;
if ( m_hStudioHdr ! = MDLHANDLE_INVALID | | m_pStudioHdr ! = NULL )
{
Assert ( m_pStudioHdr ? m_pStudioHdr - > GetRenderHdr ( ) = = mdlcache - > GetStudioHdr ( m_hStudioHdr ) : m_hStudioHdr = = MDLHANDLE_INVALID ) ;
return ;
}
const model_t * mdl = GetModel ( ) ;
if ( ! mdl )
return ;
m_hStudioHdr = modelinfo - > GetCacheHandle ( mdl ) ;
if ( m_hStudioHdr = = MDLHANDLE_INVALID )
return ;
const studiohdr_t * pStudioHdr = mdlcache - > LockStudioHdr ( m_hStudioHdr ) ;
if ( ! pStudioHdr )
{
m_hStudioHdr = MDLHANDLE_INVALID ;
return ;
}
CStudioHdr * pNewWrapper = new CStudioHdr ;
pNewWrapper - > Init ( pStudioHdr , mdlcache ) ;
Assert ( pNewWrapper - > IsValid ( ) ) ;
if ( pNewWrapper - > GetVirtualModel ( ) )
{
MDLHandle_t hVirtualModel = VoidPtrToMDLHandle ( pStudioHdr - > VirtualModel ( ) ) ;
mdlcache - > LockStudioHdr ( hVirtualModel ) ;
}
m_pStudioHdr = pNewWrapper ; // must be last to ensure virtual model correctly set up
}
void C_BaseAnimating : : UnlockStudioHdr ( )
{
if ( m_hStudioHdr ! = MDLHANDLE_INVALID )
{
studiohdr_t * pStudioHdr = mdlcache - > GetStudioHdr ( m_hStudioHdr ) ;
Assert ( m_pStudioHdr & & m_pStudioHdr - > GetRenderHdr ( ) = = pStudioHdr ) ;
#if 0
// XXX need to figure out where to flush the queue on map change to not crash
if ( ICallQueue * pCallQueue = materials - > GetRenderContext ( ) - > GetCallQueue ( ) )
{
// Parallel rendering: don't unlock model data until end of rendering
if ( pStudioHdr - > GetVirtualModel ( ) )
{
MDLHandle_t hVirtualModel = VoidPtrToMDLHandle ( m_pStudioHdr - > GetRenderHdr ( ) - > VirtualModel ( ) ) ;
pCallQueue - > QueueCall ( mdlcache , & IMDLCache : : UnlockStudioHdr , hVirtualModel ) ;
}
pCallQueue - > QueueCall ( mdlcache , & IMDLCache : : UnlockStudioHdr , m_hStudioHdr ) ;
}
else
# endif
{
// Immediate-mode rendering, can unlock immediately
if ( pStudioHdr - > GetVirtualModel ( ) )
{
MDLHandle_t hVirtualModel = VoidPtrToMDLHandle ( m_pStudioHdr - > GetRenderHdr ( ) - > VirtualModel ( ) ) ;
mdlcache - > UnlockStudioHdr ( hVirtualModel ) ;
}
mdlcache - > UnlockStudioHdr ( m_hStudioHdr ) ;
}
m_hStudioHdr = MDLHANDLE_INVALID ;
}
}
void C_BaseAnimating : : OnModelLoadComplete ( const model_t * pModel )
{
Assert ( m_bDynamicModelPending & & pModel = = GetModel ( ) ) ;
if ( m_bDynamicModelPending & & pModel = = GetModel ( ) )
{
m_bDynamicModelPending = false ;
OnNewModel ( ) ;
UpdateVisibility ( ) ;
}
}
void C_BaseAnimating : : ValidateModelIndex ( )
{
BaseClass : : ValidateModelIndex ( ) ;
Assert ( m_nModelIndex = = 0 | | m_AutoRefModelIndex . Get ( ) ) ;
}
CStudioHdr * C_BaseAnimating : : OnNewModel ( )
{
InvalidateMdlCache ( ) ;
// remove transition animations playback
m_SequenceTransitioner . RemoveAll ( ) ;
if ( m_pJiggleBones )
{
delete m_pJiggleBones ;
m_pJiggleBones = NULL ;
}
if ( m_bDynamicModelPending )
{
modelinfo - > UnregisterModelLoadCallback ( - 1 , this ) ;
m_bDynamicModelPending = false ;
}
m_AutoRefModelIndex . Clear ( ) ;
if ( ! GetModel ( ) | | modelinfo - > GetModelType ( GetModel ( ) ) ! = mod_studio )
return NULL ;
// Reference (and thus start loading) dynamic model
int nNewIndex = m_nModelIndex ;
if ( modelinfo - > GetModel ( nNewIndex ) ! = GetModel ( ) )
{
// XXX what's authoritative? the model pointer or the model index? what a mess.
nNewIndex = modelinfo - > GetModelIndex ( modelinfo - > GetModelName ( GetModel ( ) ) ) ;
Assert ( nNewIndex < 0 | | modelinfo - > GetModel ( nNewIndex ) = = GetModel ( ) ) ;
if ( nNewIndex < 0 )
nNewIndex = m_nModelIndex ;
}
m_AutoRefModelIndex = nNewIndex ;
if ( IsDynamicModelIndex ( nNewIndex ) & & modelinfo - > IsDynamicModelLoading ( nNewIndex ) )
{
m_bDynamicModelPending = true ;
modelinfo - > RegisterModelLoadCallback ( nNewIndex , this ) ;
}
if ( IsDynamicModelLoading ( ) )
{
// Called while dynamic model still loading -> new model, clear deferred state
m_bResetSequenceInfoOnLoad = false ;
return NULL ;
}
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( hdr = = NULL )
return NULL ;
InvalidateBoneCache ( ) ;
if ( m_pBoneMergeCache )
{
delete m_pBoneMergeCache ;
m_pBoneMergeCache = NULL ;
// recreated in BuildTransformations
}
Studio_DestroyBoneCache ( m_hitboxBoneCacheHandle ) ;
m_hitboxBoneCacheHandle = 0 ;
// Make sure m_CachedBones has space.
if ( m_CachedBoneData . Count ( ) ! = hdr - > numbones ( ) )
{
m_CachedBoneData . SetSize ( hdr - > numbones ( ) ) ;
for ( int i = 0 ; i < hdr - > numbones ( ) ; i + + )
{
SetIdentityMatrix ( m_CachedBoneData [ i ] ) ;
}
}
m_BoneAccessor . Init ( this , m_CachedBoneData . Base ( ) ) ; // Always call this in case the studiohdr_t has changed.
// Free any IK data
if ( m_pIk )
{
delete m_pIk ;
m_pIk = NULL ;
}
// Don't reallocate unless a different size.
if ( m_Attachments . Count ( ) ! = hdr - > GetNumAttachments ( ) )
{
m_Attachments . SetSize ( hdr - > GetNumAttachments ( ) ) ;
// This is to make sure we don't use the attachment before its been set up
for ( int i = 0 ; i < m_Attachments . Count ( ) ; i + + )
{
m_Attachments [ i ] . m_bAnglesComputed = false ;
m_Attachments [ i ] . m_nLastFramecount = 0 ;
# ifdef _DEBUG
m_Attachments [ i ] . m_AttachmentToWorld . Invalidate ( ) ;
m_Attachments [ i ] . m_angRotation . Init ( VEC_T_NAN , VEC_T_NAN , VEC_T_NAN ) ;
m_Attachments [ i ] . m_vOriginVelocity . Init ( VEC_T_NAN , VEC_T_NAN , VEC_T_NAN ) ;
# endif
}
}
Assert ( hdr - > GetNumPoseParameters ( ) < = ARRAYSIZE ( m_flPoseParameter ) ) ;
m_iv_flPoseParameter . SetMaxCount ( hdr - > GetNumPoseParameters ( ) ) ;
int i ;
for ( i = 0 ; i < hdr - > GetNumPoseParameters ( ) ; i + + )
{
const mstudioposeparamdesc_t & Pose = hdr - > pPoseParameter ( i ) ;
m_iv_flPoseParameter . SetLooping ( Pose . loop ! = 0.0f , i ) ;
// Note: We can't do this since if we get a DATA_UPDATE_CREATED (i.e., new entity) with both a new model and some valid pose parameters this will slam the
// pose parameters to zero and if the model goes dormant the pose parameter field will never be set to the true value. We shouldn't have to zero these out
// as they are under the control of the server and should be properly set
if ( ! IsServerEntity ( ) )
{
SetPoseParameter ( hdr , i , 0.0 ) ;
}
}
int boneControllerCount = MIN ( hdr - > numbonecontrollers ( ) , ARRAYSIZE ( m_flEncodedController ) ) ;
m_iv_flEncodedController . SetMaxCount ( boneControllerCount ) ;
for ( i = 0 ; i < boneControllerCount ; i + + )
{
bool loop = ( hdr - > pBonecontroller ( i ) - > type & ( STUDIO_XR | STUDIO_YR | STUDIO_ZR ) ) ! = 0 ;
m_iv_flEncodedController . SetLooping ( loop , i ) ;
SetBoneController ( i , 0.0 ) ;
}
InitModelEffects ( ) ;
// lookup generic eye attachment, if exists
m_iEyeAttachment = LookupAttachment ( " eyes " ) ;
// If we didn't have a model before, then we might need to go in the interpolation list now.
if ( ShouldInterpolate ( ) )
AddToInterpolationList ( ) ;
// objects with attachment points need to be queryable even if they're not solid
if ( hdr - > GetNumAttachments ( ) ! = 0 )
{
AddEFlags ( EFL_USE_PARTITION_WHEN_NOT_SOLID ) ;
}
// Most entities clear out their sequences when they change models on the server, but
// not all entities network down their m_nSequence (like multiplayer game player entities),
// so we may need to clear it out here. Force a SetSequence call no matter what, though.
int forceSequence = ShouldResetSequenceOnNewModel ( ) ? 0 : m_nSequence ;
if ( GetSequence ( ) > = hdr - > GetNumSeq ( ) )
{
forceSequence = 0 ;
}
m_nSequence = - 1 ;
SetSequence ( forceSequence ) ;
if ( m_bResetSequenceInfoOnLoad )
{
m_bResetSequenceInfoOnLoad = false ;
ResetSequenceInfo ( ) ;
}
UpdateRelevantInterpolatedVars ( ) ;
return hdr ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns index number of a given named bone
// Input : name of a bone
// Output : Bone index number or -1 if bone not found
//-----------------------------------------------------------------------------
int C_BaseAnimating : : LookupBone ( const char * szName )
{
Assert ( GetModelPtr ( ) ) ;
return Studio_BoneIndexByName ( GetModelPtr ( ) , szName ) ;
}
//=========================================================
//=========================================================
void C_BaseAnimating : : GetBonePosition ( int iBone , Vector & origin , QAngle & angles )
{
matrix3x4_t bonetoworld ;
GetBoneTransform ( iBone , bonetoworld ) ;
MatrixAngles ( bonetoworld , angles , origin ) ;
}
void C_BaseAnimating : : GetBoneTransform ( int iBone , matrix3x4_t & pBoneToWorld )
{
Assert ( GetModelPtr ( ) & & iBone > = 0 & & iBone < GetModelPtr ( ) - > numbones ( ) ) ;
CBoneCache * pcache = GetBoneCache ( NULL ) ;
matrix3x4_t * pmatrix = pcache - > GetCachedBone ( iBone ) ;
if ( ! pmatrix )
{
MatrixCopy ( EntityToWorldTransform ( ) , pBoneToWorld ) ;
return ;
}
Assert ( pmatrix ) ;
// FIXME
MatrixCopy ( * pmatrix , pBoneToWorld ) ;
}
//=============================================================================
// HPE_BEGIN:
// [menglish] Finds the bone associated with the given hitbox
//=============================================================================
int C_BaseAnimating : : GetHitboxBone ( int hitboxIndex )
{
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( pStudioHdr )
{
mstudiohitboxset_t * set = pStudioHdr - > pHitboxSet ( m_nHitboxSet ) ;
if ( set & & hitboxIndex < set - > numhitboxes )
{
return set - > pHitbox ( hitboxIndex ) - > bone ;
}
}
return 0 ;
}
//=============================================================================
// HPE_END
//=============================================================================
//-----------------------------------------------------------------------------
// Purpose: Setup to initialize our model effects once the model's loaded
//-----------------------------------------------------------------------------
void C_BaseAnimating : : InitModelEffects ( void )
{
m_bInitModelEffects = true ;
TermRopes ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Load the model's keyvalues section and create effects listed inside it
//-----------------------------------------------------------------------------
void C_BaseAnimating : : DelayedInitModelEffects ( void )
{
m_bInitModelEffects = false ;
// Parse the keyvalues and see if they want to make ropes on this model.
KeyValues * modelKeyValues = new KeyValues ( " " ) ;
if ( modelKeyValues - > LoadFromBuffer ( modelinfo - > GetModelName ( GetModel ( ) ) , modelinfo - > GetModelKeyValueText ( GetModel ( ) ) ) )
{
// Do we have a cables section?
KeyValues * pkvAllCables = modelKeyValues - > FindKey ( " Cables " ) ;
if ( pkvAllCables )
{
// Start grabbing the sounds and slotting them in
for ( KeyValues * pSingleCable = pkvAllCables - > GetFirstSubKey ( ) ; pSingleCable ; pSingleCable = pSingleCable - > GetNextKey ( ) )
{
C_RopeKeyframe * pRope = C_RopeKeyframe : : CreateFromKeyValues ( this , pSingleCable ) ;
m_Ropes . AddToTail ( pRope ) ;
}
}
if ( ! m_bNoModelParticles )
{
// Do we have a particles section?
KeyValues * pkvAllParticleEffects = modelKeyValues - > FindKey ( " Particles " ) ;
if ( pkvAllParticleEffects )
{
// Start grabbing the sounds and slotting them in
for ( KeyValues * pSingleEffect = pkvAllParticleEffects - > GetFirstSubKey ( ) ; pSingleEffect ; pSingleEffect = pSingleEffect - > GetNextKey ( ) )
{
const char * pszParticleEffect = pSingleEffect - > GetString ( " name " , " " ) ;
const char * pszAttachment = pSingleEffect - > GetString ( " attachment_point " , " " ) ;
const char * pszAttachType = pSingleEffect - > GetString ( " attachment_type " , " " ) ;
// Convert attach type
int iAttachType = GetAttachTypeFromString ( pszAttachType ) ;
if ( iAttachType = = - 1 )
{
Warning ( " Invalid attach type specified for particle effect in model '%s' keyvalues section. Trying to spawn effect '%s' with attach type of '%s' \n " , GetModelName ( ) , pszParticleEffect , pszAttachType ) ;
return ;
}
// Convert attachment point
int iAttachment = atoi ( pszAttachment ) ;
// See if we can find any attachment points matching the name
if ( pszAttachment [ 0 ] ! = ' 0 ' & & iAttachment = = 0 )
{
iAttachment = LookupAttachment ( pszAttachment ) ;
if ( iAttachment < = 0 )
{
Warning ( " Failed to find attachment point specified for particle effect in model '%s' keyvalues section. Trying to spawn effect '%s' on attachment named '%s' \n " , GetModelName ( ) , pszParticleEffect , pszAttachment ) ;
return ;
}
}
# ifdef TF_CLIENT_DLL
// Halloween Hack for Sentry Rockets
if ( ! V_strcmp ( " sentry_rocket " , pszParticleEffect ) )
{
// Halloween Spell Effect Check
int iHalloweenSpell = 0 ;
// if the owner is a Sentry, Check its owner
CBaseObject * pSentry = dynamic_cast < CBaseObject * > ( GetOwnerEntity ( ) ) ;
if ( pSentry )
{
CALL_ATTRIB_HOOK_INT_ON_OTHER ( pSentry - > GetOwner ( ) , iHalloweenSpell , halloween_pumpkin_explosions ) ;
}
else
{
CALL_ATTRIB_HOOK_INT_ON_OTHER ( GetOwnerEntity ( ) , iHalloweenSpell , halloween_pumpkin_explosions ) ;
}
if ( iHalloweenSpell > 0 )
{
pszParticleEffect = " halloween_rockettrail " ;
}
}
# endif
// Spawn the particle effect
ParticleProp ( ) - > Create ( pszParticleEffect , ( ParticleAttachment_t ) iAttachType , iAttachment ) ;
}
}
}
}
modelKeyValues - > deleteThis ( ) ;
}
void C_BaseAnimating : : TermRopes ( )
{
FOR_EACH_LL ( m_Ropes , i )
m_Ropes [ i ] - > Release ( ) ;
m_Ropes . Purge ( ) ;
}
// FIXME: redundant?
void C_BaseAnimating : : GetBoneControllers ( float controllers [ MAXSTUDIOBONECTRLS ] )
{
// interpolate two 0..1 encoded controllers to a single 0..1 controller
int i ;
for ( i = 0 ; i < MAXSTUDIOBONECTRLS ; i + + )
{
controllers [ i ] = m_flEncodedController [ i ] ;
}
}
float C_BaseAnimating : : GetPoseParameter ( int iPoseParameter )
{
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( pStudioHdr = = NULL )
return 0.0f ;
if ( pStudioHdr - > GetNumPoseParameters ( ) < iPoseParameter )
return 0.0f ;
if ( iPoseParameter < 0 )
return 0.0f ;
return m_flPoseParameter [ iPoseParameter ] ;
}
// FIXME: redundant?
void C_BaseAnimating : : GetPoseParameters ( CStudioHdr * pStudioHdr , float poseParameter [ MAXSTUDIOPOSEPARAM ] )
{
if ( ! pStudioHdr )
return ;
// interpolate pose parameters
int i ;
for ( i = 0 ; i < pStudioHdr - > GetNumPoseParameters ( ) ; i + + )
{
poseParameter [ i ] = m_flPoseParameter [ i ] ;
}
#if 0 // _DEBUG
if ( /* Q_stristr( pStudioHdr->pszName(), r_sequence_debug.GetString()) != NULL || */ r_sequence_debug . GetInt ( ) = = entindex ( ) )
{
DevMsgRT ( " %s \n " , pStudioHdr - > pszName ( ) ) ;
DevMsgRT ( " %6.2f : " , gpGlobals - > curtime ) ;
for ( i = 0 ; i < pStudioHdr - > GetNumPoseParameters ( ) ; i + + )
{
const mstudioposeparamdesc_t & Pose = pStudioHdr - > pPoseParameter ( i ) ;
DevMsgRT ( " %s %6.2f " , Pose . pszName ( ) , poseParameter [ i ] * Pose . end + ( 1 - poseParameter [ i ] ) * Pose . start ) ;
}
DevMsgRT ( " \n " ) ;
}
# endif
}
float C_BaseAnimating : : ClampCycle ( float flCycle , bool isLooping )
{
if ( isLooping )
{
// FIXME: does this work with negative framerate?
flCycle - = ( int ) flCycle ;
if ( flCycle < 0.0f )
{
flCycle + = 1.0f ;
}
}
else
{
flCycle = clamp ( flCycle , 0.0f , 0.999f ) ;
}
return flCycle ;
}
void C_BaseAnimating : : GetCachedBoneMatrix ( int boneIndex , matrix3x4_t & out )
{
MatrixCopy ( GetBone ( boneIndex ) , out ) ;
}
//-----------------------------------------------------------------------------
// Purpose: move position and rotation transforms into global matrices
//-----------------------------------------------------------------------------
void C_BaseAnimating : : BuildTransformations ( CStudioHdr * hdr , Vector * pos , Quaternion * q , const matrix3x4_t & cameraTransform , int boneMask , CBoneBitList & boneComputed )
{
VPROF_BUDGET ( " C_BaseAnimating::BuildTransformations " , VPROF_BUDGETGROUP_CLIENT_ANIMATION ) ;
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 ) ;
if ( m_pRagdoll )
{
// simulate bones and update flags
int oldWritableBones = m_BoneAccessor . GetWritableBones ( ) ;
int oldReadableBones = m_BoneAccessor . GetReadableBones ( ) ;
m_BoneAccessor . SetWritableBones ( BONE_USED_BY_ANYTHING ) ;
m_BoneAccessor . SetReadableBones ( BONE_USED_BY_ANYTHING ) ;
# if defined( REPLAY_ENABLED )
// If we're playing back a demo, override the ragdoll bones with cached version if available - otherwise, simulate.
if ( ( ! engine - > IsPlayingDemo ( ) & & ! engine - > IsPlayingTimeDemo ( ) ) | |
! CReplayRagdollCache : : Instance ( ) . IsInitialized ( ) | |
! CReplayRagdollCache : : Instance ( ) . GetFrame ( this , engine - > GetDemoPlaybackTick ( ) , boneSimulated , & m_BoneAccessor ) )
# endif
{
m_pRagdoll - > RagdollBone ( this , pbones , hdr - > numbones ( ) , boneSimulated , m_BoneAccessor ) ;
}
m_BoneAccessor . SetWritableBones ( oldWritableBones ) ;
m_BoneAccessor . SetReadableBones ( oldReadableBones ) ;
}
// For EF_BONEMERGE entities, copy the bone matrices for any bones that have matching names.
bool boneMerge = IsEffectActive ( EF_BONEMERGE ) ;
if ( boneMerge | | m_pBoneMergeCache )
{
if ( boneMerge )
{
if ( ! m_pBoneMergeCache )
{
m_pBoneMergeCache = new CBoneMergeCache ;
m_pBoneMergeCache - > Init ( this ) ;
}
m_pBoneMergeCache - > MergeMatchingBones ( boneMask ) ;
}
else
{
delete m_pBoneMergeCache ;
m_pBoneMergeCache = NULL ;
}
}
for ( int i = 0 ; i < hdr - > numbones ( ) ; i + + )
{
// Only update bones reference by the bone mask.
if ( ! ( hdr - > boneFlags ( i ) & boneMask ) )
{
continue ;
}
if ( m_pBoneMergeCache & & m_pBoneMergeCache - > IsBoneMerged ( i ) )
continue ;
// animate all non-simulated bones
if ( boneSimulated [ i ] | | CalcProceduralBone ( hdr , i , m_BoneAccessor ) )
{
continue ;
}
// skip bones that the IK has already setup
else if ( boneComputed . IsBoneMarked ( i ) )
{
// dummy operation, just used to verify in debug that this should have happened
GetBoneForWrite ( i ) ;
}
else
{
QuaternionMatrix ( q [ i ] , pos [ i ] , bonematrix ) ;
Assert ( fabs ( pos [ i ] . x ) < 100000 ) ;
Assert ( fabs ( pos [ i ] . y ) < 100000 ) ;
Assert ( fabs ( pos [ i ] . z ) < 100000 ) ;
if ( ( hdr - > boneFlags ( i ) & BONE_ALWAYS_PROCEDURAL ) & &
( hdr - > pBone ( i ) - > proctype & STUDIO_PROC_JIGGLE ) )
{
//
// Physics-based "jiggle" bone
// Bone is assumed to be along the Z axis
// Pitch around X, yaw around Y
//
// compute desired bone orientation
matrix3x4_t goalMX ;
if ( pbones [ i ] . parent = = - 1 )
{
ConcatTransforms ( cameraTransform , bonematrix , goalMX ) ;
}
else
{
ConcatTransforms ( GetBone ( pbones [ i ] . parent ) , bonematrix , goalMX ) ;
}
// get jiggle properties from QC data
mstudiojigglebone_t * jiggleInfo = ( mstudiojigglebone_t * ) pbones [ i ] . pProcedure ( ) ;
if ( ! m_pJiggleBones )
{
m_pJiggleBones = new CJiggleBones ;
}
// do jiggle physics
m_pJiggleBones - > BuildJiggleTransformations ( i , gpGlobals - > realtime , jiggleInfo , goalMX , GetBoneForWrite ( i ) ) ;
}
else if ( hdr - > boneParent ( i ) = = - 1 )
{
ConcatTransforms ( cameraTransform , bonematrix , GetBoneForWrite ( i ) ) ;
}
else
{
ConcatTransforms ( GetBone ( hdr - > boneParent ( i ) ) , bonematrix , GetBoneForWrite ( i ) ) ;
}
}
if ( hdr - > boneParent ( i ) = = - 1 )
{
// Apply client-side effects to the transformation matrix
ApplyBoneMatrixTransform ( GetBoneForWrite ( i ) ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Special effects
// Input : transform -
//-----------------------------------------------------------------------------
void C_BaseAnimating : : ApplyBoneMatrixTransform ( matrix3x4_t & transform )
{
switch ( m_nRenderFX )
{
case kRenderFxDistort :
case kRenderFxHologram :
if ( RandomInt ( 0 , 49 ) = = 0 )
{
int axis = RandomInt ( 0 , 1 ) ;
if ( axis = = 1 ) // Choose between x & z
axis = 2 ;
VectorScale ( transform [ axis ] , RandomFloat ( 1 , 1.484 ) , transform [ axis ] ) ;
}
else if ( RandomInt ( 0 , 49 ) = = 0 )
{
float offset ;
int axis = RandomInt ( 0 , 1 ) ;
if ( axis = = 1 ) // Choose between x & z
axis = 2 ;
offset = RandomFloat ( - 10 , 10 ) ;
transform [ RandomInt ( 0 , 2 ) ] [ 3 ] + = offset ;
}
break ;
case kRenderFxExplode :
{
float scale ;
scale = 1.0 + ( gpGlobals - > curtime - m_flAnimTime ) * 10.0 ;
if ( scale > 2 ) // Don't blow up more than 200%
scale = 2 ;
transform [ 0 ] [ 1 ] * = scale ;
transform [ 1 ] [ 1 ] * = scale ;
transform [ 2 ] [ 1 ] * = scale ;
}
break ;
default :
break ;
}
if ( IsModelScaled ( ) )
{
// The bone transform is in worldspace, so to scale this, we need to translate it back
float scale = GetModelScale ( ) ;
Vector pos ;
MatrixGetColumn ( transform , 3 , pos ) ;
pos - = GetRenderOrigin ( ) ;
pos * = scale ;
pos + = GetRenderOrigin ( ) ;
MatrixSetColumn ( pos , 3 , transform ) ;
VectorScale ( transform [ 0 ] , scale , transform [ 0 ] ) ;
VectorScale ( transform [ 1 ] , scale , transform [ 1 ] ) ;
VectorScale ( transform [ 2 ] , scale , transform [ 2 ] ) ;
}
}
void C_BaseAnimating : : CreateUnragdollInfo ( C_BaseAnimating * pRagdoll )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
{
return ;
}
// It's already an active ragdoll, sigh
if ( m_pRagdollInfo & & m_pRagdollInfo - > m_bActive )
{
Assert ( 0 ) ;
return ;
}
// Now do the current bone setup
pRagdoll - > SetupBones ( NULL , - 1 , BONE_USED_BY_ANYTHING , gpGlobals - > curtime ) ;
matrix3x4_t parentTransform ;
QAngle newAngles ( 0 , pRagdoll - > GetAbsAngles ( ) [ YAW ] , 0 ) ;
AngleMatrix ( GetAbsAngles ( ) , GetAbsOrigin ( ) , parentTransform ) ;
// pRagdoll->SaveRagdollInfo( hdr->numbones, parentTransform, m_BoneAccessor );
if ( ! m_pRagdollInfo )
{
m_pRagdollInfo = new RagdollInfo_t ;
Assert ( m_pRagdollInfo ) ;
if ( ! m_pRagdollInfo )
{
Msg ( " Memory allocation of RagdollInfo_t failed! \n " ) ;
return ;
}
}
Q_memset ( m_pRagdollInfo , 0 , sizeof ( * m_pRagdollInfo ) ) ;
int numbones = hdr - > numbones ( ) ;
m_pRagdollInfo - > m_bActive = true ;
m_pRagdollInfo - > m_flSaveTime = gpGlobals - > curtime ;
m_pRagdollInfo - > m_nNumBones = numbones ;
for ( int i = 0 ; i < numbones ; i + + )
{
matrix3x4_t inverted ;
matrix3x4_t output ;
if ( hdr - > boneParent ( i ) = = - 1 )
{
// Decompose into parent space
MatrixInvert ( parentTransform , inverted ) ;
}
else
{
MatrixInvert ( pRagdoll - > m_BoneAccessor . GetBone ( hdr - > boneParent ( i ) ) , inverted ) ;
}
ConcatTransforms ( inverted , pRagdoll - > m_BoneAccessor . GetBone ( i ) , output ) ;
MatrixAngles ( output ,
m_pRagdollInfo - > m_rgBoneQuaternion [ i ] ,
m_pRagdollInfo - > m_rgBonePos [ i ] ) ;
}
}
void C_BaseAnimating : : SaveRagdollInfo ( int numbones , const matrix3x4_t & cameraTransform , CBoneAccessor & pBoneToWorld )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
{
return ;
}
if ( ! m_pRagdollInfo )
{
m_pRagdollInfo = new RagdollInfo_t ;
Assert ( m_pRagdollInfo ) ;
if ( ! m_pRagdollInfo )
{
Msg ( " Memory allocation of RagdollInfo_t failed! \n " ) ;
return ;
}
memset ( m_pRagdollInfo , 0 , sizeof ( * m_pRagdollInfo ) ) ;
}
mstudiobone_t * pbones = hdr - > pBone ( 0 ) ;
m_pRagdollInfo - > m_bActive = true ;
m_pRagdollInfo - > m_flSaveTime = gpGlobals - > curtime ;
m_pRagdollInfo - > m_nNumBones = numbones ;
for ( int i = 0 ; i < numbones ; i + + )
{
matrix3x4_t inverted ;
matrix3x4_t output ;
if ( pbones [ i ] . parent = = - 1 )
{
// Decompose into parent space
MatrixInvert ( cameraTransform , inverted ) ;
}
else
{
MatrixInvert ( pBoneToWorld . GetBone ( pbones [ i ] . parent ) , inverted ) ;
}
ConcatTransforms ( inverted , pBoneToWorld . GetBone ( i ) , output ) ;
MatrixAngles ( output ,
m_pRagdollInfo - > m_rgBoneQuaternion [ i ] ,
m_pRagdollInfo - > m_rgBonePos [ i ] ) ;
}
}
bool C_BaseAnimating : : RetrieveRagdollInfo ( Vector * pos , Quaternion * q )
{
if ( ! m_bStoreRagdollInfo | | ! m_pRagdollInfo | | ! m_pRagdollInfo - > m_bActive )
return false ;
for ( int i = 0 ; i < m_pRagdollInfo - > m_nNumBones ; i + + )
{
pos [ i ] = m_pRagdollInfo - > m_rgBonePos [ i ] ;
q [ i ] = m_pRagdollInfo - > m_rgBoneQuaternion [ i ] ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Should we collide?
//-----------------------------------------------------------------------------
CollideType_t C_BaseAnimating : : GetCollideType ( void )
{
if ( IsRagdoll ( ) )
return ENTITY_SHOULD_RESPOND ;
return BaseClass : : GetCollideType ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: if the active sequence changes, keep track of the previous ones and decay them based on their decay rate
//-----------------------------------------------------------------------------
void C_BaseAnimating : : MaintainSequenceTransitions ( IBoneSetup & boneSetup , float flCycle , Vector pos [ ] , Quaternion q [ ] )
{
VPROF ( " C_BaseAnimating::MaintainSequenceTransitions " ) ;
if ( ! boneSetup . GetStudioHdr ( ) )
return ;
if ( prediction - > InPrediction ( ) )
{
m_nPrevNewSequenceParity = m_nNewSequenceParity ;
return ;
}
m_SequenceTransitioner . CheckForSequenceChange (
boneSetup . GetStudioHdr ( ) ,
GetSequence ( ) ,
m_nNewSequenceParity ! = m_nPrevNewSequenceParity ,
! IsNoInterpolationFrame ( )
) ;
m_nPrevNewSequenceParity = m_nNewSequenceParity ;
// Update the transition sequence list.
m_SequenceTransitioner . UpdateCurrent (
boneSetup . GetStudioHdr ( ) ,
GetSequence ( ) ,
flCycle ,
m_flPlaybackRate ,
gpGlobals - > curtime
) ;
// process previous sequences
for ( int i = m_SequenceTransitioner . m_animationQueue . Count ( ) - 2 ; i > = 0 ; i - - )
{
C_AnimationLayer * blend = & m_SequenceTransitioner . m_animationQueue [ i ] ;
float dt = ( gpGlobals - > curtime - blend - > m_flLayerAnimtime ) ;
flCycle = blend - > m_flCycle + dt * blend - > m_flPlaybackRate * GetSequenceCycleRate ( boneSetup . GetStudioHdr ( ) , blend - > m_nSequence ) ;
flCycle = ClampCycle ( flCycle , IsSequenceLooping ( boneSetup . GetStudioHdr ( ) , blend - > m_nSequence ) ) ;
# if 1 // _DEBUG
if ( /*Q_stristr( hdr->pszName(), r_sequence_debug.GetString()) != NULL || */ r_sequence_debug . GetInt ( ) = = entindex ( ) )
{
DevMsgRT ( " %8.4f : %30s : %5.3f : %4.2f + \n " , gpGlobals - > curtime , boneSetup . GetStudioHdr ( ) - > pSeqdesc ( blend - > m_nSequence ) . pszLabel ( ) , flCycle , ( float ) blend - > m_flWeight ) ;
}
# endif
boneSetup . AccumulatePose ( pos , q , blend - > m_nSequence , flCycle , blend - > m_flWeight , gpGlobals - > curtime , m_pIk ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *hdr -
// pos[] -
// q[] -
//-----------------------------------------------------------------------------
void C_BaseAnimating : : UnragdollBlend ( CStudioHdr * hdr , Vector pos [ ] , Quaternion q [ ] , float currentTime )
{
if ( ! hdr )
{
return ;
}
if ( ! m_pRagdollInfo | | ! m_pRagdollInfo - > m_bActive )
return ;
float dt = currentTime - m_pRagdollInfo - > m_flSaveTime ;
if ( dt > 0.2f )
{
m_pRagdollInfo - > m_bActive = false ;
return ;
}
// Slerp bone sets together
float frac = dt / 0.2f ;
frac = clamp ( frac , 0.0f , 1.0f ) ;
int i ;
for ( i = 0 ; i < hdr - > numbones ( ) ; i + + )
{
VectorLerp ( m_pRagdollInfo - > m_rgBonePos [ i ] , pos [ i ] , frac , pos [ i ] ) ;
QuaternionSlerp ( m_pRagdollInfo - > m_rgBoneQuaternion [ i ] , q [ i ] , frac , q [ i ] ) ;
}
}
void C_BaseAnimating : : AccumulateLayers ( IBoneSetup & boneSetup , Vector pos [ ] , Quaternion q [ ] , float currentTime )
{
// Nothing here
}
void C_BaseAnimating : : ChildLayerBlend ( Vector pos [ ] , Quaternion q [ ] , float currentTime , int boneMask )
{
return ;
Vector childPos [ MAXSTUDIOBONES ] ;
Quaternion childQ [ MAXSTUDIOBONES ] ;
float childPoseparam [ MAXSTUDIOPOSEPARAM ] ;
// go through all children
for ( C_BaseEntity * pChild = FirstMoveChild ( ) ; pChild ; pChild = pChild - > NextMovePeer ( ) )
{
C_BaseAnimating * pChildAnimating = pChild - > GetBaseAnimating ( ) ;
if ( pChildAnimating )
{
CStudioHdr * pChildHdr = pChildAnimating - > GetModelPtr ( ) ;
// FIXME: needs a new type of EF_BONEMERGE (EF_CHILDMERGE?)
if ( pChildHdr & & pChild - > IsEffectActive ( EF_BONEMERGE ) & & pChildHdr - > SequencesAvailable ( ) & & pChildAnimating - > m_pBoneMergeCache )
{
// FIXME: these should Inherit from the parent
GetPoseParameters ( pChildHdr , childPoseparam ) ;
IBoneSetup childBoneSetup ( pChildHdr , boneMask , childPoseparam ) ;
childBoneSetup . InitPose ( childPos , childQ ) ;
// set up the child into the parent's current pose
pChildAnimating - > m_pBoneMergeCache - > CopyParentToChild ( pos , q , childPos , childQ , boneMask ) ;
// FIXME: needs some kind of sequence
// merge over whatever bones the childs sequence modifies
childBoneSetup . AccumulatePose ( childPos , childQ , 0 , GetCycle ( ) , 1.0 , currentTime , NULL ) ;
// copy the result back into the parents bones
pChildAnimating - > m_pBoneMergeCache - > CopyChildToParent ( childPos , childQ , pos , q , boneMask ) ;
// probably needs an IK merge system of some sort =(
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Do the default sequence blending rules as done in HL1
//-----------------------------------------------------------------------------
void C_BaseAnimating : : StandardBlendingRules ( CStudioHdr * hdr , Vector pos [ ] , Quaternion q [ ] , float currentTime , int boneMask )
{
VPROF ( " C_BaseAnimating::StandardBlendingRules " ) ;
float poseparam [ MAXSTUDIOPOSEPARAM ] ;
if ( ! hdr )
return ;
if ( ! hdr - > SequencesAvailable ( ) )
{
return ;
}
if ( GetSequence ( ) > = hdr - > GetNumSeq ( ) | | GetSequence ( ) = = - 1 )
{
SetSequence ( 0 ) ;
}
GetPoseParameters ( hdr , poseparam ) ;
// build root animation
float fCycle = GetCycle ( ) ;
# if 1 //_DEBUG
if ( /* Q_stristr( hdr->pszName(), r_sequence_debug.GetString()) != NULL || */ r_sequence_debug . GetInt ( ) = = entindex ( ) )
{
DevMsgRT ( " %8.4f : %30s : %5.3f : %4.2f \n " , currentTime , hdr - > pSeqdesc ( GetSequence ( ) ) . pszLabel ( ) , fCycle , 1.0 ) ;
}
# endif
IBoneSetup boneSetup ( hdr , boneMask , poseparam ) ;
boneSetup . InitPose ( pos , q ) ;
boneSetup . AccumulatePose ( pos , q , GetSequence ( ) , fCycle , 1.0 , currentTime , m_pIk ) ;
// debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), 0, 0, "%30s %6.2f : %6.2f", hdr->pSeqdesc( GetSequence() )->pszLabel( ), fCycle, 1.0 );
MaintainSequenceTransitions ( boneSetup , fCycle , pos , q ) ;
AccumulateLayers ( boneSetup , pos , q , currentTime ) ;
CIKContext auto_ik ;
auto_ik . Init ( hdr , GetRenderAngles ( ) , GetRenderOrigin ( ) , currentTime , gpGlobals - > framecount , boneMask ) ;
boneSetup . CalcAutoplaySequences ( pos , q , currentTime , & auto_ik ) ;
if ( hdr - > numbonecontrollers ( ) )
{
float controllers [ MAXSTUDIOBONECTRLS ] ;
GetBoneControllers ( controllers ) ;
boneSetup . CalcBoneAdj ( pos , q , controllers ) ;
}
ChildLayerBlend ( pos , q , currentTime , boneMask ) ;
UnragdollBlend ( hdr , pos , q , currentTime ) ;
# ifdef STUDIO_ENABLE_PERF_COUNTERS
# if _DEBUG
if ( Q_stristr ( hdr - > pszName ( ) , r_sequence_debug . GetString ( ) ) ! = NULL )
{
DevMsgRT ( " layers %4d : bones %4d : animated %4d \n " , hdr - > m_nPerfAnimationLayers , hdr - > m_nPerfUsedBones , hdr - > m_nPerfAnimatedBones ) ;
}
# endif
# endif
}
//-----------------------------------------------------------------------------
// Purpose: Put a value into an attachment point by index
// Input : number - which point
// Output : float * - the attachment point
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : PutAttachment ( int number , const matrix3x4_t & attachmentToWorld )
{
if ( number < 1 | | number > m_Attachments . Count ( ) )
return false ;
CAttachmentData * pAtt = & m_Attachments [ number - 1 ] ;
if ( gpGlobals - > frametime > 0 & & pAtt - > m_nLastFramecount > 0 & & pAtt - > m_nLastFramecount = = gpGlobals - > framecount - 1 )
{
Vector vecPreviousOrigin , vecOrigin ;
MatrixPosition ( pAtt - > m_AttachmentToWorld , vecPreviousOrigin ) ;
MatrixPosition ( attachmentToWorld , vecOrigin ) ;
pAtt - > m_vOriginVelocity = ( vecOrigin - vecPreviousOrigin ) / gpGlobals - > frametime ;
}
else
{
pAtt - > m_vOriginVelocity . Init ( ) ;
}
pAtt - > m_nLastFramecount = gpGlobals - > framecount ;
pAtt - > m_bAnglesComputed = false ;
pAtt - > m_AttachmentToWorld = attachmentToWorld ;
# ifdef _DEBUG
pAtt - > m_angRotation . Init ( VEC_T_NAN , VEC_T_NAN , VEC_T_NAN ) ;
# endif
return true ;
}
void C_BaseAnimating : : SetupBones_AttachmentHelper ( CStudioHdr * hdr )
{
if ( ! hdr | | ! hdr - > GetNumAttachments ( ) )
return ;
// calculate attachment points
matrix3x4_t world ;
for ( int i = 0 ; i < hdr - > GetNumAttachments ( ) ; i + + )
{
const mstudioattachment_t & pattachment = hdr - > pAttachment ( i ) ;
int iBone = hdr - > GetAttachmentBone ( i ) ;
if ( ( pattachment . flags & ATTACHMENT_FLAG_WORLD_ALIGN ) = = 0 )
{
ConcatTransforms ( GetBone ( iBone ) , pattachment . local , world ) ;
}
else
{
Vector vecLocalBonePos , vecWorldBonePos ;
MatrixGetColumn ( pattachment . local , 3 , vecLocalBonePos ) ;
VectorTransform ( vecLocalBonePos , GetBone ( iBone ) , vecWorldBonePos ) ;
SetIdentityMatrix ( world ) ;
MatrixSetColumn ( vecWorldBonePos , 3 , world ) ;
}
// FIXME: this shouldn't be here, it should client side on-demand only and hooked into the bone cache!!
FormatViewModelAttachment ( i , world ) ;
PutAttachment ( i + 1 , world ) ;
}
}
bool C_BaseAnimating : : CalcAttachments ( )
{
VPROF ( " C_BaseAnimating::CalcAttachments " ) ;
// Make sure m_CachedBones is valid.
return SetupBones ( NULL , - 1 , BONE_USED_BY_ATTACHMENT , gpGlobals - > curtime ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the world location and world angles of an attachment
// Input : attachment name
// Output : location and angles
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : GetAttachment ( const char * szName , Vector & absOrigin , QAngle & absAngles )
{
return GetAttachment ( LookupAttachment ( szName ) , absOrigin , absAngles ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Get attachment point by index
// Input : number - which point
// Output : float * - the attachment point
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : GetAttachment ( int number , Vector & origin , QAngle & angles )
{
// Note: this could be more efficient, but we want the matrix3x4_t version of GetAttachment to be the origin of
// attachment generation, so a derived class that wants to fudge attachments only
// has to reimplement that version. This also makes it work like the server in that regard.
if ( number < 1 | | number > m_Attachments . Count ( ) | | ! CalcAttachments ( ) )
{
// Set this to the model origin/angles so that we don't have stack fungus in origin and angles.
origin = GetAbsOrigin ( ) ;
angles = GetAbsAngles ( ) ;
return false ;
}
CAttachmentData * pData = & m_Attachments [ number - 1 ] ;
if ( ! pData - > m_bAnglesComputed )
{
MatrixAngles ( pData - > m_AttachmentToWorld , pData - > m_angRotation ) ;
pData - > m_bAnglesComputed = true ;
}
angles = pData - > m_angRotation ;
MatrixPosition ( pData - > m_AttachmentToWorld , origin ) ;
return true ;
}
bool C_BaseAnimating : : GetAttachment ( int number , matrix3x4_t & matrix )
{
if ( number < 1 | | number > m_Attachments . Count ( ) )
return false ;
if ( ! CalcAttachments ( ) )
return false ;
matrix = m_Attachments [ number - 1 ] . m_AttachmentToWorld ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Get attachment point by index (position only)
// Input : number - which point
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : GetAttachment ( int number , Vector & origin )
{
// Note: this could be more efficient, but we want the matrix3x4_t version of GetAttachment to be the origin of
// attachment generation, so a derived class that wants to fudge attachments only
// has to reimplement that version. This also makes it work like the server in that regard.
matrix3x4_t attachmentToWorld ;
if ( ! GetAttachment ( number , attachmentToWorld ) )
{
// Set this to the model origin/angles so that we don't have stack fungus in origin and angles.
origin = GetAbsOrigin ( ) ;
return false ;
}
MatrixPosition ( attachmentToWorld , origin ) ;
return true ;
}
bool C_BaseAnimating : : GetAttachment ( const char * szName , Vector & absOrigin )
{
return GetAttachment ( LookupAttachment ( szName ) , absOrigin ) ;
}
bool C_BaseAnimating : : GetAttachmentVelocity ( int number , Vector & originVel , Quaternion & angleVel )
{
if ( number < 1 | | number > m_Attachments . Count ( ) )
{
return false ;
}
if ( ! CalcAttachments ( ) )
return false ;
originVel = m_Attachments [ number - 1 ] . m_vOriginVelocity ;
angleVel . Init ( ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Returns the attachment in local space
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : GetAttachmentLocal ( int iAttachment , matrix3x4_t & attachmentToLocal )
{
matrix3x4_t attachmentToWorld ;
if ( ! GetAttachment ( iAttachment , attachmentToWorld ) )
return false ;
matrix3x4_t worldToEntity ;
MatrixInvert ( EntityToWorldTransform ( ) , worldToEntity ) ;
ConcatTransforms ( worldToEntity , attachmentToWorld , attachmentToLocal ) ;
return true ;
}
bool C_BaseAnimating : : GetAttachmentLocal ( int iAttachment , Vector & origin , QAngle & angles )
{
matrix3x4_t attachmentToEntity ;
if ( GetAttachmentLocal ( iAttachment , attachmentToEntity ) )
{
origin . Init ( attachmentToEntity [ 0 ] [ 3 ] , attachmentToEntity [ 1 ] [ 3 ] , attachmentToEntity [ 2 ] [ 3 ] ) ;
MatrixAngles ( attachmentToEntity , angles ) ;
return true ;
}
return false ;
}
bool C_BaseAnimating : : GetAttachmentLocal ( int iAttachment , Vector & origin )
{
matrix3x4_t attachmentToEntity ;
if ( GetAttachmentLocal ( iAttachment , attachmentToEntity ) )
{
MatrixPosition ( attachmentToEntity , origin ) ;
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : GetRootBone ( matrix3x4_t & rootBone )
{
Assert ( ! IsDynamicModelLoading ( ) ) ;
if ( IsEffectActive ( EF_BONEMERGE ) & & GetMoveParent ( ) & & m_pBoneMergeCache )
return m_pBoneMergeCache - > GetRootBone ( rootBone ) ;
GetBoneTransform ( 0 , rootBone ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Move sound location to center of body
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : GetSoundSpatialization ( SpatializationInfo_t & info )
{
{
C_BaseAnimating : : AutoAllowBoneAccess boneaccess ( true , false ) ;
if ( ! BaseClass : : GetSoundSpatialization ( info ) )
return false ;
}
// move sound origin to center if npc has IK
if ( info . pOrigin & & IsNPC ( ) & & m_pIk )
{
* info . pOrigin = GetAbsOrigin ( ) ;
Vector mins , maxs , center ;
modelinfo - > GetModelBounds ( GetModel ( ) , mins , maxs ) ;
VectorAdd ( mins , maxs , center ) ;
VectorScale ( center , 0.5f , center ) ;
( * info . pOrigin ) + = center ;
}
return true ;
}
bool C_BaseAnimating : : IsViewModel ( ) const
{
return false ;
}
bool C_BaseAnimating : : IsMenuModel ( ) const
{
return false ;
}
// UNDONE: Seems kind of silly to have this when we also have the cached bones in C_BaseAnimating
CBoneCache * C_BaseAnimating : : GetBoneCache ( CStudioHdr * pStudioHdr )
{
int boneMask = BONE_USED_BY_HITBOX ;
CBoneCache * pcache = Studio_GetBoneCache ( m_hitboxBoneCacheHandle ) ;
if ( pcache )
{
if ( pcache - > IsValid ( gpGlobals - > curtime , 0.0 ) )
{
// in memory and still valid, use it!
return pcache ;
}
// in memory, but not the same bone set, destroy & rebuild
if ( ( pcache - > m_boneMask & boneMask ) ! = boneMask )
{
Studio_DestroyBoneCache ( m_hitboxBoneCacheHandle ) ;
m_hitboxBoneCacheHandle = 0 ;
pcache = NULL ;
}
}
if ( ! pStudioHdr )
pStudioHdr = GetModelPtr ( ) ;
Assert ( pStudioHdr ) ;
C_BaseAnimating : : PushAllowBoneAccess ( true , false , " GetBoneCache " ) ;
SetupBones ( NULL , - 1 , boneMask , gpGlobals - > curtime ) ;
C_BaseAnimating : : PopBoneAccess ( " GetBoneCache " ) ;
if ( pcache )
{
// still in memory but out of date, refresh the bones.
pcache - > UpdateBones ( m_CachedBoneData . Base ( ) , pStudioHdr - > numbones ( ) , gpGlobals - > curtime ) ;
}
else
{
bonecacheparams_t params ;
params . pStudioHdr = pStudioHdr ;
// HACKHACK: We need the pointer to all bones here
params . pBoneToWorld = m_CachedBoneData . Base ( ) ;
params . curtime = gpGlobals - > curtime ;
params . boneMask = boneMask ;
m_hitboxBoneCacheHandle = Studio_CreateBoneCache ( params ) ;
pcache = Studio_GetBoneCache ( m_hitboxBoneCacheHandle ) ;
}
Assert ( pcache ) ;
return pcache ;
}
class CTraceFilterSkipNPCsAndPlayers : public CTraceFilterSimple
{
public :
CTraceFilterSkipNPCsAndPlayers ( const IHandleEntity * passentity , int collisionGroup )
: CTraceFilterSimple ( passentity , collisionGroup )
{
}
virtual bool ShouldHitEntity ( IHandleEntity * pServerEntity , int contentsMask )
{
if ( CTraceFilterSimple : : ShouldHitEntity ( pServerEntity , contentsMask ) )
{
C_BaseEntity * pEntity = EntityFromEntityHandle ( pServerEntity ) ;
if ( ! pEntity )
return true ;
if ( pEntity - > IsNPC ( ) | | pEntity - > IsPlayer ( ) )
return false ;
return true ;
}
return false ;
}
} ;
/*
void drawLine ( const Vector & origin , const Vector & dest , int r , int g , int b , bool noDepthTest , float duration )
{
debugoverlay - > AddLineOverlay ( origin , dest , r , g , b , noDepthTest , duration ) ;
}
*/
//-----------------------------------------------------------------------------
// Purpose: update latched IK contacts if they're in a moving reference frame.
//-----------------------------------------------------------------------------
void C_BaseAnimating : : UpdateIKLocks ( float currentTime )
{
if ( ! m_pIk )
return ;
int targetCount = m_pIk - > m_target . Count ( ) ;
if ( targetCount = = 0 )
return ;
for ( int i = 0 ; i < targetCount ; i + + )
{
CIKTarget * pTarget = & m_pIk - > m_target [ i ] ;
if ( ! pTarget - > IsActive ( ) )
continue ;
if ( pTarget - > GetOwner ( ) ! = - 1 )
{
C_BaseEntity * pOwner = cl_entitylist - > GetEnt ( pTarget - > GetOwner ( ) ) ;
if ( pOwner ! = NULL )
{
pTarget - > UpdateOwner ( pOwner - > entindex ( ) , pOwner - > GetAbsOrigin ( ) , pOwner - > GetAbsAngles ( ) ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Find the ground or external attachment points needed by IK rules
//-----------------------------------------------------------------------------
void C_BaseAnimating : : CalculateIKLocks ( float currentTime )
{
if ( ! m_pIk )
return ;
int targetCount = m_pIk - > m_target . Count ( ) ;
if ( targetCount = = 0 )
return ;
// In TF, we might be attaching a player's view to a walking model that's using IK. If we are, it can
// get in here during the view setup code, and it's not normally supposed to be able to access the spatial
// partition that early in the rendering loop. So we allow access right here for that special case.
SpatialPartitionListMask_t curSuppressed = partition - > GetSuppressedLists ( ) ;
partition - > SuppressLists ( PARTITION_ALL_CLIENT_EDICTS , false ) ;
CBaseEntity : : PushEnableAbsRecomputations ( false ) ;
Ray_t ray ;
CTraceFilterSkipNPCsAndPlayers traceFilter ( this , GetCollisionGroup ( ) ) ;
// FIXME: trace based on gravity or trace based on angles?
Vector up ;
AngleVectors ( GetRenderAngles ( ) , NULL , NULL , & up ) ;
// FIXME: check number of slots?
float minHeight = FLT_MAX ;
float maxHeight = - FLT_MAX ;
for ( int i = 0 ; i < targetCount ; i + + )
{
trace_t trace ;
CIKTarget * pTarget = & m_pIk - > m_target [ i ] ;
if ( ! pTarget - > IsActive ( ) )
continue ;
switch ( pTarget - > type )
{
case IK_GROUND :
{
Vector estGround ;
Vector p1 , p2 ;
// adjust ground to original ground position
estGround = ( pTarget - > est . pos - GetRenderOrigin ( ) ) ;
estGround = estGround - ( estGround * up ) * up ;
estGround = GetAbsOrigin ( ) + estGround + pTarget - > est . floor * up ;
VectorMA ( estGround , pTarget - > est . height , up , p1 ) ;
VectorMA ( estGround , - pTarget - > est . height , up , p2 ) ;
float r = MAX ( pTarget - > est . radius , 1 ) ;
// don't IK to other characters
ray . Init ( p1 , p2 , Vector ( - r , - r , 0 ) , Vector ( r , r , r * 2 ) ) ;
enginetrace - > TraceRay ( ray , PhysicsSolidMaskForEntity ( ) , & traceFilter , & trace ) ;
if ( trace . m_pEnt ! = NULL & & trace . m_pEnt - > GetMoveType ( ) = = MOVETYPE_PUSH )
{
pTarget - > SetOwner ( trace . m_pEnt - > entindex ( ) , trace . m_pEnt - > GetAbsOrigin ( ) , trace . m_pEnt - > GetAbsAngles ( ) ) ;
}
else
{
pTarget - > ClearOwner ( ) ;
}
if ( trace . startsolid )
{
// trace from back towards hip
Vector tmp = estGround - pTarget - > trace . closest ;
tmp . NormalizeInPlace ( ) ;
ray . Init ( estGround - tmp * pTarget - > est . height , estGround , Vector ( - r , - r , 0 ) , Vector ( r , r , 1 ) ) ;
// debugoverlay->AddLineOverlay( ray.m_Start, ray.m_Start + ray.m_Delta, 255, 0, 0, 0, 0 );
enginetrace - > TraceRay ( ray , MASK_SOLID , & traceFilter , & trace ) ;
if ( ! trace . startsolid )
{
p1 = trace . endpos ;
VectorMA ( p1 , - pTarget - > est . height , up , p2 ) ;
ray . Init ( p1 , p2 , Vector ( - r , - r , 0 ) , Vector ( r , r , 1 ) ) ;
enginetrace - > TraceRay ( ray , MASK_SOLID , & traceFilter , & trace ) ;
}
// debugoverlay->AddLineOverlay( ray.m_Start, ray.m_Start + ray.m_Delta, 0, 255, 0, 0, 0 );
}
if ( ! trace . startsolid )
{
if ( trace . DidHitWorld ( ) )
{
// clamp normal to 33 degrees
const float limit = 0.832 ;
float dot = DotProduct ( trace . plane . normal , up ) ;
if ( dot < limit )
{
Assert ( dot > = 0 ) ;
// subtract out up component
Vector diff = trace . plane . normal - up * dot ;
// scale remainder such that it and the up vector are a unit vector
float d = sqrt ( ( 1 - limit * limit ) / DotProduct ( diff , diff ) ) ;
trace . plane . normal = up * limit + d * diff ;
}
// FIXME: this is wrong with respect to contact position and actual ankle offset
pTarget - > SetPosWithNormalOffset ( trace . endpos , trace . plane . normal ) ;
pTarget - > SetNormal ( trace . plane . normal ) ;
pTarget - > SetOnWorld ( true ) ;
// only do this on forward tracking or commited IK ground rules
if ( pTarget - > est . release < 0.1 )
{
// keep track of ground height
float offset = DotProduct ( pTarget - > est . pos , up ) ;
if ( minHeight > offset )
minHeight = offset ;
if ( maxHeight < offset )
maxHeight = offset ;
}
// FIXME: if we don't drop legs, running down hills looks horrible
/*
if ( DotProduct ( pTarget - > est . pos , up ) < DotProduct ( estGround , up ) )
{
pTarget - > est . pos = estGround ;
}
*/
}
else if ( trace . DidHitNonWorldEntity ( ) )
{
pTarget - > SetPos ( trace . endpos ) ;
pTarget - > SetAngles ( GetRenderAngles ( ) ) ;
// only do this on forward tracking or commited IK ground rules
if ( pTarget - > est . release < 0.1 )
{
float offset = DotProduct ( pTarget - > est . pos , up ) ;
if ( minHeight > offset )
minHeight = offset ;
if ( maxHeight < offset )
maxHeight = offset ;
}
// FIXME: if we don't drop legs, running down hills looks horrible
/*
if ( DotProduct ( pTarget - > est . pos , up ) < DotProduct ( estGround , up ) )
{
pTarget - > est . pos = estGround ;
}
*/
}
else
{
pTarget - > IKFailed ( ) ;
}
}
else
{
if ( ! trace . DidHitWorld ( ) )
{
pTarget - > IKFailed ( ) ;
}
else
{
pTarget - > SetPos ( trace . endpos ) ;
pTarget - > SetAngles ( GetRenderAngles ( ) ) ;
pTarget - > SetOnWorld ( true ) ;
}
}
/*
debugoverlay - > AddTextOverlay ( p1 , i , 0 , " %d %.1f %.1f %.1f " , i ,
pTarget - > latched . deltaPos . x , pTarget - > latched . deltaPos . y , pTarget - > latched . deltaPos . z ) ;
debugoverlay - > AddBoxOverlay ( pTarget - > est . pos , Vector ( - r , - r , - 1 ) , Vector ( r , r , 1 ) , QAngle ( 0 , 0 , 0 ) , 255 , 0 , 0 , 0 , 0 ) ;
*/
// debugoverlay->AddBoxOverlay( pTarget->latched.pos, Vector( -2, -2, 2 ), Vector( 2, 2, 6), QAngle( 0, 0, 0 ), 0, 255, 0, 0, 0 );
}
break ;
case IK_ATTACHMENT :
{
C_BaseEntity * pEntity = NULL ;
float flDist = pTarget - > est . radius ;
// FIXME: make entity finding sticky!
// FIXME: what should the radius check be?
for ( CEntitySphereQuery sphere ( pTarget - > est . pos , 64 ) ; ( pEntity = sphere . GetCurrentEntity ( ) ) ! = NULL ; sphere . NextEntity ( ) )
{
C_BaseAnimating * pAnim = pEntity - > GetBaseAnimating ( ) ;
if ( ! pAnim )
continue ;
int iAttachment = pAnim - > LookupAttachment ( pTarget - > offset . pAttachmentName ) ;
if ( iAttachment < = 0 )
continue ;
Vector origin ;
QAngle angles ;
pAnim - > GetAttachment ( iAttachment , origin , angles ) ;
// debugoverlay->AddBoxOverlay( origin, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), QAngle( 0, 0, 0 ), 255, 0, 0, 0, 0 );
float d = ( pTarget - > est . pos - origin ) . Length ( ) ;
if ( d > = flDist )
continue ;
flDist = d ;
pTarget - > SetPos ( origin ) ;
pTarget - > SetAngles ( angles ) ;
// debugoverlay->AddBoxOverlay( pTarget->est.pos, Vector( -pTarget->est.radius, -pTarget->est.radius, -pTarget->est.radius ), Vector( pTarget->est.radius, pTarget->est.radius, pTarget->est.radius), QAngle( 0, 0, 0 ), 0, 255, 0, 0, 0 );
}
if ( flDist > = pTarget - > est . radius )
{
// debugoverlay->AddBoxOverlay( pTarget->est.pos, Vector( -pTarget->est.radius, -pTarget->est.radius, -pTarget->est.radius ), Vector( pTarget->est.radius, pTarget->est.radius, pTarget->est.radius), QAngle( 0, 0, 0 ), 0, 0, 255, 0, 0 );
// no solution, disable ik rule
pTarget - > IKFailed ( ) ;
}
}
break ;
}
}
# if defined( HL2_CLIENT_DLL )
if ( minHeight < FLT_MAX )
{
input - > AddIKGroundContactInfo ( entindex ( ) , minHeight , maxHeight ) ;
}
# endif
CBaseEntity : : PopEnableAbsRecomputations ( ) ;
partition - > SuppressLists ( curSuppressed , true ) ;
}
bool C_BaseAnimating : : GetPoseParameterRange ( int index , float & minValue , float & maxValue )
{
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( pStudioHdr )
{
if ( index > = 0 & & index < pStudioHdr - > GetNumPoseParameters ( ) )
{
const mstudioposeparamdesc_t & pose = pStudioHdr - > pPoseParameter ( index ) ;
minValue = pose . start ;
maxValue = pose . end ;
return true ;
}
}
minValue = 0.0f ;
maxValue = 1.0f ;
return false ;
}
//-----------------------------------------------------------------------------
// Purpose: Do HL1 style lipsynch
//-----------------------------------------------------------------------------
void C_BaseAnimating : : ControlMouth ( CStudioHdr * pstudiohdr )
{
if ( ! MouthInfo ( ) . NeedsEnvelope ( ) )
return ;
if ( ! pstudiohdr )
return ;
int index = LookupPoseParameter ( pstudiohdr , LIPSYNC_POSEPARAM_NAME ) ;
if ( index ! = - 1 )
{
float value = GetMouth ( ) - > mouthopen / 64.0 ;
float raw = value ;
if ( value > 1.0 )
value = 1.0 ;
float start , end ;
GetPoseParameterRange ( index , start , end ) ;
value = ( 1.0 - value ) * start + value * end ;
//Adrian - Set the pose parameter value.
//It has to be called "mouth".
SetPoseParameter ( pstudiohdr , index , value ) ;
// Reset interpolation here since the client is controlling this rather than the server...
m_iv_flPoseParameter . SetHistoryValuesForItem ( index , raw ) ;
}
}
CMouthInfo * C_BaseAnimating : : GetMouth ( void )
{
return & m_mouth ;
}
# ifdef DEBUG_BONE_SETUP_THREADING
ConVar cl_warn_thread_contested_bone_setup ( " cl_warn_thread_contested_bone_setup " , " 0 " ) ;
# endif
//ConVar cl_threaded_bone_setup("cl_threaded_bone_setup", "1", 0, "Enable parallel processing of C_BaseAnimating::SetupBones()" );
//-----------------------------------------------------------------------------
// Purpose: Do the default sequence blending rules as done in HL1
//-----------------------------------------------------------------------------
static void SetupBonesOnBaseAnimating ( C_BaseAnimating * & pBaseAnimating )
{
if ( ! pBaseAnimating - > GetMoveParent ( ) )
pBaseAnimating - > SetupBones ( NULL , - 1 , - 1 , gpGlobals - > curtime ) ;
}
static void PreThreadedBoneSetup ( )
{
mdlcache - > BeginLock ( ) ;
}
static void PostThreadedBoneSetup ( )
{
mdlcache - > EndLock ( ) ;
}
static bool g_bInThreadedBoneSetup ;
static bool g_bDoThreadedBoneSetup ;
void C_BaseAnimating : : InitBoneSetupThreadPool ( )
{
}
void C_BaseAnimating : : ShutdownBoneSetupThreadPool ( )
{
}
void C_BaseAnimating : : ThreadedBoneSetup ( )
{
g_bDoThreadedBoneSetup = true ; //cl_threaded_bone_setup.GetBool();
if ( g_bDoThreadedBoneSetup )
{
int nCount = g_PreviousBoneSetups . Count ( ) ;
if ( nCount > 1 )
{
g_bInThreadedBoneSetup = true ;
ParallelProcess ( " C_BaseAnimating::ThreadedBoneSetup " , g_PreviousBoneSetups . Base ( ) , nCount , & SetupBonesOnBaseAnimating , & PreThreadedBoneSetup , & PostThreadedBoneSetup ) ;
g_bInThreadedBoneSetup = false ;
}
}
g_iPreviousBoneCounter + + ;
g_PreviousBoneSetups . RemoveAll ( ) ;
}
bool C_BaseAnimating : : SetupBones ( matrix3x4_t * pBoneToWorldOut , int nMaxBones , int boneMask , float currentTime )
{
VPROF_BUDGET ( " C_BaseAnimating::SetupBones " , VPROF_BUDGETGROUP_CLIENT_ANIMATION ) ;
//=============================================================================
// HPE_BEGIN:
// [pfreese] Added the check for pBoneToWorldOut != NULL in this debug warning
// code. SetupBones is called in the CSS anytime an attachment wants its
// parent's transform, hence this warning is hit extremely frequently.
// I'm not actually sure if this is the right "fix" for this, as the bones are
// actually accessed as part of the setup process, but since I'm not clear on the
// purpose of this dev warning, I'm including this comment block.
//=============================================================================
if ( pBoneToWorldOut ! = NULL & & ! IsBoneAccessAllowed ( ) )
{
static float lastWarning = 0.0f ;
// Prevent spammage!!!
if ( gpGlobals - > realtime > = lastWarning + 1.0f )
{
DevMsgRT ( " *** ERROR: Bone access not allowed (entity %i:%s) \n " , index , GetClassname ( ) ) ;
lastWarning = gpGlobals - > realtime ;
}
}
//boneMask = BONE_USED_BY_ANYTHING; // HACK HACK - this is a temp fix until we have accessors for bones to find out where problems are.
if ( GetSequence ( ) = = - 1 )
return false ;
if ( boneMask = = - 1 )
{
boneMask = m_iPrevBoneMask ;
}
// We should get rid of this someday when we have solutions for the odd cases where a bone doesn't
// get setup and its transform is asked for later.
if ( cl_SetupAllBones . GetInt ( ) )
{
boneMask | = BONE_USED_BY_ANYTHING ;
}
// Set up all bones if recording, too
if ( IsToolRecording ( ) )
{
boneMask | = BONE_USED_BY_ANYTHING ;
}
if ( g_bInThreadedBoneSetup )
{
if ( ! m_BoneSetupLock . TryLock ( ) )
{
return false ;
}
}
# ifdef DEBUG_BONE_SETUP_THREADING
if ( cl_warn_thread_contested_bone_setup . GetBool ( ) )
{
if ( ! m_BoneSetupLock . TryLock ( ) )
{
Msg ( " Contested bone setup in frame %d! \n " , gpGlobals - > framecount ) ;
}
else
{
m_BoneSetupLock . Unlock ( ) ;
}
}
# endif
AUTO_LOCK ( m_BoneSetupLock ) ;
if ( g_bInThreadedBoneSetup )
{
m_BoneSetupLock . Unlock ( ) ;
}
if ( m_iMostRecentModelBoneCounter ! = g_iModelBoneCounter )
{
// Clear out which bones we've touched this frame if this is
// the first time we've seen this object this frame.
if ( LastBoneChangedTime ( ) > = m_flLastBoneSetupTime )
{
m_BoneAccessor . SetReadableBones ( 0 ) ;
m_BoneAccessor . SetWritableBones ( 0 ) ;
m_flLastBoneSetupTime = currentTime ;
}
m_iPrevBoneMask = m_iAccumulatedBoneMask ;
m_iAccumulatedBoneMask = 0 ;
# ifdef STUDIO_ENABLE_PERF_COUNTERS
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( hdr )
{
hdr - > ClearPerfCounters ( ) ;
}
# endif
}
int nBoneCount = m_CachedBoneData . Count ( ) ;
if ( g_bDoThreadedBoneSetup & & ! g_bInThreadedBoneSetup & & ( nBoneCount > = 16 ) & & ! GetMoveParent ( ) & & m_iMostRecentBoneSetupRequest ! = g_iPreviousBoneCounter )
{
m_iMostRecentBoneSetupRequest = g_iPreviousBoneCounter ;
Assert ( g_PreviousBoneSetups . Find ( this ) = = - 1 ) ;
g_PreviousBoneSetups . AddToTail ( this ) ;
}
// Keep track of everthing asked for over the entire frame
m_iAccumulatedBoneMask | = boneMask ;
// Make sure that we know that we've already calculated some bone stuff this time around.
m_iMostRecentModelBoneCounter = g_iModelBoneCounter ;
// Have we cached off all bones meeting the flag set?
if ( ( m_BoneAccessor . GetReadableBones ( ) & boneMask ) ! = boneMask )
{
MDLCACHE_CRITICAL_SECTION ( ) ;
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr | | ! hdr - > SequencesAvailable ( ) )
return false ;
// Setup our transform based on render angles and origin.
matrix3x4_t parentTransform ;
AngleMatrix ( GetRenderAngles ( ) , GetRenderOrigin ( ) , parentTransform ) ;
// Load the boneMask with the total of what was asked for last frame.
boneMask | = m_iPrevBoneMask ;
// Allow access to the bones we're setting up so we don't get asserts in here.
int oldReadableBones = m_BoneAccessor . GetReadableBones ( ) ;
m_BoneAccessor . SetWritableBones ( m_BoneAccessor . GetReadableBones ( ) | boneMask ) ;
m_BoneAccessor . SetReadableBones ( m_BoneAccessor . GetWritableBones ( ) ) ;
if ( hdr - > flags ( ) & STUDIOHDR_FLAGS_STATIC_PROP )
{
MatrixCopy ( parentTransform , GetBoneForWrite ( 0 ) ) ;
}
else
{
TrackBoneSetupEnt ( this ) ;
// This is necessary because it's possible that CalculateIKLocks will trigger our move children
// to call GetAbsOrigin(), and they'll use our OLD bone transforms to get their attachments
// since we're right in the middle of setting up our new transforms.
//
// Setting this flag forces move children to keep their abs transform invalidated.
AddFlag ( EFL_SETTING_UP_BONES ) ;
// NOTE: For model scaling, we need to opt out of IK because it will mark the bones as already being calculated
if ( ! IsModelScaled ( ) )
{
// only allocate an ik block if the npc can use it
if ( ! m_pIk & & hdr - > numikchains ( ) > 0 & & ! ( m_EntClientFlags & ENTCLIENTFLAG_DONTUSEIK ) )
{
m_pIk = new CIKContext ;
}
}
else
{
// Reset the IK
if ( m_pIk )
{
delete m_pIk ;
m_pIk = NULL ;
}
}
Vector pos [ MAXSTUDIOBONES ] ;
Quaternion q [ MAXSTUDIOBONES ] ;
# if defined(FP_EXCEPTIONS_ENABLED) || defined(DBGFLAG_ASSERT)
// Having these uninitialized means that some bugs are very hard
// to reproduce. A memset of 0xFF is a simple way of getting NaNs.
memset ( pos , 0xFF , sizeof ( pos ) ) ;
memset ( q , 0xFF , sizeof ( q ) ) ;
# endif
int bonesMaskNeedRecalc = boneMask | oldReadableBones ; // Hack to always recalc bones, to fix the arm jitter in the new CS player anims until Ken makes the real fix
if ( m_pIk )
{
if ( Teleported ( ) | | IsNoInterpolationFrame ( ) )
m_pIk - > ClearTargets ( ) ;
m_pIk - > Init ( hdr , GetRenderAngles ( ) , GetRenderOrigin ( ) , currentTime , gpGlobals - > framecount , bonesMaskNeedRecalc ) ;
}
// Let pose debugger know that we are blending
g_pPoseDebugger - > StartBlending ( this , hdr ) ;
StandardBlendingRules ( hdr , pos , q , currentTime , bonesMaskNeedRecalc ) ;
CBoneBitList boneComputed ;
// don't calculate IK on ragdolls
if ( m_pIk & & ! IsRagdoll ( ) )
{
UpdateIKLocks ( currentTime ) ;
m_pIk - > UpdateTargets ( pos , q , m_BoneAccessor . GetBoneArrayForWrite ( ) , boneComputed ) ;
CalculateIKLocks ( currentTime ) ;
m_pIk - > SolveDependencies ( pos , q , m_BoneAccessor . GetBoneArrayForWrite ( ) , boneComputed ) ;
}
BuildTransformations ( hdr , pos , q , parentTransform , bonesMaskNeedRecalc , boneComputed ) ;
RemoveFlag ( EFL_SETTING_UP_BONES ) ;
ControlMouth ( hdr ) ;
}
if ( ! ( oldReadableBones & BONE_USED_BY_ATTACHMENT ) & & ( boneMask & BONE_USED_BY_ATTACHMENT ) )
{
SetupBones_AttachmentHelper ( hdr ) ;
}
}
// Do they want to get at the bone transforms? If it's just making sure an aiment has
// its bones setup, it doesn't need the transforms yet.
if ( pBoneToWorldOut )
{
if ( nMaxBones > = m_CachedBoneData . Count ( ) )
{
memcpy ( pBoneToWorldOut , m_CachedBoneData . Base ( ) , sizeof ( matrix3x4_t ) * m_CachedBoneData . Count ( ) ) ;
}
else
{
Warning ( " SetupBones: invalid bone array size (%d - needs %d) \n " , nMaxBones , m_CachedBoneData . Count ( ) ) ;
return false ;
}
}
return true ;
}
C_BaseAnimating * C_BaseAnimating : : FindFollowedEntity ( )
{
C_BaseEntity * follow = GetFollowedEntity ( ) ;
if ( ! follow )
return NULL ;
if ( follow - > IsDormant ( ) )
return NULL ;
if ( ! follow - > GetModel ( ) )
{
Warning ( " mod_studio: MOVETYPE_FOLLOW with no model. \n " ) ;
return NULL ;
}
if ( modelinfo - > GetModelType ( follow - > GetModel ( ) ) ! = mod_studio )
{
Warning ( " Attached %s (mod_studio) to %s (%d) \n " ,
modelinfo - > GetModelName ( GetModel ( ) ) ,
modelinfo - > GetModelName ( follow - > GetModel ( ) ) ,
modelinfo - > GetModelType ( follow - > GetModel ( ) ) ) ;
return NULL ;
}
return assert_cast < C_BaseAnimating * > ( follow ) ;
}
void C_BaseAnimating : : InvalidateBoneCache ( )
{
m_iMostRecentModelBoneCounter = g_iModelBoneCounter - 1 ;
m_flLastBoneSetupTime = - FLT_MAX ;
}
bool C_BaseAnimating : : IsBoneCacheValid ( ) const
{
return m_iMostRecentModelBoneCounter = = g_iModelBoneCounter ;
}
// Causes an assert to happen if bones or attachments are used while this is false.
struct BoneAccess
{
BoneAccess ( )
{
bAllowBoneAccessForNormalModels = false ;
bAllowBoneAccessForViewModels = false ;
tag = NULL ;
}
bool bAllowBoneAccessForNormalModels ;
bool bAllowBoneAccessForViewModels ;
char const * tag ;
} ;
static CUtlVector < BoneAccess > g_BoneAccessStack ;
static BoneAccess g_BoneAcessBase ;
bool C_BaseAnimating : : IsBoneAccessAllowed ( ) const
{
if ( IsViewModel ( ) )
return g_BoneAcessBase . bAllowBoneAccessForViewModels ;
else
return g_BoneAcessBase . bAllowBoneAccessForNormalModels ;
}
// (static function)
void C_BaseAnimating : : PushAllowBoneAccess ( bool bAllowForNormalModels , bool bAllowForViewModels , char const * tagPush )
{
BoneAccess save = g_BoneAcessBase ;
g_BoneAccessStack . AddToTail ( save ) ;
Assert ( g_BoneAccessStack . Count ( ) < 32 ) ; // Most likely we are leaking "PushAllowBoneAccess" calls if PopBoneAccess is never called. Consider using AutoAllowBoneAccess.
g_BoneAcessBase . bAllowBoneAccessForNormalModels = bAllowForNormalModels ;
g_BoneAcessBase . bAllowBoneAccessForViewModels = bAllowForViewModels ;
g_BoneAcessBase . tag = tagPush ;
}
void C_BaseAnimating : : PopBoneAccess ( char const * tagPop )
{
// Validate that pop matches the push
Assert ( ( g_BoneAcessBase . tag = = tagPop ) | | ( g_BoneAcessBase . tag & & g_BoneAcessBase . tag ! = ( char const * ) 1 & & tagPop & & tagPop ! = ( char const * ) 1 & & ! strcmp ( g_BoneAcessBase . tag , tagPop ) ) ) ;
int lastIndex = g_BoneAccessStack . Count ( ) - 1 ;
if ( lastIndex < 0 )
{
Assert ( ! " C_BaseAnimating::PopBoneAccess: Stack is empty!!! " ) ;
return ;
}
g_BoneAcessBase = g_BoneAccessStack [ lastIndex ] ;
g_BoneAccessStack . Remove ( lastIndex ) ;
}
C_BaseAnimating : : AutoAllowBoneAccess : : AutoAllowBoneAccess ( bool bAllowForNormalModels , bool bAllowForViewModels )
{
C_BaseAnimating : : PushAllowBoneAccess ( bAllowForNormalModels , bAllowForViewModels , ( char const * ) 1 ) ;
}
C_BaseAnimating : : AutoAllowBoneAccess : : ~ AutoAllowBoneAccess ( )
{
C_BaseAnimating : : PopBoneAccess ( ( char const * ) 1 ) ;
}
// (static function)
void C_BaseAnimating : : InvalidateBoneCaches ( )
{
g_iModelBoneCounter + + ;
}
bool C_BaseAnimating : : ShouldDraw ( )
{
return ! IsDynamicModelLoading ( ) & & BaseClass : : ShouldDraw ( ) ;
}
ConVar r_drawothermodels ( " r_drawothermodels " , " 1 " , FCVAR_CHEAT , " 0=Off, 1=Normal, 2=Wireframe " ) ;
//-----------------------------------------------------------------------------
// Purpose: Draws the object
// Input : flags -
//-----------------------------------------------------------------------------
int C_BaseAnimating : : DrawModel ( int flags )
{
VPROF_BUDGET ( " C_BaseAnimating::DrawModel " , VPROF_BUDGETGROUP_MODEL_RENDERING ) ;
if ( ! m_bReadyToDraw )
return 0 ;
int drawn = 0 ;
# ifdef TF_CLIENT_DLL
ValidateModelIndex ( ) ;
# endif
if ( r_drawothermodels . GetInt ( ) )
{
MDLCACHE_CRITICAL_SECTION ( ) ;
int extraFlags = 0 ;
if ( r_drawothermodels . GetInt ( ) = = 2 )
{
extraFlags | = STUDIO_WIREFRAME ;
}
if ( flags & STUDIO_SHADOWDEPTHTEXTURE )
{
extraFlags | = STUDIO_SHADOWDEPTHTEXTURE ;
}
if ( flags & STUDIO_SSAODEPTHTEXTURE )
{
extraFlags | = STUDIO_SSAODEPTHTEXTURE ;
}
if ( ( flags & ( STUDIO_SSAODEPTHTEXTURE | STUDIO_SHADOWDEPTHTEXTURE ) ) = = 0 & &
g_pStudioStatsEntity ! = NULL & & g_pStudioStatsEntity = = GetClientRenderable ( ) )
{
extraFlags | = STUDIO_GENERATE_STATS ;
}
// Necessary for lighting blending
CreateModelInstance ( ) ;
if ( ! IsFollowingEntity ( ) )
{
drawn = InternalDrawModel ( flags | extraFlags ) ;
}
else
{
// this doesn't draw unless master entity is visible and it's a studio model!!!
C_BaseAnimating * follow = FindFollowedEntity ( ) ;
if ( follow )
{
// recompute master entity bone structure
int baseDrawn = follow - > DrawModel ( 0 ) ;
// draw entity
// FIXME: Currently only draws if aiment is drawn.
// BUGBUG: Fixup bbox and do a separate cull for follow object
if ( baseDrawn )
{
drawn = InternalDrawModel ( STUDIO_RENDER | extraFlags ) ;
}
}
}
}
// If we're visualizing our bboxes, draw them
DrawBBoxVisualizations ( ) ;
return drawn ;
}
//-----------------------------------------------------------------------------
// Gets the hitbox-to-world transforms, returns false if there was a problem
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : HitboxToWorldTransforms ( matrix3x4_t * pHitboxToWorld [ MAXSTUDIOBONES ] )
{
MDLCACHE_CRITICAL_SECTION ( ) ;
if ( ! GetModel ( ) )
return false ;
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( ! pStudioHdr )
return false ;
mstudiohitboxset_t * set = pStudioHdr - > pHitboxSet ( GetHitboxSet ( ) ) ;
if ( ! set )
return false ;
if ( ! set - > numhitboxes )
return false ;
CBoneCache * pCache = GetBoneCache ( pStudioHdr ) ;
pCache - > ReadCachedBonePointers ( pHitboxToWorld , pStudioHdr - > numbones ( ) ) ;
return true ;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : OnPostInternalDrawModel ( ClientModelRenderInfo_t * pInfo )
{
return true ;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : OnInternalDrawModel ( ClientModelRenderInfo_t * pInfo )
{
if ( m_hLightingOriginRelative . Get ( ) )
{
C_InfoLightingRelative * pInfoLighting = assert_cast < C_InfoLightingRelative * > ( m_hLightingOriginRelative . Get ( ) ) ;
pInfoLighting - > GetLightingOffset ( pInfo - > lightingOffset ) ;
pInfo - > pLightingOffset = & pInfo - > lightingOffset ;
}
if ( m_hLightingOrigin )
{
pInfo - > pLightingOrigin = & ( m_hLightingOrigin - > GetAbsOrigin ( ) ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void C_BaseAnimating : : DoInternalDrawModel ( ClientModelRenderInfo_t * pInfo , DrawModelState_t * pState , matrix3x4_t * pBoneToWorldArray )
{
if ( pState )
{
modelrender - > DrawModelExecute ( * pState , * pInfo , pBoneToWorldArray ) ;
}
if ( vcollide_wireframe . GetBool ( ) )
{
if ( IsRagdoll ( ) )
{
m_pRagdoll - > DrawWireframe ( ) ;
}
else if ( IsSolid ( ) & & CollisionProp ( ) - > GetSolid ( ) = = SOLID_VPHYSICS )
{
vcollide_t * pCollide = modelinfo - > GetVCollide ( GetModelIndex ( ) ) ;
if ( pCollide & & pCollide - > solidCount = = 1 )
{
static color32 debugColor = { 0 , 255 , 255 , 0 } ;
matrix3x4_t matrix ;
AngleMatrix ( GetAbsAngles ( ) , GetAbsOrigin ( ) , matrix ) ;
engine - > DebugDrawPhysCollide ( pCollide - > solids [ 0 ] , NULL , matrix , debugColor ) ;
if ( VPhysicsGetObject ( ) )
{
static color32 debugColorPhys = { 255 , 0 , 0 , 0 } ;
matrix3x4_t matrix ;
VPhysicsGetObject ( ) - > GetPositionMatrix ( & matrix ) ;
engine - > DebugDrawPhysCollide ( pCollide - > solids [ 0 ] , NULL , matrix , debugColorPhys ) ;
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Draws the object
// Input : flags -
//-----------------------------------------------------------------------------
int C_BaseAnimating : : InternalDrawModel ( int flags )
{
VPROF ( " C_BaseAnimating::InternalDrawModel " ) ;
if ( ! GetModel ( ) )
return 0 ;
// This should never happen, but if the server class hierarchy has bmodel entities derived from CBaseAnimating or does a
// SetModel with the wrong type of model, this could occur.
if ( modelinfo - > GetModelType ( GetModel ( ) ) ! = mod_studio )
{
return BaseClass : : DrawModel ( flags ) ;
}
// Make sure hdr is valid for drawing
if ( ! GetModelPtr ( ) )
return 0 ;
UpdateBoneAttachments ( ) ;
if ( IsEffectActive ( EF_ITEM_BLINK ) )
{
flags | = STUDIO_ITEM_BLINK ;
}
ClientModelRenderInfo_t info ;
ClientModelRenderInfo_t * pInfo ;
pInfo = & info ;
pInfo - > flags = flags ;
pInfo - > pRenderable = this ;
pInfo - > instance = GetModelInstance ( ) ;
pInfo - > entity_index = index ;
pInfo - > pModel = GetModel ( ) ;
pInfo - > origin = GetRenderOrigin ( ) ;
pInfo - > angles = GetRenderAngles ( ) ;
pInfo - > skin = GetSkin ( ) ;
pInfo - > body = GetBody ( ) ;
pInfo - > hitboxset = m_nHitboxSet ;
if ( ! OnInternalDrawModel ( pInfo ) )
{
return 0 ;
}
Assert ( ! pInfo - > pModelToWorld ) ;
if ( ! pInfo - > pModelToWorld )
{
pInfo - > pModelToWorld = & pInfo - > modelToWorld ;
// Turns the origin + angles into a matrix
AngleMatrix ( pInfo - > angles , pInfo - > origin , pInfo - > modelToWorld ) ;
}
DrawModelState_t state ;
matrix3x4_t * pBoneToWorld = NULL ;
bool bMarkAsDrawn = modelrender - > DrawModelSetup ( * pInfo , & state , NULL , & pBoneToWorld ) ;
// Scale the base transform if we don't have a bone hierarchy
if ( IsModelScaled ( ) )
{
CStudioHdr * pHdr = GetModelPtr ( ) ;
if ( pHdr & & pBoneToWorld & & pHdr - > numbones ( ) = = 1 )
{
// Scale the bone to world at this point
const float flScale = GetModelScale ( ) ;
VectorScale ( ( * pBoneToWorld ) [ 0 ] , flScale , ( * pBoneToWorld ) [ 0 ] ) ;
VectorScale ( ( * pBoneToWorld ) [ 1 ] , flScale , ( * pBoneToWorld ) [ 1 ] ) ;
VectorScale ( ( * pBoneToWorld ) [ 2 ] , flScale , ( * pBoneToWorld ) [ 2 ] ) ;
}
}
DoInternalDrawModel ( pInfo , ( bMarkAsDrawn & & ( pInfo - > flags & STUDIO_RENDER ) ) ? & state : NULL , pBoneToWorld ) ;
OnPostInternalDrawModel ( pInfo ) ;
return bMarkAsDrawn ;
}
extern ConVar muzzleflash_light ;
void C_BaseAnimating : : ProcessMuzzleFlashEvent ( )
{
// If we have an attachment, then stick a light on it.
if ( muzzleflash_light . GetBool ( ) )
{
//FIXME: We should really use a named attachment for this
if ( m_Attachments . Count ( ) > 0 )
{
Vector vAttachment ;
QAngle dummyAngles ;
GetAttachment ( 1 , vAttachment , dummyAngles ) ;
// Make an elight
dlight_t * el = effects - > CL_AllocElight ( LIGHT_INDEX_MUZZLEFLASH + index ) ;
el - > origin = vAttachment ;
el - > radius = random - > RandomInt ( 32 , 64 ) ;
el - > decay = el - > radius / 0.05f ;
el - > die = gpGlobals - > curtime + 0.05f ;
el - > color . r = 255 ;
el - > color . g = 192 ;
el - > color . b = 64 ;
el - > color . exponent = 5 ;
}
}
}
//-----------------------------------------------------------------------------
// Internal routine to process animation events for studiomodels
//-----------------------------------------------------------------------------
void C_BaseAnimating : : DoAnimationEvents ( CStudioHdr * pStudioHdr )
{
if ( ! pStudioHdr )
return ;
# ifdef DEBUG
bool watch = dbganimmodel . GetString ( ) [ 0 ] & & V_stristr ( pStudioHdr - > pszName ( ) , dbganimmodel . GetString ( ) ) ;
# else
bool watch = false ; // Q_strstr( hdr->name, "rifle" ) ? true : false;
# endif
//Adrian: eh? This should never happen.
if ( GetSequence ( ) = = - 1 )
return ;
// build root animation
float flEventCycle = GetCycle ( ) ;
// If we're invisible, don't draw the muzzle flash
bool bIsInvisible = ! IsVisible ( ) & & ! IsViewModel ( ) & & ! IsMenuModel ( ) ;
if ( bIsInvisible & & ! clienttools - > IsInRecordingMode ( ) )
return ;
// add in muzzleflash effect
if ( ShouldMuzzleFlash ( ) )
{
DisableMuzzleFlash ( ) ;
ProcessMuzzleFlashEvent ( ) ;
}
// If we're invisible, don't process animation events.
if ( bIsInvisible )
return ;
// If we don't have any sequences, don't do anything
int nStudioNumSeq = pStudioHdr - > GetNumSeq ( ) ;
if ( nStudioNumSeq < 1 )
{
Warning ( " %s[%d]: no sequences? \n " , GetDebugName ( ) , entindex ( ) ) ;
Assert ( nStudioNumSeq > = 1 ) ;
return ;
}
int nSeqNum = GetSequence ( ) ;
if ( nSeqNum > = nStudioNumSeq )
{
// This can happen e.g. while reloading Heavy's shotgun, switch to the minigun.
Warning ( " %s[%d]: Playing sequence %d but there's only %d in total? \n " , GetDebugName ( ) , entindex ( ) , nSeqNum , nStudioNumSeq ) ;
return ;
}
mstudioseqdesc_t & seqdesc = pStudioHdr - > pSeqdesc ( nSeqNum ) ;
if ( seqdesc . numevents = = 0 )
return ;
// Forces anim event indices to get set and returns pEvent(0);
mstudioevent_t * pevent = GetEventIndexForSequence ( seqdesc ) ;
if ( watch )
{
Msg ( " %i cycle %f \n " , gpGlobals - > tickcount , GetCycle ( ) ) ;
}
bool resetEvents = m_nResetEventsParity ! = m_nPrevResetEventsParity ;
m_nPrevResetEventsParity = m_nResetEventsParity ;
if ( m_nEventSequence ! = GetSequence ( ) | | resetEvents )
{
if ( watch )
{
Msg ( " new seq: %i - old seq: %i - reset: %s - m_flCycle %f - Model Name: %s - (time %.3f) \n " ,
GetSequence ( ) , m_nEventSequence ,
resetEvents ? " true " : " false " ,
GetCycle ( ) , pStudioHdr - > pszName ( ) ,
gpGlobals - > curtime ) ;
}
m_nEventSequence = GetSequence ( ) ;
flEventCycle = 0.0f ;
m_flPrevEventCycle = - 0.01 ; // back up to get 0'th frame animations
}
// stalled?
if ( flEventCycle = = m_flPrevEventCycle )
return ;
if ( watch )
{
Msg ( " %i (seq %d cycle %.3f ) evcycle %.3f prevevcycle %.3f (time %.3f) \n " ,
gpGlobals - > tickcount ,
GetSequence ( ) ,
GetCycle ( ) ,
flEventCycle ,
m_flPrevEventCycle ,
gpGlobals - > curtime ) ;
}
// check for looping
BOOL bLooped = false ;
if ( flEventCycle < = m_flPrevEventCycle )
{
if ( m_flPrevEventCycle - flEventCycle > 0.5 )
{
bLooped = true ;
}
else
{
// things have backed up, which is bad since it'll probably result in a hitch in the animation playback
// but, don't play events again for the same time slice
return ;
}
}
// This makes sure events that occur at the end of a sequence occur are
// sent before events that occur at the beginning of a sequence.
if ( bLooped )
{
for ( int i = 0 ; i < ( int ) seqdesc . numevents ; i + + )
{
// ignore all non-client-side events
if ( pevent [ i ] . type & AE_TYPE_NEWEVENTSYSTEM )
{
if ( ! ( pevent [ i ] . type & AE_TYPE_CLIENT ) )
continue ;
}
else if ( pevent [ i ] . event < 5000 ) //Adrian - Support the old event system
continue ;
if ( pevent [ i ] . cycle < = m_flPrevEventCycle )
continue ;
if ( watch )
{
Msg ( " %i FE %i Looped cycle %f, prev %f ev %f (time %.3f) \n " ,
gpGlobals - > tickcount ,
pevent [ i ] . event ,
pevent [ i ] . cycle ,
m_flPrevEventCycle ,
flEventCycle ,
gpGlobals - > curtime ) ;
}
FireEvent ( GetAbsOrigin ( ) , GetAbsAngles ( ) , pevent [ i ] . event , pevent [ i ] . pszOptions ( ) ) ;
}
// Necessary to get the next loop working
m_flPrevEventCycle = - 0.01 ;
}
for ( int i = 0 ; i < ( int ) seqdesc . numevents ; i + + )
{
if ( pevent [ i ] . type & AE_TYPE_NEWEVENTSYSTEM )
{
if ( ! ( pevent [ i ] . type & AE_TYPE_CLIENT ) )
continue ;
}
else if ( pevent [ i ] . event < 5000 ) //Adrian - Support the old event system
continue ;
if ( ( pevent [ i ] . cycle > m_flPrevEventCycle & & pevent [ i ] . cycle < = flEventCycle ) )
{
if ( watch )
{
Msg ( " %i (seq: %d) FE %i Normal cycle %f, prev %f ev %f (time %.3f) \n " ,
gpGlobals - > tickcount ,
GetSequence ( ) ,
pevent [ i ] . event ,
pevent [ i ] . cycle ,
m_flPrevEventCycle ,
flEventCycle ,
gpGlobals - > curtime ) ;
}
FireEvent ( GetAbsOrigin ( ) , GetAbsAngles ( ) , pevent [ i ] . event , pevent [ i ] . pszOptions ( ) ) ;
}
}
m_flPrevEventCycle = flEventCycle ;
}
//-----------------------------------------------------------------------------
// Purpose: Parses a muzzle effect event and sends it out for drawing
// Input : *options - event parameters in text format
// isFirstPerson - whether this is coming from an NPC or the player
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : DispatchMuzzleEffect ( const char * options , bool isFirstPerson )
{
const char * p = options ;
char token [ 128 ] ;
int weaponType = 0 ;
// Get the first parameter
p = nexttoken ( token , p , ' ' ) ;
// Find the weapon type
if ( token )
{
//TODO: Parse the type from a list instead
if ( Q_stricmp ( token , " COMBINE " ) = = 0 )
{
weaponType = MUZZLEFLASH_COMBINE ;
}
else if ( Q_stricmp ( token , " SMG1 " ) = = 0 )
{
weaponType = MUZZLEFLASH_SMG1 ;
}
else if ( Q_stricmp ( token , " PISTOL " ) = = 0 )
{
weaponType = MUZZLEFLASH_PISTOL ;
}
else if ( Q_stricmp ( token , " SHOTGUN " ) = = 0 )
{
weaponType = MUZZLEFLASH_SHOTGUN ;
}
else if ( Q_stricmp ( token , " 357 " ) = = 0 )
{
weaponType = MUZZLEFLASH_357 ;
}
else if ( Q_stricmp ( token , " RPG " ) = = 0 )
{
weaponType = MUZZLEFLASH_RPG ;
}
else
{
//NOTENOTE: This means you specified an invalid muzzleflash type, check your spelling?
Assert ( 0 ) ;
}
}
else
{
//NOTENOTE: This means that there wasn't a proper parameter passed into the animevent
Assert ( 0 ) ;
return false ;
}
// Get the second parameter
p = nexttoken ( token , p , ' ' ) ;
int attachmentIndex = - 1 ;
// Find the attachment name
if ( token )
{
attachmentIndex = LookupAttachment ( token ) ;
// Found an invalid attachment
if ( attachmentIndex < = 0 )
{
//NOTENOTE: This means that the attachment you're trying to use is invalid
Assert ( 0 ) ;
return false ;
}
}
else
{
//NOTENOTE: This means that there wasn't a proper parameter passed into the animevent
Assert ( 0 ) ;
return false ;
}
// Send it out
tempents - > MuzzleFlash ( weaponType , GetRefEHandle ( ) , attachmentIndex , isFirstPerson ) ;
return true ;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void MaterialFootstepSound ( C_BaseAnimating * pEnt , bool bLeftFoot , float flVolume )
{
trace_t tr ;
Vector traceStart ;
QAngle angles ;
int attachment ;
//!!!PERF - These string lookups here aren't the swiftest, but
// this doesn't get called very frequently unless a lot of NPCs
// are using this code.
if ( bLeftFoot )
{
attachment = pEnt - > LookupAttachment ( " LeftFoot " ) ;
}
else
{
attachment = pEnt - > LookupAttachment ( " RightFoot " ) ;
}
if ( attachment = = - 1 )
{
// Exit if this NPC doesn't have the proper attachments.
return ;
}
pEnt - > GetAttachment ( attachment , traceStart , angles ) ;
UTIL_TraceLine ( traceStart , traceStart - Vector ( 0 , 0 , 48.0f ) , MASK_SHOT_HULL , pEnt , COLLISION_GROUP_NONE , & tr ) ;
if ( tr . fraction < 1.0 & & tr . m_pEnt )
{
surfacedata_t * psurf = physprops - > GetSurfaceData ( tr . surface . surfaceProps ) ;
if ( psurf )
{
EmitSound_t params ;
if ( bLeftFoot )
{
params . m_pSoundName = physprops - > GetString ( psurf - > sounds . stepleft ) ;
}
else
{
params . m_pSoundName = physprops - > GetString ( psurf - > sounds . stepright ) ;
}
CPASAttenuationFilter filter ( pEnt , params . m_pSoundName ) ;
params . m_bWarnOnDirectWaveReference = true ;
params . m_flVolume = flVolume ;
pEnt - > EmitSound ( filter , pEnt - > entindex ( ) , params ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *origin -
// *angles -
// event -
// *options -
// numAttachments -
// attachments[] -
//-----------------------------------------------------------------------------
void C_BaseAnimating : : FireEvent ( const Vector & origin , const QAngle & angles , int event , const char * options )
{
Vector attachOrigin ;
QAngle attachAngles ;
switch ( event )
{
case AE_CL_CREATE_PARTICLE_EFFECT :
{
int iAttachment = - 1 ;
int iAttachType = PATTACH_ABSORIGIN_FOLLOW ;
char token [ 256 ] ;
char szParticleEffect [ 256 ] ;
// Get the particle effect name
const char * p = options ;
p = nexttoken ( token , p , ' ' ) ;
if ( token )
{
const char * mtoken = ModifyEventParticles ( token ) ;
if ( ! mtoken | | mtoken [ 0 ] = = ' \0 ' )
return ;
Q_strncpy ( szParticleEffect , mtoken , sizeof ( szParticleEffect ) ) ;
}
// Get the attachment type
p = nexttoken ( token , p , ' ' ) ;
if ( token )
{
iAttachType = GetAttachTypeFromString ( token ) ;
if ( iAttachType = = - 1 )
{
Warning ( " Invalid attach type specified for particle effect anim event. Trying to spawn effect '%s' with attach type of '%s' \n " , szParticleEffect , token ) ;
return ;
}
}
// Get the attachment point index
p = nexttoken ( token , p , ' ' ) ;
if ( token )
{
iAttachment = atoi ( token ) ;
// See if we can find any attachment points matching the name
if ( token [ 0 ] ! = ' 0 ' & & iAttachment = = 0 )
{
iAttachment = LookupAttachment ( token ) ;
if ( iAttachment < = 0 )
{
Warning ( " Failed to find attachment point specified for particle effect anim event. Trying to spawn effect '%s' on attachment named '%s' \n " , szParticleEffect , token ) ;
return ;
}
}
}
// Spawn the particle effect
ParticleProp ( ) - > Create ( szParticleEffect , ( ParticleAttachment_t ) iAttachType , iAttachment ) ;
}
break ;
case AE_CL_PLAYSOUND :
{
CLocalPlayerFilter filter ;
if ( m_Attachments . Count ( ) > 0 )
{
GetAttachment ( 1 , attachOrigin , attachAngles ) ;
EmitSound ( filter , GetSoundSourceIndex ( ) , options , & attachOrigin ) ;
}
else
{
EmitSound ( filter , GetSoundSourceIndex ( ) , options , & GetAbsOrigin ( ) ) ;
}
}
break ;
case AE_CL_STOPSOUND :
{
StopSound ( GetSoundSourceIndex ( ) , options ) ;
}
break ;
case CL_EVENT_FOOTSTEP_LEFT :
{
# ifndef HL2MP
char pSoundName [ 256 ] ;
if ( ! options | | ! options [ 0 ] )
{
options = " NPC_CombineS " ;
}
Vector vel ;
EstimateAbsVelocity ( vel ) ;
// If he's moving fast enough, play the run sound
if ( vel . Length2DSqr ( ) > RUN_SPEED_ESTIMATE_SQR )
{
Q_snprintf ( pSoundName , 256 , " %s.RunFootstepLeft " , options ) ;
}
else
{
Q_snprintf ( pSoundName , 256 , " %s.FootstepLeft " , options ) ;
}
EmitSound ( pSoundName ) ;
# endif
}
break ;
case CL_EVENT_FOOTSTEP_RIGHT :
{
# ifndef HL2MP
char pSoundName [ 256 ] ;
if ( ! options | | ! options [ 0 ] )
{
options = " NPC_CombineS " ;
}
Vector vel ;
EstimateAbsVelocity ( vel ) ;
// If he's moving fast enough, play the run sound
if ( vel . Length2DSqr ( ) > RUN_SPEED_ESTIMATE_SQR )
{
Q_snprintf ( pSoundName , 256 , " %s.RunFootstepRight " , options ) ;
}
else
{
Q_snprintf ( pSoundName , 256 , " %s.FootstepRight " , options ) ;
}
EmitSound ( pSoundName ) ;
# endif
}
break ;
case CL_EVENT_MFOOTSTEP_LEFT :
{
MaterialFootstepSound ( this , true , VOL_NORM * 0.5f ) ;
}
break ;
case CL_EVENT_MFOOTSTEP_RIGHT :
{
MaterialFootstepSound ( this , false , VOL_NORM * 0.5f ) ;
}
break ;
case CL_EVENT_MFOOTSTEP_LEFT_LOUD :
{
MaterialFootstepSound ( this , true , VOL_NORM ) ;
}
break ;
case CL_EVENT_MFOOTSTEP_RIGHT_LOUD :
{
MaterialFootstepSound ( this , false , VOL_NORM ) ;
}
break ;
// Eject brass
case CL_EVENT_EJECTBRASS1 :
if ( m_Attachments . Count ( ) > 0 )
{
if ( MainViewOrigin ( ) . DistToSqr ( GetAbsOrigin ( ) ) < ( 256 * 256 ) )
{
Vector attachOrigin ;
QAngle attachAngles ;
if ( GetAttachment ( 2 , attachOrigin , attachAngles ) )
{
tempents - > EjectBrass ( attachOrigin , attachAngles , GetAbsAngles ( ) , atoi ( options ) ) ;
}
}
}
break ;
case AE_MUZZLEFLASH :
{
// Send out the effect for a player
DispatchMuzzleEffect ( options , true ) ;
break ;
}
case AE_NPC_MUZZLEFLASH :
{
// Send out the effect for an NPC
DispatchMuzzleEffect ( options , false ) ;
break ;
}
// OBSOLETE EVENTS. REPLACED BY NEWER SYSTEMS.
// See below in FireObsoleteEvent() for comments on what to use instead.
case AE_CLIENT_EFFECT_ATTACH :
case CL_EVENT_DISPATCHEFFECT0 :
case CL_EVENT_DISPATCHEFFECT1 :
case CL_EVENT_DISPATCHEFFECT2 :
case CL_EVENT_DISPATCHEFFECT3 :
case CL_EVENT_DISPATCHEFFECT4 :
case CL_EVENT_DISPATCHEFFECT5 :
case CL_EVENT_DISPATCHEFFECT6 :
case CL_EVENT_DISPATCHEFFECT7 :
case CL_EVENT_DISPATCHEFFECT8 :
case CL_EVENT_DISPATCHEFFECT9 :
case CL_EVENT_MUZZLEFLASH0 :
case CL_EVENT_MUZZLEFLASH1 :
case CL_EVENT_MUZZLEFLASH2 :
case CL_EVENT_MUZZLEFLASH3 :
case CL_EVENT_NPC_MUZZLEFLASH0 :
case CL_EVENT_NPC_MUZZLEFLASH1 :
case CL_EVENT_NPC_MUZZLEFLASH2 :
case CL_EVENT_NPC_MUZZLEFLASH3 :
case CL_EVENT_SPARK0 :
case CL_EVENT_SOUND :
FireObsoleteEvent ( origin , angles , event , options ) ;
break ;
case AE_CL_ENABLE_BODYGROUP :
{
int index = FindBodygroupByName ( options ) ;
if ( index > = 0 )
{
SetBodygroup ( index , 1 ) ;
}
}
break ;
case AE_CL_DISABLE_BODYGROUP :
{
int index = FindBodygroupByName ( options ) ;
if ( index > = 0 )
{
SetBodygroup ( index , 0 ) ;
}
}
break ;
case AE_CL_BODYGROUP_SET_VALUE :
{
char szBodygroupName [ 256 ] ;
int value = 0 ;
char token [ 256 ] ;
const char * p = options ;
// Bodygroup Name
p = nexttoken ( token , p , ' ' ) ;
if ( token )
{
Q_strncpy ( szBodygroupName , token , sizeof ( szBodygroupName ) ) ;
}
// Get the desired value
p = nexttoken ( token , p , ' ' ) ;
if ( token )
{
value = atoi ( token ) ;
}
int index = FindBodygroupByName ( szBodygroupName ) ;
if ( index > = 0 )
{
SetBodygroup ( index , value ) ;
}
}
break ;
default :
break ;
}
}
//-----------------------------------------------------------------------------
// Purpose: These events are all obsolete events, left here to support old games.
// Their systems have all been replaced with better ones.
//-----------------------------------------------------------------------------
void C_BaseAnimating : : FireObsoleteEvent ( const Vector & origin , const QAngle & angles , int event , const char * options )
{
Vector attachOrigin ;
QAngle attachAngles ;
switch ( event )
{
// Obsolete. Use the AE_CL_CREATE_PARTICLE_EFFECT event instead, which uses the artist driven particle system & editor.
case AE_CLIENT_EFFECT_ATTACH :
{
int iAttachment = - 1 ;
int iParam = 0 ;
char token [ 128 ] ;
char effectFunc [ 128 ] ;
const char * p = options ;
p = nexttoken ( token , p , ' ' ) ;
if ( token )
{
Q_strncpy ( effectFunc , token , sizeof ( effectFunc ) ) ;
}
p = nexttoken ( token , p , ' ' ) ;
if ( token )
{
iAttachment = atoi ( token ) ;
}
p = nexttoken ( token , p , ' ' ) ;
if ( token )
{
iParam = atoi ( token ) ;
}
if ( iAttachment ! = - 1 & & m_Attachments . Count ( ) > = iAttachment )
{
GetAttachment ( iAttachment , attachOrigin , attachAngles ) ;
// Fill out the generic data
CEffectData data ;
data . m_vOrigin = attachOrigin ;
data . m_vAngles = attachAngles ;
AngleVectors ( attachAngles , & data . m_vNormal ) ;
data . m_hEntity = GetRefEHandle ( ) ;
data . m_nAttachmentIndex = iAttachment + 1 ;
data . m_fFlags = iParam ;
DispatchEffect ( effectFunc , data ) ;
}
}
break ;
// Obsolete. Use the AE_CL_CREATE_PARTICLE_EFFECT event instead, which uses the artist driven particle system & editor.
case CL_EVENT_DISPATCHEFFECT0 :
case CL_EVENT_DISPATCHEFFECT1 :
case CL_EVENT_DISPATCHEFFECT2 :
case CL_EVENT_DISPATCHEFFECT3 :
case CL_EVENT_DISPATCHEFFECT4 :
case CL_EVENT_DISPATCHEFFECT5 :
case CL_EVENT_DISPATCHEFFECT6 :
case CL_EVENT_DISPATCHEFFECT7 :
case CL_EVENT_DISPATCHEFFECT8 :
case CL_EVENT_DISPATCHEFFECT9 :
{
int iAttachment = - 1 ;
// First person muzzle flashes
switch ( event )
{
case CL_EVENT_DISPATCHEFFECT0 :
iAttachment = 0 ;
break ;
case CL_EVENT_DISPATCHEFFECT1 :
iAttachment = 1 ;
break ;
case CL_EVENT_DISPATCHEFFECT2 :
iAttachment = 2 ;
break ;
case CL_EVENT_DISPATCHEFFECT3 :
iAttachment = 3 ;
break ;
case CL_EVENT_DISPATCHEFFECT4 :
iAttachment = 4 ;
break ;
case CL_EVENT_DISPATCHEFFECT5 :
iAttachment = 5 ;
break ;
case CL_EVENT_DISPATCHEFFECT6 :
iAttachment = 6 ;
break ;
case CL_EVENT_DISPATCHEFFECT7 :
iAttachment = 7 ;
break ;
case CL_EVENT_DISPATCHEFFECT8 :
iAttachment = 8 ;
break ;
case CL_EVENT_DISPATCHEFFECT9 :
iAttachment = 9 ;
break ;
}
if ( iAttachment ! = - 1 & & m_Attachments . Count ( ) > iAttachment )
{
GetAttachment ( iAttachment + 1 , attachOrigin , attachAngles ) ;
// Fill out the generic data
CEffectData data ;
data . m_vOrigin = attachOrigin ;
data . m_vAngles = attachAngles ;
AngleVectors ( attachAngles , & data . m_vNormal ) ;
data . m_hEntity = GetRefEHandle ( ) ;
data . m_nAttachmentIndex = iAttachment + 1 ;
DispatchEffect ( options , data ) ;
}
}
break ;
// Obsolete. Use the AE_MUZZLEFLASH / AE_NPC_MUZZLEFLASH events instead.
case CL_EVENT_MUZZLEFLASH0 :
case CL_EVENT_MUZZLEFLASH1 :
case CL_EVENT_MUZZLEFLASH2 :
case CL_EVENT_MUZZLEFLASH3 :
case CL_EVENT_NPC_MUZZLEFLASH0 :
case CL_EVENT_NPC_MUZZLEFLASH1 :
case CL_EVENT_NPC_MUZZLEFLASH2 :
case CL_EVENT_NPC_MUZZLEFLASH3 :
{
int iAttachment = - 1 ;
bool bFirstPerson = true ;
// First person muzzle flashes
switch ( event )
{
case CL_EVENT_MUZZLEFLASH0 :
iAttachment = 0 ;
break ;
case CL_EVENT_MUZZLEFLASH1 :
iAttachment = 1 ;
break ;
case CL_EVENT_MUZZLEFLASH2 :
iAttachment = 2 ;
break ;
case CL_EVENT_MUZZLEFLASH3 :
iAttachment = 3 ;
break ;
// Third person muzzle flashes
case CL_EVENT_NPC_MUZZLEFLASH0 :
iAttachment = 0 ;
bFirstPerson = false ;
break ;
case CL_EVENT_NPC_MUZZLEFLASH1 :
iAttachment = 1 ;
bFirstPerson = false ;
break ;
case CL_EVENT_NPC_MUZZLEFLASH2 :
iAttachment = 2 ;
bFirstPerson = false ;
break ;
case CL_EVENT_NPC_MUZZLEFLASH3 :
iAttachment = 3 ;
bFirstPerson = false ;
break ;
}
if ( iAttachment ! = - 1 & & m_Attachments . Count ( ) > iAttachment )
{
GetAttachment ( iAttachment + 1 , attachOrigin , attachAngles ) ;
int entId = render - > GetViewEntity ( ) ;
ClientEntityHandle_t hEntity = ClientEntityList ( ) . EntIndexToHandle ( entId ) ;
tempents - > MuzzleFlash ( attachOrigin , attachAngles , atoi ( options ) , hEntity , bFirstPerson ) ;
}
}
break ;
// Obsolete: Use the AE_CL_CREATE_PARTICLE_EFFECT event instead, which uses the artist driven particle system & editor.
case CL_EVENT_SPARK0 :
{
Vector vecForward ;
GetAttachment ( 1 , attachOrigin , attachAngles ) ;
AngleVectors ( attachAngles , & vecForward ) ;
g_pEffects - > Sparks ( attachOrigin , atoi ( options ) , 1 , & vecForward ) ;
}
break ;
// Obsolete: Use the AE_CL_PLAYSOUND event instead, which doesn't rely on a magic number in the .qc
case CL_EVENT_SOUND :
{
CLocalPlayerFilter filter ;
if ( m_Attachments . Count ( ) > 0 )
{
GetAttachment ( 1 , attachOrigin , attachAngles ) ;
EmitSound ( filter , GetSoundSourceIndex ( ) , options , & attachOrigin ) ;
}
else
{
EmitSound ( filter , GetSoundSourceIndex ( ) , options ) ;
}
}
break ;
default :
break ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : IsSelfAnimating ( )
{
if ( m_bClientSideAnimation )
return true ;
// Yes, we use animtime.
int iMoveType = GetMoveType ( ) ;
if ( iMoveType ! = MOVETYPE_STEP & &
iMoveType ! = MOVETYPE_NONE & &
iMoveType ! = MOVETYPE_WALK & &
iMoveType ! = MOVETYPE_FLY & &
iMoveType ! = MOVETYPE_FLYGRAVITY )
{
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose: Called by networking code when an entity is new to the PVS or comes down with the EF_NOINTERP flag set.
// The position history data is flushed out right after this call, so we need to store off the current data
// in the latched fields so we try to interpolate
// Input : *ent -
// full_reset -
//-----------------------------------------------------------------------------
void C_BaseAnimating : : ResetLatched ( void )
{
// Reset the IK
if ( m_pIk )
{
delete m_pIk ;
m_pIk = NULL ;
}
BaseClass : : ResetLatched ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : Interpolate ( float flCurrentTime )
{
// ragdolls don't need interpolation
if ( m_pRagdoll )
return true ;
VPROF ( " C_BaseAnimating::Interpolate " ) ;
Vector oldOrigin ;
QAngle oldAngles ;
Vector oldVel ;
float flOldCycle = GetCycle ( ) ;
int nChangeFlags = 0 ;
if ( ! m_bClientSideAnimation )
m_iv_flCycle . SetLooping ( IsSequenceLooping ( GetSequence ( ) ) ) ;
int bNoMoreChanges ;
int retVal = BaseInterpolatePart1 ( flCurrentTime , oldOrigin , oldAngles , oldVel , bNoMoreChanges ) ;
if ( retVal = = INTERPOLATE_STOP )
{
if ( bNoMoreChanges )
RemoveFromInterpolationList ( ) ;
return true ;
}
// Did cycle change?
if ( GetCycle ( ) ! = flOldCycle )
nChangeFlags | = ANIMATION_CHANGED ;
if ( bNoMoreChanges )
RemoveFromInterpolationList ( ) ;
BaseInterpolatePart2 ( oldOrigin , oldAngles , oldVel , nChangeFlags ) ;
return true ;
}
//-----------------------------------------------------------------------------
// returns true if we're currently being ragdolled
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : IsRagdoll ( ) const
{
return m_pRagdoll & & ( m_nRenderFX = = kRenderFxRagdoll ) ;
}
//-----------------------------------------------------------------------------
// returns true if we're currently being ragdolled
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : IsAboutToRagdoll ( ) const
{
return ( m_nRenderFX = = kRenderFxRagdoll ) ;
}
//-----------------------------------------------------------------------------
// Lets us check our sequence number after a network update
//-----------------------------------------------------------------------------
int C_BaseAnimating : : RestoreData ( const char * context , int slot , int type )
{
int retVal = BaseClass : : RestoreData ( context , slot , type ) ;
CStudioHdr * pHdr = GetModelPtr ( ) ;
if ( pHdr & & m_nSequence > = pHdr - > GetNumSeq ( ) )
{
// Don't let a network update give us an invalid sequence
m_nSequence = 0 ;
}
return retVal ;
}
//-----------------------------------------------------------------------------
// implements these so ragdolls can handle frustum culling & leaf visibility
//-----------------------------------------------------------------------------
void C_BaseAnimating : : GetRenderBounds ( Vector & theMins , Vector & theMaxs )
{
if ( IsRagdoll ( ) )
{
m_pRagdoll - > GetRagdollBounds ( theMins , theMaxs ) ;
}
else if ( GetModel ( ) )
{
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( ! pStudioHdr | | ! pStudioHdr - > SequencesAvailable ( ) | | GetSequence ( ) = = - 1 )
{
theMins = vec3_origin ;
theMaxs = vec3_origin ;
return ;
}
if ( ! VectorCompare ( vec3_origin , pStudioHdr - > view_bbmin ( ) ) | | ! VectorCompare ( vec3_origin , pStudioHdr - > view_bbmax ( ) ) )
{
// clipping bounding box
VectorCopy ( pStudioHdr - > view_bbmin ( ) , theMins ) ;
VectorCopy ( pStudioHdr - > view_bbmax ( ) , theMaxs ) ;
}
else
{
// movement bounding box
VectorCopy ( pStudioHdr - > hull_min ( ) , theMins ) ;
VectorCopy ( pStudioHdr - > hull_max ( ) , theMaxs ) ;
}
mstudioseqdesc_t & seqdesc = pStudioHdr - > pSeqdesc ( GetSequence ( ) ) ;
VectorMin ( seqdesc . bbmin , theMins , theMins ) ;
VectorMax ( seqdesc . bbmax , theMaxs , theMaxs ) ;
}
else
{
theMins = vec3_origin ;
theMaxs = vec3_origin ;
}
// Scale this up depending on if our model is currently scaling
const float flScale = GetModelScale ( ) ;
theMaxs * = flScale ;
theMins * = flScale ;
}
//-----------------------------------------------------------------------------
// implements these so ragdolls can handle frustum culling & leaf visibility
//-----------------------------------------------------------------------------
const Vector & C_BaseAnimating : : GetRenderOrigin ( void )
{
if ( IsRagdoll ( ) )
{
return m_pRagdoll - > GetRagdollOrigin ( ) ;
}
else
{
return BaseClass : : GetRenderOrigin ( ) ;
}
}
const QAngle & C_BaseAnimating : : GetRenderAngles ( void )
{
if ( IsRagdoll ( ) )
{
return vec3_angle ;
}
else
{
return BaseClass : : GetRenderAngles ( ) ;
}
}
void C_BaseAnimating : : RagdollMoved ( void )
{
SetAbsOrigin ( m_pRagdoll - > GetRagdollOrigin ( ) ) ;
SetAbsAngles ( vec3_angle ) ;
Vector mins , maxs ;
m_pRagdoll - > GetRagdollBounds ( mins , maxs ) ;
SetCollisionBounds ( mins , maxs ) ;
// If the ragdoll moves, its render-to-texture shadow is dirty
InvalidatePhysicsRecursive ( ANIMATION_CHANGED ) ;
}
//-----------------------------------------------------------------------------
// Purpose: My physics object has been updated, react or extract data
//-----------------------------------------------------------------------------
void C_BaseAnimating : : VPhysicsUpdate ( IPhysicsObject * pPhysics )
{
// FIXME: Should make sure the physics objects being passed in
// is the ragdoll physics object, but I think it's pretty safe not to check
if ( IsRagdoll ( ) )
{
m_pRagdoll - > VPhysicsUpdate ( pPhysics ) ;
RagdollMoved ( ) ;
return ;
}
BaseClass : : VPhysicsUpdate ( pPhysics ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : updateType -
//-----------------------------------------------------------------------------
void C_BaseAnimating : : PreDataUpdate ( DataUpdateType_t updateType )
{
VPROF ( " C_BaseAnimating::PreDataUpdate " ) ;
m_flOldCycle = GetCycle ( ) ;
m_nOldSequence = GetSequence ( ) ;
m_flOldModelScale = GetModelScale ( ) ;
int i ;
for ( i = 0 ; i < MAXSTUDIOBONECTRLS ; i + + )
{
m_flOldEncodedController [ i ] = m_flEncodedController [ i ] ;
}
for ( i = 0 ; i < MAXSTUDIOPOSEPARAM ; i + + )
{
m_flOldPoseParameters [ i ] = m_flPoseParameter [ i ] ;
}
BaseClass : : PreDataUpdate ( updateType ) ;
}
void C_BaseAnimating : : NotifyShouldTransmit ( ShouldTransmitState_t state )
{
BaseClass : : NotifyShouldTransmit ( state ) ;
if ( state = = SHOULDTRANSMIT_START )
{
// If he's been firing a bunch, then he comes back into the PVS, his muzzle flash
// will show up even if he isn't firing now.
DisableMuzzleFlash ( ) ;
m_nPrevResetEventsParity = m_nResetEventsParity ;
m_nEventSequence = GetSequence ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : updateType -
//-----------------------------------------------------------------------------
void C_BaseAnimating : : PostDataUpdate ( DataUpdateType_t updateType )
{
BaseClass : : PostDataUpdate ( updateType ) ;
if ( m_bClientSideAnimation )
{
SetCycle ( m_flOldCycle ) ;
AddToClientSideAnimationList ( ) ;
}
else
{
RemoveFromClientSideAnimationList ( ) ;
}
bool bBoneControllersChanged = false ;
int i ;
for ( i = 0 ; i < MAXSTUDIOBONECTRLS & & ! bBoneControllersChanged ; i + + )
{
if ( m_flOldEncodedController [ i ] ! = m_flEncodedController [ i ] )
{
bBoneControllersChanged = true ;
}
}
bool bPoseParametersChanged = false ;
for ( i = 0 ; i < MAXSTUDIOPOSEPARAM & & ! bPoseParametersChanged ; i + + )
{
if ( m_flOldPoseParameters [ i ] ! = m_flPoseParameter [ i ] )
{
bPoseParametersChanged = true ;
}
}
// Cycle change? Then re-render
bool bAnimationChanged = m_flOldCycle ! = GetCycle ( ) | | bBoneControllersChanged | | bPoseParametersChanged ;
bool bSequenceChanged = m_nOldSequence ! = GetSequence ( ) ;
bool bScaleChanged = ( m_flOldModelScale ! = GetModelScale ( ) ) ;
if ( bAnimationChanged | | bSequenceChanged | | bScaleChanged )
{
InvalidatePhysicsRecursive ( ANIMATION_CHANGED ) ;
}
if ( bAnimationChanged | | bSequenceChanged )
{
if ( m_bClientSideAnimation )
{
ClientSideAnimationChanged ( ) ;
}
}
// reset prev cycle if new sequence
if ( m_nNewSequenceParity ! = m_nPrevNewSequenceParity )
{
// It's important not to call Reset() on a static prop, because if we call
// Reset(), then the entity will stay in the interpolated entities list
// forever, wasting CPU.
MDLCACHE_CRITICAL_SECTION ( ) ;
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( hdr & & ! ( hdr - > flags ( ) & STUDIOHDR_FLAGS_STATIC_PROP ) )
{
m_iv_flCycle . Reset ( ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : bnewentity -
//-----------------------------------------------------------------------------
void C_BaseAnimating : : OnPreDataChanged ( DataUpdateType_t updateType )
{
BaseClass : : OnPreDataChanged ( updateType ) ;
m_bLastClientSideFrameReset = m_bClientSideFrameReset ;
}
void C_BaseAnimating : : ForceSetupBonesAtTime ( matrix3x4_t * pBonesOut , float flTime )
{
// blow the cached prev bones
InvalidateBoneCache ( ) ;
// reset root position to flTime
Interpolate ( flTime ) ;
// Setup bone state at the given time
SetupBones ( pBonesOut , MAXSTUDIOBONES , BONE_USED_BY_ANYTHING , flTime ) ;
}
void C_BaseAnimating : : GetRagdollInitBoneArrays ( matrix3x4_t * pDeltaBones0 , matrix3x4_t * pDeltaBones1 , matrix3x4_t * pCurrentBones , float boneDt )
{
ForceSetupBonesAtTime ( pDeltaBones0 , gpGlobals - > curtime - boneDt ) ;
ForceSetupBonesAtTime ( pDeltaBones1 , gpGlobals - > curtime ) ;
float ragdollCreateTime = PhysGetSyncCreateTime ( ) ;
if ( ragdollCreateTime ! = gpGlobals - > curtime )
{
// The next simulation frame begins before the end of this frame
// so initialize the ragdoll at that time so that it will reach the current
// position at curtime. Otherwise the ragdoll will simulate forward from curtime
// and pop into the future a bit at this point of transition
ForceSetupBonesAtTime ( pCurrentBones , ragdollCreateTime ) ;
}
else
{
memcpy ( pCurrentBones , m_CachedBoneData . Base ( ) , sizeof ( matrix3x4_t ) * m_CachedBoneData . Count ( ) ) ;
}
}
C_BaseAnimating * C_BaseAnimating : : CreateRagdollCopy ( )
{
//Adrian: We now create a separate entity that becomes this entity's ragdoll.
//That way the server side version of this entity can go away.
//Plus we can hook save/restore code to these ragdolls so they don't fall on restore anymore.
C_ClientRagdoll * pRagdoll = new C_ClientRagdoll ( false ) ;
if ( pRagdoll = = NULL )
return NULL ;
TermRopes ( ) ;
const model_t * model = GetModel ( ) ;
const char * pModelName = modelinfo - > GetModelName ( model ) ;
if ( pRagdoll - > InitializeAsClientEntity ( pModelName , RENDER_GROUP_OPAQUE_ENTITY ) = = false )
{
pRagdoll - > Release ( ) ;
return NULL ;
}
// move my current model instance to the ragdoll's so decals are preserved.
SnatchModelInstance ( pRagdoll ) ;
// We need to take these from the entity
pRagdoll - > SetAbsOrigin ( GetAbsOrigin ( ) ) ;
pRagdoll - > SetAbsAngles ( GetAbsAngles ( ) ) ;
pRagdoll - > IgniteRagdoll ( this ) ;
pRagdoll - > TransferDissolveFrom ( this ) ;
pRagdoll - > InitModelEffects ( ) ;
if ( AddRagdollToFadeQueue ( ) = = true )
{
pRagdoll - > m_bImportant = NPC_IsImportantNPC ( this ) ;
s_RagdollLRU . MoveToTopOfLRU ( pRagdoll , pRagdoll - > m_bImportant ) ;
pRagdoll - > m_bFadeOut = true ;
}
m_builtRagdoll = true ;
AddEffects ( EF_NODRAW ) ;
if ( IsEffectActive ( EF_NOSHADOW ) )
{
pRagdoll - > AddEffects ( EF_NOSHADOW ) ;
}
pRagdoll - > m_nRenderFX = kRenderFxRagdoll ;
pRagdoll - > SetRenderMode ( GetRenderMode ( ) ) ;
pRagdoll - > SetRenderColor ( GetRenderColor ( ) . r , GetRenderColor ( ) . g , GetRenderColor ( ) . b , GetRenderColor ( ) . a ) ;
pRagdoll - > m_nBody = m_nBody ;
pRagdoll - > m_nSkin = GetSkin ( ) ;
pRagdoll - > m_vecForce = m_vecForce ;
pRagdoll - > m_nForceBone = m_nForceBone ;
pRagdoll - > SetNextClientThink ( CLIENT_THINK_ALWAYS ) ;
pRagdoll - > SetModelName ( AllocPooledString ( pModelName ) ) ;
pRagdoll - > SetModelScale ( GetModelScale ( ) ) ;
return pRagdoll ;
}
C_BaseAnimating * C_BaseAnimating : : BecomeRagdollOnClient ( )
{
MoveToLastReceivedPosition ( true ) ;
GetAbsOrigin ( ) ;
C_BaseAnimating * pRagdoll = CreateRagdollCopy ( ) ;
matrix3x4_t boneDelta0 [ MAXSTUDIOBONES ] ;
matrix3x4_t boneDelta1 [ MAXSTUDIOBONES ] ;
matrix3x4_t currentBones [ MAXSTUDIOBONES ] ;
const float boneDt = 0.1f ;
GetRagdollInitBoneArrays ( boneDelta0 , boneDelta1 , currentBones , boneDt ) ;
pRagdoll - > InitAsClientRagdoll ( boneDelta0 , boneDelta1 , currentBones , boneDt ) ;
return pRagdoll ;
}
bool C_BaseAnimating : : InitAsClientRagdoll ( const matrix3x4_t * pDeltaBones0 , const matrix3x4_t * pDeltaBones1 , const matrix3x4_t * pCurrentBonePosition , float boneDt , bool bFixedConstraints )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr | | m_pRagdoll | | m_builtRagdoll )
return false ;
m_builtRagdoll = true ;
// Store off our old mins & maxs
m_vecPreRagdollMins = WorldAlignMins ( ) ;
m_vecPreRagdollMaxs = WorldAlignMaxs ( ) ;
// Force MOVETYPE_STEP interpolation
MoveType_t savedMovetype = GetMoveType ( ) ;
SetMoveType ( MOVETYPE_STEP ) ;
// HACKHACK: force time to last interpolation position
m_flPlaybackRate = 1 ;
m_pRagdoll = CreateRagdoll ( this , hdr , m_vecForce , m_nForceBone , pDeltaBones0 , pDeltaBones1 , pCurrentBonePosition , boneDt , bFixedConstraints ) ;
// Cause the entity to recompute its shadow type and make a
// version which only updates when physics state changes
// NOTE: We have to do this after m_pRagdoll is assigned above
// because that's what ShadowCastType uses to figure out which type of shadow to use.
DestroyShadow ( ) ;
CreateShadow ( ) ;
// Cache off ragdoll bone positions/quaternions
if ( m_bStoreRagdollInfo & & m_pRagdoll )
{
matrix3x4_t parentTransform ;
AngleMatrix ( GetAbsAngles ( ) , GetAbsOrigin ( ) , parentTransform ) ;
// FIXME/CHECK: This might be too expensive to do every frame???
SaveRagdollInfo ( hdr - > numbones ( ) , parentTransform , m_BoneAccessor ) ;
}
SetMoveType ( savedMovetype ) ;
// Now set the dieragdoll sequence to get transforms for all
// non-simulated bones
m_nRestoreSequence = GetSequence ( ) ;
SetSequence ( SelectWeightedSequence ( ACT_DIERAGDOLL ) ) ;
m_nPrevSequence = GetSequence ( ) ;
m_flPlaybackRate = 0 ;
UpdatePartitionListEntry ( ) ;
NoteRagdollCreationTick ( this ) ;
UpdateVisibility ( ) ;
# if defined( REPLAY_ENABLED )
// If Replay is enabled on server, add an entry to the ragdoll recorder for this entity
ConVar * pReplayEnable = ( ConVar * ) cvar - > FindVar ( " replay_enable " ) ;
if ( m_pRagdoll & & pReplayEnable & & pReplayEnable - > GetInt ( ) & & ! engine - > IsPlayingDemo ( ) & & ! engine - > IsPlayingTimeDemo ( ) )
{
CReplayRagdollRecorder & RagdollRecorder = CReplayRagdollRecorder : : Instance ( ) ;
int nStartTick = TIME_TO_TICKS ( engine - > GetLastTimeStamp ( ) ) ;
RagdollRecorder . AddEntry ( this , nStartTick , m_pRagdoll - > RagdollBoneCount ( ) ) ;
}
# endif
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : bnewentity -
//-----------------------------------------------------------------------------
void C_BaseAnimating : : OnDataChanged ( DataUpdateType_t updateType )
{
// don't let server change sequences after becoming a ragdoll
if ( m_pRagdoll & & GetSequence ( ) ! = m_nPrevSequence )
{
SetSequence ( m_nPrevSequence ) ;
m_flPlaybackRate = 0 ;
}
if ( ! m_pRagdoll & & m_nRestoreSequence ! = - 1 )
{
SetSequence ( m_nRestoreSequence ) ;
m_nRestoreSequence = - 1 ;
}
if ( updateType = = DATA_UPDATE_CREATED )
{
m_nPrevSequence = - 1 ;
m_nRestoreSequence = - 1 ;
}
bool modelchanged = false ;
// UNDONE: The base class does this as well. So this is kind of ugly
// but getting a model by index is pretty cheap...
const model_t * pModel = modelinfo - > GetModel ( GetModelIndex ( ) ) ;
if ( pModel ! = GetModel ( ) )
{
modelchanged = true ;
}
BaseClass : : OnDataChanged ( updateType ) ;
if ( ( updateType = = DATA_UPDATE_CREATED ) | | modelchanged )
{
ResetLatched ( ) ;
// if you have this pose parameter, activate HL1-style lipsync/wave envelope tracking
if ( LookupPoseParameter ( LIPSYNC_POSEPARAM_NAME ) ! = - 1 )
{
MouthInfo ( ) . ActivateEnvelope ( ) ;
}
}
// If there's a significant change, make sure the shadow updates
if ( modelchanged | | ( GetSequence ( ) ! = m_nPrevSequence ) )
{
InvalidatePhysicsRecursive ( ANIMATION_CHANGED ) ;
m_nPrevSequence = GetSequence ( ) ;
}
// Only need to think if animating client side
if ( m_bClientSideAnimation )
{
// Check to see if we should reset our frame
if ( m_bClientSideFrameReset ! = m_bLastClientSideFrameReset )
{
ResetClientsideFrame ( ) ;
}
}
// build a ragdoll if necessary
if ( m_nRenderFX = = kRenderFxRagdoll & & ! m_builtRagdoll )
{
BecomeRagdollOnClient ( ) ;
}
//HACKHACK!!!
if ( m_nRenderFX = = kRenderFxRagdoll & & m_builtRagdoll = = true )
{
if ( m_pRagdoll = = NULL )
AddEffects ( EF_NODRAW ) ;
}
if ( m_pRagdoll & & m_nRenderFX ! = kRenderFxRagdoll )
{
ClearRagdoll ( ) ;
}
// If ragdolling and get EF_NOINTERP, we probably were dead and are now respawning,
// don't do blend out of ragdoll at respawn spot.
if ( IsNoInterpolationFrame ( ) & &
m_pRagdollInfo & &
m_pRagdollInfo - > m_bActive )
{
Msg ( " delete ragdoll due to nointerp \n " ) ;
// Remove ragdoll info
delete m_pRagdollInfo ;
m_pRagdollInfo = NULL ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseAnimating : : AddEntity ( void )
{
// Server says don't interpolate this frame, so set previous info to new info.
if ( IsNoInterpolationFrame ( ) )
{
ResetLatched ( ) ;
}
BaseClass : : AddEntity ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Get the index of the attachment point with the specified name
//-----------------------------------------------------------------------------
int C_BaseAnimating : : LookupAttachment ( const char * pAttachmentName )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
{
return - 1 ;
}
// NOTE: Currently, the network uses 0 to mean "no attachment"
// thus the client must add one to the index of the attachment
// UNDONE: Make the server do this too to be consistent.
return Studio_FindAttachment ( hdr , pAttachmentName ) + 1 ;
}
//-----------------------------------------------------------------------------
// Purpose: Get a random index of an attachment point with the specified substring in its name
//-----------------------------------------------------------------------------
int C_BaseAnimating : : LookupRandomAttachment ( const char * pAttachmentNameSubstring )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
{
return - 1 ;
}
// NOTE: Currently, the network uses 0 to mean "no attachment"
// thus the client must add one to the index of the attachment
// UNDONE: Make the server do this too to be consistent.
return Studio_FindRandomAttachment ( hdr , pAttachmentNameSubstring ) + 1 ;
}
void C_BaseAnimating : : ClientSideAnimationChanged ( )
{
if ( ! m_bClientSideAnimation | | m_ClientSideAnimationListHandle = = INVALID_CLIENTSIDEANIMATION_LIST_HANDLE )
return ;
MDLCACHE_CRITICAL_SECTION ( ) ;
clientanimating_t & anim = g_ClientSideAnimationList . Element ( m_ClientSideAnimationListHandle ) ;
Assert ( anim . pAnimating = = this ) ;
anim . flags = ComputeClientSideAnimationFlags ( ) ;
m_SequenceTransitioner . CheckForSequenceChange (
GetModelPtr ( ) ,
GetSequence ( ) ,
m_nNewSequenceParity ! = m_nPrevNewSequenceParity ,
! IsNoInterpolationFrame ( )
) ;
}
unsigned int C_BaseAnimating : : ComputeClientSideAnimationFlags ( )
{
return FCLIENTANIM_SEQUENCE_CYCLE ;
}
void C_BaseAnimating : : UpdateClientSideAnimation ( )
{
// Update client side animation
if ( m_bClientSideAnimation )
{
Assert ( m_ClientSideAnimationListHandle ! = INVALID_CLIENTSIDEANIMATION_LIST_HANDLE ) ;
if ( GetSequence ( ) ! = - 1 )
{
// latch old values
OnLatchInterpolatedVariables ( LATCH_ANIMATION_VAR ) ;
// move frame forward
FrameAdvance ( 0.0f ) ; // 0 means to use the time we last advanced instead of a constant
}
}
else
{
Assert ( m_ClientSideAnimationListHandle = = INVALID_CLIENTSIDEANIMATION_LIST_HANDLE ) ;
}
}
void C_BaseAnimating : : Simulate ( )
{
if ( m_bInitModelEffects )
{
DelayedInitModelEffects ( ) ;
}
if ( gpGlobals - > frametime ! = 0.0f )
{
DoAnimationEvents ( GetModelPtr ( ) ) ;
}
BaseClass : : Simulate ( ) ;
if ( IsNoInterpolationFrame ( ) )
{
ResetLatched ( ) ;
}
if ( GetSequence ( ) ! = - 1 & & m_pRagdoll & & ( m_nRenderFX ! = kRenderFxRagdoll ) )
{
ClearRagdoll ( ) ;
}
}
bool C_BaseAnimating : : TestCollision ( const Ray_t & ray , unsigned int fContentsMask , trace_t & tr )
{
if ( ray . m_IsRay & & IsSolidFlagSet ( FSOLID_CUSTOMRAYTEST ) )
{
if ( ! TestHitboxes ( ray , fContentsMask , tr ) )
return true ;
return tr . DidHit ( ) ;
}
if ( ! ray . m_IsRay & & IsSolidFlagSet ( FSOLID_CUSTOMBOXTEST ) )
{
if ( ! TestHitboxes ( ray , fContentsMask , tr ) )
return true ;
return true ;
}
// We shouldn't get here.
Assert ( 0 ) ;
return false ;
}
// UNDONE: This almost works. The client entities have no control over their solid box
// Also they have no ability to expose FSOLID_ flags to the engine to force the accurate
// collision tests.
// Add those and the client hitboxes will be robust
bool C_BaseAnimating : : TestHitboxes ( const Ray_t & ray , unsigned int fContentsMask , trace_t & tr )
{
VPROF ( " C_BaseAnimating::TestHitboxes " ) ;
MDLCACHE_CRITICAL_SECTION ( ) ;
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( ! pStudioHdr )
return false ;
mstudiohitboxset_t * set = pStudioHdr - > pHitboxSet ( m_nHitboxSet ) ;
if ( ! set | | ! set - > numhitboxes )
return false ;
// Use vcollide for box traces.
if ( ! ray . m_IsRay )
return false ;
// This *has* to be true for the existing code to function correctly.
Assert ( ray . m_StartOffset = = vec3_origin ) ;
CBoneCache * pCache = GetBoneCache ( pStudioHdr ) ;
matrix3x4_t * hitboxbones [ MAXSTUDIOBONES ] ;
pCache - > ReadCachedBonePointers ( hitboxbones , pStudioHdr - > numbones ( ) ) ;
if ( TraceToStudio ( physprops , ray , pStudioHdr , set , hitboxbones , fContentsMask , GetRenderOrigin ( ) , GetModelScale ( ) , tr ) )
{
mstudiobbox_t * pbox = set - > pHitbox ( tr . hitbox ) ;
mstudiobone_t * pBone = pStudioHdr - > pBone ( pbox - > bone ) ;
tr . surface . name = " **studio** " ;
tr . surface . flags = SURF_HITBOX ;
tr . surface . surfaceProps = physprops - > GetSurfaceIndex ( pBone - > pszSurfaceProp ( ) ) ;
if ( IsRagdoll ( ) )
{
IPhysicsObject * pReplace = m_pRagdoll - > GetElement ( tr . physicsbone ) ;
if ( pReplace )
{
VPhysicsSetObject ( NULL ) ;
VPhysicsSetObject ( pReplace ) ;
}
}
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Check sequence framerate
// Input : iSequence -
// Output : float
//-----------------------------------------------------------------------------
float C_BaseAnimating : : GetSequenceCycleRate ( CStudioHdr * pStudioHdr , int iSequence )
{
if ( ! pStudioHdr )
return 0.0f ;
return Studio_CPS ( pStudioHdr , pStudioHdr - > pSeqdesc ( iSequence ) , iSequence , m_flPoseParameter ) ;
}
float C_BaseAnimating : : GetAnimTimeInterval ( void ) const
{
# define MAX_ANIMTIME_INTERVAL 0.2f
float flInterval = MIN ( gpGlobals - > curtime - m_flAnimTime , MAX_ANIMTIME_INTERVAL ) ;
return flInterval ;
}
//-----------------------------------------------------------------------------
// Sets the cycle, marks the entity as being dirty
//-----------------------------------------------------------------------------
void C_BaseAnimating : : SetCycle ( float flCycle )
{
if ( m_flCycle ! = flCycle )
{
m_flCycle = flCycle ;
InvalidatePhysicsRecursive ( ANIMATION_CHANGED ) ;
}
}
//-----------------------------------------------------------------------------
// Sets the sequence, marks the entity as being dirty
//-----------------------------------------------------------------------------
void C_BaseAnimating : : SetSequence ( int nSequence )
{
if ( m_nSequence ! = nSequence )
{
/*
CStudioHdr * hdr = GetModelPtr ( ) ;
// Assert( hdr );
if ( hdr )
{
Assert ( nSequence > = 0 & & nSequence < hdr - > GetNumSeq ( ) ) ;
}
*/
m_nSequence = nSequence ;
InvalidatePhysicsRecursive ( ANIMATION_CHANGED ) ;
if ( m_bClientSideAnimation )
{
ClientSideAnimationChanged ( ) ;
}
}
}
//=========================================================
// StudioFrameAdvance - advance the animation frame up some interval (default 0.1) into the future
//=========================================================
void C_BaseAnimating : : StudioFrameAdvance ( )
{
if ( m_bClientSideAnimation )
return ;
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
return ;
# ifdef DEBUG
bool watch = dbganimmodel . GetString ( ) [ 0 ] & & V_stristr ( hdr - > pszName ( ) , dbganimmodel . GetString ( ) ) ;
# else
bool watch = false ; // Q_strstr( hdr->name, "rifle" ) ? true : false;
# endif
//if (!anim.prevanimtime)
//{
//anim.prevanimtime = m_flAnimTime = gpGlobals->curtime;
//}
// How long since last animtime
float flInterval = GetAnimTimeInterval ( ) ;
if ( flInterval < = 0.001 )
{
// Msg("%s : %s : %5.3f (skip)\n", STRING(pev->classname), GetSequenceName( GetSequence() ), GetCycle() );
return ;
}
UpdateModelScale ( ) ;
//anim.prevanimtime = m_flAnimTime;
float cycleAdvance = flInterval * GetSequenceCycleRate ( hdr , GetSequence ( ) ) * m_flPlaybackRate ;
float flNewCycle = GetCycle ( ) + cycleAdvance ;
m_flAnimTime = gpGlobals - > curtime ;
if ( watch )
{
Msg ( " %s %6.3f : %6.3f (%.3f) \n " , GetClassname ( ) , gpGlobals - > curtime , m_flAnimTime , flInterval ) ;
}
if ( flNewCycle < 0.0f | | flNewCycle > = 1.0f )
{
if ( IsSequenceLooping ( hdr , GetSequence ( ) ) )
{
flNewCycle - = ( int ) ( flNewCycle ) ;
}
else
{
flNewCycle = ( flNewCycle < 0.0f ) ? 0.0f : 1.0f ;
}
m_bSequenceFinished = true ; // just in case it wasn't caught in GetEvents
}
SetCycle ( flNewCycle ) ;
m_flGroundSpeed = GetSequenceGroundSpeed ( hdr , GetSequence ( ) ) * GetModelScale ( ) ;
#if 0
// I didn't have a test case for this, but it seems like the right thing to do. Check multi-player!
// Msg("%s : %s : %5.1f\n", GetClassname(), GetSequenceName( GetSequence() ), GetCycle() );
InvalidatePhysicsRecursive ( ANIMATION_CHANGED ) ;
# endif
if ( watch )
{
Msg ( " %s : %s : %5.1f \n " , GetClassname ( ) , GetSequenceName ( GetSequence ( ) ) , GetCycle ( ) ) ;
}
}
float C_BaseAnimating : : GetSequenceGroundSpeed ( CStudioHdr * pStudioHdr , int iSequence )
{
float t = SequenceDuration ( pStudioHdr , iSequence ) ;
if ( t > 0 )
{
return GetSequenceMoveDist ( pStudioHdr , iSequence ) / t ;
}
else
{
return 0 ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//
// Input : iSequence -
//
// Output : float
//-----------------------------------------------------------------------------
float C_BaseAnimating : : GetSequenceMoveDist ( CStudioHdr * pStudioHdr , int iSequence )
{
Vector vecReturn ;
: : GetSequenceLinearMotion ( pStudioHdr , iSequence , m_flPoseParameter , & vecReturn ) ;
return vecReturn . Length ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//
// Input : iSequence -
// *pVec -
//
//-----------------------------------------------------------------------------
void C_BaseAnimating : : GetSequenceLinearMotion ( int iSequence , Vector * pVec )
{
: : GetSequenceLinearMotion ( GetModelPtr ( ) , iSequence , m_flPoseParameter , pVec ) ;
}
void C_BaseAnimating : : GetBlendedLinearVelocity ( Vector * pVec )
{
Vector vecDist ;
float flDuration ;
GetSequenceLinearMotion ( GetSequence ( ) , & vecDist ) ;
flDuration = SequenceDuration ( GetSequence ( ) ) ;
VectorScale ( vecDist , 1.0 / flDuration , * pVec ) ;
Vector tmp ;
for ( int i = m_SequenceTransitioner . m_animationQueue . Count ( ) - 2 ; i > = 0 ; i - - )
{
C_AnimationLayer * blend = & m_SequenceTransitioner . m_animationQueue [ i ] ;
GetSequenceLinearMotion ( blend - > m_nSequence , & vecDist ) ;
flDuration = SequenceDuration ( blend - > m_nSequence ) ;
VectorScale ( vecDist , 1.0 / flDuration , tmp ) ;
float flWeight = blend - > GetFadeout ( gpGlobals - > curtime ) ;
* pVec = Lerp ( flWeight , * pVec , tmp ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : flInterval -
// Output : float
//-----------------------------------------------------------------------------
float C_BaseAnimating : : FrameAdvance ( float flInterval )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
return 0.0f ;
# ifdef DEBUG
bool bWatch = dbganimmodel . GetString ( ) [ 0 ] & & V_stristr ( hdr - > pszName ( ) , dbganimmodel . GetString ( ) ) ;
# else
bool bWatch = false ; // Q_strstr( hdr->name, "medkit_large" ) ? true : false;
# endif
float curtime = gpGlobals - > curtime ;
if ( flInterval = = 0.0f )
{
flInterval = ( curtime - m_flAnimTime ) ;
if ( flInterval < = 0.001f )
{
return 0.0f ;
}
}
if ( ! m_flAnimTime )
{
flInterval = 0.0f ;
}
float cyclerate = GetSequenceCycleRate ( hdr , GetSequence ( ) ) ;
float addcycle = flInterval * cyclerate * m_flPlaybackRate ;
if ( GetServerIntendedCycle ( ) ! = - 1.0f )
{
// The server would like us to ease in a correction so that we will animate the same on the client and server.
// So we will actually advance the average of what we would have done and what the server wants.
float serverCycle = GetServerIntendedCycle ( ) ;
float serverAdvance = serverCycle - GetCycle ( ) ;
bool adjustOkay = serverAdvance > 0.0f ; // only want to go forward. backing up looks really jarring, even when slight
if ( serverAdvance < - 0.8f )
{
// Oh wait, it was just a wraparound from .9 to .1.
serverAdvance + = 1 ;
adjustOkay = true ;
}
if ( adjustOkay )
{
float originalAdvance = addcycle ;
addcycle = ( serverAdvance + addcycle ) / 2 ;
const float MAX_CYCLE_ADJUSTMENT = 0.1f ;
addcycle = MIN ( MAX_CYCLE_ADJUSTMENT , addcycle ) ; // Don't do too big of a jump; it's too jarring as well.
DevMsg ( 2 , " (%d): Cycle latch used to correct %.2f in to %.2f instead of %.2f. \n " ,
entindex ( ) , GetCycle ( ) , GetCycle ( ) + addcycle , GetCycle ( ) + originalAdvance ) ;
}
SetServerIntendedCycle ( - 1.0f ) ; // Only use a correction once, it isn't valid any time but right now.
}
float flNewCycle = GetCycle ( ) + addcycle ;
m_flAnimTime = curtime ;
if ( bWatch )
{
Msg ( " %i CLIENT Time: %6.3f : (Interval %f) : cycle %f rate %f add %f \n " ,
gpGlobals - > tickcount , gpGlobals - > curtime , flInterval , flNewCycle , cyclerate , addcycle ) ;
}
if ( ( flNewCycle < 0.0f ) | | ( flNewCycle > = 1.0f ) )
{
if ( IsSequenceLooping ( hdr , GetSequence ( ) ) )
{
flNewCycle - = ( int ) ( flNewCycle ) ;
}
else
{
flNewCycle = ( flNewCycle < 0.0f ) ? 0.0f : 1.0f ;
}
m_bSequenceFinished = true ;
}
SetCycle ( flNewCycle ) ;
return flInterval ;
}
// Stubs for weapon prediction
void C_BaseAnimating : : ResetSequenceInfo ( void )
{
if ( GetSequence ( ) = = - 1 )
{
SetSequence ( 0 ) ;
}
if ( IsDynamicModelLoading ( ) )
{
m_bResetSequenceInfoOnLoad = true ;
return ;
}
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
m_flGroundSpeed = GetSequenceGroundSpeed ( pStudioHdr , GetSequence ( ) ) * GetModelScale ( ) ;
m_bSequenceLoops = ( ( GetSequenceFlags ( pStudioHdr , GetSequence ( ) ) & STUDIO_LOOPING ) ! = 0 ) ;
// m_flAnimTime = gpGlobals->time;
m_flPlaybackRate = 1.0 ;
m_bSequenceFinished = false ;
m_flLastEventCheck = 0 ;
m_nNewSequenceParity = ( m_nNewSequenceParity + 1 ) & EF_PARITY_MASK ;
m_nResetEventsParity = ( m_nResetEventsParity + 1 ) & EF_PARITY_MASK ;
// FIXME: why is this called here? Nothing should have changed to make this nessesary
SetEventIndexForSequence ( pStudioHdr - > pSeqdesc ( GetSequence ( ) ) ) ;
}
//=========================================================
//=========================================================
bool C_BaseAnimating : : IsSequenceLooping ( CStudioHdr * pStudioHdr , int iSequence )
{
return ( : : GetSequenceFlags ( pStudioHdr , iSequence ) & STUDIO_LOOPING ) ! = 0 ;
}
float C_BaseAnimating : : SequenceDuration ( CStudioHdr * pStudioHdr , int iSequence )
{
if ( ! pStudioHdr )
{
return 0.1f ;
}
if ( iSequence > = pStudioHdr - > GetNumSeq ( ) | | iSequence < 0 )
{
DevWarning ( 2 , " C_BaseAnimating::SequenceDuration( %d ) out of range \n " , iSequence ) ;
return 0.1 ;
}
return Studio_Duration ( pStudioHdr , iSequence , m_flPoseParameter ) ;
}
int C_BaseAnimating : : FindTransitionSequence ( int iCurrentSequence , int iGoalSequence , int * piDir )
{
CStudioHdr * hdr = GetModelPtr ( ) ;
if ( ! hdr )
{
return - 1 ;
}
if ( piDir = = NULL )
{
int iDir = 1 ;
int sequence = : : FindTransitionSequence ( hdr , iCurrentSequence , iGoalSequence , & iDir ) ;
if ( iDir ! = 1 )
return - 1 ;
else
return sequence ;
}
return : : FindTransitionSequence ( hdr , iCurrentSequence , iGoalSequence , piDir ) ;
}
void C_BaseAnimating : : SetBodygroup ( int iGroup , int iValue )
{
// SetBodygroup is not supported on pending dynamic models. Wait for it to load!
// XXX TODO we could buffer up the group and value if we really needed to. -henryg
Assert ( GetModelPtr ( ) ) ;
: : SetBodygroup ( GetModelPtr ( ) , m_nBody , iGroup , iValue ) ;
}
int C_BaseAnimating : : GetBodygroup ( int iGroup )
{
Assert ( IsDynamicModelLoading ( ) | | GetModelPtr ( ) ) ;
return IsDynamicModelLoading ( ) ? 0 : : : GetBodygroup ( GetModelPtr ( ) , m_nBody , iGroup ) ;
}
const char * C_BaseAnimating : : GetBodygroupName ( int iGroup )
{
Assert ( IsDynamicModelLoading ( ) | | GetModelPtr ( ) ) ;
return IsDynamicModelLoading ( ) ? " " : : : GetBodygroupName ( GetModelPtr ( ) , iGroup ) ;
}
int C_BaseAnimating : : FindBodygroupByName ( const char * name )
{
Assert ( IsDynamicModelLoading ( ) | | GetModelPtr ( ) ) ;
return IsDynamicModelLoading ( ) ? - 1 : : : FindBodygroupByName ( GetModelPtr ( ) , name ) ;
}
int C_BaseAnimating : : GetBodygroupCount ( int iGroup )
{
Assert ( IsDynamicModelLoading ( ) | | GetModelPtr ( ) ) ;
return IsDynamicModelLoading ( ) ? 0 : : : GetBodygroupCount ( GetModelPtr ( ) , iGroup ) ;
}
int C_BaseAnimating : : GetNumBodyGroups ( void )
{
Assert ( IsDynamicModelLoading ( ) | | GetModelPtr ( ) ) ;
return IsDynamicModelLoading ( ) ? 0 : : : GetNumBodyGroups ( GetModelPtr ( ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : setnum -
//-----------------------------------------------------------------------------
void C_BaseAnimating : : SetHitboxSet ( int setnum )
{
if ( IsDynamicModelLoading ( ) )
return ;
# ifdef _DEBUG
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( ! pStudioHdr )
return ;
if ( setnum > pStudioHdr - > numhitboxsets ( ) )
{
// Warn if an bogus hitbox set is being used....
static bool s_bWarned = false ;
if ( ! s_bWarned )
{
Warning ( " Using bogus hitbox set in entity %s! \n " , GetClassname ( ) ) ;
s_bWarned = true ;
}
setnum = 0 ;
}
# endif
m_nHitboxSet = setnum ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *setname -
//-----------------------------------------------------------------------------
void C_BaseAnimating : : SetHitboxSetByName ( const char * setname )
{
if ( IsDynamicModelLoading ( ) )
return ;
m_nHitboxSet = FindHitboxSetByName ( GetModelPtr ( ) , setname ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int C_BaseAnimating : : GetHitboxSet ( void )
{
return m_nHitboxSet ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : char const
//-----------------------------------------------------------------------------
const char * C_BaseAnimating : : GetHitboxSetName ( void )
{
if ( IsDynamicModelLoading ( ) )
return " " ;
return : : GetHitboxSetName ( GetModelPtr ( ) , m_nHitboxSet ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int C_BaseAnimating : : GetHitboxSetCount ( void )
{
if ( IsDynamicModelLoading ( ) )
return 0 ;
return : : GetHitboxSetCount ( GetModelPtr ( ) ) ;
}
static Vector hullcolor [ 8 ] =
{
Vector ( 1.0 , 1.0 , 1.0 ) ,
Vector ( 1.0 , 0.5 , 0.5 ) ,
Vector ( 0.5 , 1.0 , 0.5 ) ,
Vector ( 1.0 , 1.0 , 0.5 ) ,
Vector ( 0.5 , 0.5 , 1.0 ) ,
Vector ( 1.0 , 0.5 , 1.0 ) ,
Vector ( 0.5 , 1.0 , 1.0 ) ,
Vector ( 1.0 , 1.0 , 1.0 )
} ;
//-----------------------------------------------------------------------------
// Purpose: Draw the current hitboxes
//-----------------------------------------------------------------------------
void C_BaseAnimating : : DrawClientHitboxes ( float duration /*= 0.0f*/ , bool monocolor /*= false*/ )
{
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( ! pStudioHdr )
return ;
mstudiohitboxset_t * set = pStudioHdr - > pHitboxSet ( m_nHitboxSet ) ;
if ( ! set )
return ;
Vector position ;
QAngle angles ;
int r = 255 ;
int g = 0 ;
int b = 0 ;
for ( int i = 0 ; i < set - > numhitboxes ; i + + )
{
mstudiobbox_t * pbox = set - > pHitbox ( i ) ;
GetBonePosition ( pbox - > bone , position , angles ) ;
if ( ! monocolor )
{
int j = ( pbox - > group % 8 ) ;
r = ( int ) ( 255.0f * hullcolor [ j ] [ 0 ] ) ;
g = ( int ) ( 255.0f * hullcolor [ j ] [ 1 ] ) ;
b = ( int ) ( 255.0f * hullcolor [ j ] [ 2 ] ) ;
}
debugoverlay - > AddBoxOverlay ( position , pbox - > bbmin , pbox - > bbmax , angles , r , g , b , 0 , duration ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : activity -
// Output : int C_BaseAnimating::SelectWeightedSequence
//-----------------------------------------------------------------------------
int C_BaseAnimating : : SelectWeightedSequence ( int activity )
{
Assert ( activity ! = ACT_INVALID ) ;
return : : SelectWeightedSequence ( GetModelPtr ( ) , activity ) ;
}
//=========================================================
//=========================================================
int C_BaseAnimating : : LookupPoseParameter ( CStudioHdr * pstudiohdr , const char * szName )
{
if ( ! pstudiohdr )
return 0 ;
for ( int i = 0 ; i < pstudiohdr - > GetNumPoseParameters ( ) ; i + + )
{
if ( stricmp ( pstudiohdr - > pPoseParameter ( i ) . pszName ( ) , szName ) = = 0 )
{
return i ;
}
}
// AssertMsg( 0, UTIL_VarArgs( "poseparameter %s couldn't be mapped!!!\n", szName ) );
return - 1 ; // Error
}
//=========================================================
//=========================================================
float C_BaseAnimating : : SetPoseParameter ( CStudioHdr * pStudioHdr , const char * szName , float flValue )
{
return SetPoseParameter ( pStudioHdr , LookupPoseParameter ( pStudioHdr , szName ) , flValue ) ;
}
float C_BaseAnimating : : SetPoseParameter ( CStudioHdr * pStudioHdr , int iParameter , float flValue )
{
if ( ! pStudioHdr )
{
Assert ( ! " C_BaseAnimating::SetPoseParameter: model missing " ) ;
return flValue ;
}
if ( iParameter > = 0 )
{
float flNewValue ;
flValue = Studio_SetPoseParameter ( pStudioHdr , iParameter , flValue , flNewValue ) ;
m_flPoseParameter [ iParameter ] = flNewValue ;
}
return flValue ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *label -
// Output : int
//-----------------------------------------------------------------------------
int C_BaseAnimating : : LookupSequence ( const char * label )
{
Assert ( GetModelPtr ( ) ) ;
return : : LookupSequence ( GetModelPtr ( ) , label ) ;
}
void C_BaseAnimating : : Release ( )
{
ClearRagdoll ( ) ;
BaseClass : : Release ( ) ;
}
void C_BaseAnimating : : Clear ( void )
{
InvalidateMdlCache ( ) ;
Q_memset ( & m_mouth , 0 , sizeof ( m_mouth ) ) ;
m_flCycle = 0 ;
m_flOldCycle = 0 ;
m_bResetSequenceInfoOnLoad = false ;
m_bDynamicModelPending = false ;
m_AutoRefModelIndex . Clear ( ) ;
BaseClass : : Clear ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Clear current ragdoll
//-----------------------------------------------------------------------------
void C_BaseAnimating : : ClearRagdoll ( )
{
if ( m_pRagdoll )
{
// immediately mark the member ragdoll as being NULL,
// so that we have no reentrancy problems with the delete
// (such as the disappearance of the ragdoll physics waking up
// IVP which causes other objects to move and have a touch
// callback on the ragdoll entity, which was a crash on TF)
// That is to say: it is vital that the member be cleared out
// BEFORE the delete occurs.
CRagdoll * RESTRICT pDoomed = m_pRagdoll ;
m_pRagdoll = NULL ;
delete pDoomed ;
// Set to null so that the destructor's call to DestroyObject won't destroy
// m_pObjects[ 0 ] twice since that's the physics object for the prop
VPhysicsSetObject ( NULL ) ;
// If we have ragdoll mins/maxs, we've just come out of ragdoll, so restore them
if ( m_vecPreRagdollMins ! = vec3_origin | | m_vecPreRagdollMaxs ! = vec3_origin )
{
SetCollisionBounds ( m_vecPreRagdollMins , m_vecPreRagdollMaxs ) ;
}
# if defined( REPLAY_ENABLED )
// Delete entry from ragdoll recorder if Replay is enabled on server
ConVar * pReplayEnable = ( ConVar * ) cvar - > FindVar ( " replay_enable " ) ;
if ( pReplayEnable & & pReplayEnable - > GetInt ( ) & & ! engine - > IsPlayingDemo ( ) & & ! engine - > IsPlayingTimeDemo ( ) )
{
CReplayRagdollRecorder & RagdollRecorder = CReplayRagdollRecorder : : Instance ( ) ;
RagdollRecorder . StopRecordingRagdoll ( this ) ;
}
# endif
}
m_builtRagdoll = false ;
}
//-----------------------------------------------------------------------------
// Purpose: Looks up an activity by name.
// Input : label - Name of the activity, ie "ACT_IDLE".
// Output : Returns the activity ID or ACT_INVALID.
//-----------------------------------------------------------------------------
int C_BaseAnimating : : LookupActivity ( const char * label )
{
Assert ( GetModelPtr ( ) ) ;
return : : LookupActivity ( GetModelPtr ( ) , label ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//
// Input : iSequence -
//
// Output : char
//-----------------------------------------------------------------------------
const char * C_BaseAnimating : : GetSequenceActivityName ( int iSequence )
{
if ( iSequence = = - 1 )
{
return " Not Found! " ;
}
if ( ! GetModelPtr ( ) )
return " No model! " ;
return : : GetSequenceActivityName ( GetModelPtr ( ) , iSequence ) ;
}
//=========================================================
//=========================================================
float C_BaseAnimating : : SetBoneController ( int iController , float flValue )
{
Assert ( GetModelPtr ( ) ) ;
CStudioHdr * pmodel = GetModelPtr ( ) ;
Assert ( iController > = 0 & & iController < NUM_BONECTRLS ) ;
float controller = m_flEncodedController [ iController ] ;
float retVal = Studio_SetController ( pmodel , iController , flValue , controller ) ;
m_flEncodedController [ iController ] = controller ;
return retVal ;
}
void C_BaseAnimating : : GetAimEntOrigin ( IClientEntity * pAttachedTo , Vector * pAbsOrigin , QAngle * pAbsAngles )
{
CBaseEntity * pMoveParent ;
if ( IsEffectActive ( EF_BONEMERGE ) & & IsEffectActive ( EF_BONEMERGE_FASTCULL ) & & ( pMoveParent = GetMoveParent ( ) ) ! = NULL )
{
// Doing this saves a lot of CPU.
* pAbsOrigin = pMoveParent - > WorldSpaceCenter ( ) ;
* pAbsAngles = pMoveParent - > GetRenderAngles ( ) ;
}
else
{
if ( ! m_pBoneMergeCache | | ! m_pBoneMergeCache - > GetAimEntOrigin ( pAbsOrigin , pAbsAngles ) )
BaseClass : : GetAimEntOrigin ( pAttachedTo , pAbsOrigin , pAbsAngles ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//
// Input : iSequence -
//
// Output : char
//-----------------------------------------------------------------------------
const char * C_BaseAnimating : : GetSequenceName ( int iSequence )
{
if ( iSequence = = - 1 )
{
return " Not Found! " ;
}
if ( ! GetModelPtr ( ) )
return " No model! " ;
return : : GetSequenceName ( GetModelPtr ( ) , iSequence ) ;
}
Activity C_BaseAnimating : : GetSequenceActivity ( int iSequence )
{
if ( iSequence = = - 1 )
{
return ACT_INVALID ;
}
if ( ! GetModelPtr ( ) )
return ACT_INVALID ;
return ( Activity ) : : GetSequenceActivity ( GetModelPtr ( ) , iSequence ) ;
}
//-----------------------------------------------------------------------------
// returns the sequence keyvalue text as a KeyValues pointer
//-----------------------------------------------------------------------------
KeyValues * C_BaseAnimating : : GetSequenceKeyValues ( int iSequence )
{
const char * szText = Studio_GetKeyValueText ( GetModelPtr ( ) , iSequence ) ;
if ( szText )
{
KeyValues * seqKeyValues = new KeyValues ( " " ) ;
if ( seqKeyValues - > LoadFromBuffer ( modelinfo - > GetModelName ( GetModel ( ) ) , szText ) )
{
return seqKeyValues ;
}
seqKeyValues - > deleteThis ( ) ;
}
return NULL ;
}
//-----------------------------------------------------------------------------
// Computes a box that surrounds all hitboxes
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : ComputeHitboxSurroundingBox ( Vector * pVecWorldMins , Vector * pVecWorldMaxs )
{
// Note that this currently should not be called during position recomputation because of IK.
// The code below recomputes bones so as to get at the hitboxes,
// which causes IK to trigger, which causes raycasts against the other entities to occur,
// which is illegal to do while in the computeabsposition phase.
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( ! pStudioHdr )
return false ;
mstudiohitboxset_t * set = pStudioHdr - > pHitboxSet ( m_nHitboxSet ) ;
if ( ! set | | ! set - > numhitboxes )
return false ;
CBoneCache * pCache = GetBoneCache ( pStudioHdr ) ;
matrix3x4_t * hitboxbones [ MAXSTUDIOBONES ] ;
pCache - > ReadCachedBonePointers ( hitboxbones , pStudioHdr - > numbones ( ) ) ;
// Compute a box in world space that surrounds this entity
pVecWorldMins - > Init ( FLT_MAX , FLT_MAX , FLT_MAX ) ;
pVecWorldMaxs - > Init ( - FLT_MAX , - FLT_MAX , - FLT_MAX ) ;
Vector vecBoxAbsMins , vecBoxAbsMaxs ;
for ( int i = 0 ; i < set - > numhitboxes ; i + + )
{
mstudiobbox_t * pbox = set - > pHitbox ( i ) ;
TransformAABB ( * hitboxbones [ pbox - > bone ] , pbox - > bbmin , pbox - > bbmax , vecBoxAbsMins , vecBoxAbsMaxs ) ;
VectorMin ( * pVecWorldMins , vecBoxAbsMins , * pVecWorldMins ) ;
VectorMax ( * pVecWorldMaxs , vecBoxAbsMaxs , * pVecWorldMaxs ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Computes a box that surrounds all hitboxes, in entity space
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : ComputeEntitySpaceHitboxSurroundingBox ( Vector * pVecWorldMins , Vector * pVecWorldMaxs )
{
// Note that this currently should not be called during position recomputation because of IK.
// The code below recomputes bones so as to get at the hitboxes,
// which causes IK to trigger, which causes raycasts against the other entities to occur,
// which is illegal to do while in the computeabsposition phase.
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( ! pStudioHdr )
return false ;
mstudiohitboxset_t * set = pStudioHdr - > pHitboxSet ( m_nHitboxSet ) ;
if ( ! set | | ! set - > numhitboxes )
return false ;
CBoneCache * pCache = GetBoneCache ( pStudioHdr ) ;
matrix3x4_t * hitboxbones [ MAXSTUDIOBONES ] ;
pCache - > ReadCachedBonePointers ( hitboxbones , pStudioHdr - > numbones ( ) ) ;
// Compute a box in world space that surrounds this entity
pVecWorldMins - > Init ( FLT_MAX , FLT_MAX , FLT_MAX ) ;
pVecWorldMaxs - > Init ( - FLT_MAX , - FLT_MAX , - FLT_MAX ) ;
matrix3x4_t worldToEntity , boneToEntity ;
MatrixInvert ( EntityToWorldTransform ( ) , worldToEntity ) ;
Vector vecBoxAbsMins , vecBoxAbsMaxs ;
for ( int i = 0 ; i < set - > numhitboxes ; i + + )
{
mstudiobbox_t * pbox = set - > pHitbox ( i ) ;
ConcatTransforms ( worldToEntity , * hitboxbones [ pbox - > bone ] , boneToEntity ) ;
TransformAABB ( boneToEntity , pbox - > bbmin , pbox - > bbmax , vecBoxAbsMins , vecBoxAbsMaxs ) ;
VectorMin ( * pVecWorldMins , vecBoxAbsMins , * pVecWorldMins ) ;
VectorMax ( * pVecWorldMaxs , vecBoxAbsMaxs , * pVecWorldMaxs ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : scale -
//-----------------------------------------------------------------------------
void C_BaseAnimating : : SetModelScale ( float scale , float change_duration /*= 0.0f*/ )
{
if ( change_duration > 0.0f )
{
ModelScale * mvs = ( ModelScale * ) CreateDataObject ( MODELSCALE ) ;
mvs - > m_flModelScaleStart = m_flModelScale ;
mvs - > m_flModelScaleGoal = scale ;
mvs - > m_flModelScaleStartTime = gpGlobals - > curtime ;
mvs - > m_flModelScaleFinishTime = mvs - > m_flModelScaleStartTime + change_duration ;
}
else
{
m_flModelScale = scale ;
RefreshCollisionBounds ( ) ;
if ( HasDataObjectType ( MODELSCALE ) )
{
DestroyDataObject ( MODELSCALE ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseAnimating : : UpdateModelScale ( )
{
ModelScale * mvs = ( ModelScale * ) GetDataObject ( MODELSCALE ) ;
if ( ! mvs )
{
return ;
}
float dt = mvs - > m_flModelScaleFinishTime - mvs - > m_flModelScaleStartTime ;
Assert ( dt > 0.0f ) ;
float frac = ( gpGlobals - > curtime - mvs - > m_flModelScaleStartTime ) / dt ;
frac = clamp ( frac , 0.0f , 1.0f ) ;
if ( gpGlobals - > curtime > = mvs - > m_flModelScaleFinishTime )
{
m_flModelScale = mvs - > m_flModelScaleGoal ;
DestroyDataObject ( MODELSCALE ) ;
}
else
{
m_flModelScale = Lerp ( frac , mvs - > m_flModelScaleStart , mvs - > m_flModelScaleGoal ) ;
}
RefreshCollisionBounds ( ) ;
}
void C_BaseAnimating : : RefreshCollisionBounds ( void )
{
CollisionProp ( ) - > RefreshScaledCollisionBounds ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Clientside bone follower class. Used just to visualize them.
// Bone followers WON'T be sent to the client if VISUALIZE_FOLLOWERS is
// undefined in the server's physics_bone_followers.cpp
//-----------------------------------------------------------------------------
class C_BoneFollower : public C_BaseEntity
{
DECLARE_CLASS ( C_BoneFollower , C_BaseEntity ) ;
DECLARE_CLIENTCLASS ( ) ;
public :
C_BoneFollower ( void )
{
}
bool ShouldDraw ( void ) ;
int DrawModel ( int flags ) ;
bool TestCollision ( const Ray_t & ray , unsigned int mask , trace_t & trace ) ;
private :
int m_modelIndex ;
int m_solidIndex ;
} ;
IMPLEMENT_CLIENTCLASS_DT ( C_BoneFollower , DT_BoneFollower , CBoneFollower )
RecvPropInt ( RECVINFO ( m_modelIndex ) ) ,
RecvPropInt ( RECVINFO ( m_solidIndex ) ) ,
END_RECV_TABLE ( )
void VCollideWireframe_ChangeCallback ( IConVar * pConVar , char const * pOldString , float flOldValue )
{
for ( C_BaseEntity * pEntity = ClientEntityList ( ) . FirstBaseEntity ( ) ; pEntity ; pEntity = ClientEntityList ( ) . NextBaseEntity ( pEntity ) )
{
pEntity - > UpdateVisibility ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns whether object should render.
//-----------------------------------------------------------------------------
bool C_BoneFollower : : ShouldDraw ( void )
{
return ( vcollide_wireframe . GetBool ( ) ) ; //MOTODO
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int C_BoneFollower : : DrawModel ( int flags )
{
vcollide_t * pCollide = modelinfo - > GetVCollide ( m_modelIndex ) ;
if ( pCollide )
{
static color32 debugColor = { 0 , 255 , 255 , 0 } ;
matrix3x4_t matrix ;
AngleMatrix ( GetAbsAngles ( ) , GetAbsOrigin ( ) , matrix ) ;
engine - > DebugDrawPhysCollide ( pCollide - > solids [ m_solidIndex ] , NULL , matrix , debugColor ) ;
}
return 1 ;
}
bool C_BoneFollower : : TestCollision ( const Ray_t & ray , unsigned int mask , trace_t & trace )
{
vcollide_t * pCollide = modelinfo - > GetVCollide ( m_modelIndex ) ;
Assert ( pCollide & & pCollide - > solidCount > m_solidIndex ) ;
physcollision - > TraceBox ( ray , pCollide - > solids [ m_solidIndex ] , GetAbsOrigin ( ) , GetAbsAngles ( ) , & trace ) ;
if ( trace . fraction > = 1 )
return false ;
// return owner as trace hit
trace . m_pEnt = GetOwnerEntity ( ) ;
trace . hitgroup = 0 ; //m_hitGroup;
trace . physicsbone = 0 ; //m_physicsBone; // UNDONE: Get physics bone index & hitgroup
return trace . DidHit ( ) ;
}
void C_BaseAnimating : : DisableMuzzleFlash ( )
{
m_nOldMuzzleFlashParity = m_nMuzzleFlashParity ;
}
void C_BaseAnimating : : DoMuzzleFlash ( )
{
m_nMuzzleFlashParity = ( m_nMuzzleFlashParity + 1 ) & ( ( 1 < < EF_MUZZLEFLASH_BITS ) - 1 ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void DevMsgRT ( char const * pMsg , . . . )
{
if ( gpGlobals - > frametime ! = 0.0f )
{
va_list argptr ;
va_start ( argptr , pMsg ) ;
//
{
static char string [ 1024 ] ;
Q_vsnprintf ( string , sizeof ( string ) , pMsg , argptr ) ;
DevMsg ( 1 , " %s " , string ) ;
}
// DevMsg( pMsg, argptr );
va_end ( argptr ) ;
}
}
void C_BaseAnimating : : ForceClientSideAnimationOn ( )
{
m_bClientSideAnimation = true ;
AddToClientSideAnimationList ( ) ;
}
void C_BaseAnimating : : AddToClientSideAnimationList ( )
{
// Already in list
if ( m_ClientSideAnimationListHandle ! = INVALID_CLIENTSIDEANIMATION_LIST_HANDLE )
return ;
clientanimating_t list ( this , 0 ) ;
m_ClientSideAnimationListHandle = g_ClientSideAnimationList . AddToTail ( list ) ;
ClientSideAnimationChanged ( ) ;
UpdateRelevantInterpolatedVars ( ) ;
}
void C_BaseAnimating : : RemoveFromClientSideAnimationList ( )
{
// Not in list yet
if ( INVALID_CLIENTSIDEANIMATION_LIST_HANDLE = = m_ClientSideAnimationListHandle )
return ;
unsigned int c = g_ClientSideAnimationList . Count ( ) ;
Assert ( m_ClientSideAnimationListHandle < c ) ;
unsigned int last = c - 1 ;
if ( last = = m_ClientSideAnimationListHandle )
{
// Just wipe the final entry
g_ClientSideAnimationList . FastRemove ( last ) ;
}
else
{
clientanimating_t lastEntry = g_ClientSideAnimationList [ last ] ;
// Remove the last entry
g_ClientSideAnimationList . FastRemove ( last ) ;
// And update it's handle to point to this slot.
lastEntry . pAnimating - > m_ClientSideAnimationListHandle = m_ClientSideAnimationListHandle ;
g_ClientSideAnimationList [ m_ClientSideAnimationListHandle ] = lastEntry ;
}
// Invalidate our handle no matter what.
m_ClientSideAnimationListHandle = INVALID_CLIENTSIDEANIMATION_LIST_HANDLE ;
UpdateRelevantInterpolatedVars ( ) ;
}
// static method
void C_BaseAnimating : : UpdateClientSideAnimations ( )
{
VPROF_BUDGET ( " UpdateClientSideAnimations " , VPROF_BUDGETGROUP_CLIENT_ANIMATION ) ;
int c = g_ClientSideAnimationList . Count ( ) ;
for ( int i = 0 ; i < c ; + + i )
{
clientanimating_t & anim = g_ClientSideAnimationList . Element ( i ) ;
if ( ! ( anim . flags & FCLIENTANIM_SEQUENCE_CYCLE ) )
continue ;
Assert ( anim . pAnimating ) ;
anim . pAnimating - > UpdateClientSideAnimation ( ) ;
}
}
CBoneList * C_BaseAnimating : : RecordBones ( CStudioHdr * hdr , matrix3x4_t * pBoneState )
{
if ( ! ToolsEnabled ( ) )
return NULL ;
VPROF_BUDGET ( " C_BaseAnimating::RecordBones " , VPROF_BUDGETGROUP_TOOLS ) ;
// Possible optimization: Instead of inverting everything while recording, record the pos/q stuff into a structure instead?
Assert ( hdr ) ;
// Setup our transform based on render angles and origin.
matrix3x4_t parentTransform ;
AngleMatrix ( GetRenderAngles ( ) , GetRenderOrigin ( ) , parentTransform ) ;
CBoneList * boneList = CBoneList : : Alloc ( ) ;
Assert ( boneList ) ;
boneList - > m_nBones = hdr - > numbones ( ) ;
for ( int i = 0 ; i < hdr - > numbones ( ) ; i + + )
{
matrix3x4_t inverted ;
matrix3x4_t output ;
mstudiobone_t * bone = hdr - > pBone ( i ) ;
// Only update bones referenced during setup
if ( ! ( bone - > flags & BONE_USED_BY_ANYTHING ) )
{
boneList - > m_quatRot [ i ] . Init ( 0.0f , 0.0f , 0.0f , 1.0f ) ; // Init by default sets all 0's, which is invalid
boneList - > m_vecPos [ i ] . Init ( ) ;
continue ;
}
if ( bone - > parent = = - 1 )
{
// Decompose into parent space
MatrixInvert ( parentTransform , inverted ) ;
}
else
{
MatrixInvert ( pBoneState [ bone - > parent ] , inverted ) ;
}
ConcatTransforms ( inverted , pBoneState [ i ] , output ) ;
MatrixAngles ( output ,
boneList - > m_quatRot [ i ] ,
boneList - > m_vecPos [ i ] ) ;
}
return boneList ;
}
void C_BaseAnimating : : GetToolRecordingState ( KeyValues * msg )
{
if ( ! ToolsEnabled ( ) )
return ;
VPROF_BUDGET ( " C_BaseAnimating::GetToolRecordingState " , VPROF_BUDGETGROUP_TOOLS ) ;
// Force the animation to drive bones
CStudioHdr * hdr = GetModelPtr ( ) ;
matrix3x4_t * pBones = ( matrix3x4_t * ) _alloca ( ( hdr ? hdr - > numbones ( ) : 1 ) * sizeof ( matrix3x4_t ) ) ;
if ( hdr )
{
SetupBones ( pBones , hdr - > numbones ( ) , BONE_USED_BY_ANYTHING , gpGlobals - > curtime ) ;
}
else
{
SetupBones ( NULL , - 1 , BONE_USED_BY_ANYTHING , gpGlobals - > curtime ) ;
}
BaseClass : : GetToolRecordingState ( msg ) ;
static BaseAnimatingRecordingState_t state ;
state . m_nSkin = GetSkin ( ) ;
state . m_nBody = m_nBody ;
state . m_nSequence = m_nSequence ;
state . m_pBoneList = NULL ;
msg - > SetPtr ( " baseanimating " , & state ) ;
msg - > SetInt ( " viewmodel " , IsViewModel ( ) ? 1 : 0 ) ;
if ( hdr )
{
state . m_pBoneList = RecordBones ( hdr , pBones ) ;
}
}
void C_BaseAnimating : : CleanupToolRecordingState ( KeyValues * msg )
{
if ( ! ToolsEnabled ( ) )
return ;
BaseAnimatingRecordingState_t * pState = ( BaseAnimatingRecordingState_t * ) msg - > GetPtr ( " baseanimating " ) ;
if ( pState & & pState - > m_pBoneList )
{
pState - > m_pBoneList - > Release ( ) ;
}
BaseClass : : CleanupToolRecordingState ( msg ) ;
}
LocalFlexController_t C_BaseAnimating : : GetNumFlexControllers ( void )
{
CStudioHdr * pstudiohdr = GetModelPtr ( ) ;
if ( ! pstudiohdr )
return LocalFlexController_t ( 0 ) ;
return pstudiohdr - > numflexcontrollers ( ) ;
}
const char * C_BaseAnimating : : GetFlexDescFacs ( int iFlexDesc )
{
CStudioHdr * pstudiohdr = GetModelPtr ( ) ;
if ( ! pstudiohdr )
return 0 ;
mstudioflexdesc_t * pflexdesc = pstudiohdr - > pFlexdesc ( iFlexDesc ) ;
return pflexdesc - > pszFACS ( ) ;
}
const char * C_BaseAnimating : : GetFlexControllerName ( LocalFlexController_t iFlexController )
{
CStudioHdr * pstudiohdr = GetModelPtr ( ) ;
if ( ! pstudiohdr )
return 0 ;
mstudioflexcontroller_t * pflexcontroller = pstudiohdr - > pFlexcontroller ( iFlexController ) ;
return pflexcontroller - > pszName ( ) ;
}
const char * C_BaseAnimating : : GetFlexControllerType ( LocalFlexController_t iFlexController )
{
CStudioHdr * pstudiohdr = GetModelPtr ( ) ;
if ( ! pstudiohdr )
return 0 ;
mstudioflexcontroller_t * pflexcontroller = pstudiohdr - > pFlexcontroller ( iFlexController ) ;
return pflexcontroller - > pszType ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the fade scale of the entity in question
// Output : unsigned char - 0 - 255 alpha value
//-----------------------------------------------------------------------------
unsigned char C_BaseAnimating : : GetClientSideFade ( void )
{
return UTIL_ComputeEntityFade ( this , m_fadeMinDist , m_fadeMaxDist , m_flFadeScale ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Note that we've been transmitted a sequence
//-----------------------------------------------------------------------------
void C_BaseAnimating : : SetReceivedSequence ( void )
{
m_bReceivedSequence = true ;
}
//-----------------------------------------------------------------------------
// Purpose: See if we should force reset our sequence on a new model
//-----------------------------------------------------------------------------
bool C_BaseAnimating : : ShouldResetSequenceOnNewModel ( void )
{
return ( m_bReceivedSequence = = false ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseAnimating : : UpdateBoneAttachments ( void )
{
if ( ! m_pAttachedTo )
return ;
// Assert( IsFollowingEntity() );
// Assert( m_boneIndexAttached >= 0 );
C_BaseAnimating * follow = FindFollowedEntity ( ) ;
if ( follow & & ( m_boneIndexAttached > = 0 ) )
{
matrix3x4_t boneToWorld , localSpace ;
follow - > GetCachedBoneMatrix ( m_boneIndexAttached , boneToWorld ) ;
AngleMatrix ( m_boneAngles , m_bonePosition , localSpace ) ;
ConcatTransforms ( boneToWorld , localSpace , GetBoneForWrite ( 0 ) ) ;
Vector absOrigin ;
MatrixGetColumn ( GetBone ( 0 ) , 3 , absOrigin ) ;
SetAbsOrigin ( absOrigin ) ;
QAngle absAngle ;
MatrixAngles ( GetBone ( 0 ) , absAngle ) ;
SetAbsAngles ( absAngle ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseAnimating : : AttachEntityToBone ( C_BaseAnimating * attachTarget , int boneIndexAttached , Vector bonePosition , QAngle boneAngles )
{
if ( ! attachTarget )
return ;
SetCollisionGroup ( COLLISION_GROUP_DEBRIS ) ;
FollowEntity ( attachTarget ) ;
SetOwnerEntity ( attachTarget ) ;
// Assert( boneIndexAttached >= 0 ); // We should be attaching to a bone.
if ( boneIndexAttached > = 0 )
{
m_boneIndexAttached = boneIndexAttached ;
m_bonePosition = bonePosition ;
m_boneAngles = boneAngles ;
}
m_BoneAccessor . SetReadableBones ( BONE_USED_BY_ANYTHING ) ;
m_BoneAccessor . SetWritableBones ( BONE_USED_BY_ANYTHING ) ;
attachTarget - > AddBoneAttachment ( this ) ;
NotifyBoneAttached ( attachTarget ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseAnimating : : NotifyBoneAttached ( C_BaseAnimating * attachTarget )
{
// If we're already attached to something, remove us from it.
if ( m_pAttachedTo )
{
m_pAttachedTo - > RemoveBoneAttachment ( this ) ;
m_pAttachedTo = NULL ;
}
// Remember the new attach target.
m_pAttachedTo = attachTarget ;
// Special case: if we just attached to the local player and he is hidden, hide us as well.
C_BasePlayer * pPlayer = dynamic_cast < C_BasePlayer * > ( attachTarget ) ;
if ( pPlayer & & pPlayer - > IsLocalPlayer ( ) )
{
if ( ! C_BasePlayer : : ShouldDrawLocalPlayer ( ) )
{
AddEffects ( EF_NODRAW ) ;
}
}
else
{
RemoveEffects ( EF_NODRAW ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseAnimating : : AddBoneAttachment ( C_BaseAnimating * newBoneAttachment )
{
if ( ! newBoneAttachment )
return ;
m_BoneAttachments . AddToTail ( newBoneAttachment ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseAnimating : : RemoveBoneAttachment ( C_BaseAnimating * boneAttachment )
{
if ( ! boneAttachment )
return ;
m_BoneAttachments . FindAndRemove ( boneAttachment ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int C_BaseAnimating : : GetNumBoneAttachments ( )
{
return m_BoneAttachments . Count ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
C_BaseAnimating * C_BaseAnimating : : GetBoneAttachment ( int i )
{
if ( m_BoneAttachments . IsValidIndex ( i ) )
{
return m_BoneAttachments [ i ] ;
}
return NULL ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseAnimating : : DestroyBoneAttachments ( )
{
while ( GetNumBoneAttachments ( ) )
{
C_BaseAnimating * pAttachment = GetBoneAttachment ( 0 ) ;
if ( pAttachment )
{
pAttachment - > Release ( ) ;
}
else
{
m_BoneAttachments . Remove ( 0 ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseAnimating : : MoveBoneAttachments ( C_BaseAnimating * attachTarget )
{
if ( ! attachTarget )
return ;
// Move all of our bone attachments to this new object.
// Preserves the specific bone and attachment location information.
while ( GetNumBoneAttachments ( ) )
{
C_BaseAnimating * pAttachment = GetBoneAttachment ( 0 ) ;
if ( pAttachment )
{
pAttachment - > AttachEntityToBone ( attachTarget ) ;
}
else
{
m_BoneAttachments . Remove ( 0 ) ;
}
}
}