diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/client/c_baseviewmodel.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/client/c_baseviewmodel.cpp')
| -rw-r--r-- | mp/src/game/client/c_baseviewmodel.cpp | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/mp/src/game/client/c_baseviewmodel.cpp b/mp/src/game/client/c_baseviewmodel.cpp new file mode 100644 index 00000000..0999fee0 --- /dev/null +++ b/mp/src/game/client/c_baseviewmodel.cpp @@ -0,0 +1,498 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Client side view model implementation. Responsible for drawing
+// the view model.
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "c_baseviewmodel.h"
+#include "model_types.h"
+#include "hud.h"
+#include "view_shared.h"
+#include "iviewrender.h"
+#include "view.h"
+#include "mathlib/vmatrix.h"
+#include "cl_animevent.h"
+#include "eventlist.h"
+#include "tools/bonelist.h"
+#include <KeyValues.h>
+#include "hltvcamera.h"
+
+#if defined( REPLAY_ENABLED )
+#include "replay/replaycamera.h"
+#include "replay/ireplaysystem.h"
+#include "replay/ienginereplay.h"
+#endif
+
+// NVNT haptics system interface
+#include "haptics/ihaptics.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#ifdef CSTRIKE_DLL
+ ConVar cl_righthand( "cl_righthand", "1", FCVAR_ARCHIVE, "Use right-handed view models." );
+#endif
+
+#ifdef TF_CLIENT_DLL
+ ConVar cl_flipviewmodels( "cl_flipviewmodels", "0", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_NOT_CONNECTED, "Flip view models." );
+#endif
+
+void PostToolMessage( HTOOLHANDLE hEntity, KeyValues *msg );
+
+void FormatViewModelAttachment( Vector &vOrigin, bool bInverse )
+{
+ // Presumably, SetUpView has been called so we know our FOV and render origin.
+ const CViewSetup *pViewSetup = view->GetPlayerViewSetup();
+
+ float worldx = tan( pViewSetup->fov * M_PI/360.0 );
+ float viewx = tan( pViewSetup->fovViewmodel * M_PI/360.0 );
+
+ // aspect ratio cancels out, so only need one factor
+ // the difference between the screen coordinates of the 2 systems is the ratio
+ // of the coefficients of the projection matrices (tan (fov/2) is that coefficient)
+ float factorX = worldx / viewx;
+
+ float factorY = factorX;
+
+ // Get the coordinates in the viewer's space.
+ Vector tmp = vOrigin - pViewSetup->origin;
+ Vector vTransformed( MainViewRight().Dot( tmp ), MainViewUp().Dot( tmp ), MainViewForward().Dot( tmp ) );
+
+ // Now squash X and Y.
+ if ( bInverse )
+ {
+ if ( factorX != 0 && factorY != 0 )
+ {
+ vTransformed.x /= factorX;
+ vTransformed.y /= factorY;
+ }
+ else
+ {
+ vTransformed.x = 0.0f;
+ vTransformed.y = 0.0f;
+ }
+ }
+ else
+ {
+ vTransformed.x *= factorX;
+ vTransformed.y *= factorY;
+ }
+
+
+
+ // Transform back to world space.
+ Vector vOut = (MainViewRight() * vTransformed.x) + (MainViewUp() * vTransformed.y) + (MainViewForward() * vTransformed.z);
+ vOrigin = pViewSetup->origin + vOut;
+}
+
+
+void C_BaseViewModel::FormatViewModelAttachment( int nAttachment, matrix3x4_t &attachmentToWorld )
+{
+ Vector vecOrigin;
+ MatrixPosition( attachmentToWorld, vecOrigin );
+ ::FormatViewModelAttachment( vecOrigin, false );
+ PositionMatrix( vecOrigin, attachmentToWorld );
+}
+
+
+bool C_BaseViewModel::IsViewModel() const
+{
+ return true;
+}
+
+void C_BaseViewModel::UncorrectViewModelAttachment( Vector &vOrigin )
+{
+ // Unformat the attachment.
+ ::FormatViewModelAttachment( vOrigin, true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose
+//-----------------------------------------------------------------------------
+void C_BaseViewModel::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ // We override sound requests so that we can play them locally on the owning player
+ if ( ( event == AE_CL_PLAYSOUND ) || ( event == CL_EVENT_SOUND ) )
+ {
+ // Only do this if we're owned by someone
+ if ( GetOwner() != NULL )
+ {
+ CLocalPlayerFilter filter;
+ EmitSound( filter, GetOwner()->GetSoundSourceIndex(), options, &GetAbsOrigin() );
+ return;
+ }
+ }
+
+ // Otherwise pass the event to our associated weapon
+ C_BaseCombatWeapon *pWeapon = GetActiveWeapon();
+ if ( pWeapon )
+ {
+ // NVNT notify the haptics system of our viewmodel's event
+ if ( haptics )
+ haptics->ProcessHapticEvent(4,"Weapons",pWeapon->GetName(),"AnimationEvents",VarArgs("%i",event));
+
+ bool bResult = pWeapon->OnFireEvent( this, origin, angles, event, options );
+ if ( !bResult )
+ {
+ BaseClass::FireEvent( origin, angles, event, options );
+ }
+ }
+}
+
+bool C_BaseViewModel::Interpolate( float currentTime )
+{
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ // Make sure we reset our animation information if we've switch sequences
+ UpdateAnimationParity();
+
+ bool bret = BaseClass::Interpolate( currentTime );
+
+ // Hack to extrapolate cycle counter for view model
+ float elapsed_time = currentTime - m_flAnimTime;
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+
+ // Predicted viewmodels have fixed up interval
+ if ( GetPredictable() || IsClientCreated() )
+ {
+ Assert( pPlayer );
+ float curtime = pPlayer ? pPlayer->GetFinalPredictedTime() : gpGlobals->curtime;
+ elapsed_time = curtime - m_flAnimTime;
+ // Adjust for interpolated partial frame
+ if ( !engine->IsPaused() )
+ {
+ elapsed_time += ( gpGlobals->interpolation_amount * TICK_INTERVAL );
+ }
+ }
+
+ // Prediction errors?
+ if ( elapsed_time < 0 )
+ {
+ elapsed_time = 0;
+ }
+
+ float dt = elapsed_time * GetSequenceCycleRate( pStudioHdr, GetSequence() ) * GetPlaybackRate();
+ if ( dt >= 1.0f )
+ {
+ if ( !IsSequenceLooping( GetSequence() ) )
+ {
+ dt = 0.999f;
+ }
+ else
+ {
+ dt = fmod( dt, 1.0f );
+ }
+ }
+
+ SetCycle( dt );
+ return bret;
+}
+
+
+inline bool C_BaseViewModel::ShouldFlipViewModel()
+{
+#ifdef CSTRIKE_DLL
+ // If cl_righthand is set, then we want them all right-handed.
+ CBaseCombatWeapon *pWeapon = m_hWeapon.Get();
+ if ( pWeapon )
+ {
+ const FileWeaponInfo_t *pInfo = &pWeapon->GetWpnData();
+ return pInfo->m_bAllowFlipping && pInfo->m_bBuiltRightHanded != cl_righthand.GetBool();
+ }
+#endif
+
+#ifdef TF_CLIENT_DLL
+ CBaseCombatWeapon *pWeapon = m_hWeapon.Get();
+ if ( pWeapon )
+ {
+ return pWeapon->m_bFlipViewModel != cl_flipviewmodels.GetBool();
+ }
+#endif
+
+ return false;
+}
+
+
+void C_BaseViewModel::ApplyBoneMatrixTransform( matrix3x4_t& transform )
+{
+ if ( ShouldFlipViewModel() )
+ {
+ matrix3x4_t viewMatrix, viewMatrixInverse;
+
+ // We could get MATERIAL_VIEW here, but this is called sometimes before the renderer
+ // has set that matrix. Luckily, this is called AFTER the CViewSetup has been initialized.
+ const CViewSetup *pSetup = view->GetPlayerViewSetup();
+ AngleMatrix( pSetup->angles, pSetup->origin, viewMatrixInverse );
+ MatrixInvert( viewMatrixInverse, viewMatrix );
+
+ // Transform into view space.
+ matrix3x4_t temp, temp2;
+ ConcatTransforms( viewMatrix, transform, temp );
+
+ // Flip it along X.
+
+ // (This is the slower way to do it, and it equates to negating the top row).
+ //matrix3x4_t mScale;
+ //SetIdentityMatrix( mScale );
+ //mScale[0][0] = 1;
+ //mScale[1][1] = -1;
+ //mScale[2][2] = 1;
+ //ConcatTransforms( mScale, temp, temp2 );
+ temp[1][0] = -temp[1][0];
+ temp[1][1] = -temp[1][1];
+ temp[1][2] = -temp[1][2];
+ temp[1][3] = -temp[1][3];
+
+ // Transform back out of view space.
+ ConcatTransforms( viewMatrixInverse, temp, transform );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: check if weapon viewmodel should be drawn
+//-----------------------------------------------------------------------------
+bool C_BaseViewModel::ShouldDraw()
+{
+ if ( engine->IsHLTV() )
+ {
+ return ( HLTVCamera()->GetMode() == OBS_MODE_IN_EYE &&
+ HLTVCamera()->GetPrimaryTarget() == GetOwner() );
+ }
+#if defined( REPLAY_ENABLED )
+ else if ( g_pEngineClientReplay->IsPlayingReplayDemo() )
+ {
+ return ( ReplayCamera()->GetMode() == OBS_MODE_IN_EYE &&
+ ReplayCamera()->GetPrimaryTarget() == GetOwner() );
+ }
+#endif
+ else
+ {
+ return BaseClass::ShouldDraw();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Render the weapon. Draw the Viewmodel if the weapon's being carried
+// by this player, otherwise draw the worldmodel.
+//-----------------------------------------------------------------------------
+int C_BaseViewModel::DrawModel( int flags )
+{
+ if ( !m_bReadyToDraw )
+ return 0;
+
+ if ( flags & STUDIO_RENDER )
+ {
+ // Determine blending amount and tell engine
+ float blend = (float)( GetFxBlend() / 255.0f );
+
+ // Totally gone
+ if ( blend <= 0.0f )
+ return 0;
+
+ // Tell engine
+ render->SetBlend( blend );
+
+ float color[3];
+ GetColorModulation( color );
+ render->SetColorModulation( color );
+ }
+
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ C_BaseCombatWeapon *pWeapon = GetOwningWeapon();
+ int ret;
+ // If the local player's overriding the viewmodel rendering, let him do it
+ if ( pPlayer && pPlayer->IsOverridingViewmodel() )
+ {
+ ret = pPlayer->DrawOverriddenViewmodel( this, flags );
+ }
+ else if ( pWeapon && pWeapon->IsOverridingViewmodel() )
+ {
+ ret = pWeapon->DrawOverriddenViewmodel( this, flags );
+ }
+ else
+ {
+ ret = BaseClass::DrawModel( flags );
+ }
+
+ // Now that we've rendered, reset the animation restart flag
+ if ( flags & STUDIO_RENDER )
+ {
+ if ( m_nOldAnimationParity != m_nAnimationParity )
+ {
+ m_nOldAnimationParity = m_nAnimationParity;
+ }
+ // Tell the weapon itself that we've rendered, in case it wants to do something
+ if ( pWeapon )
+ {
+ pWeapon->ViewModelDrawn( this );
+ }
+ }
+
+ return ret;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int C_BaseViewModel::InternalDrawModel( int flags )
+{
+ CMatRenderContextPtr pRenderContext( materials );
+ if ( ShouldFlipViewModel() )
+ pRenderContext->CullMode( MATERIAL_CULLMODE_CW );
+
+ int ret = BaseClass::InternalDrawModel( flags );
+
+ pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );
+
+ return ret;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called by the player when the player's overriding the viewmodel drawing. Avoids infinite recursion.
+//-----------------------------------------------------------------------------
+int C_BaseViewModel::DrawOverriddenViewmodel( int flags )
+{
+ return BaseClass::DrawModel( flags );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int C_BaseViewModel::GetFxBlend( void )
+{
+ // See if the local player wants to override the viewmodel's rendering
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pPlayer && pPlayer->IsOverridingViewmodel() )
+ {
+ pPlayer->ComputeFxBlend();
+ return pPlayer->GetFxBlend();
+ }
+
+ C_BaseCombatWeapon *pWeapon = GetOwningWeapon();
+ if ( pWeapon && pWeapon->IsOverridingViewmodel() )
+ {
+ pWeapon->ComputeFxBlend();
+ return pWeapon->GetFxBlend();
+ }
+
+ return BaseClass::GetFxBlend();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool C_BaseViewModel::IsTransparent( void )
+{
+ // See if the local player wants to override the viewmodel's rendering
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pPlayer && pPlayer->IsOverridingViewmodel() )
+ {
+ return pPlayer->ViewModel_IsTransparent();
+ }
+
+ C_BaseCombatWeapon *pWeapon = GetOwningWeapon();
+ if ( pWeapon && pWeapon->IsOverridingViewmodel() )
+ return pWeapon->ViewModel_IsTransparent();
+
+ return BaseClass::IsTransparent();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_BaseViewModel::UsesPowerOfTwoFrameBufferTexture( void )
+{
+ // See if the local player wants to override the viewmodel's rendering
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pPlayer && pPlayer->IsOverridingViewmodel() )
+ {
+ return pPlayer->ViewModel_IsUsingFBTexture();
+ }
+
+ C_BaseCombatWeapon *pWeapon = GetOwningWeapon();
+ if ( pWeapon && pWeapon->IsOverridingViewmodel() )
+ {
+ return pWeapon->ViewModel_IsUsingFBTexture();
+ }
+
+ return BaseClass::UsesPowerOfTwoFrameBufferTexture();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If the animation parity of the weapon has changed, we reset cycle to avoid popping
+//-----------------------------------------------------------------------------
+void C_BaseViewModel::UpdateAnimationParity( void )
+{
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+
+ // If we're predicting, then we don't use animation parity because we change the animations on the clientside
+ // while predicting. When not predicting, only the server changes the animations, so a parity mismatch
+ // tells us if we need to reset the animation.
+ if ( m_nOldAnimationParity != m_nAnimationParity && !GetPredictable() )
+ {
+ float curtime = (pPlayer && IsIntermediateDataAllocated()) ? pPlayer->GetFinalPredictedTime() : gpGlobals->curtime;
+ // FIXME: this is bad
+ // Simulate a networked m_flAnimTime and m_flCycle
+ // FIXME: Do we need the magic 0.1?
+ SetCycle( 0.0f ); // GetSequenceCycleRate( GetSequence() ) * 0.1;
+ m_flAnimTime = curtime;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update global map state based on data received
+// Input : bnewentity -
+//-----------------------------------------------------------------------------
+void C_BaseViewModel::OnDataChanged( DataUpdateType_t updateType )
+{
+ SetPredictionEligible( true );
+ BaseClass::OnDataChanged(updateType);
+}
+
+void C_BaseViewModel::PostDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PostDataUpdate(updateType);
+ OnLatchInterpolatedVariables( LATCH_ANIMATION_VAR );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Add entity to visible view models list
+//-----------------------------------------------------------------------------
+void C_BaseViewModel::AddEntity( void )
+{
+ // Server says don't interpolate this frame, so set previous info to new info.
+ if ( IsNoInterpolationFrame() )
+ {
+ ResetLatched();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseViewModel::GetBoneControllers(float controllers[MAXSTUDIOBONECTRLS])
+{
+ BaseClass::GetBoneControllers( controllers );
+
+ // Tell the weapon itself that we've rendered, in case it wants to do something
+ C_BaseCombatWeapon *pWeapon = GetActiveWeapon();
+ if ( pWeapon )
+ {
+ pWeapon->GetViewmodelBoneControllers( this, controllers );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : RenderGroup_t
+//-----------------------------------------------------------------------------
+RenderGroup_t C_BaseViewModel::GetRenderGroup()
+{
+ return RENDER_GROUP_VIEW_MODEL_OPAQUE;
+}
|