You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1206 lines
41 KiB
C++
1206 lines
41 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
// Description : Loading trees, buildings, ragister/unregister entities for rendering
|
|
|
|
|
|
#include "Cry3DEngine_precompiled.h"
|
|
|
|
#include "StatObj.h"
|
|
#include "ObjMan.h"
|
|
#include "VisAreas.h"
|
|
|
|
#include "CullBuffer.h"
|
|
#include "3dEngine.h"
|
|
#include "IndexedMesh.h"
|
|
#include "DecalRenderNode.h"
|
|
#include "FogVolumeRenderNode.h"
|
|
#include "GeomCacheRenderNode.h"
|
|
#include "CryPhysicsDeprecation.h"
|
|
|
|
int CObjManager::m_nUpdateStreamingPrioriryRoundId = 1;
|
|
int CObjManager::m_nUpdateStreamingPrioriryRoundIdFast = 1;
|
|
int CObjManager::s_nLastStreamingMemoryUsage = 0;
|
|
|
|
struct CObjManager_Cmp_Streamable_Priority
|
|
{
|
|
// returns true if v1 < v2
|
|
ILINE bool operator()(const SStreamAbleObject& v1, const SStreamAbleObject& v2) const
|
|
{
|
|
IStreamable* arrObj[2] = { v1.GetStreamAbleObject(), v2.GetStreamAbleObject() };
|
|
|
|
// compare priorities
|
|
if (v1.fCurImportance > v2.fCurImportance)
|
|
{
|
|
return true;
|
|
}
|
|
if (v1.fCurImportance < v2.fCurImportance)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// give low lod's and small meshes higher priority
|
|
int MemUsage0 = v1.GetStreamableContentMemoryUsage();
|
|
int MemUsage1 = v2.GetStreamableContentMemoryUsage();
|
|
if (MemUsage0 < MemUsage1)
|
|
{
|
|
return true;
|
|
}
|
|
if (MemUsage0 > MemUsage1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// fix sorting consistency
|
|
if (arrObj[0] > arrObj[1])
|
|
{
|
|
return true;
|
|
}
|
|
if (arrObj[0] < arrObj[1])
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
void CObjManager::RegisterForStreaming(IStreamable* pObj)
|
|
{
|
|
SStreamAbleObject streamAbleObject(pObj);
|
|
if (m_arrStreamableObjects.Find(streamAbleObject) < 0)
|
|
{
|
|
m_arrStreamableObjects.Add(streamAbleObject);
|
|
|
|
#ifdef OBJMAN_STREAM_STATS
|
|
if (m_pStreamListener)
|
|
{
|
|
string name;
|
|
pObj->GetStreamableName(name);
|
|
m_pStreamListener->OnCreatedStreamedObject(name.c_str(), pObj);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void CObjManager::UnregisterForStreaming(IStreamable* pObj)
|
|
{
|
|
if (m_arrStreamableObjects.size() > 0)
|
|
{
|
|
SStreamAbleObject streamAbleObject(pObj, false);
|
|
bool deleted = m_arrStreamableObjects.Delete(streamAbleObject);
|
|
|
|
#ifdef OBJMAN_STREAM_STATS
|
|
if (deleted && m_pStreamListener)
|
|
{
|
|
m_pStreamListener->OnDestroyedStreamedObject(pObj);
|
|
}
|
|
#endif
|
|
|
|
if (m_arrStreamableObjects.empty())
|
|
{
|
|
stl::free_container(m_arrStreamableObjects);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CObjManager::UpdateObjectsStreamingPriority(bool bSyncLoad, const SRenderingPassInfo& passInfo)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE_LEGACYONLY;
|
|
AZ_TRACE_METHOD();
|
|
|
|
const size_t nPrecachePoints = m_vStreamPreCachePointDefs.size();
|
|
const bool bNeedsUnique = nPrecachePoints > 1;
|
|
|
|
if (bSyncLoad)
|
|
{
|
|
PrintMessage("Updating level streaming priorities for %" PRISIZE_T " cameras (LevelFrameId = %d)", nPrecachePoints, Get3DEngine()->GetStreamingFramesSinceLevelStart());
|
|
for (size_t pci = 0; pci < nPrecachePoints; ++pci)
|
|
{
|
|
PrintMessage("-- %f %f %f",
|
|
m_vStreamPreCacheCameras[m_vStreamPreCachePointDefs[pci].nId].vPosition.x,
|
|
m_vStreamPreCacheCameras[m_vStreamPreCachePointDefs[pci].nId].vPosition.y,
|
|
m_vStreamPreCacheCameras[m_vStreamPreCachePointDefs[pci].nId].vPosition.z);
|
|
}
|
|
}
|
|
|
|
CVisAreaManager* pVisAreaMgr = GetVisAreaManager();
|
|
|
|
if (bSyncLoad)
|
|
{
|
|
m_arrStreamingNodeStack.clear();
|
|
}
|
|
|
|
bool bPrecacheNear = true;
|
|
|
|
if (bSyncLoad || (passInfo.GetFrameID() & 3) || GetFloatCVar(e_StreamCgfFastUpdateMaxDistance) == 0)
|
|
{
|
|
bPrecacheNear = false;
|
|
|
|
if (!m_arrStreamingNodeStack.Count())
|
|
{
|
|
FRAME_PROFILER("UpdateObjectsStreamingPriority_Init", GetSystem(), PROFILE_3DENGINE);
|
|
|
|
if (GetCVars()->e_StreamCgf == 2)
|
|
{
|
|
PrintMessage("UpdateObjectsStreamingPriority_Restart %d", passInfo.GetFrameID());
|
|
}
|
|
|
|
for (size_t ppIdx = 0, ppCount = nPrecachePoints; ppIdx != ppCount; ++ppIdx)
|
|
{
|
|
const Vec3& vPrecachePoint = m_vStreamPreCacheCameras[ppIdx].vPosition;
|
|
|
|
CVisArea* pCurArea = pVisAreaMgr ? (CVisArea*)pVisAreaMgr->GetVisAreaFromPos(vPrecachePoint) : NULL;
|
|
if (CVisArea* pRoot0 = pCurArea)
|
|
{
|
|
m_tmpAreas0.Clear();
|
|
pRoot0->AddConnectedAreas(m_tmpAreas0, GetCVars()->e_StreamPredictionMaxVisAreaRecursion);
|
|
|
|
bool bFoundOutside = false;
|
|
|
|
if (!GetCVars()->e_StreamPredictionAlwaysIncludeOutside)
|
|
{
|
|
for (int v = 0; v < m_tmpAreas0.Count(); v++)
|
|
{
|
|
CVisArea* pN1 = m_tmpAreas0[v];
|
|
if (pN1->IsPortal() && pN1->m_lstConnections.Count() == 1)
|
|
{
|
|
bFoundOutside = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bFoundOutside = true;
|
|
}
|
|
|
|
if (bFoundOutside)
|
|
{
|
|
if (Get3DEngine()->IsObjectTreeReady())
|
|
{
|
|
m_arrStreamingNodeStack.Add(Get3DEngine()->GetObjectTree());
|
|
}
|
|
}
|
|
|
|
for (int v = 0; v < m_tmpAreas0.Count(); v++)
|
|
{
|
|
CVisArea* pN1 = m_tmpAreas0[v];
|
|
assert(bNeedsUnique || m_arrStreamingNodeStack.Find(pN1->m_pObjectsTree) < 0);
|
|
if (pN1->m_pObjectsTree)
|
|
{
|
|
m_arrStreamingNodeStack.Add(pN1->m_pObjectsTree);
|
|
}
|
|
}
|
|
}
|
|
else if (GetVisAreaManager())
|
|
{
|
|
if (Get3DEngine()->IsObjectTreeReady())
|
|
{
|
|
m_arrStreamingNodeStack.Add(Get3DEngine()->GetObjectTree());
|
|
}
|
|
|
|
// find portals around
|
|
m_tmpAreas0.Clear();
|
|
GetVisAreaManager()->MakeActiveEntransePortalsList(NULL, m_tmpAreas0, NULL, passInfo);
|
|
|
|
// make list of areas for streaming
|
|
m_tmpAreas1.Clear();
|
|
for (int p = 0; p < m_tmpAreas0.Count(); p++)
|
|
{
|
|
if (CVisArea* pRoot = m_tmpAreas0[p])
|
|
{
|
|
pRoot->AddConnectedAreas(m_tmpAreas1, GetCVars()->e_StreamPredictionMaxVisAreaRecursion);
|
|
}
|
|
}
|
|
|
|
// fill list of object trees
|
|
for (int v = 0; v < m_tmpAreas1.Count(); v++)
|
|
{
|
|
CVisArea* pN1 = m_tmpAreas1[v];
|
|
assert(bNeedsUnique || m_arrStreamingNodeStack.Find(pN1->m_pObjectsTree) < 0);
|
|
if (pN1->m_pObjectsTree)
|
|
{
|
|
m_arrStreamingNodeStack.Add(pN1->m_pObjectsTree);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Get3DEngine()->IsObjectTreeReady())
|
|
{
|
|
m_arrStreamingNodeStack.Add(Get3DEngine()->GetObjectTree());
|
|
}
|
|
}
|
|
}
|
|
|
|
IF_UNLIKELY (bNeedsUnique)
|
|
{
|
|
std::sort(m_arrStreamingNodeStack.begin(), m_arrStreamingNodeStack.end());
|
|
m_arrStreamingNodeStack.resize(std::distance(m_arrStreamingNodeStack.begin(), std::unique(m_arrStreamingNodeStack.begin(), m_arrStreamingNodeStack.end())));
|
|
}
|
|
}
|
|
|
|
{
|
|
// Time-sliced scene streaming priority update
|
|
// Update scene faster if in zoom and if camera moving fast
|
|
float fMaxTimeToSpendMS = GetCVars()->e_StreamPredictionUpdateTimeSlice * max(Get3DEngine()->GetAverageCameraSpeed() * .5f, 1.f) / max(passInfo.GetZoomFactor(), 0.1f);
|
|
fMaxTimeToSpendMS = min(fMaxTimeToSpendMS, GetCVars()->e_StreamPredictionUpdateTimeSlice * 2.f);
|
|
|
|
CTimeValue maxTimeToSpend;
|
|
maxTimeToSpend.SetSeconds(fMaxTimeToSpendMS * 0.001f);
|
|
|
|
const CTimeValue startTime = GetTimer()->GetAsyncTime();
|
|
|
|
const float fMinDist = GetFloatCVar(e_StreamPredictionMinFarZoneDistance);
|
|
const float fMaxViewDistance = Get3DEngine()->GetMaxViewDistance();
|
|
|
|
{
|
|
FRAME_PROFILER("UpdateObjectsStreamingPriority_MarkNodes", GetSystem(), PROFILE_3DENGINE);
|
|
|
|
while (m_arrStreamingNodeStack.Count())
|
|
{
|
|
COctreeNode* pLast = m_arrStreamingNodeStack.Last();
|
|
m_arrStreamingNodeStack.DeleteLast();
|
|
|
|
pLast->UpdateStreamingPriority(m_arrStreamingNodeStack, fMinDist, fMaxViewDistance, false, &m_vStreamPreCacheCameras[0], nPrecachePoints, passInfo);
|
|
|
|
if (!bSyncLoad && (GetTimer()->GetAsyncTime() - startTime) > maxTimeToSpend)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!m_arrStreamingNodeStack.Count())
|
|
{
|
|
// Round has done.
|
|
++m_nUpdateStreamingPrioriryRoundId;
|
|
}
|
|
}
|
|
|
|
if (bPrecacheNear || bSyncLoad)
|
|
{
|
|
FRAME_PROFILER("UpdateObjectsStreamingPriority_Mark_NEAR_Nodes", GetSystem(), PROFILE_3DENGINE);
|
|
|
|
PodArray<COctreeNode*> fastStreamingNodeStack;
|
|
const int nVisAreaRecursion = min(GetCVars()->e_StreamPredictionMaxVisAreaRecursion, 2);
|
|
|
|
for (size_t ppIdx = 0, ppCount = nPrecachePoints; ppIdx != ppCount; ++ppIdx)
|
|
{
|
|
const Vec3& vPrecachePoint = m_vStreamPreCacheCameras[ppIdx].vPosition;
|
|
|
|
CVisArea* pCurArea = pVisAreaMgr ? (CVisArea*)pVisAreaMgr->GetVisAreaFromPos(vPrecachePoint) : NULL;
|
|
if (CVisArea* pRoot0 = pCurArea)
|
|
{
|
|
m_tmpAreas0.Clear();
|
|
pRoot0->AddConnectedAreas(m_tmpAreas0, nVisAreaRecursion);
|
|
|
|
bool bFoundOutside = false;
|
|
|
|
if (!GetCVars()->e_StreamPredictionAlwaysIncludeOutside)
|
|
{
|
|
for (int v = 0; v < m_tmpAreas0.Count(); v++)
|
|
{
|
|
CVisArea* pN1 = m_tmpAreas0[v];
|
|
if (pN1->IsPortal() && pN1->m_lstConnections.Count() == 1)
|
|
{
|
|
bFoundOutside = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bFoundOutside = true;
|
|
}
|
|
|
|
if (bFoundOutside)
|
|
{
|
|
if (Get3DEngine()->IsObjectTreeReady())
|
|
{
|
|
fastStreamingNodeStack.Add(Get3DEngine()->GetObjectTree());
|
|
}
|
|
}
|
|
|
|
for (int v = 0; v < m_tmpAreas0.Count(); v++)
|
|
{
|
|
CVisArea* pN1 = m_tmpAreas0[v];
|
|
assert(bNeedsUnique || fastStreamingNodeStack.Find(pN1->m_pObjectsTree) < 0);
|
|
if (pN1->m_pObjectsTree)
|
|
{
|
|
fastStreamingNodeStack.Add(pN1->m_pObjectsTree);
|
|
}
|
|
}
|
|
}
|
|
else if (GetVisAreaManager())
|
|
{
|
|
if (Get3DEngine()->IsObjectTreeReady())
|
|
{
|
|
fastStreamingNodeStack.Add(Get3DEngine()->GetObjectTree());
|
|
}
|
|
|
|
// find portals around
|
|
m_tmpAreas0.Clear();
|
|
GetVisAreaManager()->MakeActiveEntransePortalsList(NULL, m_tmpAreas0, NULL, passInfo);
|
|
|
|
// make list of areas for streaming
|
|
m_tmpAreas1.Clear();
|
|
for (int p = 0; p < m_tmpAreas0.Count(); p++)
|
|
{
|
|
if (CVisArea* pRoot = m_tmpAreas0[p])
|
|
{
|
|
pRoot->AddConnectedAreas(m_tmpAreas1, nVisAreaRecursion);
|
|
}
|
|
}
|
|
|
|
// fill list of object trees
|
|
for (int v = 0; v < m_tmpAreas1.Count(); v++)
|
|
{
|
|
CVisArea* pN1 = m_tmpAreas1[v];
|
|
assert(bNeedsUnique || fastStreamingNodeStack.Find(pN1->m_pObjectsTree) < 0);
|
|
if (pN1->m_pObjectsTree)
|
|
{
|
|
fastStreamingNodeStack.Add(pN1->m_pObjectsTree);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Get3DEngine()->IsObjectTreeReady())
|
|
{
|
|
fastStreamingNodeStack.Add(Get3DEngine()->GetObjectTree());
|
|
}
|
|
}
|
|
}
|
|
|
|
IF_UNLIKELY (bNeedsUnique)
|
|
{
|
|
std::sort(fastStreamingNodeStack.begin(), fastStreamingNodeStack.end());
|
|
fastStreamingNodeStack.resize(std::distance(fastStreamingNodeStack.begin(), std::unique(fastStreamingNodeStack.begin(), fastStreamingNodeStack.end())));
|
|
}
|
|
|
|
const float fMaxDist = max(0.f, GetFloatCVar(e_StreamCgfFastUpdateMaxDistance) - GetFloatCVar(e_StreamPredictionDistanceFar));
|
|
|
|
while (fastStreamingNodeStack.Count())
|
|
{
|
|
COctreeNode* pLast = fastStreamingNodeStack.Last();
|
|
fastStreamingNodeStack.DeleteLast();
|
|
|
|
pLast->UpdateStreamingPriority(fastStreamingNodeStack, 0.f, fMaxDist, true, &m_vStreamPreCacheCameras[0], nPrecachePoints, passInfo);
|
|
}
|
|
|
|
m_nUpdateStreamingPrioriryRoundIdFast++;
|
|
}
|
|
}
|
|
|
|
void CObjManager::CheckTextureReadyFlag()
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
if (m_lstStaticTypes.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
static uint32 nSID = 0;
|
|
static uint32 nGroupId = 0;
|
|
|
|
if (nSID >= m_lstStaticTypes.size())
|
|
{
|
|
nSID = 0;
|
|
}
|
|
|
|
PodArray<StatInstGroup>& rGroupTable = m_lstStaticTypes[nSID];
|
|
|
|
if (nGroupId >= rGroupTable.size())
|
|
{
|
|
nGroupId = 0;
|
|
nSID++;
|
|
}
|
|
|
|
nGroupId++;
|
|
}
|
|
|
|
void CObjManager::ProcessObjectsStreaming(const SRenderingPassInfo& passInfo)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE_LEGACYONLY;
|
|
AZ_TRACE_METHOD();
|
|
|
|
IF (!GetCVars()->e_StreamCgf, 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// this assert is most likely triggered by forgetting to call
|
|
// 3dEngine::SyncProcessStreamingUpdate at the end of the frame, leading to multiple
|
|
// updates, but not starting and/or stopping streaming tasks
|
|
assert(m_bNeedProcessObjectsStreaming_Finish == false);
|
|
if (m_bNeedProcessObjectsStreaming_Finish == true)
|
|
{
|
|
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_ERROR, "ProcessObjectsStreaming invoked without a following ProcessObjectsStreaming_Finish, please check your update logic");
|
|
}
|
|
|
|
const CCamera& rCamera = passInfo.GetCamera();
|
|
|
|
float fTimeStart = GetTimer()->GetAsyncCurTime();
|
|
|
|
bool bSyncLoad = Get3DEngine()->IsStatObjSyncLoad();
|
|
|
|
if (!m_bCameraPrecacheOverridden)
|
|
{
|
|
SObjManPrecacheCamera& precachePoint = m_vStreamPreCacheCameras[0];
|
|
|
|
if (rCamera.GetPosition().GetDistance(precachePoint.vPosition) >= GetFloatCVar(e_StreamCgfGridUpdateDistance))
|
|
{
|
|
Vec3 vOffset = Get3DEngine()->GetAverageCameraMoveDir() * GetFloatCVar(e_StreamPredictionAhead);
|
|
vOffset.z *= .5f;
|
|
precachePoint.vPosition = rCamera.GetPosition() + vOffset;
|
|
|
|
// Raycast for precache points
|
|
CRY_PHYSICS_REPLACEMENT_ASSERT();
|
|
|
|
if IsCVarConstAccess(constexpr) (bool(GetFloatCVar(e_StreamPredictionAheadDebug)))
|
|
{
|
|
DrawSphere(precachePoint.vPosition, 0.5f);
|
|
}
|
|
}
|
|
|
|
if (Distance::Point_AABBSq(precachePoint.vPosition, precachePoint.bbox) > 0.0f)
|
|
{
|
|
precachePoint.bbox = AABB(precachePoint.vPosition, GetCVars()->e_StreamPredictionBoxRadius);
|
|
}
|
|
}
|
|
|
|
if (bSyncLoad && Get3DEngine()->IsShadersSyncLoad())
|
|
{
|
|
PrintMessage("Pre-caching render meshes, shaders and textures");
|
|
}
|
|
else if (bSyncLoad)
|
|
{
|
|
PrintMessage("Pre-caching render meshes for camera position");
|
|
}
|
|
|
|
CTimeValue currentTime = gEnv->pTimer->GetAsyncTime();
|
|
|
|
bool bSyncLoadPoints = m_bCameraPrecacheOverridden || Get3DEngine()->IsContentPrecacheRequested() || bSyncLoad || (GetCVars()->e_StreamCgf == 3) || (GetCVars()->e_StreamCgfDebugHeatMap != 0);
|
|
UpdateObjectsStreamingPriority(bSyncLoadPoints, passInfo);
|
|
|
|
// Remove stale precache points
|
|
|
|
size_t ppWriteIdx = 0;
|
|
for (size_t ppIdx = 0, ppCount = m_vStreamPreCachePointDefs.size(); ppIdx != ppCount; ++ppIdx)
|
|
{
|
|
SObjManPrecachePoint& pp = m_vStreamPreCachePointDefs[ppIdx];
|
|
|
|
if (ppIdx == 0 || currentTime < pp.expireTime)
|
|
{
|
|
m_vStreamPreCachePointDefs[ppWriteIdx] = pp;
|
|
m_vStreamPreCacheCameras[ppWriteIdx] = m_vStreamPreCacheCameras[ppIdx];
|
|
++ppWriteIdx;
|
|
}
|
|
}
|
|
m_vStreamPreCachePointDefs.resize(ppWriteIdx);
|
|
m_vStreamPreCacheCameras.resize(ppWriteIdx);
|
|
|
|
m_bCameraPrecacheOverridden = false;
|
|
|
|
m_bNeedProcessObjectsStreaming_Finish = true;
|
|
ProcessObjectsStreaming_Impl(bSyncLoad, passInfo);
|
|
|
|
// during precache don't run asynchrony and sync directly to ensure the ESYSTEM_EVENT_LEVEL_PRECACHED
|
|
// event is send to activate the renderthread
|
|
// this also applies to the editor, since it calls the render function multiple times and thus invoking the
|
|
// function multiple times without syncing
|
|
if (bSyncLoad || gEnv->IsEditor())
|
|
{
|
|
ProcessObjectsStreaming_Finish();
|
|
}
|
|
|
|
if (bSyncLoad)
|
|
{
|
|
float t = GetTimer()->GetAsyncCurTime() - fTimeStart;
|
|
if (t > (1.0f / 15.0f))
|
|
{
|
|
PrintMessage("Finished pre-caching in %.1f sec", t);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CObjManager::ProcessObjectsStreaming_Impl(bool bSyncLoad, const SRenderingPassInfo& passInfo)
|
|
{
|
|
ProcessObjectsStreaming_Sort(bSyncLoad, passInfo);
|
|
ProcessObjectsStreaming_Release();
|
|
#ifdef OBJMAN_STREAM_STATS
|
|
ProcessObjectsStreaming_Stats(passInfo);
|
|
#endif
|
|
ProcessObjectsStreaming_InitLoad(bSyncLoad);
|
|
}
|
|
|
|
void CObjManager::ProcessObjectsStreaming_Sort(bool bSyncLoad, const SRenderingPassInfo& passInfo)
|
|
{
|
|
int nNumStreamableObjects = m_arrStreamableObjects.Count();
|
|
|
|
static float fLastTime = 0;
|
|
const float fTime = GetTimer()->GetAsyncCurTime();
|
|
|
|
// call sort only every 100 ms
|
|
if (nNumStreamableObjects && ((fTime > fLastTime + 0.1f) || bSyncLoad))
|
|
{
|
|
FRAME_PROFILER("ProcessObjectsStreaming_Sort", GetSystem(), PROFILE_3DENGINE);
|
|
|
|
SStreamAbleObject* arrStreamableObjects = &m_arrStreamableObjects[0];
|
|
assert(arrStreamableObjects);
|
|
|
|
const float fMeshStreamingMaxIImportance = 10.f;
|
|
|
|
if (bSyncLoad)
|
|
{
|
|
// just put file offset into importance
|
|
for (int i = 0; i < nNumStreamableObjects; i++)
|
|
{
|
|
SStreamAbleObject& rObj = arrStreamableObjects[i];
|
|
|
|
if (!rObj.GetStreamAbleObject()->IsUnloadable())
|
|
{
|
|
rObj.fCurImportance = fMeshStreamingMaxIImportance;
|
|
continue;
|
|
}
|
|
|
|
string fileName;
|
|
rObj.GetStreamAbleObject()->GetStreamableName(fileName);
|
|
int nOffset = (int)(GetPak()->GetFileOffsetOnMedia(fileName.c_str()) / 1024);
|
|
rObj.fCurImportance = -(float)nOffset;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// use data of previous prediction round since current round is not finished yet
|
|
int nRoundId = CObjManager::m_nUpdateStreamingPrioriryRoundId - 1;
|
|
|
|
for (int i = 0; i < nNumStreamableObjects; i++)
|
|
{
|
|
SStreamAbleObject& rObj = arrStreamableObjects[i];
|
|
|
|
if (!rObj.GetStreamAbleObject()->IsUnloadable())
|
|
{
|
|
rObj.fCurImportance = fMeshStreamingMaxIImportance;
|
|
continue;
|
|
}
|
|
|
|
// compute importance of objects for selected nRoundId
|
|
rObj.fCurImportance = -1000.f;
|
|
|
|
IStreamable::SInstancePriorityInfo* pInfo = &(rObj.GetStreamAbleObject()->m_arrUpdateStreamingPrioriryRoundInfo[0]);
|
|
|
|
for (int nRoundIdx = 0; nRoundIdx < 2; nRoundIdx++)
|
|
{
|
|
if (pInfo[nRoundIdx].nRoundId == nRoundId)
|
|
{
|
|
rObj.fCurImportance = pInfo[nRoundIdx].fMaxImportance;
|
|
if (rObj.GetLastDrawMainFrameId() > (passInfo.GetMainFrameID() - GetCVars()->e_RNTmpDataPoolMaxFrames))
|
|
{
|
|
rObj.fCurImportance += GetFloatCVar(e_StreamCgfVisObjPriority);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort(&arrStreamableObjects[0], &arrStreamableObjects[nNumStreamableObjects], CObjManager_Cmp_Streamable_Priority());
|
|
|
|
fLastTime = fTime;
|
|
}
|
|
}
|
|
|
|
void CObjManager::ProcessObjectsStreaming_Release()
|
|
{
|
|
FRAME_PROFILER("ProcessObjectsStreaming_Release", GetSystem(), PROFILE_3DENGINE);
|
|
int nMemoryUsage = 0;
|
|
|
|
int nNumStreamableObjects = m_arrStreamableObjects.Count();
|
|
SStreamAbleObject* arrStreamableObjects = nNumStreamableObjects ? &m_arrStreamableObjects[0] : NULL;
|
|
|
|
for (int nObjId = 0; nObjId < nNumStreamableObjects; nObjId++)
|
|
{
|
|
const SStreamAbleObject& rObj = arrStreamableObjects[nObjId];
|
|
|
|
nMemoryUsage += rObj.GetStreamableContentMemoryUsage();
|
|
|
|
bool bUnload = nMemoryUsage >= GetCVars()->e_StreamCgfPoolSize * 1024 * 1024;
|
|
|
|
if (!bUnload && GetCVars()->e_StreamCgfDebug == 4)
|
|
{
|
|
if (rObj.GetStreamAbleObject()->m_arrUpdateStreamingPrioriryRoundInfo[0].nRoundId < (CObjManager::m_nUpdateStreamingPrioriryRoundId - 8))
|
|
{
|
|
bUnload = true;
|
|
}
|
|
}
|
|
|
|
if (bUnload && rObj.GetStreamAbleObject()->IsUnloadable())
|
|
{
|
|
if (rObj.GetStreamAbleObject()->m_eStreamingStatus == ecss_Ready)
|
|
{
|
|
m_arrStreamableToRelease.push_back(rObj.GetStreamAbleObject());
|
|
}
|
|
|
|
// remove from list if not active for long time
|
|
if (rObj.GetStreamAbleObject()->m_eStreamingStatus == ecss_NotLoaded)
|
|
{
|
|
if (rObj.GetStreamAbleObject()->m_arrUpdateStreamingPrioriryRoundInfo[0].nRoundId < (CObjManager::m_nUpdateStreamingPrioriryRoundId - 8))
|
|
{
|
|
rObj.GetStreamAbleObject()->m_arrUpdateStreamingPrioriryRoundInfo[0].nRoundId = 0;
|
|
m_arrStreamableToDelete.push_back(rObj.GetStreamAbleObject());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
s_nLastStreamingMemoryUsage = nMemoryUsage;
|
|
}
|
|
|
|
void CObjManager::ProcessObjectsStreaming_InitLoad(bool bSyncLoad)
|
|
{
|
|
FRAME_PROFILER("ProcessObjectsStreaming_InitLoad", GetSystem(), PROFILE_3DENGINE);
|
|
|
|
int nMaxInProgress = GetCVars()->e_StreamCgfMaxTasksInProgress;
|
|
int nMaxToStart = GetCVars()->e_StreamCgfMaxNewTasksPerUpdate;
|
|
int nMaxMemUsage = GetCVars()->e_StreamCgfPoolSize * 1024 * 1024;
|
|
int nNumStreamableObjects = m_arrStreamableObjects.Count();
|
|
SStreamAbleObject* arrStreamableObjects = nNumStreamableObjects ? &m_arrStreamableObjects[0] : NULL;
|
|
|
|
int nMemoryUsage = 0;
|
|
int nInProgress = 0;
|
|
int nInProgressMem = 0;
|
|
int nStarted = 0;
|
|
|
|
SMeshPoolStatistics stats;
|
|
GetRenderer()->EF_Query(EFQ_GetMeshPoolInfo, stats);
|
|
size_t poolLimit = stats.nPoolSize << 2; // 4 times the pool limit because the rendermesh pool has been reduced
|
|
|
|
for (int nObjId = 0; nObjId < nNumStreamableObjects; nObjId++)
|
|
{
|
|
const SStreamAbleObject& rObj = m_arrStreamableObjects[nObjId];
|
|
if (rObj.GetStreamAbleObject()->m_eStreamingStatus == ecss_InProgress)
|
|
{
|
|
nInProgress++;
|
|
nInProgressMem += rObj.GetStreamableContentMemoryUsage();
|
|
}
|
|
}
|
|
|
|
for (int nObjId = 0; (nObjId < nNumStreamableObjects) && ((nInProgress < nMaxInProgress && (nInProgressMem < static_cast<int>(poolLimit) || !poolLimit) && nStarted < nMaxToStart) || bSyncLoad); nObjId++)
|
|
{
|
|
const SStreamAbleObject& rObj = arrStreamableObjects[nObjId];
|
|
IStreamable* pStatObj = rObj.GetStreamAbleObject();
|
|
|
|
int size = rObj.GetStreamableContentMemoryUsage();
|
|
if (poolLimit && poolLimit <= (size_t)(nInProgressMem + size))
|
|
{
|
|
// Actually the above check is an implicit size limit on CGFs - which is awful because it can lead to meshes never
|
|
// streamed in. This has to change after crysis2 - for now, this will include a white list of meshes that need to be
|
|
// loaded for crysis2 in the prism2 level.
|
|
if ((size_t)poolLimit <= (size_t)size)
|
|
{
|
|
string name;
|
|
pStatObj->GetStreamableName(name);
|
|
CryLogAlways("[WARNING] object '%s' skipped because too large (%d kb (>= %" PRISIZE_T " kb limit))", name.c_str(), size / 1024, poolLimit / 1024);
|
|
continue;
|
|
}
|
|
|
|
if (!bSyncLoad)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
nMemoryUsage += size;
|
|
if (nMemoryUsage >= nMaxMemUsage)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (rObj.GetStreamAbleObject()->m_eStreamingStatus == ecss_NotLoaded)
|
|
{
|
|
m_arrStreamableToLoad.push_back(rObj.GetStreamAbleObject());
|
|
nInProgressMem += size;
|
|
++nInProgress;
|
|
++nStarted;
|
|
|
|
if ((GetCVars()->e_AutoPrecacheCgf == 2) && (nObjId > nNumStreamableObjects / 2))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CObjManager::ProcessObjectsStreaming_Finish()
|
|
{
|
|
if (m_bNeedProcessObjectsStreaming_Finish == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LOADING_TIME_PROFILE_SECTION;
|
|
|
|
int nNumStreamableObjects = m_arrStreamableObjects.Count();
|
|
m_bNeedProcessObjectsStreaming_Finish = false;
|
|
|
|
FRAME_PROFILER("ProcessObjectsStreaming_Finish", GetSystem(), PROFILE_3DENGINE);
|
|
bool bSyncLoad = Get3DEngine()->IsStatObjSyncLoad();
|
|
|
|
{
|
|
LOADING_TIME_PROFILE_SECTION;
|
|
|
|
// now unload the stat object
|
|
while (!m_arrStreamableToRelease.empty())
|
|
{
|
|
IStreamable* pStatObj = m_arrStreamableToRelease.back();
|
|
m_arrStreamableToRelease.DeleteLast();
|
|
if (pStatObj)
|
|
{
|
|
pStatObj->ReleaseStreamableContent();
|
|
|
|
#ifdef OBJMAN_STREAM_STATS
|
|
if (m_pStreamListener)
|
|
{
|
|
m_pStreamListener->OnUnloadedStreamedObject(pStatObj);
|
|
}
|
|
#endif
|
|
|
|
if (GetCVars()->e_StreamCgfDebug == 2)
|
|
{
|
|
string sName;
|
|
pStatObj->GetStreamableName(sName);
|
|
PrintMessage("Unloaded: %s", sName.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "ProcessObjectsStreaming_Finish is trying to release streamable content of a deleted pStatObj");
|
|
}
|
|
}
|
|
|
|
int nSyncObjCounter = 0;
|
|
|
|
// start streaming of stat objects
|
|
if (bSyncLoad)
|
|
{
|
|
gEnv->pRenderer->EnableBatchMode(true);
|
|
}
|
|
|
|
//const uint nMaxNumberOfSyncStreamingRequests = GetCVars()->e_AutoPrecacheCgfMaxTasks;
|
|
|
|
std::vector<IReadStreamPtr> streamsToFinish;
|
|
|
|
GetISystem()->GetStreamEngine()->BeginReadGroup(); // Make sure new streaming requests are accumulated
|
|
|
|
for (size_t nId = 0; nId < m_arrStreamableToLoad.size(); nId++)
|
|
{
|
|
IStreamable* pStatObj = m_arrStreamableToLoad[nId];
|
|
|
|
// If there is a rendermesh pool present, only submit streaming job if
|
|
// the pool size in the renderer is sufficient. NOTE: This is a weak
|
|
// test. Better test would be to actually preallocate the memory here and
|
|
// only submit the streaming job if the memory could be obtained.
|
|
int size = pStatObj->GetStreamableContentMemoryUsage();
|
|
|
|
if (GetCVars()->e_StreamCgfDebug == 2)
|
|
{
|
|
string sName;
|
|
pStatObj->GetStreamableName(sName);
|
|
PrintMessage("Loading: %s", sName.c_str());
|
|
}
|
|
|
|
pStatObj->StartStreaming(false, NULL);
|
|
|
|
#ifdef OBJMAN_STREAM_STATS
|
|
if (m_pStreamListener)
|
|
{
|
|
m_pStreamListener->OnRequestedStreamedObject(pStatObj);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
GetISystem()->GetStreamEngine()->EndReadGroup(); // Make sure new streaming requests are accumulated
|
|
|
|
if (bSyncLoad)
|
|
{
|
|
gEnv->pRenderer->EnableBatchMode(false);
|
|
}
|
|
|
|
if (bSyncLoad && nSyncObjCounter)
|
|
{
|
|
PrintMessage("Finished synchronous pre-cache of render meshes for %d CGF's", nSyncObjCounter);
|
|
}
|
|
|
|
m_arrStreamableToLoad.clear();
|
|
|
|
// remove no longer needed objects from list
|
|
while (!m_arrStreamableToDelete.empty())
|
|
{
|
|
IStreamable* pStatObj = m_arrStreamableToDelete.back();
|
|
m_arrStreamableToDelete.DeleteLast();
|
|
SStreamAbleObject stramAbleObject(pStatObj);
|
|
m_arrStreamableObjects.Delete(stramAbleObject);
|
|
|
|
#ifdef OBJMAN_STREAM_STATS
|
|
if (m_pStreamListener)
|
|
{
|
|
m_pStreamListener->OnDestroyedStreamedObject(pStatObj);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef OBJMAN_STREAM_STATS
|
|
void CObjManager::ProcessObjectsStreaming_Stats(const SRenderingPassInfo& passInfo)
|
|
{
|
|
IStreamedObjectListener* pListener = m_pStreamListener;
|
|
if (!pListener)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int nNumStreamableObjects = m_arrStreamableObjects.Count();
|
|
SStreamAbleObject* arrStreamableObjects = nNumStreamableObjects ? &m_arrStreamableObjects[0] : NULL;
|
|
|
|
int nCurrentFrameId = passInfo.GetMainFrameID();
|
|
|
|
void* pBegunUse[512];
|
|
void* pEndUse[512];
|
|
|
|
int nBegunUse = 0;
|
|
int nEndUse = 0;
|
|
|
|
for (int nObjId = 0; nObjId < nNumStreamableObjects; nObjId++)
|
|
{
|
|
const SStreamAbleObject& rObj = arrStreamableObjects[nObjId];
|
|
IStreamable* pObj = rObj.GetStreamAbleObject();
|
|
|
|
if (pObj->m_eStreamingStatus == ecss_Ready)
|
|
{
|
|
int framesSinceLastUse = nCurrentFrameId - pObj->GetLastDrawMainFrameId();
|
|
|
|
if (framesSinceLastUse < 2)
|
|
{
|
|
if (!pObj->m_nStatsInUse)
|
|
{
|
|
pObj->m_nStatsInUse = 1;
|
|
pBegunUse[nBegunUse++] = pObj;
|
|
|
|
IF_UNLIKELY (nBegunUse == sizeof(pBegunUse) / sizeof(pBegunUse[0]))
|
|
{
|
|
pListener->OnBegunUsingStreamedObjects(pBegunUse, nBegunUse);
|
|
nBegunUse = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pObj->m_nStatsInUse)
|
|
{
|
|
pObj->m_nStatsInUse = 0;
|
|
pEndUse[nEndUse++] = pObj;
|
|
|
|
IF_UNLIKELY (nEndUse == sizeof(pEndUse) / sizeof(pEndUse[0]))
|
|
{
|
|
pListener->OnEndedUsingStreamedObjects(pEndUse, nEndUse);
|
|
nEndUse = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nBegunUse)
|
|
{
|
|
pListener->OnBegunUsingStreamedObjects(pBegunUse, nBegunUse);
|
|
}
|
|
|
|
if (nEndUse)
|
|
{
|
|
pListener->OnEndedUsingStreamedObjects(pEndUse, nEndUse);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CObjManager::GetObjectsStreamingStatus(I3DEngine::SObjectsStreamingStatus& outStatus)
|
|
{
|
|
outStatus.nReady = outStatus.nInProgress = outStatus.nTotal = outStatus.nAllocatedBytes = outStatus.nMemRequired = 0;
|
|
outStatus.nMeshPoolSize = GetCVars()->e_StreamCgfPoolSize;
|
|
|
|
for (LoadedObjects::iterator it = m_lstLoadedObjects.begin(); it != m_lstLoadedObjects.end(); ++it)
|
|
{
|
|
IStatObj* pStatObj = *it;
|
|
if (pStatObj->IsSubObject())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pStatObj->GetLodLevel0())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (int l = 0; l < MAX_STATOBJ_LODS_NUM; l++)
|
|
{
|
|
if (CStatObj* pLod = (CStatObj*)pStatObj->GetLodObject(l))
|
|
{
|
|
outStatus.nTotal++;
|
|
}
|
|
}
|
|
}
|
|
|
|
outStatus.nActive = m_arrStreamableObjects.Count();
|
|
|
|
for (int nObjId = 0; nObjId < m_arrStreamableObjects.Count(); nObjId++)
|
|
{
|
|
const SStreamAbleObject& rStreamAbleObject = m_arrStreamableObjects[nObjId];
|
|
|
|
if (rStreamAbleObject.GetStreamAbleObject()->m_eStreamingStatus == ecss_Ready)
|
|
{
|
|
outStatus.nReady++;
|
|
}
|
|
|
|
if (rStreamAbleObject.GetStreamAbleObject()->m_eStreamingStatus == ecss_InProgress)
|
|
{
|
|
outStatus.nInProgress++;
|
|
}
|
|
|
|
if (rStreamAbleObject.GetStreamAbleObject()->m_eStreamingStatus == ecss_Ready)
|
|
{
|
|
outStatus.nAllocatedBytes += rStreamAbleObject.GetStreamableContentMemoryUsage();
|
|
}
|
|
|
|
if (rStreamAbleObject.GetStreamAbleObject()->m_arrUpdateStreamingPrioriryRoundInfo[0].nRoundId >= (CObjManager::m_nUpdateStreamingPrioriryRoundId - 4))
|
|
{
|
|
outStatus.nMemRequired += rStreamAbleObject.GetStreamableContentMemoryUsage();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CObjManager::PrecacheStatObjMaterial(_smart_ptr<IMaterial> pMaterial, const float fEntDistance, IStatObj* pStatObj, bool bFullUpdate, bool bDrawNear)
|
|
{
|
|
// FUNCTION_PROFILER_3DENGINE;
|
|
|
|
if (!pMaterial && pStatObj)
|
|
{
|
|
pMaterial = pStatObj->GetMaterial();
|
|
}
|
|
|
|
if (!pMaterial)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pStatObj)
|
|
{
|
|
for (int i = 0; i < pStatObj->GetSubObjectCount(); i++)
|
|
{
|
|
PrecacheStatObjMaterial(pMaterial, fEntDistance, pStatObj->GetSubObject(i)->pStatObj, bFullUpdate, bDrawNear);
|
|
}
|
|
}
|
|
|
|
if (CMatInfo* pMatInfo = static_cast<CMatInfo*>(pMaterial.get()))
|
|
{
|
|
CStatObj* pCStatObj = static_cast<CStatObj*>(pStatObj);
|
|
pMatInfo->PrecacheMaterial(fEntDistance, (pStatObj ? pStatObj->GetRenderMesh() : NULL), bFullUpdate, bDrawNear);
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
void CollectRenderMeshMaterials(_smart_ptr<IMaterial> pMaterial, IRenderMesh* pRenderMesh, std::vector<std::pair<_smart_ptr<IMaterial>, float> >& collectedMaterials)
|
|
{
|
|
if (!pMaterial || !pRenderMesh)
|
|
{
|
|
return;
|
|
}
|
|
|
|
stl::push_back_unique(collectedMaterials, std::pair<_smart_ptr<IMaterial>, float>(pMaterial, 1.0f));
|
|
|
|
TRenderChunkArray& chunks = pRenderMesh->GetChunks();
|
|
const uint subMtlCount = pMaterial->GetSubMtlCount();
|
|
|
|
const uint numChunks = chunks.size();
|
|
for (uint i = 0; i < numChunks; ++i)
|
|
{
|
|
CRenderChunk& chunk = chunks[i];
|
|
if (chunk.nNumIndices > 0 && chunk.nNumVerts > 0 && chunk.m_nMatID < subMtlCount)
|
|
{
|
|
stl::push_back_unique(collectedMaterials, std::pair<_smart_ptr<IMaterial>, float>(pMaterial->GetSubMtl(chunk.m_nMatID), chunk.m_texelAreaDensity));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CObjManager::PrecacheStatObj(IStatObj* pStatObj, int nLod, const Matrix34A& statObjMatrix, _smart_ptr<IMaterial> pMaterial, float fImportance, float fEntDistance, bool bFullUpdate, bool bHighPriority)
|
|
{
|
|
if (!pStatObj)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int minLod = pStatObj->GetMinUsableLod();
|
|
const int maxLod = (int)pStatObj->GetMaxUsableLod();
|
|
const int minPrecacheLod = clamp_tpl(nLod - 1, minLod, maxLod);
|
|
const int maxPrecacheLod = clamp_tpl(nLod + 1, minLod, maxLod);
|
|
|
|
for (int currentLod = minPrecacheLod; currentLod <= maxPrecacheLod; ++currentLod)
|
|
{
|
|
IStatObj* pLodStatObj = pStatObj->GetLodObject(currentLod, true);
|
|
_smart_ptr<IMaterial> pLodMaterial = pLodStatObj->GetMaterial();
|
|
PrecacheStatObjMaterial(pMaterial ? pMaterial : pLodMaterial, fEntDistance, pLodStatObj, bFullUpdate, bHighPriority);
|
|
pStatObj->UpdateStreamableComponents(fImportance, statObjMatrix, bFullUpdate, nLod);
|
|
}
|
|
}
|
|
|
|
void CObjManager::UpdateRenderNodeStreamingPriority(IRenderNode* pObj, float fEntDistanceReal, float fImportanceFactor, bool bFullUpdate, const SRenderingPassInfo& passInfo, bool bHighPriority)
|
|
{
|
|
// FUNCTION_PROFILER_3DENGINE;
|
|
|
|
if (pObj->m_dwRndFlags & ERF_HIDDEN)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pObj->m_fWSMaxViewDist < 0.01f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// check cvars
|
|
const EERType nodeType = pObj->GetRenderNodeType();
|
|
|
|
const float fEntDistance = max(0.f, fEntDistanceReal - GetFloatCVar(e_StreamPredictionDistanceNear));
|
|
|
|
float fImportance = (1.f - (fEntDistance / ((pObj->m_fWSMaxViewDist + GetFloatCVar(e_StreamPredictionDistanceFar))))) * fImportanceFactor;
|
|
|
|
if (fImportance < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float fObjScale = 1.f;
|
|
AABB objBox(pObj->GetBBox());
|
|
|
|
switch (nodeType)
|
|
{
|
|
case eERType_Decal:
|
|
if (!passInfo.RenderDecals())
|
|
{
|
|
return;
|
|
}
|
|
fObjScale = max(0.001f, ((CDecalRenderNode*)pObj)->CDecalRenderNode::GetMatrix().GetColumn0().GetLength());
|
|
break;
|
|
case eERType_WaterVolume:
|
|
if (!passInfo.RenderWaterVolumes())
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
case eERType_Light:
|
|
if (!GetCVars()->e_DynamicLights)
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
case eERType_FogVolume:
|
|
fObjScale = max(0.001f, ((CFogVolumeRenderNode*)pObj)->CFogVolumeRenderNode::GetMatrix().GetColumn0().GetLength());
|
|
break;
|
|
case eERType_Cloud:
|
|
case eERType_DistanceCloud:
|
|
fObjScale = max(0.001f, pObj->GetBBox().GetRadius());
|
|
break;
|
|
#if defined(USE_GEOM_CACHES)
|
|
case eERType_GeomCache:
|
|
if (!passInfo.RenderGeomCaches())
|
|
{
|
|
return;
|
|
}
|
|
fObjScale = max(0.001f, ((CGeomCacheRenderNode*)pObj)->CGeomCacheRenderNode::GetMatrix().GetColumn0().GetLength());
|
|
#endif
|
|
default:
|
|
if (!passInfo.RenderEntities())
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
float fInvObjScale = 1.0f / fObjScale;
|
|
int nLod = CObjManager::GetObjectLOD(pObj, fEntDistanceReal);
|
|
_smart_ptr<IMaterial> pRenderNodeMat = pObj->GetMaterialOverride();
|
|
|
|
if (pObj->m_pRNTmpData)
|
|
{
|
|
if IsCVarConstAccess(constexpr) (GetFloatCVar(e_StreamCgfGridUpdateDistance) != 0.0f || GetFloatCVar(e_StreamPredictionAhead) != 0.0f || GetFloatCVar(e_StreamPredictionMinFarZoneDistance) != 0.0f)
|
|
{
|
|
float fDistanceToCam = sqrt_tpl(Distance::Point_AABBSq(passInfo.GetCamera().GetPosition(), objBox)) * passInfo.GetZoomFactor();
|
|
pObj->m_pRNTmpData->userData.nWantedLod = CObjManager::GetObjectLOD(pObj, fDistanceToCam);
|
|
}
|
|
else
|
|
{
|
|
pObj->m_pRNTmpData->userData.nWantedLod = nLod;
|
|
}
|
|
}
|
|
|
|
const int nSlotCount = pObj->GetSlotCount();
|
|
for (int nEntSlot = 0; nEntSlot < nSlotCount; ++nEntSlot)
|
|
{
|
|
bool bDrawNear = false;
|
|
|
|
_smart_ptr<IMaterial> pSlotMat = pObj->GetEntitySlotMaterial(nEntSlot, false, &bDrawNear);
|
|
if (!pSlotMat)
|
|
{
|
|
pSlotMat = pRenderNodeMat;
|
|
}
|
|
|
|
// If the object is in camera space, don't use the prediction position.
|
|
const float fEntPrecacheDistance = (bDrawNear)
|
|
? sqrt_tpl(Distance::Point_AABBSq(passInfo.GetCamera().GetPosition(), objBox))
|
|
: fEntDistance;
|
|
|
|
bDrawNear |= bHighPriority;
|
|
|
|
Matrix34A matParent;
|
|
CStatObj* pStatObj = (CStatObj*)pObj->GetEntityStatObj(nEntSlot, 0, &matParent, false);
|
|
if (pStatObj)
|
|
{
|
|
_smart_ptr<IMaterial> pStatObjMat = pStatObj->GetMaterial();
|
|
PrecacheStatObj(pStatObj, nLod, matParent, pSlotMat ? pSlotMat : pStatObjMat, fImportance, fEntDistanceReal * fInvObjScale, bFullUpdate, bHighPriority);
|
|
}
|
|
|
|
#if defined(USE_GEOM_CACHES)
|
|
//This is the legacy case where the CGeomCacheRenderNode is a slot in a CComponentRenderer
|
|
else if (CGeomCacheRenderNode* pGeomCacheRenderNode = static_cast<CGeomCacheRenderNode*>(pObj->GetGeomCacheRenderNode(nEntSlot, &matParent, false)))
|
|
{
|
|
pGeomCacheRenderNode->UpdateStreamableComponents(fImportance, fEntDistance, bFullUpdate, nLod, fInvObjScale, bFullUpdate);
|
|
}
|
|
//For the newer AZ systems CGeomCacheRenderNodes are not tied to the CComponentRenderer
|
|
else if (nodeType == eERType_GeomCache)
|
|
{
|
|
CGeomCacheRenderNode* geomCacheRenderNode = static_cast<CGeomCacheRenderNode*>(pObj);
|
|
geomCacheRenderNode->UpdateStreamableComponents(fImportance, fEntDistance, bFullUpdate, nLod, fInvObjScale, bFullUpdate);
|
|
}
|
|
#endif
|
|
else if (pSlotMat)
|
|
{
|
|
pSlotMat->PrecacheMaterial(fEntDistance * fInvObjScale, pObj->GetRenderMesh(nLod), bFullUpdate, bHighPriority);
|
|
}
|
|
}
|
|
}
|