This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
2020-08-04 13:13:01 -04:00

588 lines
22 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef BASEANIMATING_H
#define BASEANIMATING_H
#ifdef _WIN32
#pragma once
#endif
#include "baseentity.h"
#include "datacache/idatacache.h"
#include "entityoutput.h"
#include "studio.h"
#include "tier0/threadtools.h"
struct animevent_t;
struct matrix3x4_t;
class CIKContext;
class KeyValues;
FORWARD_DECLARE_HANDLE(memhandle_t);
#define BCF_NO_ANIMATION_SKIP \
(1 << 0) // Do not allow PVS animation skipping (mostly for attachments
// being critical to an entity)
#define BCF_IS_IN_SPAWN \
(1 << 1) // Is currently inside of spawn, always evaluate animations
class CBaseAnimating : public CBaseEntity {
public:
DECLARE_CLASS(CBaseAnimating, CBaseEntity);
CBaseAnimating();
~CBaseAnimating();
DECLARE_PREDICTABLE();
enum { NUM_POSEPAREMETERS = 24, NUM_BONECTRLS = 4 };
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
virtual void SetModel(const char *szModelName);
virtual void Activate();
virtual void Spawn();
virtual void Precache();
virtual void SetTransmit(CCheckTransmitInfo *pInfo, bool bAlways);
virtual int Restore(IRestore &restore);
virtual void OnRestore();
CStudioHdr *GetModelPtr(void);
void InvalidateMdlCache();
virtual CStudioHdr *OnNewModel();
virtual CBaseAnimating *GetBaseAnimating() { return this; }
// Cycle access
void SetCycle(float flCycle);
float GetCycle() const;
float GetAnimTimeInterval(void) const;
// Call this in your constructor to tell it that you will not use animtime.
// Then the interpolation will be done correctly on the client. This
// defaults to off.
void UseClientSideAnimation();
// Tells whether or not we're using client-side animation. Used for
// controlling the transmission of animtime.
bool IsUsingClientSideAnimation() { return m_bClientSideAnimation; }
// Basic NPC Animation functions
virtual float GetIdealSpeed() const;
virtual float GetIdealAccel() const;
virtual void
StudioFrameAdvance(); // advance animation frame to some time in the future
void StudioFrameAdvanceManual(float flInterval);
bool IsValidSequence(int iSequence);
inline float GetPlaybackRate();
inline void SetPlaybackRate(float rate);
inline int GetSequence() { return m_nSequence; }
virtual void SetSequence(int nSequence);
/* inline */ void ResetSequence(int nSequence);
// FIXME: push transitions support down into CBaseAnimating?
virtual bool IsActivityFinished(void) { return m_bSequenceFinished; }
inline bool IsSequenceFinished(void) { return m_bSequenceFinished; }
inline bool SequenceLoops(void) { return m_bSequenceLoops; }
bool IsSequenceLooping(CStudioHdr *pStudioHdr, int iSequence);
inline bool IsSequenceLooping(int iSequence) {
return IsSequenceLooping(GetModelPtr(), iSequence);
}
inline float SequenceDuration(void) {
return SequenceDuration(m_nSequence);
}
float SequenceDuration(CStudioHdr *pStudioHdr, int iSequence);
inline float SequenceDuration(int iSequence) {
return SequenceDuration(GetModelPtr(), iSequence);
}
float GetSequenceCycleRate(CStudioHdr *pStudioHdr, int iSequence);
inline float GetSequenceCycleRate(int iSequence) {
return GetSequenceCycleRate(GetModelPtr(), iSequence);
}
float GetLastVisibleCycle(CStudioHdr *pStudioHdr, int iSequence);
virtual float GetSequenceGroundSpeed(CStudioHdr *pStudioHdr, int iSequence);
inline float GetSequenceGroundSpeed(int iSequence) {
return GetSequenceGroundSpeed(GetModelPtr(), iSequence);
}
void ResetActivityIndexes(void);
void ResetEventIndexes(void);
int SelectWeightedSequence(Activity activity);
int SelectWeightedSequence(Activity activity, int curSequence);
int SelectWeightedSequenceFromModifiers(Activity activity,
CUtlSymbol *pActivityModifiers,
int iModifierCount);
int SelectHeaviestSequence(Activity activity);
int LookupActivity(const char *label);
int LookupSequence(const char *label);
KeyValues *GetSequenceKeyValues(int iSequence);
float GetSequenceMoveYaw(int iSequence);
float GetSequenceMoveDist(CStudioHdr *pStudioHdr, int iSequence);
inline float GetSequenceMoveDist(int iSequence) {
return GetSequenceMoveDist(GetModelPtr(), iSequence);
}
void GetSequenceLinearMotion(int iSequence, Vector *pVec);
const char *GetSequenceName(int iSequence);
const char *GetSequenceActivityName(int iSequence);
Activity GetSequenceActivity(int iSequence);
void ResetSequenceInfo();
// This will stop animation until you call ResetSequenceInfo() at some point
// in the future
inline void StopAnimation(void) { m_flPlaybackRate = 0; }
virtual void ClampRagdollForce(const Vector &vecForceIn,
Vector *vecForceOut) {
*vecForceOut = vecForceIn;
} // Base class does nothing.
virtual bool BecomeRagdollOnClient(const Vector &force);
virtual bool IsRagdoll();
virtual bool CanBecomeRagdoll(
void); // Check if this entity will ragdoll when dead.
virtual void GetSkeleton(CStudioHdr *pStudioHdr, Vector pos[],
Quaternion q[], int boneMask);
virtual void GetBoneTransform(int iBone, matrix3x4_t &pBoneToWorld);
virtual void SetupBones(matrix3x4_t *pBoneToWorld, int boneMask);
virtual void CalculateIKLocks(float currentTime);
virtual void Teleport(const Vector *newPosition, const QAngle *newAngles,
const Vector *newVelocity);
bool HasAnimEvent(int nSequence, int nEvent);
virtual void DispatchAnimEvents(
CBaseAnimating
*eventHandler); // Handle events that have happend since last time
// called up until X seconds into the future
virtual void HandleAnimEvent(animevent_t *pEvent);
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(const char *szName);
float GetPoseParameter(int iParameter);
bool GetPoseParameterRange(int index, float &minValue, float &maxValue);
bool HasPoseParameter(int iSequence, const char *szName);
bool HasPoseParameter(int iSequence, int iParameter);
float EdgeLimitPoseParameter(int iParameter, float flValue,
float flBase = 0.0f);
protected:
// The modus operandi for pose parameters is that you should not use the
// const char * version of the functions in general code -- it causes many
// many string comparisons, which is slower than you think. Better is to
// save off your pose parameters in member variables in your derivation of
// this function:
virtual void PopulatePoseParameters(void);
public:
int LookupBone(const char *szName);
void GetBonePosition(const char *szName, Vector &origin, QAngle &angles);
void GetBonePosition(int iBone, Vector &origin, QAngle &angles);
int GetPhysicsBone(int boneIndex);
int GetNumBones(void);
int FindTransitionSequence(int iCurrentSequence, int iGoalSequence,
int *piDir);
bool GotoSequence(int iCurrentSequence, float flCurrentCycle,
float flCurrentRate, int iGoalSequence,
int &iNextSequence, float &flCycle, int &iDir);
int GetEntryNode(int iSequence);
int GetExitNode(int iSequence);
void GetEyeballs(Vector &origin, QAngle &angles); // ?? remove ??
int LookupAttachment(const char *szName);
// These return the attachment in world space
bool GetAttachment(const char *szName, Vector &absOrigin,
QAngle &absAngles);
bool GetAttachment(int iAttachment, Vector &absOrigin, QAngle &absAngles);
int GetAttachmentBone(int iAttachment);
virtual bool GetAttachment(int iAttachment, matrix3x4_t &attachmentToWorld);
// These return the attachment in the space of the entity
bool GetAttachmentLocal(const char *szName, Vector &origin, QAngle &angles);
bool GetAttachmentLocal(int iAttachment, Vector &origin, QAngle &angles);
bool GetAttachmentLocal(int iAttachment, matrix3x4_t &attachmentToLocal);
// Non-angle versions of the attachments in world space
bool GetAttachment(const char *szName, Vector &absOrigin,
Vector *forward = NULL, Vector *right = NULL,
Vector *up = NULL);
bool GetAttachment(int iAttachment, Vector &absOrigin,
Vector *forward = NULL, Vector *right = NULL,
Vector *up = NULL);
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);
void SetHitboxSet(int setnum);
void SetHitboxSetByName(const char *setname);
int GetHitboxSet(void);
char const *GetHitboxSetName(void);
int GetHitboxSetCount(void);
int GetHitboxBone(int hitboxIndex);
bool LookupHitbox(const char *szName, int &outSet, int &outBox);
// Computes a box that surrounds all hitboxes
bool ComputeHitboxSurroundingBox(Vector *pVecWorldMins,
Vector *pVecWorldMaxs);
bool ComputeEntitySpaceHitboxSurroundingBox(Vector *pVecWorldMins,
Vector *pVecWorldMaxs);
// Clone a CBaseAnimating from another (copies model & sequence data)
void CopyAnimationDataFrom(CBaseAnimating *pSource);
int ExtractBbox(int sequence, Vector &mins, Vector &maxs);
void SetSequenceBox(void);
int RegisterPrivateActivity(const char *pszActivityName);
void ResetClientsideFrame(void);
// Controllers.
virtual void InitBoneControllers(void);
// Return's the controller's angle/position in bone space.
float GetBoneController(int iController);
// Maps the angle/position value you specify into the bone's start/end and
// sets the specified controller to the value.
float SetBoneController(int iController, float flValue);
void GetVelocity(Vector *vVelocity, AngularImpulse *vAngVelocity);
// these two need to move somewhere else
LocalFlexController_t GetNumFlexControllers(void);
const char *GetFlexDescFacs(int iFlexDesc);
const char *GetFlexControllerName(LocalFlexController_t iFlexController);
const char *GetFlexControllerType(LocalFlexController_t iFlexController);
virtual Vector GetGroundSpeedVelocity(void);
bool GetIntervalMovement(float flIntervalUsed, bool &bMoveSeqFinished,
Vector &newPosition, QAngle &newAngles);
bool GetSequenceMovement(int nSequence, float fromCycle, float toCycle,
Vector &deltaPosition, QAngle &deltaAngles);
float GetInstantaneousVelocity(float flInterval = 0.0);
float GetEntryVelocity(int iSequence);
float GetExitVelocity(int iSequence);
float GetMovementFrame(float flDist);
bool HasMovement(int iSequence);
void ReportMissingActivity(int iActivity);
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);
class CBoneCache *GetBoneCache(void);
void InvalidateBoneCache();
void InvalidateBoneCacheIfOlderThan(float deltaTime);
virtual int DrawDebugTextOverlays(void);
// See note in code re: bandwidth usage!!!
void DrawServerHitboxes(float duration = 0.0f, bool monocolor = false);
void DrawRawSkeleton(matrix3x4_t boneToWorld[], int boneMask,
bool noDepthTest = true, float duration = 0.0f,
bool monocolor = false);
void SetModelScale(float scale, float change_duration = 0.0f);
float GetModelScale() const { return m_flModelScale; }
void UpdateModelScale();
virtual void RefreshCollisionBounds(void);
// also calculate IK on server? (always done on client)
void EnableServerIK();
void DisableServerIK();
// for ragdoll vs. car
int GetHitboxesFrontside(int *boxList, int boxMax, const Vector &normal,
float dist);
void GetInputDispatchEffectPosition(const char *sInputString,
Vector &pOrigin, QAngle &pAngles);
virtual void ModifyOrAppendCriteria(AI_CriteriaSet &set);
// Send a muzzle flash event to the client for this entity.
void DoMuzzleFlash();
// Fire
virtual void Ignite(float flFlameLifetime, bool bNPCOnly = true,
float flSize = 0.0f,
bool bCalledByLevelDesigner = false);
virtual void IgniteLifetime(float flFlameLifetime);
virtual void IgniteNumHitboxFires(int iNumHitBoxFires);
virtual void IgniteHitboxFireScale(float flHitboxFireScale);
virtual void Extinguish() { RemoveFlag(FL_ONFIRE); }
bool IsOnFire() { return ((GetFlags() & FL_ONFIRE) != 0); }
void Scorch(int rate, int floor);
void InputIgnite(inputdata_t &inputdata);
void InputIgniteLifetime(inputdata_t &inputdata);
void InputIgniteNumHitboxFires(inputdata_t &inputdata);
void InputIgniteHitboxFireScale(inputdata_t &inputdata);
void InputBecomeRagdoll(inputdata_t &inputdata);
// Dissolve, returns true if the ragdoll has been created
bool Dissolve(const char *pMaterialName, float flStartTime,
bool bNPCOnly = true, int nDissolveType = 0,
Vector vDissolverOrigin = vec3_origin, int iMagnitude = 0);
bool IsDissolving() { return ((GetFlags() & FL_DISSOLVING) != 0); }
void TransferDissolveFrom(CBaseAnimating *pAnim);
// animation needs
float
m_flGroundSpeed; // computed linear movement rate for current sequence
float m_flLastEventCheck; // cycle index of when events were last checked
virtual void SetLightingOriginRelative(
CBaseEntity *pLightingOriginRelative);
void SetLightingOriginRelative(string_t strLightingOriginRelative);
CBaseEntity *GetLightingOriginRelative();
virtual void SetLightingOrigin(CBaseEntity *pLightingOrigin);
void SetLightingOrigin(string_t strLightingOrigin);
CBaseEntity *GetLightingOrigin();
const float *GetPoseParameterArray() { return m_flPoseParameter.Base(); }
const float *GetEncodedControllerArray() {
return m_flEncodedController.Base();
}
void BuildMatricesWithBoneMerge(const CStudioHdr *pStudioHdr,
const QAngle &angles, const Vector &origin,
const Vector pos[MAXSTUDIOBONES],
const Quaternion q[MAXSTUDIOBONES],
matrix3x4_t bonetoworld[MAXSTUDIOBONES],
CBaseAnimating *pParent,
CBoneCache *pParentCache);
void SetFadeDistance(float minFadeDist, float maxFadeDist);
int GetBoneCacheFlags(void) { return m_fBoneCacheFlags; }
inline void SetBoneCacheFlags(unsigned short fFlag) {
m_fBoneCacheFlags |= fFlag;
}
inline void ClearBoneCacheFlags(unsigned short fFlag) {
m_fBoneCacheFlags &= ~fFlag;
}
bool PrefetchSequence(int iSequence);
private:
void LockStudioHdr();
void UnlockStudioHdr();
void StudioFrameAdvanceInternal(CStudioHdr *pStudioHdr, float flInterval);
void InputSetLightingOriginRelative(inputdata_t &inputdata);
void InputSetLightingOrigin(inputdata_t &inputdata);
void InputSetModelScale(inputdata_t &inputdata);
bool CanSkipAnimation(void);
public:
CNetworkVar(int, m_nForceBone);
CNetworkVector(m_vecForce);
CNetworkVar(int, m_nSkin);
CNetworkVar(int, m_nBody);
CNetworkVar(int, m_nHitboxSet);
// For making things thin during barnacle swallowing, e.g.
CNetworkVar(float, m_flModelScale);
// was pev->framerate
CNetworkVar(float, m_flPlaybackRate);
public:
void InitStepHeightAdjust(void);
void SetIKGroundContactInfo(float minHeight, float maxHeight);
void UpdateStepOrigin(void);
protected:
float m_flIKGroundContactTime;
float m_flIKGroundMinHeight;
float m_flIKGroundMaxHeight;
float m_flEstIkFloor; // debounced
float m_flEstIkOffset;
CIKContext *m_pIk;
int m_iIKCounter;
public:
Vector GetStepOrigin(void) const;
QAngle GetStepAngles(void) const;
private:
bool m_bSequenceFinished; // flag set when StudioAdvanceFrame moves across
// a frame boundry
bool m_bSequenceLoops; // true if the sequence loops
bool m_bResetSequenceInfoOnLoad; // true if a ResetSequenceInfo was queued
// up during dynamic load
float m_flDissolveStartTime;
// was pev->frame
CNetworkVar(float, m_flCycle);
CNetworkVar(int, m_nSequence);
CNetworkArray(float, m_flPoseParameter,
NUM_POSEPAREMETERS); // must be private so manual mode works!
CNetworkArray(float, m_flEncodedController,
NUM_BONECTRLS); // bone controller setting (0..1)
// Client-side animation (useful for looping animation objects)
CNetworkVar(bool, m_bClientSideAnimation);
CNetworkVar(bool, m_bClientSideFrameReset);
CNetworkVar(int, m_nNewSequenceParity);
CNetworkVar(int, m_nResetEventsParity);
// Incremented each time the entity is told to do a muzzle flash.
// The client picks up the change and draws the flash.
CNetworkVar(unsigned char, m_nMuzzleFlashParity);
CNetworkHandle(CBaseEntity, m_hLightingOrigin);
CNetworkHandle(CBaseEntity, m_hLightingOriginRelative);
string_t m_iszLightingOriginRelative; // for reading from the file only
string_t m_iszLightingOrigin; // for reading from the file only
memhandle_t m_boneCacheHandle;
unsigned short m_fBoneCacheFlags; // Used for bone cache state on model
protected:
CNetworkVar(float, m_fadeMinDist); // Point at which fading is absolute
CNetworkVar(float, m_fadeMaxDist); // Point at which fading is inactive
CNetworkVar(float, m_flFadeScale); // Scale applied to min / max
public:
COutputEvent m_OnIgnite;
private:
CStudioHdr *m_pStudioHdr;
CThreadFastMutex m_StudioHdrInitLock;
CThreadFastMutex m_BoneSetupMutex;
// FIXME: necessary so that cyclers can hack m_bSequenceFinished
friend class CFlexCycler;
friend class CCycler;
friend class CBlendingCycler;
};
//-----------------------------------------------------------------------------
// Purpose: return a pointer to an updated studiomdl cache cache
//-----------------------------------------------------------------------------
inline CStudioHdr *CBaseAnimating::GetModelPtr(void) {
if (IsDynamicModelLoading()) return NULL;
#ifdef _DEBUG
if (!HushAsserts()) {
// GetModelPtr() is often called before OnNewModel() so go ahead and set
// it up first chance.
static IDataCacheSection *pModelCache =
datacache->FindSection("ModelData");
AssertOnce(pModelCache->IsFrameLocking());
}
#endif
if (!m_pStudioHdr && GetModel()) {
LockStudioHdr();
}
return (m_pStudioHdr && m_pStudioHdr->IsValid()) ? m_pStudioHdr : NULL;
}
inline void CBaseAnimating::InvalidateMdlCache() {
UnlockStudioHdr();
if (m_pStudioHdr != NULL) {
delete m_pStudioHdr;
m_pStudioHdr = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: Serves the 90% case of calling SetSequence / ResetSequenceInfo.
//-----------------------------------------------------------------------------
/*
inline void CBaseAnimating::ResetSequence(int nSequence)
{
m_nSequence = nSequence;
ResetSequenceInfo();
}
*/
inline float CBaseAnimating::GetPlaybackRate() { return m_flPlaybackRate; }
inline void CBaseAnimating::SetPlaybackRate(float rate) {
m_flPlaybackRate = rate;
}
inline void CBaseAnimating::SetLightingOrigin(CBaseEntity *pLightingOrigin) {
m_hLightingOrigin = pLightingOrigin;
}
inline CBaseEntity *CBaseAnimating::GetLightingOrigin() {
return m_hLightingOrigin;
}
inline void CBaseAnimating::SetLightingOriginRelative(
CBaseEntity *pLightingOriginRelative) {
m_hLightingOriginRelative = pLightingOriginRelative;
}
inline CBaseEntity *CBaseAnimating::GetLightingOriginRelative() {
return m_hLightingOriginRelative;
}
//-----------------------------------------------------------------------------
// Cycle access
//-----------------------------------------------------------------------------
inline float CBaseAnimating::GetCycle() const { return m_flCycle; }
inline void CBaseAnimating::SetCycle(float flCycle) { m_flCycle = flCycle; }
EXTERN_SEND_TABLE(DT_BaseAnimating);
#define ANIMATION_SEQUENCE_BITS 12 // 4096 sequences
#define ANIMATION_SKIN_BITS \
10 // 1024 body skin selections FIXME: this seems way high
#define ANIMATION_BODY_BITS 32 // body combinations
#define ANIMATION_HITBOXSET_BITS 2 // hit box sets
#if defined(TF_DLL)
#define ANIMATION_POSEPARAMETER_BITS 8 // pose parameter resolution
#else
#define ANIMATION_POSEPARAMETER_BITS 11 // pose parameter resolution
#endif
#define ANIMATION_PLAYBACKRATE_BITS \
8 // default playback rate, only used on leading edge detect sequence
// changes
#endif // BASEANIMATING_H