!BI (CE-16391) (3dengine) cl 1643612,1646719 added a flag to suppress warning message...
[CRYENGINE.git] / Code / CryEngine / Cry3DEngine / ObjMan.cpp
blob4eb2809b9c311f3bafcddb63c49a59f25c0db0d5
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 // -------------------------------------------------------------------------
4 // File name: statobjman.cpp
5 // Version: v1.00
6 // Created: 28/5/2001 by Vladimir Kajalin
7 // Compilers: Visual Studio.NET
8 // Description: Loading trees, buildings, ragister/unregister entities for rendering
9 // -------------------------------------------------------------------------
10 // History:
12 ////////////////////////////////////////////////////////////////////////////
14 #include "StdAfx.h"
16 #include "StatObj.h"
17 #include "ObjMan.h"
18 #include "VisAreas.h"
19 #include "terrain_sector.h"
20 #include "PolygonClipContext.h"
21 #include "3dEngine.h"
22 #include "IndexedMesh.h"
23 #include "Brush.h"
24 #include "Vegetation.h"
25 #include "terrain.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>
36 #endif
38 //////////////////////////////////////////////////////////////////////////
39 IStatObj* CObjManager::GetStaticObjectByTypeID(int nTypeID)
41 if (nTypeID >= 0 && nTypeID < m_lstStaticTypes.Count())
42 return m_lstStaticTypes[nTypeID].pStatObj;
44 return 0;
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);
69 if (bDeleteAll)
70 rGroupTable.Free();
73 //////////////////////////////////////////////////////////////////////////
74 void CObjManager::UnloadObjects(bool bDeleteAll)
76 UnloadVegetationModels(bDeleteAll);
77 UnloadFarObjects();
79 CleanStreamingData();
81 m_pRMBox = 0;
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;
91 if (bDeleteAll)
93 m_lockedObjects.clear(); // Lock/Unlock resources will not work with this.
95 // Release default stat obj.
96 m_pDefaultCGF = 0;
98 m_nameToObjectMap.clear();
99 m_lstLoadedObjects.clear();
101 int nNumLeaks = 0;
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())
110 nNumLeaks++;
111 Warning("StatObj not deleted: %s (%s) RefCount: %d", pStatObj->m_szFileName.c_str(), pStatObj->m_szGeomName.c_str(), pStatObj->m_nUsers);
113 #endif //_RELEASE
116 #ifndef _RELEASE
117 // deleting leaked objects
118 if (nNumLeaks > 0)
120 Warning("CObjManager::CheckObjectLeaks: %d object(s) found in memory", nNumLeaks);
122 #endif //_RELEASE
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];
132 delete pStatObj;
135 #ifdef POOL_STATOBJ_ALLOCS
136 assert(m_statObjPool->GetTotalMemory().nUsed == 0);
137 #endif
139 m_bGarbageCollectionEnabled = true;
141 #ifdef POOL_STATOBJ_ALLOCS
142 m_statObjPool->FreeMemoryIfEmpty();
143 #endif
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
173 int m_nTasksNum;
175 CLevelStatObjLoader()
177 m_nTasksNum = 0;
180 void StartStreaming(const char* pFileName)
182 m_nTasksNum++;
184 // request the file
185 StreamReadParams params;
186 params.dwUserData = 0;
187 params.nSize = 0;
188 params.pBuffer = NULL;
189 params.nLoadTime = 0;
190 params.nMaxLoadTime = 0;
191 params.ePriority = estpUrgent;
192 GetSystem()->GetStreamEngine()->StartRead(eStreamTaskTypeGeometry, pFileName, this, &params);
195 virtual void StreamOnComplete(IReadStream* pStream, unsigned nError)
197 if (!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());
206 m_nTasksNum--;
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;
238 int nCgfCounter = 0;
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);
247 CCryFile 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);
265 else
267 cgfFilename = token;
270 if (bVerboseLogging)
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());
277 nCgfCounter++;
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();
284 delete[]buf;
287 //////////////////////////////////////////////////////////////////////////
289 // Request objects loading from Streaming System.
290 if (const char* pCgfName = pResList->GetFirst())
292 while (pCgfName)
294 if (strstr(pCgfName, ".cgf"))
296 const char* sLodName = strstr(pCgfName, "_lod");
297 if (sLodName && (sLodName[4] >= '0' && sLodName[4] <= '9'))
299 // Ignore Lod files.
300 pCgfName = pResList->GetNext();
301 continue;
304 cgfFilename = pCgfName;
306 if (bVerboseLogging)
308 CryLog("%s", cgfFilename.c_str());
310 CStatObj* pStatObj = GetObjManager()->LoadStatObj(cgfFilename.c_str(), NULL, 0, true, 0);
311 if (pStatObj)
313 if (pStatObj->m_bMeshStrippedCGF)
315 nInLevelCacheCount++;
318 //cgfStreamer.StartStreaming(cgfFilename.c_str());
319 nCgfCounter++;
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();
338 if (bCgfCacheExist)
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
352 , bool bUseStreaming
353 , unsigned long nLoadingFlags
354 , const void* pData
355 , int nDataSize
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);
364 if (!m_pDefaultCGF)
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");
381 if (ppSubObject)
382 *ppSubObject = NULL;
384 if (!strcmp(__szFileName, "NOFILE"))
386 // make empty object to be filled from outside
387 CStatObj* pObject = new CStatObj();
388 m_lstLoadedObjects.insert(pObject);
389 return 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));
403 else
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;
417 _szGeomName = 0;
420 // Try to find already loaded object
421 CStatObj* pObject = 0;
423 int flagCloth = 0;
424 if (_szGeomName && !strcmp(_szGeomName, "cloth"))
425 _szGeomName = 0, flagCloth = STATIC_OBJECT_DYNAMIC | STATIC_OBJECT_CLONE;
426 else
428 pObject = stl::find_in_map(m_nameToObjectMap, CONST_TEMP_STRING(sFilename), NULL);
429 if (pObject)
431 if (!bUseStreaming && pObject->m_bCanUnload)
433 pObject->DisableStreaming();
436 assert(!pData);
437 if (!pObject->m_bLodsLoaded && !pData)
439 pObject->LoadLowLODs(bUseStreaming, nLoadingFlags);
442 if (_szGeomName && _szGeomName[0])
444 // Return SubObject.
445 CStatObj::SSubObject* pSubObject = pObject->FindSubObject(_szGeomName);
446 if (!pSubObject || !pSubObject->pStatObj)
447 return 0;
448 if (pSubObject->pStatObj)
450 if (ppSubObject)
451 *ppSubObject = pSubObject;
452 return (CStatObj*)pSubObject->pStatObj;
455 return pObject;
459 // Load new CGF
460 pObject = new CStatObj();
461 pObject->m_nFlags |= flagCloth;
463 bUseStreaming &= (GetCVars()->e_StreamCgf != 0);
465 if (bUseStreaming)
466 pObject->m_bCanUnload = true;
467 if (bForceBreakable)
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);
474 // object not found
475 // if geom name is specified - just return 0
476 if (_szGeomName && _szGeomName[0])
478 delete pObject;
479 return 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;
489 else*/
491 delete pObject;
492 return m_pDefaultCGF;
496 // now try to load lods
497 if (!pData)
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])
516 // Return SubObject.
517 CStatObj::SSubObject* pSubObject = pObject->FindSubObject(_szGeomName);
518 if (!pSubObject || !pSubObject->pStatObj)
519 return 0;
520 if (pSubObject->pStatObj)
522 if (ppSubObject)
523 *ppSubObject = pSubObject;
524 return (CStatObj*)pSubObject->pStatObj;
528 return pObject;
531 //////////////////////////////////////////////////////////////////////////
532 bool CObjManager::InternalDeleteObject(CStatObj* pObject)
534 assert(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);
544 else
546 //Warning( "CObjManager::ReleaseObject called on object not loaded in ObjectManager %s",pObject->m_szFileName.c_str() );
547 //return false;
550 delete pObject;
551 return true;
553 else if (m_bLockCGFResources)
555 // Put them to locked stat obj list.
556 stl::push_back_unique(m_lockedObjects, pObject);
559 return false;
562 CStatObj* CObjManager::AllocateStatObj()
564 #ifdef POOL_STATOBJ_ALLOCS
565 return (CStatObj*)m_statObjPool->Allocate();
566 #else
567 return (CStatObj*)malloc(sizeof(CStatObj));
568 #endif
571 void CObjManager::FreeStatObj(CStatObj* pObj)
573 #ifdef POOL_STATOBJ_ALLOCS
574 m_statObjPool->Deallocate(pObj);
575 #else
576 free(pObj);
577 #endif
580 CObjManager::CObjManager() :
581 m_pDefaultCGF(NULL),
582 m_decalsToPrecreate(),
583 m_bNeedProcessObjectsStreaming_Finish(false),
584 m_CullThread()
586 #ifdef POOL_STATOBJ_ALLOCS
587 m_statObjPool = new stl::PoolAllocator<sizeof(CStatObj), stl::PSyncMultiThread, alignof(CStatObj)>(stl::FHeap().PageSize(64)); // 20Kb per page
588 #endif
590 m_vStreamPreCachePointDefs.Add(SObjManPrecachePoint());
591 m_vStreamPreCacheCameras.Add(SObjManPrecacheCamera());
592 m_nNextPrecachePointId = 0;
593 m_bCameraPrecacheOverridden = false;
595 m_pObjManager = this;
597 m_fCurrTime = 0.0f;
599 m_vSkyColor.Set(0, 0, 0);
600 m_fSunSkyRel = 0;
601 m_vSunColor.Set(0, 0, 0);
602 m_fILMul = 1.0f;
603 m_fSkyBrightMul = 0.3f;
604 m_fSSAOAmount = 1.f;
605 m_fSSAOContrast = 1.f;
606 m_fGIAmount = 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;
631 m_pRMBox = NULL;
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()
644 if (m_pRMBox)
645 return;
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);
657 // 6-------7
658 // / /|
659 // 2-------3 |
660 // | | |
661 // | 4 | 5
662 // | |/
663 // 0-------1
665 static const vtx_idx arrIndices[] =
667 // front + back
668 1, 0, 2,
669 2, 3, 1,
670 5, 6, 4,
671 5, 7, 6,
672 // left + right
673 0, 6, 2,
674 0, 4, 6,
675 1, 3, 7,
676 1, 7, 5,
677 // top + bottom
678 3, 2, 6,
679 6, 7, 3,
680 1, 4, 0,
681 1, 5, 4
684 m_pRMBox = GetRenderer()->CreateRenderMeshInitialized(
685 arrVerts,
686 CRY_ARRAY_COUNT(arrVerts),
687 EDefaultInputLayouts::P3F_C4B_T2F,
688 arrIndices,
689 CRY_ARRAY_COUNT(arrIndices),
690 prtTriangleList,
691 "OcclusionQueryCube", "OcclusionQueryCube",
692 eRMT_Static);
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
702 m_pDefaultCGF = 0;
704 // free brushes
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();
714 UnloadObjects(true);
715 #ifdef POOL_STATOBJ_ALLOCS
716 delete m_statObjPool;
717 #endif
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++)
732 if (nLodMain != nLO)
734 if (arrLodLastTimeUsed[nLO] > prevLodLastTimeUsed)
736 prevLodLastTimeUsed = arrLodLastTimeUsed[nLO];
740 if (nLodMain == 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)));
752 int nLodsNum = 0;
754 // Render current lod and (if needed) previous lod
755 for (int nLO = nLodMin; nLO <= nLodMax && nLodsNum<2; nLO++)
757 if (arrLodLastTimeUsed[nLO] < prevLodLastTimeUsed)
758 continue;
760 CLodValue lodSubValue;
762 if (nLodMain == nLO)
764 // Incoming LOD
766 float fDissolveMaxDistRef = max(fDissolveRef, fDistFadeRef);
768 lodSubValue = CLodValue(nLO, int(fDissolveMaxDistRef*255.f), -1);
770 else
772 // Out-coming LOD
774 float fDissolveMaxDistRef = min(fDissolveRef, 1.f - fDistFadeRef);
776 lodSubValue = CLodValue(-1, int(fDissolveMaxDistRef*255.f), nLO);
779 arrlodValuesOut[nLodsNum] = lodSubValue;
780 nLodsNum++;
783 return nLodsNum;
786 // mostly xy size
787 float CObjManager::GetXYRadius(int type)
789 if ((m_lstStaticTypes.Count() <= type || !m_lstStaticTypes[type].pStatObj))
790 return 0;
792 Vec3 vSize = m_lstStaticTypes[type].pStatObj->GetBoxMax() - m_lstStaticTypes[type].pStatObj->GetBoxMin();
793 vSize.z *= 0.5f;
795 float fXYRadius = vSize.GetLength() * 0.5f;
797 return fXYRadius;
800 bool CObjManager::GetStaticObjectBBox(int nType, Vec3& vBoxMin, Vec3& vBoxMax)
802 if ((m_lstStaticTypes.Count() <= nType || !m_lstStaticTypes[nType].pStatObj))
803 return 0;
805 vBoxMin = m_lstStaticTypes[nType].pStatObj->GetBoxMin();
806 vBoxMax = m_lstStaticTypes[nType].pStatObj->GetBoxMax();
808 return true;
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();
835 while (pMesh)
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;
864 #endif
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();
885 if (bCleanUpTree)
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)
909 next = it;
910 ++next;
911 CStatObj* p = (CStatObj*)(*it);
912 if (p->m_nUsers <= 0)
914 CheckForGarbage(p);
919 ClearStatObjGarbage();
922 //////////////////////////////////////////////////////////////////////////
923 void CObjManager::GetLoadedStatObjArray(IStatObj** pObjectsArray, int& nCount)
925 if (!pObjectsArray)
927 nCount = m_lstLoadedObjects.size();
928 return;
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)
940 m_dwRndFlags = 0;
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;
950 if (bHideability)
951 m_dwRndFlags |= ERF_HIDABLE;
952 if (bHideabilitySecondary)
953 m_dwRndFlags |= ERF_HIDABLE_SECONDARY;
954 if (bPickable)
955 m_dwRndFlags |= ERF_PICKABLE;
956 if (!bAllowIndoor)
957 m_dwRndFlags |= ERF_OUTDOORONLY;
958 if (bGIMode)
959 m_dwRndFlags |= ERF_GI_MODE_BIT0; // corresponds to IRenderNode::eGM_StaticVoxelization
961 uint32 nSpec = (uint32)minConfigSpec;
962 if (nSpec != 0)
964 m_dwRndFlags &= ~ERF_SPEC_BITS_MASK;
965 m_dwRndFlags |= (nSpec << ERF_SPEC_BITS_SHIFT) & ERF_SPEC_BITS_MASK;
968 if (GetStatObj())
970 fVegRadiusVert = GetStatObj()->GetRadiusVert();
971 fVegRadiusHor = GetStatObj()->GetRadiusHors();
972 fVegRadius = max(fVegRadiusVert, fVegRadiusHor);
974 else
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;
988 else
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();
1002 #endif
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
1015 int nPosStride = 0;
1016 byte* pPos = pRenderMesh->GetPosPtr(nPosStride, FSL_READ);
1018 // get indices
1019 vtx_idx* pInds = pRenderMesh->GetIndexPtr(FSL_READ);
1020 int nInds = pRenderMesh->GetIndicesCount();
1021 assert(nInds % 3 == 0);
1023 // test tris
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)
1029 continue;
1031 // skip transparent and alpha test
1032 if (pMat)
1034 const SShaderItem& shaderItem = pMat->GetShaderItem(pChunk->m_nMatID);
1035 if (!shaderItem.m_pShader || shaderItem.m_pShader->GetFlags() & EF_NODRAW)
1036 continue;
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]]);
1051 AABB triBox;
1052 triBox.min = v0;
1053 triBox.max = v0;
1054 triBox.Add(v1);
1055 triBox.Add(v2);
1057 if (Overlap::Sphere_AABB(Sphere(vInPos, fRadius), triBox))
1058 return true;
1062 return false;
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())
1076 garbage.resize(0);
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.
1083 CStatObj* pStatObj;
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);
1094 else
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()
1127 if (!m_pRMBox)
1129 MakeUnitCube();
1131 return m_pRMBox;
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)
1168 return true;
1170 if (pLodValue && pLodValue->LodA() == -1 && pLodValue->DissolveRefB() == 255)
1171 return true;
1173 if (pLodValue && pLodValue->LodB() == -1 && pLodValue->DissolveRefA() == 255)
1174 return true;
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);
1201 return true;
1204 // Permanent object needs to be filled first time,
1205 if (pTempData && pTempData->userData.pOwnerNode)
1206 pTempData->userData.nStatObjLastModificationId = GetResourcesModificationChecksum(pTempData->userData.pOwnerNode);
1208 else
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;
1218 return false;
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;
1247 ZeroStruct(vert);
1248 vert.st = Vec2(0.0f, 0.0f);
1249 vert.color.dcolor = -1;
1251 // verts
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);
1265 // tangents
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));
1271 // indices
1272 arrIndices.Add(0);
1273 arrIndices.Add(1);
1274 arrIndices.Add(2);
1275 arrIndices.Add(1);
1276 arrIndices.Add(3);
1277 arrIndices.Add(2);
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;