#ifndef C_BASEANIMATING_H #define C_BASEANIMATING_H #ifdef _WIN32 #pragma once #endif #include "c_baseentity.h" #include "studio.h" #include "utlvector.h" #include "ragdoll.h" #include "mouthinfo.h" #include "ai_activity.h" #include "animationlayer.h" #include "sequence_Transitioner.h" #include "bone_accessor.h" #include "bone_merge_cache.h" #include "ragdoll_shared.h" #include "threadtools.h" #include "idatacache.h" #include "baseentity_shared.h" #define LIPSYNC_POSEPARAM_NAME "mouth" #define NUM_HITBOX_FIRES 10 class IRagdoll; class CIKContext; class CIKState; class ConVar; class C_RopeKeyframe; class CBoneBitList; class CBoneList; class KeyValues; class CJiggleBones; class IBoneSetup; FORWARD_DECLARE_HANDLE(memhandle_t); typedef unsigned short MDLHandle_t; extern ConVar vcollide_wireframe; struct ClientModelRenderInfo_t : public ModelRenderInfo_t { matrix3x4_t lightingOffset; matrix3x4_t modelToWorld; }; struct RagdollInfo_t { bool m_bActive; float m_flSaveTime; int m_nNumBones; Vector m_rgBonePos[MAXSTUDIOBONES]; Quaternion m_rgBoneQuaternion[MAXSTUDIOBONES]; }; class CAttachmentData { public: matrix3x4_t m_AttachmentToWorld; QAngle m_angRotation; Vector m_vOriginVelocity; int m_nLastFramecount : 31; int m_bAnglesComputed : 1; }; typedef unsigned int ClientSideAnimationListHandle_t; #define INVALID_CLIENTSIDEANIMATION_LIST_HANDLE (ClientSideAnimationListHandle_t)~0 class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback { public: DECLARE_CLASS(C_BaseAnimating, C_BaseEntity); DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); DECLARE_INTERPOLATION(); enum { NUM_POSEPAREMETERS = 24, NUM_BONECTRLS = 4 }; C_BaseAnimating(); ~C_BaseAnimating(); virtual C_BaseAnimating* GetBaseAnimating() { return this; } bool UsesPowerOfTwoFrameBufferTexture(void); virtual bool Interpolate(float currentTime); virtual void Simulate(); virtual void Release(); float GetAnimTimeInterval(void) const; virtual unsigned char GetClientSideFade(void); virtual void GetBoneControllers(float controllers[MAXSTUDIOBONECTRLS]); virtual float SetBoneController(int iController, float flValue); LocalFlexController_t GetNumFlexControllers(void); const char* GetFlexDescFacs(int iFlexDesc); const char* GetFlexControllerName(LocalFlexController_t iFlexController); const char* GetFlexControllerType(LocalFlexController_t iFlexController); virtual void GetAimEntOrigin(IClientEntity* pAttachedTo, Vector* pAbsOrigin, QAngle* pAbsAngles); bool ComputeHitboxSurroundingBox(Vector* pVecWorldMins, Vector* pVecWorldMaxs); bool ComputeEntitySpaceHitboxSurroundingBox(Vector* pVecWorldMins, Vector* pVecWorldMaxs); bool HitboxToWorldTransforms(matrix3x4_t* pHitboxToWorld[MAXSTUDIOBONES]); float ClampCycle(float cycle, bool isLooping); virtual void GetPoseParameters(CStudioHdr* pStudioHdr, float poseParameter[MAXSTUDIOPOSEPARAM]); virtual void BuildTransformations(CStudioHdr* pStudioHdr, Vector* pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList& boneComputed); virtual void ApplyBoneMatrixTransform(matrix3x4_t& transform); virtual int VPhysicsGetObjectList(IPhysicsObject** pList, int listMax); virtual bool SetupBones(matrix3x4_t* pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime); virtual void UpdateIKLocks(float currentTime); virtual void CalculateIKLocks(float currentTime); virtual bool ShouldDraw(); virtual int DrawModel(int flags); virtual int InternalDrawModel(int flags); virtual bool OnInternalDrawModel(ClientModelRenderInfo_t* pInfo); virtual bool OnPostInternalDrawModel(ClientModelRenderInfo_t* pInfo); void DoInternalDrawModel(ClientModelRenderInfo_t* pInfo, DrawModelState_t* pState, matrix3x4_t* pBoneToWorldArray = NULL); virtual CMouthInfo* GetMouth(); virtual void ControlMouth(CStudioHdr* pStudioHdr); virtual void DoAnimationEvents(CStudioHdr* pStudio); virtual void FireEvent(const Vector& origin, const QAngle& angles, int event, const char* options); virtual void FireObsoleteEvent(const Vector& origin, const QAngle& angles, int event, const char* options); virtual const char* ModifyEventParticles(const char* token) { return token; } virtual bool DispatchMuzzleEffect(const char* options, bool isFirstPerson); virtual void ValidateModelIndex(void); virtual CStudioHdr* OnNewModel(void); CStudioHdr* GetModelPtr() const; void InvalidateMdlCache(); virtual void SetPredictable(bool state); void UseClientSideAnimation(); virtual void StandardBlendingRules(CStudioHdr* pStudioHdr, Vector pos[], Quaternion q[], float currentTime, int boneMask); void UnragdollBlend(CStudioHdr* hdr, Vector pos[], Quaternion q[], float currentTime); void MaintainSequenceTransitions(IBoneSetup& boneSetup, float flCycle, Vector pos[], Quaternion q[]); virtual void AccumulateLayers(IBoneSetup& boneSetup, Vector pos[], Quaternion q[], float currentTime); virtual void ChildLayerBlend(Vector pos[], Quaternion q[], float currentTime, int boneMask); int LookupAttachment(const char* pAttachmentName); int LookupRandomAttachment(const char* pAttachmentNameSubstring); int LookupPoseParameter(CStudioHdr* pStudioHdr, const char* szName); inline int LookupPoseParameter(const char* szName) { return LookupPoseParameter(GetModelPtr(), szName); } float SetPoseParameter(CStudioHdr* pStudioHdr, const char* szName, float flValue); inline float SetPoseParameter(const char* szName, float flValue) { return SetPoseParameter(GetModelPtr(), szName, flValue); } float SetPoseParameter(CStudioHdr* pStudioHdr, int iParameter, float flValue); inline float SetPoseParameter(int iParameter, float flValue) { return SetPoseParameter(GetModelPtr(), iParameter, flValue); } float GetPoseParameter(int iPoseParameter); bool GetPoseParameterRange(int iPoseParameter, float& minValue, float& maxValue); int LookupBone(const char* szName); void GetBonePosition(int iBone, Vector& origin, QAngle& angles); void GetBoneTransform(int iBone, matrix3x4_t& pBoneToWorld); int GetHitboxBone(int hitboxIndex); virtual void AttachEntityToBone(C_BaseAnimating* attachTarget, int boneIndexAttached = -1, Vector bonePosition = Vector(0, 0, 0), QAngle boneAngles = QAngle(0, 0, 0)); void AddBoneAttachment(C_BaseAnimating* newBoneAttachment); void RemoveBoneAttachment(C_BaseAnimating* boneAttachment); void RemoveBoneAttachments(); void DestroyBoneAttachments(); void MoveBoneAttachments(C_BaseAnimating* attachTarget); int GetNumBoneAttachments(); C_BaseAnimating* GetBoneAttachment(int i); virtual void NotifyBoneAttached(C_BaseAnimating* attachTarget); virtual void UpdateBoneAttachments(void); virtual void PreDataUpdate(DataUpdateType_t updateType); virtual void PostDataUpdate(DataUpdateType_t updateType); virtual int RestoreData(const char* context, int slot, int type); virtual void NotifyShouldTransmit(ShouldTransmitState_t state); virtual void OnPreDataChanged(DataUpdateType_t updateType); virtual void OnDataChanged(DataUpdateType_t updateType); virtual void AddEntity(void); void ForceClientSideAnimationOn(); void AddToClientSideAnimationList(); void RemoveFromClientSideAnimationList(); virtual bool IsSelfAnimating(); virtual void ResetLatched(); virtual void GetRenderBounds(Vector& theMins, Vector& theMaxs); virtual const Vector& GetRenderOrigin(void); virtual const QAngle& GetRenderAngles(void); virtual bool GetSoundSpatialization(SpatializationInfo_t& info); bool GetAttachment(const char* szName, Vector& absOrigin); bool GetAttachment(const char* szName, Vector& absOrigin, QAngle& absAngles); virtual bool GetAttachment(int number, Vector& origin); virtual bool GetAttachment(int number, Vector& origin, QAngle& angles); virtual bool GetAttachment(int number, matrix3x4_t& matrix); virtual bool GetAttachmentVelocity(int number, Vector& originVel, Quaternion& angleVel); bool GetAttachmentLocal(int iAttachment, matrix3x4_t& attachmentToLocal); bool GetAttachmentLocal(int iAttachment, Vector& origin, QAngle& angles); bool GetAttachmentLocal(int iAttachment, Vector& origin); bool GetRootBone(matrix3x4_t& rootBone); virtual ShadowType_t ShadowCastType(); virtual CollideType_t GetCollideType(void); virtual bool TestCollision(const Ray_t& ray, unsigned int fContentsMask, trace_t& tr); virtual bool TestHitboxes(const Ray_t& ray, unsigned int fContentsMask, trace_t& tr); bool IsRagdoll() const; bool IsAboutToRagdoll() const; virtual C_BaseAnimating* BecomeRagdollOnClient(); C_BaseAnimating* CreateRagdollCopy(); bool InitAsClientRagdoll(const matrix3x4_t* pDeltaBones0, const matrix3x4_t* pDeltaBones1, const matrix3x4_t* pCurrentBonePosition, float boneDt, bool bFixedConstraints = false); void IgniteRagdoll(C_BaseAnimating* pSource); void TransferDissolveFrom(C_BaseAnimating* pSource); virtual void SaveRagdollInfo(int numbones, const matrix3x4_t& cameraTransform, CBoneAccessor& pBoneToWorld); virtual bool RetrieveRagdollInfo(Vector* pos, Quaternion* q); virtual void Clear(void); void ClearRagdoll(); void CreateUnragdollInfo(C_BaseAnimating* pRagdoll); void ForceSetupBonesAtTime(matrix3x4_t* pBonesOut, float flTime); virtual void GetRagdollInitBoneArrays(matrix3x4_t* pDeltaBones0, matrix3x4_t* pDeltaBones1, matrix3x4_t* pCurrentBones, float boneDt); virtual int GetBody() { return m_nBody; } virtual int GetSkin() { return m_nSkin; } bool IsOnFire() { return ((GetFlags() & FL_ONFIRE) != 0); } inline float GetPlaybackRate(); inline void SetPlaybackRate(float rate); void SetModelScale(float scale, float change_duration = 0.0f); float GetModelScale() const { return m_flModelScale; } inline bool IsModelScaleFractional() const; inline bool IsModelScaled() const; void UpdateModelScale(void); virtual void RefreshCollisionBounds(void); int GetSequence(); virtual void SetSequence(int nSequence); inline void ResetSequence(int nSequence); float GetSequenceGroundSpeed(CStudioHdr* pStudioHdr, int iSequence); inline float GetSequenceGroundSpeed(int iSequence) { return GetSequenceGroundSpeed(GetModelPtr(), iSequence); } bool IsSequenceLooping(CStudioHdr* pStudioHdr, int iSequence); inline bool IsSequenceLooping(int iSequence) { return IsSequenceLooping(GetModelPtr(), iSequence); } float GetSequenceMoveDist(CStudioHdr* pStudioHdr, int iSequence); void GetSequenceLinearMotion(int iSequence, Vector* pVec); void GetBlendedLinearVelocity(Vector* pVec); int LookupSequence(const char* label); int LookupActivity(const char* label); char const* GetSequenceName(int iSequence); char const* GetSequenceActivityName(int iSequence); Activity GetSequenceActivity(int iSequence); KeyValues* GetSequenceKeyValues(int iSequence); virtual void StudioFrameAdvance(); virtual float FrameAdvance(float flInterval = 0.0f); virtual float GetSequenceCycleRate(CStudioHdr* pStudioHdr, int iSequence); virtual void UpdateClientSideAnimation(); void ClientSideAnimationChanged(); virtual unsigned int ComputeClientSideAnimationFlags(); virtual void ResetClientsideFrame(void) { SetCycle(0); } void SetCycle(float flCycle); float GetCycle() const; void SetBodygroup(int iGroup, int iValue); int GetBodygroup(int iGroup); const char* GetBodygroupName(int iGroup); int FindBodygroupByName(const char* name); int GetBodygroupCount(int iGroup); int GetNumBodyGroups(void); class CBoneCache* GetBoneCache(CStudioHdr* pStudioHdr); void SetHitboxSet(int setnum); void SetHitboxSetByName(const char* setname); int GetHitboxSet(void); char const* GetHitboxSetName(void); int GetHitboxSetCount(void); void DrawClientHitboxes(float duration = 0.0f, bool monocolor = false); C_BaseAnimating* FindFollowedEntity(); virtual bool IsActivityFinished(void) { return m_bSequenceFinished; } inline bool IsSequenceFinished(void); inline bool SequenceLoops(void) { return m_bSequenceLoops; } virtual void UncorrectViewModelAttachment(Vector& vOrigin) {} void InvalidateBoneCache(); bool IsBoneCacheValid() const; void GetCachedBoneMatrix(int boneIndex, matrix3x4_t& out); const matrix3x4_t& GetBone(int iBone) const; matrix3x4_t& GetBoneForWrite(int iBone); struct AutoAllowBoneAccess { AutoAllowBoneAccess(bool bAllowForNormalModels, bool bAllowForViewModels); ~AutoAllowBoneAccess(void); }; static void PushAllowBoneAccess(bool bAllowForNormalModels, bool bAllowForViewModels, char const* tagPush); static void PopBoneAccess(char const* tagPop); static void ThreadedBoneSetup(); static void InitBoneSetupThreadPool(); static void ShutdownBoneSetupThreadPool(); static void InvalidateBoneCaches(); virtual void VPhysicsUpdate(IPhysicsObject* pPhysics); void DisableMuzzleFlash(); virtual void DoMuzzleFlash(); bool ShouldMuzzleFlash() const; virtual void ProcessMuzzleFlashEvent(); static void UpdateClientSideAnimations(); void InitModelEffects(void); virtual void SetServerIntendedCycle(float intended) { (void)intended; } virtual float GetServerIntendedCycle(void) { return -1.0f; } int SelectWeightedSequence(int activity); void ResetSequenceInfo(void); float SequenceDuration(void); float SequenceDuration(CStudioHdr* pStudioHdr, int iSequence); inline float SequenceDuration(int iSequence) { return SequenceDuration(GetModelPtr(), iSequence); } int FindTransitionSequence(int iCurrentSequence, int iGoalSequence, int* piDir); void RagdollMoved(void); virtual void GetToolRecordingState(KeyValues* msg); virtual void CleanupToolRecordingState(KeyValues* msg); void SetReceivedSequence(void); virtual bool ShouldResetSequenceOnNewModel(void); virtual bool IsViewModel() const; protected: virtual void FormatViewModelAttachment(int nAttachment, matrix3x4_t& attachmentToWorld) {} bool IsBoneAccessAllowed() const; CMouthInfo& MouthInfo(); virtual bool IsMenuModel() const; virtual int GetStudioBody(void) { return m_nBody; } virtual bool CalcAttachments(); private: virtual float LastBoneChangedTime() { return FLT_MAX; } CBoneList* RecordBones(CStudioHdr* hdr, matrix3x4_t* pBoneState); bool PutAttachment(int number, const matrix3x4_t& attachmentToWorld); void TermRopes(); void DelayedInitModelEffects(void); void UpdateRelevantInterpolatedVars(); void AddBaseAnimatingInterpolatedVars(); void RemoveBaseAnimatingInterpolatedVars(); public: CRagdoll* m_pRagdoll; int m_nSkin; int m_nBody; int m_nHitboxSet; CSequenceTransitioner m_SequenceTransitioner; protected: CIKContext* m_pIk; int m_iEyeAttachment; float m_flPlaybackRate; bool m_bStoreRagdollInfo; RagdollInfo_t* m_pRagdollInfo; Vector m_vecForce; int m_nForceBone; unsigned long m_iMostRecentModelBoneCounter; unsigned long m_iMostRecentBoneSetupRequest; int m_iPrevBoneMask; int m_iAccumulatedBoneMask; CBoneAccessor m_BoneAccessor; CThreadFastMutex m_BoneSetupLock; ClientSideAnimationListHandle_t m_ClientSideAnimationListHandle; bool m_bClientSideFrameReset; CUtlVector > m_BoneAttachments; int m_boneIndexAttached; Vector m_bonePosition; QAngle m_boneAngles; CHandle m_pAttachedTo; protected: float m_fadeMinDist; float m_fadeMaxDist; float m_flFadeScale; private: float m_flGroundSpeed; float m_flLastEventCheck; bool m_bSequenceFinished; bool m_bSequenceLoops; CMouthInfo m_mouth; CNetworkVar(float, m_flModelScale); float m_flPoseParameter[MAXSTUDIOPOSEPARAM]; CInterpolatedVarArray< float, MAXSTUDIOPOSEPARAM > m_iv_flPoseParameter; float m_flOldPoseParameters[MAXSTUDIOPOSEPARAM]; int m_nPrevSequence; int m_nRestoreSequence; CUtlLinkedList m_Ropes; float m_flPrevEventCycle; int m_nEventSequence; float m_flEncodedController[MAXSTUDIOBONECTRLS]; CInterpolatedVarArray< float, MAXSTUDIOBONECTRLS > m_iv_flEncodedController; float m_flOldEncodedController[MAXSTUDIOBONECTRLS]; bool m_bClientSideAnimation; bool m_bLastClientSideFrameReset; int m_nNewSequenceParity; int m_nResetEventsParity; int m_nPrevNewSequenceParity; int m_nPrevResetEventsParity; bool m_builtRagdoll; Vector m_vecPreRagdollMins; Vector m_vecPreRagdollMaxs; int m_nSequence; bool m_bReceivedSequence; protected: float m_flCycle; CInterpolatedVar< float > m_iv_flCycle; float m_flOldCycle; bool m_bNoModelParticles; private: float m_flOldModelScale; int m_nOldSequence; CBoneMergeCache* m_pBoneMergeCache; CUtlVector< matrix3x4_t > m_CachedBoneData; memhandle_t m_hitboxBoneCacheHandle; float m_flLastBoneSetupTime; CJiggleBones* m_pJiggleBones; CUtlVector m_Attachments; void SetupBones_AttachmentHelper(CStudioHdr* pStudioHdr); EHANDLE m_hLightingOrigin; EHANDLE m_hLightingOriginRelative; CNetworkVar(unsigned char, m_nMuzzleFlashParity); unsigned char m_nOldMuzzleFlashParity; bool m_bInitModelEffects; bool m_bDynamicModelAllowed; bool m_bDynamicModelPending; bool m_bResetSequenceInfoOnLoad; CRefCountedModelIndex m_AutoRefModelIndex; public: void EnableDynamicModels() { m_bDynamicModelAllowed = true; } bool IsDynamicModelLoading() const { return m_bDynamicModelPending; } private: virtual void OnModelLoadComplete(const model_t* pModel); private: void LockStudioHdr(); void UnlockStudioHdr(); mutable CStudioHdr* m_pStudioHdr; mutable MDLHandle_t m_hStudioHdr; CThreadFastMutex m_StudioHdrInitLock; }; enum { RAGDOLL_FRICTION_OFF = -2, RAGDOLL_FRICTION_NONE, RAGDOLL_FRICTION_IN, RAGDOLL_FRICTION_HOLD, RAGDOLL_FRICTION_OUT, }; class C_ClientRagdoll : public C_BaseAnimating, public IPVSNotify { public: C_ClientRagdoll(bool bRestoring = true); DECLARE_CLASS(C_ClientRagdoll, C_BaseAnimating); DECLARE_DATADESC(); virtual void OnPVSStatusChanged(bool bInPVS); virtual void Release(void); virtual void SetupWeights(const matrix3x4_t* pBoneToWorld, int nFlexWeightCount, float* pFlexWeights, float* pFlexDelayedWeights); virtual void ImpactTrace(trace_t* pTrace, int iDamageType, const char* pCustomImpactName); void ClientThink(void); void ReleaseRagdoll(void) { m_bReleaseRagdoll = true; } bool ShouldSavePhysics(void) { return true; } virtual void OnSave(); virtual void OnRestore(); virtual int ObjectCaps(void) { return BaseClass::ObjectCaps() | FCAP_SAVE_NON_NETWORKABLE; } virtual IPVSNotify* GetPVSNotifyInterface() { return this; } void HandleAnimatedFriction(void); virtual void SUB_Remove(void); void FadeOut(void); virtual float LastBoneChangedTime(); bool m_bFadeOut; bool m_bImportant; float m_flEffectTime; private: int m_iCurrentFriction; int m_iMinFriction; int m_iMaxFriction; float m_flFrictionModTime; float m_flFrictionTime; int m_iFrictionAnimState; bool m_bReleaseRagdoll; bool m_bFadingOut; float m_flScaleEnd[NUM_HITBOX_FIRES]; float m_flScaleTimeStart[NUM_HITBOX_FIRES]; float m_flScaleTimeEnd[NUM_HITBOX_FIRES]; }; inline void C_BaseAnimating::ResetSequence(int nSequence) { SetSequence(nSequence); ResetSequenceInfo(); } inline float C_BaseAnimating::GetPlaybackRate() { return m_flPlaybackRate; } inline void C_BaseAnimating::SetPlaybackRate(float rate) { m_flPlaybackRate = rate; } inline const matrix3x4_t& C_BaseAnimating::GetBone(int iBone) const { return m_BoneAccessor.GetBone(iBone); } inline matrix3x4_t& C_BaseAnimating::GetBoneForWrite(int iBone) { return m_BoneAccessor.GetBoneForWrite(iBone); } inline bool C_BaseAnimating::ShouldMuzzleFlash() const { return m_nOldMuzzleFlashParity != m_nMuzzleFlashParity; } inline float C_BaseAnimating::GetCycle() const { return m_flCycle; } inline CStudioHdr* C_BaseAnimating::GetModelPtr() const { if (IsDynamicModelLoading()) return NULL; #ifdef _DEBUG #endif if (!m_pStudioHdr) { const_cast(this)->LockStudioHdr(); } Assert(m_pStudioHdr ? m_pStudioHdr->GetRenderHdr() == mdlcache->GetStudioHdr(m_hStudioHdr) : m_hStudioHdr == MDLHANDLE_INVALID); return m_pStudioHdr; } inline void C_BaseAnimating::InvalidateMdlCache() { if (m_pStudioHdr) { UnlockStudioHdr(); delete m_pStudioHdr; m_pStudioHdr = NULL; } } inline bool C_BaseAnimating::IsModelScaleFractional() const { COMPILE_TIME_ASSERT(sizeof(m_flModelScale) == sizeof(int)); return *((const int*)&m_flModelScale) < 0x3f800000; } inline bool C_BaseAnimating::IsModelScaled() const { return (m_flModelScale > 1.0f + FLT_EPSILON || m_flModelScale < 1.0f - FLT_EPSILON); } inline int C_BaseAnimating::GetSequence() { return m_nSequence; } inline bool C_BaseAnimating::IsSequenceFinished(void) { return m_bSequenceFinished; } inline float C_BaseAnimating::SequenceDuration(void) { return SequenceDuration(GetSequence()); } inline CMouthInfo& C_BaseAnimating::MouthInfo() { return m_mouth; } void GetColumn(matrix3x4_t& src, int column, Vector& dest); void SetColumn(Vector& src, int column, matrix3x4_t& dest); EXTERN_RECV_TABLE(DT_BaseAnimating); extern void DevMsgRT(PRINTF_FORMAT_STRING char const* pMsg, ...); #endif