//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // // $NoKeywords: $ //===========================================================================// #include "cbase.h" #include "c_baseanimatingoverlay.h" #include "animation.h" #include "bone_setup.h" #if defined( _PS3 ) #include "bone_setup_PS3.h" #endif #include "tier0/vprof.h" #include "engine/ivdebugoverlay.h" #include "datacache/imdlcache.h" #include "eventlist.h" #include "toolframework_client.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; template class CInterpolatedVar; mstudioevent_for_client_server_t *GetEventIndexForSequence( mstudioseqdesc_t &seqdesc ); void C_AnimationLayer::SetOwner( C_BaseAnimatingOverlay *pOverlay ) { m_pOwner = pOverlay; } C_BaseAnimatingOverlay *C_AnimationLayer::GetOwner() const { return m_pOwner; } void C_AnimationLayer::Reset() { if ( m_pOwner ) { int nFlags = 0; if ( m_nSequence != 0 || m_flWeight != 0.0f ) { nFlags |= BOUNDS_CHANGED; } if ( m_flCycle != 0.0f ) { nFlags |= ANIMATION_CHANGED; } if ( nFlags ) { m_pOwner->InvalidatePhysicsRecursive( nFlags ); } } m_nSequence = 0; m_flPrevCycle = 0; m_flWeight = 0; m_flWeightDeltaRate = 0; m_flPlaybackRate = 0; m_flCycle = 0; m_flLayerAnimtime = 0; m_flLayerFadeOuttime = 0; } void C_AnimationLayer::SetSequence( int nSequence ) { if ( m_pOwner && m_nSequence != nSequence ) { m_pOwner->InvalidatePhysicsRecursive( BOUNDS_CHANGED ); } m_nSequence = nSequence; } void C_AnimationLayer::SetCycle( float flCycle ) { if ( m_pOwner && m_flCycle != flCycle ) { m_pOwner->InvalidatePhysicsRecursive( ANIMATION_CHANGED ); } m_flCycle = flCycle; } void C_AnimationLayer::SetOrder( int order ) { if ( m_pOwner && ( m_nOrder != order ) ) { if ( m_nOrder == C_BaseAnimatingOverlay::MAX_OVERLAYS || order == C_BaseAnimatingOverlay::MAX_OVERLAYS ) { m_pOwner->InvalidatePhysicsRecursive( BOUNDS_CHANGED ); } } m_nOrder = order; } void C_AnimationLayer::SetWeight( float flWeight ) { if ( m_pOwner && m_flWeight != flWeight ) { if ( m_flWeight == 0.0f || flWeight == 0.0f ) { m_pOwner->InvalidatePhysicsRecursive( BOUNDS_CHANGED ); } } m_flWeight = flWeight; } C_BaseAnimatingOverlay::C_BaseAnimatingOverlay() { // NOTE: We zero the memory in the max capacity m_Layer vector in dt_ultvector_common.h // FIXME: where does this initialization go now? // AddVar( m_Layer, &m_iv_AnimOverlay, LATCH_ANIMATION_VAR ); } #undef CBaseAnimatingOverlay void RecvProxy_SequenceChanged( const CRecvProxyData *pData, void *pStruct, void *pOut ) { CAnimationLayer *pLayer = (CAnimationLayer *)pStruct; if ( pLayer->GetOwner() ) pLayer->GetOwner()->NotifyOnLayerChangeSequence( pLayer, pData->m_Value.m_Int ); pLayer->SetSequence( pData->m_Value.m_Int ); } void RecvProxy_WeightChanged( const CRecvProxyData *pData, void *pStruct, void *pOut ) { CAnimationLayer *pLayer = (CAnimationLayer *)pStruct; if ( pLayer->GetOwner() ) pLayer->GetOwner()->NotifyOnLayerChangeWeight( pLayer, pData->m_Value.m_Float ); pLayer->SetWeight( pData->m_Value.m_Float ); } void RecvProxy_WeightDeltaRateChanged( const CRecvProxyData *pData, void *pStruct, void *pOut ) { CAnimationLayer *pLayer = (CAnimationLayer *)pStruct; pLayer->SetWeightDeltaRate( pData->m_Value.m_Float ); } void RecvProxy_CycleChanged( const CRecvProxyData *pData, void *pStruct, void *pOut ) { CAnimationLayer *pLayer = (CAnimationLayer *)pStruct; if ( pLayer->GetOwner() ) pLayer->GetOwner()->NotifyOnLayerChangeCycle( pLayer, pData->m_Value.m_Float ); pLayer->SetCycle( pData->m_Value.m_Float ); } void RecvProxy_PlaybackRateChanged( const CRecvProxyData *pData, void *pStruct, void *pOut ) { CAnimationLayer *pLayer = (CAnimationLayer *)pStruct; pLayer->SetPlaybackRate( pData->m_Value.m_Float ); } void RecvProxy_OrderChanged( const CRecvProxyData *pData, void *pStruct, void *pOut ) { CAnimationLayer *pLayer = (CAnimationLayer *)pStruct; pLayer->SetOrder( pData->m_Value.m_Int ); } BEGIN_RECV_TABLE_NOBASE(CAnimationLayer, DT_Animationlayer) RecvPropInt( RECVINFO_NAME(m_nSequence, m_nSequence), 0, RecvProxy_SequenceChanged ), RecvPropFloat( RECVINFO_NAME(m_flCycle, m_flCycle), 0, RecvProxy_CycleChanged ), RecvPropFloat( RECVINFO_NAME(m_flPlaybackRate, m_flPlaybackRate), 0, RecvProxy_PlaybackRateChanged ), RecvPropFloat( RECVINFO_NAME(m_flPrevCycle, m_flPrevCycle)), RecvPropFloat( RECVINFO_NAME(m_flWeight, m_flWeight), 0, RecvProxy_WeightChanged ), RecvPropFloat( RECVINFO_NAME(m_flWeightDeltaRate, m_flWeightDeltaRate), 0, RecvProxy_WeightDeltaRateChanged ), RecvPropInt( RECVINFO_NAME(m_nOrder, m_nOrder), 0, RecvProxy_OrderChanged ) 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 < CAnimationLayer > *pVec = &pEnt->m_AnimOverlay; CUtlVector< CInterpolatedVar< CAnimationLayer > > *pVecIV = &pEnt->m_iv_AnimOverlay; Assert( (char*)pVec - (char*)pEnt == offsetToUtlVector ); Assert( pVec->Count() == pVecIV->Count() || pVecIV->Count() == 0 ); Assert( pVec->Count() <= C_BaseAnimatingOverlay::MAX_OVERLAYS ); int diff = len - pVec->Count(); if ( diff != 0 ) { // remove all entries for ( int i=0; i < pVec->Count(); i++ ) { pEnt->RemoveVar( &pVec->Element( i ) ); } pEnt->InvalidatePhysicsRecursive( BOUNDS_CHANGED ); // adjust vector sizes if ( diff > 0 ) { for ( int i = 0; i < diff; ++i ) { int j = pVec->AddToTail( ); (*pVec)[j].SetOwner( pEnt ); } 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 // Ensure capacity pVec->EnsureCapacity( len ); int nNumAllocated = pVec->NumAllocated(); // This is important to do because EnsureCapacity doesn't actually call the constructors // on the elements, but we need them to be initialized, otherwise it'll have out-of-range // values which will piss off the datatable encoder. UtlVector_InitializeAllocatedElements( pVec->Base() + pVec->Count(), nNumAllocated - pVec->Count() ); } BEGIN_RECV_TABLE_NOBASE( C_BaseAnimatingOverlay, DT_OverlayVars ) RecvPropUtlVector( RECVINFO_UTLVECTOR_SIZEFN( m_AnimOverlay, ResizeAnimationLayerCallback ), 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() CAnimationLayer* C_BaseAnimatingOverlay::GetAnimOverlay( int i, bool bUseOrder ) { Assert( i >= 0 && i < MAX_OVERLAYS ); if ( !m_AnimOverlay.Count() ) return NULL; if ( bUseOrder ) { FOR_EACH_VEC( m_AnimOverlay, j ) { if ( m_AnimOverlay[j].GetOrder() == i ) return &m_AnimOverlay[j]; } } return &m_AnimOverlay[i]; } void C_BaseAnimatingOverlay::SetNumAnimOverlays( int num ) { if ( m_AnimOverlay.Count() < num ) { int nCountToAdd = num - m_AnimOverlay.Count(); for ( int i = 0; i < nCountToAdd; ++i ) { int j = m_AnimOverlay.AddToTail( ); m_AnimOverlay[j].SetOwner( this ); } } else if ( m_AnimOverlay.Count() > num ) { m_AnimOverlay.RemoveMultiple( num, m_AnimOverlay.Count() - num ); InvalidatePhysicsRecursive( BOUNDS_CHANGED ); } // Ensure capacity m_AnimOverlay.EnsureCapacity( C_BaseAnimatingOverlay::MAX_OVERLAYS ); int nNumAllocated = m_AnimOverlay.NumAllocated(); // This is important to do because EnsureCapacity doesn't actually call the constructors // on the elements, but we need them to be initialized, otherwise it'll have out-of-range // values which will piss off the datatable encoder. UtlVector_InitializeAllocatedElements( m_AnimOverlay.Base() + m_AnimOverlay.Count(), nNumAllocated - m_AnimOverlay.Count() ); } int C_BaseAnimatingOverlay::GetNumAnimOverlays() const { return m_AnimOverlay.Count(); } void C_BaseAnimatingOverlay::GetRenderBounds( Vector& theMins, Vector& theMaxs ) { BaseClass::GetRenderBounds( theMins, theMaxs ); if ( IsRagdoll() ) return; MDLCACHE_CRITICAL_SECTION(); 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 && m_AnimOverlay[i].m_nOrder != MAX_OVERLAYS ) { 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 ); } } } bool C_BaseAnimatingOverlay::Interpolate( float flCurrentTime ) { bool bOk = BaseClass::Interpolate( flCurrentTime ); CheckForLayerPhysicsInvalidate(); return bOk; } void C_BaseAnimatingOverlay::CheckForLayerChanges( CStudioHdr *hdr, float currentTime ) { CDisableRangeChecks disableRangeChecks; // FIXME: damn, there has to be a better way than this. int i; for (i = 0; i < m_iv_AnimOverlay.Count(); i++) { CDisableRangeChecks disableRangeChecks; int iHead, iPrev1, iPrev2; m_iv_AnimOverlay[i].GetInterpolationInfo( currentTime, &iHead, &iPrev1, &iPrev2 ); // fake up previous cycle values. float t0; CAnimationLayer *pHead = m_iv_AnimOverlay[i].GetHistoryValue( iHead, t0 ); // reset previous float t1; CAnimationLayer *pPrev1 = m_iv_AnimOverlay[i].GetHistoryValue( iPrev1, t1 ); // reset previous previous float t2; CAnimationLayer *pPrev2 = m_iv_AnimOverlay[i].GetHistoryValue( iPrev2, t2 ); if ( !pHead || !pPrev1 || pHead->m_nSequence == pPrev1->m_nSequence ) continue; #if 1 // _DEBUG if (r_sequence_debug.GetInt() == entindex()) { DevMsgRT( "(%7.4f : %30s : %5.3f : %4.2f : %1d)\n", t0, hdr->pSeqdesc( pHead->m_nSequence ).pszLabel(), (float)pHead->m_flCycle, (float)pHead->m_flWeight, i ); DevMsgRT( "(%7.4f : %30s : %5.3f : %4.2f : %1d)\n", t1, hdr->pSeqdesc( pPrev1->m_nSequence ).pszLabel(), (float)pPrev1->m_flCycle, (float)pPrev1->m_flWeight, i ); if (pPrev2) DevMsgRT( "(%7.4f : %30s : %5.3f : %4.2f : %1d)\n", t2, hdr->pSeqdesc( pPrev2->m_nSequence ).pszLabel(), (float)pPrev2->m_flCycle, (float)pPrev2->m_flWeight, i ); } #endif pPrev1->m_nSequence = pHead->m_nSequence; pPrev1->m_flCycle = pHead->m_flPrevCycle; pPrev1->m_flWeight = pHead->m_flWeight; if (pPrev2) { float num = 0; if ( fabs( t0 - t1 ) > 0.001f ) num = (t2 - t1) / (t0 - t1); pPrev2->m_nSequence = pHead->m_nSequence; float flTemp; if (IsSequenceLooping( hdr, pHead->m_nSequence )) { flTemp = LoopingLerp( num, (float)pHead->m_flPrevCycle, (float)pHead->m_flCycle ); } else { flTemp = Lerp( num, (float)pHead->m_flPrevCycle, (float)pHead->m_flCycle ); } pPrev2->m_flCycle = flTemp; pPrev2->m_flWeight = pHead->m_flWeight; } /* if (stricmp( r_seq_overlay_debug.GetString(), hdr->name ) == 0) { DevMsgRT( "(%30s %6.2f : %6.2f : %6.2f)\n", hdr->pSeqdesc( pHead->nSequence ).pszLabel(), (float)pPrev2->m_flCycle, (float)pPrev1->m_flCycle, (float)pHead->m_flCycle ); } */ m_iv_AnimOverlay[i].SetLooping( IsSequenceLooping( hdr, pHead->m_nSequence ) ); m_iv_AnimOverlay[i].Interpolate( currentTime ); // reset event indexes m_flOverlayPrevEventCycle[i] = pHead->m_flPrevCycle - 0.01; } } //#define DEBUG_TF2_OVERLAYS void C_BaseAnimatingOverlay::AccumulateLayers( IBoneSetup &boneSetup, BoneVector pos[], BoneQuaternion q[], float currentTime ) { BaseClass::AccumulateLayers( boneSetup, pos, q, currentTime ); int i; // resort the layers int layer[MAX_OVERLAYS]; for (i = 0; i < MAX_OVERLAYS; i++) { layer[i] = MAX_OVERLAYS; } for (i = 0; i < m_AnimOverlay.Count(); i++) { CAnimationLayer *pLayer = GetAnimOverlay( i ); if ( pLayer ) { layer[i] = clamp( pLayer->GetOrder(), 0, MAX_OVERLAYS - 1 ); } } CheckForLayerChanges( boneSetup.GetStudioHdr(), currentTime ); int nSequences = boneSetup.GetStudioHdr()->GetNumSeq(); // add in the overlay layers int j; for (j = 0; j < MAX_OVERLAYS; j++) { i = layer[ j ]; if ( i >= m_AnimOverlay.Count() ) { #if defined( DEBUG_TF2_OVERLAYS ) engine->Con_NPrintf( 10 + j, "%30s %6.2f : %6.2f : %1d", " ", 0.f, 0.f, i ); #endif continue; } if ( m_AnimOverlay[i].m_nSequence >= nSequences ) continue; /* DevMsgRT( 1 , "%.3f %.3f %.3f\n", currentTime, fWeight, dadt ); debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), -j - 1, 0, "%2d(%s) : %6.2f : %6.2f", m_AnimOverlay[i].m_nSequence, boneSetup.GetStudioHdr()->pSeqdesc( m_AnimOverlay[i].m_nSequence )->pszLabel(), m_AnimOverlay[i].m_flCycle, m_AnimOverlay[i].m_flWeight ); */ float fWeight = m_AnimOverlay[i].m_flWeight; if ( fWeight <= 0.0f ) { #if defined( DEBUG_TF2_OVERLAYS ) engine->Con_NPrintf( 10 + j, "%30s %6.2f : %6.2f : %1d", " ", 0.f, 0.f, i ); #endif continue; } // check to see if the sequence changed // FIXME: move this to somewhere more reasonable // do a nice spline interpolation of the values // if ( m_AnimOverlay[i].m_nSequence != m_iv_AnimOverlay.GetPrev( i )->nSequence ) float fCycle = m_AnimOverlay[ i ].m_flCycle; fCycle = ClampCycle( fCycle, IsSequenceLooping( m_AnimOverlay[i].m_nSequence ) ); if ( !IsFinite( fCycle ) ) { AssertMsg( false, "fCycle is nan!" ); fCycle = 0; } if (fWeight > 1.0f) { fWeight = 1.0f; } boneSetup.AccumulatePose( pos, q, m_AnimOverlay[i].m_nSequence, fCycle, fWeight, currentTime, m_pIk ); #if defined( DEBUG_TF2_OVERLAYS ) engine->Con_NPrintf( 10 + j, "%30s %6.2f : %6.2f : %1d", boneSetup.GetStudioHdr()->pSeqdesc( m_AnimOverlay[i].m_nSequence ).pszLabel(), fCycle, fWeight, i ); #endif #if 1 // _DEBUG if (r_sequence_debug.GetInt() == entindex()) { if (1) { DevMsgRT( "%8.4f : %30s : %5.3f : %4.2f : %1d\n", currentTime, boneSetup.GetStudioHdr()->pSeqdesc( m_AnimOverlay[i].m_nSequence ).pszLabel(), fCycle, fWeight, i ); } else { int iHead, iPrev1, iPrev2; m_iv_AnimOverlay[i].GetInterpolationInfo( currentTime, &iHead, &iPrev1, &iPrev2 ); // fake up previous cycle values. float t0; CAnimationLayer *pHead = m_iv_AnimOverlay[i].GetHistoryValue( iHead, t0 ); // reset previous float t1; CAnimationLayer *pPrev1 = m_iv_AnimOverlay[i].GetHistoryValue( iPrev1, t1 ); // reset previous previous float t2; CAnimationLayer *pPrev2 = m_iv_AnimOverlay[i].GetHistoryValue( iPrev2, t2 ); if ( pHead && pPrev1 && pPrev2 ) { DevMsgRT( "%6.2f : %30s %6.2f (%6.2f:%6.2f:%6.2f) : %6.2f (%6.2f:%6.2f:%6.2f) : %1d\n", currentTime, boneSetup.GetStudioHdr()->pSeqdesc( m_AnimOverlay[i].m_nSequence ).pszLabel(), fCycle, (float)pPrev2->m_flCycle, (float)pPrev1->m_flCycle, (float)pHead->m_flCycle, fWeight, (float)pPrev2->m_flWeight, (float)pPrev1->m_flWeight, (float)pHead->m_flWeight, i ); } else { DevMsgRT( "%6.2f : %30s %6.2f : %6.2f : %1d\n", currentTime, boneSetup.GetStudioHdr()->pSeqdesc( m_AnimOverlay[i].m_nSequence ).pszLabel(), fCycle, fWeight, i ); } } } #endif } //RegenerateDispatchedLayers( boneSetup, pos, q, currentTime ); } //----------------------------------------------------------------------------- // Purpose: Check to see if the sequence or weapon changed, if so find a matching sequence or clear the dispatch //----------------------------------------------------------------------------- bool C_BaseAnimatingOverlay::UpdateDispatchLayer( CAnimationLayer *pLayer, CStudioHdr *pWeaponStudioHdr, int iSequence ) { if ( !pWeaponStudioHdr || !pLayer ) { if ( pLayer ) pLayer->m_nDispatchedDst = ACT_INVALID; return false; } if ( pLayer->m_pDispatchedStudioHdr != pWeaponStudioHdr || pLayer->m_nDispatchedSrc != iSequence || pLayer->m_nDispatchedDst >= pWeaponStudioHdr->GetNumSeq() ) { pLayer->m_pDispatchedStudioHdr = pWeaponStudioHdr; pLayer->m_nDispatchedSrc = iSequence; if ( pWeaponStudioHdr ) { const char *pszLayerName = GetSequenceName( iSequence ); pLayer->m_nDispatchedDst = pWeaponStudioHdr->LookupSequence( pszLayerName ); } else { pLayer->m_nDispatchedDst = ACT_INVALID; } } return (pLayer->m_nDispatchedDst != ACT_INVALID ); } //----------------------------------------------------------------------------- // Purpose: Play overlay sequences on dispatched model, merge results back to parent model //----------------------------------------------------------------------------- void C_BaseAnimatingOverlay::AccumulateInterleavedDispatchedLayers( C_BaseAnimatingOverlay *pWeapon, IBoneSetup &boneSetup, BoneVector pos[], BoneQuaternion q[], float currentTime, bool bSetupInvisibleWeapon /* = false */ ) { bool bSetupWeapon = pWeapon != NULL && pWeapon->m_pBoneMergeCache != NULL && (pWeapon->IsVisible() || bSetupInvisibleWeapon) ; // reset event frame indices, etc CheckForLayerChanges( boneSetup.GetStudioHdr(), currentTime ); if ( bSetupWeapon ) { CStudioHdr *pWeaponStudioHdr = pWeapon->GetModelPtr(); // copy matching player pose params to weapon pose params pWeapon->m_pBoneMergeCache->MergeMatchingPoseParams(); float poseparam[MAXSTUDIOPOSEPARAM]; pWeapon->GetPoseParameters( pWeaponStudioHdr, poseparam ); // build a temporary setup for the weapon CIKContext weaponIK; weaponIK.Init( pWeaponStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, 0, BONE_USED_BY_BONE_MERGE ); IBoneSetup weaponSetup( pWeaponStudioHdr, BONE_USED_BY_BONE_MERGE, poseparam ); BoneVector weaponPos[MAXSTUDIOBONES]; BoneQuaternionAligned weaponQ[MAXSTUDIOBONES]; int nSequences = boneSetup.GetStudioHdr()->GetNumSeq(); for ( int nLayerIdx = 0; nLayerIdx < GetNumAnimOverlays(); nLayerIdx++ ) { CAnimationLayer *pLayer = GetAnimOverlay(nLayerIdx); if ( pLayer->GetSequence() <= 1 || pLayer->GetSequence() >= nSequences || pLayer->GetWeight() <= 0 ) continue; float fCycle = pLayer->GetCycle(); fCycle = ClampCycle( fCycle, IsSequenceLooping( pLayer->GetSequence() ) ); UpdateDispatchLayer( pLayer, pWeaponStudioHdr, pLayer->GetSequence() ); if ( pLayer->m_nDispatchedDst > 0 && pLayer->m_nDispatchedDst < pWeaponStudioHdr->GetNumSeq() ) { // copy player bones to weapon setup bones pWeapon->m_pBoneMergeCache->CopyFromFollow( pos, q, BONE_USED_BY_BONE_MERGE, weaponPos, weaponQ ); // respect ik rules on archetypal sequence, even if we're not playing it //mstudioseqdesc_t &seqdesc = ((CStudioHdr *)m_pStudioHdr)->pSeqdesc( pLayer->GetSequence() ); //m_pIk->AddDependencies( seqdesc, pLayer->GetSequence(), pLayer->GetCycle(), m_flPoseParameter, pLayer->GetWeight() ); // now that the weapon bones are in the position of the current player bones, set up the weapon animation onto that weaponSetup.AccumulatePose( weaponPos, weaponQ, pLayer->m_nDispatchedDst, pLayer->GetCycle(), pLayer->GetWeight(), currentTime, &weaponIK ); //DrawSkeleton( this->GetModelPtr(), BONE_USED_BY_ANYTHING ); //pWeapon->DrawSkeleton( pWeaponStudioHdr, BONE_USED_BY_ANYTHING ); // merge weapon bones back pWeapon->m_pBoneMergeCache->CopyToFollow( weaponPos, weaponQ, BONE_USED_BY_BONE_MERGE, pos, q ); weaponIK.CopyTo( m_pIk, pWeapon->m_pBoneMergeCache->GetRawIndexMapping() ); } else { boneSetup.AccumulatePose( pos, q, pLayer->GetSequence(), fCycle, pLayer->GetWeight(), currentTime, m_pIk ); } } //if ( bRanAnyWeaponLayers ) //{ // // // CBoneBitList boneComputed; // // pWeapon->UpdateIKLocks( currentTime ); // weaponIK.UpdateTargets( pos, q, pWeapon->m_BoneAccessor.GetBoneArrayForWrite(), boneComputed ); // // pWeapon->CalculateIKLocks( currentTime ); // weaponIK.SolveDependencies( pos, q, pWeapon->m_BoneAccessor.GetBoneArrayForWrite(), boneComputed ); // //} } else { int nSequences = boneSetup.GetStudioHdr()->GetNumSeq(); for ( int nLayerIdx = 0; nLayerIdx < GetNumAnimOverlays(); nLayerIdx++ ) { CAnimationLayer *pLayer = GetAnimOverlay(nLayerIdx); if ( pLayer->GetSequence() < 0 || pLayer->GetSequence() >= nSequences || pLayer->GetWeight() <= 0 ) continue; float fCycle = pLayer->GetCycle(); fCycle = ClampCycle( fCycle, IsSequenceLooping( pLayer->GetSequence() ) ); boneSetup.AccumulatePose( pos, q, pLayer->GetSequence(), fCycle, pLayer->GetWeight(), currentTime, m_pIk ); } } } void C_BaseAnimatingOverlay::AccumulateDispatchedLayers( C_BaseAnimatingOverlay *pWeapon, CStudioHdr *pWeaponStudioHdr, IBoneSetup &boneSetup, BoneVector pos[], BoneQuaternion q[], float currentTime ) { if ( !pWeapon->m_pBoneMergeCache ) return; if ( !pWeapon->IsVisible() ) return; // copy matching player pose params to weapon pose params pWeapon->m_pBoneMergeCache->MergeMatchingPoseParams(); float poseparam[MAXSTUDIOPOSEPARAM]; pWeapon->GetPoseParameters( pWeaponStudioHdr, poseparam ); // build a temporary setup for the weapon CIKContext weaponIK; weaponIK.Init( pWeaponStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, 0, BONE_USED_BY_BONE_MERGE ); IBoneSetup weaponSetup( pWeaponStudioHdr, BONE_USED_BY_BONE_MERGE, poseparam ); BoneVector weaponPos[MAXSTUDIOBONES]; BoneQuaternionAligned weaponQ[MAXSTUDIOBONES]; // copy player bones to weapon setup bones pWeapon->m_pBoneMergeCache->CopyFromFollow( pos, q, BONE_USED_BY_BONE_MERGE, weaponPos, weaponQ ); // do layer animations // FIXME: some of the layers are player layers, not weapon layers // FIXME: how to interleave? for ( int i=0; i < GetNumAnimOverlays(); i++ ) { CAnimationLayer *pLayer = GetAnimOverlay( i ); if ( pLayer->GetOrder() >= MAX_OVERLAYS || pLayer->GetSequence() <= 1 || pLayer->GetWeight() <= 0.0f ) continue; UpdateDispatchLayer( pLayer, pWeaponStudioHdr, pLayer->GetSequence() ); if ( pLayer->m_nDispatchedDst > 0 && pLayer->m_nDispatchedDst < pWeaponStudioHdr->GetNumSeq() ) { weaponSetup.AccumulatePose( weaponPos, weaponQ, pLayer->m_nDispatchedDst, pLayer->GetCycle(), pLayer->GetWeight(), currentTime, &weaponIK ); } } // FIXME: merge weaponIK into m_pIK CBoneBitList boneComputed; pWeapon->UpdateIKLocks( currentTime ); weaponIK.UpdateTargets( weaponPos, weaponQ, pWeapon->m_BoneAccessor.GetBoneArrayForWrite(), boneComputed ); pWeapon->CalculateIKLocks( currentTime ); weaponIK.SolveDependencies( weaponPos, weaponQ, pWeapon->m_BoneAccessor.GetBoneArrayForWrite(), boneComputed ); // merge weapon bones back pWeapon->m_pBoneMergeCache->CopyToFollow( weaponPos, weaponQ, BONE_USED_BY_BONE_MERGE, pos, q ); } //----------------------------------------------------------------------------- // Purpose: Duplicate parent models dispatched overlay sequences so that any local bones get animated //----------------------------------------------------------------------------- void C_BaseAnimatingOverlay::RegenerateDispatchedLayers( IBoneSetup &boneSetup, BoneVector pos[], BoneQuaternion q[], float currentTime ) { // find who I'm following and see if I'm their dispatched model if ( m_pBoneMergeCache && m_pBoneMergeCache->IsCopied() ) { C_BaseEntity *pFollowEnt = GetFollowedEntity(); if ( pFollowEnt ) { C_BaseAnimatingOverlay *pFollow = pFollowEnt->GetBaseAnimatingOverlay(); if ( pFollow ) { for ( int i=0; i < pFollow->GetNumAnimOverlays(); i++ ) { CAnimationLayer *pLayer = pFollow->GetAnimOverlay( i ); if ( pLayer->m_pDispatchedStudioHdr == NULL || pLayer->GetOrder() >= MAX_OVERLAYS || pLayer->GetSequence() == -1 || pLayer->GetWeight() <= 0.0f ) continue; // FIXME: why do the CStudioHdr's not match? if ( pLayer->m_pDispatchedStudioHdr->GetRenderHdr() == boneSetup.GetStudioHdr()->GetRenderHdr() ) { if ( pLayer->m_nDispatchedDst != ACT_INVALID ) { boneSetup.AccumulatePose( pos, q, pLayer->m_nDispatchedDst, pLayer->m_flCycle, pLayer->m_flWeight, currentTime, m_pIk ); } } } } } } } #if defined( _PS3 ) void C_BaseAnimatingOverlay::AccumulateLayers_AddPoseCalls( IBoneSetup_PS3 &boneSetup, BoneVector pos[], BoneQuaternion q[], float currentTime ) { BaseClass::AccumulateLayers_AddPoseCalls( boneSetup, pos, q, currentTime ); int i; // resort the layers int layer[MAX_OVERLAYS]; for (i = 0; i < MAX_OVERLAYS; i++) { layer[i] = MAX_OVERLAYS; } for (i = 0; i < m_AnimOverlay.Count(); i++) { if (m_AnimOverlay[i].m_nOrder < MAX_OVERLAYS) { /* Assert( layer[m_AnimOverlay[i].m_nOrder] == MAX_OVERLAYS ); layer[m_AnimOverlay[i].m_nOrder] = i; */ // hacky code until initialization of new layers is finished if ( layer[m_AnimOverlay[i].m_nOrder] != MAX_OVERLAYS ) { m_AnimOverlay[i].SetOrder( MAX_OVERLAYS ); } else { layer[m_AnimOverlay[i].m_nOrder] = i; } } } CheckForLayerChanges( boneSetup.GetStudioHdr(), currentTime ); int nSequences = boneSetup.GetStudioHdr()->GetNumSeq(); // add in the overlay layers int j; for (j = 0; j < MAX_OVERLAYS; j++) { i = layer[ j ]; if ( i >= m_AnimOverlay.Count() ) { #if defined( DEBUG_TF2_OVERLAYS ) engine->Con_NPrintf( 10 + j, "%30s %6.2f : %6.2f : %1d", " ", 0.f, 0.f, i ); #endif continue; } if ( m_AnimOverlay[i].m_nSequence >= nSequences ) continue; /* DevMsgRT( 1 , "%.3f %.3f %.3f\n", currentTime, fWeight, dadt ); debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), -j - 1, 0, "%2d(%s) : %6.2f : %6.2f", m_AnimOverlay[i].m_nSequence, boneSetup.GetStudioHdr()->pSeqdesc( m_AnimOverlay[i].m_nSequence )->pszLabel(), m_AnimOverlay[i].m_flCycle, m_AnimOverlay[i].m_flWeight ); */ float fWeight = m_AnimOverlay[i].m_flWeight; if ( fWeight <= 0.0f ) { #if defined( DEBUG_TF2_OVERLAYS ) engine->Con_NPrintf( 10 + j, "%30s %6.2f : %6.2f : %1d", " ", 0.f, 0.f, i ); #endif continue; } // check to see if the sequence changed // FIXME: move this to somewhere more reasonable // do a nice spline interpolation of the values // if ( m_AnimOverlay[i].m_nSequence != m_iv_AnimOverlay.GetPrev( i )->nSequence ) float fCycle = m_AnimOverlay[ i ].m_flCycle; fCycle = ClampCycle( fCycle, IsSequenceLooping( m_AnimOverlay[i].m_nSequence ) ); if (fWeight > 1.0f) { fWeight = 1.0f; } // boneSetup.AccumulatePose( pos, q, m_AnimOverlay[i].m_nSequence, fCycle, fWeight, currentTime, m_pIk ); boneSetup.AccumulatePose_AddToBoneJob( boneSetup.GetBoneJobSPU(), m_AnimOverlay[i].m_nSequence, fCycle, fWeight, m_pIk, 0 ); #if defined( DEBUG_TF2_OVERLAYS ) engine->Con_NPrintf( 10 + j, "%30s %6.2f : %6.2f : %1d", boneSetup.GetStudioHdr()->pSeqdesc( m_AnimOverlay[i].m_nSequence ).pszLabel(), fCycle, fWeight, i ); #endif #if 1 // _DEBUG if (r_sequence_debug.GetInt() == entindex()) { if (1) { DevMsgRT( "%8.4f : %30s : %5.3f : %4.2f : %1d\n", currentTime, boneSetup.GetStudioHdr()->pSeqdesc( m_AnimOverlay[i].m_nSequence ).pszLabel(), fCycle, fWeight, i ); } else { int iHead, iPrev1, iPrev2; m_iv_AnimOverlay[i].GetInterpolationInfo( currentTime, &iHead, &iPrev1, &iPrev2 ); // fake up previous cycle values. float t0; CAnimationLayer *pHead = m_iv_AnimOverlay[i].GetHistoryValue( iHead, t0 ); // reset previous float t1; CAnimationLayer *pPrev1 = m_iv_AnimOverlay[i].GetHistoryValue( iPrev1, t1 ); // reset previous previous float t2; CAnimationLayer *pPrev2 = m_iv_AnimOverlay[i].GetHistoryValue( iPrev2, t2 ); if ( pHead && pPrev1 && pPrev2 ) { DevMsgRT( "%6.2f : %30s %6.2f (%6.2f:%6.2f:%6.2f) : %6.2f (%6.2f:%6.2f:%6.2f) : %1d\n", currentTime, boneSetup.GetStudioHdr()->pSeqdesc( m_AnimOverlay[i].m_nSequence ).pszLabel(), fCycle, (float)pPrev2->m_flCycle, (float)pPrev1->m_flCycle, (float)pHead->m_flCycle, fWeight, (float)pPrev2->m_flWeight, (float)pPrev1->m_flWeight, (float)pHead->m_flWeight, i ); } else { DevMsgRT( "%6.2f : %30s %6.2f : %6.2f : %1d\n", currentTime, boneSetup.GetStudioHdr()->pSeqdesc( m_AnimOverlay[i].m_nSequence ).pszLabel(), fCycle, fWeight, i ); } } } #endif } } #endif // _PS3 void C_BaseAnimatingOverlay::DoAnimationEvents( CStudioHdr *pStudioHdr ) { MDLCACHE_CRITICAL_SECTION(); if ( !pStudioHdr || !pStudioHdr->SequencesAvailable() ) return; int nSequences = pStudioHdr->GetNumSeq(); BaseClass::DoAnimationEvents( pStudioHdr ); bool watch = false; // Q_strstr( hdr->name, "rifle" ) ? true : false; CheckForLayerChanges( pStudioHdr, gpGlobals->curtime ); // !!! int j; for (j = 0; j < m_AnimOverlay.Count(); j++) { if ( m_AnimOverlay[j].m_nSequence < 0 || m_AnimOverlay[j].m_nSequence >= nSequences ) { continue; } // Don't bother with 0-weight layers if ( m_AnimOverlay[j].m_flWeight == 0.0f || m_AnimOverlay[j].m_nOrder == MAX_OVERLAYS ) { 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 = GetEventIndexForSequence( seqdesc ); // 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_OldSystem() < EVENT_CLIENT ) //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, (float)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_OldSystem() < EVENT_CLIENT ) //Adrian - Support the old event system continue; bool bStartedSequence = ( m_flOverlayPrevEventCycle[j] > m_AnimOverlay[j].m_flCycle || m_flOverlayPrevEventCycle[j] == 0 ); if ( ( ( pevent[i].cycle > m_flOverlayPrevEventCycle[j] || bStartedSequence && pevent[i].cycle == 0 ) && 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, (int)m_AnimOverlay[j].m_nSequence, (int)pevent[i].Event(), (float)pevent[i].cycle, (float)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; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_BaseAnimatingOverlay::CheckInterpChanges( void ) { CDisableRangeChecks disableRangeChecks; for (int i = 0; i < m_AnimOverlay.Count(); i++) { int iHead, iPrev1, iPrev2; m_iv_AnimOverlay[i].GetInterpolationInfo( gpGlobals->curtime, &iHead, &iPrev1, &iPrev2 ); float t0; CAnimationLayer *pHead = m_iv_AnimOverlay[i].GetHistoryValue( iHead, t0 ); float t1; CAnimationLayer *pPrev = m_iv_AnimOverlay[i].GetHistoryValue( iPrev1, t1 ); if ( !pHead || !pPrev ) continue; m_AnimOverlay[ i ].m_nInvalidatePhysicsBits = CheckForSequenceBoxChanges( *pHead, *pPrev ); } CheckForLayerPhysicsInvalidate(); } void C_BaseAnimatingOverlay::CheckForLayerPhysicsInvalidate( void ) { // When the layers interpolate they may change the animation or bbox so we // have them accumulate the changes and call InvalidatePhysicsRecursive if any // changes are needed. int nInvalidatePhysicsChangeBits = 0; int nLayerCount = m_AnimOverlay.Count(); for ( int i = 0; i < nLayerCount; ++i ) { int nChangeBits = m_AnimOverlay[ i ].m_nInvalidatePhysicsBits; if ( nChangeBits ) { nInvalidatePhysicsChangeBits |= nChangeBits; continue; } } if ( nInvalidatePhysicsChangeBits ) { InvalidatePhysicsRecursive( nInvalidatePhysicsChangeBits ); } }