//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "baseviewmodel_shared.h" #include "datacache/imdlcache.h" #if defined( CLIENT_DLL ) #include "iprediction.h" #include "prediction.h" #include "client_virtualreality.h" #include "sourcevr/isourcevirtualreality.h" #else #include "vguiscreen.h" #endif #if defined( CLIENT_DLL ) && defined( SIXENSE ) #include "sixense/in_sixense.h" #include "sixense/sixense_convars_extern.h" #endif #ifdef SIXENSE extern ConVar in_forceuser; #include "iclientmode.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define VIEWMODEL_ANIMATION_PARITY_BITS 3 #define SCREEN_OVERLAY_MATERIAL "vgui/screens/vgui_overlay" //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CBaseViewModel::CBaseViewModel() { #if defined( CLIENT_DLL ) // NOTE: We do this here because the color is never transmitted for the view model. m_nOldAnimationParity = 0; m_EntClientFlags |= ENTCLIENTFLAG_ALWAYS_INTERPOLATE; #endif SetRenderColor( 255, 255, 255, 255 ); // View model of this weapon m_sVMName = NULL_STRING; // Prefix of the animations that should be used by the player carrying this weapon m_sAnimationPrefix = NULL_STRING; m_nViewModelIndex = 0; m_nAnimationParity = 0; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CBaseViewModel::~CBaseViewModel() { } void CBaseViewModel::UpdateOnRemove( void ) { BaseClass::UpdateOnRemove(); DestroyControlPanels(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseViewModel::Precache( void ) { } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseViewModel::Spawn( void ) { Precache( ); SetSize( Vector( -8, -4, -2), Vector(8, 4, 2) ); SetSolid( SOLID_NONE ); } #if defined ( CSTRIKE_DLL ) && !defined ( CLIENT_DLL ) #define VGUI_CONTROL_PANELS #endif #if defined ( TF_DLL ) #define VGUI_CONTROL_PANELS #endif #ifdef INVASION_DLL #define VGUI_CONTROL_PANELS #endif //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseViewModel::SetControlPanelsActive( bool bState ) { #if defined( VGUI_CONTROL_PANELS ) // Activate control panel screens for ( int i = m_hScreens.Count(); --i >= 0; ) { if (m_hScreens[i].Get()) { m_hScreens[i]->SetActive( bState ); } } #endif } //----------------------------------------------------------------------------- // This is called by the base object when it's time to spawn the control panels //----------------------------------------------------------------------------- void CBaseViewModel::SpawnControlPanels() { #if defined( VGUI_CONTROL_PANELS ) char buf[64]; // Destroy existing panels DestroyControlPanels(); CBaseCombatWeapon *weapon = m_hWeapon.Get(); if ( weapon == NULL ) { return; } MDLCACHE_CRITICAL_SECTION(); // FIXME: Deal with dynamically resizing control panels? // If we're attached to an entity, spawn control panels on it instead of use CBaseAnimating *pEntityToSpawnOn = this; char *pOrgLL = "controlpanel%d_ll"; char *pOrgUR = "controlpanel%d_ur"; char *pAttachmentNameLL = pOrgLL; char *pAttachmentNameUR = pOrgUR; /* if ( IsBuiltOnAttachment() ) { pEntityToSpawnOn = dynamic_cast((CBaseEntity*)m_hBuiltOnEntity.Get()); if ( pEntityToSpawnOn ) { char sBuildPointLL[64]; char sBuildPointUR[64]; Q_snprintf( sBuildPointLL, sizeof( sBuildPointLL ), "bp%d_controlpanel%%d_ll", m_iBuiltOnPoint ); Q_snprintf( sBuildPointUR, sizeof( sBuildPointUR ), "bp%d_controlpanel%%d_ur", m_iBuiltOnPoint ); pAttachmentNameLL = sBuildPointLL; pAttachmentNameUR = sBuildPointUR; } else { pEntityToSpawnOn = this; } } */ Assert( pEntityToSpawnOn ); // Lookup the attachment point... int nPanel; for ( nPanel = 0; true; ++nPanel ) { Q_snprintf( buf, sizeof( buf ), pAttachmentNameLL, nPanel ); int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nLLAttachmentIndex <= 0) { // Try and use my panels then pEntityToSpawnOn = this; Q_snprintf( buf, sizeof( buf ), pOrgLL, nPanel ); nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nLLAttachmentIndex <= 0) return; } Q_snprintf( buf, sizeof( buf ), pAttachmentNameUR, nPanel ); int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nURAttachmentIndex <= 0) { // Try and use my panels then Q_snprintf( buf, sizeof( buf ), pOrgUR, nPanel ); nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nURAttachmentIndex <= 0) return; } const char *pScreenName; weapon->GetControlPanelInfo( nPanel, pScreenName ); if (!pScreenName) continue; const char *pScreenClassname; weapon->GetControlPanelClassName( nPanel, pScreenClassname ); if ( !pScreenClassname ) continue; // Compute the screen size from the attachment points... matrix3x4_t panelToWorld; pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld ); matrix3x4_t worldToPanel; MatrixInvert( panelToWorld, worldToPanel ); // Now get the lower right position + transform into panel space Vector lr, lrlocal; pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld ); MatrixGetColumn( panelToWorld, 3, lr ); VectorTransform( lr, worldToPanel, lrlocal ); float flWidth = lrlocal.x; float flHeight = lrlocal.y; CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex ); pScreen->ChangeTeam( GetTeamNumber() ); pScreen->SetActualSize( flWidth, flHeight ); pScreen->SetActive( false ); pScreen->MakeVisibleOnlyToTeammates( false ); #ifdef INVASION_DLL pScreen->SetOverlayMaterial( SCREEN_OVERLAY_MATERIAL ); #endif pScreen->SetAttachedToViewModel( true ); int nScreen = m_hScreens.AddToTail( ); m_hScreens[nScreen].Set( pScreen ); } #endif } void CBaseViewModel::DestroyControlPanels() { #if defined( VGUI_CONTROL_PANELS ) // Kill the control panels int i; for ( i = m_hScreens.Count(); --i >= 0; ) { DestroyVGuiScreen( m_hScreens[i].Get() ); } m_hScreens.RemoveAll(); #endif } //----------------------------------------------------------------------------- // Purpose: // Input : *pEntity - //----------------------------------------------------------------------------- void CBaseViewModel::SetOwner( CBaseEntity *pEntity ) { m_hOwner = pEntity; #if !defined( CLIENT_DLL ) // Make sure we're linked into hierarchy //SetParent( pEntity ); #endif } //----------------------------------------------------------------------------- // Purpose: // Input : nIndex - //----------------------------------------------------------------------------- void CBaseViewModel::SetIndex( int nIndex ) { m_nViewModelIndex = nIndex; Assert( m_nViewModelIndex < (1 << VIEWMODEL_INDEX_BITS) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CBaseViewModel::ViewModelIndex( ) const { return m_nViewModelIndex; } //----------------------------------------------------------------------------- // Purpose: Pass our visibility on to our child screens //----------------------------------------------------------------------------- void CBaseViewModel::AddEffects( int nEffects ) { if ( nEffects & EF_NODRAW ) { SetControlPanelsActive( false ); } BaseClass::AddEffects( nEffects ); } //----------------------------------------------------------------------------- // Purpose: Pass our visibility on to our child screens //----------------------------------------------------------------------------- void CBaseViewModel::RemoveEffects( int nEffects ) { if ( nEffects & EF_NODRAW ) { SetControlPanelsActive( true ); } BaseClass::RemoveEffects( nEffects ); } //----------------------------------------------------------------------------- // Purpose: // Input : *modelname - //----------------------------------------------------------------------------- void CBaseViewModel::SetWeaponModel( const char *modelname, CBaseCombatWeapon *weapon ) { m_hWeapon = weapon; #if defined( CLIENT_DLL ) SetModel( modelname ); #else string_t str; if ( modelname != NULL ) { str = MAKE_STRING( modelname ); } else { str = NULL_STRING; } if ( str != m_sVMName ) { // Msg( "SetWeaponModel %s at %f\n", modelname, gpGlobals->curtime ); m_sVMName = str; SetModel( STRING( m_sVMName ) ); // Create any vgui control panels associated with the weapon SpawnControlPanels(); bool showControlPanels = weapon && weapon->ShouldShowControlPanels(); SetControlPanelsActive( showControlPanels ); } #endif } //----------------------------------------------------------------------------- // Purpose: // Output : CBaseCombatWeapon //----------------------------------------------------------------------------- CBaseCombatWeapon *CBaseViewModel::GetOwningWeapon( void ) { return m_hWeapon.Get(); } //----------------------------------------------------------------------------- // Purpose: // Input : sequence - //----------------------------------------------------------------------------- void CBaseViewModel::SendViewModelMatchingSequence( int sequence ) { // since all we do is send a sequence number down to the client, // set this here so other weapons code knows which sequence is playing. SetSequence( sequence ); m_nAnimationParity = ( m_nAnimationParity + 1 ) & ( (1<curtime; #else CBaseCombatWeapon *weapon = m_hWeapon.Get(); bool showControlPanels = weapon && weapon->ShouldShowControlPanels(); SetControlPanelsActive( showControlPanels ); #endif // Restart animation at frame 0 SetCycle( 0 ); ResetSequenceInfo(); } #if defined( CLIENT_DLL ) #include "ivieweffects.h" #endif void CBaseViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles ) { // UNDONE: Calc this on the server? Disabled for now as it seems unnecessary to have this info on the server #if defined( CLIENT_DLL ) QAngle vmangoriginal = eyeAngles; QAngle vmangles = eyeAngles; Vector vmorigin = eyePosition; CBaseCombatWeapon *pWeapon = m_hWeapon.Get(); //Allow weapon lagging if ( pWeapon != NULL ) { #if defined( CLIENT_DLL ) if ( !prediction->InPrediction() ) #endif { // add weapon-specific bob pWeapon->AddViewmodelBob( this, vmorigin, vmangles ); #if defined ( CSTRIKE_DLL ) CalcViewModelLag( vmorigin, vmangles, vmangoriginal ); #endif } } // Add model-specific bob even if no weapon associated (for head bob for off hand models) AddViewModelBob( owner, vmorigin, vmangles ); #if !defined ( CSTRIKE_DLL ) // This was causing weapon jitter when rotating in updated CS:S; original Source had this in above InPrediction block 07/14/10 // Add lag CalcViewModelLag( vmorigin, vmangles, vmangoriginal ); #endif #if defined( CLIENT_DLL ) if ( !prediction->InPrediction() ) { // Let the viewmodel shake at about 10% of the amplitude of the player's view vieweffects->ApplyShake( vmorigin, vmangles, 0.1 ); } #endif if( UseVR() ) { g_ClientVirtualReality.OverrideViewModelTransform( vmorigin, vmangles, pWeapon && pWeapon->ShouldUseLargeViewModelVROverride() ); } SetLocalOrigin( vmorigin ); SetLocalAngles( vmangles ); #ifdef SIXENSE if( g_pSixenseInput->IsEnabled() && (owner->GetObserverMode()==OBS_MODE_NONE) && !UseVR() ) { const float max_gun_pitch = 20.0f; float viewmodel_fov_ratio = g_pClientMode->GetViewModelFOV()/owner->GetFOV(); QAngle gun_angles = g_pSixenseInput->GetViewAngleOffset() * -viewmodel_fov_ratio; // Clamp pitch a bit to minimize seeing back of viewmodel if( gun_angles[PITCH] < -max_gun_pitch ) { gun_angles[PITCH] = -max_gun_pitch; } #ifdef WIN32 // ShouldFlipViewModel comes up unresolved on osx? Mabye because it's defined inline? fixme if( ShouldFlipViewModel() ) { gun_angles[YAW] *= -1.0f; } #endif vmangles = EyeAngles() + gun_angles; SetLocalAngles( vmangles ); } #endif #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float g_fMaxViewModelLag = 1.5f; void CBaseViewModel::CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& original_angles ) { Vector vOriginalOrigin = origin; QAngle vOriginalAngles = angles; // Calculate our drift Vector forward; AngleVectors( angles, &forward, NULL, NULL ); if ( gpGlobals->frametime != 0.0f ) { Vector vDifference; VectorSubtract( forward, m_vecLastFacing, vDifference ); float flSpeed = 5.0f; // If we start to lag too far behind, we'll increase the "catch up" speed. Solves the problem with fast cl_yawspeed, m_yaw or joysticks // rotating quickly. The old code would slam lastfacing with origin causing the viewmodel to pop to a new position float flDiff = vDifference.Length(); if ( (flDiff > g_fMaxViewModelLag) && (g_fMaxViewModelLag > 0.0f) ) { float flScale = flDiff / g_fMaxViewModelLag; flSpeed *= flScale; } // FIXME: Needs to be predictable? VectorMA( m_vecLastFacing, flSpeed * gpGlobals->frametime, vDifference, m_vecLastFacing ); // Make sure it doesn't grow out of control!!! VectorNormalize( m_vecLastFacing ); VectorMA( origin, 5.0f, vDifference * -1.0f, origin ); Assert( m_vecLastFacing.IsValid() ); } Vector right, up; AngleVectors( original_angles, &forward, &right, &up ); float pitch = original_angles[ PITCH ]; if ( pitch > 180.0f ) pitch -= 360.0f; else if ( pitch < -180.0f ) pitch += 360.0f; if ( g_fMaxViewModelLag == 0.0f ) { origin = vOriginalOrigin; angles = vOriginalAngles; } //FIXME: These are the old settings that caused too many exposed polys on some models VectorMA( origin, -pitch * 0.035f, forward, origin ); VectorMA( origin, -pitch * 0.03f, right, origin ); VectorMA( origin, -pitch * 0.02f, up, origin); } //----------------------------------------------------------------------------- // Stub to keep networking consistent for DEM files //----------------------------------------------------------------------------- #if defined( CLIENT_DLL ) extern void RecvProxy_EffectFlags( const CRecvProxyData *pData, void *pStruct, void *pOut ); void RecvProxy_SequenceNum( const CRecvProxyData *pData, void *pStruct, void *pOut ); #endif //----------------------------------------------------------------------------- // Purpose: Resets anim cycle when the server changes the weapon on us //----------------------------------------------------------------------------- #if defined( CLIENT_DLL ) static void RecvProxy_Weapon( const CRecvProxyData *pData, void *pStruct, void *pOut ) { CBaseViewModel *pViewModel = ((CBaseViewModel*)pStruct); CBaseCombatWeapon *pOldWeapon = pViewModel->GetOwningWeapon(); // Chain through to the default recieve proxy ... RecvProxy_IntToEHandle( pData, pStruct, pOut ); // ... and reset our cycle index if the server is switching weapons on us CBaseCombatWeapon *pNewWeapon = pViewModel->GetOwningWeapon(); if ( pNewWeapon != pOldWeapon ) { // Restart animation at frame 0 pViewModel->SetCycle( 0 ); pViewModel->m_flAnimTime = gpGlobals->curtime; } } #endif LINK_ENTITY_TO_CLASS( viewmodel, CBaseViewModel ); IMPLEMENT_NETWORKCLASS_ALIASED( BaseViewModel, DT_BaseViewModel ) BEGIN_NETWORK_TABLE_NOBASE(CBaseViewModel, DT_BaseViewModel) #if !defined( CLIENT_DLL ) SendPropModelIndex(SENDINFO(m_nModelIndex)), SendPropInt (SENDINFO(m_nBody), 8), SendPropInt (SENDINFO(m_nSkin), 10), SendPropInt (SENDINFO(m_nSequence), 8, SPROP_UNSIGNED), SendPropInt (SENDINFO(m_nViewModelIndex), VIEWMODEL_INDEX_BITS, SPROP_UNSIGNED), SendPropFloat (SENDINFO(m_flPlaybackRate), 8, SPROP_ROUNDUP, -4.0, 12.0f), SendPropInt (SENDINFO(m_fEffects), 10, SPROP_UNSIGNED), SendPropInt (SENDINFO(m_nAnimationParity), 3, SPROP_UNSIGNED ), SendPropEHandle (SENDINFO(m_hWeapon)), SendPropEHandle (SENDINFO(m_hOwner)), SendPropInt( SENDINFO( m_nNewSequenceParity ), EF_PARITY_BITS, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_nResetEventsParity ), EF_PARITY_BITS, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_nMuzzleFlashParity ), EF_MUZZLEFLASH_BITS, SPROP_UNSIGNED ), #if !defined( INVASION_DLL ) && !defined( INVASION_CLIENT_DLL ) SendPropArray (SendPropFloat(SENDINFO_ARRAY(m_flPoseParameter), 8, 0, 0.0f, 1.0f), m_flPoseParameter), #endif #else RecvPropInt (RECVINFO(m_nModelIndex)), RecvPropInt (RECVINFO(m_nSkin)), RecvPropInt (RECVINFO(m_nBody)), RecvPropInt (RECVINFO(m_nSequence), 0, RecvProxy_SequenceNum ), RecvPropInt (RECVINFO(m_nViewModelIndex)), RecvPropFloat (RECVINFO(m_flPlaybackRate)), RecvPropInt (RECVINFO(m_fEffects), 0, RecvProxy_EffectFlags ), RecvPropInt (RECVINFO(m_nAnimationParity)), RecvPropEHandle (RECVINFO(m_hWeapon), RecvProxy_Weapon ), RecvPropEHandle (RECVINFO(m_hOwner)), RecvPropInt( RECVINFO( m_nNewSequenceParity )), RecvPropInt( RECVINFO( m_nResetEventsParity )), RecvPropInt( RECVINFO( m_nMuzzleFlashParity )), #if !defined( INVASION_DLL ) && !defined( INVASION_CLIENT_DLL ) RecvPropArray(RecvPropFloat(RECVINFO(m_flPoseParameter[0]) ), m_flPoseParameter ), #endif #endif END_NETWORK_TABLE() #ifdef CLIENT_DLL BEGIN_PREDICTION_DATA( CBaseViewModel ) // Networked DEFINE_PRED_FIELD( m_nModelIndex, FIELD_SHORT, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ), DEFINE_PRED_FIELD( m_nSkin, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_nBody, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_nSequence, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_nViewModelIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD_TOL( m_flPlaybackRate, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.125f ), DEFINE_PRED_FIELD( m_fEffects, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_OVERRIDE ), DEFINE_PRED_FIELD( m_nAnimationParity, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_hWeapon, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_flAnimTime, FIELD_FLOAT, 0 ), DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ), DEFINE_FIELD( m_flTimeWeaponIdle, FIELD_FLOAT ), DEFINE_FIELD( m_Activity, FIELD_INTEGER ), DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_PRIVATE | FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ), END_PREDICTION_DATA() void RecvProxy_SequenceNum( const CRecvProxyData *pData, void *pStruct, void *pOut ) { CBaseViewModel *model = (CBaseViewModel *)pStruct; if (pData->m_Value.m_Int != model->GetSequence()) { MDLCACHE_CRITICAL_SECTION(); model->SetSequence(pData->m_Value.m_Int); model->m_flAnimTime = gpGlobals->curtime; model->SetCycle(0); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CBaseViewModel::LookupAttachment( const char *pAttachmentName ) { if ( m_hWeapon.Get() && m_hWeapon.Get()->WantsToOverrideViewmodelAttachments() ) return m_hWeapon.Get()->LookupAttachment( pAttachmentName ); return BaseClass::LookupAttachment( pAttachmentName ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CBaseViewModel::GetAttachment( int number, matrix3x4_t &matrix ) { if ( m_hWeapon.Get() && m_hWeapon.Get()->WantsToOverrideViewmodelAttachments() ) return m_hWeapon.Get()->GetAttachment( number, matrix ); return BaseClass::GetAttachment( number, matrix ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CBaseViewModel::GetAttachment( int number, Vector &origin ) { if ( m_hWeapon.Get() && m_hWeapon.Get()->WantsToOverrideViewmodelAttachments() ) return m_hWeapon.Get()->GetAttachment( number, origin ); return BaseClass::GetAttachment( number, origin ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CBaseViewModel::GetAttachment( int number, Vector &origin, QAngle &angles ) { if ( m_hWeapon.Get() && m_hWeapon.Get()->WantsToOverrideViewmodelAttachments() ) return m_hWeapon.Get()->GetAttachment( number, origin, angles ); return BaseClass::GetAttachment( number, origin, angles ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CBaseViewModel::GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel ) { if ( m_hWeapon.Get() && m_hWeapon.Get()->WantsToOverrideViewmodelAttachments() ) return m_hWeapon.Get()->GetAttachmentVelocity( number, originVel, angleVel ); return BaseClass::GetAttachmentVelocity( number, originVel, angleVel ); } #endif