!B (CE-20767) Enabled opaque rendering and shadows for GPU particles. Added ZPassGPU...
[CRYENGINE.git] / Code / CryEngine / Cry3DEngine / StatObjConstr.cpp
blob68eea3d8eb038f5e452cfc8da28993a4b5dbc777
1 // Copyright 2001-2019 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: creation
9 // -------------------------------------------------------------------------
10 // History:
12 ////////////////////////////////////////////////////////////////////////////
14 #include "StdAfx.h"
16 #include "StatObj.h"
18 #include "IndexedMesh.h"
19 #include "../RenderDll/Common/Shadow_Renderer.h"
20 #include <CryRenderer/IRenderer.h>
21 #include <CryMemory/CrySizer.h>
22 #include "ObjMan.h"
23 #include "PolygonClipContext.h"
24 #include "MatMan.h"
25 #include "RenderMeshMerger.h"
26 #include "MergedMeshGeometry.h"
28 #define MAX_VERTICES_MERGABLE 15000
29 #define MAX_TRIS_IN_LOD_0 512
30 #define TRIS_IN_LOD_WARNING_RAIO (1.5f)
31 // Minimal ratio of Lod(n-1)/Lod(n) polygons to consider LOD for sub-object merging.
32 #define MIN_TRIS_IN_MERGED_LOD_RAIO (1.5f)
34 DEFINE_INTRUSIVE_LINKED_LIST(CStatObj)
36 //////////////////////////////////////////////////////////////////////////
37 CStatObj::CStatObj()
39 m_pAsyncUpdateContext = 0;
40 m_nNodeCount = 0;
41 m_nMergedMemoryUsage = 0;
42 m_nUsers = 0; // reference counter
43 m_nLastDrawMainFrameId = 0;
44 m_nFlags = 0;
45 m_pLODs = 0;
46 m_lastBooleanOpScale = 1.f;
47 m_fGeometricMeanFaceArea = 0.f;
48 m_fLodDistance = 0.0f;
50 Init();
53 //////////////////////////////////////////////////////////////////////////
54 void CStatObj::Init()
56 m_pAsyncUpdateContext = 0;
57 m_pIndexedMesh = 0;
58 m_lockIdxMesh = 0;
59 m_nRenderTrisCount = m_nLoadedTrisCount = m_nLoadedVertexCount = 0;
60 m_nRenderMatIds = 0;
61 m_fRadiusHors = 0;
62 m_fRadiusVert = 0;
63 m_pParentObject = 0;
64 m_pClonedSourceObject = 0;
65 m_bVehicleOnlyPhysics = 0;
66 m_bBreakableByGame = 0;
67 m_idmatBreakable = -1;
69 m_nLoadedLodsNum = 1;
70 m_nMinUsableLod0 = 0;
71 m_nMaxUsableLod0 = 0;
72 m_nMaxUsableLod = 0;
73 m_pLod0 = 0;
75 m_aiVegetationRadius = -1.0f;
76 m_phys_mass = -1.0f;
77 m_phys_density = -1.0f;
79 m_AABB = AABB(0.f);
80 m_vVegCenter.Set(0, 0, 0);
82 m_fGeometricMeanFaceArea = 0.f;
83 m_fLodDistance = 0.0f;
84 m_depthSortOffset.Set(0, 0, 0);
86 m_pRenderMesh = 0;
88 m_bDefaultObject = false;
90 if (m_pLODs)
91 for (int i = 0; i < MAX_STATOBJ_LODS_NUM; i++)
92 if (m_pLODs[i])
93 m_pLODs[i]->Init();
95 m_pReadStream = 0;
96 m_nModificationId = 0;
97 m_nSubObjectMeshCount = 0;
99 m_nRenderMeshMemoryUsage = 0;
100 m_arrRenderMeshesPotentialMemoryUsage[0] = m_arrRenderMeshesPotentialMemoryUsage[1] = -1;
102 m_bCanUnload = false;
103 m_bLodsLoaded = false;
104 m_bDefaultObject = false;
105 m_bOpenEdgesTested = false;
106 m_bSubObject = false;
107 m_bSharesChildren = false;
108 m_bHasDeformationMorphs = false;
109 m_bTmpIndexedMesh = false;
110 m_bMerged = false;
111 m_bMergedLODs = false;
112 m_bUnmergable = false;
113 m_bLowSpecLod0Set = false;
114 m_bHaveOcclusionProxy = false;
115 m_bCheckGarbage = false;
116 m_bLodsAreLoadedFromSeparateFile = false;
117 m_bNoHitRefinement = false;
118 m_bDontOccludeExplosions = false;
119 m_isDeformable = false;
120 m_isProxyTooBig = false;
121 m_bHasStreamOnlyCGF = true;
123 // Assign default material originally.
124 m_pMaterial = GetMatMan()->GetDefaultMaterial();
126 m_pLattice = 0;
127 m_pSpines = 0;
128 m_nSpines = 0;
129 m_pBoneMapping = 0;
130 m_pLastBooleanOp = 0;
131 m_pMapFaceToFace0 = 0;
132 m_pClothTangentsData = 0;
133 m_pSkinInfo = 0;
134 m_hasClothTangentsData = m_hasSkinInfo = 0;
135 m_pDelayedSkinParams = 0;
136 m_arrPhysGeomInfo.m_array.clear();
138 m_nInitialSubObjHideMask = 0;
140 #if !defined (_RELEASE)
141 m_fStreamingStart = 0.0f;
142 #endif
144 #ifdef OBJMAN_STREAM_STATS
145 m_nStatoscopeState = 0;
146 #endif
149 //////////////////////////////////////////////////////////////////////////
150 CStatObj::~CStatObj()
152 ShutDown();
155 void CStatObj::ShutDown()
157 if (m_pReadStream)
159 // We don't need this stream anymore.
160 m_pReadStream->Abort();
161 m_pReadStream = NULL;
164 SAFE_DELETE(m_pAsyncUpdateContext);
166 // assert (IsHeapValid());
168 SAFE_DELETE(m_pIndexedMesh);
169 // assert (IsHeapValid());
171 for (int n = 0; n < m_arrPhysGeomInfo.GetGeomCount(); n++)
172 if (m_arrPhysGeomInfo[n])
174 if (m_arrPhysGeomInfo[n]->pGeom->GetForeignData() == (void*)this)
175 m_arrPhysGeomInfo[n]->pGeom->SetForeignData(0, 0);
176 GetPhysicalWorld()->GetGeomManager()->UnregisterGeometry(m_arrPhysGeomInfo[n]);
178 m_arrPhysGeomInfo.m_array.clear();
180 _smart_ptr<IRenderMesh> pNullMesh = nullptr;
181 m_pStreamedRenderMesh = pNullMesh;
182 m_pMergedRenderMesh = pNullMesh;
183 SetRenderMesh(pNullMesh);
185 // assert (IsHeapValid());
187 /* // SDynTexture is not accessible for 3dengine
189 for(int i=0; i<FAR_TEX_COUNT; i++)
190 if(m_arrSpriteTexPtr[i])
191 m_arrSpriteTexPtr[i]->ReleaseDynamicRT(true);
193 for(int i=0; i<FAR_TEX_COUNT_60; i++)
194 if(m_arrSpriteTexPtr_60[i])
195 m_arrSpriteTexPtr_60[i]->ReleaseDynamicRT(true);
198 SAFE_RELEASE(m_pLattice);
200 if (m_pLODs)
201 for (int i = 0; i < MAX_STATOBJ_LODS_NUM; i++)
202 if (m_pLODs[i])
204 if (m_pLODs[i]->m_pParentObject)
205 GetObjManager()->UnregisterForStreaming(m_pLODs[i]->m_pParentObject);
206 else
207 GetObjManager()->UnregisterForStreaming(m_pLODs[i]);
209 // Sub objects do not own the LODs, so they should not delete them.
210 m_pLODs[i] = 0;
213 //////////////////////////////////////////////////////////////////////////
214 // Handle sub-objects and parents.
215 //////////////////////////////////////////////////////////////////////////
216 for (size_t i = 0; i < m_subObjects.size(); i++)
218 CStatObj* pChildObj = (CStatObj*)m_subObjects[i].pStatObj;
219 if (pChildObj)
221 if (!m_bSharesChildren)
222 pChildObj->m_pParentObject = NULL;
223 GetObjManager()->UnregisterForStreaming(pChildObj);
224 pChildObj->Release();
227 m_subObjects.clear();
229 if (m_pParentObject && !m_pParentObject->m_subObjects.empty())
231 // Remove this StatObject from sub-objects of the parent.
232 SSubObject* pSubObjects = &m_pParentObject->m_subObjects[0];
233 for (int i = 0, num = m_pParentObject->m_subObjects.size(); i < num; i++)
235 if (pSubObjects[i].pStatObj == this)
237 m_pParentObject->m_subObjects.erase(m_pParentObject->m_subObjects.begin() + i);
238 break;
243 //////////////////////////////////////////////////////////////////////////
245 FreeFoliageData();
247 if (m_pMapFaceToFace0)
248 delete[] m_pMapFaceToFace0;
249 m_pMapFaceToFace0 = 0;
250 if (m_hasClothTangentsData && !m_pClonedSourceObject)
251 delete[] m_pClothTangentsData;
252 if (m_hasSkinInfo && !m_pClonedSourceObject)
253 delete[] m_pSkinInfo;
254 m_pClothTangentsData = 0;
255 m_hasClothTangentsData = 0;
256 m_pSkinInfo = 0;
257 m_hasSkinInfo = 0;
259 SAFE_DELETE(m_pDelayedSkinParams);
261 SAFE_RELEASE(m_pClonedSourceObject);
263 GetObjManager()->UnregisterForStreaming(this);
265 SAFE_DELETE_ARRAY(m_pLODs);
268 //////////////////////////////////////////////////////////////////////////
269 int CStatObj::Release()
271 // Has to be thread safe, as it can be called by a worker thread for deferred plane breaks
272 int newRef = CryInterlockedDecrement(&m_nUsers);
273 if (newRef <= 1)
275 if (m_pParentObject && m_pParentObject->m_nUsers <= 0)
277 GetObjManager()->CheckForGarbage(m_pParentObject);
279 if (newRef <= 0)
281 GetObjManager()->CheckForGarbage(this);
284 return newRef;
287 //////////////////////////////////////////////////////////////////////////
288 void* CStatObj::operator new(size_t size)
290 CObjManager* pObjManager = GetObjManager();
291 assert(size == sizeof(CStatObj));
292 return pObjManager->AllocateStatObj();
295 void CStatObj::operator delete(void* pToFree)
297 CObjManager* pObjManager = GetObjManager();
298 pObjManager->FreeStatObj((CStatObj*)pToFree);
301 //////////////////////////////////////////////////////////////////////////
302 void CStatObj::FreeIndexedMesh()
304 if (!m_lockIdxMesh)
306 WriteLock lock(m_lockIdxMesh);
307 delete m_pIndexedMesh;
308 m_pIndexedMesh = 0;
312 //////////////////////////////////////////////////////////////////////////
313 void CStatObj::CalcRadiuses()
315 if (!m_AABB.min.IsValid() || !m_AABB.max.IsValid())
317 Error("CStatObj::CalcRadiuses: Invalid bbox, File name: %s", m_szFileName.c_str());
318 m_AABB.min.zero();
319 m_AABB.max.zero();
322 float dxh = (float)max(fabs(GetBoxMax().x), fabs(GetBoxMin().x));
323 float dyh = (float)max(fabs(GetBoxMax().y), fabs(GetBoxMin().y));
324 m_fRadiusHors = (float)sqrt_tpl(dxh * dxh + dyh * dyh);
325 m_fRadiusVert = (GetBoxMax().z - 0) * 0.5f;// never change this
326 m_vVegCenter = m_AABB.GetCenter();
327 m_vVegCenter.z = m_fRadiusVert;
330 void CStatObj::MakeRenderMesh()
332 if (gEnv->IsDedicated())
333 return;
335 FUNCTION_PROFILER_3DENGINE;
337 _smart_ptr<IRenderMesh> pNullMesh = nullptr;
338 SetRenderMesh(pNullMesh);
340 if (!m_pIndexedMesh || m_pIndexedMesh->GetSubSetCount() == 0)
341 return;
343 CMesh* pMesh = m_pIndexedMesh->GetMesh();
345 m_nRenderTrisCount = 0;
346 //////////////////////////////////////////////////////////////////////////
347 // Initialize Mesh subset material flags.
348 //////////////////////////////////////////////////////////////////////////
349 for (int i = 0; i < pMesh->GetSubSetCount(); i++)
351 SMeshSubset& subset = pMesh->m_subsets[i];
352 if (!(subset.nMatFlags & MTL_FLAG_NODRAW))
354 m_nRenderTrisCount += subset.nNumIndices / 3;
357 //////////////////////////////////////////////////////////////////////////
358 if (!m_nRenderTrisCount)
359 return;
361 if (!(GetFlags() & STATIC_OBJECT_DYNAMIC))
362 m_pRenderMesh = GetRenderer()->CreateRenderMesh("StatObj_Static", GetFilePath(), NULL, eRMT_Static);
363 else
365 m_pRenderMesh = GetRenderer()->CreateRenderMesh("StatObj_Dynamic", GetFilePath(), NULL, eRMT_Dynamic);
366 m_pRenderMesh->KeepSysMesh(true);
369 SMeshBoneMapping_uint16* const pBoneMap = pMesh->m_pBoneMapping;
370 pMesh->m_pBoneMapping = 0;
371 uint32 nFlags = 0;
372 nFlags |= (!GetCVars()->e_StreamCgf && Get3DEngine()->m_bInLoad) ? FSM_SETMESH_ASYNC : 0;
373 m_pRenderMesh->SetMesh(*pMesh, 0, nFlags, NULL, false);
374 pMesh->m_pBoneMapping = pBoneMap;
377 //////////////////////////////////////////////////////////////////////////
378 void CStatObj::SetMaterial(IMaterial* pMaterial)
380 m_pMaterial = pMaterial;
383 ///////////////////////////////////////////////////////////////////////////////////////
384 Vec3 CStatObj::GetHelperPos(const char* szHelperName)
386 SSubObject* pSubObj = FindSubObject(szHelperName);
387 if (!pSubObj)
388 return Vec3(0, 0, 0);
390 return Vec3(pSubObj->tm.m03, pSubObj->tm.m13, pSubObj->tm.m23);
393 ///////////////////////////////////////////////////////////////////////////////////////
394 const Matrix34& CStatObj::GetHelperTM(const char* szHelperName)
396 SSubObject* pSubObj = FindSubObject(szHelperName);
398 if (!pSubObj)
400 static Matrix34 identity(IDENTITY);
401 return identity;
404 return pSubObj->tm;
407 bool CStatObj::IsSameObject(const char* szFileName, const char* szGeomName)
409 // cmp object names
410 if (szGeomName)
412 if (stricmp(szGeomName, m_szGeomName) != 0)
413 return false;
416 // Normilize file name
417 char szFileNameNorm[MAX_PATH_LENGTH] = "";
418 char* pszDest = szFileNameNorm;
419 const char* pszSource = szFileName;
420 while (*pszSource)
422 if (*pszSource == '\\')
423 *pszDest++ = '/';
424 else
425 *pszDest++ = *pszSource;
426 pszSource++;
428 *pszDest = 0;
430 // cmp file names
431 if (stricmp(szFileNameNorm, m_szFileName) != 0)
432 return false;
434 return true;
437 void CStatObj::GetMemoryUsage(ICrySizer* pSizer) const
440 SIZER_COMPONENT_NAME(pSizer, "Self");
441 pSizer->AddObject(this, sizeof(*this));
445 SIZER_COMPONENT_NAME(pSizer, "subObjects");
446 pSizer->AddObject(m_subObjects);
450 SIZER_COMPONENT_NAME(pSizer, "Strings");
451 pSizer->AddObject(m_szFileName);
452 pSizer->AddObject(m_szGeomName);
453 pSizer->AddObject(m_szProperties);
457 SIZER_COMPONENT_NAME(pSizer, "Material");
458 pSizer->AddObject(m_pMaterial);
462 SIZER_COMPONENT_NAME(pSizer, "PhysGeomInfo");
463 pSizer->AddObject(m_arrPhysGeomInfo);
466 if (m_pLODs)
467 for (int i = 1; i < MAX_STATOBJ_LODS_NUM; i++)
469 SIZER_COMPONENT_NAME(pSizer, "StatObjLods");
470 pSizer->AddObject(m_pLODs[i]);
473 if (m_pIndexedMesh)
475 SIZER_COMPONENT_NAME(pSizer, "Mesh");
476 pSizer->AddObject(m_pIndexedMesh);
479 int nVtx = 0;
480 if (m_pIndexedMesh)
481 nVtx = m_pIndexedMesh->GetVertexCount();
482 else if (m_pRenderMesh)
483 nVtx = m_pRenderMesh->GetVerticesCount();
485 if (m_pSpines)
487 SIZER_COMPONENT_NAME(pSizer, "StatObj Foliage Data");
488 pSizer->AddObject(m_pSpines, sizeof(m_pSpines[0]), m_nSpines);
489 if (m_pBoneMapping)
490 pSizer->AddObject(m_pBoneMapping, sizeof(m_pBoneMapping[0]), nVtx);
491 for (int i = 0; i < m_nSpines; i++)
492 pSizer->AddObject(m_pSpines[i].pVtx, sizeof(Vec3) * 2 + sizeof(Vec4), m_pSpines[i].nVtx);
495 if (m_hasClothTangentsData && (!m_pClonedSourceObject || m_pClonedSourceObject == this))
497 SIZER_COMPONENT_NAME(pSizer, "Deformable StatObj ClothTangents");
498 pSizer->AddObject(m_pClothTangentsData, nVtx * sizeof(m_pClothTangentsData[0]));
501 if (m_hasSkinInfo && (!m_pClonedSourceObject || m_pClonedSourceObject == this))
503 SIZER_COMPONENT_NAME(pSizer, "Deformable StatObj SkinData");
504 pSizer->AddObject(m_pSkinInfo, (nVtx + 1) * sizeof(m_pSkinInfo[0]));
507 if (m_pMapFaceToFace0)
509 SIZER_COMPONENT_NAME(pSizer, "Deformable StatObj Mesh");
510 pSizer->AddObject(m_pMapFaceToFace0, sizeof(m_pMapFaceToFace0[0]) * max(m_nLoadedTrisCount, m_nRenderTrisCount));
514 //////////////////////////////////////////////////////////////////////////
515 void CStatObj::SetLodObject(int nLod, IStatObj* pObj)
517 assert(nLod > 0 && nLod < MAX_STATOBJ_LODS_NUM);
518 if (nLod <= 0 || nLod >= MAX_STATOBJ_LODS_NUM)
519 return;
521 CStatObj* pLod = (CStatObj*)pObj;
522 if (strstr(m_szProperties, "lowspeclod0"))
523 m_bLowSpecLod0Set = true;
525 bool bLodCompound = pLod && (pLod->GetFlags() & STATIC_OBJECT_COMPOUND) != 0;
527 if (pLod && !bLodCompound)
529 // Check if low lod decrease amount of used materials.
530 int nPrevLodMatIds = m_nRenderMatIds;
531 int nPrevLodTris = m_nLoadedTrisCount;
532 if (nLod > 1 && m_pLODs && m_pLODs[nLod - 1])
534 nPrevLodMatIds = m_pLODs[nLod - 1]->m_nRenderMatIds;
535 nPrevLodTris = m_pLODs[nLod - 1]->m_nLoadedTrisCount;
538 if (GetCVars()->e_LodsForceUse)
540 if ((int)m_nMaxUsableLod < nLod)
541 m_nMaxUsableLod = nLod;
543 else
545 int min_tris = GetCVars()->e_LodMinTtris;
546 if (((pLod->m_nLoadedTrisCount >= min_tris || nPrevLodTris >= (3 * min_tris) / 2)
547 || pLod->m_nRenderMatIds < nPrevLodMatIds) && nLod > (int)m_nMaxUsableLod)
549 m_nMaxUsableLod = nLod;
553 if (m_pParentObject && (m_pParentObject->m_nMaxUsableLod < m_nMaxUsableLod))
554 m_pParentObject->m_nMaxUsableLod = m_nMaxUsableLod;
556 pLod->m_pLod0 = this;
557 pLod->m_pMaterial = m_pMaterial; // Lod must use same material as parent.
559 if (pLod->m_nLoadedTrisCount > MAX_TRIS_IN_LOD_0)
561 if ((strstr(pLod->GetProperties(), "lowspeclod0") != 0) && !m_bLowSpecLod0Set)
563 m_bLowSpecLod0Set = true;
564 m_nMaxUsableLod0 = nLod;
566 if (!m_bLowSpecLod0Set)
567 m_nMaxUsableLod0 = nLod;
569 if (nLod + 1 > (int)m_nLoadedLodsNum)
570 m_nLoadedLodsNum = nLod + 1;
572 // When assigning lod to child object.
573 if (m_pParentObject)
575 if (nLod + 1 > (int)m_pParentObject->m_nLoadedLodsNum)
576 m_pParentObject->m_nLoadedLodsNum = nLod + 1;
580 if (pLod && bLodCompound)
582 m_nMaxUsableLod = nLod;
583 pLod->m_bUnmergable = m_bUnmergable;
586 if (!m_pLODs && pLod)
587 m_pLODs = new _smart_ptr<CStatObj>[MAX_STATOBJ_LODS_NUM];
589 if (m_pLODs)
590 m_pLODs[nLod] = pLod;
593 //////////////////////////////////////////////////////////////////////////
594 IStatObj* CStatObj::GetLodObject(int nLodLevel, bool bReturnNearest)
596 if (nLodLevel < 1)
598 return this;
601 if (!m_pLODs)
603 if (bReturnNearest)
604 return this;
605 else
606 return NULL;
609 if(bReturnNearest)
610 nLodLevel = min(nLodLevel, MAX_STATOBJ_LODS_NUM - 1);
612 CStatObj* pLod = 0;
613 if (nLodLevel < MAX_STATOBJ_LODS_NUM)
615 pLod = m_pLODs[nLodLevel];
617 if (bReturnNearest && !pLod)
619 for (int deltaLod = 1; deltaLod < MAX_STATOBJ_LODS_NUM; ++deltaLod)
621 int lower = nLodLevel - deltaLod;
622 if (0 < lower && m_pLODs[lower])
623 return m_pLODs[lower];
624 if (lower == 0)
625 return this;
627 int upper = nLodLevel + deltaLod;
628 if (upper < MAX_STATOBJ_LODS_NUM && m_pLODs[upper])
629 return m_pLODs[upper];
634 return pLod;
637 bool CStatObj::IsPhysicsExist() const
639 return m_arrPhysGeomInfo.GetGeomCount() > 0;
642 bool CStatObj::IsSphereOverlap(const Sphere& sSphere)
644 if (m_pRenderMesh && Overlap::Sphere_AABB(sSphere, m_AABB))
646 // if inside bbox
647 int nPosStride = 0;
648 int nInds = m_pRenderMesh->GetIndicesCount();
649 const byte* pPos = m_pRenderMesh->GetPosPtr(nPosStride, FSL_READ);
650 vtx_idx* pInds = m_pRenderMesh->GetIndexPtr(FSL_READ);
652 if (pInds && pPos)
653 for (int i = 0; (i + 2) < nInds; i += 3)
655 // test all triangles of water surface strip
656 Vec3 v0 = (*(Vec3*)&pPos[nPosStride * pInds[i + 0]]);
657 Vec3 v1 = (*(Vec3*)&pPos[nPosStride * pInds[i + 1]]);
658 Vec3 v2 = (*(Vec3*)&pPos[nPosStride * pInds[i + 2]]);
659 Vec3 vBoxMin = v0;
660 vBoxMin.CheckMin(v1);
661 vBoxMin.CheckMin(v2);
662 Vec3 vBoxMax = v0;
663 vBoxMax.CheckMax(v1);
664 vBoxMax.CheckMax(v2);
666 if (Overlap::Sphere_AABB(sSphere, AABB(vBoxMin, vBoxMax)))
667 return true;
671 return false;
674 //////////////////////////////////////////////////////////////////////////
675 void CStatObj::Invalidate(bool bPhysics, float tolerance)
677 if (m_pIndexedMesh)
679 m_pIndexedMesh->CalcBBox();
681 m_AABB = m_pIndexedMesh->m_bbox;
683 MakeRenderMesh();
684 m_nLoadedVertexCount = m_pIndexedMesh->GetVertexCount();
685 m_nLoadedTrisCount = m_pIndexedMesh->GetFaceCount();
686 if (!m_nLoadedTrisCount)
688 m_nLoadedTrisCount = m_pIndexedMesh->GetIndexCount() / 3;
690 CalcRadiuses();
692 if (bPhysics)
694 PhysicalizeGeomType(PHYS_GEOM_TYPE_DEFAULT, *m_pIndexedMesh->GetMesh(), tolerance, 0);
697 ReleaseIndexedMesh(true);
700 //////////////////////////////////////////////////////////////////////////
701 // Iterate through sub objects and update hide mask and sub obj mesh count.
702 //////////////////////////////////////////////////////////////////////////
703 m_nSubObjectMeshCount = 0;
704 for (size_t i = 0, numsub = m_subObjects.size(); i < numsub; i++)
706 SSubObject& subObj = m_subObjects[i];
707 if (subObj.pStatObj && subObj.nType == STATIC_SUB_OBJECT_MESH)
709 m_nSubObjectMeshCount++;
713 UnMergeSubObjectsRenderMeshes();
716 //////////////////////////////////////////////////////////////////////////
717 IStatObj* CStatObj::Clone(bool bCloneGeometry, bool bCloneChildren, bool bMeshesOnly)
719 if (m_bDefaultObject)
720 return this;
722 CStatObj* pNewObj = new CStatObj;
724 pNewObj->m_pClonedSourceObject = m_pClonedSourceObject ? m_pClonedSourceObject : this;
725 pNewObj->m_pClonedSourceObject->AddRef(); // Cloned object will keep a reference to this.
727 pNewObj->m_nLoadedTrisCount = m_nLoadedTrisCount;
728 pNewObj->m_nLoadedVertexCount = m_nLoadedVertexCount;
729 pNewObj->m_nRenderTrisCount = m_nRenderTrisCount;
731 if (bCloneGeometry)
733 if (m_bMerged)
735 UnMergeSubObjectsRenderMeshes();
737 if (m_pIndexedMesh && !m_bMerged)
739 pNewObj->m_pIndexedMesh = new CIndexedMesh;
740 pNewObj->m_pIndexedMesh->CopyFrom(*m_pIndexedMesh->GetMesh());
742 if (m_pRenderMesh && !m_bMerged)
744 _smart_ptr<IRenderMesh> pRenderMesh = GetRenderer()->CreateRenderMesh(
745 "StatObj_Cloned",
746 pNewObj->GetFilePath(),
747 NULL,
748 ((GetFlags() & STATIC_OBJECT_DYNAMIC) ? eRMT_Dynamic : eRMT_Static));
750 m_pRenderMesh->CopyTo(pRenderMesh);
751 pNewObj->SetRenderMesh(pRenderMesh);
754 else
756 if (m_pRenderMesh)
757 if (m_pMergedRenderMesh != m_pRenderMesh)
758 pNewObj->SetRenderMesh(m_pRenderMesh);
759 else
760 pNewObj->m_pRenderMesh = m_pRenderMesh;
761 pNewObj->m_pMergedRenderMesh = m_pMergedRenderMesh;
762 pNewObj->m_bMerged = m_pMergedRenderMesh != NULL;
765 pNewObj->m_szFileName = m_szFileName;
766 pNewObj->m_szGeomName = m_szGeomName;
768 // Default material.
769 pNewObj->m_pMaterial = m_pMaterial;
771 for (int i = 0; i < m_arrPhysGeomInfo.GetGeomCount(); i++)
773 pNewObj->m_arrPhysGeomInfo.SetPhysGeom(m_arrPhysGeomInfo[i], i, m_arrPhysGeomInfo.GetGeomType(i));
774 if (pNewObj->m_arrPhysGeomInfo[i])
775 GetPhysicalWorld()->GetGeomManager()->AddRefGeometry(pNewObj->m_arrPhysGeomInfo[i]);
777 pNewObj->m_phys_mass = m_phys_mass;
778 pNewObj->m_phys_density = m_phys_density;
779 pNewObj->m_AABB = m_AABB;
780 pNewObj->m_vVegCenter = m_vVegCenter;
782 pNewObj->m_fRadiusHors = m_fRadiusHors;
783 pNewObj->m_fRadiusVert = m_fRadiusVert;
785 pNewObj->m_nFlags = m_nFlags | STATIC_OBJECT_CLONE;
787 pNewObj->m_fGeometricMeanFaceArea = m_fGeometricMeanFaceArea;
788 pNewObj->m_fLodDistance = m_fLodDistance;
789 pNewObj->m_depthSortOffset = m_depthSortOffset;
791 //////////////////////////////////////////////////////////////////////////
792 // Internal Flags.
793 //////////////////////////////////////////////////////////////////////////
794 pNewObj->m_bCanUnload = false;
795 pNewObj->m_bDefaultObject = m_bDefaultObject;
796 pNewObj->m_bOpenEdgesTested = m_bOpenEdgesTested;
797 pNewObj->m_bSubObject = false; // [anton] since parent is not copied anyway
798 pNewObj->m_bVehicleOnlyPhysics = m_bVehicleOnlyPhysics;
799 pNewObj->m_idmatBreakable = m_idmatBreakable;
800 pNewObj->m_bBreakableByGame = m_bBreakableByGame;
801 pNewObj->m_bHasDeformationMorphs = m_bHasDeformationMorphs;
802 pNewObj->m_bTmpIndexedMesh = m_bTmpIndexedMesh;
803 pNewObj->m_bHaveOcclusionProxy = m_bHaveOcclusionProxy;
804 pNewObj->m_bHasStreamOnlyCGF = m_bHasStreamOnlyCGF;
805 pNewObj->m_eStreamingStatus = m_eStreamingStatus;
806 //////////////////////////////////////////////////////////////////////////
808 int numSubObj = (int)m_subObjects.size();
809 if (bMeshesOnly)
811 numSubObj = 0;
812 for (size_t i = 0; i < m_subObjects.size(); i++)
814 if (m_subObjects[i].nType == STATIC_SUB_OBJECT_MESH)
815 numSubObj++;
816 else
817 break;
820 pNewObj->m_subObjects.reserve(numSubObj);
821 for (int i = 0; i < numSubObj; i++)
823 pNewObj->m_subObjects.push_back(m_subObjects[i]);
824 pNewObj->m_subObjects[i].pFoliage = 0;
825 if (m_subObjects[i].pStatObj)
827 if (bCloneChildren)
829 pNewObj->m_subObjects[i].pStatObj = m_subObjects[i].pStatObj->Clone(bCloneGeometry, bCloneChildren, bMeshesOnly);
830 pNewObj->m_subObjects[i].pStatObj->AddRef();
831 ((CStatObj*)(pNewObj->m_subObjects[i].pStatObj))->m_pParentObject = this;
833 else
835 m_subObjects[i].pStatObj->AddRef();
836 ((CStatObj*)(m_subObjects[i].pStatObj))->m_nFlags |= STATIC_OBJECT_MULTIPLE_PARENTS;
840 pNewObj->m_nSubObjectMeshCount = m_nSubObjectMeshCount;
841 if (!bCloneChildren)
842 pNewObj->m_bSharesChildren = true;
844 if (pNewObj->m_hasClothTangentsData = m_hasClothTangentsData)
845 pNewObj->m_pClothTangentsData = m_pClothTangentsData;
846 if (pNewObj->m_hasSkinInfo = m_hasSkinInfo)
847 pNewObj->m_pSkinInfo = m_pSkinInfo;
849 return pNewObj;
851 //////////////////////////////////////////////////////////////////////////
853 IIndexedMesh* CStatObj::GetIndexedMesh(bool bCreateIfNone)
855 WriteLock lock(m_lockIdxMesh);
856 if (m_pIndexedMesh)
857 return m_pIndexedMesh;
858 else if (m_pRenderMesh && bCreateIfNone)
861 if (m_pRenderMesh->GetIndexedMesh(m_pIndexedMesh = new CIndexedMesh) == NULL)
863 // GetIndexMesh will free the IndexedMesh object if an allocation failed
864 m_pIndexedMesh = NULL;
865 return NULL;
868 CMesh* pMesh = m_pIndexedMesh->GetMesh();
869 if (!pMesh || pMesh->m_subsets.size() <= 0)
871 m_pIndexedMesh->Release();
872 m_pIndexedMesh = 0;
873 return 0;
875 m_bTmpIndexedMesh = true;
877 int i, j, i0 = pMesh->m_subsets[0].nFirstVertId + pMesh->m_subsets[0].nNumVerts;
878 for (i = j = 1; i < pMesh->m_subsets.size(); i++)
879 if (pMesh->m_subsets[i].nFirstVertId - i0 < pMesh->m_subsets[j].nFirstVertId - i0)
880 j = i;
881 if (j < pMesh->m_subsets.size() && pMesh->m_subsets[0].nPhysicalizeType == PHYS_GEOM_TYPE_DEFAULT &&
882 pMesh->m_subsets[j].nPhysicalizeType != PHYS_GEOM_TYPE_DEFAULT && pMesh->m_subsets[j].nFirstVertId > i0)
884 pMesh->m_subsets[j].nNumVerts += pMesh->m_subsets[j].nFirstVertId - i0;
885 pMesh->m_subsets[j].nFirstVertId = i0;
887 return m_pIndexedMesh;
889 return 0;
892 IIndexedMesh* CStatObj::CreateIndexedMesh()
894 if (!m_pIndexedMesh)
895 m_pIndexedMesh = new CIndexedMesh();
897 return m_pIndexedMesh;
900 void CStatObj::ReleaseIndexedMesh(bool bRenderMeshUpdated)
902 WriteLock lock(m_lockIdxMesh);
903 if (m_bTmpIndexedMesh && m_pIndexedMesh)
905 int istart, iend;
906 CMesh* pMesh = m_pIndexedMesh->GetMesh();
907 if (m_pRenderMesh && !bRenderMeshUpdated)
909 TRenderChunkArray& Chunks = m_pRenderMesh->GetChunks();
910 for (int i = 0; i < pMesh->m_subsets.size(); i++)
911 Chunks[i].m_nMatFlags |= pMesh->m_subsets[i].nMatFlags & 1 << 30;
913 if (bRenderMeshUpdated && m_pBoneMapping)
915 for (int i = iend = 0; i < (int)pMesh->m_subsets.size(); i++)
916 if ((pMesh->m_subsets[i].nMatFlags & (MTL_FLAG_NOPHYSICALIZE | MTL_FLAG_NODRAW)) == MTL_FLAG_NOPHYSICALIZE)
918 for (istart = iend++; iend < (int)m_chunkBoneIds.size() && m_chunkBoneIds[iend]; iend++)
920 if (!pMesh->m_subsets[i].nNumIndices)
922 m_chunkBoneIds.erase(m_chunkBoneIds.begin() + istart, m_chunkBoneIds.begin() + iend);
923 iend = istart;
927 m_pIndexedMesh->Release();
928 m_pIndexedMesh = 0;
929 m_bTmpIndexedMesh = false;
933 //////////////////////////////////////////////////////////////////////////
934 static bool SubObjectsOfCompoundHaveLOD(const CStatObj* pStatObj, int nLod)
936 int numSubObjects = pStatObj->GetSubObjectCount();
937 for (int i = 0; i < numSubObjects; ++i)
939 const IStatObj::SSubObject* pSubObject = const_cast<CStatObj*>(pStatObj)->GetSubObject(i);
940 if (!pSubObject)
941 continue;
942 const CStatObj* pChildObj = (const CStatObj*)pSubObject->pStatObj;
943 if (!pChildObj)
944 continue;
945 if (!pChildObj->m_pLODs)
946 continue;
947 const CStatObj* pLod = pChildObj->m_pLODs[nLod];
948 if (pLod)
949 return true;
951 return false;
954 //////////////////////////////////////////////////////////////////////////
955 void CStatObj::TryMergeSubObjects(bool bFromStreaming)
957 // sub meshes merging
958 if (GetCVars()->e_StatObjMerge)
960 if (!m_bUnmergable && !IsDeformable())
962 MergeSubObjectsRenderMeshes(bFromStreaming, this, 0);
964 if (!bFromStreaming && !m_pLODs && m_nFlags & STATIC_OBJECT_COMPOUND)
966 // Check if LODs were not split (production mode)
967 for (int i = 1; i < MAX_STATOBJ_LODS_NUM; ++i)
969 if (!SubObjectsOfCompoundHaveLOD(this, i))
970 continue;
972 CStatObj* pStatObj = new CStatObj();
973 pStatObj->m_szFileName = m_szFileName;
974 static_assert(MAX_STATOBJ_LODS_NUM < 10, "Increase size of lodName buffer");
975 char lodName[] = "-mlod?"; // '?' is a placeholder for the number
976 ltoa(i, &lodName[5], 10);
977 pStatObj->m_szFileName.append(lodName);
978 pStatObj->m_szGeomName = m_szGeomName;
979 pStatObj->m_bSubObject = true;
981 SetLodObject(i, pStatObj);
982 m_bMergedLODs = true;
986 if (m_pLODs)
988 for (int i = 1; i < MAX_STATOBJ_LODS_NUM; i++)
990 if (m_pLODs[i])
992 m_pLODs[i]->MergeSubObjectsRenderMeshes(bFromStreaming, this, i);
1000 //////////////////////////////////////////////////////////////////////////
1001 void CStatObj::MergeSubObjectsRenderMeshes(bool bFromStreaming, CStatObj* pLod0, int nLod)
1003 if (m_bUnmergable)
1004 return;
1006 MEMSTAT_CONTEXT(EMemStatContextType::Other, "Merged StatObj");
1007 FUNCTION_PROFILER_3DENGINE;
1009 m_bMerged = false;
1010 m_pMergedRenderMesh = 0;
1012 int nSubObjCount = (int)pLod0->m_subObjects.size();
1014 std::vector<SRenderMeshInfoInput> lstRMI;
1016 SRenderMeshInfoInput rmi;
1018 rmi.pMat = m_pMaterial;
1019 rmi.mat.SetIdentity();
1020 rmi.pMesh = 0;
1021 rmi.pSrcRndNode = 0;
1023 for (int s = 0; s < nSubObjCount; s++)
1025 CStatObj::SSubObject* pSubObj = &pLod0->m_subObjects[s];
1026 if (pSubObj->pStatObj && pSubObj->nType == STATIC_SUB_OBJECT_MESH)
1028 CStatObj* pStatObj = (CStatObj*)pSubObj->pStatObj->GetLodObject(nLod, true); // Get lod, if not exist get lowest existing.
1029 if (pStatObj)
1031 CryAutoCriticalSection lock(pStatObj->m_streamingMeshLock);
1032 if (pStatObj->m_pRenderMesh || pStatObj->m_pStreamedRenderMesh)
1034 rmi.pMesh = pStatObj->m_pRenderMesh ? pStatObj->m_pRenderMesh : pStatObj->m_pStreamedRenderMesh;
1035 rmi.mat = pSubObj->tm;
1036 rmi.bIdentityMatrix = pSubObj->bIdentityMatrix;
1037 rmi.nSubObjectIndex = s;
1038 lstRMI.push_back(rmi);
1044 _smart_ptr<IRenderMesh> pMergedMesh = 0;
1045 if (lstRMI.size() == 1 && lstRMI[0].bIdentityMatrix)
1047 // If identity matrix and only mesh-subobject use it as a merged render mesh.
1048 pMergedMesh = rmi.pMesh;
1050 else if (!lstRMI.empty())
1052 SMergeInfo info;
1053 info.sMeshName = GetFilePath();
1054 info.sMeshType = "StatObj_Merged";
1055 info.bMergeToOneRenderMesh = true;
1056 info.pUseMaterial = m_pMaterial;
1057 pMergedMesh = GetSharedRenderMeshMerger()->MergeRenderMeshes(&lstRMI[0], lstRMI.size(), info);
1060 if (pMergedMesh)
1062 if (bFromStreaming)
1064 CryAutoCriticalSection lock(m_streamingMeshLock);
1065 m_pMergedRenderMesh = pMergedMesh;
1066 m_pStreamedRenderMesh = pMergedMesh;
1068 else
1070 m_pMergedRenderMesh = pMergedMesh;
1071 SetRenderMesh(pMergedMesh);
1074 m_bMerged = true;
1075 if (m_pLod0)
1077 // Make sure upmost lod is also marked to be merged.
1078 m_pLod0->m_bMerged = true;
1083 bool CStatObj::IsMatIDReferencedByObj(uint16 matID)
1085 //Check root obj
1086 if (IRenderMesh* pRenderMesh = GetRenderMesh())
1088 TRenderChunkArray& Chunks = pRenderMesh->GetChunks();
1090 for (int nChunkId = 0; nChunkId < Chunks.size(); nChunkId++)
1092 if (Chunks[nChunkId].m_nMatID == matID)
1094 return true;
1099 //check children
1100 int nSubObjCount = (int)m_subObjects.size();
1101 for (int s = 0; s < nSubObjCount; s++)
1103 CStatObj::SSubObject* pSubObj = &m_subObjects[s];
1105 if (pSubObj->pStatObj)
1107 CStatObj* pSubStatObj = (CStatObj*)pSubObj->pStatObj;
1109 if (IRenderMesh* pSubRenderMesh = pSubStatObj->GetRenderMesh())
1111 TRenderChunkArray& Chunks = pSubRenderMesh->GetChunks();
1113 for (int nChunkId = 0; nChunkId < Chunks.size(); nChunkId++)
1115 if (Chunks[nChunkId].m_nMatID == matID)
1117 return true;
1124 return false;
1127 //////////////////////////////////////////////////////////////////////////
1128 bool CStatObj::CanMergeSubObjects()
1130 if (gEnv->IsDedicated())
1131 return false;
1133 int nSubMeshes = 0;
1134 int nTotalVertexCount = 0;
1135 int nTotalTriCount = 0;
1136 int nTotalRenderChunksCount = 0;
1138 int nSubObjCount = (int)m_subObjects.size();
1139 for (int s = 0; s < nSubObjCount; s++)
1141 CStatObj::SSubObject* pSubObj = &m_subObjects[s];
1142 if (pSubObj->pStatObj && pSubObj->nType == STATIC_SUB_OBJECT_MESH && !pSubObj->bHidden)
1144 CStatObj* pStatObj = (CStatObj*)pSubObj->pStatObj;
1145 if (pStatObj->m_pMaterial != m_pMaterial || pStatObj->m_nSpines) // All materials must be same, and no bendable foliage
1147 return false;
1149 nSubMeshes++;
1150 nTotalVertexCount += pStatObj->m_nLoadedVertexCount;
1151 nTotalTriCount += pStatObj->m_nLoadedTrisCount;
1152 nTotalRenderChunksCount += pStatObj->m_nRenderMatIds;
1156 // Check for mat_breakable surface type in material
1157 if (m_pMaterial)
1159 int nSubMtls = m_pMaterial->GetSubMtlCount();
1160 if (nSubMtls > 0)
1162 for (int i = 0; i < nSubMtls; ++i)
1164 IMaterial* pSubMtl = m_pMaterial->GetSafeSubMtl(i);
1165 if (pSubMtl)
1167 ISurfaceType* pSFType = pSubMtl->GetSurfaceType();
1169 // This is breakable glass.
1170 // Do not merge meshes that have procedural physics breakability in them.
1171 if (pSFType && pSFType->GetBreakability() != 0)
1173 //if mesh is streamed, we must assume the material is referenced
1174 if (m_bMeshStrippedCGF)
1176 return false;
1178 else if (IsMatIDReferencedByObj((uint16)i))
1180 return false;
1186 else // nSubMtls==0
1188 ISurfaceType* pSFType = m_pMaterial->GetSurfaceType();
1190 // This is breakable glass.
1191 // Do not merge meshes that have procedural physics breakability in them.
1192 if (pSFType && pSFType->GetBreakability() != 0)
1194 return false;
1199 if (nTotalVertexCount > MAX_VERTICES_MERGABLE || nSubMeshes <= 1 || nTotalRenderChunksCount <= 1)
1201 return false;
1203 if ((nTotalTriCount / nTotalRenderChunksCount) > GetCVars()->e_StatObjMergeMaxTrisPerDrawCall)
1205 return false; // tris to draw calls ratio is already not so bad
1207 return true;
1210 //////////////////////////////////////////////////////////////////////////
1211 void CStatObj::UnMergeSubObjectsRenderMeshes()
1213 _smart_ptr<IRenderMesh> pNullMesh = nullptr;
1214 if (m_bMerged)
1216 m_bMerged = false;
1217 m_pMergedRenderMesh = 0;
1218 SetRenderMesh(pNullMesh);
1220 if (m_bMergedLODs)
1222 delete[] m_pLODs;
1223 m_pLODs = 0;
1227 //------------------------------------------------------------------------------
1228 // This function is recovered from previous FindSubObject function which was then
1229 // changed and creating many CGA model issues (some joints never move).
1230 CStatObj::SSubObject* CStatObj::FindSubObject_CGA(const char* sNodeName)
1232 uint32 numSubObjects = m_subObjects.size();
1233 for (uint32 i = 0; i < numSubObjects; i++)
1235 if (stricmp(m_subObjects[i].name.c_str(), sNodeName) == 0)
1237 return &m_subObjects[i];
1240 return 0;
1243 //////////////////////////////////////////////////////////////////////////
1244 CStatObj::SSubObject* CStatObj::FindSubObject(const char* sNodeName)
1246 uint32 numSubObjects = m_subObjects.size();
1247 int len = 1; // some objects has ' ' in the beginning
1248 for (; sNodeName[len] > ' ' && sNodeName[len] != ',' && sNodeName[len] != ';'; len++)
1250 for (uint32 i = 0; i < numSubObjects; i++)
1252 if (strnicmp(m_subObjects[i].name.c_str(), sNodeName, len) == 0 && m_subObjects[i].name[len] == 0)
1254 return &m_subObjects[i];
1257 return 0;
1260 //////////////////////////////////////////////////////////////////////////
1261 CStatObj::SSubObject* CStatObj::FindSubObject_StrStr(const char* sNodeName)
1263 uint32 numSubObjects = m_subObjects.size();
1264 for (uint32 i = 0; i < numSubObjects; i++)
1266 if (stristr(m_subObjects[i].name.c_str(), sNodeName))
1268 return &m_subObjects[i];
1271 return 0;
1274 //////////////////////////////////////////////////////////////////////////
1275 inline void InitializeSubObject(IStatObj::SSubObject& so)
1277 so.localTM.SetIdentity();
1278 so.name = "";
1279 so.properties = "";
1280 so.nType = STATIC_SUB_OBJECT_MESH;
1281 so.pWeights = 0;
1282 so.pFoliage = 0;
1283 so.nParent = -1;
1284 so.tm.SetIdentity();
1285 so.bIdentityMatrix = true;
1286 so.bHidden = false;
1287 so.helperSize = Vec3(0, 0, 0);
1288 so.pStatObj = 0;
1289 so.bShadowProxy = 0;
1292 //////////////////////////////////////////////////////////////////////////
1293 IStatObj::SSubObject& CStatObj::AddSubObject(IStatObj* pSubObj)
1295 assert(pSubObj);
1296 SetSubObjectCount(m_subObjects.size() + 1);
1297 InitializeSubObject(m_subObjects[m_subObjects.size() - 1]);
1298 m_subObjects[m_subObjects.size() - 1].pStatObj = pSubObj;
1299 pSubObj->AddRef();
1300 return m_subObjects[m_subObjects.size() - 1];
1303 //////////////////////////////////////////////////////////////////////////
1304 bool CStatObj::RemoveSubObject(int nIndex)
1306 if (nIndex >= 0 && nIndex < (int)m_subObjects.size())
1308 SSubObject* pSubObj = &m_subObjects[nIndex];
1309 CStatObj* pChildObj = (CStatObj*)pSubObj->pStatObj;
1310 if (pChildObj)
1312 if (!m_bSharesChildren)
1313 pChildObj->m_pParentObject = NULL;
1314 pChildObj->Release();
1316 m_subObjects.erase(m_subObjects.begin() + nIndex);
1317 Invalidate(true);
1318 return true;
1320 return false;
1323 //////////////////////////////////////////////////////////////////////////
1324 void CStatObj::SetSubObjectCount(int nCount)
1326 // remove sub objects.
1327 while ((int)m_subObjects.size() > nCount)
1329 RemoveSubObject(m_subObjects.size() - 1);
1332 SSubObject subobj;
1333 InitializeSubObject(subobj);
1334 m_subObjects.resize(nCount, subobj);
1336 if (nCount > 0)
1338 m_nFlags |= STATIC_OBJECT_COMPOUND;
1340 else
1342 m_nFlags &= ~STATIC_OBJECT_COMPOUND;
1344 Invalidate(true);
1345 UnMergeSubObjectsRenderMeshes();
1348 //////////////////////////////////////////////////////////////////////////
1349 bool CStatObj::CopySubObject(int nToIndex, IStatObj* pFromObj, int nFromIndex)
1351 SSubObject* pSrcSubObj = pFromObj->GetSubObject(nFromIndex);
1352 if (!pSrcSubObj)
1353 return false;
1355 if (nToIndex >= (int)m_subObjects.size())
1357 SetSubObjectCount(nToIndex + 1);
1358 if (pFromObj == this)
1359 pSrcSubObj = pFromObj->GetSubObject(nFromIndex);
1362 m_subObjects[nToIndex] = *pSrcSubObj;
1363 if (pSrcSubObj->pStatObj)
1364 pSrcSubObj->pStatObj->AddRef();
1365 m_subObjects[nToIndex].pFoliage = 0;
1367 Invalidate(true);
1369 return true;
1372 //////////////////////////////////////////////////////////////////////////
1373 bool CStatObj::GetPhysicalProperties(float& mass, float& density)
1375 mass = m_phys_mass;
1376 density = m_phys_density;
1377 if (mass < 0 && density < 0)
1378 return false;
1379 return true;
1382 //////////////////////////////////////////////////////////////////////////
1383 bool CStatObj::RayIntersection(SRayHitInfo& hitInfo, IMaterial* pCustomMtl)
1385 Vec3 vOut;
1387 bool bNonDirectionalTest = hitInfo.inRay.direction.IsZero();
1389 // First check if ray intersect objects bounding box.
1390 if (!bNonDirectionalTest && !Intersect::Ray_AABB(hitInfo.inRay, m_AABB, vOut))
1391 return false;
1393 if (bNonDirectionalTest && !Overlap::AABB_AABB(
1394 AABB(hitInfo.inRay.origin - Vec3(hitInfo.fMaxHitDistance), hitInfo.inRay.origin + Vec3(hitInfo.fMaxHitDistance)),
1395 m_AABB))
1396 return false;
1398 _smart_ptr<IRenderMesh> pRenderMesh = m_pRenderMesh;
1400 // Sometimes, object has no base lod mesh. So need to hit test with low level mesh.
1401 // If the distance from camera is larger then a base lod distance, then base lod mesh is not loaded yet.
1402 assert(m_nMaxUsableLod < MAX_STATOBJ_LODS_NUM && (m_nMaxUsableLod == 0 || (m_pLODs && m_pLODs[m_nMaxUsableLod])));
1403 if (!pRenderMesh && m_nMaxUsableLod > 0 && m_pLODs && m_pLODs[m_nMaxUsableLod])
1404 pRenderMesh = m_pLODs[m_nMaxUsableLod]->GetRenderMesh();
1406 if ((m_nFlags & STATIC_OBJECT_COMPOUND) && !m_bMerged)
1408 SRayHitInfo hitOut;
1409 bool bAnyHit = false;
1410 float fMinDistance = FLT_MAX;
1411 for (int i = 0, num = m_subObjects.size(); i < num; i++)
1413 if (m_subObjects[i].pStatObj && m_subObjects[i].nType == STATIC_SUB_OBJECT_MESH && !m_subObjects[i].bHidden)
1415 if (((CStatObj*)(m_subObjects[i].pStatObj))->m_nFlags & STATIC_OBJECT_HIDDEN)
1416 continue;
1418 Matrix34 invertedTM = m_subObjects[i].tm.GetInverted();
1420 SRayHitInfo hit = hitInfo;
1422 // Transform ray into sub-object local space.
1423 hit.inReferencePoint = invertedTM.TransformPoint(hit.inReferencePoint);
1424 hit.inRay.origin = invertedTM.TransformPoint(hit.inRay.origin);
1425 hit.inRay.direction = invertedTM.TransformVector(hit.inRay.direction);
1427 #if defined(FEATURE_SVO_GI)
1428 int nFirstTriangleId = hit.pHitTris ? hit.pHitTris->Count() : 0;
1429 #endif
1431 if (((CStatObj*)m_subObjects[i].pStatObj)->RayIntersection(hit, pCustomMtl))
1433 if (hit.fDistance < fMinDistance)
1435 bAnyHit = true;
1436 hitOut = hit;
1440 #if defined(FEATURE_SVO_GI)
1441 // transform collected triangles from sub-object space into object space
1442 if (hit.pHitTris)
1444 for (int t = nFirstTriangleId; t < hit.pHitTris->Count(); t++)
1446 SRayHitTriangle& ht = hit.pHitTris->GetAt(t);
1447 for (int c = 0; c < 3; c++)
1448 ht.v[c] = m_subObjects[i].tm.TransformPoint(ht.v[c]);
1451 #endif
1454 if (bAnyHit)
1456 // Restore input ray/reference point.
1457 hitOut.inReferencePoint = hitInfo.inReferencePoint;
1458 hitOut.inRay = hitInfo.inRay;
1460 hitInfo = hitOut;
1461 return true;
1464 else
1466 if (pRenderMesh)
1468 bool bRes = CRenderMeshUtils::RayIntersection(pRenderMesh, hitInfo, pCustomMtl);
1469 return bRes;
1472 return false;
1475 bool CStatObj::LineSegIntersection(const Lineseg& lineSeg, Vec3& hitPos, int& surfaceTypeId)
1477 bool intersects = false;
1478 if (m_pRenderMesh)
1480 m_pRenderMesh->LockForThreadAccess();
1481 int numIndices = m_pRenderMesh->GetIndicesCount();
1482 int numVertices = m_pRenderMesh->GetVerticesCount();
1483 if (numIndices && numVertices)
1485 int posStride;
1486 uint8* pPositions = (uint8*)m_pRenderMesh->GetPosPtr(posStride, FSL_READ);
1487 vtx_idx* pIndices = m_pRenderMesh->GetIndexPtr(FSL_READ);
1489 TRenderChunkArray& Chunks = m_pRenderMesh->GetChunks();
1490 int nChunkCount = Chunks.size();
1491 for (int nChunkId = 0; nChunkId < nChunkCount; nChunkId++)
1493 CRenderChunk* pChunk = &Chunks[nChunkId];
1494 if (!(pChunk->m_nMatFlags & MTL_FLAG_NODRAW))
1496 int lastIndex = pChunk->nFirstIndexId + pChunk->nNumIndices;
1497 for (int i = pChunk->nFirstIndexId; i < lastIndex; )
1499 const Vec3& v0 = *(Vec3*)(pPositions + pIndices[i++] * posStride);
1500 const Vec3& v1 = *(Vec3*)(pPositions + pIndices[i++] * posStride);
1501 const Vec3& v2 = *(Vec3*)(pPositions + pIndices[i++] * posStride);
1503 if (Intersect::Lineseg_Triangle(lineSeg, v0, v2, v1, hitPos) || // Front face
1504 Intersect::Lineseg_Triangle(lineSeg, v0, v1, v2, hitPos)) // Back face
1506 IMaterial* pMtl = m_pMaterial->GetSafeSubMtl(pChunk->m_nMatID);
1507 surfaceTypeId = pMtl->GetSurfaceTypeId();
1508 intersects = true;
1509 break;
1515 m_pRenderMesh->UnlockStream(VSF_GENERAL);
1516 m_pRenderMesh->UnlockIndexStream();
1517 m_pRenderMesh->UnLockForThreadAccess();
1519 return intersects;
1522 void CStatObj::SetRenderMesh(_smart_ptr<IRenderMesh>& pRM)
1524 CRY_PROFILE_FUNCTION(PROFILE_LOADING_ONLY);
1526 if (pRM.get() == m_pRenderMesh.get())
1527 return;
1530 CryAutoCriticalSection lock(m_streamingMeshLock);
1531 m_pRenderMesh = pRM;
1533 IncrementModificationId();
1536 if (m_pRenderMesh)
1538 m_nRenderTrisCount = 0;
1539 m_nRenderMatIds = 0;
1541 TRenderChunkArray& Chunks = m_pRenderMesh->GetChunks();
1542 for (int nChunkId = 0; nChunkId < Chunks.size(); nChunkId++)
1544 CRenderChunk* pChunk = &Chunks[nChunkId];
1545 if (pChunk->m_nMatFlags & MTL_FLAG_NODRAW || !pChunk->pRE)
1546 continue;
1548 if (pChunk->nNumIndices > 0)
1550 m_nRenderMatIds++;
1551 m_nRenderTrisCount += pChunk->nNumIndices / 3;
1554 m_nLoadedTrisCount = pRM->GetIndicesCount() / 3;
1555 m_nLoadedVertexCount = pRM->GetVerticesCount();
1558 // this will prepare all deformable object during loading instead when needed.
1559 // Thus is will prevent runtime stalls (300ms when shooting a taxi with 6000 vertices)
1560 // but will incrase memory since every deformable information needs to be loaded(500kb for the taxi)
1561 // So this is more a workaround, the real solution would precompute the data in the RC and load
1562 // the data only when needed into memory
1563 if (GetCVars()->e_PrepareDeformableObjectsAtLoadTime && m_pRenderMesh && m_pDelayedSkinParams)
1565 PrepareSkinData(m_pDelayedSkinParams->mtxSkelToMesh, m_pDelayedSkinParams->pPhysSkel, m_pDelayedSkinParams->r);
1569 //////////////////////////////////////////////////////////////////////////
1570 int CStatObj::CountChildReferences()
1572 // Check if it must be released.
1573 int nChildRefs = 0;
1574 int numChilds = (int)m_subObjects.size();
1575 for (int i = 0; i < numChilds; i++)
1577 IStatObj::SSubObject& so = m_subObjects[i];
1578 CStatObj* pChildStatObj = (CStatObj*)so.pStatObj;
1579 if (!pChildStatObj) // All stat objects must be at the begining of the array//
1580 break;
1582 // if I'm the real parent of this child, check that no-one else is referencing it, otherwise it doesn't matter if I get deleted
1583 if (pChildStatObj->m_pParentObject == this)
1585 bool bIgnoreSharedStatObj = false;
1586 for (int k = 0; k < i; k++)
1588 if (pChildStatObj == m_subObjects[k].pStatObj)
1590 // If we share pointer to this stat obj then do not count it again.
1591 bIgnoreSharedStatObj = true;
1592 nChildRefs -= 1; // 1 reference from current object should be ignored.
1593 break;
1596 if (!bIgnoreSharedStatObj)
1598 nChildRefs += pChildStatObj->m_nUsers - 1; // 1 reference from parent should be ignored.
1602 assert(nChildRefs >= 0);
1603 return nChildRefs;
1606 IStatObj* CStatObj::GetLowestLod()
1608 if (int nLowestLod = CStatObj::GetMinUsableLod())
1609 return m_pLODs ? (CStatObj*)m_pLODs[nLowestLod] : (CStatObj*)NULL;
1610 return this;
1613 //////////////////////////////////////////////////////////////////////////
1614 int CStatObj::FindHighestLOD(int nBias)
1616 int nLowestLod = CStatObj::GetMinUsableLod();
1618 // if requested lod is not ready - find nearest ready one
1619 int nLod = CLAMP((int)m_nMaxUsableLod + nBias, nLowestLod, (int)m_nMaxUsableLod);
1621 while (nLod && nLod >= nLowestLod && (!m_pLODs || !m_pLODs[nLod] || !m_pLODs[nLod]->GetRenderMesh()))
1622 nLod--;
1624 if (nLod == 0 && !GetRenderMesh())
1625 nLod = -1;
1627 return nLod;
1630 //////////////////////////////////////////////////////////////////////////
1631 void CStatObj::GetStatisticsNonRecursive(SStatistics& si)
1633 CStatObj* pStatObj = this;
1635 for (int lod = 0; lod < MAX_STATOBJ_LODS_NUM; lod++)
1637 CStatObj* pLod = (CStatObj*)pStatObj->GetLodObject(lod);
1638 if (pLod)
1640 //if (!pLod0 && pLod->GetRenderMesh())
1641 //pLod0 = pLod;
1643 if (lod > 0 && lod + 1 > si.nLods) // Assign last existing lod.
1644 si.nLods = lod + 1;
1646 si.nVerticesPerLod[lod] += pLod->m_nLoadedVertexCount;
1647 si.nIndicesPerLod[lod] += pLod->m_nLoadedTrisCount * 3;
1648 si.nMeshSize += pLod->m_nRenderMeshMemoryUsage;
1650 IRenderMesh* pRenderMesh = pLod->GetRenderMesh();
1651 if (pRenderMesh)
1653 si.nMeshSizeLoaded += pRenderMesh->GetMemoryUsage(0, IRenderMesh::MEM_USAGE_ONLY_STREAMS);
1654 //if (si.pTextureSizer)
1655 //pRenderMesh->GetTextureMemoryUsage(0,si.pTextureSizer);
1656 //if (si.pTextureSizer2)
1657 //pRenderMesh->GetTextureMemoryUsage(0,si.pTextureSizer2);
1662 si.nIndices += m_nLoadedTrisCount * 3;
1663 si.nVertices += m_nLoadedVertexCount;
1665 for (int j = 0; j < pStatObj->m_arrPhysGeomInfo.GetGeomCount(); j++)
1667 if (pStatObj->GetPhysGeom(j) && pStatObj->GetPhysGeom(j)->pGeom)
1669 ICrySizer* pPhysSizer = GetISystem()->CreateSizer();
1670 pStatObj->GetPhysGeom(j)->pGeom->GetMemoryStatistics(pPhysSizer);
1671 int sz = pPhysSizer->GetTotalSize();
1672 si.nPhysProxySize += sz;
1673 si.nPhysProxySizeMax = max(si.nPhysProxySizeMax, sz);
1674 si.nPhysPrimitives += pStatObj->GetPhysGeom(j)->pGeom->GetPrimitiveCount();
1675 pPhysSizer->Release();
1680 //////////////////////////////////////////////////////////////////////////
1681 void CStatObj::GetStatistics(SStatistics& si)
1683 si.bSplitLods = m_bLodsAreLoadedFromSeparateFile;
1685 bool bMultiSubObj = (GetFlags() & STATIC_OBJECT_COMPOUND) != 0;
1686 if (!bMultiSubObj)
1688 GetStatisticsNonRecursive(si);
1689 si.nSubMeshCount = 0;
1690 si.nNumRefs = GetRefCount();
1691 si.nDrawCalls = m_nRenderMatIds;
1693 else
1695 si.nNumRefs = GetRefCount();
1697 std::vector<void*> addedObjects;
1698 for (int k = 0; k < GetSubObjectCount(); k++)
1700 if (!GetSubObject(k))
1701 continue;
1702 CStatObj* pSubObj = (CStatObj*)GetSubObject(k)->pStatObj;
1704 int nSubObjectType = GetSubObject(k)->nType;
1705 if (nSubObjectType != STATIC_SUB_OBJECT_MESH && nSubObjectType != STATIC_SUB_OBJECT_HELPER_MESH)
1706 continue;
1708 if (stl::find(addedObjects, pSubObj))
1709 continue;
1710 addedObjects.push_back(pSubObj);
1712 if (pSubObj)
1714 pSubObj->GetStatisticsNonRecursive(si);
1715 si.nSubMeshCount++;
1717 if (nSubObjectType == STATIC_SUB_OBJECT_MESH)
1719 si.nDrawCalls += pSubObj->m_nRenderMatIds;
1722 if (pSubObj->GetRefCount() > si.nNumRefs)
1723 si.nNumRefs = pSubObj->GetRefCount();
1728 // Only do rough estimation based on the material
1729 // This is more consistent when streaming is enabled and render mesh may no exist
1730 if (m_pMaterial)
1732 if (si.pTextureSizer)
1733 m_pMaterial->GetTextureMemoryUsage(si.pTextureSizer);
1734 if (si.pTextureSizer2)
1735 m_pMaterial->GetTextureMemoryUsage(si.pTextureSizer2);