diff --git a/sp/src/game/client/c_baseanimating.h b/sp/src/game/client/c_baseanimating.h index 5f30d8f8b..0b26c0fdc 100644 --- a/sp/src/game/client/c_baseanimating.h +++ b/sp/src/game/client/c_baseanimating.h @@ -591,16 +591,19 @@ protected: float m_flOldCycle; bool m_bNoModelParticles; + // GSTRINGMIGRATION made protected + CBoneMergeCache *m_pBoneMergeCache; // This caches the strcmp lookups that it has to do + // when merg + CJiggleBones *m_pJiggleBones; + // END GSTRINGMIGRATION + private: float m_flOldModelScale; int m_nOldSequence; - CBoneMergeCache *m_pBoneMergeCache; // This caches the strcmp lookups that it has to do - // when merg CUtlVector< matrix3x4_t > m_CachedBoneData; // never access this directly. Use m_BoneAccessor. memhandle_t m_hitboxBoneCacheHandle; float m_flLastBoneSetupTime; - CJiggleBones *m_pJiggleBones; // Calculated attachment points CUtlVector m_Attachments; diff --git a/sp/src/game/client/client_gstring.vpc b/sp/src/game/client/client_gstring.vpc index e3a21b762..f3bd88fe5 100644 --- a/sp/src/game/client/client_gstring.vpc +++ b/sp/src/game/client/client_gstring.vpc @@ -45,6 +45,8 @@ $Project "Client (G-String)" $File "gstring\c_muzzleflash_effect.h" $File "gstring\c_bobmodel.cpp" $File "gstring\c_bobmodel.h" + $File "gstring\c_firstpersonbody.cpp" + $File "gstring\c_firstpersonbody.h" $Folder "vgui" { diff --git a/sp/src/game/client/gstring/c_bobmodel.h b/sp/src/game/client/gstring/c_bobmodel.h index 05c84ffe9..ce895f6fe 100644 --- a/sp/src/game/client/gstring/c_bobmodel.h +++ b/sp/src/game/client/gstring/c_bobmodel.h @@ -9,6 +9,10 @@ class C_BobModel : public C_BaseAnimating public: C_BobModel(); + virtual int ObjectCaps() { + return FCAP_DONT_SAVE; + }; + virtual bool ShouldDraw() { return true; } virtual int DrawModel( int flags ) { return 0; } virtual void FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ) {} diff --git a/sp/src/game/client/gstring/c_firstpersonbody.cpp b/sp/src/game/client/gstring/c_firstpersonbody.cpp new file mode 100644 index 000000000..7d5d746df --- /dev/null +++ b/sp/src/game/client/gstring/c_firstpersonbody.cpp @@ -0,0 +1,183 @@ + +#include "cbase.h" +#include "c_firstpersonbody.h" + +#include "bone_setup.h" +#include "jigglebones.h" +#include "viewrender.h" + + +C_FirstpersonBody::C_FirstpersonBody() + : m_iBoneNeck( -1 ) + , m_iBoneArmL( -1 ) + , m_iBoneArmR( -1 ) + , m_iPoseParam_MoveYaw( -1 ) +{ +} + +CStudioHdr *C_FirstpersonBody::OnNewModel() +{ + CStudioHdr *pRet = BaseClass::OnNewModel(); + + m_iBoneNeck = LookupBone( "ValveBiped.Bip01_Neck1" ); + m_iBoneArmL = LookupBone( "ValveBiped.Bip01_L_UpperArm" ); + m_iBoneArmR = LookupBone( "ValveBiped.Bip01_R_UpperArm" ); + m_iPoseParam_MoveYaw = LookupPoseParameter( "move_yaw" ); + + return pRet; +} + +ShadowType_t C_FirstpersonBody::ShadowCastType() +{ + return SHADOWS_SIMPLE; +} + +void C_FirstpersonBody::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion *q, + const matrix3x4_t &cameraTransform, int boneMask, CBoneBitList &boneComputed ) +{ + if ( !hdr ) + return; + + matrix3x4_t bonematrix; + bool boneSimulated[MAXSTUDIOBONES]; + + // no bones have been simulated + memset( boneSimulated, 0, sizeof(boneSimulated) ); + mstudiobone_t *pbones = hdr->pBone( 0 ); + + if ( m_pRagdoll ) + { + // simulate bones and update flags + int oldWritableBones = m_BoneAccessor.GetWritableBones(); + int oldReadableBones = m_BoneAccessor.GetReadableBones(); + m_BoneAccessor.SetWritableBones( BONE_USED_BY_ANYTHING ); + m_BoneAccessor.SetReadableBones( BONE_USED_BY_ANYTHING ); + +#if defined( REPLAY_ENABLED ) + // If we're playing back a demo, override the ragdoll bones with cached version if available - otherwise, simulate. + if ( ( !engine->IsPlayingDemo() && !engine->IsPlayingTimeDemo() ) || + !CReplayRagdollCache::Instance().IsInitialized() || + !CReplayRagdollCache::Instance().GetFrame( this, engine->GetDemoPlaybackTick(), boneSimulated, &m_BoneAccessor ) ) +#endif + { + m_pRagdoll->RagdollBone( this, pbones, hdr->numbones(), boneSimulated, m_BoneAccessor ); + } + + m_BoneAccessor.SetWritableBones( oldWritableBones ); + m_BoneAccessor.SetReadableBones( oldReadableBones ); + } + + // For EF_BONEMERGE entities, copy the bone matrices for any bones that have matching names. + bool boneMerge = IsEffectActive(EF_BONEMERGE); + if ( boneMerge || m_pBoneMergeCache ) + { + if ( boneMerge ) + { + if ( !m_pBoneMergeCache ) + { + m_pBoneMergeCache = new CBoneMergeCache; + m_pBoneMergeCache->Init( this ); + } + m_pBoneMergeCache->MergeMatchingBones( boneMask ); + } + else + { + delete m_pBoneMergeCache; + m_pBoneMergeCache = NULL; + } + } + + for (int i = 0; i < hdr->numbones(); i++) + { + // Only update bones reference by the bone mask. + if ( !( hdr->boneFlags( i ) & boneMask ) ) + { + continue; + } + + if ( m_pBoneMergeCache && m_pBoneMergeCache->IsBoneMerged( i ) ) + continue; + + // animate all non-simulated bones + if ( boneSimulated[i] || CalcProceduralBone( hdr, i, m_BoneAccessor )) + { + continue; + } + // skip bones that the IK has already setup + else if (boneComputed.IsBoneMarked( i )) + { + // dummy operation, just used to verify in debug that this should have happened + GetBoneForWrite( i ); + } + else + { + QuaternionMatrix( q[i], pos[i], bonematrix ); + + Assert( fabs( pos[i].x ) < 100000 ); + Assert( fabs( pos[i].y ) < 100000 ); + Assert( fabs( pos[i].z ) < 100000 ); + + if ( (hdr->boneFlags( i ) & BONE_ALWAYS_PROCEDURAL) && + (hdr->pBone( i )->proctype & STUDIO_PROC_JIGGLE) ) + { + // + // Physics-based "jiggle" bone + // Bone is assumed to be along the Z axis + // Pitch around X, yaw around Y + // + + // compute desired bone orientation + matrix3x4_t goalMX; + + if (pbones[i].parent == -1) + { + ConcatTransforms( cameraTransform, bonematrix, goalMX ); + } + else + { + ConcatTransforms( GetBone( pbones[i].parent ), bonematrix, goalMX ); + } + + // get jiggle properties from QC data + mstudiojigglebone_t *jiggleInfo = (mstudiojigglebone_t *)pbones[i].pProcedure( ); + + if (!m_pJiggleBones) + { + m_pJiggleBones = new CJiggleBones; + } + + // do jiggle physics + m_pJiggleBones->BuildJiggleTransformations( i, gpGlobals->realtime, jiggleInfo, goalMX, GetBoneForWrite( i ) ); + + } + else if (hdr->boneParent(i) == -1) + { + ConcatTransforms( cameraTransform, bonematrix, GetBoneForWrite( i ) ); + } + else + { + ConcatTransforms( GetBone( hdr->boneParent(i) ), bonematrix, GetBoneForWrite( i ) ); + } + } + + if (hdr->boneParent(i) == -1) + { + MatrixScaleBy( 0.9f, GetBoneForWrite( i ) ); + } + + if ( i == m_iBoneNeck + || i == m_iBoneArmR + || i == m_iBoneArmL ) + { + MatrixScaleBy( 0.01f, GetBoneForWrite( i ) ); + } + } +} + +int C_FirstpersonBody::DrawModel( int flags ) +{ + if ( CurrentViewID() == VIEW_SHADOW_DEPTH_TEXTURE ) + return 0; + + return BaseClass::DrawModel( flags ); +} diff --git a/sp/src/game/client/gstring/c_firstpersonbody.h b/sp/src/game/client/gstring/c_firstpersonbody.h new file mode 100644 index 000000000..39fa496db --- /dev/null +++ b/sp/src/game/client/gstring/c_firstpersonbody.h @@ -0,0 +1,34 @@ +#ifndef C_FIRSTPERSONBODY_H +#define C_FIRSTPERSONBODY_H + + +class C_FirstpersonBody : public C_BaseAnimating +{ + DECLARE_CLASS( C_FirstpersonBody, C_BaseAnimating ); +public: + C_FirstpersonBody(); + + virtual int ObjectCaps() { + return FCAP_DONT_SAVE; + }; + + virtual void BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion *q, + const matrix3x4_t &cameraTransform, int boneMask, CBoneBitList &boneComputed ); + + virtual CStudioHdr *OnNewModel(); + + virtual ShadowType_t ShadowCastType(); + + virtual int DrawModel( int flags ); + + int m_iPoseParam_MoveYaw; + +private: + int m_iBoneNeck; + int m_iBoneArmL; + int m_iBoneArmR; + +}; + + +#endif \ No newline at end of file diff --git a/sp/src/game/client/gstring/c_gstring_player.cpp b/sp/src/game/client/gstring/c_gstring_player.cpp index bc141d1af..90b1c896d 100644 --- a/sp/src/game/client/gstring/c_gstring_player.cpp +++ b/sp/src/game/client/gstring/c_gstring_player.cpp @@ -6,6 +6,8 @@ #include "flashlighteffect.h" #include "c_muzzleflash_effect.h" #include "c_bobmodel.h" +#include "c_firstpersonbody.h" + #define FLASHLIGHT_DISTANCE 1000 @@ -22,6 +24,7 @@ // } //} + IMPLEMENT_CLIENTCLASS_DT( C_GstringPlayer, DT_CGstringPlayer, CGstringPlayer ) RecvPropBool( RECVINFO( m_bNightvisionActive ) ), @@ -38,6 +41,7 @@ C_GstringPlayer::C_GstringPlayer() , m_pBobViewModel( NULL ) , m_flBobModelAmount( 0.0f ) , m_angLastBobAngle( vec3_angle ) + , m_pBodyModel( NULL ) { m_bHasUseEntity = false; } @@ -50,6 +54,11 @@ C_GstringPlayer::~C_GstringPlayer() { m_pBobViewModel->Release(); } + + if ( m_pBodyModel != NULL ) + { + m_pBodyModel->Release(); + } } bool C_GstringPlayer::IsNightvisionActive() const @@ -100,6 +109,8 @@ void C_GstringPlayer::ClientThink() ProcessMuzzleFlashEvent(); } + + UpdateBodyModel(); } void C_GstringPlayer::OverrideView( CViewSetup *pSetup ) @@ -342,3 +353,102 @@ float C_GstringPlayer::GetFlashlightDot() const { return m_flFlashlightDot; } + +void C_GstringPlayer::UpdateBodyModel() +{ + if ( m_pBodyModel == NULL ) + { + m_pBodyModel = new C_FirstpersonBody(); + m_pBodyModel->InitializeAsClientEntity( "models/humans/group03/female_01.mdl", RENDER_GROUP_OPAQUE_ENTITY ); + m_pBodyModel->Spawn(); + m_pBodyModel->AddEffects( EF_NOINTERP ); + } + + QAngle angle = GetRenderAngles(); + angle.x = 0; + angle.z = 0; + + Vector fwd, right, up; + AngleVectors( angle, &fwd, &right, &up ); + + const float flSpeed = GetAbsVelocity().Length2D(); + const bool bInAir = ( GetFlags() & FL_ONGROUND ) == 0; + const bool bDuck = m_Local.m_bDucked + || m_Local.m_bDucking; + const bool bMoving = flSpeed > 40.0f; + + static float flBackOffset = 13.0f; + float flBackOffsetDesired = bDuck ? 18.0f : 13.0f; + + if ( flBackOffset != flBackOffsetDesired ) + { + flBackOffset = Approach( flBackOffsetDesired, flBackOffset, gpGlobals->frametime * 25.0f ); + } + + Vector origin = GetRenderOrigin() - fwd * flBackOffset; + if ( !bDuck + || m_Local.m_bInDuckJump && bInAir ) + { + origin.z += GetViewOffset().z - VEC_VIEW.z; + } + + m_pBodyModel->m_EntClientFlags |= ENTCLIENTFLAG_DONTUSEIK; + m_pBodyModel->SetAbsOrigin( origin ); + m_pBodyModel->SetAbsAngles( angle ); + + Activity actDesired = ACT_IDLE; + float flPlaybackrate = 1.0f; + + if ( bInAir ) + { + actDesired = ACT_JUMP; + } + else + { + if ( bMoving ) + { + actDesired = bDuck ? ACT_RUN_CROUCH : ACT_RUN; + } + else + { + actDesired = bDuck ? ACT_COVER_LOW : ACT_IDLE; + } + } + + Vector vecVelocity = GetAbsVelocity(); + vecVelocity.z = 0.0f; + float flLength = vecVelocity.NormalizeInPlace(); + + if ( flLength > 40.0f + && m_pBodyModel->m_iPoseParam_MoveYaw >= 0 ) + { + VectorYawRotate( vecVelocity, -angle.y, vecVelocity ); + + float flYaw = atan2( vecVelocity.y, vecVelocity.x ); + flYaw = AngleNormalizePositive( flYaw ); + + static float flYawLast = 0.0f; + flYawLast = ApproachAngle( flYaw, flYawLast, gpGlobals->frametime * 10.0f ); + + m_pBodyModel->SetPoseParameter( m_pBodyModel->m_iPoseParam_MoveYaw, RAD2DEG( AngleNormalize( flYawLast ) ) ); + } + + if ( m_pBodyModel->GetSequenceActivity( m_pBodyModel->GetSequence() ) + != actDesired ) + { + m_pBodyModel->SetSequence( m_pBodyModel->SelectWeightedSequence( actDesired ) ); + } + + if ( !bInAir && bMoving ) + { + float flGroundSpeed = m_pBodyModel->GetSequenceGroundSpeed( m_pBodyModel->GetSequence() ); + + if ( flGroundSpeed > 0.0f ) + { + flPlaybackrate = flSpeed / flGroundSpeed; + } + } + + m_pBodyModel->SetPlaybackRate( flPlaybackrate ); + m_pBodyModel->StudioFrameAdvance(); +} \ No newline at end of file diff --git a/sp/src/game/client/gstring/c_gstring_player.h b/sp/src/game/client/gstring/c_gstring_player.h index 86b3da5ce..ea9801f78 100644 --- a/sp/src/game/client/gstring/c_gstring_player.h +++ b/sp/src/game/client/gstring/c_gstring_player.h @@ -5,6 +5,7 @@ class C_MuzzleflashEffect; class C_BobModel; +class C_FirstpersonBody; class C_GstringPlayer : public C_BaseHLPlayer { @@ -35,6 +36,8 @@ public: protected: private: + void UpdateBodyModel(); + CNetworkVar( bool, m_bNightvisionActive ); float m_flNightvisionFraction; @@ -53,6 +56,8 @@ private: QAngle m_angLastBobAngle; CNetworkVar( bool, m_bHasUseEntity ); + + C_FirstpersonBody *m_pBodyModel; }; inline C_GstringPlayer *ToGstringPlayer( C_BaseEntity *pPlayer ) diff --git a/sp/src/game/server/gstring/cgstring_player.cpp b/sp/src/game/server/gstring/cgstring_player.cpp index 5648eb651..1eb8645c9 100644 --- a/sp/src/game/server/gstring/cgstring_player.cpp +++ b/sp/src/game/server/gstring/cgstring_player.cpp @@ -30,6 +30,8 @@ void CGstringPlayer::Precache() PrecacheScriptSound( "nightvision.off" ); PrecacheScriptSound( "nightvision.unavailable" ); + PrecacheModel( "models/humans/group03/female_01.mdl" ); + BaseClass::Precache(); } diff --git a/sp/src/lib/public/mathlib.lib b/sp/src/lib/public/mathlib.lib index 1036527e5..36016252c 100644 Binary files a/sp/src/lib/public/mathlib.lib and b/sp/src/lib/public/mathlib.lib differ diff --git a/sp/src/lib/public/raytrace.lib b/sp/src/lib/public/raytrace.lib index 4b6cfbe0e..aaedb9c3b 100644 Binary files a/sp/src/lib/public/raytrace.lib and b/sp/src/lib/public/raytrace.lib differ diff --git a/sp/src/lib/public/tier1.lib b/sp/src/lib/public/tier1.lib index 7292780e7..24740f2e4 100644 Binary files a/sp/src/lib/public/tier1.lib and b/sp/src/lib/public/tier1.lib differ diff --git a/sp/src/lib/public/vgui_controls.lib b/sp/src/lib/public/vgui_controls.lib index e75ec2cbb..6f6ca48d6 100644 Binary files a/sp/src/lib/public/vgui_controls.lib and b/sp/src/lib/public/vgui_controls.lib differ