Kamay Xutax
2e5867b300
This cause looping issues due to cycle, until I find a proper solution for it, it is disabled for accuracy purposes and also because we can't trust the client. This is really hard to see though with high tickrate.
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_ANIMATION_VAR );
|
|
}
|
|
// 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 );
|
|
} |