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.
o3de/Code/CryEngine/Cry3DEngine/StatObjStream.cpp

530 lines
16 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
#include "Cry3DEngine_precompiled.h"
#include "StatObj.h"
#include "IndexedMesh.h"
#include "../RenderDll/Common/Shadow_Renderer.h"
#include <IRenderer.h>
#include <CrySizer.h>
#include "CGF/CGFLoader.h"
#include "CGF/CGFSaver.h"
#include "CGF/ReadOnlyChunkFile.h"
#define GEOM_INFO_FILE_EXT "ginfo"
#define MESH_NAME_FOR_MAIN "main"
extern const char* stristr(const char* szString, const char* szSubstring);
extern void TransformMesh(CMesh& mesh, Matrix34 tm);
#if !defined (_RELEASE)
float CStatObj::s_fStreamingTime = 0.0f;
int CStatObj::s_nBandwidth = 0;
#endif
void CStatObj::StreamAsyncOnComplete(IReadStream* pStream, unsigned nError)
{
FUNCTION_PROFILER_3DENGINE;
if (nError == ERROR_CANT_OPEN_FILE && m_bHasStreamOnlyCGF)
{
// Deliberately ignore
}
else if (pStream->IsError())
{ // file was not loaded successfully
m_eStreamingStatus = ecss_Ready;
if (pStream->GetError() != ERROR_USER_ABORT && pStream->GetError() != ERROR_ABORTED_ON_SHUTDOWN)
{
Error("CStatObj::StreamAsyncOnComplete: Error loading CGF: %s Error: %s", m_szFileName.c_str(), pStream->GetErrorName());
}
}
else
{
#if !defined (_RELEASE)
float timeinseconds = (gEnv->pTimer->GetCurrTime() - m_fStreamingStart);
s_nBandwidth += pStream->GetBytesRead();
s_fStreamingTime += timeinseconds;
m_fStreamingStart = 0.0f;
#endif
if (!LoadStreamRenderMeshes(0, pStream->GetBuffer(), pStream->GetBytesRead(), strstr(m_szFileName.c_str(), "_lod") != NULL))
{
Error("CStatObj::StreamOnComplete_LoadCGF_FromMemBlock, filename=%s", m_szFileName.c_str());
}
}
pStream->FreeTemporaryMemory(); // We dont need internal stream loaded buffer anymore.
}
//////////////////////////////////////////////////////////////////////////
void CStatObj::StreamOnComplete(IReadStream* pStream, unsigned nError)
{
FUNCTION_PROFILER_3DENGINE;
// Synchronous callback.
if (nError == ERROR_CANT_OPEN_FILE && m_bHasStreamOnlyCGF)
{
m_bHasStreamOnlyCGF = 0;
m_eStreamingStatus = ecss_NotLoaded;
}
else if (pStream->IsError())
{
// file was not loaded successfully
if (pStream->GetError() != ERROR_USER_ABORT && pStream->GetError() != ERROR_ABORTED_ON_SHUTDOWN)
{
Error("CStatObj::StreamOnComplete: Error loading CGF: %s Error: %s", m_szFileName.c_str(), pStream->GetErrorName());
}
m_eStreamingStatus = ecss_Ready;
}
else
{
CommitStreamRenderMeshes();
//////////////////////////////////////////////////////////////////////////
// Forces vegetation sprites to be marked for regeneration from new mesh.
//////////////////////////////////////////////////////////////////////////
for (int nSID = 0; nSID < m_pObjManager->GetListStaticTypes().Count(); nSID++)
{
PodArray<StatInstGroup>& rGroupTable = m_pObjManager->GetListStaticTypes()[nSID];
for (int nGroupId = 0, nGroups = rGroupTable.Count(); nGroupId < nGroups; nGroupId++)
{
StatInstGroup& rGroup = rGroupTable[nGroupId];
if (rGroup.pStatObj == this)
{
rGroup.Update(GetCVars(), Get3DEngine()->GetGeomDetailScreenRes());
}
}
}
//////////////////////////////////////////////////////////////////////////
#ifdef OBJMAN_STREAM_STATS
if (m_pStreamListener)
{
m_pStreamListener->OnReceivedStreamedObject(static_cast<IStreamable*>(this));
}
#endif
m_eStreamingStatus = ecss_Ready;
}
m_pReadStream = 0;
}
void CStatObj::GetStreamFilePath(stack_string& strOut)
{
strOut = m_szFileName.c_str();
strOut.append("m");
}
//////////////////////////////////////////////////////////////////////////
void CStatObj::StartStreaming(bool bFinishNow, IReadStream_AutoPtr* ppStream)
{
assert(!m_pParentObject);
assert(m_eStreamingStatus == ecss_NotLoaded);
if (m_eStreamingStatus != ecss_NotLoaded)
{
return;
}
// start streaming
StreamReadParams params;
params.dwUserData = 0;
params.nSize = 0;
params.pBuffer = NULL;
params.nLoadTime = 10000;
params.nMaxLoadTime = 10000;
if (bFinishNow)
{
params.ePriority = estpUrgent;
}
if (m_szFileName.empty())
{
assert(!"CStatObj::StartStreaming: CGF name is empty");
m_eStreamingStatus = ecss_Ready;
if (ppStream)
{
*ppStream = NULL;
}
return;
}
#if !defined (_RELEASE)
m_fStreamingStart = gEnv->pTimer->GetCurrTime();
#endif //!defined (_RELEASE)
const char* path = m_szFileName.c_str();
stack_string streamPath;
if (m_bHasStreamOnlyCGF)
{
GetStreamFilePath(streamPath);
path = streamPath.c_str();
}
m_pReadStream = GetSystem()->GetStreamEngine()->StartRead(eStreamTaskTypeGeometry, path, this, &params);
if (ppStream)
{
(*ppStream) = m_pReadStream;
}
if (!bFinishNow)
{
m_eStreamingStatus = ecss_InProgress;
}
else if (!ppStream)
{
m_pReadStream->Wait();
}
}
void CStatObj::ReleaseStreamableContent()
{
assert(!m_pParentObject);
assert(!m_pClonedSourceObject);
assert(!m_bSharesChildren);
bool bLodsAreLoadedFromSeparateFile = m_pLod0 ? m_pLod0->IsLodsAreLoadedFromSeparateFile() : m_bLodsAreLoadedFromSeparateFile;
if (!bLodsAreLoadedFromSeparateFile)
{
for (int nLod = 0; nLod < MAX_STATOBJ_LODS_NUM; nLod++)
{
CStatObj* pLod = (CStatObj*)GetLodObject(nLod);
if (!pLod)
{
continue;
}
pLod->SetRenderMesh(0);
pLod->m_eStreamingStatus = ecss_NotLoaded;
if (pLod->m_pParentObject)
{
pLod->m_pParentObject->SetRenderMesh(0);
pLod->m_pParentObject->m_eStreamingStatus = ecss_NotLoaded;
}
}
}
for (int s = 0; s < GetSubObjectCount(); s++)
{
SSubObject& sub = *GetSubObject(s);
if (!sub.pStatObj)
{
continue;
}
if (bLodsAreLoadedFromSeparateFile)
{
CStatObj* pSubLod = (CStatObj*)sub.pStatObj;
if (!pSubLod)
{
continue;
}
pSubLod->SetRenderMesh(0);
pSubLod->m_eStreamingStatus = ecss_NotLoaded;
if (pSubLod->m_pParentObject)
{
pSubLod->m_pParentObject->SetRenderMesh(0);
pSubLod->m_pParentObject->m_eStreamingStatus = ecss_NotLoaded;
}
}
else
{
for (int nLod = 0; nLod < MAX_STATOBJ_LODS_NUM; nLod++)
{
CStatObj* pSubLod = (CStatObj*)sub.pStatObj->GetLodObject(nLod);
if (!pSubLod)
{
continue;
}
pSubLod->SetRenderMesh(0);
pSubLod->m_eStreamingStatus = ecss_NotLoaded;
if (pSubLod->m_pParentObject)
{
pSubLod->m_pParentObject->SetRenderMesh(0);
pSubLod->m_pParentObject->m_eStreamingStatus = ecss_NotLoaded;
}
}
}
}
SetRenderMesh(0);
m_pMergedRenderMesh = 0;
m_eStreamingStatus = ecss_NotLoaded;
}
int CStatObj::GetStreamableContentMemoryUsage([[maybe_unused]] bool bJustForDebug)
{
assert(!m_pParentObject || bJustForDebug); // only parents allowed to be registered for streaming
bool bLodsAreLoadedFromSeparateFile = m_pLod0 ? m_pLod0->IsLodsAreLoadedFromSeparateFile() : m_bLodsAreLoadedFromSeparateFile;
bool bCountLods = !bLodsAreLoadedFromSeparateFile;
if (m_arrRenderMeshesPotentialMemoryUsage[bCountLods] < 0)
{
int nCount = 0;
if (bCountLods)
{
if (m_pLODs)
{
for (int nLod = 1; nLod < MAX_STATOBJ_LODS_NUM; nLod++)
{
CStatObj* pLod = (CStatObj*)m_pLODs[nLod];
if (!pLod)
{
continue;
}
nCount += pLod->m_nRenderMeshMemoryUsage;
}
}
}
nCount += m_nRenderMeshMemoryUsage;
for (int s = 0; s < GetSubObjectCount(); s++)
{
SSubObject& sub = *GetSubObject(s);
if (!sub.pStatObj)
{
continue;
}
if (bCountLods)
{
CStatObj* pSubStatObj = (CStatObj*)sub.pStatObj;
if (pSubStatObj->m_pLODs)
{
for (int nLod = 1; nLod < MAX_STATOBJ_LODS_NUM; nLod++)
{
CStatObj* pSubLod = pSubStatObj->m_pLODs[nLod];
if (!pSubLod)
{
continue;
}
nCount += pSubLod->m_nRenderMeshMemoryUsage;
}
}
}
nCount += ((CStatObj*)sub.pStatObj)->m_nRenderMeshMemoryUsage;
}
m_arrRenderMeshesPotentialMemoryUsage[bCountLods] = nCount;
}
if (m_pMergedRenderMesh)
{
//TODO: Need to have a function to calculate memory usage.
//m_nMergedMemoryUsage = m_pMergedRenderMesh->GetAllocatedBytes(true);
m_nMergedMemoryUsage = m_pMergedRenderMesh->GetVerticesCount() * (sizeof(SPipTangents) + sizeof(SVF_P3S_C4B_T2S)) + m_pMergedRenderMesh->GetIndicesCount() * sizeof(uint16);
}
else
if (!GetCVars()->e_StatObjMerge)
{
m_nMergedMemoryUsage = 0;
}
return m_nMergedMemoryUsage + m_arrRenderMeshesPotentialMemoryUsage[bCountLods];
}
void CStatObj::UpdateStreamingPrioriryInternal(const Matrix34A& objMatrix, float fImportance, bool bFullUpdate)
{
int nRoundId = GetObjManager()->GetUpdateStreamingPrioriryRoundId();
if (m_pParentObject && m_bSubObject)
{ // stream parent for sub-objects
m_pParentObject->UpdateStreamingPrioriryInternal(objMatrix, fImportance, bFullUpdate);
}
else if (!m_bSubObject)
{ // stream object itself
if (m_bCanUnload)
{
if (UpdateStreamingPrioriryLowLevel(fImportance, nRoundId, bFullUpdate))
{
GetObjManager()->RegisterForStreaming(this);
}
}
}
else if (m_pLod0)
{ // sub-object lod without parent
m_pLod0->UpdateStreamingPrioriryInternal(objMatrix, fImportance, bFullUpdate);
assert(!m_pLod0->IsLodsAreLoadedFromSeparateFile());
}
else if (m_bCanUnload)
{
assert(!"Invalid CGF hierarchy");
}
}
bool CStatObj::UpdateStreamableComponents(float fImportance, const Matrix34A& objMatrix, bool bFullUpdate, int nNewLod)
{
if (m_pLod0) // redirect to lod0, otherwise we fail to pre-cache neighbor LODs
{
return m_pLod0->UpdateStreamableComponents(fImportance, objMatrix, bFullUpdate, nNewLod);
}
#ifndef _RELEASE
if (GetCVars()->e_StreamCgfDebug && strlen(GetCVars()->e_StreamCgfDebugFilter->GetString()) && strstr(m_szFileName, GetCVars()->e_StreamCgfDebugFilter->GetString()))
{
PrintMessage(__FUNCTION__);
}
#endif
// FUNCTION_PROFILER_3DENGINE;
if (m_nFlags & STATIC_OBJECT_HIDDEN)
{
return false;
}
nNewLod = min(nNewLod, MAX_STATOBJ_LODS_NUM - 1);
if ((m_nFlags & STATIC_OBJECT_COMPOUND) && SubObjectCount())
{
for (int s = 0, num = SubObjectCount(); s < num; s++)
{
IStatObj::SSubObject& subObj = SubObject(s);
if (subObj.pStatObj && subObj.nType == STATIC_SUB_OBJECT_MESH && !subObj.bShadowProxy)
{
Matrix34 subObjMatrix = objMatrix * subObj.tm;
CStatObj* pSubStatObj = ((CStatObj*)subObj.pStatObj);
if (pSubStatObj->m_nLoadedTrisCount < 1)
{
continue;
}
for (int l = nNewLod; l <= (nNewLod + 1) && l < MAX_STATOBJ_LODS_NUM; l++)
{
if (CStatObj* pLod = (CStatObj*)pSubStatObj->GetLodObject(l, true))
{
pLod->UpdateStreamingPrioriryInternal(objMatrix, fImportance, bFullUpdate);
if (!(CStatObj*)pSubStatObj->m_pLODs)
{
break;
}
}
}
}
}
}
else if (m_nLoadedTrisCount >= 1)
{
for (int l = nNewLod; l <= (nNewLod + 1) && l < MAX_STATOBJ_LODS_NUM; l++)
{
if (CStatObj* pLod = (CStatObj*)GetLodObject(l, true))
{
pLod->UpdateStreamingPrioriryInternal(objMatrix, fImportance, bFullUpdate);
if (!m_pLODs)
{
break;
}
}
}
}
// update also next state CGF
if (!m_szStreamingDependencyFilePath.empty())
{
if (CStatObj* pNextState = (CStatObj*)GetObjManager()->FindStaticObjectByFilename(m_szStreamingDependencyFilePath))
{
pNextState->UpdateStreamableComponents(fImportance, objMatrix, bFullUpdate, nNewLod);
}
}
if (m_pParentObject && !m_pParentObject->m_szStreamingDependencyFilePath.empty())
{
if (CStatObj* pNextState = (CStatObj*)GetObjManager()->FindStaticObjectByFilename(m_pParentObject->m_szStreamingDependencyFilePath))
{
pNextState->UpdateStreamableComponents(fImportance, objMatrix, bFullUpdate, nNewLod);
}
}
return true;
}
void CStatObj::DisableStreaming()
{
for (int nLodLevel = 0; nLodLevel < MAX_STATOBJ_LODS_NUM; nLodLevel++)
{
if (CStatObj* pLodObj = (CStatObj*)GetLodObject(nLodLevel))
{
pLodObj->m_nLastDrawMainFrameId = GetRenderer()->GetFrameID(false) + 1000;
pLodObj->UpdateStreamingPrioriryLowLevel(1.f, GetObjManager()->GetUpdateStreamingPrioriryRoundId(), true);
pLodObj->m_bCanUnload = false;
// only register the parent object for streaming, it will stream in all subobject + lods
if (pLodObj->m_pParentObject)
{
GetObjManager()->RegisterForStreaming(pLodObj->m_pParentObject);
}
else
{
GetObjManager()->RegisterForStreaming(pLodObj);
}
}
}
}
bool CStatObj::CheckForStreamingDependencyLoop(const char* szFilenameDependancy) const
{
IObjManager* pObjManager = GetObjManager();
while (szFilenameDependancy)
{
const CStatObj* pStatObjDependency = static_cast<const CStatObj*>(pObjManager->FindStaticObjectByFilename(szFilenameDependancy));
if (!pStatObjDependency)
{
return false;
}
if (this == pStatObjDependency)
{
return true;
}
szFilenameDependancy = pStatObjDependency->m_szStreamingDependencyFilePath.c_str();
}
return false;
}