/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #ifndef CRYINCLUDE_CRYANIMATION_MODEL_H #define CRYINCLUDE_CRYANIMATION_MODEL_H #pragma once #include "ModelAnimationSet.h" //embedded #include "ModelMesh.h" //embedded #include "Skeleton.h" #include class CFacialModel; struct IDynamicController; class CParamLoader; enum EJointFlag { #if defined(ENABLE_BBOX_EXCLUDE_LIST) eJointFlag_ExcludeFromBBox = BIT(0), #endif eJointFlag_NameHasForearm = BIT(1), eJointFlag_NameHasCalf = BIT(2), eJointFlag_NameHasPelvisOrHeadOrSpineOrThigh = BIT(3), }; #define NODEINDEX_NONE (MAX_JOINT_AMOUNT - 1) #define NO_ENTRY_FOUND -1 template class CInt32HashMap { private: inline uint32 HashFunction(uint32 key) const { return (key * 1664525 + 1013904223) % BUCKET_AMOUNT; }; struct Node { T value; int32 key; JointIdType next; Node() : next(NODEINDEX_NONE) {}; }; struct Bucket { JointIdType firstNode; Bucket() : firstNode(NODEINDEX_NONE) {}; }; Bucket m_buckets[BUCKET_AMOUNT]; Node* m_nodes; JointIdType m_nextFreeNode; int m_nodeAmount; public: CInt32HashMap(int nodeAmount) { m_nodes = new Node[nodeAmount]; m_nodeAmount = nodeAmount; m_nextFreeNode = 0; } ~CInt32HashMap() { delete[] m_nodes; } void Clear() { for (int i = 0; i < BUCKET_AMOUNT; ++i) { m_buckets[i].firstNode = NODEINDEX_NONE; } for (int i = 0; i < m_nodeAmount; ++i) { m_nodes[i].next = NODEINDEX_NONE; } m_nextFreeNode = 0; } void Insert(uint32 key, T value) { uint32 index = HashFunction(key); CRY_ASSERT(value != NODEINDEX_NONE); CRY_ASSERT(m_nextFreeNode < m_nodeAmount); CRY_ASSERT(index < BUCKET_AMOUNT); if (m_buckets[index].firstNode == NODEINDEX_NONE) { m_nodes[m_nextFreeNode].value = value; m_nodes[m_nextFreeNode].key = key; m_buckets[index].firstNode = m_nextFreeNode; m_nextFreeNode++; return; } else { JointIdType node = m_buckets[index].firstNode; while (1) { if (m_nodes[node].next == NODEINDEX_NONE) { m_nodes[m_nextFreeNode].value = value; m_nodes[m_nextFreeNode].key = key; m_nodes[node].next = m_nextFreeNode; m_nextFreeNode++; return; } node = m_nodes[node].next; } } } int32 Retrieve(uint32 key) const { uint32 index = HashFunction(key); JointIdType node = m_buckets[index].firstNode; while (node != NODEINDEX_NONE) { if (m_nodes[node].key == key) { return m_nodes[node].value; } node = m_nodes[node].next; } return NO_ENTRY_FOUND; } void GetMemoryUsage(ICrySizer* pSizer) const { pSizer->AddObject(m_buckets, sizeof(Bucket) * BUCKET_AMOUNT); pSizer->AddObject(m_nodes, sizeof(Node) * m_nodeAmount); pSizer->AddObject(m_nextFreeNode); pSizer->AddObject(m_nodeAmount); } }; //---------------------------------------------------------------------- struct IdxAndName { int32 m_idxJoint; const char* m_strJoint; IdxAndName() { m_idxJoint = -1; ; m_strJoint = 0; } void GetMemoryUsage(ICrySizer* pSizer) const { pSizer->AddObject(m_strJoint); } }; struct IKLimbType { LimbIKDefinitionHandle m_nHandle; uint32 m_nSolver; //32-bit string uint32 m_nInterations; //only need for iterative solvers f32 m_fThreshold; //only need for iterative solvers f32 m_fStepSize; //only need for iterative solvers DynArray m_arrJointChain; DynArray m_arrLimbChildren; DynArray m_arrRootToEndEffector; IKLimbType() { m_nHandle = 0; m_nSolver = 0; } void GetMemoryUsage(ICrySizer* pSizer) const { pSizer->AddObject(m_arrJointChain); pSizer->AddObject(m_arrLimbChildren); pSizer->AddObject(m_arrRootToEndEffector); } }; struct SRecoilJoints { const char* m_strJointName; int16 m_nIdx; int16 m_nArm; f32 m_fWeight; f32 m_fDelay; SRecoilJoints() { m_strJointName = 0; m_nIdx = -1; m_nArm = 0; m_fWeight = 0.0f; m_fDelay = 0.0f; }; void GetMemoryUsage(ICrySizer* pSizer) const {} }; struct ProcAdjust { const char* m_strJointName; int16 m_nIdx; ProcAdjust() { m_strJointName = 0; m_nIdx = -1; }; void GetMemoryUsage(ICrySizer* pSizer) const {} }; struct RecoilDesc { RecoilDesc() { m_weaponRightJointIndex = -1; m_weaponLeftJointIndex = -1; } DynArray m_joints; string m_ikHandleLeft; string m_ikHandleRight; int32 m_weaponRightJointIndex; int32 m_weaponLeftJointIndex; }; struct PoseBlenderAimDesc { PoseBlenderAimDesc() { m_error = 0; } uint32 m_error; DynArray m_blends; DynArray m_rotations; DynArray m_positions; DynArray m_procAdjustments; }; struct PoseBlenderLookDesc { PoseBlenderLookDesc() { m_error = 0; m_eyeLimitHalfYawRadians = DEG2RAD(45); m_eyeLimitPitchRadiansUp = DEG2RAD(45); m_eyeLimitPitchRadiansDown = DEG2RAD(45); } uint32 m_error; f32 m_eyeLimitHalfYawRadians; f32 m_eyeLimitPitchRadiansUp; f32 m_eyeLimitPitchRadiansDown; string m_eyeAttachmentLeftName; string m_eyeAttachmentRightName; DynArray m_blends; DynArray m_rotations; DynArray m_positions; }; // Animation Driven IK struct ADIKTarget { LimbIKDefinitionHandle m_nHandle; int32 m_idxTarget; const char* m_strTarget; int32 m_idxWeight; const char* m_strWeight; ADIKTarget() { m_nHandle = 0; m_idxTarget = -1; m_strTarget = 0; m_idxWeight = -1; m_strWeight = 0; } void GetMemoryUsage(ICrySizer* pSizer) const {} }; //---------------------------------------------------------------------- // CDefault Skeleton //---------------------------------------------------------------------- class CDefaultSkeleton : public IDefaultSkeleton { private: static uint s_guidLast; friend class CharacterManager; public: CDefaultSkeleton(const char* pSkeletonFilePath, uint32 type, uint64 nCRC64 = 0); virtual ~CDefaultSkeleton(); uint GetGuid() const { return m_guid; } void SetInstanceCounter(uint32 numInstances) { m_nInstanceCounter = numInstances; } void SetKeepInMemory(bool nKiM) { m_nKeepInMemory = nKiM; } uint32 GetKeepInMemory() const {return m_nKeepInMemory; } void AddRef() { ++m_nRefCounter; } void Release(); void DeleteIfNotReferenced() { if (m_nRefCounter <= 0) { delete this; } } int GetRefCounter() const { return m_nRefCounter; } virtual const char* GetModelFilePath() const { return m_strFilePath.c_str(); } uint64 const GetModelFilePathCRC64() const override { return m_nFilePathCRC64; } const char* GetModelAnimEventDatabaseCStr() const { return m_strAnimEventFilePath.c_str(); } void SetModelAnimEventDatabase(const string& sAnimEventDatabasePath) { m_strAnimEventFilePath = sAnimEventDatabasePath; } uint32 SizeOfDefaultSkeleton() const; void CopyAndAdjustSkeletonParams(const CDefaultSkeleton* pCDefaultSkeletonSrc); int32 RemapIdx(const CDefaultSkeleton* pCDefaultSkeletonSrc, const int32 idx); virtual bool HasDynamicController(uint32 controlCrc) const; virtual const IDynamicController* GetDynamicController(uint32 controlCrc) const; void UpdateAnimationDynamicControllers(); //----------------------------------------------------------------------- //----- Interfaces to access the default-skeleton ----- //----------------------------------------------------------------------- struct SJoint { SJoint() { m_nOffsetChildren = 0; m_numChildren = 0; m_flags = 0; m_idxParent = -1; //index of parent-joint m_numLevels = 0; m_nJointCRC32 = 0; m_nJointCRC32Lower = 0; m_CGAObject = 0; m_ObjectID = -1; m_NodeID = ~0; #if ENABLE_CRY_PHYSICS m_PhysInfo.pPhysGeom = 0; #endif m_fMass = 0.0f; } void GetMemoryUsage(ICrySizer* pSizer) const { pSizer->AddObject(m_CGAObject); pSizer->AddObject(m_strJointName); } //-------------------------------------------------------------------------------- string m_strJointName; //the name of the joint uint32 m_nJointCRC32; //case sensitive CRC32 of joint-name. Used to access case-sensitive controllers in in CAF files uint32 m_nJointCRC32Lower; //lower case CRC32 of joint-name. int32 m_nOffsetChildren; //this is 0 if there are no children uint16 m_numChildren; //how many children does this joint have uint16 m_flags; int16 m_idxParent; //index of parent-joint. if the idx==-1 then this joint is the root. Usually this values are > 0 uint16 m_numLevels; //#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#- //--------> the rest is deprecated and will disappear sooner or later //#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#- #if ENABLE_CRY_PHYSICS CryBonePhysics m_PhysInfo; #endif f32 m_fMass; //this doesn't need to be in every joint _smart_ptr m_CGAObject; // Static object controlled by this joint. uint32 m_NodeID; // CGA-node uint16 m_ObjectID; // used by CGA }; virtual uint32 GetJointCount() const { return m_arrModelJoints.size(); }; virtual int32 GetJointIDByCRC32(uint32 crc32) const { if (m_pJointsCRCToIDMap) { return m_pJointsCRCToIDMap->Retrieve(crc32); } else { return -1; } } virtual uint32 GetJointCRC32ByID(int32 nJointID) const { int32 numJoints = m_arrModelJoints.size(); if (nJointID >= 0 && nJointID < numJoints) { return m_arrModelJoints[nJointID].m_nJointCRC32Lower; } else { return -1; } } virtual const char* GetJointNameByID(int32 nJointID) const // Return name of bone from bone table, return zero id nId is out of range { int32 numJoints = m_arrModelJoints.size(); if (nJointID >= 0 && nJointID < numJoints) { return m_arrModelJoints[nJointID].m_strJointName.c_str(); } CRY_ASSERT_MESSAGE(false, "GetJointNameByID - Index out of range!"); return ""; // invalid bone id } virtual int32 GetJointIDByName(const char* strJointName) const { uint32 crc32 = CCrc32::ComputeLowercase(strJointName); if (m_pJointsCRCToIDMap) { return m_pJointsCRCToIDMap->Retrieve(crc32); } else { return -1; } } virtual int32 GetJointParentIDByID(int32 nChildID) const { int32 numJoints = m_arrModelJoints.size(); if (nChildID >= 0 && nChildID < numJoints) { return m_arrModelJoints[nChildID].m_idxParent; } AZ_Warning("Animation", nChildID >= 0 && nChildID < numJoints, "Joint id %d is out of range.", nChildID); return -1; } virtual int32 GetControllerIDByID(int32 nJointID) const { int32 numJoints = m_arrModelJoints.size(); if (nJointID >= 0 && nJointID < numJoints) { return m_arrModelJoints[nJointID].m_nJointCRC32; } else { return -1; } } virtual const QuatT& GetDefaultAbsJointByID(uint32 nJointIdx) const { uint32 jointCount = m_poseDefaultData.GetJointCount(); if (nJointIdx >= 0 && nJointIdx < jointCount) { return m_poseDefaultData.GetJointAbsolute(nJointIdx); } CRY_ASSERT(nJointIdx >= 0 && nJointIdx < jointCount); return g_IdentityQuatT; }; virtual const QuatT& GetDefaultRelJointByID(uint32 nJointIdx) const { uint32 jointCount = m_poseDefaultData.GetJointCount(); if (nJointIdx >= 0 && nJointIdx < jointCount) { return m_poseDefaultData.GetJointRelative(nJointIdx); } CRY_ASSERT(nJointIdx >= 0 && nJointIdx < jointCount); return g_IdentityQuatT; }; #if ENABLE_CRY_PHYSICS virtual const phys_geometry* GetJointPhysGeom(uint32 jointIndex) const { return m_arrModelJoints[jointIndex].m_PhysInfo.pPhysGeom; } #endif virtual bool CreateCHRPARAMS(const char* paramFileName); bool LoadNewSKEL(const char* szFilePath, uint32 nLoadingFlags); void LoadCHRPARAMS(const char* paramFileName, const std::string* optionalBuffer = nullptr); bool IsAnimInParamFilters(const char * animationPath, CParamLoader& paramLoader, uint32& animListIDOut); bool AddAnimationIfInAnimationSetFilters(const char* animationPath); bool RemoveAnimation(const char* animationPath); #if ENABLE_CRY_PHYSICS //--> setup of physical proxies bool SetupPhysicalProxies(const DynArray& arrPhyBoneMeshes, const DynArray& arrBoneEntities, _smart_ptr pIMaterial, const char* filename); static bool ParsePhysInfoProperties_ROPE(CryBonePhysics& pi, const DynArray& props); static DynArray GetPhysInfoProperties_ROPE(const CryBonePhysics& pi, int32 nRopeOrGrid); #endif // ENABLE_CRY_PHYSICS CDefaultSkeleton::SJoint* GetParent(int32 i) { int32 pidx = m_arrModelJoints[i].m_idxParent; if (pidx < 0) { return 0; } return &m_arrModelJoints[pidx]; } int32 GetLimbDefinitionIdx(LimbIKDefinitionHandle nHandle) const { uint32 numLimbTypes = m_IKLimbTypes.size(); for (uint32 lt = 0; lt < numLimbTypes; lt++) { if (nHandle == m_IKLimbTypes[lt].m_nHandle) { return lt; } } return -1; } const IKLimbType* GetLimbDefinition(int32 index) const { return &m_IKLimbTypes[index]; } void PrepareJointIDHash() { int numBones = m_arrModelJoints.size(); if (m_pJointsCRCToIDMap) { delete m_pJointsCRCToIDMap; } m_pJointsCRCToIDMap = new CInt32HashMap(numBones); for (int i = 0; i < numBones; ++i) { uint32 crc32Lower = m_arrModelJoints[i].m_nJointCRC32Lower; m_pJointsCRCToIDMap->Insert(crc32Lower, static_cast(i)); } } void InitializeHardcodedJointsProperty(); const Skeleton::CPoseData& GetPoseData() const { return m_poseDefaultData; } uint32 SizeOfSkeleton() const { return 0; } void GetMemoryUsage(ICrySizer* pSizer) const; void VerifyHierarchy(); DynArray m_arrModelJoints; // This is the bone hierarchy. All the bones of the hierarchy are present in this array, joint DynamicControllers may also be present in this array std::map m_arrDynamicControllers; // This is the dynamic controllers supported by this Skeleton. CInt32HashMap* m_pJointsCRCToIDMap; //this dramatically accelerates access to JointIDs by CRC - overall consumption should be less than 100 kb throughout the game (2-3 kb per ModelSkeleton) Skeleton::CPoseData m_poseDefaultData; string m_strFeetLockIKHandle[MAX_FEET_AMOUNT]; DynArray m_IKLimbTypes; DynArray m_ADIKTargets; RecoilDesc m_recoilDesc; PoseBlenderLookDesc m_poseBlenderLookDesc; PoseBlenderAimDesc m_poseBlenderAimDesc; uint32 m_usePhysProxyBBox; DynArray m_BBoxIncludeList; AABB m_AABBExtension; AABB m_ModelAABB; //AABB of the model in default pose bool m_bHasPhysics2; DynArray m_arrBackupPhyBoneMeshes; //collision proxi DynArray m_arrBackupBoneEntities; //physical-bones #if ENABLE_CRY_PHYSICS DynArray m_arrBackupPhysInfo; #endif // ENABLE_CRY_PHYSICS uint m_guid; //----------------------------------------------------------------------- // Returns the interface for animations applicable to this model //----------------------------------------------------------------------- DynArray m_animListIDs; IAnimationSet* GetIAnimationSet() { return m_pAnimationSet; } const IAnimationSet* GetIAnimationSet() const { return m_pAnimationSet.get(); } bool LoadAnimations(CParamLoader& paramLoader); //loads animations by using the output of the ParamLoader const string GetDefaultAnimDir(); uint32 LoadAnimationFiles(CParamLoader& paramLoader, uint32 listID); // load animation files that were parsed from the param file into memory (That are not in DBA) uint32 LoadAnimationFile(const char* animationPath, CParamLoader& paramLoader, uint32 listID); uint32 ReuseAnimationFiles(CParamLoader& paramLoader, uint32 listID); // files that are already in memory can be reused _smart_ptr m_pAnimationSet; DynArray< DynArray > m_arrAnimationLOD; ////////////////////////////////////////////////////////////////////////// void CreateFacialInstance();// Called on a model after it was completely loaded. void LoadFaceLib(const char* faceLibFile, const char* animDirName, CDefaultSkeleton* pDefaultSkeleton); CFacialModel* GetFacialModel() { return m_pFacialModel; } const CFacialModel* GetFacialModel() const { return m_pFacialModel; } private: const string m_strFilePath; const uint64 m_nFilePathCRC64; AZStd::atomic_int m_nRefCounter, m_nInstanceCounter; int m_nKeepInMemory; CFacialModel* m_pFacialModel; string m_strAnimEventFilePath; //#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#- //----> CModelMeshm, CGAs and all interfaces to access the mesh and the IMaterials will be removed in the future //#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#- public: _smart_ptr GetIMaterial() const { if (m_pCGA_Object) { return m_pCGA_Object->GetMaterial(); } return m_ModelMesh.m_pIDefaultMaterial; } TRenderChunkArray* GetRenderMeshMaterials() { if (m_ModelMesh.m_pIRenderMesh) { return &m_ModelMesh.m_pIRenderMesh->GetChunks(); } else { return NULL; } } uint32 GetModelMeshCount() const { return 1; } CModelMesh* GetModelMesh() { return &m_ModelMesh; } virtual void PrecacheMesh(bool bFullUpdate, int nRoundId, int nLod); virtual Vec3 GetRenderMeshOffset() const { return m_ModelMesh.m_vRenderMeshOffset; }; virtual IRenderMesh* GetIRenderMesh() const { return m_ModelMesh.m_pIRenderMesh; } virtual uint32 GetTextureMemoryUsage2(ICrySizer* pSizer = 0) const; virtual uint32 GetMeshMemoryUsage(ICrySizer* pSizer = 0) const; CModelMesh m_ModelMesh; //still using one LOD uint32 m_ObjectType; _smart_ptr m_pCGA_Object; }; #endif // CRYINCLUDE_CRYANIMATION_MODEL_H