!BI (CE-16391) (3dengine) cl 1643612,1646719 added a flag to suppress warning message...
[CRYENGINE.git] / Code / CryEngine / Cry3DEngine / StatObjLoad.cpp
blob99f85d2577d2bbc555feb9c74cd8f2e72c145489
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 // -------------------------------------------------------------------------
4 // File name: statobjconstr.cpp
5 // Version: v1.00
6 // Created: 28/5/2001 by Vladimir Kajalin
7 // Compilers: Visual Studio.NET
8 // Description: loading
9 // -------------------------------------------------------------------------
10 // History:
12 ////////////////////////////////////////////////////////////////////////////
14 #include "StdAfx.h"
16 #include "StatObj.h"
17 #include "IndexedMesh.h"
18 #include "MatMan.h"
20 #include "CGF/CGFLoader.h"
21 #include "CGF/CGFSaver.h"
22 #include "CGF/ReadOnlyChunkFile.h"
24 #include <CryMemory/CryMemoryManager.h>
26 #define GEOM_INFO_FILE_EXT "ginfo"
27 #define MESH_NAME_FOR_MAIN "main"
28 #define PHYSICS_BREAKABLE_JOINT "$joint"
29 #define PHYSICS_COMPILED_JOINTS "$compiled_joints"
31 void CStatObj::Refresh(int nFlags)
33 if (nFlags & FRO_GEOMETRY)
35 if (nFlags & FRO_FORCERELOAD)
37 m_bLodsLoaded = false;
40 if (m_bCheckGarbage)
42 GetObjManager()->UnregisterForGarbage(this);
45 const int oldModificationId = m_nModificationId;
47 ShutDown();
48 Init();
49 bool bRes = LoadCGF(m_szFileName, false, 0, 0, 0);
51 // Shutdown/Init sequence might produce same modification id as before, so we make sure to store a different value.
52 m_nModificationId = oldModificationId + 1;
54 LoadLowLODs(false, 0);
55 TryMergeSubObjects(false);
57 if (!bRes)
59 // load default in case of error
60 ShutDown();
61 Init();
62 LoadCGF(DEFAULT_CGF_NAME, 0, 0, 0, 0);
63 m_bDefaultObject = true;
66 return;
70 void CStatObj::LoadLowLODs(bool bUseStreaming, unsigned long nLoadingFlags)
72 if (!LoadLowLODS_Prep(bUseStreaming, nLoadingFlags))
73 return;
75 int nLoadedLods = 1;
76 CStatObj* loadedLods[MAX_STATOBJ_LODS_NUM];
77 for (int nLodLevel = 0; nLodLevel < MAX_STATOBJ_LODS_NUM; nLodLevel++)
78 loadedLods[nLodLevel] = 0;
80 for (int nLodLevel = 1; nLodLevel < MAX_STATOBJ_LODS_NUM; nLodLevel++)
82 CStatObj* pLodStatObj = LoadLowLODS_Load(nLodLevel, bUseStreaming, nLoadingFlags, NULL, 0);
83 if (!pLodStatObj)
84 break;
85 nLoadedLods++;
87 loadedLods[nLodLevel] = pLodStatObj;
90 LoadLowLODS_Finalize(nLoadedLods, loadedLods);
93 bool CStatObj::LoadLowLODS_Prep(bool bUseStreaming, unsigned long nLoadingFlags)
95 m_bLodsLoaded = true;
97 if (nLoadingFlags & ELoadingFlagsIgnoreLoDs)
99 return false;
102 const char* sFileExt = PathUtil::GetExt(m_szFileName);
104 if (m_nLoadedLodsNum > 1 && GetFlags() & STATIC_OBJECT_COMPOUND)
106 for (int nLodLevel = 1; nLodLevel < MAX_STATOBJ_LODS_NUM; nLodLevel++)
108 // make lod file name
109 char sLodFileName[512];
110 char sLodNum[8];
111 cry_strcpy(sLodFileName, m_szFileName);
112 char* sPointSeparator = strchr(sLodFileName, '.');
113 if (sPointSeparator)
114 *sPointSeparator = '\0'; // Terminate at the dot
115 cry_strcat(sLodFileName, "_lod");
116 ltoa(nLodLevel, sLodNum, 10);
117 cry_strcat(sLodFileName, sLodNum);
118 cry_strcat(sLodFileName, ".");
119 cry_strcat(sLodFileName, sFileExt);
121 if (IsValidFile(sLodFileName))
123 m_nLoadedLodsNum = 1;
124 break;
129 if (m_nLoadedLodsNum > 1)
130 return false;
132 m_nLoadedLodsNum = 1;
134 if (!GetCVars()->e_Lods)
135 return false;
137 if (m_bSubObject) // Never do this for sub objects.
138 return false;
140 return true;
143 CStatObj* CStatObj::LoadLowLODS_Load(int nLodLevel, bool bUseStreaming, unsigned long nLoadingFlags, const void* pData, int nDataLen)
145 const char* sFileExt = PathUtil::GetExt(m_szFileName);
147 // make lod file name
148 char sLodFileName[512];
149 char sLodNum[8];
150 cry_strcpy(sLodFileName, m_szFileName);
151 char* sPointSeparator = strchr(sLodFileName, '.');
152 if (sPointSeparator)
153 *sPointSeparator = '\0'; // Terminate at the dot
154 cry_strcat(sLodFileName, "_lod");
155 ltoa(nLodLevel, sLodNum, 10);
156 cry_strcat(sLodFileName, sLodNum);
157 cry_strcat(sLodFileName, ".");
158 cry_strcat(sLodFileName, sFileExt);
160 CStatObj* pLodStatObj = m_pLODs ? (CStatObj*)m_pLODs[nLodLevel] : (CStatObj*)NULL;
162 // try to load
163 bool bRes = false;
165 pLodStatObj = stl::find_in_map(m_pObjManager->m_nameToObjectMap, CONST_TEMP_STRING(sLodFileName), NULL);
167 if (pLodStatObj)
169 pLodStatObj->m_pLod0 = this; // Must be here.
170 bRes = true;
172 typedef std::set<CStatObj*> LoadedObjects;
173 LoadedObjects::iterator it = m_pObjManager->m_lstLoadedObjects.find(pLodStatObj);
174 if (it != m_pObjManager->m_lstLoadedObjects.end())
176 m_pObjManager->m_lstLoadedObjects.erase(it);
177 m_pObjManager->m_nameToObjectMap.erase(CONST_TEMP_STRING(sLodFileName));
180 else if (pData || IsValidFile(sLodFileName))
182 if (!pLodStatObj)
184 pLodStatObj = new CStatObj();
185 pLodStatObj->m_pLod0 = this; // Must be here.
188 if (bUseStreaming && GetCVars()->e_StreamCgf)
189 pLodStatObj->m_bCanUnload = true;
191 bRes = pLodStatObj->LoadCGF(sLodFileName, true, nLoadingFlags, pData, nDataLen);
194 if (!bRes)
196 if ((m_pLODs ? (CStatObj*)m_pLODs[nLodLevel] : (CStatObj*)NULL) != pLodStatObj)
198 SAFE_RELEASE(pLodStatObj);
200 SetLodObject(nLodLevel, 0);
201 return NULL;
204 bool bLodCompound = (pLodStatObj->GetFlags() & STATIC_OBJECT_COMPOUND) != 0;
205 bool bLod0Compund = (GetFlags() & STATIC_OBJECT_COMPOUND) != 0;
207 SetLodObject(nLodLevel, pLodStatObj);
209 if (bLodCompound != bLod0Compund)
211 // LOD0 and LOD differ.
212 FileWarning(0, sLodFileName, "Invalid LOD%d, LOD%d have different merging property from LOD0", nLodLevel, nLodLevel);
215 return pLodStatObj;
218 void CStatObj::LoadLowLODS_Finalize(int nLoadedLods, CStatObj* loadedLods[MAX_STATOBJ_LODS_NUM])
220 //////////////////////////////////////////////////////////////////////////
221 // Put LODs into the sub objects.
222 //////////////////////////////////////////////////////////////////////////
223 if (nLoadedLods > 1)
225 m_bLodsAreLoadedFromSeparateFile = true;
227 for (int i = 0; i < (int)m_subObjects.size(); i++)
229 SSubObject* pSubObject = &m_subObjects[i];
230 if (!pSubObject->pStatObj || pSubObject->nType != STATIC_SUB_OBJECT_MESH)
231 continue;
233 CStatObj* pSubStatObj = (CStatObj*)pSubObject->pStatObj;
235 // int nLoadedTrisCount = ((CStatObj*)pSubObject->pStatObj)->m_nLoadedTrisCount;
237 for (int nLodLevel = 1; nLodLevel < nLoadedLods; nLodLevel++)
239 if (loadedLods[nLodLevel] != 0 && loadedLods[nLodLevel]->m_nSubObjectMeshCount > 0)
241 SSubObject* pLodSubObject = loadedLods[nLodLevel]->FindSubObject(pSubObject->name);
242 if (pLodSubObject && pLodSubObject->pStatObj && pLodSubObject->nType == STATIC_SUB_OBJECT_MESH)
244 pSubStatObj->SetLodObject(nLodLevel, (CStatObj*)pLodSubObject->pStatObj);
248 if (pSubStatObj)
249 pSubStatObj->CleanUnusedLods();
253 CleanUnusedLods();
255 for (int nLodLevel = 0; nLodLevel < MAX_STATOBJ_LODS_NUM; nLodLevel++)
257 if (loadedLods[nLodLevel])
259 GetObjManager()->CheckForGarbage(loadedLods[nLodLevel]);
264 //////////////////////////////////////////////////////////////////////////
265 void CStatObj::CleanUnusedLods()
267 //////////////////////////////////////////////////////////////////////////
268 // Free render resources for unused upper LODs.
269 //////////////////////////////////////////////////////////////////////////
270 if (m_nLoadedLodsNum > 1)
272 int nMinLod = GetMinUsableLod();
273 nMinLod = clamp_tpl(nMinLod, 0, (int)m_nLoadedLodsNum - 1);
274 for (int i = 0; i < nMinLod; i++)
276 CStatObj* pStatObj = (CStatObj*)GetLodObject(i);
277 if (!pStatObj)
278 continue;
280 if (pStatObj->m_pRenderMesh)
282 pStatObj->SetRenderMesh(0);
286 //////////////////////////////////////////////////////////////////////////
289 void TransformMesh(CMesh& mesh, Matrix34 tm)
291 const int nVerts = mesh.GetVertexCount();
292 if (mesh.m_pPositions)
294 for (int i = 0; i < nVerts; i++)
296 mesh.m_pPositions[i] = tm.TransformPoint(mesh.m_pPositions[i]);
299 else if (mesh.m_pPositionsF16)
301 for (int i = 0; i < nVerts; i++)
303 mesh.m_pPositionsF16[i] = tm.TransformPoint(mesh.m_pPositionsF16[i].ToVec3());
308 #if INCLUDE_MEMSTAT_CONTEXTS
309 static string FindCGFSourceFilename(const char* filename)
311 CChunkFile infoChunkFile;
312 if (!infoChunkFile.Read(filename))
314 return string();
317 for (int i = 0, n = infoChunkFile.NumChunks(); i < n; ++i)
319 const IChunkFile::ChunkDesc* const pChunkDesc = infoChunkFile.GetChunk(i);
320 if (pChunkDesc->chunkType == ChunkType_SourceInfo)
322 return (const char*)pChunkDesc->data;
326 return string();
328 #endif //INCLUDE_MEMSTAT_CONTEXTS
330 //////////////////////////////////////////////////////////////////////////
331 bool CStatObj::LoadStreamRenderMeshes(const char* filename, const void* pData, const int nDataSize, bool bLod)
333 MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_CGF, EMemStatContextFlags::MSF_Instance, "%s", m_szFileName.c_str());
334 LOADING_TIME_PROFILE_SECTION;
336 CLoaderCGF cgfLoader(util::pool_allocate, util::pool_free, GetCVars()->e_StatObjTessellationMode != 2 || bLod);
337 CStackContainer<CContentCGF> contentContainer(InplaceFactory(m_szFileName));
338 CContentCGF* pCGF = contentContainer.get();
340 bool bMeshAssigned = false;
342 //////////////////////////////////////////////////////////////////////////
343 // Load CGF.
344 //////////////////////////////////////////////////////////////////////////
345 class Listener : public ILoaderCGFListener
347 public:
348 virtual void Warning(const char* format) {}
349 virtual void Error(const char* format) {}
350 virtual bool IsValidationEnabled() { return false; }
353 bool bLoaded = false;
354 bool bLoadedChunks = false;
356 Listener listener;
357 CReadOnlyChunkFile chunkFile(false, true); // Chunk file must exist until CGF is completely loaded, and if loading from file do not make a copy of it.
359 if (filename && filename[0])
360 bLoadedChunks = chunkFile.Read(filename);
361 else
362 bLoadedChunks = chunkFile.ReadFromMemory(pData, nDataSize);
364 if (bLoadedChunks)
365 bLoaded = cgfLoader.LoadCGF(contentContainer.get(), m_szFileName, chunkFile, &listener, 0);
367 if (!bLoaded)
369 FileWarning(0, m_szFileName, "CGF Streaming Failed: %s", cgfLoader.GetLastError());
370 return false;
373 //////////////////////////////////////////////////////////////////////////
375 int nSubObjCount = (int)m_subObjects.size();
377 bool bBreakNodeLoop = false;
379 for (int i = 0; i < pCGF->GetNodeCount() && !bBreakNodeLoop; i++)
381 CNodeCGF* pNode = pCGF->GetNode(i);
382 if (!pNode->pMesh)
383 continue;
385 bool bNodeIsValidMesh = (pNode->type == CNodeCGF::NODE_MESH || (pNode->type == CNodeCGF::NODE_HELPER && pNode->helperType == HP_GEOMETRY));
386 if (!bNodeIsValidMesh)
387 continue;
389 CStatObj* pStatObj = 0;
390 for (int s = 0; s < nSubObjCount && !pStatObj; s++)
392 CStatObj* pSubStatObj = (CStatObj*)m_subObjects[s].pStatObj;
393 if (!pSubStatObj)
394 continue;
395 for (int nLod = 0; nLod < MAX_STATOBJ_LODS_NUM; nLod++)
397 CStatObj* pSubLod = (CStatObj*)pSubStatObj->GetLodObject(nLod);
398 if (!pSubLod)
399 continue;
400 if (0 == strcmp(pSubLod->m_cgfNodeName.c_str(), pNode->name))
402 pStatObj = pSubLod;
403 break;
408 if (!pStatObj && m_nSubObjectMeshCount <= 1)
410 // If we do not have sub objects, assign the root StatObj to be used, and then not check anymore other nodes.
411 for (int nLod = 0; nLod < MAX_STATOBJ_LODS_NUM && !pStatObj; nLod++)
413 CStatObj* pLod = (CStatObj*)GetLodObject(nLod);
414 if (!pLod)
415 continue;
416 if (0 == strcmp(pLod->m_cgfNodeName.c_str(), pNode->name))
418 pStatObj = pLod;
419 break;
423 if (pStatObj)
425 // add mesh to sync setup queue
426 pStatObj->m_pStreamedRenderMesh = pStatObj->MakeRenderMesh(pNode->pMesh, true);
427 if (pStatObj->m_pStreamedRenderMesh)
429 bMeshAssigned = true;
431 //////////////////////////////////////////////////////////////////////////
432 // FIXME: Qtangents not generated for foliage in RC, we must do that here.
433 //////////////////////////////////////////////////////////////////////////
434 if (pStatObj->m_nSpines && pStatObj->m_pSpines) // foliage
435 pStatObj->m_pStreamedRenderMesh->GenerateQTangents();
439 if (!bMeshAssigned && gEnv->pRenderer)
441 Warning("RenderMesh not assigned %s", m_szFileName.c_str());
444 //////////////////////////////////////////////////////////////////////////
445 // Merge sub-objects for the new lod.
446 if (GetCVars()->e_StatObjMerge)
448 CStatObj* pLod0 = (m_pLod0 != 0) ? (CStatObj*)m_pLod0 : this;
449 pLod0->TryMergeSubObjects(true);
451 //////////////////////////////////////////////////////////////////////////
453 return true;
456 //////////////////////////////////////////////////////////////////////////
457 void CStatObj::CommitStreamRenderMeshes()
459 if (m_pStreamedRenderMesh)
461 CryAutoCriticalSection lock(m_streamingMeshLock);
462 SetRenderMesh(m_pStreamedRenderMesh);
463 m_pStreamedRenderMesh = 0;
465 if (m_pLODs)
467 for (int nLod = 0; nLod < MAX_STATOBJ_LODS_NUM; nLod++)
469 CStatObj* pLodObj = m_pLODs[nLod];
470 if (pLodObj && pLodObj->m_pStreamedRenderMesh)
472 CryAutoCriticalSection lock(pLodObj->m_streamingMeshLock);
474 pLodObj->SetRenderMesh(pLodObj->m_pStreamedRenderMesh);
475 pLodObj->m_pStreamedRenderMesh = 0;
480 for (size_t i = 0, num = m_subObjects.size(); i < num; ++i)
482 CStatObj* pSubObj = (CStatObj*)m_subObjects[i].pStatObj;
483 if (pSubObj)
485 pSubObj->CommitStreamRenderMeshes();
490 bool CStatObj::IsDeformable()
492 if (Cry3DEngineBase::GetCVars()->e_MergedMeshes == 0)
493 return false;
495 // Create deformable subobject is present
496 if (m_isDeformable)
498 return true;
500 else
501 for (int i = 0, n = GetSubObjectCount(); i < n; ++i)
503 IStatObj::SSubObject* subObject = GetSubObject(i);
504 if (!subObject)
505 continue;
506 if (CStatObj* pChild = static_cast<CStatObj*>(GetSubObject(i)->pStatObj))
508 if (pChild->m_isDeformable)
509 return true;
513 return false;
516 //////////////////////////////////////////////////////////////////////////
517 bool CStatObj::LoadCGF(const char* filename, bool bLod, unsigned long nLoadingFlags, const void* pData, const int nDataSize)
519 FUNCTION_PROFILER_3DENGINE;
521 CRY_DEFINE_ASSET_SCOPE("CGF", filename);
523 if (m_bSubObject) // Never execute this on the sub objects.
524 return true;
526 #if INCLUDE_MEMSTAT_CONTEXTS
527 MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_CGF, EMemStatContextFlags::MSF_Instance, "%s", filename);
528 #endif
530 PrintComment("Loading %s", filename);
531 if (!bLod)
532 GetConsole()->TickProgressBar();
534 m_nRenderTrisCount = m_nLoadedTrisCount = 0;
535 m_nLoadedVertexCount = 0;
536 m_szFileName = filename;
537 m_szFileName.replace('\\', '/');
539 // Determine if stream only cgf is available
540 stack_string streamPath;
541 GetStreamFilePath(streamPath);
542 m_bHasStreamOnlyCGF = gEnv->pCryPak->IsFileExist(streamPath.c_str());
544 if (!m_bCanUnload)
546 if (m_bHasStreamOnlyCGF)
548 if (!LoadCGF_Int(filename, bLod, nLoadingFlags, pData, nDataSize))
549 return false;
550 return LoadStreamRenderMeshes(streamPath.c_str(), 0, 0, bLod);
554 return LoadCGF_Int(filename, bLod, nLoadingFlags, pData, nDataSize);
557 //////////////////////////////////////////////////////////////////////////
558 static _smart_ptr<IMaterial> LoadCGFMaterial(CMatMan* pMatMan, const char* szMaterialName, const char* szCgfFilename, unsigned long nLoadingFlags)
560 _smart_ptr<IMaterial> pMaterial = pMatMan->LoadCGFMaterial(szMaterialName, szCgfFilename, nLoadingFlags);
561 if (pMaterial->IsDefault())
563 // If the material file is not found then let's try to use material file
564 // with the name that is the same as the name of the .cgf file.
565 const string cgfBasedMaterialName = PathUtil::GetFileName(szCgfFilename);
566 pMaterial = pMatMan->LoadCGFMaterial(cgfBasedMaterialName.c_str(), szCgfFilename, nLoadingFlags);
567 if (pMaterial->IsDefault())
569 pMatMan->FileWarning(0, szCgfFilename, "CGF is unable to load its material, see XML reader error above for material info.");
571 else
573 CryLog("Loaded material \"%s\" instead of missing \"%s\" (file \"%s\")", cgfBasedMaterialName.c_str(), szMaterialName, szCgfFilename);
577 #if defined(FEATURE_SVO_GI)
578 if ((Cry3DEngineBase::GetCVars()->e_svoTI_Active >= 0) && (gEnv->IsEditor() || Cry3DEngineBase::GetCVars()->e_svoTI_Apply))
579 pMaterial->SetKeepLowResSysCopyForDiffTex();
580 #endif
582 return pMaterial;
585 bool CStatObj::LoadCGF_Int(const char* filename, bool bLod, unsigned long nLoadingFlags, const void* pData, const int nDataSize)
587 CLoaderCGF cgfLoader(util::pool_allocate, util::pool_free, GetCVars()->e_StatObjTessellationMode != 2 || bLod);
588 CStackContainer<CContentCGF> contentContainer(InplaceFactory(filename));
589 CContentCGF* pCGF = contentContainer.get();
591 //////////////////////////////////////////////////////////////////////////
592 // Load CGF.
593 //////////////////////////////////////////////////////////////////////////
594 class Listener : public ILoaderCGFListener
596 public:
597 virtual void Warning(const char* format) { Cry3DEngineBase::Warning("%s", format); }
598 virtual void Error(const char* format) { Cry3DEngineBase::Error("%s", format); }
599 virtual bool IsValidationEnabled() { return Cry3DEngineBase::GetCVars()->e_StatObjValidate != 0; }
602 #if !defined(_RELEASE)
603 if (GetCVars()->e_CGFMaxFileSize >= 0 && !stristr(filename, DEFAULT_CGF_NAME))
605 size_t fileSize = gEnv->pCryPak->FGetSize(filename, true);
606 if (fileSize > (size_t)GetCVars()->e_CGFMaxFileSize << 10)
608 FileWarning(0, filename, "CGF Loading Failed: file '%s' (size %3.3f kb) exceeds size limit (max %3.3f kb)",
609 filename, fileSize / 1024.f, (GetCVars()->e_CGFMaxFileSize << 10) / 1024.f);
610 return false;
613 #endif
615 Listener listener;
616 CReadOnlyChunkFile chunkFile(false, bLod); // Chunk file must exist until CGF is completely loaded, and if loading from file do not make a copy of it.
618 bool bLoaded = false;
619 if (nDataSize)
621 if (chunkFile.ReadFromMemory(pData, nDataSize))
622 bLoaded = cgfLoader.LoadCGF(contentContainer.get(), filename, chunkFile, &listener, nLoadingFlags);
624 else
626 bLoaded = cgfLoader.LoadCGF(contentContainer.get(), filename, chunkFile, &listener, nLoadingFlags);
628 if (!bLoaded)
630 if (!(nLoadingFlags & IStatObj::ELoadingFlagsNoErrorIfFail))
632 FileWarning(0, filename, "CGF Loading Failed: %s", cgfLoader.GetLastError());
635 return false;
637 //////////////////////////////////////////////////////////////////////////
639 INDENT_LOG_DURING_SCOPE(true, "While loading static object geometry '%s'", filename);
641 CExportInfoCGF* pExportInfo = pCGF->GetExportInfo();
642 CNodeCGF* pFirstMeshNode = NULL;
643 CMesh* pFirstMesh = NULL;
644 m_nSubObjectMeshCount = 0;
646 if (!pExportInfo->bCompiledCGF)
648 FileWarning(0, m_szFileName, "CGF is not compiled, use RC");
649 return false;
652 m_bMeshStrippedCGF = pExportInfo->bNoMesh;
654 bool bHasJoints = false;
655 if (nLoadingFlags & ELoadingFlagsForceBreakable)
656 m_nFlags |= STATIC_OBJECT_DYNAMIC;
658 m_nNodeCount = pCGF->GetNodeCount();
660 //////////////////////////////////////////////////////////////////////////
661 // Find out number of meshes, and get pointer to the first found mesh.
662 //////////////////////////////////////////////////////////////////////////
663 for (int i = 0; i < pCGF->GetNodeCount(); i++)
665 CNodeCGF* pNode = pCGF->GetNode(i);
666 if (pNode->type == CNodeCGF::NODE_MESH)
668 if (m_szProperties.empty())
670 m_szProperties = pNode->properties; // Take properties from the first mesh node.
671 m_szProperties.MakeLower();
673 m_nSubObjectMeshCount++;
674 if (!pFirstMeshNode)
676 pFirstMeshNode = pNode;
677 pFirstMesh = pNode->pMesh;
680 else if (!strncmp(pNode->name, PHYSICS_BREAKABLE_JOINT, strlen(PHYSICS_BREAKABLE_JOINT)) || !strcmp(pNode->name, PHYSICS_COMPILED_JOINTS))
681 bHasJoints = true;
684 bool bIsLod0Merged = false;
685 if (bLod && m_pLod0)
687 // This is a log object, check if parent was merged or not.
688 bIsLod0Merged = m_pLod0->m_nSubObjectMeshCount == 0;
691 if (pExportInfo->bMergeAllNodes || (m_nSubObjectMeshCount <= 1 && !bHasJoints && (!bLod || bIsLod0Merged)))
693 // If we merging all nodes, ignore sub object meshes.
694 m_nSubObjectMeshCount = 0;
696 if (pCGF->GetCommonMaterial())
698 if (nLoadingFlags & ELoadingFlagsPreviewMode)
700 m_pMaterial = GetMatMan()->GetDefaultMaterial();
701 m_pMaterial->AddRef();
703 else
705 m_pMaterial = ::LoadCGFMaterial(GetMatMan(), pCGF->GetCommonMaterial()->name, m_szFileName.c_str(), nLoadingFlags);
710 // Prepare material and mesh for the billboards
711 if (GetCVars()->e_VegetationBillboards >= 0)
713 CheckCreateBillboardMaterial();
716 // Fail if mesh was not complied by RC
717 if (pFirstMesh && pFirstMesh->GetFaceCount() > 0)
719 FileWarning(0, filename, "CGF is not compiled");
720 return false;
723 if (GetCVars()->e_StatObjValidate)
725 const char* pErrorDescription = 0;
726 if (pFirstMesh && (!pFirstMesh->Validate(&pErrorDescription)))
728 FileWarning(0, filename, "CGF has invalid merged mesh (%s)", pErrorDescription);
729 assert(!"CGF has invalid merged mesh");
730 return false;
732 if (!pCGF->ValidateMeshes(&pErrorDescription))
734 FileWarning(0, filename, "CGF has invalid meshes (%s)", pErrorDescription);
735 assert(!"CGF has invalid meshes");
736 return false;
740 // Common of all sub nodes bbox.
741 AABB commonBBox;
742 commonBBox.Reset();
744 bool bHaveMeshNamedMain = false;
745 bool bHasBreakableJoints = false;
746 bool bHasCompiledJoints = false;
747 bool bRenderMeshLoaded = false; // even if streaming is disabled we may load now from stripped cgf so meshes will fail to load - in this case we will stream it later
749 //////////////////////////////////////////////////////////////////////////
750 // Create StatObj from Mesh.
751 //////////////////////////////////////////////////////////////////////////
753 _smart_ptr<IRenderMesh> pMainMesh;
755 if (pExportInfo->bMergeAllNodes || m_nSubObjectMeshCount == 0)
757 if (pFirstMeshNode)
759 m_AABB.min = pFirstMeshNode->meshInfo.bboxMin;
760 m_AABB.max = pFirstMeshNode->meshInfo.bboxMax;
761 m_fGeometricMeanFaceArea = pFirstMeshNode->meshInfo.fGeometricMean;
762 commonBBox = m_AABB;
763 m_nRenderTrisCount = m_nLoadedTrisCount = pFirstMeshNode->meshInfo.nIndices / 3;
764 m_nLoadedVertexCount = pFirstMeshNode->meshInfo.nVerts;
765 m_cgfNodeName = pFirstMeshNode->name;
766 CalcRadiuses();
768 if (pFirstMesh)
770 // Assign mesh to this static object.
771 _smart_ptr<IRenderMesh> pRenderMesh = MakeRenderMesh(pFirstMesh, !m_bCanUnload);
772 SetRenderMesh(pRenderMesh);
773 pMainMesh = m_pRenderMesh;
774 bRenderMeshLoaded |= (m_pRenderMesh != 0);
776 else
778 // If mesh not known now try to estimate its memory usage.
779 m_nRenderMeshMemoryUsage = CMesh::ApproximateRenderMeshMemoryUsage(pFirstMeshNode->meshInfo.nVerts, pFirstMeshNode->meshInfo.nIndices);
780 CalcRadiuses();
783 //////////////////////////////////////////////////////////////////////////
784 // Physicalize merged geometry.
785 //////////////////////////////////////////////////////////////////////////
786 if (!bLod)
788 PhysicalizeCompiled(pFirstMeshNode);
792 //////////////////////////////////////////////////////////////////////////
794 scratch_vector<CNodeCGF*> nodes;
795 static const size_t lodNamePrefixLength = strlen(CGF_NODE_NAME_LOD_PREFIX);
797 //////////////////////////////////////////////////////////////////////////
798 // Create SubObjects.
799 //////////////////////////////////////////////////////////////////////////
800 if (pCGF->GetNodeCount() > 1 || m_nSubObjectMeshCount > 0)
802 nodes.reserve(pCGF->GetNodeCount());
804 scratch_vector<std::pair<CNodeCGF*, CStatObj*>> meshToObject;
805 meshToObject.reserve(pCGF->GetNodeCount());
807 //////////////////////////////////////////////
808 // Count required subobjects and reserve space
809 //////////////////////////////////////////////
810 size_t nSubObjects = 0;
811 for (int ii = 0; ii < pCGF->GetNodeCount(); ii++)
813 CNodeCGF* pNode = pCGF->GetNode(ii);
815 if (pNode->bPhysicsProxy)
816 continue;
818 if (pNode->type == CNodeCGF::NODE_MESH)
820 if (pExportInfo->bMergeAllNodes || m_nSubObjectMeshCount == 0) // Only add helpers, ignore meshes.
821 continue;
823 else if (pNode->type == CNodeCGF::NODE_HELPER)
825 switch (pNode->helperType)
827 case HP_GEOMETRY:
829 if (strnicmp(pNode->name, CGF_NODE_NAME_LOD_PREFIX, lodNamePrefixLength) == 0)
830 continue;
832 break;
836 ++nSubObjects;
838 m_subObjects.reserve(nSubObjects);
839 //////////////////////////////////////////////
841 int nNumMeshes = 0;
842 for (int ii = 0; ii < pCGF->GetNodeCount(); ii++)
844 CNodeCGF* pNode = pCGF->GetNode(ii);
846 if (pNode->bPhysicsProxy)
847 continue;
849 SSubObject subObject;
850 subObject.pStatObj = 0;
851 subObject.bIdentityMatrix = pNode->bIdentityMatrix;
852 subObject.bHidden = false;
853 subObject.tm = pNode->worldTM;
854 subObject.localTM = pNode->localTM;
855 subObject.name = pNode->name;
856 subObject.properties = pNode->properties;
857 subObject.nParent = -1;
858 subObject.pWeights = 0;
859 subObject.pFoliage = 0;
860 subObject.helperSize.Set(0, 0, 0);
862 if (pNode->type == CNodeCGF::NODE_MESH)
864 if (pExportInfo->bMergeAllNodes || m_nSubObjectMeshCount == 0) // Only add helpers, ignore meshes.
865 continue;
867 nNumMeshes++;
868 subObject.nType = STATIC_SUB_OBJECT_MESH;
870 if (stristr(pNode->name, "shadowproxy") != 0)
871 subObject.bShadowProxy = true;
873 if (stricmp(pNode->name, MESH_NAME_FOR_MAIN) == 0)
874 bHaveMeshNamedMain = true;
876 else if (pNode->type == CNodeCGF::NODE_LIGHT)
877 subObject.nType = STATIC_SUB_OBJECT_LIGHT;
878 else if (pNode->type == CNodeCGF::NODE_HELPER)
880 if (!bHasBreakableJoints && !strncmp(pNode->name, PHYSICS_BREAKABLE_JOINT, strlen(PHYSICS_BREAKABLE_JOINT)))
881 bHasBreakableJoints = true;
882 if (!bHasCompiledJoints && !strcmp(pNode->name, PHYSICS_COMPILED_JOINTS))
883 bHasBreakableJoints = bHasCompiledJoints = true;
885 switch (pNode->helperType)
887 case HP_POINT:
888 subObject.nType = STATIC_SUB_OBJECT_POINT;
889 break;
890 case HP_DUMMY:
891 subObject.nType = STATIC_SUB_OBJECT_DUMMY;
892 subObject.helperSize = (pNode->helperSize * 0.01f);
893 break;
894 case HP_XREF:
895 subObject.nType = STATIC_SUB_OBJECT_XREF;
896 break;
897 case HP_CAMERA:
898 subObject.nType = STATIC_SUB_OBJECT_CAMERA;
899 break;
900 case HP_GEOMETRY:
902 subObject.nType = STATIC_SUB_OBJECT_HELPER_MESH;
903 subObject.bHidden = true; // Helpers are not rendered.
905 break;
906 default:
907 assert(0); // unknown type.
911 // Only when multiple meshes inside.
912 // If only 1 mesh inside, Do not create a separate CStatObj for it.
913 if ((m_nSubObjectMeshCount > 0 && pNode->type == CNodeCGF::NODE_MESH) ||
914 (subObject.nType == STATIC_SUB_OBJECT_HELPER_MESH))
916 if (pNode->pSharedMesh)
918 // Try to find already create StatObj for a shred mesh node
919 for (int k = 0, num = (int)meshToObject.size(); k < num; k++)
921 if (pNode->pSharedMesh == meshToObject[k].first)
923 subObject.pStatObj = meshToObject[k].second;
924 break;
929 if (!subObject.pStatObj)
931 // Create a StatObj from the CGF node.
932 subObject.pStatObj = MakeStatObjFromCgfNode(pCGF, pNode, bLod, nLoadingFlags, commonBBox);
933 if (pNode->pSharedMesh)
934 meshToObject.push_back(std::make_pair(pNode->pSharedMesh, static_cast<CStatObj*>(subObject.pStatObj)));
935 else
936 meshToObject.push_back(std::make_pair(pNode, static_cast<CStatObj*>(subObject.pStatObj)));
937 bRenderMeshLoaded |= (((CStatObj*)subObject.pStatObj)->m_pRenderMesh != 0);
941 //////////////////////////////////////////////////////////////////////////
942 // Check if helper object is a LOD
943 //////////////////////////////////////////////////////////////////////////
944 if ((subObject.nType == STATIC_SUB_OBJECT_HELPER_MESH) &&
945 (strnicmp(pNode->name, CGF_NODE_NAME_LOD_PREFIX, lodNamePrefixLength) == 0))
947 // Check if helper object is a LOD
949 if (!subObject.pStatObj)
951 continue;
954 CStatObj* pLodStatObj = (CStatObj*)subObject.pStatObj;
955 CStatObj* pStatObjParent = this;
956 if (!pExportInfo->bMergeAllNodes && m_nSubObjectMeshCount > 0 && pNode->pParent)
958 // We are attached to some object, find it.
959 for (int i = 0, num = nodes.size(); i < num; i++)
961 if (nodes[i] == pNode->pParent)
963 pStatObjParent = (CStatObj*)m_subObjects[i].pStatObj;
964 break;
968 if (!pStatObjParent)
970 continue;
973 const int nLodLevel = atoi(pNode->name + lodNamePrefixLength);
974 if ((nLodLevel >= 1) && (nLodLevel < MAX_STATOBJ_LODS_NUM))
976 if (!pStatObjParent->m_pLODs || !pStatObjParent->m_pLODs[nLodLevel])
978 pStatObjParent->SetLodObject(nLodLevel, pLodStatObj);
980 else
982 const char* existingGeoName = pStatObjParent->m_pLODs[nLodLevel]->GetGeoName();
983 FileWarning(0, m_szFileName.c_str(), "Duplicated LOD helper %s (%s). Existing geometry name: %s", pNode->name, m_szFileName.c_str(), existingGeoName);
987 continue;
989 //////////////////////////////////////////////////////////////////////////
991 if (subObject.pStatObj)
993 subObject.pStatObj->AddRef();
996 m_subObjects.push_back(subObject);
997 nodes.push_back(pNode);
1000 // Delete not assigned stat objects.
1001 for (int k = 0, num = (int)meshToObject.size(); k < num; k++)
1003 if (meshToObject[k].second->m_nUsers == 0)
1005 delete meshToObject[k].second;
1009 // Assign SubObject parent pointers.
1010 int nNumCgfNodes = (int)nodes.size();
1011 if (nNumCgfNodes > 0)
1013 CNodeCGF** pNodes = &nodes[0];
1015 //////////////////////////////////////////////////////////////////////////
1016 // Move meshes to beginning, Sort sub-objects so that meshes are first.
1017 for (int i = 0; i < nNumCgfNodes; i++)
1019 if (pNodes[i]->type != CNodeCGF::NODE_MESH)
1021 // check if any more meshes exist.
1022 if (i < nNumMeshes)
1024 // Try to find next mesh and place it here.
1025 for (int j = i + 1; j < nNumCgfNodes; j++)
1027 if (pNodes[j]->type == CNodeCGF::NODE_MESH)
1029 // Swap objects at j to i.
1030 std::swap(pNodes[i], pNodes[j]);
1031 std::swap(m_subObjects[i], m_subObjects[j]);
1032 break;
1038 //////////////////////////////////////////////////////////////////////////
1040 // Assign Parent nodes.
1041 for (int i = 0; i < nNumCgfNodes; i++)
1043 CNodeCGF* pParentNode = pNodes[i]->pParent;
1044 if (pParentNode)
1046 for (int j = 0; j < nNumCgfNodes; j++)
1048 if (pNodes[j] == pParentNode)
1050 m_subObjects[i].nParent = j;
1051 break;
1057 //////////////////////////////////////////////////////////////////////////
1058 // Handle Main/Remain meshes used for Destroyable Objects.
1059 //////////////////////////////////////////////////////////////////////////
1060 if (bHaveMeshNamedMain)
1062 // If have mesh named main, then mark all sub object hidden except the one called "Main".
1063 for (int i = 0, n = m_subObjects.size(); i < n; i++)
1065 if (m_subObjects[i].nType == STATIC_SUB_OBJECT_MESH)
1067 if (stricmp(m_subObjects[i].name, MESH_NAME_FOR_MAIN) == 0)
1068 m_subObjects[i].bHidden = false;
1069 else
1070 m_subObjects[i].bHidden = true;
1074 //////////////////////////////////////////////////////////////////////////
1078 if (m_nSubObjectMeshCount > 0)
1080 m_AABB = commonBBox;
1081 CalcRadiuses();
1084 //////////////////////////////////////////////////////////////////////////
1085 // Physicalize physics proxy nodes.
1086 //////////////////////////////////////////////////////////////////////////
1087 if (!bLod)
1089 for (int i = 0, numNodes = pCGF->GetNodeCount(); i < numNodes; i++)
1091 CNodeCGF* pNode = pCGF->GetNode(i);
1092 if (pNode->bPhysicsProxy)
1094 CStatObj* pStatObjParent = this;
1095 if (pNode->pParent)
1096 for (int j = nodes.size() - 1; j >= 0; j--)
1097 if (nodes[j] == pNode->pParent && m_subObjects[j].pStatObj)
1099 pStatObjParent = (CStatObj*)m_subObjects[j].pStatObj;
1100 break;
1102 pStatObjParent->PhysicalizeCompiled(pNode, 1);
1107 //////////////////////////////////////////////////////////////////////////
1108 // Analyze foliage info.
1109 //////////////////////////////////////////////////////////////////////////
1110 if (!bLod && (pExportInfo->bMergeAllNodes || m_nSubObjectMeshCount == 0))
1112 AnalyzeFoliage(pMainMesh, pCGF);
1114 //////////////////////////////////////////////////////////////////////////
1116 for (int i = 0; i < pCGF->GetNodeCount(); i++)
1117 if (strstr(pCGF->GetNode(i)->properties, "deformable"))
1118 m_nFlags |= STATIC_OBJECT_DEFORMABLE;
1120 if (m_nSubObjectMeshCount > 0)
1121 m_nFlags |= STATIC_OBJECT_COMPOUND;
1122 else
1123 m_nFlags &= ~STATIC_OBJECT_COMPOUND;
1125 if (!bLod && !m_szProperties.empty())
1127 ParseProperties();
1130 if (!bLod)
1132 CPhysicalizeInfoCGF* pPi = pCGF->GetPhysicalizeInfo();
1133 if (pPi->nRetTets)
1135 m_pLattice = GetPhysicalWorld()->GetGeomManager()->CreateTetrLattice(pPi->pRetVtx, pPi->nRetVtx, pPi->pRetTets, pPi->nRetTets);
1139 if (m_bHasDeformationMorphs)
1141 int i, j;
1142 for (i = GetSubObjectCount() - 1; i >= 0; i--)
1143 if ((j = SubobjHasDeformMorph(i)) >= 0)
1144 GetSubObject(i)->pStatObj->SetDeformationMorphTarget(GetSubObject(j)->pStatObj);
1145 m_bUnmergable = 1;
1148 // Only objects with breakable physics joints can be merged.
1149 if (!bHasBreakableJoints)
1151 m_bUnmergable = true;
1154 // sub meshes merging
1155 if (GetCVars()->e_StatObjMerge)
1157 if (!m_bUnmergable)
1159 if (!CanMergeSubObjects())
1161 m_bUnmergable = true;
1166 // Merging always produces 16 bit meshes, so disable for 32 bit meshes for now
1167 if (pFirstMesh && pFirstMesh->m_pPositions && !bHasCompiledJoints)
1169 m_bUnmergable = true;
1172 if (!m_bCanUnload && bRenderMeshLoaded)
1173 m_eStreamingStatus = ecss_Ready;
1175 const std::vector<CStatObj*> allObjects = GatherAllObjects();
1176 for (CStatObj* obj : allObjects)
1178 // Determine if the cgf is deformable
1179 if (stristr(obj->m_szGeomName.c_str(), "bendable") && stristr(obj->m_szProperties.c_str(), "mergedmesh_deform"))
1181 obj->m_isDeformable = 1;
1182 obj->DisableStreaming();
1185 // Read the depth sort offset
1186 Vec3 depthSortOffset;
1187 if (std::sscanf(obj->m_szProperties.c_str(), "depthoffset(x:%f,y:%f,z:%f)", &depthSortOffset.x, &depthSortOffset.y, &depthSortOffset.z) == 3)
1189 obj->m_depthSortOffset = depthSortOffset;
1193 // Recursive computation of m_fLODDistance for compound- and sub-objects
1194 ComputeAndStoreLodDistances();
1196 return true;
1199 //////////////////////////////////////////////////////////////////////////
1200 std::vector<CStatObj*> CStatObj::GatherAllObjects()
1202 const int subObjectsCount = GetSubObjectCount();
1204 std::vector<CStatObj*> allObjects;
1205 allObjects.reserve(subObjectsCount + 1);
1207 allObjects.push_back(this);
1209 for (int i = 0; i < subObjectsCount; ++i)
1211 IStatObj::SSubObject* subObject = GetSubObject(i);
1212 if (subObject && subObject->pStatObj)
1214 allObjects.push_back(static_cast<CStatObj*>(subObject->pStatObj));
1218 return allObjects;
1221 //////////////////////////////////////////////////////////////////////////
1222 CStatObj* CStatObj::MakeStatObjFromCgfNode(CContentCGF* pCGF, CNodeCGF* pNode, bool bLod, int nLoadingFlags, AABB& commonBBox)
1224 CNodeCGF* pTMNode = pNode;
1225 if (pNode->pSharedMesh)
1227 pNode = pNode->pSharedMesh;
1230 // Calc bbox.
1231 if (pNode->type == CNodeCGF::NODE_MESH)
1233 AABB box(pNode->meshInfo.bboxMin, pNode->meshInfo.bboxMax);
1234 box.SetTransformedAABB(pTMNode->worldTM, box);
1235 commonBBox.Add(box.min);
1236 commonBBox.Add(box.max);
1239 CStatObj* pStatObj = new CStatObj;
1241 pStatObj->m_szFileName = m_szFileName;
1242 pStatObj->m_szGeomName = pNode->name;
1243 pStatObj->m_bSubObject = true;
1245 if (pNode->type == CNodeCGF::NODE_MESH)
1247 pStatObj->m_pParentObject = this;
1250 pStatObj->m_szProperties = pNode->properties;
1251 pStatObj->m_szProperties.MakeLower();
1252 if (!bLod && !pStatObj->m_szProperties.empty())
1254 pStatObj->ParseProperties();
1257 if (pNode->pMaterial)
1259 if (nLoadingFlags & ELoadingFlagsPreviewMode)
1261 pStatObj->m_pMaterial = GetMatMan()->GetDefaultMaterial();
1262 pStatObj->m_pMaterial->AddRef();
1264 else
1266 pStatObj->m_pMaterial = ::LoadCGFMaterial(GetMatMan(), pNode->pMaterial->name, m_szFileName.c_str(), nLoadingFlags);
1268 if (!m_pMaterial || m_pMaterial->IsDefault())
1269 m_pMaterial = pStatObj->m_pMaterial; // take it as a general stat obj material.
1271 if (!pStatObj->m_pMaterial)
1272 pStatObj->m_pMaterial = m_pMaterial;
1274 pStatObj->m_AABB.min = pNode->meshInfo.bboxMin;
1275 pStatObj->m_AABB.max = pNode->meshInfo.bboxMax;
1276 pStatObj->m_nRenderMatIds = pNode->meshInfo.nSubsets;
1277 pStatObj->m_nRenderTrisCount = pStatObj->m_nLoadedTrisCount = pNode->meshInfo.nIndices / 3;
1278 pStatObj->m_nLoadedVertexCount = pNode->meshInfo.nVerts;
1279 pStatObj->m_fGeometricMeanFaceArea = pNode->meshInfo.fGeometricMean;
1280 pStatObj->CalcRadiuses();
1282 if (nLoadingFlags & ELoadingFlagsForceBreakable)
1283 pStatObj->m_nFlags |= STATIC_OBJECT_DYNAMIC;
1285 if (pNode->pMesh)
1287 _smart_ptr<IRenderMesh> pRenderMesh = pStatObj->MakeRenderMesh(pNode->pMesh, !m_bCanUnload);
1288 pStatObj->SetRenderMesh(pRenderMesh);
1290 else
1292 // If mesh not known now try to estimate its memory usage.
1293 pStatObj->m_nRenderMeshMemoryUsage = CMesh::ApproximateRenderMeshMemoryUsage(pNode->meshInfo.nVerts, pNode->meshInfo.nIndices);
1295 pStatObj->m_cgfNodeName = pNode->name;
1297 if (!bLod)
1299 pStatObj->PhysicalizeCompiled(pNode);
1300 pStatObj->AnalyzeFoliage(pStatObj->m_pRenderMesh, pCGF);
1302 if (pNode->pSkinInfo)
1304 pStatObj->m_pSkinInfo = (SSkinVtx*)pNode->pSkinInfo;
1305 pStatObj->m_hasSkinInfo = 1;
1306 pNode->pSkinInfo = 0;
1309 return pStatObj;
1312 //////////////////////////////////////////////////////////////////////////
1313 _smart_ptr<IRenderMesh> CStatObj::MakeRenderMesh(CMesh* pMesh, bool bDoRenderMesh)
1315 FUNCTION_PROFILER_3DENGINE;
1317 if (!pMesh)
1318 return 0;
1320 m_AABB = pMesh->m_bbox;
1321 m_fGeometricMeanFaceArea = pMesh->m_geometricMeanFaceArea;
1323 CalcRadiuses();
1325 m_nLoadedTrisCount = pMesh->GetIndexCount() / 3;
1326 m_nLoadedVertexCount = pMesh->GetVertexCount();
1327 if (!m_nLoadedTrisCount)
1328 return 0;
1330 m_nRenderTrisCount = 0;
1331 m_nRenderMatIds = 0;
1332 //////////////////////////////////////////////////////////////////////////
1333 // Initialize Mesh subset material flags.
1334 //////////////////////////////////////////////////////////////////////////
1335 for (int i = 0; i < pMesh->GetSubSetCount(); i++)
1337 SMeshSubset& subset = pMesh->m_subsets[i];
1338 IMaterial* pMtl = m_pMaterial->GetSafeSubMtl(subset.nMatID);
1339 subset.nMatFlags = pMtl->GetFlags();
1340 if (subset.nPhysicalizeType == PHYS_GEOM_TYPE_NONE && pMtl->GetSurfaceType()->GetPhyscalParams().pierceability >= 10)
1341 subset.nMatFlags |= MTL_FLAG_NOPHYSICALIZE;
1342 if (!(subset.nMatFlags & MTL_FLAG_NODRAW) && (subset.nNumIndices > 0))
1344 m_nRenderMatIds++;
1345 m_nRenderTrisCount += subset.nNumIndices / 3;
1348 //////////////////////////////////////////////////////////////////////////
1350 if (!m_nRenderTrisCount)
1351 return 0;
1353 _smart_ptr<IRenderMesh> pOutRenderMesh;
1355 // Create renderable mesh.
1356 if (gEnv->pRenderer)
1358 if (!pMesh)
1359 return 0;
1360 if (pMesh->GetSubSetCount() == 0)
1361 return 0;
1363 size_t nRenderMeshSize = ~0U;
1364 if (bDoRenderMesh)
1366 pOutRenderMesh = GetRenderer()->CreateRenderMesh("StatObj", m_szFileName.c_str());
1368 if (m_idmatBreakable >= 0 || m_bBreakableByGame)
1370 // need to keep mesh data in system memory for breakable meshes
1371 pOutRenderMesh->KeepSysMesh(true);
1374 // we cannot use FSM_CREATE_DEVICE_MESH flag since we can have an async call to the renderer!
1376 uint32 nFlags = 0;
1377 nFlags |= GetCVars()->e_StreamCgf ? 0 : FSM_CREATE_DEVICE_MESH;
1378 nFlags |= (!GetCVars()->e_StreamCgf && Get3DEngine()->m_bInLoad) ? FSM_SETMESH_ASYNC : 0;
1379 #ifdef MESH_TESSELLATION_ENGINE
1380 nFlags |= FSM_ENABLE_NORMALSTREAM;
1381 #endif
1382 nRenderMeshSize = pOutRenderMesh->SetMesh(*pMesh, 0, nFlags, NULL, true);
1383 if (nRenderMeshSize == ~0U)
1384 return 0;
1387 bool arrMaterialSupportsTeselation[32];
1388 ZeroStruct(arrMaterialSupportsTeselation);
1391 m_nRenderMeshMemoryUsage = (nRenderMeshSize == ~0U) ? pMesh->EstimateRenderMeshMemoryUsage() : nRenderMeshSize;
1392 //m_nRenderMeshMemoryUsage = pMesh->EstimateRenderMeshMemoryUsage();
1395 return pOutRenderMesh;
1398 static inline CNodeCGF* CreateNodeCGF(CContentCGF* pCGF, CStatObj* pStatObj, const char* name, CNodeCGF* pParent, CMaterialCGF* pMaterial, const Matrix34& localTM = Matrix34(IDENTITY), const char* properties = 0)
1400 CNodeCGF* pNode = NULL;
1402 // Add single node for merged mesh.
1403 pNode = new CNodeCGF;
1405 if (!pNode)
1407 CryLog("SaveToCgf: failed to allocate CNodeCGF aborting");
1408 return 0;
1411 cry_sprintf(pNode->name, "%s", name);
1412 pNode->properties = properties;
1413 pNode->localTM = localTM;
1414 pNode->worldTM = pParent ? pParent->worldTM * localTM : localTM;
1415 pNode->bIdentityMatrix = localTM.IsIdentity();
1416 pNode->pParent = pParent;
1417 pNode->pMaterial = pMaterial;
1418 pNode->nPhysicalizeFlags = 0;
1420 if (pStatObj && pStatObj->GetIndexedMesh())
1422 pNode->pMesh = new CMesh;
1423 pNode->pMesh->CopyFrom(*(pStatObj->GetIndexedMesh()->GetMesh()));
1424 pNode->pMesh->m_bbox = pStatObj->GetAABB();
1425 pNode->type = CNodeCGF::NODE_MESH;
1427 else
1428 pNode->type = CNodeCGF::NODE_HELPER;
1430 if (pStatObj)
1431 pStatObj->SavePhysicalizeData(pNode);
1432 pCGF->AddNode(pNode);
1434 const int subobjCount = pStatObj ? pStatObj->GetSubObjectCount() : 0;
1435 std::vector<CNodeCGF*> nodes;
1436 nodes.resize(subobjCount);
1437 for (int subidx = 0; subidx < subobjCount; ++subidx)
1438 nodes[subidx] = pNode;
1439 for (int subidx = 0; subidx < subobjCount; ++subidx)
1441 IStatObj::SSubObject* pSubObj = pStatObj->GetSubObject(subidx);
1442 if (!(nodes[subidx] = CreateNodeCGF(pCGF, (CStatObj*)pSubObj->pStatObj, pSubObj->pStatObj ? pSubObj->pStatObj->GetGeoName() : pSubObj->name.c_str(),
1443 pSubObj->nParent >= 0 ? nodes[pSubObj->nParent] : pNode, pMaterial, pSubObj->localTM, pSubObj->properties)))
1444 pNode = 0;
1446 return pNode;
1449 //////////////////////////////////////////////////////////////////////////
1450 // Save statobj to the CGF file.
1451 bool CStatObj::SaveToCGF(const char* sFilename, IChunkFile** pOutChunkFile, bool bHavePhiscalProxy)
1453 #if defined(INCLUDE_SAVECGF)
1454 CContentCGF* pCGF = new CContentCGF(sFilename);
1456 pCGF->GetExportInfo()->bCompiledCGF = true;
1457 pCGF->GetExportInfo()->bMergeAllNodes = (GetSubObjectCount() <= 0);
1458 pCGF->GetExportInfo()->bHavePhysicsProxy = bHavePhiscalProxy;
1459 cry_strcpy(pCGF->GetExportInfo()->rc_version_string, "From Sandbox");
1461 CChunkFile* pChunkFile = new CChunkFile();
1462 if (pOutChunkFile)
1463 *pOutChunkFile = pChunkFile;
1465 CMaterialCGF* pMaterialCGF = new CMaterialCGF;
1466 if (m_pMaterial)
1467 cry_strcpy(pMaterialCGF->name, m_pMaterial->GetName());
1468 else
1469 pMaterialCGF->name[0] = 0;
1470 pMaterialCGF->nPhysicalizeType = PHYS_GEOM_TYPE_DEFAULT;
1471 pMaterialCGF->bOldMaterial = false;
1472 pMaterialCGF->nChunkId = 0;
1474 // Array of sub materials.
1475 //std::vector<CMaterialCGF*> subMaterials;
1477 bool bResult = false;
1478 if (CreateNodeCGF(pCGF, this, GetGeoName() ? GetGeoName() : "Merged", NULL, pMaterialCGF))
1480 CSaverCGF cgfSaver(*pChunkFile);
1482 const bool bNeedEndianSwap = false;
1483 const bool bUseQtangents = false;
1484 const bool bStorePositionsAsF16 = false;
1485 const bool bStoreIndicesAsU16 = (sizeof(vtx_idx) == sizeof(uint16));
1487 cgfSaver.SaveContent(pCGF, bNeedEndianSwap, bStorePositionsAsF16, bUseQtangents, bStoreIndicesAsU16);
1489 bResult = true;
1492 if (!pOutChunkFile && bResult)
1494 bResult = pChunkFile->Write(sFilename);
1495 pChunkFile->Release();
1498 delete pCGF;
1500 return bResult;
1501 #else // #if defined(INCLUDE_SAVECGF)
1502 #if !defined(_RELEASE)
1503 __debugbreak();
1504 #endif
1505 return false;
1506 #endif
1509 //////////////////////////////////////////////////////////////////////////
1510 inline char* trim_whitespaces(char* str, char* strEnd)
1512 char* first = str;
1513 while (first < strEnd && (*first == ' ' || *first == '\t'))
1514 first++;
1515 char* s = strEnd - 1;
1516 while (s >= first && (*s == ' ' || *s == '\t'))
1517 *s-- = 0;
1518 return first;
1521 //////////////////////////////////////////////////////////////////////////
1522 void CStatObj::ParseProperties()
1524 FUNCTION_PROFILER_3DENGINE;
1526 int nLen = m_szProperties.size();
1527 if (nLen >= 4090)
1529 Warning("CGF '%s' have longer then 4K geometry info file", m_szFileName.c_str());
1530 nLen = 4090;
1533 char properties[4096];
1534 memcpy(properties, m_szProperties.c_str(), nLen);
1535 properties[nLen] = 0;
1537 char* str = properties;
1538 char* strEnd = str + nLen;
1539 while (str < strEnd)
1541 char* line = str;
1542 while (str < strEnd && *str != '\n' && *str != '\r')
1543 str++;
1544 char* lineEnd = str;
1545 *lineEnd = 0;
1546 str++;
1547 while (str < strEnd && (*str == '\n' || *str == '\r')) // Skip all \r\n at end.
1548 str++;
1550 if (*line == '/' || *line == '#') // skip comments
1552 continue;
1555 if (line < lineEnd)
1557 // Parse line.
1558 char* l = line;
1559 while (l < lineEnd && *l != '=')
1560 l++;
1561 if (l < lineEnd)
1563 *l = 0;
1564 char* left = line;
1565 char* right = l + 1;
1567 // remove white spaces from left and right.
1568 left = trim_whitespaces(left, l);
1569 right = trim_whitespaces(right, lineEnd);
1571 //////////////////////////////////////////////////////////////////////////
1572 if (0 == strcmp(left, "mass"))
1574 m_phys_mass = (float)atof(right);
1576 else if (0 == strcmp(left, "density"))
1578 m_phys_density = (float)atof(right);
1580 //////////////////////////////////////////////////////////////////////////
1583 else
1585 // There`s no = on the line, must be a flag.
1586 //////////////////////////////////////////////////////////////////////////
1587 if (0 == strcmp(line, "entity"))
1589 // pickable
1590 m_nFlags |= STATIC_OBJECT_SPAWN_ENTITY;
1592 else if (0 == strcmp(line, "no_player_collide"))
1594 m_nFlags |= STATIC_OBJECT_NO_PLAYER_COLLIDE;
1596 else if (0 == strcmp(line, "pickable"))
1598 m_nFlags |= STATIC_OBJECT_PICKABLE;
1600 else if (0 == strcmp(line, "no_auto_hidepoints"))
1602 m_nFlags |= STATIC_OBJECT_NO_AUTO_HIDEPOINTS;
1604 else if (0 == strcmp(line, "dynamic"))
1606 m_nFlags |= STATIC_OBJECT_DYNAMIC;
1608 else if (0 == strcmp(line, "no_hit_refinement"))
1610 m_bNoHitRefinement = true;
1611 for (int i = m_arrPhysGeomInfo.GetGeomCount() - 1; i >= 0; i--)
1612 m_arrPhysGeomInfo[i]->pGeom->SetForeignData(0, 0);
1614 else if (0 == strcmp(line, "no_explosion_occlusion"))
1616 m_bDontOccludeExplosions = true;
1618 //////////////////////////////////////////////////////////////////////////
1624 //////////////////////////////////////////////////////////////////////////
1625 void CStatObj::CheckCreateBillboardMaterial()
1627 // Check if billboard textures exist
1628 const char * arrTextureSuffixes[2] = { "_billbalb.dds", "_billbnorm.dds" };
1629 int nBillboardTexturesFound = 0;
1630 assert(EFTT_DIFFUSE == 0 && EFTT_NORMALS == 1);
1631 for (int nSlot = EFTT_DIFFUSE; nSlot <= EFTT_NORMALS; nSlot++)
1633 string szTextureName = m_szFileName;
1634 if (szTextureName.find(".cgf") != string::npos)
1636 szTextureName.replace(".cgf", arrTextureSuffixes[nSlot]);
1637 if (gEnv->pCryPak->IsFileExist(szTextureName.c_str()))
1638 nBillboardTexturesFound++;
1642 // create billboard material and cgf
1643 if (nBillboardTexturesFound == 2)
1645 m_pBillboardMaterial = GetMatMan()->LoadMaterial("%ENGINE%/EngineAssets/Materials/billboard_default", false);
1647 if (m_pBillboardMaterial)
1649 // clone reference material and assign new textures
1650 m_pBillboardMaterial = GetMatMan()->CloneMaterial(m_pBillboardMaterial);
1652 SShaderItem& shaderItem = m_pBillboardMaterial->GetShaderItem();
1653 SInputShaderResources* inputShaderResources = gEnv->pRenderer->EF_CreateInputShaderResource(shaderItem.m_pShaderResources);
1654 for (int nSlot = EFTT_DIFFUSE; nSlot <= EFTT_NORMALS; nSlot++)
1656 string szTextureName = m_szFileName;
1657 szTextureName.replace(".cgf", arrTextureSuffixes[nSlot]);
1658 inputShaderResources->m_Textures[nSlot].m_Name = szTextureName;
1660 SShaderItem newShaderItem = gEnv->pRenderer->EF_LoadShaderItem(shaderItem.m_pShader->GetName(), false, 0, inputShaderResources, shaderItem.m_pShader->GetGenerationMask());
1661 m_pBillboardMaterial->AssignShaderItem(newShaderItem);