!I (1670409):
[CRYENGINE.git] / Code / CryEngine / CryAnimation / LoaderCHR.cpp
blob31a83097b78d79f4ccf8b7894a7ab6687d1926fd
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "stdafx.h"
4 #include "LoaderCHR.h"
6 #include <Cry3DEngine/I3DEngine.h>
7 #include <CryAnimation/ICryAnimation.h>
8 #include "ModelMesh.h"
9 #include "ModelSkin.h"
10 #include "Model.h"
11 #include "FacialAnimation/FaceAnimation.h"
12 #include "FacialAnimation/FacialModel.h"
13 #include "FacialAnimation/FaceEffectorLibrary.h"
14 #include "AnimEventLoader.h"
15 #include "CharacterManager.h"
16 #include <CryCore/Platform/IPlatformOS.h>
18 namespace CryCHRLoader_LoadNewCHR_Helpers
21 static bool FindFirstMesh(CMesh*& pMesh, CNodeCGF*& pGFXNode, CContentCGF* pContent, const char* filenameNoExt, int lod)
23 uint32 numNodes = pContent->GetNodeCount();
24 pGFXNode = 0;
25 for (uint32 n = 0; n < numNodes; n++)
27 CNodeCGF* pNode = pContent->GetNode(n);
28 if (pNode->type == CNodeCGF::NODE_MESH && pNode->pMesh)
30 pGFXNode = pNode;
31 break;
34 if (pGFXNode == 0)
36 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, filenameNoExt, "Failed to Load Character file. GFXNode not found");
37 return false;
40 pMesh = pGFXNode->pMesh;
41 if (pMesh && pMesh->m_pBoneMapping == 0)
43 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, filenameNoExt, "Failed to Load Character file. Skeleton-Initial-Positions are missing");
44 return false;
47 return true;
50 } // namespace CryCHRLoader_LoadNewCHR_Helpers
52 bool CryCHRLoader::BeginLoadCHRRenderMesh(CDefaultSkeleton* pSkel, const DynArray<CCharInstance*>& pCharInstances, EStreamTaskPriority estp)
54 const char* szFilePath = pSkel->GetModelFilePath();
56 LOADING_TIME_PROFILE_SECTION(g_pISystem);
58 static_assert(sizeof(TFace) == 6, "Invalid type size!");
60 MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_CHR, 0, "LoadCharacter %s", szFilePath);
62 CRY_DEFINE_ASSET_SCOPE(CRY_SKEL_FILE_EXT, szFilePath);
64 const char* szExt = PathUtil::GetExt(szFilePath);
65 m_strGeomFileNameNoExt.assign(szFilePath, *szExt ? szExt - 1 : szExt);
67 if (m_strGeomFileNameNoExt.empty())
69 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, szFilePath, "Wrong character filename");
70 return false;
73 //--------------------------------------------------------------------------------------------
75 CModelMesh* pModelMesh = pSkel->GetModelMesh();
76 assert(pModelMesh);
78 const stack_string filename = stack_string(m_strGeomFileNameNoExt.c_str()) + "." + szExt;
79 const stack_string lodName = (pModelMesh->m_stream.bHasMeshFile) ? (filename + "m") : (filename);
81 m_pModelSkel = pSkel;
82 m_RefByInstances = pCharInstances;
84 StreamReadParams params;
85 params.nFlags = 0;//IStreamEngine::FLAGS_NO_SYNC_CALLBACK;
86 params.dwUserData = 0;
87 params.ePriority = estp;
88 IStreamEngine* pStreamEngine = gEnv->pSystem->GetStreamEngine();
89 m_pStream = pStreamEngine->StartRead(eStreamTaskTypeAnimation, lodName.c_str(), this, &params);
91 return m_pStream != NULL;
94 void CryCHRLoader::AbortLoad()
96 if (m_pStream)
97 m_pStream->Abort();
100 void CryCHRLoader::StreamAsyncOnComplete(IReadStream* pStream, unsigned nError)
102 if (!nError)
104 if (m_pModelSkel)
106 EndStreamSkel(pStream);
108 else if (m_pModelSkin)
110 EndStreamSkinAsync(pStream);
114 pStream->FreeTemporaryMemory();
117 void CryCHRLoader::EndStreamSkel(IReadStream* pStream)
119 using namespace CryCHRLoader_LoadNewCHR_Helpers;
121 const char* lodName = pStream->GetName();
123 SmartContentCGF pCGF = g_pI3DEngine->CreateChunkfileContent(lodName);
124 bool bLoaded = g_pI3DEngine->LoadChunkFileContentFromMem(pCGF, pStream->GetBuffer(), pStream->GetBytesRead(), IStatObj::ELoadingFlagsJustGeometry, false, false);
126 if (!bLoaded || !pCGF)
128 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, lodName, "%s: The Chunk-Loader failed to load the file.", __FUNCTION__);
129 return;
132 CContentCGF* pLodContent = pCGF;
134 CNodeCGF* pMeshNode = 0;
135 CMesh* pMesh = 0;
136 if (!FindFirstMesh(pMesh, pMeshNode, pLodContent, m_strGeomFileNameNoExt.c_str(), 0))
137 return;
139 CModelMesh* pModelMesh = m_pModelSkel->GetModelMesh();
140 assert(pModelMesh);
142 m_pNewRenderMesh = pModelMesh->InitRenderMeshAsync(pMesh, m_pModelSkel->GetModelFilePath(), 0, m_arrNewRenderChunks);
145 void CryCHRLoader::StreamOnComplete(IReadStream* pStream, unsigned nError)
147 if (m_pModelSkel)
149 CModelMesh* pModelMesh = m_pModelSkel->GetModelMesh();
150 assert(pModelMesh);
152 pModelMesh->InitRenderMeshSync(m_arrNewRenderChunks, m_pNewRenderMesh);
153 m_pNewRenderMesh = nullptr;
154 m_arrNewRenderChunks.clear();
156 else if (m_pModelSkin)
158 EndStreamSkinSync(pStream);
160 int nRenderLod = (int)pStream->GetParams().dwUserData;
163 ClearModel();
166 // cleans up the resources allocated during load
167 void CryCHRLoader::ClearModel()
169 m_strGeomFileNameNoExt = "";
170 m_pStream = nullptr;
171 m_arrNewRenderChunks.clear();
172 m_pNewRenderMesh = nullptr;
173 m_RefByInstances.clear();
176 //----------------------------------------------------------------------------------
177 //----------------------------------------------------------------------------------
178 //----------------------------------------------------------------------------------
179 //---- this belongs into another file (i.e.CDefaultSkeleton) -------------------
180 //----------------------------------------------------------------------------------
181 //----------------------------------------------------------------------------------
182 //----------------------------------------------------------------------------------
183 namespace SkelLoader_Helpers
186 struct LevelOfDetail
188 CContentCGF* m_pContentCGF;
189 CContentCGF* m_pContentMeshCGF;
191 LevelOfDetail()
193 m_pContentCGF = 0;
194 m_pContentMeshCGF = 0;
197 ~LevelOfDetail()
199 if (m_pContentCGF)
200 g_pI3DEngine->ReleaseChunkfileContent(m_pContentCGF), m_pContentCGF = 0;
201 if (m_pContentMeshCGF)
202 g_pI3DEngine->ReleaseChunkfileContent(m_pContentMeshCGF), m_pContentMeshCGF = 0;
206 static bool InitializeBones(CDefaultSkeleton* pDefaultSkeleton, CSkinningInfo* pSkinningInfo, const char* szFilePath)
208 const int lod = 0;
209 uint32 numBones = pSkinningInfo->m_arrBonesDesc.size();
210 assert(numBones);
211 if (numBones >= MAX_JOINT_AMOUNT)
213 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, szFilePath, "Too many Joints in model. Current Limit is: %d", MAX_JOINT_AMOUNT);
214 return false;
217 if (!numBones)
219 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, szFilePath, "Model has no Joints.");
220 return false;
223 pDefaultSkeleton->m_poseDefaultData.Initialize(numBones);
224 pDefaultSkeleton->m_arrModelJoints.resize(numBones);
226 for (uint32 nBone = 0; nBone < numBones; ++nBone)
228 const CryBoneDescData& boneDesc = pSkinningInfo->m_arrBonesDesc[nBone];
229 assert(boneDesc.m_DefaultB2W.IsOrthonormalRH());
230 if (!boneDesc.m_DefaultB2W.IsOrthonormalRH())
232 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, szFilePath, "Bone %d: B2W is not orthonormal RH in lod %d", nBone, lod);
233 return false;
236 CDefaultSkeleton::SJoint& joint = pDefaultSkeleton->m_arrModelJoints[nBone];
237 joint.m_idxParent = -1;
238 int32 offset = boneDesc.m_nOffsetParent;
239 if (offset)
240 joint.m_idxParent = int32(nBone + offset);
242 joint.m_strJointName = &boneDesc.m_arrBoneName[0];
243 joint.m_nJointCRC32Lower = CCrc32::ComputeLowercase(&boneDesc.m_arrBoneName[0]);
244 joint.m_nJointCRC32 = boneDesc.m_nControllerID;
245 joint.m_PhysInfo = boneDesc.m_PhysInfo[0];
246 joint.m_fMass = boneDesc.m_fMass;
248 pDefaultSkeleton->m_poseDefaultData.GetJointsAbsolute()[nBone] = QuatT(boneDesc.m_DefaultB2W);
249 pDefaultSkeleton->m_poseDefaultData.GetJointsAbsolute()[nBone].q.Normalize();
250 pDefaultSkeleton->m_poseDefaultData.GetJointsRelative()[nBone] = pDefaultSkeleton->m_poseDefaultData.GetJointsAbsolute()[nBone];
251 if (joint.m_idxParent >= 0)
252 pDefaultSkeleton->m_poseDefaultData.GetJointsRelative()[nBone] = pDefaultSkeleton->m_poseDefaultData.GetJointsAbsolute()[joint.m_idxParent].GetInverted() * pDefaultSkeleton->m_poseDefaultData.GetJointsAbsolute()[nBone];
253 pDefaultSkeleton->m_poseDefaultData.GetJointsRelative()[nBone].q.Normalize();
256 uint32 nRootCounter = 0;
257 for (uint32 j = 0; j < numBones; j++)
259 int32 idxParent = pDefaultSkeleton->m_arrModelJoints[j].m_idxParent;
260 if (idxParent < 0)
261 nRootCounter++; //count the root joints
263 if (nRootCounter != 1)
265 //something went wrong. this model has multiple roots
266 for (uint32 j = 0; j < numBones; j++)
267 pDefaultSkeleton->m_arrModelJoints[j].m_PhysInfo.pPhysGeom = 0; //delete all "Chunk-IDs" to prevent the FatalError in the destructor.
268 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, szFilePath, "CryAnimation: multiple root in skeleton. Please fix the model.");
269 return false;
272 const char* RootName = pDefaultSkeleton->m_arrModelJoints[0].m_strJointName.c_str();
273 if (RootName[0] == 'B' && RootName[1] == 'i' && RootName[2] == 'p' && RootName[3] == '0' && RootName[4] == '1')
275 //This character-model is a biped and it was made in CharacterStudio.
276 //CharacterStudio is not allowing us to project the Bip01 on the ground and change its orientation; thats why we have to do it here.
277 pDefaultSkeleton->m_poseDefaultData.GetJointsAbsolute()[0].SetIdentity();
278 pDefaultSkeleton->m_poseDefaultData.GetJointsRelative()[0].SetIdentity();
279 uint32 numJoints = pDefaultSkeleton->m_arrModelJoints.size();
280 for (uint32 bone = 1; bone < numJoints; bone++)
282 //adjust the pelvis (and all other joints directly linked Bip01)
283 int32 idxParent = pDefaultSkeleton->m_arrModelJoints[bone].m_idxParent;
284 if (idxParent == 0)
285 pDefaultSkeleton->m_poseDefaultData.GetJointsRelative()[bone] = pDefaultSkeleton->m_poseDefaultData.GetJointsAbsolute()[bone];
289 QuatT orientation = pDefaultSkeleton->m_poseDefaultData.GetJointsAbsolute()[0];
290 uint32 IsIdentiy = Quat::IsEquivalent(Quat(IDENTITY), orientation.q, 0.14f); // 0.14 radians == ~8.0 degrees
291 if (IsIdentiy == 0)
293 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, szFilePath, "CryAnimation: Default-Skeleton has wrong orientation. Please use the CryEngine conventions when building a model");
294 //CRY_ASSERT_MESSAGE(!"Inverted model orientation!", strGeomFileName);
295 return 0;
298 return true;
301 static const char* FindFirstMesh(CMesh*& pMesh, CNodeCGF*& pGFXNode, CContentCGF* pContent)
303 uint32 numNodes = pContent->GetNodeCount();
304 pGFXNode = 0;
305 for (uint32 n = 0; n < numNodes; n++)
307 CNodeCGF* pNode = pContent->GetNode(n);
308 if (pNode->type == CNodeCGF::NODE_MESH && pNode->pMesh)
310 pGFXNode = pNode;
311 break;
314 if (pGFXNode == 0)
316 return "Failed to Load Character file. GFXNode not found";
319 pMesh = pGFXNode->pMesh;
320 if (pMesh && pMesh->m_pBoneMapping == 0)
322 return "Failed to Load Character file. Skeleton-Initial-Positions are missing";
325 return nullptr;
328 static const char* FindFirstMeshAndMaterial(CMesh*& pMesh, CNodeCGF*& pGFXNode, _smart_ptr<IMaterial>& pMaterial, CContentCGF* pContent, const char* filenameNoExt)
330 pGFXNode = 0;
331 for (int i = 0, limit = pContent->GetNodeCount(); i < limit; ++i)
333 CNodeCGF* pNode = pContent->GetNode(i);
334 if (pNode->type == CNodeCGF::NODE_MESH)
336 pGFXNode = pNode;
337 break;
340 if (!pGFXNode)
342 return "Failed to Load Character file. GFXNode not found";
345 pMesh = pGFXNode->pMesh;
346 if (pMesh && !pMesh->m_pBoneMapping)
348 return "Failed to Load Character file. Skeleton-Initial-Positions are missing";
351 if (!pMaterial)
353 if (pGFXNode->pMaterial)
354 pMaterial = g_pI3DEngine->GetMaterialManager()->LoadCGFMaterial(pGFXNode->pMaterial->name, PathUtil::GetPathWithoutFilename(filenameNoExt).c_str());
355 else
356 pMaterial = g_pI3DEngine->GetMaterialManager()->GetDefaultMaterial();
358 return nullptr;
361 static const char* FindFirstMaterial(_smart_ptr<IMaterial>& pMaterial, CContentCGF* pContent, const char* szFilenameNoExt)
363 if (!pContent->GetMaterialCount())
365 return "Failed to Load Character file. No material found for physicalization.";
368 pMaterial = g_pI3DEngine->GetMaterialManager()->LoadCGFMaterial(pContent->GetMaterial(0)->name, PathUtil::GetPathWithoutFilename(szFilenameNoExt).c_str());
370 return nullptr;
373 } // namespace SkelLoader_Helpers
375 bool CDefaultSkeleton::LoadNewSKEL(const char* szFilePath, uint32 nLoadingFlags)
377 using namespace SkelLoader_Helpers;
379 LOADING_TIME_PROFILE_SECTION_ARGS(szFilePath);
381 static_assert(sizeof(TFace) == 6, "Invalid type size!");
383 MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_CHR, 0, "LoadCharacter %s", szFilePath);
385 CRY_DEFINE_ASSET_SCOPE(CRY_SKEL_FILE_EXT, szFilePath);
387 const char* szExt = PathUtil::GetExt(szFilePath);
388 stack_string strGeomFileNameNoExt;
389 strGeomFileNameNoExt.assign(szFilePath, *szExt ? szExt - 1 : szExt);
390 if (strGeomFileNameNoExt.empty())
392 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, szFilePath, "Wrong character filename");
393 return 0;
396 //--------------------------------------------------------------------------------------------
397 #ifndef _RELEASE
399 uint32 isSKEL = stricmp(szExt, CRY_SKEL_FILE_EXT) == 0;
400 if (isSKEL)
402 string strFilePathLOD1 = strGeomFileNameNoExt + "_lod1" + "." + szExt;
403 bool exist = g_pISystem->GetIPak()->IsFileExist(strFilePathLOD1.c_str());
404 if (exist)
405 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, 0, "!CryAnimation: LODs in CHRs are not supported any more. Please remove them from the build: %s", strFilePathLOD1.c_str());
408 #endif
410 //--------------------------------------------------------------------------------------------
412 LevelOfDetail cgfs; //in case of early exit we call the destructor automatically and delete all CContentCGFs
413 cgfs.m_pContentCGF = g_pI3DEngine->CreateChunkfileContent(szFilePath);
414 if (cgfs.m_pContentCGF == 0)
415 CryFatalError("CryAnimation error: issue in Chunk-Loader");
416 bool bLoaded = g_pI3DEngine->LoadChunkFileContent(cgfs.m_pContentCGF, szFilePath, 0);
417 if (bLoaded == 0)
419 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, szFilePath, "CryAnimation: The Chunk-Loader failed to load the file.");
420 return 0;
423 if (!Console::GetInst().ca_StreamCHR)
425 string lodmName = string(szFilePath) + "m";
426 bool bHasMeshFile = gEnv->pCryPak->IsFileExist(lodmName.c_str());
427 if (bHasMeshFile)
429 cgfs.m_pContentMeshCGF = g_pI3DEngine->CreateChunkfileContent(lodmName);
430 g_pI3DEngine->LoadChunkFileContent(cgfs.m_pContentMeshCGF, lodmName.c_str());
434 //----------------------------------------------------------------------------------------------------------------------
435 //--- initialize the ModelSkel
436 //----------------------------------------------------------------------------------------------------------------------
437 CSkinningInfo* pSkinningInfo = cgfs.m_pContentCGF->GetSkinningInfo();
438 if (pSkinningInfo == 0)
440 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, szFilePath, "CryAnimation: Skinning info is missing in lod %d", 0);
441 return 0;
444 //--------------------------------------------------------------------------
445 //---- initialize the default animation skeleton
446 //--------------------------------------------------------------------------
447 const uint32 isSkeletonValid = InitializeBones(this, pSkinningInfo, szFilePath);
449 CMesh* pMesh = nullptr;
450 CNodeCGF* pMeshNode = nullptr;
451 _smart_ptr<IMaterial> pMaterial = nullptr;
452 FindFirstMeshAndMaterial(pMesh, pMeshNode, pMaterial, cgfs.m_pContentCGF, strGeomFileNameNoExt.c_str());
454 if (!pMaterial)
456 const char* const szError = FindFirstMaterial(pMaterial, cgfs.m_pContentCGF, strGeomFileNameNoExt.c_str());
457 if (szError)
459 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, 0, "!CryAnimation: Error while loading material: %s", szError);
463 //------------------------------------------------------------------------------------
464 //--- initialize Physical Proxies
465 //------------------------------------------------------------------------------------
466 if (1)
468 uint32 numJoints = m_arrModelJoints.size();
469 m_arrBackupPhysInfo.resize(numJoints);
470 for (uint32 j = 0; j < numJoints; j++)
471 m_arrBackupPhysInfo[j] = m_arrModelJoints[j].m_PhysInfo;
472 m_arrBackupPhyBoneMeshes = pSkinningInfo->m_arrPhyBoneMeshes;
473 m_arrBackupBoneEntities = pSkinningInfo->m_arrBoneEntities;
474 if (!SetupPhysicalProxies(m_arrBackupPhyBoneMeshes, m_arrBackupBoneEntities, pMaterial, szFilePath))
475 return 0;
477 if (isSkeletonValid == 0)
478 return 0;
480 //------------------------------------------------------------------------------------
482 VerifyHierarchy();
484 if (1)
486 //----------------------------------------------------------------------------------------------------------------------------------
487 //--- initialize ModelMesh MorphTargets and RenderMesh
488 //--- There can be only one Render LOD in the Base-Model (Even this is optional. Usually the Base-Model has an empty skeleton)
489 //----------------------------------------------------------------------------------------------------------------------------------
491 if (cgfs.m_pContentMeshCGF)
493 const auto szError = FindFirstMesh(pMesh, pMeshNode, cgfs.m_pContentMeshCGF);
494 if (szError)
496 g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, strGeomFileNameNoExt.c_str(), szError);
497 return false;
501 if (pMesh)
503 m_ModelMeshEnabled = true;
505 CModelMesh* pModelMesh = GetModelMesh();
506 assert(pModelMesh);
508 uint32 success = pModelMesh->InitMesh(pMesh, pMeshNode, pMaterial, pSkinningInfo, m_strFilePath.c_str(), 0);
509 if (success == 0)
510 return 0;
512 string meshFilename = string(szFilePath) + 'm';
513 pModelMesh->m_stream.bHasMeshFile = gEnv->pCryPak->IsFileExist(meshFilename.c_str());
517 //---------------------------------------------------------------------------------------------------------------
519 InitializeHardcodedJointsProperty();
520 RebuildJointLookupCaches();
521 stack_string paramFileName = strGeomFileNameNoExt + "." + CRY_CHARACTER_PARAM_FILE_EXT;
522 uint32 isPrevMode = nLoadingFlags & CA_PreviewMode;
523 if (isPrevMode == 0)
524 LoadCHRPARAMS(paramFileName);
526 m_animListIDs.clear();
527 return 1;
530 void CDefaultSkeleton::LoadCHRPARAMS(const char* paramFileName)
532 CParamLoader& paramLoader = g_pCharacterManager->GetParamLoader();
533 #ifdef EDITOR_PCDEBUGCODE
534 bool hasInjectedParams = paramLoader.HasInjectedCHRPARAMS(paramFileName);
535 #else
536 bool hasInjectedParams = false;
537 #endif
538 // If the new .params file exists, load it since it can be done much faster and the file is cleaner (all XML)
539 if (hasInjectedParams || g_pIPak->IsFileExist(paramFileName))
541 // this will be refilled by LoadXML
542 m_animListIDs.clear();
544 bool ok = paramLoader.LoadXML(this, GetDefaultAnimDir(), paramFileName, m_animListIDs);
545 if (ok)
546 LoadAnimations(paramLoader);
550 //------------------------------------------------------------------------------------------------------------
551 // loads animations for a model by composing a complete animation list and loading it into an AnimationSet
552 //------------------------------------------------------------------------------------------------------------
553 bool CDefaultSkeleton::LoadAnimations(CParamLoader& paramLoader)
555 //LOADING_TIME_PROFILE_SECTION(g_pISystem);
557 // the number of animations loaded
558 uint32 numAnimAssets = 0;
560 int32 numAnims = 0;
562 CRY_PROFILE_FUNCTION(PROFILE_ANIMATION);
564 if (m_animListIDs.size() == 0)
566 return false;
569 // attributes of the compound list
570 // all those attributes have to be stored on a per list basis,
571 // keeping it for the topmost list in memory is not enough
572 // since another list might use it as a dependency which does not
573 // override it
574 DynArray<string> modelTracksDatabases;
575 DynArray<string> lockedDatabases;
576 string animEventDatabase;
577 string faceLibFile;
578 string faceLibDir;
579 CAnimationSet::FacialAnimationSet::container_type facialAnimations;
581 const int32 nListIDs = m_animListIDs.size();
582 for (int32 i = nListIDs - 1; i >= 0; --i) // by walking backwards the 'parent' lists come first
584 const uint32 animListID = m_animListIDs[i];
585 if (animListID < paramLoader.GetParsedListNumber())
587 const SAnimListInfo& animList = paramLoader.GetParsedList(animListID);
588 numAnims += animList.arrAnimFiles.size();
589 modelTracksDatabases.push_back(animList.modelTracksDatabases);
590 lockedDatabases.push_back(animList.lockedDatabases);
592 if (animEventDatabase.empty())
594 animEventDatabase = animList.animEventDatabase; // first one wins --> so outermost animeventdb wins
597 if (faceLibFile.empty())
599 faceLibFile = animList.faceLibFile;
600 faceLibDir = animList.faceLibDir;
605 m_pAnimationSet->prepareLoadCAFs(numAnims);
607 uint32 numTracksDataBases = modelTracksDatabases.size();
608 if (numTracksDataBases)
609 g_AnimationManager.CreateGlobalHeaderDBA(modelTracksDatabases);
611 // Lock Persistent Databases
612 DynArray<string>::iterator it;
613 for (it = lockedDatabases.begin(); it != lockedDatabases.end(); ++it)
615 g_AnimationManager.DBA_LockStatus(it->c_str(), 1, false);
618 if (!animEventDatabase.empty())
620 SetModelAnimEventDatabase(animEventDatabase);
623 if (!faceLibFile.empty())
625 CreateFacialInstance();
626 LoadFaceLib(faceLibFile, faceLibDir, this);
629 for (int32 i = nListIDs - 1; i >= 0; --i)
631 const SAnimListInfo& animList = paramLoader.GetParsedList(m_animListIDs[i]);
632 uint32 numAnimNames = animList.arrAnimFiles.size();
634 // this is where the Animation List Cache kicks in
635 if (!animList.headersLoaded)
637 paramLoader.ExpandWildcards(m_animListIDs[i]);
638 numAnimAssets += LoadAnimationFiles(paramLoader, m_animListIDs[i]);
639 // this list has been processed, dont do it again!
640 paramLoader.SetHeadersLoaded(m_animListIDs[i]);
642 else
644 numAnimAssets += ReuseAnimationFiles(paramLoader, m_animListIDs[i]);
647 if (!animList.facialAnimations.empty())
648 facialAnimations.insert(facialAnimations.end(), animList.facialAnimations.begin(), animList.facialAnimations.end());
651 // Store the list of facial animations in the model animation set.
652 if (!facialAnimations.empty())
653 m_pAnimationSet->GetFacialAnimations().SwapElementsWithVector(facialAnimations);
655 AnimEventLoader::LoadAnimationEventDatabase(this, GetModelAnimEventDatabaseCStr());
657 uint32 dddd = g_AnimationManager.m_arrGlobalCAF.size();
659 if (numAnimAssets > 1)
661 g_pILog->UpdateLoadingScreen(" %u animation-assets loaded (total assets: %d)", numAnimAssets, g_AnimationManager.m_arrGlobalCAF.size());
664 if (Console::GetInst().ca_MemoryUsageLog)
666 CryModuleMemoryInfo info;
667 CryGetMemoryInfoForModule(&info);
668 g_pILog->UpdateLoadingScreen("Memstat %i", (int)(info.allocated - info.freed));
671 // If there is a facial model, but no expression library, we should create an empty library for it.
672 // When we assign the library to the facial model it will automatically be assigned the morphs as expressions.
674 CFacialModel* pFacialModel = GetFacialModel();
675 CFacialEffectorsLibrary* pEffectorsLibrary = (pFacialModel ? pFacialModel->GetFacialEffectorsLibrary() : 0);
676 if (pFacialModel && !pEffectorsLibrary)
678 CFacialAnimation* pFacialAnimationManager = (g_pCharacterManager ? g_pCharacterManager->GetFacialAnimation() : 0);
679 CFacialEffectorsLibrary* pNewLibrary = new CFacialEffectorsLibrary(pFacialAnimationManager);
680 const string& filePath = GetModelFilePath();
681 if (!filePath.empty())
683 string libraryFilePath = PathUtil::ReplaceExtension(filePath, ".fxl");
684 pNewLibrary->SetName(libraryFilePath);
686 if (pFacialModel && pNewLibrary)
687 pFacialModel->AssignLibrary(pNewLibrary);
691 if (Console::GetInst().ca_PrecacheAnimationSets)
693 CAnimationSet& animSet = *m_pAnimationSet;
694 uint32 animCount = animSet.GetAnimationCount();
695 for (uint32 i = 0; i < animCount; ++i)
697 const ModelAnimationHeader* pHeader = animSet.GetModelAnimationHeader(i);
698 if (pHeader)
700 if (pHeader->m_nAssetType == CAF_File)
702 uint32 globalID = pHeader->m_nGlobalAnimId;
703 if (globalID > 0)
705 PREFAST_ASSUME(g_pCharacterManager);
706 g_AnimationManager.m_arrGlobalCAF[globalID].StartStreamingCAF();
713 m_pAnimationSet->VerifyLMGs();
715 return m_arrModelJoints.size() != 0;
718 void CDefaultSkeleton::LoadFaceLib(const char* faceLibFile, const char* animDirName, CDefaultSkeleton* pDefaultSkeleton)
720 const char* pFilePath = GetModelFilePath();
721 const char* szExt = PathUtil::GetExt(pFilePath);
722 stack_string strGeomFileNameNoExt;
723 strGeomFileNameNoExt.assign(pFilePath, *szExt ? szExt - 1 : szExt);
725 LOADING_TIME_PROFILE_SECTION(g_pISystem);
726 // Facial expressions library.
727 _smart_ptr<IFacialEffectorsLibrary> pLib;
729 pLib = g_pCharacterManager->GetIFacialAnimation()->LoadEffectorsLibrary(faceLibFile);
730 if (!pLib)
732 // Search in animation directory .chr file directory.
733 pLib = g_pCharacterManager->GetIFacialAnimation()->LoadEffectorsLibrary(
734 PathUtil::Make(animDirName, faceLibFile));
736 if (!pLib)
738 // Search in .chr file directory.
739 pLib = g_pCharacterManager->GetIFacialAnimation()->LoadEffectorsLibrary(
740 PathUtil::Make(PathUtil::GetParentDirectory(strGeomFileNameNoExt), stack_string(faceLibFile)));
742 if (pLib)
744 if (pDefaultSkeleton->GetFacialModel())
746 pDefaultSkeleton->GetFacialModel()->AssignLibrary(pLib);
749 else
751 gEnv->pLog->LogError("CryAnimation: Facial Effector Library %s not found (when loading %s.chrparams)", faceLibFile, strGeomFileNameNoExt.c_str());
755 //-------------------------------------------------------------------------------------------------
756 #define MAX_STRING_LENGTH (256)
758 uint32 CDefaultSkeleton::ReuseAnimationFiles(CParamLoader& paramLoader, uint32 listID)
760 LOADING_TIME_PROFILE_SECTION(g_pISystem);
762 const SAnimListInfo& animList = paramLoader.GetParsedList(listID);
763 int32 numAnims = animList.arrLoadedAnimFiles.size();
764 uint32 result = 0;
766 m_pAnimationSet->PrepareToReuseAnimations(numAnims);
768 for (int i = 0; i < numAnims; ++i)
770 const ModelAnimationHeader& animHeader = animList.arrLoadedAnimFiles[i];
772 m_pAnimationSet->ReuseAnimation(animHeader);
773 result++;
776 return result;
779 uint32 CDefaultSkeleton::LoadAnimationFiles(CParamLoader& paramLoader, uint32 listID)
781 const char* pFullFilePath = GetModelFilePath();
782 const char* szExt = PathUtil::GetExt(pFullFilePath);
783 stack_string strGeomFileNameNoExt;
784 strGeomFileNameNoExt.assign(pFullFilePath, *szExt ? szExt - 1 : szExt);
786 LOADING_TIME_PROFILE_SECTION(g_pISystem);
787 // go through all Anims on stack and load them
788 uint32 nAnimID = 0;
789 int32 numAnims = 0;
791 const SAnimListInfo& animList = paramLoader.GetParsedList(listID);
793 #if BLENDSPACE_VISUALIZATION
794 const char* strInternalType_Para1D = "InternalPara1D";
795 int32 nDefAnimID = m_pAnimationSet->GetAnimIDByName(strInternalType_Para1D);
796 if (nDefAnimID < 0)
798 int32 nGAnimID = m_pAnimationSet->LoadFileLMG("InternalPara1D.LMG", strInternalType_Para1D);
799 if (nGAnimID)
800 CryFatalError("The GlobalID of the default Para1D must be 0");
801 int nAnimID0 = m_pAnimationSet->GetAnimIDByName(strInternalType_Para1D);
802 if (nAnimID0 >= 0)
804 ModelAnimationHeader* pAnim = (ModelAnimationHeader*)m_pAnimationSet->GetModelAnimationHeader(nAnimID0);
805 if (pAnim)
807 nAnimID++;
808 paramLoader.AddLoadedHeader(listID, *pAnim); // store ModelAnimationHeader to reuse it in the case that a second model will need it
812 #endif
814 numAnims = animList.arrAnimFiles.size();
815 for (int i = 0; i < numAnims; i++)
817 const char* pFilePath = animList.arrAnimFiles[i].m_FilePathQ;
818 const char* pAnimName = animList.arrAnimFiles[i].m_AnimNameQ;
819 const char* fileExt = PathUtil::GetExt(pFilePath);
821 bool IsBAD = (0 == stricmp(fileExt, ""));
822 if (IsBAD)
823 continue;
825 bool IsCHR = (0 == stricmp(fileExt, CRY_SKEL_FILE_EXT));
826 if (IsCHR)
827 continue;
828 bool IsSKIN = (0 == stricmp(fileExt, CRY_SKIN_FILE_EXT));
829 if (IsSKIN)
830 continue;
831 bool IsCDF = (0 == stricmp(fileExt, "cdf"));
832 if (IsCDF)
833 continue;
834 bool IsCGA = (0 == stricmp(fileExt, "cga"));
835 if (IsCGA)
836 continue;
838 uint32 IsCAF = (stricmp(fileExt, "caf") == 0);
839 uint32 IsLMG = (stricmp(fileExt, "lmg") == 0) || (stricmp(fileExt, "bspace") == 0) || (stricmp(fileExt, "comb") == 0);
840 assert(IsCAF + IsLMG);
842 bool IsAimPose = 0;
843 bool IsLookPose = 0;
844 if (IsCAF)
846 uint32 numAimDB = m_poseBlenderAimDesc.m_blends.size();
847 for (uint32 d = 0; d < numAimDB; d++)
849 const char* strAimIK_Token = m_poseBlenderAimDesc.m_blends[d].m_AnimToken;
850 IsAimPose = (CryStringUtils::stristr(pAnimName, strAimIK_Token) != 0);
851 if (IsAimPose)
852 break;
854 uint32 numLookDB = m_poseBlenderLookDesc.m_blends.size();
855 for (uint32 d = 0; d < numLookDB; d++)
857 const char* strLookIK_Token = m_poseBlenderLookDesc.m_blends[d].m_AnimToken;
858 IsLookPose = (CryStringUtils::stristr(pAnimName, strLookIK_Token) != 0);
859 if (IsLookPose)
860 break;
864 if (IsCAF && IsAimPose)
865 IsCAF = 0;
866 if (IsCAF && IsLookPose)
867 IsCAF = 0;
869 // ben: it can happen that a AnimID is defined in a list and in one of its dependencies that
870 // was included after the definition of that animation, this is a different behavior than
871 // before, so errors might pop up that did not pop up before
872 CryFixedStringT<256> standupAnimType;
874 int32 nGlobalAnimID = -1;
875 if (IsCAF)
877 nGlobalAnimID = m_pAnimationSet->LoadFileCAF(pFilePath, pAnimName);
880 if (IsAimPose || IsLookPose)
882 nGlobalAnimID = m_pAnimationSet->LoadFileAIM(pFilePath, pAnimName, this);
885 if (IsLMG)
887 nGlobalAnimID = m_pAnimationSet->LoadFileLMG(pFilePath, pAnimName);
890 int nAnimID0 = m_pAnimationSet->GetAnimIDByName(pAnimName);
891 if (nAnimID0 >= 0)
893 ModelAnimationHeader* pAnim = (ModelAnimationHeader*)m_pAnimationSet->GetModelAnimationHeader(nAnimID0);
894 if (pAnim)
896 nAnimID++;
897 // store ModelAnimationHeader to reuse it in the case that a second model will need it
898 paramLoader.AddLoadedHeader(listID, *pAnim);
901 else
903 gEnv->pLog->LogError("CryAnimation: Animation (caf) file \"%s\" could not be read (it's an animation of \"%s.%s\")", animList.arrAnimFiles[i].m_FilePathQ, strGeomFileNameNoExt.c_str(), CRY_SKEL_FILE_EXT);
907 return nAnimID;
910 const string CDefaultSkeleton::GetDefaultAnimDir()
912 const char* pFilePath = GetModelFilePath();
913 const char* szExt = PathUtil::GetExt(pFilePath);
914 stack_string strGeomFileNameNoExt;
915 strGeomFileNameNoExt.assign(pFilePath, *szExt ? szExt - 1 : szExt);
917 stack_string animDirName = PathUtil::GetParentDirectory(strGeomFileNameNoExt, 3);
918 if (!animDirName.empty())
919 return animDirName += "/animations";
920 else
921 return animDirName += "animations";