Kamay Xutax
9d17d2252c
Now we can debug properly lag compensation for animations and make it more perfect without using the CUserCmd struct For now it's used to sync better with client but in theory this can be removed soon. There's a lot of work to do in anim layers too.
435 lines
No EOL
12 KiB
C++
435 lines
No EOL
12 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "c_baseanimatingoverlay.h"
|
|
#include "bone_setup.h"
|
|
#include "interpolatedvar.h"
|
|
#include "studio.h"
|
|
#include "tier0/vprof.h"
|
|
#include "engine/ivdebugoverlay.h"
|
|
#include "datacache/imdlcache.h"
|
|
#include "eventlist.h"
|
|
|
|
#include "dt_utlvector_recv.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
extern ConVar r_sequence_debug;
|
|
|
|
C_BaseAnimatingOverlay::C_BaseAnimatingOverlay()
|
|
{
|
|
// FIXME: where does this initialization go now?
|
|
//for ( int i=0; i < MAX_OVERLAYS; i++ )
|
|
//{
|
|
// memset( &m_Layer[i], 0, sizeof(m_Layer[0]) );
|
|
// m_Layer[i].m_nOrder = MAX_OVERLAYS;
|
|
//}
|
|
|
|
// FIXME: where does this initialization go now?
|
|
// AddVar( m_Layer, &m_iv_AnimOverlay, LATCH_ANIMATION_VAR );
|
|
}
|
|
|
|
#undef CBaseAnimatingOverlay
|
|
|
|
|
|
|
|
BEGIN_RECV_TABLE_NOBASE(CAnimationLayer, DT_Animationlayer)
|
|
RecvPropInt( RECVINFO_NAME(m_nSequence, m_nSequence)),
|
|
RecvPropFloat( RECVINFO_NAME(m_flCycle, m_flCycle)),
|
|
RecvPropFloat( RECVINFO_NAME(m_flPrevCycle, m_flPrevCycle)),
|
|
RecvPropFloat( RECVINFO_NAME(m_flWeight, m_flWeight)),
|
|
RecvPropInt( RECVINFO_NAME(m_nOrder, m_nOrder))
|
|
END_RECV_TABLE()
|
|
|
|
const char *s_m_iv_AnimOverlayNames[C_BaseAnimatingOverlay::MAX_OVERLAYS] =
|
|
{
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay00",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay01",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay02",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay03",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay04",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay05",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay06",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay07",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay08",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay09",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay10",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay11",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay12",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay13",
|
|
"C_BaseAnimatingOverlay::m_iv_AnimOverlay14"
|
|
};
|
|
|
|
void ResizeAnimationLayerCallback( void *pStruct, int offsetToUtlVector, int len )
|
|
{
|
|
C_BaseAnimatingOverlay *pEnt = (C_BaseAnimatingOverlay*)pStruct;
|
|
CUtlVector < C_AnimationLayer > *pVec = &pEnt->m_AnimOverlay;
|
|
CUtlVector< CInterpolatedVar< C_AnimationLayer > > *pVecIV = &pEnt->m_iv_AnimOverlay;
|
|
|
|
Assert( (char*)pVec - (char*)pEnt == offsetToUtlVector );
|
|
Assert( pVec->Count() == pVecIV->Count() );
|
|
Assert( pVec->Count() <= C_BaseAnimatingOverlay::MAX_OVERLAYS );
|
|
|
|
int diff = len - pVec->Count();
|
|
|
|
|
|
|
|
if ( diff == 0 )
|
|
return;
|
|
|
|
// remove all entries
|
|
for ( int i=0; i < pVec->Count(); i++ )
|
|
{
|
|
pEnt->RemoveVar( &pVec->Element( i ) );
|
|
}
|
|
|
|
// adjust vector sizes
|
|
if ( diff > 0 )
|
|
{
|
|
pVec->AddMultipleToTail( diff );
|
|
pVecIV->AddMultipleToTail( diff );
|
|
}
|
|
else
|
|
{
|
|
pVec->RemoveMultiple( len, -diff );
|
|
pVecIV->RemoveMultiple( len, -diff );
|
|
}
|
|
|
|
// Rebind all the variables in the ent's list.
|
|
for ( int i=0; i < len; i++ )
|
|
{
|
|
IInterpolatedVar *pWatcher = &pVecIV->Element( i );
|
|
pWatcher->SetDebugName( s_m_iv_AnimOverlayNames[i] );
|
|
pEnt->AddVar( &pVec->Element( i ), pWatcher, LATCH_SIMULATION_VAR, true );
|
|
}
|
|
// FIXME: need to set historical values of nOrder in pVecIV to MAX_OVERLAY
|
|
|
|
}
|
|
|
|
BEGIN_RECV_TABLE_NOBASE( C_BaseAnimatingOverlay, DT_OverlayVars )
|
|
RecvPropUtlVector(
|
|
RECVINFO_UTLVECTOR( m_AnimOverlay ),
|
|
C_BaseAnimatingOverlay::MAX_OVERLAYS,
|
|
RecvPropDataTable(NULL, 0, 0, &REFERENCE_RECV_TABLE( DT_Animationlayer ) ) )
|
|
END_RECV_TABLE()
|
|
|
|
|
|
IMPLEMENT_CLIENTCLASS_DT( C_BaseAnimatingOverlay, DT_BaseAnimatingOverlay, CBaseAnimatingOverlay )
|
|
RecvPropDataTable( "overlay_vars", 0, 0, &REFERENCE_RECV_TABLE( DT_OverlayVars ) )
|
|
END_RECV_TABLE()
|
|
|
|
BEGIN_PREDICTION_DATA( C_BaseAnimatingOverlay )
|
|
|
|
/*
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[0][2].m_nSequence, FIELD_INTEGER ),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[0][2].m_flCycle, FIELD_FLOAT ),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[0][2].m_flPlaybackRate, FIELD_FLOAT),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[0][2].m_flWeight, FIELD_FLOAT),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[1][2].m_nSequence, FIELD_INTEGER ),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[1][2].m_flCycle, FIELD_FLOAT ),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[1][2].m_flPlaybackRate, FIELD_FLOAT),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[1][2].m_flWeight, FIELD_FLOAT),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[2][2].m_nSequence, FIELD_INTEGER ),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[2][2].m_flCycle, FIELD_FLOAT ),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[2][2].m_flPlaybackRate, FIELD_FLOAT),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[2][2].m_flWeight, FIELD_FLOAT),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[3][2].m_nSequence, FIELD_INTEGER ),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[3][2].m_flCycle, FIELD_FLOAT ),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[3][2].m_flPlaybackRate, FIELD_FLOAT),
|
|
DEFINE_FIELD( C_BaseAnimatingOverlay, m_Layer[3][2].m_flWeight, FIELD_FLOAT),
|
|
*/
|
|
|
|
END_PREDICTION_DATA()
|
|
|
|
C_AnimationLayer* C_BaseAnimatingOverlay::GetAnimOverlay( int i )
|
|
{
|
|
Assert( i >= 0 && i < MAX_OVERLAYS );
|
|
return &m_AnimOverlay[i];
|
|
}
|
|
|
|
|
|
void C_BaseAnimatingOverlay::SetNumAnimOverlays( int num )
|
|
{
|
|
if ( m_AnimOverlay.Count() < num )
|
|
{
|
|
m_AnimOverlay.AddMultipleToTail( num - m_AnimOverlay.Count() );
|
|
}
|
|
else if ( m_AnimOverlay.Count() > num )
|
|
{
|
|
m_AnimOverlay.RemoveMultiple( num, m_AnimOverlay.Count() - num );
|
|
}
|
|
}
|
|
|
|
|
|
int C_BaseAnimatingOverlay::GetNumAnimOverlays() const
|
|
{
|
|
return m_AnimOverlay.Count();
|
|
}
|
|
|
|
|
|
void C_BaseAnimatingOverlay::GetRenderBounds( Vector& theMins, Vector& theMaxs )
|
|
{
|
|
BaseClass::GetRenderBounds( theMins, theMaxs );
|
|
|
|
if ( !IsRagdoll() )
|
|
{
|
|
CStudioHdr *pStudioHdr = GetModelPtr();
|
|
if ( !pStudioHdr || !pStudioHdr->SequencesAvailable() )
|
|
return;
|
|
|
|
int nSequences = pStudioHdr->GetNumSeq();
|
|
|
|
int i;
|
|
for (i = 0; i < m_AnimOverlay.Count(); i++)
|
|
{
|
|
if (m_AnimOverlay[i].m_flWeight > 0.0)
|
|
{
|
|
if ( m_AnimOverlay[i].m_nSequence >= nSequences )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( m_AnimOverlay[i].m_nSequence );
|
|
VectorMin( seqdesc.bbmin, theMins, theMins );
|
|
VectorMax( seqdesc.bbmax, theMaxs, theMaxs );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void C_BaseAnimatingOverlay::AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Quaternion q[], float currentTime )
|
|
{
|
|
BaseClass::AccumulateLayers(boneSetup, pos, q, currentTime);
|
|
|
|
// sort the layers
|
|
int layer[MAX_OVERLAYS] = {};
|
|
int i;
|
|
for (i = 0; i < m_AnimOverlay.Count(); i++)
|
|
{
|
|
layer[i] = MAX_OVERLAYS;
|
|
}
|
|
for (i = 0; i < m_AnimOverlay.Count(); i++)
|
|
{
|
|
CAnimationLayer &pLayer = m_AnimOverlay[i];
|
|
if( (pLayer.m_flWeight > 0) && pLayer.IsActive() && pLayer.m_nOrder >= 0 && pLayer.m_nOrder < m_AnimOverlay.Count())
|
|
{
|
|
layer[pLayer.m_nOrder] = i;
|
|
}
|
|
}
|
|
for (i = 0; i < m_AnimOverlay.Count(); i++)
|
|
{
|
|
if (layer[i] >= 0 && layer[i] < m_AnimOverlay.Count())
|
|
{
|
|
CAnimationLayer &pLayer = m_AnimOverlay[layer[i]];
|
|
// UNDONE: Is it correct to use overlay weight for IK too?
|
|
boneSetup.AccumulatePose( pos, q, pLayer.m_nSequence, pLayer.m_flCycle, pLayer.m_flWeight, currentTime, m_pIk );
|
|
}
|
|
}
|
|
}
|
|
|
|
void C_BaseAnimatingOverlay::DoAnimationEvents( CStudioHdr *pStudioHdr )
|
|
{
|
|
if ( !pStudioHdr || !pStudioHdr->SequencesAvailable() )
|
|
return;
|
|
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
int nSequences = pStudioHdr->GetNumSeq();
|
|
|
|
BaseClass::DoAnimationEvents( pStudioHdr );
|
|
|
|
bool watch = false; // Q_strstr( hdr->name, "rifle" ) ? true : false;
|
|
|
|
int j;
|
|
for (j = 0; j < m_AnimOverlay.Count(); j++)
|
|
{
|
|
if ( m_AnimOverlay[j].m_nSequence >= nSequences )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( m_AnimOverlay[j].m_nSequence );
|
|
if ( seqdesc.numevents == 0 )
|
|
continue;
|
|
|
|
// stalled?
|
|
if (m_AnimOverlay[j].m_flCycle == m_flOverlayPrevEventCycle[j])
|
|
continue;
|
|
|
|
bool bLoopingSequence = IsSequenceLooping( m_AnimOverlay[j].m_nSequence );
|
|
|
|
bool bLooped = false;
|
|
|
|
//in client code, m_flOverlayPrevEventCycle is set to -1 when we first start an overlay, looping or not
|
|
if ( bLoopingSequence &&
|
|
m_flOverlayPrevEventCycle[j] > 0.0f &&
|
|
m_AnimOverlay[j].m_flCycle <= m_flOverlayPrevEventCycle[j] )
|
|
{
|
|
if (m_flOverlayPrevEventCycle[j] - m_AnimOverlay[j].m_flCycle > 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;
|
|
}
|
|
}
|
|
|
|
mstudioevent_t *pevent = seqdesc.pEvent( 0 );
|
|
|
|
// 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_flOverlayPrevEventCycle[j] )
|
|
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_flOverlayPrevEventCycle[j],
|
|
(float)m_AnimOverlay[j].m_flCycle,
|
|
gpGlobals->curtime );
|
|
}
|
|
|
|
|
|
FireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() );
|
|
}
|
|
|
|
// Necessary to get the next loop working
|
|
m_flOverlayPrevEventCycle[j] = -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_flOverlayPrevEventCycle[j] && pevent[i].cycle <= m_AnimOverlay[j].m_flCycle) )
|
|
{
|
|
if ( watch )
|
|
{
|
|
Msg( "%i (seq: %d) FE %i Normal cycle %f, prev %f ev %f (time %.3f)\n",
|
|
gpGlobals->tickcount,
|
|
m_AnimOverlay[j].m_nSequence.GetRaw(),
|
|
pevent[i].event,
|
|
pevent[i].cycle,
|
|
m_flOverlayPrevEventCycle[j],
|
|
(float)m_AnimOverlay[j].m_flCycle,
|
|
gpGlobals->curtime );
|
|
}
|
|
|
|
FireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() );
|
|
}
|
|
}
|
|
|
|
m_flOverlayPrevEventCycle[j] = m_AnimOverlay[j].m_flCycle;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CStudioHdr *C_BaseAnimatingOverlay::OnNewModel()
|
|
{
|
|
CStudioHdr *hdr = BaseClass::OnNewModel();
|
|
|
|
// Clear out animation layers
|
|
for ( int i=0; i < m_AnimOverlay.Count(); i++ )
|
|
{
|
|
m_AnimOverlay[i].Reset();
|
|
m_AnimOverlay[i].m_nOrder = MAX_OVERLAYS;
|
|
}
|
|
|
|
return hdr;
|
|
}
|
|
|
|
void C_BaseAnimatingOverlay::GetSkeleton( CStudioHdr* pStudioHdr, Vector pos[], Quaternion q[], int boneMask, float currentTime )
|
|
{
|
|
if(!pStudioHdr)
|
|
{
|
|
Assert(!"C_BaseAnimating::GetSkeleton() without a model");
|
|
return;
|
|
}
|
|
|
|
if (!pStudioHdr->SequencesAvailable())
|
|
{
|
|
return;
|
|
}
|
|
|
|
float poseparameters[MAXSTUDIOPOSEPARAM];
|
|
GetPoseParameters(pStudioHdr, poseparameters);
|
|
|
|
IBoneSetup boneSetup( pStudioHdr, boneMask, poseparameters );
|
|
boneSetup.InitPose( pos, q );
|
|
|
|
boneSetup.AccumulatePose( pos, q, GetSequence(), GetCycle(), 1.0, currentTime, m_pIk );
|
|
|
|
// sort the layers
|
|
int layer[MAX_OVERLAYS] = {};
|
|
int i;
|
|
for (i = 0; i < m_AnimOverlay.Count(); i++)
|
|
{
|
|
layer[i] = MAX_OVERLAYS;
|
|
}
|
|
for (i = 0; i < m_AnimOverlay.Count(); i++)
|
|
{
|
|
CAnimationLayer &pLayer = m_AnimOverlay[i];
|
|
if( (pLayer.m_flWeight > 0) && pLayer.IsActive() && pLayer.m_nOrder >= 0 && pLayer.m_nOrder < m_AnimOverlay.Count())
|
|
{
|
|
layer[pLayer.m_nOrder] = i;
|
|
}
|
|
}
|
|
for (i = 0; i < m_AnimOverlay.Count(); i++)
|
|
{
|
|
if (layer[i] >= 0 && layer[i] < m_AnimOverlay.Count())
|
|
{
|
|
CAnimationLayer &pLayer = m_AnimOverlay[layer[i]];
|
|
// UNDONE: Is it correct to use overlay weight for IK too?
|
|
boneSetup.AccumulatePose( pos, q, pLayer.m_nSequence, pLayer.m_flCycle, pLayer.m_flWeight, currentTime, m_pIk );
|
|
}
|
|
}
|
|
|
|
if ( m_pIk )
|
|
{
|
|
CIKContext auto_ik;
|
|
auto_ik.Init( pStudioHdr, GetRenderAngles(), GetRenderOrigin(), currentTime, 0, boneMask );
|
|
boneSetup.CalcAutoplaySequences( pos, q, currentTime, &auto_ik );
|
|
}
|
|
else
|
|
{
|
|
boneSetup.CalcAutoplaySequences(pos, q, currentTime, NULL);
|
|
}
|
|
|
|
float controllers[MAXSTUDIOBONECTRLS];
|
|
GetBoneControllers(controllers);
|
|
|
|
boneSetup.CalcBoneAdj( pos, q, controllers );
|
|
} |