1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 // -------------------------------------------------------------------------
4 // File name: statobjman.cpp
6 // Created: 28/5/2001 by Vladimir Kajalin
7 // Compilers: Visual Studio.NET
8 // Description: Loading trees, buildings, ragister/unregister entities for rendering
9 // -------------------------------------------------------------------------
12 ////////////////////////////////////////////////////////////////////////////
19 #include "terrain_sector.h"
20 #include "PolygonClipContext.h"
22 #include "IndexedMesh.h"
24 #include "Vegetation.h"
26 #include "ObjectsTree.h"
27 #include <CrySystem/File/IResourceManager.h>
28 #include "DecalRenderNode.h"
30 #define BRUSH_LIST_FILE "brushlist.txt"
31 #define CGF_LEVEL_CACHE_PAK "cgf.pak"
33 //Platform specific includes MemoryBarrier
34 #if defined(CRY_PLATFORM_WINDOWS) || defined(CRY_PLATFORM_DURANGO)
35 #include <CryCore/Platform/CryWindows.h>
38 //////////////////////////////////////////////////////////////////////////
39 IStatObj
* CObjManager::GetStaticObjectByTypeID(int nTypeID
)
41 if (nTypeID
>= 0 && nTypeID
< m_lstStaticTypes
.Count())
42 return m_lstStaticTypes
[nTypeID
].pStatObj
;
47 IStatObj
* CObjManager::FindStaticObjectByFilename(const char* filename
)
49 return stl::find_in_map(m_nameToObjectMap
, CONST_TEMP_STRING(filename
), NULL
);
52 void CObjManager::UnloadVegetationModels(bool bDeleteAll
)
54 PodArray
<StatInstGroup
>& rGroupTable
= m_lstStaticTypes
;
55 for (uint32 nGroupId
= 0; nGroupId
< rGroupTable
.size(); nGroupId
++)
57 StatInstGroup
& rGroup
= rGroupTable
[nGroupId
];
59 rGroup
.pStatObj
= NULL
;
60 rGroup
.pMaterial
= NULL
;
62 for (int j
= 0; j
< FAR_TEX_COUNT
; ++j
)
64 SVegetationSpriteLightInfo
& rLightInfo
= rGroup
.m_arrSSpriteLightInfo
[j
];
65 SAFE_RELEASE(rLightInfo
.m_pDynTexture
);
73 //////////////////////////////////////////////////////////////////////////
74 void CObjManager::UnloadObjects(bool bDeleteAll
)
76 UnloadVegetationModels(bDeleteAll
);
83 m_decalsToPrecreate
.resize(0);
85 // Clear all objects that are in the garbage collector.
86 ClearStatObjGarbage();
88 stl::free_container(m_checkForGarbage
);
89 m_bGarbageCollectionEnabled
= false;
93 m_lockedObjects
.clear(); // Lock/Unlock resources will not work with this.
95 // Release default stat obj.
98 m_nameToObjectMap
.clear();
99 m_lstLoadedObjects
.clear();
102 std::vector
<CStatObj
*> garbage
;
103 for (CStatObj
* pStatObj
= CStatObj::get_intrusive_list_root(); pStatObj
; pStatObj
= pStatObj
->get_next_intrusive())
105 garbage
.push_back(pStatObj
);
107 #if !defined(_RELEASE)
108 if (!pStatObj
->IsDefaultObject())
111 Warning("StatObj not deleted: %s (%s) RefCount: %d", pStatObj
->m_szFileName
.c_str(), pStatObj
->m_szGeomName
.c_str(), pStatObj
->m_nUsers
);
117 // deleting leaked objects
120 Warning("CObjManager::CheckObjectLeaks: %d object(s) found in memory", nNumLeaks
);
124 for (int i
= 0, num
= (int)garbage
.size(); i
< num
; i
++)
126 CStatObj
* pStatObj
= garbage
[i
];
127 pStatObj
->ShutDown();
129 for (int i
= 0, num
= (int)garbage
.size(); i
< num
; i
++)
131 CStatObj
* pStatObj
= garbage
[i
];
135 #ifdef POOL_STATOBJ_ALLOCS
136 assert(m_statObjPool
->GetTotalMemory().nUsed
== 0);
139 m_bGarbageCollectionEnabled
= true;
141 #ifdef POOL_STATOBJ_ALLOCS
142 m_statObjPool
->FreeMemoryIfEmpty();
145 stl::free_container(m_lstTmpCastingNodes
);
146 stl::free_container(m_decalsToPrecreate
);
147 stl::free_container(m_tmpAreas0
);
148 stl::free_container(m_tmpAreas1
);
149 for (size_t rl
= 0; rl
< MAX_RECURSION_LEVELS
; ++rl
)
151 for (size_t ti
= 0; ti
< nThreadsNum
; ++ti
)
152 stl::free_container(m_arrVegetationSprites
[rl
][ti
]);
155 m_lstStaticTypes
.Free();
158 //////////////////////////////////////////////////////////////////////////
159 void CObjManager::CleanStreamingData()
161 stl::free_container(m_arrStreamingNodeStack
);
163 stl::free_container(m_arrStreamableToRelease
);
164 stl::free_container(m_arrStreamableToLoad
);
165 stl::free_container(m_arrStreamableToDelete
);
168 //////////////////////////////////////////////////////////////////////////
169 // class for asyncronous preloading of level CGF's
170 //////////////////////////////////////////////////////////////////////////
171 struct CLevelStatObjLoader
: public IStreamCallback
, public Cry3DEngineBase
175 CLevelStatObjLoader()
180 void StartStreaming(const char* pFileName
)
185 StreamReadParams params
;
186 params
.dwUserData
= 0;
188 params
.pBuffer
= NULL
;
189 params
.nLoadTime
= 0;
190 params
.nMaxLoadTime
= 0;
191 params
.ePriority
= estpUrgent
;
192 GetSystem()->GetStreamEngine()->StartRead(eStreamTaskTypeGeometry
, pFileName
, this, ¶ms
);
195 virtual void StreamOnComplete(IReadStream
* pStream
, unsigned nError
)
199 string szName
= pStream
->GetName();
200 // remove game folder from path
201 const char* szInGameName
= strstr(szName
, "\\");
202 // load CGF from memory
203 GetObjManager()->LoadStatObj(szInGameName
+ 1, NULL
, NULL
, true, 0, pStream
->GetBuffer(), pStream
->GetBytesRead());
210 //////////////////////////////////////////////////////////////////////////
211 // Preload in efficient way all CGF's used in level
212 //////////////////////////////////////////////////////////////////////////
213 void CObjManager::PreloadLevelObjects()
215 LOADING_TIME_PROFILE_SECTION
;
217 // Starting a new level, so make sure the round ids are ahead of what they were in the last level
218 m_nUpdateStreamingPrioriryRoundId
+= 8;
219 m_nUpdateStreamingPrioriryRoundIdFast
+= 8;
221 PrintMessage("Starting loading level CGF's ...");
222 INDENT_LOG_DURING_SCOPE();
224 float fStartTime
= GetCurAsyncTimeSec();
226 bool bCgfCacheExist
= false;
227 if (GetCVars()->e_StreamCgf
!= 0)
229 // Only when streaming enable use no-mesh cgf pak.
230 //bCgfCacheExist = GetISystem()->GetIResourceManager()->LoadLevelCachePak( CGF_LEVEL_CACHE_PAK,"" );
232 IResourceList
* pResList
= GetISystem()->GetIResourceManager()->GetLevelResourceList();
234 // Construct streamer object
235 CLevelStatObjLoader cgfStreamer
;
237 CryPathString cgfFilename
;
239 int nInLevelCacheCount
= 0;
241 bool bVerboseLogging
= GetCVars()->e_StatObjPreload
> 1;
243 //////////////////////////////////////////////////////////////////////////
244 // Enumerate all .CGF inside level from the "brushlist.txt" file.
246 string brushListFilename
= Get3DEngine()->GetLevelFilePath(BRUSH_LIST_FILE
);
248 if (file
.Open(brushListFilename
.c_str(), "rb") && file
.GetLength() > 0)
250 int nFileLength
= file
.GetLength();
251 char* buf
= new char[nFileLength
+ 1];
252 buf
[nFileLength
] = 0; // Null terminate
253 file
.ReadRaw(buf
, nFileLength
);
255 // Parse file, every line in a file represents a resource filename.
256 char seps
[] = "\r\n";
257 char* token
= strtok(buf
, seps
);
258 while (token
!= NULL
)
260 int nAliasLen
= sizeof("%level%") - 1;
261 if (strncmp(token
, "%level%", nAliasLen
) == 0)
263 cgfFilename
= Get3DEngine()->GetLevelFilePath(token
+ nAliasLen
);
272 CryLog("%s", cgfFilename
.c_str());
274 // Do not use streaming for the Brushes from level.pak.
275 GetObjManager()->LoadStatObj(cgfFilename
.c_str(), NULL
, 0, false, 0);
276 //cgfStreamer.StartStreaming(cgfFilename.c_str());
279 token
= strtok(NULL
, seps
);
281 //This loop can take a few seconds, so we should refresh the loading screen and call the loading tick functions to ensure that no big gaps in coverage occur.
282 SYNCHRONOUS_LOADING_TICK();
287 //////////////////////////////////////////////////////////////////////////
289 // Request objects loading from Streaming System.
290 if (const char* pCgfName
= pResList
->GetFirst())
294 if (strstr(pCgfName
, ".cgf"))
296 const char* sLodName
= strstr(pCgfName
, "_lod");
297 if (sLodName
&& (sLodName
[4] >= '0' && sLodName
[4] <= '9'))
300 pCgfName
= pResList
->GetNext();
304 cgfFilename
= pCgfName
;
308 CryLog("%s", cgfFilename
.c_str());
310 CStatObj
* pStatObj
= GetObjManager()->LoadStatObj(cgfFilename
.c_str(), NULL
, 0, true, 0);
313 if (pStatObj
->m_bMeshStrippedCGF
)
315 nInLevelCacheCount
++;
318 //cgfStreamer.StartStreaming(cgfFilename.c_str());
321 //This loop can take a few seconds, so we should refresh the loading screen and call the loading tick functions to ensure that no big gaps in coverage occur.
322 SYNCHRONOUS_LOADING_TICK();
325 pCgfName
= pResList
->GetNext();
329 // PrintMessage("Finished requesting level CGF's: %d objects in %.1f sec", nCgfCounter, GetCurAsyncTimeSec()-fStartTime);
331 // Continue updating streaming system until all CGF's are loaded
332 if (cgfStreamer
.m_nTasksNum
> 0)
334 LOADING_TIME_PROFILE_SECTION_NAMED("CObjManager::PreloadLevelObjects_StreamEngine_Update");
335 GetSystem()->GetStreamEngine()->UpdateAndWait();
340 //GetISystem()->GetIResourceManager()->UnloadLevelCachePak( CGF_LEVEL_CACHE_PAK );
343 float dt
= GetCurAsyncTimeSec() - fStartTime
;
344 PrintMessage("Finished loading level CGF's: %d objects loaded (%d from LevelCache) in %.1f sec", nCgfCounter
, nInLevelCacheCount
, dt
);
347 //////////////////////////////////////////////////////////////////////////////////////////////////////////
348 // Create / delete object
349 //////////////////////////////////////////////////////////////////////////////////////////////////////////
350 CStatObj
* CObjManager::LoadStatObj(const char* __szFileName
351 , const char* _szGeomName
, IStatObj::SSubObject
** ppSubObject
353 , unsigned long nLoadingFlags
356 , const char* szBlockName
)
358 if (!m_pDefaultCGF
&& strcmp(__szFileName
, DEFAULT_CGF_NAME
) != 0)
360 // Load default object if not yet loaded.
361 const char* sDefaulObjFilename
= DEFAULT_CGF_NAME
;
362 // prepare default object
363 m_pDefaultCGF
= LoadStatObj(sDefaulObjFilename
, NULL
, NULL
, false, nLoadingFlags
);
366 Error("CObjManager::LoadStatObj: Default object not found (%s)", sDefaulObjFilename
);
367 m_pDefaultCGF
= new CStatObj();
369 m_pDefaultCGF
->m_bDefaultObject
= true;
372 if (CryStringUtils::stristr(__szFileName
, "_lod"))
374 Warning("Failed to load cgf: %s, '_lod' meshes can be loaded only internally as part of multi-lod CGF loading", __szFileName
);
375 return m_pDefaultCGF
;
378 LOADING_TIME_PROFILE_SECTION_ARGS(__szFileName
);
379 MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other
, 0, "Static Geometry");
384 if (!strcmp(__szFileName
, "NOFILE"))
386 // make empty object to be filled from outside
387 CStatObj
* pObject
= new CStatObj();
388 m_lstLoadedObjects
.insert(pObject
);
392 // Normalize file name
393 char sFilename
[_MAX_PATH
];
395 //////////////////////////////////////////////////////////////////////////
396 // Remap %level% alias if needed an unify filename
398 int nAliasNameLen
= sizeof("%level%") - 1;
399 if (strncmp(__szFileName
, "%level%", nAliasNameLen
) == 0)
401 cry_strcpy(sFilename
, Get3DEngine()->GetLevelFilePath(__szFileName
+ nAliasNameLen
));
405 cry_strcpy(sFilename
, __szFileName
);
408 PREFAST_SUPPRESS_WARNING(6054) // sFilename is null terminated
409 std::replace(sFilename
, sFilename
+ strlen(sFilename
), '\\', '/'); // To Unix Path
411 //////////////////////////////////////////////////////////////////////////
413 bool bForceBreakable
= strstr(sFilename
, "break") != 0;
414 if (_szGeomName
&& !strcmp(_szGeomName
, "#ForceBreakable"))
416 bForceBreakable
= true;
420 // Try to find already loaded object
421 CStatObj
* pObject
= 0;
424 if (_szGeomName
&& !strcmp(_szGeomName
, "cloth"))
425 _szGeomName
= 0, flagCloth
= STATIC_OBJECT_DYNAMIC
| STATIC_OBJECT_CLONE
;
428 pObject
= stl::find_in_map(m_nameToObjectMap
, CONST_TEMP_STRING(sFilename
), NULL
);
431 if (!bUseStreaming
&& pObject
->m_bCanUnload
)
433 pObject
->DisableStreaming();
437 if (!pObject
->m_bLodsLoaded
&& !pData
)
439 pObject
->LoadLowLODs(bUseStreaming
, nLoadingFlags
);
442 if (_szGeomName
&& _szGeomName
[0])
445 CStatObj::SSubObject
* pSubObject
= pObject
->FindSubObject(_szGeomName
);
446 if (!pSubObject
|| !pSubObject
->pStatObj
)
448 if (pSubObject
->pStatObj
)
451 *ppSubObject
= pSubObject
;
452 return (CStatObj
*)pSubObject
->pStatObj
;
460 pObject
= new CStatObj();
461 pObject
->m_nFlags
|= flagCloth
;
463 bUseStreaming
&= (GetCVars()->e_StreamCgf
!= 0);
466 pObject
->m_bCanUnload
= true;
468 nLoadingFlags
|= IStatObj::ELoadingFlagsForceBreakable
;
470 if (!pObject
->LoadCGF(sFilename
, strstr(sFilename
, "_lod") != NULL
, nLoadingFlags
, pData
, nDataSize
))
472 if (!(nLoadingFlags
& IStatObj::ELoadingFlagsNoErrorIfFail
))
473 Error("Failed to load cgf: %s", __szFileName
);
475 // if geom name is specified - just return 0
476 if (_szGeomName
&& _szGeomName
[0])
482 // make unique default CGF for every case of missing CGF, this will make export process more reliable and help finding missing CGF's in pure game
483 /* pObject->m_bCanUnload = false;
484 if (m_bEditor && pObject->LoadCGF( DEFAULT_CGF_NAME, false, nLoadingFlags, pData, nDataSize ))
486 pObject->m_szFileName = sFilename;
487 pObject->m_bDefaultObject = true;
492 return m_pDefaultCGF
;
496 // now try to load lods
499 pObject
->LoadLowLODs(bUseStreaming
, nLoadingFlags
);
502 if (!pObject
->m_bCanUnload
)
504 // even if streaming is disabled we register object for potential streaming (streaming system will never unload it)
505 pObject
->DisableStreaming();
508 // sub meshes merging
509 pObject
->TryMergeSubObjects(false);
511 m_lstLoadedObjects
.insert(pObject
);
512 m_nameToObjectMap
[pObject
->m_szFileName
] = pObject
;
514 if (_szGeomName
&& _szGeomName
[0])
517 CStatObj::SSubObject
* pSubObject
= pObject
->FindSubObject(_szGeomName
);
518 if (!pSubObject
|| !pSubObject
->pStatObj
)
520 if (pSubObject
->pStatObj
)
523 *ppSubObject
= pSubObject
;
524 return (CStatObj
*)pSubObject
->pStatObj
;
531 //////////////////////////////////////////////////////////////////////////
532 bool CObjManager::InternalDeleteObject(CStatObj
* pObject
)
536 if (!m_bLockCGFResources
&& !IsResourceLocked(pObject
->m_szFileName
))
538 LoadedObjects::iterator it
= m_lstLoadedObjects
.find(pObject
);
539 if (it
!= m_lstLoadedObjects
.end())
541 m_lstLoadedObjects
.erase(it
);
542 m_nameToObjectMap
.erase(pObject
->m_szFileName
);
546 //Warning( "CObjManager::ReleaseObject called on object not loaded in ObjectManager %s",pObject->m_szFileName.c_str() );
553 else if (m_bLockCGFResources
)
555 // Put them to locked stat obj list.
556 stl::push_back_unique(m_lockedObjects
, pObject
);
562 CStatObj
* CObjManager::AllocateStatObj()
564 #ifdef POOL_STATOBJ_ALLOCS
565 return (CStatObj
*)m_statObjPool
->Allocate();
567 return (CStatObj
*)malloc(sizeof(CStatObj
));
571 void CObjManager::FreeStatObj(CStatObj
* pObj
)
573 #ifdef POOL_STATOBJ_ALLOCS
574 m_statObjPool
->Deallocate(pObj
);
580 CObjManager::CObjManager() :
582 m_decalsToPrecreate(),
583 m_bNeedProcessObjectsStreaming_Finish(false),
586 #ifdef POOL_STATOBJ_ALLOCS
587 m_statObjPool
= new stl::PoolAllocator
<sizeof(CStatObj
), stl::PSyncMultiThread
, alignof(CStatObj
)>(stl::FHeap().PageSize(64)); // 20Kb per page
590 m_vStreamPreCachePointDefs
.Add(SObjManPrecachePoint());
591 m_vStreamPreCacheCameras
.Add(SObjManPrecacheCamera());
592 m_nNextPrecachePointId
= 0;
593 m_bCameraPrecacheOverridden
= false;
595 m_pObjManager
= this;
599 m_vSkyColor
.Set(0, 0, 0);
601 m_vSunColor
.Set(0, 0, 0);
603 m_fSkyBrightMul
= 0.3f
;
605 m_fSSAOContrast
= 1.f
;
607 m_rainParams
.nUpdateFrameID
= -1;
608 m_rainParams
.fAmount
= 0.f
;
609 m_rainParams
.fRadius
= 1.f
;
610 m_rainParams
.vWorldPos
.Set(0, 0, 0);
611 m_rainParams
.vColor
.Set(1, 1, 1);
612 m_rainParams
.fFakeGlossiness
= 0.5f
;
613 m_rainParams
.fFakeReflectionAmount
= 1.5f
;
614 m_rainParams
.fDiffuseDarkening
= 0.5f
;
615 m_rainParams
.fRainDropsAmount
= 0.5f
;
616 m_rainParams
.fRainDropsSpeed
= 1.f
;
617 m_rainParams
.fRainDropsLighting
= 1.f
;
618 m_rainParams
.fMistAmount
= 3.f
;
619 m_rainParams
.fMistHeight
= 8.f
;
620 m_rainParams
.fPuddlesAmount
= 1.5f
;
621 m_rainParams
.fPuddlesMaskAmount
= 1.0f
;
622 m_rainParams
.fPuddlesRippleAmount
= 2.0f
;
623 m_rainParams
.fSplashesAmount
= 1.3f
;
624 m_rainParams
.bIgnoreVisareas
= false;
625 m_rainParams
.bDisableOcclusion
= false;
627 m_fMaxViewDistanceScale
= 1.f
;
628 m_fGSMMaxDistance
= 0;
629 m_bLockCGFResources
= false;
632 m_bGarbageCollectionEnabled
= true;
634 m_decalsToPrecreate
.reserve(128);
636 // init queue for check occlusion
637 m_CheckOcclusionQueue
.Init(GetCVars()->e_CheckOcclusionQueueSize
);
638 m_CheckOcclusionOutputQueue
.Init(GetCVars()->e_CheckOcclusionOutputQueueSize
);
641 // make unit box for occlusion test
642 void CObjManager::MakeUnitCube()
647 SVF_P3F_C4B_T2F arrVerts
[8];
648 arrVerts
[0].xyz
= Vec3(0, 0, 0);
649 arrVerts
[1].xyz
= Vec3(1, 0, 0);
650 arrVerts
[2].xyz
= Vec3(0, 0, 1);
651 arrVerts
[3].xyz
= Vec3(1, 0, 1);
652 arrVerts
[4].xyz
= Vec3(0, 1, 0);
653 arrVerts
[5].xyz
= Vec3(1, 1, 0);
654 arrVerts
[6].xyz
= Vec3(0, 1, 1);
655 arrVerts
[7].xyz
= Vec3(1, 1, 1);
665 static const vtx_idx arrIndices
[] =
684 m_pRMBox
= GetRenderer()->CreateRenderMeshInitialized(
686 CRY_ARRAY_COUNT(arrVerts
),
687 EDefaultInputLayouts::P3F_C4B_T2F
,
689 CRY_ARRAY_COUNT(arrIndices
),
691 "OcclusionQueryCube", "OcclusionQueryCube",
694 m_pRMBox
->SetChunk(NULL
, 0, CRY_ARRAY_COUNT(arrVerts
), 0, CRY_ARRAY_COUNT(arrIndices
), 1.0f
, 0);
696 m_bGarbageCollectionEnabled
= true;
699 CObjManager::~CObjManager()
701 // free default object
705 /* assert(!m_lstBrushContainer.Count());
706 for(int i=0; i<m_lstBrushContainer.Count(); i++)
708 if(m_lstBrushContainer[i]->GetEntityStatObj())
709 ReleaseObject((CStatObj*)m_lstBrushContainer[i]->GetEntityStatObj());
710 delete m_lstBrushContainer[i];
712 m_lstBrushContainer.Reset();
715 #ifdef POOL_STATOBJ_ALLOCS
716 delete m_statObjPool
;
720 int CObjManager::ComputeDissolve(const CLodValue
&lodValueIn
, SRenderNodeTempData
* pTempData
, IRenderNode
* pEnt
, float fEntDistance
, CLodValue arrlodValuesOut
[2])
722 int nLodMain
= CLAMP(0, lodValueIn
.LodA(), MAX_STATOBJ_LODS_NUM
- 1);
723 int nLodMin
= max(nLodMain
- 1, 0);
724 int nLodMax
= min(nLodMain
+ 1, MAX_STATOBJ_LODS_NUM
- 1);
726 float prevLodLastTimeUsed
= 0;
727 float * arrLodLastTimeUsed
= pTempData
->userData
.arrLodLastTimeUsed
;
729 // Find when previous lod was used as primary lod last time and update last time used for current primary lod
730 for (int nLO
= nLodMin
; nLO
<= nLodMax
; nLO
++)
734 if (arrLodLastTimeUsed
[nLO
] > prevLodLastTimeUsed
)
736 prevLodLastTimeUsed
= arrLodLastTimeUsed
[nLO
];
741 arrLodLastTimeUsed
[nLO
] = GetCurTimeSec();
744 float fDissolveRef
= 1.f
- SATURATE((GetCurTimeSec() - prevLodLastTimeUsed
) / GetCVars()->e_LodTransitionTime
);
746 prevLodLastTimeUsed
= max(prevLodLastTimeUsed
, GetCurTimeSec() - GetCVars()->e_LodTransitionTime
);
748 // Compute also max view distance fading
749 const float fDistFadeInterval
= 2.f
;
750 float fDistFadeRef
= SATURATE(min(fEntDistance
/ pEnt
->m_fWSMaxViewDist
* 5.f
- 4.f
, ((fEntDistance
- pEnt
->m_fWSMaxViewDist
) / fDistFadeInterval
+ 1.f
)));
754 // Render current lod and (if needed) previous lod
755 for (int nLO
= nLodMin
; nLO
<= nLodMax
&& nLodsNum
<2; nLO
++)
757 if (arrLodLastTimeUsed
[nLO
] < prevLodLastTimeUsed
)
760 CLodValue lodSubValue
;
766 float fDissolveMaxDistRef
= max(fDissolveRef
, fDistFadeRef
);
768 lodSubValue
= CLodValue(nLO
, int(fDissolveMaxDistRef
*255.f
), -1);
774 float fDissolveMaxDistRef
= min(fDissolveRef
, 1.f
- fDistFadeRef
);
776 lodSubValue
= CLodValue(-1, int(fDissolveMaxDistRef
*255.f
), nLO
);
779 arrlodValuesOut
[nLodsNum
] = lodSubValue
;
787 float CObjManager::GetXYRadius(int type
)
789 if ((m_lstStaticTypes
.Count() <= type
|| !m_lstStaticTypes
[type
].pStatObj
))
792 Vec3 vSize
= m_lstStaticTypes
[type
].pStatObj
->GetBoxMax() - m_lstStaticTypes
[type
].pStatObj
->GetBoxMin();
795 float fXYRadius
= vSize
.GetLength() * 0.5f
;
800 bool CObjManager::GetStaticObjectBBox(int nType
, Vec3
& vBoxMin
, Vec3
& vBoxMax
)
802 if ((m_lstStaticTypes
.Count() <= nType
|| !m_lstStaticTypes
[nType
].pStatObj
))
805 vBoxMin
= m_lstStaticTypes
[nType
].pStatObj
->GetBoxMin();
806 vBoxMax
= m_lstStaticTypes
[nType
].pStatObj
->GetBoxMax();
811 void CObjManager::GetMemoryUsage(class ICrySizer
* pSizer
) const
814 SIZER_COMPONENT_NAME(pSizer
, "Self");
815 pSizer
->AddObject(this, sizeof(*this));
819 SIZER_COMPONENT_NAME(pSizer
, "StaticTypes");
820 pSizer
->AddObject(m_lstStaticTypes
);
823 for (int i
= 0; i
< MAX_RECURSION_LEVELS
; i
++)
825 SIZER_COMPONENT_NAME(pSizer
, "VegetationSprites");
826 for (int t
= 0; t
< nThreadsNum
; t
++)
828 pSizer
->AddObject(m_arrVegetationSprites
[i
][t
]);
833 SIZER_COMPONENT_NAME(pSizer
, "CMesh");
834 CIndexedMesh
* pMesh
= CIndexedMesh::get_intrusive_list_root();
837 pSizer
->AddObject(pMesh
);
838 pMesh
= pMesh
->get_next_intrusive();
843 SIZER_COMPONENT_NAME(pSizer
, "StatObj");
844 for (CStatObj
* pStatObj
= CStatObj::get_intrusive_list_root(); pStatObj
; pStatObj
= pStatObj
->get_next_intrusive())
846 pStatObj
->GetMemoryUsage(pSizer
);
851 SIZER_COMPONENT_NAME(pSizer
, "EmptyNodes");
852 pSizer
->AddObject(COctreeNode::m_arrEmptyNodes
);
856 // retrieves the bandwidth calculations for the audio streaming
857 void CObjManager::GetBandwidthStats(float* fBandwidthRequested
)
859 #if !defined (_RELEASE) || defined(ENABLE_STATOSCOPE_RELEASE)
860 if (fBandwidthRequested
&& CStatObj::s_fStreamingTime
!= 0.0f
)
862 *fBandwidthRequested
= (CStatObj::s_nBandwidth
/ CStatObj::s_fStreamingTime
) / 1024.0f
;
867 void CObjManager::ReregisterEntitiesInArea(AABB
* pBox
, bool bCleanUpTree
)
869 PodArray
<SRNInfo
> lstEntitiesInArea
;
871 Get3DEngine()->MoveObjectsIntoListGlobal(&lstEntitiesInArea
, pBox
, true);
873 if (GetVisAreaManager())
874 GetVisAreaManager()->MoveObjectsIntoList(&lstEntitiesInArea
, pBox
, true);
876 for (int i
= 0; i
< lstEntitiesInArea
.Count(); i
++)
878 IVisArea
* pPrevArea
= lstEntitiesInArea
[i
].pNode
->GetEntityVisArea();
879 Get3DEngine()->UnRegisterEntityDirect(lstEntitiesInArea
[i
].pNode
);
881 if (lstEntitiesInArea
[i
].pNode
->GetRenderNodeType() == eERType_Decal
)
882 ((CDecalRenderNode
*)lstEntitiesInArea
[i
].pNode
)->RequestUpdate();
887 Get3DEngine()->GetObjectsTree()->CleanUpTree();
888 if (GetVisAreaManager())
889 GetVisAreaManager()->CleanUpTrees();
892 for (int i
= 0; i
< lstEntitiesInArea
.Count(); i
++)
894 Get3DEngine()->RegisterEntity(lstEntitiesInArea
[i
].pNode
);
898 void CObjManager::FreeNotUsedCGFs()
900 //assert(!m_bLockCGFResources);
901 m_lockedObjects
.clear();
903 if (!m_bLockCGFResources
)
905 //Timur, You MUST use next here, or with erase you invalidating
906 LoadedObjects::iterator next
;
907 for (LoadedObjects::iterator it
= m_lstLoadedObjects
.begin(); it
!= m_lstLoadedObjects
.end(); it
= next
)
911 CStatObj
* p
= (CStatObj
*)(*it
);
912 if (p
->m_nUsers
<= 0)
919 ClearStatObjGarbage();
922 //////////////////////////////////////////////////////////////////////////
923 void CObjManager::GetLoadedStatObjArray(IStatObj
** pObjectsArray
, int& nCount
)
927 nCount
= m_lstLoadedObjects
.size();
931 CObjManager::LoadedObjects::iterator it
= m_lstLoadedObjects
.begin();
932 for (int i
= 0; i
< nCount
&& it
!= m_lstLoadedObjects
.end(); ++i
, ++it
)
934 pObjectsArray
[i
] = *it
;
938 void StatInstGroup::Update(CVars
* pCVars
, int nGeomDetailScreenRes
)
942 static ICVar
* pObjShadowCastSpec
= gEnv
->pConsole
->GetCVar("e_ObjShadowCastSpec");
943 if (nCastShadowMinSpec
<= pObjShadowCastSpec
->GetIVal())
945 m_dwRndFlags
|= ERF_CASTSHADOWMAPS
| ERF_HAS_CASTSHADOWMAPS
;
948 if (bDynamicDistanceShadows
)
949 m_dwRndFlags
|= ERF_DYNAMIC_DISTANCESHADOWS
;
951 m_dwRndFlags
|= ERF_HIDABLE
;
952 if (bHideabilitySecondary
)
953 m_dwRndFlags
|= ERF_HIDABLE_SECONDARY
;
955 m_dwRndFlags
|= ERF_PICKABLE
;
957 m_dwRndFlags
|= ERF_OUTDOORONLY
;
959 m_dwRndFlags
|= ERF_GI_MODE_BIT0
; // corresponds to IRenderNode::eGM_StaticVoxelization
961 uint32 nSpec
= (uint32
)minConfigSpec
;
964 m_dwRndFlags
&= ~ERF_SPEC_BITS_MASK
;
965 m_dwRndFlags
|= (nSpec
<< ERF_SPEC_BITS_SHIFT
) & ERF_SPEC_BITS_MASK
;
970 fVegRadiusVert
= GetStatObj()->GetRadiusVert();
971 fVegRadiusHor
= GetStatObj()->GetRadiusHors();
972 fVegRadius
= max(fVegRadiusVert
, fVegRadiusHor
);
976 fVegRadiusHor
= fVegRadius
= fVegRadiusVert
= 0;
979 if (bUseSprites
&& fVegRadius
> 0)
981 m_fSpriteSwitchDist
= 18.f
* fVegRadius
* fSize
*
982 max(pCVars
->e_VegetationSpritesDistanceCustomRatioMin
, fSpriteDistRatio
);
983 m_fSpriteSwitchDist
*= pCVars
->e_VegetationSpritesDistanceRatio
;
984 m_fSpriteSwitchDist
*= max(1.f
, (float)nGeomDetailScreenRes
/ 1024.f
);
985 if (m_fSpriteSwitchDist
< pCVars
->e_VegetationSpritesMinDistance
)
986 m_fSpriteSwitchDist
= pCVars
->e_VegetationSpritesMinDistance
;
989 m_fSpriteSwitchDist
= 1000000.f
;
991 for (int j
= 0; j
< FAR_TEX_COUNT
; ++j
)
993 SVegetationSpriteLightInfo
& rLightInfo
= m_arrSSpriteLightInfo
[j
];
994 if (rLightInfo
.m_pDynTexture
)
995 rLightInfo
.m_pDynTexture
->SetFlags(rLightInfo
.m_pDynTexture
->GetFlags() | IDynTexture::fNeedRegenerate
);
998 #if defined(FEATURE_SVO_GI)
999 IMaterial
* pMat
= pMaterial
? pMaterial
.get() : (pStatObj
? pStatObj
->GetMaterial() : 0);
1000 if (pMat
&& (Cry3DEngineBase::GetCVars()->e_svoTI_Active
>= 0) && (gEnv
->IsEditor() || Cry3DEngineBase::GetCVars()->e_svoTI_Apply
))
1001 pMat
->SetKeepLowResSysCopyForDiffTex();
1005 float StatInstGroup::GetAlignToTerrainAmount() const
1007 return fAlignToTerrainCoefficient
;
1010 bool CObjManager::SphereRenderMeshIntersection(IRenderMesh
* pRenderMesh
, const Vec3
& vInPos
, const float fRadius
, IMaterial
* pMat
)
1012 FUNCTION_PROFILER_3DENGINE
;
1014 // get position offset and stride
1016 byte
* pPos
= pRenderMesh
->GetPosPtr(nPosStride
, FSL_READ
);
1019 vtx_idx
* pInds
= pRenderMesh
->GetIndexPtr(FSL_READ
);
1020 int nInds
= pRenderMesh
->GetIndicesCount();
1021 assert(nInds
% 3 == 0);
1024 TRenderChunkArray
& Chunks
= pRenderMesh
->GetChunks();
1025 for (int nChunkId
= 0; nChunkId
< Chunks
.size(); nChunkId
++)
1027 CRenderChunk
* pChunk
= &Chunks
[nChunkId
];
1028 if (pChunk
->m_nMatFlags
& MTL_FLAG_NODRAW
|| !pChunk
->pRE
)
1031 // skip transparent and alpha test
1034 const SShaderItem
& shaderItem
= pMat
->GetShaderItem(pChunk
->m_nMatID
);
1035 if (!shaderItem
.m_pShader
|| shaderItem
.m_pShader
->GetFlags() & EF_NODRAW
)
1039 int nLastIndexId
= pChunk
->nFirstIndexId
+ pChunk
->nNumIndices
;
1040 for (int i
= pChunk
->nFirstIndexId
; i
< nLastIndexId
; i
+= 3)
1042 assert((int)pInds
[i
+ 0] < pRenderMesh
->GetVerticesCount());
1043 assert((int)pInds
[i
+ 1] < pRenderMesh
->GetVerticesCount());
1044 assert((int)pInds
[i
+ 2] < pRenderMesh
->GetVerticesCount());
1046 // get triangle vertices
1047 Vec3 v0
= (*(Vec3
*)&pPos
[nPosStride
* pInds
[i
+ 0]]);
1048 Vec3 v1
= (*(Vec3
*)&pPos
[nPosStride
* pInds
[i
+ 1]]);
1049 Vec3 v2
= (*(Vec3
*)&pPos
[nPosStride
* pInds
[i
+ 2]]);
1057 if (Overlap::Sphere_AABB(Sphere(vInPos
, fRadius
), triBox
))
1065 //////////////////////////////////////////////////////////////////////////
1066 void CObjManager::ClearStatObjGarbage()
1068 FUNCTION_PROFILER_3DENGINE
;
1070 std::vector
<CStatObj
*> garbage
;
1072 CryMT::vector
<CStatObj
*>::AutoLock
lock(m_checkForGarbage
.get_lock());
1074 while (!m_checkForGarbage
.empty())
1078 // Make sure all stat objects inside this array are unique.
1079 if (!m_checkForGarbage
.empty())
1081 // Only check explicitly added objects.
1082 // First ShutDown object clearing all pointers.
1084 while (m_checkForGarbage
.try_pop_back(pStatObj
))
1086 if (pStatObj
->m_bCheckGarbage
)
1088 // Check if it must be released.
1089 int nChildRefs
= pStatObj
->CountChildReferences();
1090 if (pStatObj
->m_nUsers
<= 0 && nChildRefs
<= 0)
1092 garbage
.push_back(pStatObj
);
1096 pStatObj
->m_bCheckGarbage
= false;
1102 // First ShutDown object clearing all pointers.
1103 for (int i
= 0, num
= (int)garbage
.size(); i
< num
; i
++)
1105 CStatObj
* pStatObj
= garbage
[i
];
1107 if (!m_bLockCGFResources
&& !IsResourceLocked(pStatObj
->m_szFileName
))
1109 // only shutdown object if it can be deleted by InternalDeleteObject()
1110 pStatObj
->ShutDown();
1114 // Then delete all garbage objects.
1115 for (int i
= 0, num
= (int)garbage
.size(); i
< num
; i
++)
1117 CStatObj
* pStatObj
= garbage
[i
];
1118 InternalDeleteObject(pStatObj
);
1124 //////////////////////////////////////////////////////////////////////////
1125 IRenderMesh
* CObjManager::GetRenderMeshBox()
1134 //////////////////////////////////////////////////////////////////////////
1135 void CObjManager::CheckForGarbage(CStatObj
* pObject
)
1137 if (m_bGarbageCollectionEnabled
&&
1138 !pObject
->m_bCheckGarbage
)
1140 pObject
->m_bCheckGarbage
= true;
1141 m_checkForGarbage
.push_back(pObject
);
1145 //////////////////////////////////////////////////////////////////////////
1146 void CObjManager::UnregisterForGarbage(CStatObj
* pObject
)
1148 CRY_ASSERT(pObject
);
1150 if (m_bGarbageCollectionEnabled
&&
1151 pObject
->m_bCheckGarbage
)
1153 m_checkForGarbage
.try_remove(pObject
);
1154 pObject
->m_bCheckGarbage
= false;
1158 //////////////////////////////////////////////////////////////////////////
1159 bool CObjManager::AddOrCreatePersistentRenderObject(SRenderNodeTempData
* pTempData
, CRenderObject
*& pRenderObject
, const CLodValue
* pLodValue
, const IRenderView::SInstanceUpdateInfo
& instanceUpdateInfo
, const SRenderingPassInfo
& passInfo
) const
1161 CRY_ASSERT(pRenderObject
== nullptr);
1163 const auto shouldCreatePermanentObject
= (GetCVars()->e_PermanentRenderObjects
&& (pTempData
|| pRenderObject
) && GetCVars()->e_DebugDraw
== 0 && (!pLodValue
|| !pLodValue
->DissolveRefA())) &&
1164 !(passInfo
.IsRecursivePass() || (pTempData
&& (pTempData
->userData
.m_pFoliage
|| (pTempData
->userData
.pOwnerNode
&& (pTempData
->userData
.pOwnerNode
->GetRndFlags() & ERF_SELECTED
)))));
1165 if (shouldCreatePermanentObject
)
1167 if (pLodValue
&& pLodValue
->LodA() == -1 && pLodValue
->LodB() == -1)
1170 if (pLodValue
&& pLodValue
->LodA() == -1 && pLodValue
->DissolveRefB() == 255)
1173 if (pLodValue
&& pLodValue
->LodB() == -1 && pLodValue
->DissolveRefA() == 255)
1176 int nLod
= pLodValue
? CLAMP(0, pLodValue
->LodA(), MAX_STATOBJ_LODS_NUM
- 1) : 0;
1178 uint32 passId
= passInfo
.IsShadowPass() ? 1 : 0;
1179 uint32 passMask
= 1 << passId
;
1181 pRenderObject
= pTempData
->GetRenderObject(nLod
);
1183 // Update instance only for dirty objects
1184 const auto instanceDataDirty
= pRenderObject
->m_bInstanceDataDirty
;
1185 passInfo
.GetIRenderView()->AddPermanentObject(pRenderObject
, instanceUpdateInfo
, instanceDataDirty
, passInfo
);
1187 // Has this object already been filled?
1188 int previousMask
= CryInterlockedExchangeOr(reinterpret_cast<volatile LONG
*>(&pRenderObject
->m_passReadyMask
), passMask
);
1189 if (previousMask
& passMask
) // Object drawn once => fast path.
1191 if (instanceDataDirty
)
1193 // Update instance matrix
1194 pRenderObject
->m_II
.m_Matrix
= instanceUpdateInfo
.objectMatrix
;
1195 pRenderObject
->m_bInstanceDataDirty
= false;
1198 if (GetCVars()->e_BBoxes
&& pTempData
&& pTempData
->userData
.pOwnerNode
)
1199 GetObjManager()->RenderObjectDebugInfo(pTempData
->userData
.pOwnerNode
, pRenderObject
->m_fDistance
, passInfo
);
1204 // Permanent object needs to be filled first time,
1205 if (pTempData
&& pTempData
->userData
.pOwnerNode
)
1206 pTempData
->userData
.nStatObjLastModificationId
= GetResourcesModificationChecksum(pTempData
->userData
.pOwnerNode
);
1210 // Fallback to temporary render object
1211 pRenderObject
= passInfo
.GetIRenderView()->AllocateTemporaryRenderObject();
1214 // We do not have a persistant render object
1215 // Always update instance matrix
1216 pRenderObject
->m_II
.m_Matrix
= instanceUpdateInfo
.objectMatrix
;
1221 //////////////////////////////////////////////////////////////////////////
1222 uint32
CObjManager::GetResourcesModificationChecksum(IRenderNode
* pOwnerNode
) const
1224 uint32 nModificationId
= 1;
1226 if (CStatObj
* pStatObj
= (CStatObj
*)pOwnerNode
->GetEntityStatObj())
1227 nModificationId
+= pStatObj
->GetModificationId();
1229 if (CMatInfo
* pMatInfo
= (CMatInfo
*)pOwnerNode
->GetMaterial())
1230 nModificationId
+= pMatInfo
->GetModificationId();
1232 if (pOwnerNode
->GetRenderNodeType() == eERType_TerrainSector
)
1233 nModificationId
+= ((CTerrainNode
*)pOwnerNode
)->GetMaterialsModificationId();
1235 return nModificationId
;
1238 IRenderMesh
* CObjManager::GetBillboardRenderMesh(IMaterial
* pMaterial
)
1240 if(!m_pBillboardMesh
)
1242 PodArray
<SVF_P3F_C4B_T2F
> arrVertices
;
1243 PodArray
<SPipTangents
> arrTangents
;
1244 PodArray
<vtx_idx
> arrIndices
;
1246 SVF_P3F_C4B_T2F vert
;
1248 vert
.st
= Vec2(0.0f
, 0.0f
);
1249 vert
.color
.dcolor
= -1;
1252 vert
.xyz
.Set(+.5, 0, -.5);
1253 vert
.st
= Vec2(1, 1);
1254 arrVertices
.Add(vert
);
1255 vert
.xyz
.Set(-.5, 0, -.5);
1256 vert
.st
= Vec2(0, 1);
1257 arrVertices
.Add(vert
);
1258 vert
.xyz
.Set(+.5, 0, +.5);
1259 vert
.st
= Vec2(1, 0);
1260 arrVertices
.Add(vert
);
1261 vert
.xyz
.Set(-.5, 0, +.5);
1262 vert
.st
= Vec2(0, 0);
1263 arrVertices
.Add(vert
);
1266 arrTangents
.Add(SPipTangents(Vec3(1, 0, 0), Vec3(0, 0, 1), 1));
1267 arrTangents
.Add(SPipTangents(Vec3(1, 0, 0), Vec3(0, 0, 1), 1));
1268 arrTangents
.Add(SPipTangents(Vec3(1, 0, 0), Vec3(0, 0, 1), 1));
1269 arrTangents
.Add(SPipTangents(Vec3(1, 0, 0), Vec3(0, 0, 1), 1));
1279 m_pBillboardMesh
= Cry3DEngineBase::GetRenderer()->CreateRenderMeshInitialized(
1280 arrVertices
.GetElements(), arrVertices
.Count(), EDefaultInputLayouts::P3F_C4B_T2F
,
1281 arrIndices
.GetElements(), arrIndices
.Count(), prtTriangleList
,
1282 "Billboard", "Billboard", eRMT_Static
, 1, 0, NULL
, NULL
, false, true,
1283 arrTangents
.GetElements());
1285 m_pBillboardMesh
->SetChunk(pMaterial
, 0, arrVertices
.Count(), 0, arrIndices
.Count(), 1);
1288 return m_pBillboardMesh
;