From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/client/c_baseviewmodel.cpp | 498 +++++++++++++++++++++++++++++++++ 1 file changed, 498 insertions(+) create mode 100644 mp/src/game/client/c_baseviewmodel.cpp (limited to 'mp/src/game/client/c_baseviewmodel.cpp') 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 +#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; +} -- cgit v1.2.3