css_enhanced_waf/game/client/c_baseanimatingoverlay.cpp
Kamay Xutax 854991ab8c Fixed lag compensation for animations
The problem is that we still trust the client, although now we have a
good base to start with; The key difference here is that we don't need
to use anymore the cs player animestate client side anymore
because server side values are used

There are minor bugs like fire effect but they can be fixed
2024-07-11 02:12:51 +02:00

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_ANIMATION_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 );
}