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.
1100 lines
32 KiB
C++
1100 lines
32 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 : Manages geometry cache data
|
|
|
|
#include "Cry3DEngine_precompiled.h"
|
|
|
|
#if defined(USE_GEOM_CACHES)
|
|
|
|
#include <CryPath.h>
|
|
#include "GeomCache.h"
|
|
#include "GeomCacheManager.h"
|
|
#include "GeomCacheDecoder.h"
|
|
#include "StringUtils.h"
|
|
#include "MatMan.h"
|
|
|
|
#include <CryPhysicsDeprecation.h>
|
|
|
|
CGeomCache::CGeomCache(const char* pFileName)
|
|
: m_bValid(false)
|
|
, m_bLoaded(false)
|
|
, m_refCount(0)
|
|
, m_fileName(pFileName)
|
|
, m_lastDrawMainFrameId(0)
|
|
, m_bPlaybackFromMemory(false)
|
|
, m_numFrames(0)
|
|
, m_compressedAnimationDataSize(0)
|
|
, m_totalUncompressedAnimationSize(0)
|
|
, m_numStreams(0)
|
|
, m_staticMeshDataOffset(0)
|
|
, m_aabb(0.0f)
|
|
{
|
|
m_bUseStreaming = GetCVars()->e_StreamCgf > 0;
|
|
m_staticDataHeader.m_compressedSize = 0;
|
|
m_staticDataHeader.m_uncompressedSize = 0;
|
|
|
|
string materialPath = PathUtil::ReplaceExtension(m_fileName, "mtl");
|
|
m_pMaterial = GetMatMan()->LoadMaterial(materialPath);
|
|
|
|
if (!LoadGeomCache())
|
|
{
|
|
FileWarning(0, m_fileName.c_str(), "Failed to load geometry cache: %s", m_lastError.c_str());
|
|
stl::free_container(m_lastError);
|
|
}
|
|
}
|
|
|
|
CGeomCache::~CGeomCache()
|
|
{
|
|
Shutdown();
|
|
}
|
|
|
|
int CGeomCache::AddRef()
|
|
{
|
|
++m_refCount;
|
|
return m_refCount;
|
|
}
|
|
|
|
int CGeomCache::Release()
|
|
{
|
|
assert(m_refCount >= 0);
|
|
|
|
--m_refCount;
|
|
int refCount = m_refCount;
|
|
if (m_refCount <= 0)
|
|
{
|
|
GetGeomCacheManager()->DeleteGeomCache(this);
|
|
}
|
|
|
|
return refCount;
|
|
}
|
|
|
|
void CGeomCache::SetMaterial(_smart_ptr<IMaterial> pMaterial)
|
|
{
|
|
m_pMaterial = pMaterial;
|
|
}
|
|
|
|
_smart_ptr<IMaterial> CGeomCache::GetMaterial()
|
|
{
|
|
return m_pMaterial;
|
|
}
|
|
|
|
const _smart_ptr<IMaterial> CGeomCache::GetMaterial() const
|
|
{
|
|
return m_pMaterial;
|
|
}
|
|
|
|
const char* CGeomCache::GetFilePath() const
|
|
{
|
|
return m_fileName;
|
|
}
|
|
|
|
bool CGeomCache::LoadGeomCache()
|
|
{
|
|
using namespace GeomCacheFile;
|
|
|
|
LOADING_TIME_PROFILE_SECTION;
|
|
|
|
CRY_DEFINE_ASSET_SCOPE("GeomCache", m_fileName);
|
|
|
|
AZ::IO::ScopedFileHandle geomCacheFileHandle(*gEnv->pCryPak, m_fileName.c_str(), "rb");
|
|
if (!geomCacheFileHandle.IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
size_t bytesRead = 0;
|
|
|
|
// Read header and check signature
|
|
SHeader header;
|
|
bytesRead = gEnv->pCryPak->FReadRaw((void*)&header, 1, sizeof(SHeader), geomCacheFileHandle);
|
|
if (bytesRead != sizeof(SHeader))
|
|
{
|
|
m_lastError = "Could not read header";
|
|
return false;
|
|
}
|
|
|
|
if (header.m_signature != kFileSignature)
|
|
{
|
|
m_lastError = "Bad file signature";
|
|
return false;
|
|
}
|
|
|
|
if (header.m_version != kCurrentVersion)
|
|
{
|
|
m_lastError = "Bad file version";
|
|
return false;
|
|
}
|
|
|
|
const bool bFileHas32BitIndexFormat = (header.m_flags & eFileHeaderFlags_32BitIndices) != 0;
|
|
if (((sizeof(vtx_idx) == sizeof(uint16)) && bFileHas32BitIndexFormat) || ((sizeof(vtx_idx) == sizeof(uint32)) && !bFileHas32BitIndexFormat))
|
|
{
|
|
m_lastError = "Index format mismatch";
|
|
return false;
|
|
}
|
|
|
|
if (header.m_blockCompressionFormat != eBlockCompressionFormat_None &&
|
|
header.m_blockCompressionFormat != eBlockCompressionFormat_Deflate &&
|
|
header.m_blockCompressionFormat != eBlockCompressionFormat_LZ4HC &&
|
|
header.m_blockCompressionFormat != eBlockCompressionFormat_ZSTD)
|
|
{
|
|
m_lastError = "Bad block compression format";
|
|
return false;
|
|
}
|
|
|
|
m_bPlaybackFromMemory = (header.m_flags & GeomCacheFile::eFileHeaderFlags_PlaybackFromMemory) != 0;
|
|
m_blockCompressionFormat = static_cast<EBlockCompressionFormat>(header.m_blockCompressionFormat);
|
|
m_totalUncompressedAnimationSize = header.m_totalUncompressedAnimationSize;
|
|
m_numFrames = header.m_numFrames;
|
|
m_aabb.min = Vec3(header.m_aabbMin[0], header.m_aabbMin[1], header.m_aabbMin[2]);
|
|
m_aabb.max = Vec3(header.m_aabbMax[0], header.m_aabbMax[1], header.m_aabbMax[2]);
|
|
|
|
// Read frame times and frame offsets.
|
|
if (!ReadFrameInfos(geomCacheFileHandle, header.m_numFrames))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const int maxPlaybackFromMemorySize = std::max(0, GetCVars()->e_GeomCacheMaxPlaybackFromMemorySize);
|
|
if (m_bPlaybackFromMemory && m_compressedAnimationDataSize > uint(maxPlaybackFromMemorySize * 1024 * 1024))
|
|
{
|
|
GetLog()->LogWarning("Animated data size of geometry cache '%s' is over memory playback limit "
|
|
"of %d MiB. Reverting to stream playback.", m_fileName.c_str(), maxPlaybackFromMemorySize);
|
|
m_bPlaybackFromMemory = false;
|
|
}
|
|
|
|
// Load static node data and physics geometries
|
|
{
|
|
std::vector<char> compressedData;
|
|
std::vector<char> decompressedData;
|
|
|
|
if (!ReadStaticBlock(geomCacheFileHandle, (EBlockCompressionFormat)(header.m_blockCompressionFormat), compressedData)
|
|
|| !DecompressStaticBlock((EBlockCompressionFormat)(header.m_blockCompressionFormat), &compressedData[0], decompressedData))
|
|
{
|
|
if (m_lastError.empty())
|
|
{
|
|
m_lastError = "Could not read or decompress static block";
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CGeomCacheStreamReader reader(&decompressedData[0], decompressedData.size());
|
|
if (!ReadNodesStaticDataRec(reader))
|
|
{
|
|
if (m_lastError.empty())
|
|
{
|
|
m_lastError = "Could not read node static data";
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_staticMeshDataOffset = gEnv->pCryPak->FTell(geomCacheFileHandle);
|
|
|
|
if (!m_bUseStreaming)
|
|
{
|
|
std::vector<char> compressedData;
|
|
std::vector<char> decompressedData;
|
|
|
|
if (!ReadStaticBlock(geomCacheFileHandle, (EBlockCompressionFormat)(header.m_blockCompressionFormat), compressedData)
|
|
|| !DecompressStaticBlock((EBlockCompressionFormat)(header.m_blockCompressionFormat), &compressedData[0], decompressedData))
|
|
{
|
|
if (m_lastError.empty())
|
|
{
|
|
m_lastError = "Could not read or decompress static block";
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CGeomCacheStreamReader reader(&decompressedData[0], decompressedData.size());
|
|
if (!ReadMeshesStaticData(reader, m_fileName.c_str()))
|
|
{
|
|
if (m_lastError.empty())
|
|
{
|
|
m_lastError = "Could not read mesh static data";
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (m_bPlaybackFromMemory && m_frameInfos.size() > 0)
|
|
{
|
|
std::vector<char> animationData;
|
|
animationData.resize(static_cast<size_t>(m_compressedAnimationDataSize));
|
|
bytesRead = gEnv->pCryPak->FReadRaw((void*)&animationData[0], 1, static_cast<uint>(m_compressedAnimationDataSize), geomCacheFileHandle);
|
|
|
|
if (!LoadAnimatedData(&animationData[0], 0))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_bLoaded = true;
|
|
}
|
|
else
|
|
{
|
|
bytesRead = gEnv->pCryPak->FReadRaw((void*)&m_staticDataHeader, 1, sizeof(GeomCacheFile::SCompressedBlockHeader), geomCacheFileHandle);
|
|
if (bytesRead != sizeof(GeomCacheFile::SCompressedBlockHeader))
|
|
{
|
|
m_lastError = "Bad data";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_bValid = true;
|
|
return true;
|
|
}
|
|
|
|
bool CGeomCache::ReadStaticBlock(AZ::IO::HandleType fileHandle, [[maybe_unused]] GeomCacheFile::EBlockCompressionFormat compressionFormat, std::vector<char>& compressedData)
|
|
{
|
|
compressedData.resize(sizeof(GeomCacheFile::SCompressedBlockHeader));
|
|
|
|
size_t bytesRead = 0;
|
|
bytesRead = gEnv->pCryPak->FReadRaw((void*)&compressedData[0], 1, sizeof(GeomCacheFile::SCompressedBlockHeader), fileHandle);
|
|
if (bytesRead != sizeof(GeomCacheFile::SCompressedBlockHeader))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_staticDataHeader = *reinterpret_cast<GeomCacheFile::SCompressedBlockHeader*>(&compressedData[0]);
|
|
compressedData.resize(sizeof(GeomCacheFile::SCompressedBlockHeader) + m_staticDataHeader.m_compressedSize);
|
|
|
|
bytesRead = gEnv->pCryPak->FReadRaw(&compressedData[sizeof(GeomCacheFile::SCompressedBlockHeader)], 1, m_staticDataHeader.m_compressedSize, fileHandle);
|
|
if (bytesRead != m_staticDataHeader.m_compressedSize)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CGeomCache::DecompressStaticBlock(GeomCacheFile::EBlockCompressionFormat compressionFormat, const char* pCompressedData, std::vector<char>& decompressedData)
|
|
{
|
|
const GeomCacheFile::SCompressedBlockHeader staticBlockHeader = *reinterpret_cast<const GeomCacheFile::SCompressedBlockHeader*>(pCompressedData);
|
|
decompressedData.resize(staticBlockHeader.m_uncompressedSize);
|
|
|
|
if (!GeomCacheDecoder::DecompressBlock(compressionFormat, &decompressedData[0], pCompressedData))
|
|
{
|
|
m_lastError = "Could not decompress static data";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CGeomCache::ReadFrameInfos(AZ::IO::HandleType fileHandle, const uint32 numFrames)
|
|
{
|
|
LOADING_TIME_PROFILE_SECTION;
|
|
|
|
size_t bytesRead;
|
|
|
|
std::vector<GeomCacheFile::SFrameInfo> fileFrameInfos;
|
|
|
|
fileFrameInfos.resize(numFrames);
|
|
bytesRead = gEnv->pCryPak->FReadRaw((void*)&fileFrameInfos[0], 1, numFrames * sizeof(GeomCacheFile::SFrameInfo), fileHandle);
|
|
if (bytesRead != (numFrames * sizeof(GeomCacheFile::SFrameInfo)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_frameInfos.resize(numFrames);
|
|
for (uint i = 0; i < numFrames; ++i)
|
|
{
|
|
m_frameInfos[i].m_frameTime = fileFrameInfos[i].m_frameTime;
|
|
m_frameInfos[i].m_frameType = fileFrameInfos[i].m_frameType;
|
|
m_frameInfos[i].m_frameSize = fileFrameInfos[i].m_frameSize;
|
|
m_frameInfos[i].m_frameOffset = fileFrameInfos[i].m_frameOffset;
|
|
m_compressedAnimationDataSize += m_frameInfos[i].m_frameSize;
|
|
}
|
|
|
|
if (m_frameInfos.front().m_frameType != GeomCacheFile::eFrameType_IFrame
|
|
|| m_frameInfos.back().m_frameType != GeomCacheFile::eFrameType_IFrame)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Assign prev/next index frames and count total data size
|
|
uint prevIFrame = 0;
|
|
for (uint i = 0; i < numFrames; ++i)
|
|
{
|
|
m_frameInfos[i].m_prevIFrame = prevIFrame;
|
|
|
|
if (m_frameInfos[i].m_frameType == GeomCacheFile::eFrameType_IFrame)
|
|
{
|
|
prevIFrame = i;
|
|
}
|
|
}
|
|
|
|
uint nextIFrame = numFrames - 1;
|
|
for (int i = numFrames - 1; i >= 0; --i)
|
|
{
|
|
m_frameInfos[i].m_nextIFrame = nextIFrame;
|
|
|
|
if (m_frameInfos[i].m_frameType == GeomCacheFile::eFrameType_IFrame)
|
|
{
|
|
nextIFrame = i;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CGeomCache::ReadMeshesStaticData(CGeomCacheStreamReader& reader, const char* pFileName)
|
|
{
|
|
LOADING_TIME_PROFILE_SECTION;
|
|
|
|
uint32 numMeshes;
|
|
if (!reader.Read(&numMeshes))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::vector<GeomCacheFile::SMeshInfo> meshInfos;
|
|
meshInfos.reserve(numMeshes);
|
|
|
|
for (uint32 i = 0; i < numMeshes; ++i)
|
|
{
|
|
GeomCacheFile::SMeshInfo meshInfo;
|
|
m_staticMeshData.push_back(SGeomCacheStaticMeshData());
|
|
SGeomCacheStaticMeshData& staticMeshData = m_staticMeshData.back();
|
|
|
|
if (!reader.Read(&meshInfo))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
staticMeshData.m_bUsePredictor = (meshInfo.m_flags & GeomCacheFile::eMeshIFrameFlags_UsePredictor) != 0;
|
|
staticMeshData.m_positionPrecision[0] = meshInfo.m_positionPrecision[0];
|
|
staticMeshData.m_positionPrecision[1] = meshInfo.m_positionPrecision[1];
|
|
staticMeshData.m_positionPrecision[2] = meshInfo.m_positionPrecision[2];
|
|
staticMeshData.m_uvMax = meshInfo.m_uvMax;
|
|
staticMeshData.m_constantStreams = static_cast<GeomCacheFile::EStreams>(meshInfo.m_constantStreams);
|
|
staticMeshData.m_animatedStreams = static_cast<GeomCacheFile::EStreams>(meshInfo.m_animatedStreams);
|
|
staticMeshData.m_numVertices = meshInfo.m_numVertices;
|
|
staticMeshData.m_aabb.min = Vec3(meshInfo.m_aabbMin[0], meshInfo.m_aabbMin[1], meshInfo.m_aabbMin[2]);
|
|
staticMeshData.m_aabb.max = Vec3(meshInfo.m_aabbMax[0], meshInfo.m_aabbMax[1], meshInfo.m_aabbMax[2]);
|
|
staticMeshData.m_hash = meshInfo.m_hash;
|
|
|
|
std::vector<char> tempName(meshInfo.m_nameLength);
|
|
if (!reader.Read(&tempName[0], meshInfo.m_nameLength))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (gEnv->IsEditor())
|
|
{
|
|
staticMeshData.m_name = &tempName[0];
|
|
}
|
|
|
|
// Read material IDs
|
|
staticMeshData.m_materialIds.resize(meshInfo.m_numMaterials);
|
|
|
|
if (!reader.Read(&staticMeshData.m_materialIds[0], meshInfo.m_numMaterials))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
meshInfos.push_back(meshInfo);
|
|
}
|
|
|
|
m_staticMeshData.reserve(numMeshes);
|
|
for (uint32 i = 0; i < numMeshes; ++i)
|
|
{
|
|
if (!ReadMeshStaticData(reader, meshInfos[i], m_staticMeshData[i], pFileName))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CGeomCache::ReadMeshStaticData(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo,
|
|
SGeomCacheStaticMeshData& staticMeshData, const char* pFileName)
|
|
{
|
|
CGeomCacheMeshManager& meshManager = GetGeomCacheManager()->GetMeshManager();
|
|
|
|
if (meshInfo.m_animatedStreams == 0)
|
|
{
|
|
// If we don't need the static data to fill dynamic meshes, just construct a static mesh
|
|
_smart_ptr<IRenderMesh> pRenderMesh = meshManager.ConstructStaticRenderMesh(reader, meshInfo, staticMeshData, pFileName);
|
|
|
|
if (!pRenderMesh)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_staticRenderMeshes.push_back(pRenderMesh);
|
|
}
|
|
else
|
|
{
|
|
// Otherwise we need the static data for filling the dynamic mesh later and read it to the vertex array in staticMeshData
|
|
if (!meshManager.ReadMeshStaticData(reader, meshInfo, staticMeshData))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CGeomCache::ReadNodesStaticDataRec(CGeomCacheStreamReader& reader)
|
|
{
|
|
LOADING_TIME_PROFILE_SECTION;
|
|
|
|
GeomCacheFile::SNodeInfo nodeInfo;
|
|
if (!reader.Read(&nodeInfo))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SGeomCacheStaticNodeData staticNodeData;
|
|
staticNodeData.m_meshOrGeometryIndex = nodeInfo.m_meshIndex;
|
|
staticNodeData.m_numChildren = nodeInfo.m_numChildren;
|
|
staticNodeData.m_type = static_cast<GeomCacheFile::ENodeType>(nodeInfo.m_type);
|
|
staticNodeData.m_transformType = static_cast<GeomCacheFile::ETransformType>(nodeInfo.m_transformType);
|
|
|
|
std::vector<char> tempName(nodeInfo.m_nameLength);
|
|
if (!reader.Read(&tempName[0], nodeInfo.m_nameLength))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (gEnv->IsEditor())
|
|
{
|
|
staticNodeData.m_name = &tempName[0];
|
|
}
|
|
|
|
staticNodeData.m_nameHash = CryStringUtils::HashString(&tempName[0]);
|
|
|
|
if (!reader.Read(&staticNodeData.m_localTransform))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (staticNodeData.m_type == GeomCacheFile::eNodeType_PhysicsGeometry)
|
|
{
|
|
uint32 geometrySize;
|
|
if (!reader.Read(&geometrySize))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::vector<char> geometryData(geometrySize);
|
|
if (!reader.Read(&geometryData[0], geometrySize))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CMemStream memStream(&geometryData[0], geometrySize, false);
|
|
// Geometry
|
|
CRY_PHYSICS_REPLACEMENT_ASSERT();
|
|
|
|
staticNodeData.m_meshOrGeometryIndex = (uint32)(m_physicsGeometries.size() - 1);
|
|
}
|
|
|
|
m_staticNodeData.push_back(staticNodeData);
|
|
|
|
for (uint32 i = 0; i < nodeInfo.m_numChildren; ++i)
|
|
{
|
|
if (!ReadNodesStaticDataRec(reader))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
float CGeomCache::GetDuration() const
|
|
{
|
|
if (m_frameInfos.empty())
|
|
{
|
|
return 0.0f;
|
|
}
|
|
else
|
|
{
|
|
return m_frameInfos.back().m_frameTime - m_frameInfos.front().m_frameTime;
|
|
}
|
|
}
|
|
|
|
IGeomCache::SStatistics CGeomCache::GetStatistics() const
|
|
{
|
|
IGeomCache::SStatistics stats;
|
|
memset(&stats, 0, sizeof(stats));
|
|
|
|
std::set<uint16> materialIds;
|
|
|
|
CGeomCacheMeshManager& meshManager = GetGeomCacheManager()->GetMeshManager();
|
|
|
|
const uint numMeshes = m_staticMeshData.size();
|
|
for (uint i = 0; i < numMeshes; ++i)
|
|
{
|
|
const SGeomCacheStaticMeshData& meshData = m_staticMeshData[i];
|
|
materialIds.insert(meshData.m_materialIds.begin(), meshData.m_materialIds.end());
|
|
|
|
stats.m_staticDataSize += static_cast<uint>(sizeof(SGeomCacheStaticMeshData));
|
|
stats.m_staticDataSize += static_cast<uint>(sizeof(vtx_idx) * meshData.m_indices.size());
|
|
stats.m_staticDataSize += static_cast<uint>(sizeof(uint32) * meshData.m_numIndices.size());
|
|
stats.m_staticDataSize += static_cast<uint>(sizeof(Vec3) * meshData.m_positions.size());
|
|
stats.m_staticDataSize += static_cast<uint>(sizeof(UCol) * meshData.m_colors.size());
|
|
stats.m_staticDataSize += static_cast<uint>(sizeof(Vec2) * meshData.m_texcoords.size());
|
|
stats.m_staticDataSize += static_cast<uint>(sizeof(SPipTangents) * meshData.m_tangents.size());
|
|
stats.m_staticDataSize += static_cast<uint>(sizeof(uint16) * meshData.m_materialIds.size());
|
|
stats.m_staticDataSize += static_cast<uint>(sizeof(uint16) * meshData.m_predictorData.size());
|
|
|
|
uint numIndices = 0;
|
|
const uint numMaterials = meshData.m_numIndices.size();
|
|
for (uint j = 0; j < numMaterials; ++j)
|
|
{
|
|
numIndices += meshData.m_numIndices[j];
|
|
}
|
|
|
|
if (meshData.m_animatedStreams == 0)
|
|
{
|
|
++stats.m_numStaticMeshes;
|
|
stats.m_numStaticVertices += meshData.m_numVertices;
|
|
stats.m_numStaticTriangles += numIndices / 3;
|
|
}
|
|
else
|
|
{
|
|
++stats.m_numAnimatedMeshes;
|
|
stats.m_numAnimatedVertices += meshData.m_numVertices;
|
|
stats.m_numAnimatedTriangles += numIndices / 3;
|
|
}
|
|
}
|
|
|
|
stats.m_staticDataSize += static_cast<uint>(m_staticNodeData.size() * sizeof(SGeomCacheStaticNodeData));
|
|
stats.m_staticDataSize += static_cast<uint>(m_frameInfos.size() * sizeof(SFrameInfo));
|
|
|
|
stats.m_bPlaybackFromMemory = m_bPlaybackFromMemory;
|
|
stats.m_averageAnimationDataRate = (float(m_compressedAnimationDataSize) / 1024.0f / 1024.0f) / float(GetDuration());
|
|
stats.m_numMaterials = static_cast<uint>(materialIds.size());
|
|
stats.m_diskAnimationDataSize = static_cast<uint>(m_compressedAnimationDataSize);
|
|
stats.m_memoryAnimationDataSize = static_cast<uint>(m_animationData.size());
|
|
|
|
return stats;
|
|
}
|
|
|
|
void CGeomCache::Shutdown()
|
|
{
|
|
if (m_pStaticDataReadStream)
|
|
{
|
|
m_pStaticDataReadStream->Abort();
|
|
m_pStaticDataReadStream = NULL;
|
|
}
|
|
|
|
GetObjManager()->UnregisterForStreaming(this);
|
|
GetGeomCacheManager()->StopCacheStreamsAndWait(this);
|
|
|
|
const uint numListeners = m_listeners.size();
|
|
for (uint i = 0; i < numListeners; ++i)
|
|
{
|
|
m_listeners[i]->OnGeomCacheStaticDataUnloaded();
|
|
}
|
|
|
|
m_eStreamingStatus = ecss_NotLoaded;
|
|
|
|
stl::free_container(m_frameInfos);
|
|
stl::free_container(m_staticMeshData);
|
|
stl::free_container(m_staticNodeData);
|
|
stl::free_container(m_physicsGeometries);
|
|
stl::free_container(m_animationData);
|
|
}
|
|
|
|
void CGeomCache::Reload()
|
|
{
|
|
Shutdown();
|
|
|
|
const bool bUseStreaming = m_bUseStreaming;
|
|
m_bUseStreaming = false;
|
|
m_bValid = false;
|
|
m_bLoaded = false;
|
|
LoadGeomCache();
|
|
m_bUseStreaming = bUseStreaming;
|
|
|
|
if (m_bLoaded)
|
|
{
|
|
const uint numListeners = m_listeners.size();
|
|
for (uint i = 0; i < numListeners; ++i)
|
|
{
|
|
m_listeners[i]->OnGeomCacheStaticDataLoaded();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FileWarning(0, m_fileName.c_str(), "Failed to load geometry cache: %s", m_lastError.c_str());
|
|
stl::free_container(m_lastError);
|
|
}
|
|
}
|
|
|
|
uint CGeomCache::GetNumFrames() const
|
|
{
|
|
return m_frameInfos.size();
|
|
}
|
|
|
|
bool CGeomCache::PlaybackFromMemory() const
|
|
{
|
|
return m_bPlaybackFromMemory;
|
|
}
|
|
|
|
uint64 CGeomCache::GetCompressedAnimationDataSize() const
|
|
{
|
|
return m_compressedAnimationDataSize;
|
|
}
|
|
|
|
char* CGeomCache::GetFrameData(const uint frameIndex)
|
|
{
|
|
assert(m_bPlaybackFromMemory);
|
|
|
|
char* pAnimationData = &m_animationData[0];
|
|
|
|
SGeomCacheFrameHeader* pFrameHeader = reinterpret_cast<SGeomCacheFrameHeader*>(
|
|
pAnimationData + (frameIndex * sizeof(SGeomCacheFrameHeader)));
|
|
|
|
return pAnimationData + pFrameHeader->m_offset;
|
|
}
|
|
|
|
const char* CGeomCache::GetFrameData(const uint frameIndex) const
|
|
{
|
|
assert(m_bPlaybackFromMemory);
|
|
|
|
const char* pAnimationData = &m_animationData[0];
|
|
|
|
const SGeomCacheFrameHeader* pFrameHeader = reinterpret_cast<const SGeomCacheFrameHeader*>(
|
|
pAnimationData + (frameIndex * sizeof(SGeomCacheFrameHeader)));
|
|
|
|
return pAnimationData + pFrameHeader->m_offset;
|
|
}
|
|
|
|
const AABB& CGeomCache::GetAABB() const
|
|
{
|
|
return m_aabb;
|
|
}
|
|
|
|
uint CGeomCache::GetFloorFrameIndex(const float time) const
|
|
{
|
|
if (m_frameInfos.empty())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const float duration = GetDuration();
|
|
float timeInCycle = fmod(time, duration);
|
|
uint numLoops = static_cast<uint>(floor(time / duration));
|
|
|
|
// Make sure that exactly at wrap around we still refer to last frame
|
|
if (timeInCycle == 0.0f && time > 0.0f)
|
|
{
|
|
timeInCycle = duration;
|
|
numLoops -= 1;
|
|
}
|
|
|
|
const uint numFrames = m_frameInfos.size();
|
|
const uint numPreviousCycleFrames = numLoops * numFrames;
|
|
uint frameInCycle;
|
|
|
|
// upper_bound = first value greater than time
|
|
SFrameInfo value = { timeInCycle };
|
|
std::vector<SFrameInfo>::const_iterator findIter =
|
|
std::upper_bound(m_frameInfos.begin(), m_frameInfos.end(), value, CompareFrameTimes);
|
|
if (findIter == m_frameInfos.begin())
|
|
{
|
|
frameInCycle = 0;
|
|
}
|
|
else if (findIter == m_frameInfos.end())
|
|
{
|
|
frameInCycle = static_cast<uint>(m_frameInfos.size() - 1);
|
|
}
|
|
else
|
|
{
|
|
frameInCycle = static_cast<uint>(findIter - m_frameInfos.begin() - 1);
|
|
}
|
|
|
|
return frameInCycle + numPreviousCycleFrames;
|
|
}
|
|
|
|
uint CGeomCache::GetCeilFrameIndex(const float time) const
|
|
{
|
|
if (m_frameInfos.empty())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const float duration = GetDuration();
|
|
float timeInCycle = fmod(time, duration);
|
|
uint numLoops = static_cast<uint>(floor(time / duration));
|
|
|
|
// Make sure that exactly at wrap around we still refer to last frame
|
|
if (timeInCycle == 0.0f && time > 0.0f)
|
|
{
|
|
timeInCycle = duration;
|
|
numLoops -= 1;
|
|
}
|
|
|
|
const uint numFrames = m_frameInfos.size();
|
|
const uint numPreviousCycleFrames = numLoops * numFrames;
|
|
uint frameInCycle;
|
|
|
|
// lower_bound = first value greater than or equal to time
|
|
SFrameInfo value = { timeInCycle };
|
|
std::vector<SFrameInfo>::const_iterator findIter =
|
|
std::lower_bound(m_frameInfos.begin(), m_frameInfos.end(), value, CompareFrameTimes);
|
|
if (findIter == m_frameInfos.end())
|
|
{
|
|
frameInCycle = static_cast<uint>(m_frameInfos.size() - 1);
|
|
}
|
|
else
|
|
{
|
|
frameInCycle = static_cast<uint>(findIter - m_frameInfos.begin());
|
|
}
|
|
|
|
return frameInCycle + numPreviousCycleFrames;
|
|
}
|
|
|
|
GeomCacheFile::EFrameType CGeomCache::GetFrameType(const uint frameIndex) const
|
|
{
|
|
const uint numFrames = m_frameInfos.size();
|
|
return (GeomCacheFile::EFrameType)m_frameInfos[frameIndex % numFrames].m_frameType;
|
|
}
|
|
|
|
uint64 CGeomCache::GetFrameOffset(const uint frameIndex) const
|
|
{
|
|
const uint numFrames = m_frameInfos.size();
|
|
return m_frameInfos[frameIndex % numFrames].m_frameOffset;
|
|
}
|
|
|
|
uint32 CGeomCache::GetFrameSize(const uint frameIndex) const
|
|
{
|
|
const uint numFrames = m_frameInfos.size();
|
|
return m_frameInfos[frameIndex % numFrames].m_frameSize;
|
|
}
|
|
|
|
float CGeomCache::GetFrameTime(const uint frameIndex) const
|
|
{
|
|
const uint numFrames = m_frameInfos.size();
|
|
const uint numLoops = frameIndex / numFrames;
|
|
const float duration = GetDuration();
|
|
return (duration * numLoops) + m_frameInfos[frameIndex % numFrames].m_frameTime;
|
|
}
|
|
|
|
uint CGeomCache::GetPrevIFrame(const uint frameIndex) const
|
|
{
|
|
const uint numFrames = m_frameInfos.size();
|
|
const uint numLoops = frameIndex / numFrames;
|
|
return (numFrames * numLoops) + m_frameInfos[frameIndex % numFrames].m_prevIFrame;
|
|
}
|
|
|
|
uint CGeomCache::GetNextIFrame(const uint frameIndex) const
|
|
{
|
|
const uint numFrames = m_frameInfos.size();
|
|
const uint numLoops = frameIndex / numFrames;
|
|
return (numFrames * numLoops) + m_frameInfos[frameIndex % numFrames].m_nextIFrame;
|
|
}
|
|
|
|
bool CGeomCache::NeedsPrevFrames(const uint frameIndex) const
|
|
{
|
|
if (GetFrameType(frameIndex) == GeomCacheFile::eFrameType_IFrame
|
|
|| GetFrameType(frameIndex - 1) == GeomCacheFile::eFrameType_IFrame)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CGeomCache::ValidateReadRange(const uint start, uint& end) const
|
|
{
|
|
const uint numFrames = m_frameInfos.size();
|
|
uint startMod = start % numFrames;
|
|
uint endMod = end % numFrames;
|
|
|
|
if (endMod < startMod)
|
|
{
|
|
end = start + (numFrames - 1 - startMod);
|
|
}
|
|
}
|
|
|
|
GeomCacheFile::EBlockCompressionFormat CGeomCache::GetBlockCompressionFormat() const
|
|
{
|
|
return m_blockCompressionFormat;
|
|
}
|
|
|
|
void CGeomCache::UpdateStreamableComponents(float importance, [[maybe_unused]] const Matrix34A& objMatrix, [[maybe_unused]] IRenderNode* pRenderNode, bool bFullUpdate)
|
|
{
|
|
if (!m_bUseStreaming)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int nRoundId = GetObjManager()->GetUpdateStreamingPrioriryRoundId();
|
|
|
|
if (UpdateStreamingPrioriryLowLevel(importance, nRoundId, bFullUpdate))
|
|
{
|
|
GetObjManager()->RegisterForStreaming(this);
|
|
}
|
|
}
|
|
|
|
void CGeomCache::StartStreaming(bool bFinishNow, IReadStream_AutoPtr* ppStream)
|
|
{
|
|
m_bValid = false;
|
|
|
|
assert(m_eStreamingStatus == ecss_NotLoaded);
|
|
if (m_eStreamingStatus != ecss_NotLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_bLoaded)
|
|
{
|
|
const uint numListeners = m_listeners.size();
|
|
for (uint i = 0; i < numListeners; ++i)
|
|
{
|
|
m_listeners[i]->OnGeomCacheStaticDataLoaded();
|
|
}
|
|
|
|
m_eStreamingStatus = ecss_Ready;
|
|
return;
|
|
}
|
|
|
|
// start streaming
|
|
StreamReadParams params;
|
|
params.dwUserData = 0;
|
|
params.nOffset = static_cast<uint>(m_staticMeshDataOffset);
|
|
params.nSize = sizeof(GeomCacheFile::SCompressedBlockHeader) + m_staticDataHeader.m_compressedSize;
|
|
params.pBuffer = NULL;
|
|
params.nLoadTime = 10000;
|
|
params.nMaxLoadTime = 10000;
|
|
|
|
if (m_bPlaybackFromMemory)
|
|
{
|
|
params.nSize += static_cast<uint>(m_compressedAnimationDataSize);
|
|
}
|
|
|
|
if (bFinishNow)
|
|
{
|
|
params.ePriority = estpUrgent;
|
|
}
|
|
|
|
if (m_fileName.empty())
|
|
{
|
|
m_eStreamingStatus = ecss_Ready;
|
|
if (ppStream)
|
|
{
|
|
*ppStream = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
m_pStaticDataReadStream = GetSystem()->GetStreamEngine()->StartRead(eStreamTaskTypeGeometry, m_fileName, this, ¶ms);
|
|
|
|
if (ppStream)
|
|
{
|
|
(*ppStream) = m_pStaticDataReadStream;
|
|
}
|
|
|
|
if (!bFinishNow)
|
|
{
|
|
m_eStreamingStatus = ecss_InProgress;
|
|
}
|
|
else if (!ppStream)
|
|
{
|
|
m_pStaticDataReadStream->Wait();
|
|
}
|
|
}
|
|
|
|
void CGeomCache::StreamOnComplete([[maybe_unused]] IReadStream* pStream, unsigned nError)
|
|
{
|
|
if (nError != 0 || !m_bValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const uint numListeners = m_listeners.size();
|
|
for (uint i = 0; i < numListeners; ++i)
|
|
{
|
|
m_listeners[i]->OnGeomCacheStaticDataLoaded();
|
|
}
|
|
|
|
m_eStreamingStatus = ecss_Ready;
|
|
|
|
m_pStaticDataReadStream = NULL;
|
|
}
|
|
|
|
void CGeomCache::StreamAsyncOnComplete(IReadStream* pStream, unsigned nError)
|
|
{
|
|
if (nError != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const char* pData = (char*)pStream->GetBuffer();
|
|
|
|
std::vector<char> decompressedData;
|
|
if (!DecompressStaticBlock((GeomCacheFile::EBlockCompressionFormat)(m_blockCompressionFormat), pData, decompressedData))
|
|
{
|
|
if (m_lastError.empty())
|
|
{
|
|
m_lastError = "Could not decompress static block";
|
|
return;
|
|
}
|
|
}
|
|
|
|
CGeomCacheStreamReader reader(&decompressedData[0], decompressedData.size());
|
|
if (!ReadMeshesStaticData(reader, m_fileName.c_str()))
|
|
{
|
|
if (m_lastError.empty())
|
|
{
|
|
m_lastError = "Could not read mesh static data";
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (m_bPlaybackFromMemory && m_frameInfos.size() > 0)
|
|
{
|
|
if (!LoadAnimatedData(pData, sizeof(GeomCacheFile::SCompressedBlockHeader) + m_staticDataHeader.m_compressedSize))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_bValid = true;
|
|
m_bLoaded = true;
|
|
|
|
pStream->FreeTemporaryMemory();
|
|
}
|
|
|
|
bool CGeomCache::LoadAnimatedData(const char* pData, const size_t bufferOffset)
|
|
{
|
|
const uint numFrames = m_frameInfos.size();
|
|
|
|
// First get size necessary for decompressing animation data
|
|
uint32 totalDecompressedAnimatedDataSize = GeomCacheDecoder::GetDecompressBufferSize(pData + bufferOffset, numFrames);
|
|
|
|
// Allocate memory and decompress blocks into it
|
|
m_animationData.resize(totalDecompressedAnimatedDataSize);
|
|
if (!GeomCacheDecoder::DecompressBlocks(m_blockCompressionFormat, &m_animationData[0], pData + bufferOffset, 0, numFrames, numFrames))
|
|
{
|
|
m_lastError = "Could not decompress animation data";
|
|
return false;
|
|
}
|
|
|
|
// Decode index frames
|
|
for (uint i = 0; i < numFrames; ++i)
|
|
{
|
|
if (m_frameInfos[i].m_frameType == GeomCacheFile::eFrameType_IFrame)
|
|
{
|
|
GeomCacheDecoder::DecodeIFrame(this, GetFrameData(i));
|
|
}
|
|
}
|
|
|
|
// Decode b-frames
|
|
for (uint i = 0; i < numFrames; ++i)
|
|
{
|
|
if (m_frameInfos[i].m_frameType == GeomCacheFile::eFrameType_BFrame)
|
|
{
|
|
char* pFrameData = GetFrameData(i);
|
|
char* pPrevFrameData[2] = { pFrameData, pFrameData };
|
|
|
|
if (NeedsPrevFrames(i))
|
|
{
|
|
pPrevFrameData[0] = GetFrameData(i - 2);
|
|
pPrevFrameData[1] = GetFrameData(i - 1);
|
|
}
|
|
|
|
char* pFloorIndexFrameData = GetFrameData(GetPrevIFrame(i));
|
|
char* pCeilIndexFrameData = GetFrameData(GetNextIFrame(i));
|
|
|
|
GeomCacheDecoder::DecodeBFrame(this, pFrameData, pPrevFrameData, pFloorIndexFrameData, pCeilIndexFrameData);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int CGeomCache::GetStreamableContentMemoryUsage([[maybe_unused]] bool bJustForDebug)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void CGeomCache::ReleaseStreamableContent()
|
|
{
|
|
const uint numListeners = m_listeners.size();
|
|
for (uint i = 0; i < numListeners; ++i)
|
|
{
|
|
m_listeners[i]->OnGeomCacheStaticDataUnloaded();
|
|
}
|
|
|
|
// Data cannot be unloaded right away, because stream could still be active,
|
|
// so we need to wait for a UnloadData callback from the geom cache managers
|
|
m_eStreamingStatus = ecss_NotLoaded;
|
|
}
|
|
|
|
void CGeomCache::GetStreamableName(string& sName)
|
|
{
|
|
sName = m_fileName;
|
|
}
|
|
|
|
uint32 CGeomCache::GetLastDrawMainFrameId()
|
|
{
|
|
return m_lastDrawMainFrameId;
|
|
}
|
|
|
|
bool CGeomCache::IsUnloadable() const
|
|
{
|
|
return m_bUseStreaming;
|
|
}
|
|
|
|
void CGeomCache::AddListener(IGeomCacheListener* pListener)
|
|
{
|
|
stl::push_back_unique(m_listeners, pListener);
|
|
}
|
|
|
|
void CGeomCache::RemoveListener(IGeomCacheListener* pListener)
|
|
{
|
|
stl::find_and_erase(m_listeners, pListener);
|
|
}
|
|
|
|
void CGeomCache::UnloadData()
|
|
{
|
|
//When not streaming, only allow data release if the geom cache has been processed by its render node
|
|
if (!m_bUseStreaming && !m_processedByRenderNode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_eStreamingStatus == ecss_NotLoaded)
|
|
{
|
|
CGeomCacheMeshManager& meshManager = GetGeomCacheManager()->GetMeshManager();
|
|
|
|
uint numStaticMesh = m_staticMeshData.size();
|
|
for (uint i = 0; i < numStaticMesh; ++i)
|
|
{
|
|
if (m_staticMeshData[i].m_animatedStreams == 0)
|
|
{
|
|
meshManager.RemoveReference(m_staticMeshData[i]);
|
|
}
|
|
}
|
|
|
|
stl::free_container(m_staticRenderMeshes);
|
|
stl::free_container(m_staticMeshData);
|
|
stl::free_container(m_animationData);
|
|
m_bLoaded = false;
|
|
}
|
|
}
|
|
|
|
#endif
|