//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //===========================================================================// // Client-side CBasePlayer #ifndef C_STUDIOFLEX_H #define C_STUDIOFLEX_H #pragma once #include "c_baseanimating.h" #include "c_baseanimatingoverlay.h" #include "sceneentity_shared.h" #include "utlvector.h" //----------------------------------------------------------------------------- // Purpose: Item in list of loaded scene files //----------------------------------------------------------------------------- class CFlexSceneFile { public: enum { MAX_FLEX_FILENAME = 128, }; char filename[MAX_FLEX_FILENAME]; void *buffer; }; // For phoneme emphasis track struct Emphasized_Phoneme; class CSentence; enum { PHONEME_CLASS_WEAK = 0, PHONEME_CLASS_NORMAL, PHONEME_CLASS_STRONG, NUM_PHONEME_CLASSES }; // Mapping for each loaded scene file used by this actor struct FS_LocalToGlobal_t { explicit FS_LocalToGlobal_t() : m_Key(0), m_nCount(0), m_Mapping(0) {} explicit FS_LocalToGlobal_t(const flexsettinghdr_t *key) : m_Key(key), m_nCount(0), m_Mapping(0) {} void SetCount(int count) { Assert(!m_Mapping); Assert(count > 0); m_nCount = count; m_Mapping = new int[m_nCount]; Q_memset(m_Mapping, 0, m_nCount * sizeof(int)); } FS_LocalToGlobal_t(const FS_LocalToGlobal_t &src) { m_Key = src.m_Key; delete m_Mapping; m_Mapping = new int[src.m_nCount]; Q_memcpy(m_Mapping, src.m_Mapping, src.m_nCount * sizeof(int)); m_nCount = src.m_nCount; } ~FS_LocalToGlobal_t() { delete m_Mapping; m_nCount = 0; m_Mapping = 0; } const flexsettinghdr_t *m_Key; int m_nCount; int *m_Mapping; }; bool FlexSettingLessFunc(const FS_LocalToGlobal_t &lhs, const FS_LocalToGlobal_t &rhs); class IHasLocalToGlobalFlexSettings { public: virtual void EnsureTranslations(const flexsettinghdr_t *pSettinghdr) = 0; }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- struct Emphasized_Phoneme { // Global fields, setup at start char classname[64]; bool required; // Global fields setup first time tracks played bool basechecked; const flexsettinghdr_t *base; const flexsetting_t *exp; // Local fields, processed for each sentence bool valid; float amount; }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class C_BaseFlex : public C_BaseAnimatingOverlay, public IHasLocalToGlobalFlexSettings { DECLARE_CLASS(C_BaseFlex, C_BaseAnimatingOverlay); public: DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); DECLARE_INTERPOLATION(); C_BaseFlex(); virtual ~C_BaseFlex(); virtual void Spawn(); virtual void InitPhonemeMappings(); void SetupMappings(char const *pchFileRoot); virtual CStudioHdr *OnNewModel(void); virtual void StandardBlendingRules(CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask); virtual void OnThreadedDrawSetup(); // model specific virtual void BuildTransformations(CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t &cameraTransform, int boneMask, CBoneBitList &boneComputed); static void LinkToGlobalFlexControllers(CStudioHdr *hdr); virtual void SetupWeights(const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights); virtual bool SetupGlobalWeights(const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights); static void RunFlexDelay(int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights, float &flFlexDelayTime); virtual void SetupLocalWeights(const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights); virtual bool UsesFlexDelayedWeights(); static void RunFlexRules(CStudioHdr *pStudioHdr, float *dest); virtual Vector SetViewTarget(CStudioHdr *pStudioHdr); virtual bool GetSoundSpatialization(SpatializationInfo_t &info); virtual void GetToolRecordingState(KeyValues *msg); // Called at the lowest level to actually apply a flex animation void AddFlexAnimation(CSceneEventInfo *info); void SetFlexWeight(LocalFlexController_t index, float value); float GetFlexWeight(LocalFlexController_t index); // Look up flex controller index by global name LocalFlexController_t FindFlexController(const char *szName); public: Vector m_viewtarget; CInterpolatedVar m_iv_viewtarget; // indexed by model local flexcontroller float m_flexWeight[MAXSTUDIOFLEXCTRL]; CInterpolatedVarArray m_iv_flexWeight; int m_blinktoggle; static int AddGlobalFlexController(const char *szName); static char const *GetGlobalFlexControllerName(int idx); // bah, this should be unified with all prev/current stuff. public: // Keep track of what scenes are being played void StartChoreoScene(CChoreoScene *scene); void RemoveChoreoScene(CChoreoScene *scene); // Start the specifics of an scene event virtual bool StartSceneEvent(CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, C_BaseEntity *pTarget); // Manipulation of events for the object // Should be called by think function to process all scene events // The default implementation resets m_flexWeight array and calls // AddSceneEvents virtual void ProcessSceneEvents(bool bFlexEvents); // Assumes m_flexWeight array has been set up, this adds the actual // currently playing // expressions to the flex weights and adds other scene events as needed virtual bool ProcessSceneEvent(bool bFlexEvents, CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event); virtual bool ProcessSequenceSceneEvent(CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event); // Remove all playing events void ClearSceneEvents(CChoreoScene *scene, bool canceled); // Stop specifics of event virtual bool ClearSceneEvent(CSceneEventInfo *info, bool fastKill, bool canceled); // Add the event to the queue for this actor void AddSceneEvent(CChoreoScene *scene, CChoreoEvent *event, C_BaseEntity *pTarget = NULL, bool bClientSide = false); // Remove the event from the queue for this actor void RemoveSceneEvent(CChoreoScene *scene, CChoreoEvent *event, bool fastKill); // Checks to see if the event should be considered "completed" bool CheckSceneEvent(float currenttime, CChoreoScene *scene, CChoreoEvent *event); // Checks to see if a event should be considered "completed" virtual bool CheckSceneEventCompletion(CSceneEventInfo *info, float currenttime, CChoreoScene *scene, CChoreoEvent *event); int FlexControllerLocalToGlobal(const flexsettinghdr_t *pSettinghdr, int key); // IHasLocalToGlobalFlexSettings virtual void EnsureTranslations(const flexsettinghdr_t *pSettinghdr); // For handling scene files void *FindSceneFile(const char *filename); private: bool RequestStartSequenceSceneEvent(CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget); bool ProcessFlexAnimationSceneEvent(CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event); bool ProcessFlexSettingSceneEvent(CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event); void AddFlexSetting(const char *expr, float scale, const flexsettinghdr_t *pSettinghdr, bool newexpression); // Array of active SceneEvents, in order oldest to newest CUtlVector m_SceneEvents; CUtlVector m_ActiveChoreoScenes; bool HasSceneEvents() const; private: CUtlRBTree m_LocalToGlobal; float m_blinktime; int m_prevblinktoggle; int m_iBlink; LocalFlexController_t m_iEyeUpdown; LocalFlexController_t m_iEyeRightleft; bool m_bSearchedForEyeFlexes; int m_iMouthAttachment; float m_flFlexDelayTime; float *m_flFlexDelayedWeight; int m_cFlexDelayedWeight; // shared flex controllers static int g_numflexcontrollers; static char *g_flexcontroller[MAXSTUDIOFLEXCTRL * 4]; // room for global set of flexcontrollers static float g_flexweight[MAXSTUDIOFLEXDESC]; protected: Emphasized_Phoneme m_PhonemeClasses[NUM_PHONEME_CLASSES]; private: C_BaseFlex(const C_BaseFlex &); // not defined, not accessible const flexsetting_t *FindNamedSetting(const flexsettinghdr_t *pSettinghdr, const char *expr); void ProcessVisemes(Emphasized_Phoneme *classes); void AddVisemesForSentence(Emphasized_Phoneme *classes, float emphasis_intensity, CSentence *sentence, float t, float dt, bool juststarted); void AddViseme(Emphasized_Phoneme *classes, float emphasis_intensity, int phoneme, float scale, bool newexpression); bool SetupEmphasisBlend(Emphasized_Phoneme *classes, int phoneme); void ComputeBlendedSetting(Emphasized_Phoneme *classes, float emphasis_intensity); #ifdef HL2_CLIENT_DLL public: Vector m_vecLean; CInterpolatedVar m_iv_vecLean; Vector m_vecShift; CInterpolatedVar m_iv_vecShift; #endif }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class CFlexSceneFileManager : CAutoGameSystem { public: CFlexSceneFileManager() : CAutoGameSystem("CFlexSceneFileManager") {} virtual bool Init(); virtual void Shutdown(); void EnsureTranslations(IHasLocalToGlobalFlexSettings *instance, const flexsettinghdr_t *pSettinghdr); void *FindSceneFile(IHasLocalToGlobalFlexSettings *instance, const char *filename, bool allowBlockingIO); private: void DeleteSceneFiles(); CUtlVector m_FileList; }; //----------------------------------------------------------------------------- // Do we have active expressions? //----------------------------------------------------------------------------- inline bool C_BaseFlex::HasSceneEvents() const { return m_SceneEvents.Count() != 0; } EXTERN_RECV_TABLE(DT_BaseFlex); float *GetVisemeWeights(int phoneme); #endif // C_STUDIOFLEX_H