void CBasePlayerAnimState::OptimizeLayerWeights( int iFirstLayer, int nLayers ) { int i; // Find the total weight of the blended layers, not including the idle layer (iFirstLayer) float totalWeight = 0.0f; for ( i=1; i < nLayers; i++ ) { CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( iFirstLayer+i ); if ( pLayer->IsActive() && pLayer->m_flWeight > 0.0f ) { totalWeight += pLayer->m_flWeight; } } // Set the idle layer's weight to be 1 minus the sum of other layer weights CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( iFirstLayer ); if ( pLayer->IsActive() && pLayer->m_flWeight > 0.0f ) { pLayer->m_flWeight = 1.0f - totalWeight; pLayer->m_flWeight = max(pLayer->m_flWeight, 0.0f); } // This part is just an optimization. Since we have the walk/run animations weighted on top of // the idle animations, all this does is disable the idle animations if the walk/runs are at // full weighting, which is whenever a guy is at full speed. // // So it saves us blending a couple animation layers whenever a guy is walking or running full speed. int iLastOne = -1; for ( i=0; i < nLayers; i++ ) { CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( iFirstLayer+i ); if ( pLayer->IsActive() && pLayer->m_flWeight > 0.99 ) iLastOne = i; } if ( iLastOne != -1 ) { for ( int i=iLastOne-1; i >= 0; i-- ) { CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( iFirstLayer+i ); #ifdef CLIENT_DLL pLayer->m_nOrder = CBaseAnimatingOverlay::MAX_OVERLAYS; #else pLayer->m_nOrder.Set( CBaseAnimatingOverlay::MAX_OVERLAYS ); pLayer->m_fFlags = 0; #endif } } }
//----------------------------------------------------------------------------- // Purpose: Override for backpeddling // Input : dt - //----------------------------------------------------------------------------- void CBasePlayerAnimState::ComputePoseParam_MoveYaw( CStudioHdr *pStudioHdr ) { VPROF( "CBasePlayerAnimState::ComputePoseParam_MoveYaw" ); //Matt: Goldsrc style animations need to not rotate the model if ( m_AnimConfig.m_LegAnimType == LEGANIM_GOLDSRC ) { #ifndef CLIENT_DLL //Adrian: Make the model's angle match the legs so the hitboxes match on both sides. GetOuter()->SetLocalAngles( QAngle( 0, m_flCurrentFeetYaw, 0 ) ); #endif } // If using goldsrc-style animations where he's moving in the direction that his feet are facing, // we don't use move yaw. if ( m_AnimConfig.m_LegAnimType != LEGANIM_9WAY && m_AnimConfig.m_LegAnimType != LEGANIM_8WAY ) return; // view direction relative to movement float flYaw; EstimateYaw(); float ang = m_flEyeYaw; if ( ang > 180.0f ) { ang -= 360.0f; } else if ( ang < -180.0f ) { ang += 360.0f; } // calc side to side turning flYaw = ang - m_flGaitYaw; // Invert for mapping into 8way blend flYaw = -flYaw; flYaw = flYaw - (int)(flYaw / 360) * 360; if (flYaw < -180) { flYaw = flYaw + 360; } else if (flYaw > 180) { flYaw = flYaw - 360; } if ( m_AnimConfig.m_LegAnimType == LEGANIM_9WAY ) { #ifndef CLIENT_DLL //Adrian: Make the model's angle match the legs so the hitboxes match on both sides. GetOuter()->SetLocalAngles( QAngle( 0, m_flCurrentFeetYaw, 0 ) ); #endif int iMoveX = GetOuter()->LookupPoseParameter( pStudioHdr, "move_x" ); int iMoveY = GetOuter()->LookupPoseParameter( pStudioHdr, "move_y" ); if ( iMoveX < 0 || iMoveY < 0 ) return; bool bIsMoving; float flPlaybackRate = CalcMovementPlaybackRate( &bIsMoving ); // Setup the 9-way blend parameters based on our speed and direction. Vector2D vCurMovePose( 0, 0 ); if ( bIsMoving ) { vCurMovePose.x = cos( DEG2RAD( flYaw ) ) * flPlaybackRate; vCurMovePose.y = -sin( DEG2RAD( flYaw ) ) * flPlaybackRate; } GetOuter()->SetPoseParameter( pStudioHdr, iMoveX, vCurMovePose.x ); GetOuter()->SetPoseParameter( pStudioHdr, iMoveY, vCurMovePose.y ); m_vLastMovePose = vCurMovePose; } else { int iMoveYaw = GetOuter()->LookupPoseParameter( pStudioHdr, "move_yaw" ); if ( iMoveYaw >= 0 ) { GetOuter()->SetPoseParameter( pStudioHdr, iMoveYaw, flYaw ); m_flLastMoveYaw = flYaw; // Now blend in his idle animation. // This makes the 8-way blend act like a 9-way blend by blending to // an idle sequence as he slows down. #if defined(CLIENT_DLL) #ifndef INFESTED_DLL bool bIsMoving; CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( MAIN_IDLE_SEQUENCE_LAYER ); pLayer->SetWeight( 1 - CalcMovementPlaybackRate( &bIsMoving ) ); if ( !bIsMoving ) { pLayer->SetWeight( 1 ); } if ( ShouldChangeSequences() ) { // Whenever this layer stops blending, we can choose a new idle sequence to blend to, so he // doesn't always use the same idle. if ( pLayer->GetWeight() < 0.02f || m_iCurrent8WayIdleSequence == -1 ) { m_iCurrent8WayIdleSequence = m_pOuter->SelectWeightedSequence( ACT_IDLE ); m_iCurrent8WayCrouchIdleSequence = m_pOuter->SelectWeightedSequence( ACT_CROUCHIDLE ); } if ( m_eCurrentMainSequenceActivity == ACT_CROUCHIDLE || m_eCurrentMainSequenceActivity == ACT_RUN_CROUCH ) pLayer->SetSequence( m_iCurrent8WayCrouchIdleSequence ); else pLayer->SetSequence( m_iCurrent8WayIdleSequence ); } pLayer->SetPlaybackRate( 1 ); pLayer->SetCycle( pLayer->GetCycle() + m_pOuter->GetSequenceCycleRate( pStudioHdr, pLayer->GetSequence() ) * gpGlobals->frametime ); pLayer->SetCycle( fmod( pLayer->GetCycle(), 1 ) ); pLayer->SetOrder( MAIN_IDLE_SEQUENCE_LAYER ); #endif #endif } } }
// ----------------------------------------------------------------------------- void CBasePlayerAnimState::DebugShowAnimState( int iStartLine ) { Vector vOuterVel; GetOuterAbsVelocity( vOuterVel ); int iLine = iStartLine; AnimStatePrintf( iLine++, "main: %s(%d), cycle: %.2f cyclerate: %.2f playbackrate: %.2f\n", GetSequenceName( m_pOuter->GetModelPtr(), m_pOuter->GetSequence() ), m_pOuter->GetSequence(), m_pOuter->GetCycle(), m_pOuter->GetSequenceCycleRate(m_pOuter->GetModelPtr(), m_pOuter->GetSequence()), m_pOuter->GetPlaybackRate() ); if ( m_AnimConfig.m_LegAnimType == LEGANIM_8WAY ) { CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( MAIN_IDLE_SEQUENCE_LAYER ); AnimStatePrintf( iLine++, "idle: %s, weight: %.2f\n", GetSequenceName( m_pOuter->GetModelPtr(), pLayer->GetSequence() ), (float)pLayer->GetWeight() ); } for ( int i=0; i < m_pOuter->GetNumAnimOverlays()-1; i++ ) { CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( AIMSEQUENCE_LAYER + i ); #ifdef CLIENT_DLL AnimStatePrintf( iLine++, "%s(%d), weight: %.2f, cycle: %.2f, order (%d), aim (%d)", !pLayer->IsActive() ? "-- ": (pLayer->GetSequence() == 0 ? "-- " : (showanimstate_activities.GetBool()) ? GetSequenceActivityName( m_pOuter->GetModelPtr(), pLayer->GetSequence() ) : GetSequenceName( m_pOuter->GetModelPtr(), pLayer->GetSequence() ) ), !pLayer->IsActive() ? 0 : (int)pLayer->GetSequence(), !pLayer->IsActive() ? 0 : (float)pLayer->GetWeight(), !pLayer->IsActive() ? 0 : (float)pLayer->GetCycle(), !pLayer->IsActive() ? 0 : (int)pLayer->GetOrder(), i ); #else AnimStatePrintf( iLine++, "%s(%d), flags (%d), weight: %.2f, cycle: %.2f, order (%d), aim (%d)", !pLayer->IsActive() ? "-- " : ( pLayer->GetSequence() == 0 ? "-- " : (showanimstate_activities.GetBool()) ? GetSequenceActivityName( m_pOuter->GetModelPtr(), pLayer->GetSequence() ) : GetSequenceName( m_pOuter->GetModelPtr(), pLayer->GetSequence() ) ), !pLayer->IsActive() ? 0 : (int)pLayer->GetSequence(), !pLayer->IsActive() ? 0 : (int)pLayer->m_fFlags,// Doesn't exist on client !pLayer->IsActive() ? 0 : (float)pLayer->GetWeight(), !pLayer->IsActive() ? 0 : (float)pLayer->GetCycle(), !pLayer->IsActive() ? 0 : (int)pLayer->m_nOrder, i ); #endif } AnimStatePrintf( iLine++, "vel: %.2f, time: %.2f, MAX: %.2f, animspeed: %.2f", vOuterVel.Length2D(), gpGlobals->curtime, GetInterpolatedGroundSpeed(), m_pOuter->GetSequenceGroundSpeed(m_pOuter->GetSequence()) ); if ( m_AnimConfig.m_LegAnimType == LEGANIM_8WAY ) { AnimStatePrintf( iLine++, "ent yaw: %.2f, body_yaw: %.2f, move_yaw: %.2f, gait_yaw: %.2f, body_pitch: %.2f", m_angRender[YAW], g_flLastBodyYaw, m_flLastMoveYaw, m_flGaitYaw, g_flLastBodyPitch ); } else { AnimStatePrintf( iLine++, "ent yaw: %.2f, body_yaw: %.2f, body_pitch: %.2f, move_x: %.2f, move_y: %.2f", m_angRender[YAW], g_flLastBodyYaw, g_flLastBodyPitch, m_vLastMovePose.x, m_vLastMovePose.y ); } // Draw a red triangle on the ground for the eye yaw. float flBaseSize = 10; float flHeight = 80; Vector vBasePos = GetOuter()->GetAbsOrigin() + Vector( 0, 0, 3 ); QAngle angles( 0, 0, 0 ); angles[YAW] = m_flEyeYaw; Vector vForward, vRight, vUp; AngleVectors( angles, &vForward, &vRight, &vUp ); debugoverlay->AddTriangleOverlay( vBasePos+vRight*flBaseSize/2, vBasePos-vRight*flBaseSize/2, vBasePos+vForward*flHeight, 255, 0, 0, 255, false, 0.01 ); // Draw a blue triangle on the ground for the body yaw. angles[YAW] = m_angRender[YAW]; AngleVectors( angles, &vForward, &vRight, &vUp ); debugoverlay->AddTriangleOverlay( vBasePos+vRight*flBaseSize/2, vBasePos-vRight*flBaseSize/2, vBasePos+vForward*flHeight, 0, 0, 255, 255, false, 0.01 ); }
void CBaseAnimatingOverlay::StudioFrameAdvance () { float flAdvance = GetAnimTimeInterval(); VerifyOrder(); BaseClass::StudioFrameAdvance(); for ( int i = 0; i < m_AnimOverlay.Count(); i++ ) { CAnimationLayer *pLayer = &m_AnimOverlay[i]; if (pLayer->IsActive()) { // Assert( !m_AnimOverlay[ i ].IsAbandoned() ); if (pLayer->IsKillMe()) { if (pLayer->m_flKillDelay > 0) { pLayer->m_flKillDelay -= flAdvance; pLayer->m_flKillDelay = clamp( pLayer->m_flKillDelay, 0.0, 1.0 ); } else if (pLayer->m_flWeight != 0.0f) { // give it at least one frame advance cycle to propagate 0.0 to client pLayer->m_flWeight -= pLayer->m_flKillRate * flAdvance; pLayer->m_flWeight = clamp( pLayer->m_flWeight.Get(), 0.0, 1.0 ); } else { // shift the other layers down in order if (ai_sequence_debug.GetBool() == true && m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) { Msg("removing %d (%d): %s : %5.3f (%.3f)\n", i, pLayer->m_nOrder.Get(), GetSequenceName( pLayer->m_nSequence ), pLayer->m_flCycle.Get(), pLayer->m_flWeight.Get() ); } FastRemoveLayer( i ); // needs at least one thing cycle dead to trigger sequence change pLayer->Dying(); continue; } } pLayer->StudioFrameAdvance( flAdvance, this ); if ( pLayer->m_bSequenceFinished && (pLayer->IsAutokill()) ) { pLayer->m_flWeight = 0.0f; pLayer->KillMe(); } } else if (pLayer->IsDying()) { pLayer->Dead(); } else if (pLayer->m_flWeight > 0.0) { // Now that the server blends, it is turning off layers all the time. Having a weight left over // when you're no longer marked as active is now harmless and commonplace. Just clean up. pLayer->Init( this ); pLayer->Dying(); } } if (ai_sequence_debug.GetBool() == true && m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) { for ( int i = 0; i < m_AnimOverlay.Count(); i++ ) { if (m_AnimOverlay[ i ].IsActive()) { /* if (m_AnimOverlay[ i ].IsAbandoned()) { Msg(" %d abandoned %.2f (%.2f)\n", i, gpGlobals->curtime, m_AnimOverlay[ i ].m_flLastAccess ); } */ Msg(" %d (%d): %s : %5.3f (%.3f)\n", i, m_AnimOverlay[ i ].m_nOrder.Get(), GetSequenceName( m_AnimOverlay[ i ].m_nSequence ), m_AnimOverlay[ i ].m_flCycle.Get(), m_AnimOverlay[ i ].m_flWeight.Get() ); } } } VerifyOrder(); }
void RecvProxy_OrderChanged( const CRecvProxyData *pData, void *pStruct, void *pOut ) { CAnimationLayer *pLayer = (CAnimationLayer *)pStruct; pLayer->SetOrder( pData->m_Value.m_Int ); }
void RecvProxy_CycleChanged( const CRecvProxyData *pData, void *pStruct, void *pOut ) { CAnimationLayer *pLayer = (CAnimationLayer *)pStruct; pLayer->SetCycle( pData->m_Value.m_Float ); }