From f2ee1fed2285ba88ce6c86bce9d26e2107bf92f5 Mon Sep 17 00:00:00 2001 From: Biohazard Date: Sat, 28 Sep 2013 17:34:53 +0200 Subject: [PATCH] - added first-person body model --- sp/src/game/client/c_baseanimating.h | 9 +- sp/src/game/client/client_gstring.vpc | 2 + sp/src/game/client/gstring/c_bobmodel.h | 4 + .../game/client/gstring/c_firstpersonbody.cpp | 183 ++++++++++++++++++ .../game/client/gstring/c_firstpersonbody.h | 34 ++++ .../game/client/gstring/c_gstring_player.cpp | 110 +++++++++++ sp/src/game/client/gstring/c_gstring_player.h | 5 + .../game/server/gstring/cgstring_player.cpp | 2 + sp/src/lib/public/mathlib.lib | Bin 2304726 -> 2304726 bytes sp/src/lib/public/raytrace.lib | Bin 779614 -> 779614 bytes sp/src/lib/public/tier1.lib | Bin 6676792 -> 6676792 bytes sp/src/lib/public/vgui_controls.lib | Bin 49846070 -> 49846070 bytes 12 files changed, 346 insertions(+), 3 deletions(-) create mode 100644 sp/src/game/client/gstring/c_firstpersonbody.cpp create mode 100644 sp/src/game/client/gstring/c_firstpersonbody.h 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 1036527e543f73415402f5043541875e9de03a3f..36016252c19b315b3b853e02cd80c53db157ef28 100644 GIT binary patch delta 755 zcmcb1wTJN<5KoY2Ha9kzEKnl8(eg?$h`m{$#5fNsD7HD~Q6&?QV?6yr2fNthcTWVE zO7}KBZF|bt_LOPcQ|3?!prjF4a(dohW&x&>z1ti9GH-u}5Ds|5BEVU)*Da_ej*%g1 z`u`4g>Gqa4EZbY&urj72l+6of1LJO+efy6Nj+dS=QHkk) zE^+XIqn>3++&!PX=!)AVv^spZan>b2fKuXv7wO#T)i$sF;KmeB_l&ENWFc#RtEoetqg%?7m)h(YXsDp ziubnPSR(+$g4=Jb5#m1qw|ZKsumBgxI*WW(hFNUWI@zV#O-hA;al-0EwvulF|a4ML{}gIj5Eig7D?*(4iItJT v2ols5%!=DBn3V*%;L0R{-ZC?W2et$Q$YZy~85!&$I;Qa|ZJ)-g>>vvOW8?&P delta 757 zcmcb1wTJN<5KoY2HZU@tEKnl8(eg?$h`m{$#5fNsD7HD~Q6&?TfzkBy9qi(h-#rmv zTDPm|Y1>oAwx>+no-&6@02LZeztF)hHa+hzvjEfDUE3S}GH-u}5Ds|5BEY$Jms?Ov z93w;2^#2{~((Ns8ShlykVP#B5D4Q3|2GZTWA((CZhG2GCQ<#(*Nc2Ys`}Q9l94|eA zq6Q%AB&Pql#KFh3W>-7UWsdDUmpRqFz)~C52#8PjmF40GNyW)>ZI6@XzTN>dOKiH| zGH$Rn3Cp;*CoJPpOobUJK7BSfFId+aZr<%{xcPY9;kqQv4J=Fz6+j??%ZD+L0puN# zzYGj7Al_jM=G)E|%)eO#ZkU9zp^*h#gD%4wpav&PMuu9D2K#oc4F2s}83N5Nppad^ zMnE0p#~W(|fLL(*jWt62CtyK7tyEZmYaLLxMLsLTEVgN#>{9I}rNTfg0>q*~EC$5l zKrFG{q*U^~IM@l`P_XTl1pCLYS8}^wuaspE%ynYZ7f4DAaDv>lr-Fe&73_)aS0ts6 z*C3Q^nFdvowvvG%9HQjKG->aR2qiuqG6KM0-X7;6bM+gNu)QouxINBZc6*$?-2Qs7 zl)`>FHIS&se!1-)`{jSzAvDDNlLu?a`zOCW@1H^~3qp#8SrOvUPppg#Mv&09U{>62 n!K@_61y?3%Zfsy?3=eJzaB$xiXJoL4=$OW*H{^yV^iUsDic^0Ay|Fzu78##Fqwxg@u}B$sh}NiLJ@ zERgv0DeTN3iS|A0%s|Wn#H`!*u(SOv0?Y2GVgt)QsA6k>P{j_!96-zo#9Z4SRB_j3 F0RYp;E^`0? delta 125 zcmcb2O8?#|{R#5S21dpkl>*H{^yV^iUsDic^0Ay|AZ;|+Cs&PW-LB@6-1d@O#_c7! zOtP~;^3$iVGpjMJ-_^c{of(K(fS7gr9(J~$MPS(-Rcv6{2UTqC531OKm;;D8ftYLi JgDUR2ECB3~F8BZd diff --git a/sp/src/lib/public/tier1.lib b/sp/src/lib/public/tier1.lib index 7292780e7d17ae15ae83a3da0fa4be329622301e..24740f2e4b1b0141bde414d5cd8c4fab1c0e7967 100644 GIT binary patch delta 1525 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 delta 5705 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