1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 // -------------------------------------------------------------------------
4 // File name: statobjconstr.cpp
6 // Created: 28/5/2001 by Vladimir Kajalin
7 // Compilers: Visual Studio.NET
8 // Description: loading
9 // -------------------------------------------------------------------------
12 ////////////////////////////////////////////////////////////////////////////
17 #include "IndexedMesh.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;
42 GetObjManager()->UnregisterForGarbage(this);
45 const int oldModificationId
= m_nModificationId
;
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);
59 // load default in case of error
62 LoadCGF(DEFAULT_CGF_NAME
, 0, 0, 0, 0);
63 m_bDefaultObject
= true;
70 void CStatObj::LoadLowLODs(bool bUseStreaming
, unsigned long nLoadingFlags
)
72 if (!LoadLowLODS_Prep(bUseStreaming
, nLoadingFlags
))
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);
87 loadedLods
[nLodLevel
] = pLodStatObj
;
90 LoadLowLODS_Finalize(nLoadedLods
, loadedLods
);
93 bool CStatObj::LoadLowLODS_Prep(bool bUseStreaming
, unsigned long nLoadingFlags
)
97 if (nLoadingFlags
& ELoadingFlagsIgnoreLoDs
)
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];
111 cry_strcpy(sLodFileName
, m_szFileName
);
112 char* sPointSeparator
= strchr(sLodFileName
, '.');
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;
129 if (m_nLoadedLodsNum
> 1)
132 m_nLoadedLodsNum
= 1;
134 if (!GetCVars()->e_Lods
)
137 if (m_bSubObject
) // Never do this for sub objects.
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];
150 cry_strcpy(sLodFileName
, m_szFileName
);
151 char* sPointSeparator
= strchr(sLodFileName
, '.');
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
;
165 pLodStatObj
= stl::find_in_map(m_pObjManager
->m_nameToObjectMap
, CONST_TEMP_STRING(sLodFileName
), NULL
);
169 pLodStatObj
->m_pLod0
= this; // Must be here.
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
))
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
);
196 if ((m_pLODs
? (CStatObj
*)m_pLODs
[nLodLevel
] : (CStatObj
*)NULL
) != pLodStatObj
)
198 SAFE_RELEASE(pLodStatObj
);
200 SetLodObject(nLodLevel
, 0);
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
);
218 void CStatObj::LoadLowLODS_Finalize(int nLoadedLods
, CStatObj
* loadedLods
[MAX_STATOBJ_LODS_NUM
])
220 //////////////////////////////////////////////////////////////////////////
221 // Put LODs into the sub objects.
222 //////////////////////////////////////////////////////////////////////////
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
)
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
);
249 pSubStatObj
->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
);
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
))
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
;
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 //////////////////////////////////////////////////////////////////////////
344 //////////////////////////////////////////////////////////////////////////
345 class Listener
: public ILoaderCGFListener
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;
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
);
362 bLoadedChunks
= chunkFile
.ReadFromMemory(pData
, nDataSize
);
365 bLoaded
= cgfLoader
.LoadCGF(contentContainer
.get(), m_szFileName
, chunkFile
, &listener
, 0);
369 FileWarning(0, m_szFileName
, "CGF Streaming Failed: %s", cgfLoader
.GetLastError());
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
);
385 bool bNodeIsValidMesh
= (pNode
->type
== CNodeCGF::NODE_MESH
|| (pNode
->type
== CNodeCGF::NODE_HELPER
&& pNode
->helperType
== HP_GEOMETRY
));
386 if (!bNodeIsValidMesh
)
389 CStatObj
* pStatObj
= 0;
390 for (int s
= 0; s
< nSubObjCount
&& !pStatObj
; s
++)
392 CStatObj
* pSubStatObj
= (CStatObj
*)m_subObjects
[s
].pStatObj
;
395 for (int nLod
= 0; nLod
< MAX_STATOBJ_LODS_NUM
; nLod
++)
397 CStatObj
* pSubLod
= (CStatObj
*)pSubStatObj
->GetLodObject(nLod
);
400 if (0 == strcmp(pSubLod
->m_cgfNodeName
.c_str(), pNode
->name
))
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
);
416 if (0 == strcmp(pLod
->m_cgfNodeName
.c_str(), pNode
->name
))
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 //////////////////////////////////////////////////////////////////////////
456 //////////////////////////////////////////////////////////////////////////
457 void CStatObj::CommitStreamRenderMeshes()
459 if (m_pStreamedRenderMesh
)
461 CryAutoCriticalSection
lock(m_streamingMeshLock
);
462 SetRenderMesh(m_pStreamedRenderMesh
);
463 m_pStreamedRenderMesh
= 0;
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
;
485 pSubObj
->CommitStreamRenderMeshes();
490 bool CStatObj::IsDeformable()
492 if (Cry3DEngineBase::GetCVars()->e_MergedMeshes
== 0)
495 // Create deformable subobject is present
501 for (int i
= 0, n
= GetSubObjectCount(); i
< n
; ++i
)
503 IStatObj::SSubObject
* subObject
= GetSubObject(i
);
506 if (CStatObj
* pChild
= static_cast<CStatObj
*>(GetSubObject(i
)->pStatObj
))
508 if (pChild
->m_isDeformable
)
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.
526 #if INCLUDE_MEMSTAT_CONTEXTS
527 MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_CGF
, EMemStatContextFlags::MSF_Instance
, "%s", filename
);
530 PrintComment("Loading %s", filename
);
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());
546 if (m_bHasStreamOnlyCGF
)
548 if (!LoadCGF_Int(filename
, bLod
, nLoadingFlags
, pData
, nDataSize
))
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.");
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();
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 //////////////////////////////////////////////////////////////////////////
593 //////////////////////////////////////////////////////////////////////////
594 class Listener
: public ILoaderCGFListener
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
);
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;
621 if (chunkFile
.ReadFromMemory(pData
, nDataSize
))
622 bLoaded
= cgfLoader
.LoadCGF(contentContainer
.get(), filename
, chunkFile
, &listener
, nLoadingFlags
);
626 bLoaded
= cgfLoader
.LoadCGF(contentContainer
.get(), filename
, chunkFile
, &listener
, nLoadingFlags
);
630 if (!(nLoadingFlags
& IStatObj::ELoadingFlagsNoErrorIfFail
))
632 FileWarning(0, filename
, "CGF Loading Failed: %s", cgfLoader
.GetLastError());
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");
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
++;
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
))
684 bool bIsLod0Merged
= false;
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();
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");
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");
732 if (!pCGF
->ValidateMeshes(&pErrorDescription
))
734 FileWarning(0, filename
, "CGF has invalid meshes (%s)", pErrorDescription
);
735 assert(!"CGF has invalid meshes");
740 // Common of all sub nodes bbox.
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)
759 m_AABB
.min
= pFirstMeshNode
->meshInfo
.bboxMin
;
760 m_AABB
.max
= pFirstMeshNode
->meshInfo
.bboxMax
;
761 m_fGeometricMeanFaceArea
= pFirstMeshNode
->meshInfo
.fGeometricMean
;
763 m_nRenderTrisCount
= m_nLoadedTrisCount
= pFirstMeshNode
->meshInfo
.nIndices
/ 3;
764 m_nLoadedVertexCount
= pFirstMeshNode
->meshInfo
.nVerts
;
765 m_cgfNodeName
= pFirstMeshNode
->name
;
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);
778 // If mesh not known now try to estimate its memory usage.
779 m_nRenderMeshMemoryUsage
= CMesh::ApproximateRenderMeshMemoryUsage(pFirstMeshNode
->meshInfo
.nVerts
, pFirstMeshNode
->meshInfo
.nIndices
);
783 //////////////////////////////////////////////////////////////////////////
784 // Physicalize merged geometry.
785 //////////////////////////////////////////////////////////////////////////
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
)
818 if (pNode
->type
== CNodeCGF::NODE_MESH
)
820 if (pExportInfo
->bMergeAllNodes
|| m_nSubObjectMeshCount
== 0) // Only add helpers, ignore meshes.
823 else if (pNode
->type
== CNodeCGF::NODE_HELPER
)
825 switch (pNode
->helperType
)
829 if (strnicmp(pNode
->name
, CGF_NODE_NAME_LOD_PREFIX
, lodNamePrefixLength
) == 0)
838 m_subObjects
.reserve(nSubObjects
);
839 //////////////////////////////////////////////
842 for (int ii
= 0; ii
< pCGF
->GetNodeCount(); ii
++)
844 CNodeCGF
* pNode
= pCGF
->GetNode(ii
);
846 if (pNode
->bPhysicsProxy
)
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.
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
)
888 subObject
.nType
= STATIC_SUB_OBJECT_POINT
;
891 subObject
.nType
= STATIC_SUB_OBJECT_DUMMY
;
892 subObject
.helperSize
= (pNode
->helperSize
* 0.01f
);
895 subObject
.nType
= STATIC_SUB_OBJECT_XREF
;
898 subObject
.nType
= STATIC_SUB_OBJECT_CAMERA
;
902 subObject
.nType
= STATIC_SUB_OBJECT_HELPER_MESH
;
903 subObject
.bHidden
= true; // Helpers are not rendered.
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
;
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
)));
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
)
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
;
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
);
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
);
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.
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
]);
1038 //////////////////////////////////////////////////////////////////////////
1040 // Assign Parent nodes.
1041 for (int i
= 0; i
< nNumCgfNodes
; i
++)
1043 CNodeCGF
* pParentNode
= pNodes
[i
]->pParent
;
1046 for (int j
= 0; j
< nNumCgfNodes
; j
++)
1048 if (pNodes
[j
] == pParentNode
)
1050 m_subObjects
[i
].nParent
= j
;
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;
1070 m_subObjects
[i
].bHidden
= true;
1074 //////////////////////////////////////////////////////////////////////////
1078 if (m_nSubObjectMeshCount
> 0)
1080 m_AABB
= commonBBox
;
1084 //////////////////////////////////////////////////////////////////////////
1085 // Physicalize physics proxy nodes.
1086 //////////////////////////////////////////////////////////////////////////
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;
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
;
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
;
1123 m_nFlags
&= ~STATIC_OBJECT_COMPOUND
;
1125 if (!bLod
&& !m_szProperties
.empty())
1132 CPhysicalizeInfoCGF
* pPi
= pCGF
->GetPhysicalizeInfo();
1135 m_pLattice
= GetPhysicalWorld()->GetGeomManager()->CreateTetrLattice(pPi
->pRetVtx
, pPi
->nRetVtx
, pPi
->pRetTets
, pPi
->nRetTets
);
1139 if (m_bHasDeformationMorphs
)
1142 for (i
= GetSubObjectCount() - 1; i
>= 0; i
--)
1143 if ((j
= SubobjHasDeformMorph(i
)) >= 0)
1144 GetSubObject(i
)->pStatObj
->SetDeformationMorphTarget(GetSubObject(j
)->pStatObj
);
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
)
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();
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
));
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
;
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();
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
;
1287 _smart_ptr
<IRenderMesh
> pRenderMesh
= pStatObj
->MakeRenderMesh(pNode
->pMesh
, !m_bCanUnload
);
1288 pStatObj
->SetRenderMesh(pRenderMesh
);
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
;
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;
1312 //////////////////////////////////////////////////////////////////////////
1313 _smart_ptr
<IRenderMesh
> CStatObj::MakeRenderMesh(CMesh
* pMesh
, bool bDoRenderMesh
)
1315 FUNCTION_PROFILER_3DENGINE
;
1320 m_AABB
= pMesh
->m_bbox
;
1321 m_fGeometricMeanFaceArea
= pMesh
->m_geometricMeanFaceArea
;
1325 m_nLoadedTrisCount
= pMesh
->GetIndexCount() / 3;
1326 m_nLoadedVertexCount
= pMesh
->GetVertexCount();
1327 if (!m_nLoadedTrisCount
)
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))
1345 m_nRenderTrisCount
+= subset
.nNumIndices
/ 3;
1348 //////////////////////////////////////////////////////////////////////////
1350 if (!m_nRenderTrisCount
)
1353 _smart_ptr
<IRenderMesh
> pOutRenderMesh
;
1355 // Create renderable mesh.
1356 if (gEnv
->pRenderer
)
1360 if (pMesh
->GetSubSetCount() == 0)
1363 size_t nRenderMeshSize
= ~0U;
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!
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
;
1382 nRenderMeshSize
= pOutRenderMesh
->SetMesh(*pMesh
, 0, nFlags
, NULL
, true);
1383 if (nRenderMeshSize
== ~0U)
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
;
1407 CryLog("SaveToCgf: failed to allocate CNodeCGF aborting");
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
;
1428 pNode
->type
= CNodeCGF::NODE_HELPER
;
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
)))
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();
1463 *pOutChunkFile
= pChunkFile
;
1465 CMaterialCGF
* pMaterialCGF
= new CMaterialCGF
;
1467 cry_strcpy(pMaterialCGF
->name
, m_pMaterial
->GetName());
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
);
1492 if (!pOutChunkFile
&& bResult
)
1494 bResult
= pChunkFile
->Write(sFilename
);
1495 pChunkFile
->Release();
1501 #else // #if defined(INCLUDE_SAVECGF)
1502 #if !defined(_RELEASE)
1509 //////////////////////////////////////////////////////////////////////////
1510 inline char* trim_whitespaces(char* str
, char* strEnd
)
1513 while (first
< strEnd
&& (*first
== ' ' || *first
== '\t'))
1515 char* s
= strEnd
- 1;
1516 while (s
>= first
&& (*s
== ' ' || *s
== '\t'))
1521 //////////////////////////////////////////////////////////////////////////
1522 void CStatObj::ParseProperties()
1524 FUNCTION_PROFILER_3DENGINE
;
1526 int nLen
= m_szProperties
.size();
1529 Warning("CGF '%s' have longer then 4K geometry info file", m_szFileName
.c_str());
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
)
1542 while (str
< strEnd
&& *str
!= '\n' && *str
!= '\r')
1544 char* lineEnd
= str
;
1547 while (str
< strEnd
&& (*str
== '\n' || *str
== '\r')) // Skip all \r\n at end.
1550 if (*line
== '/' || *line
== '#') // skip comments
1559 while (l
< lineEnd
&& *l
!= '=')
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 //////////////////////////////////////////////////////////////////////////
1585 // There`s no = on the line, must be a flag.
1586 //////////////////////////////////////////////////////////////////////////
1587 if (0 == strcmp(line
, "entity"))
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
);