主要根据CryEngine加载CGF模型的源码加以分析涉及以下两点:
- CryEngine加载CGF模型的步骤
- CGF文件分析
CryEngine加载CGF模型的步骤
//首先打开文件
gEnv->pCryPak->FOpen(const char* filename)
[ReadOnlyChunkFile.cpp]
//读文件
bool CReadOnlyChunkFile::Read(const char* filename)
//获取到的文件句柄
CReadOnlyChunkFile::m_hFile
//src:带%engine%的路径,dst:转化后的绝对路径
CCryPak::AdjustFileName(const char* src, char dst[g_nMaxPath], unsigned nFlags)
//获取文件总字节个数
size_t CCryPak::FGetSize(FILE* hFile)
//临时为文件分配的内存区域
CCachedFileRawData::CCachedFileRawData(int nAlloc)
{
//文件句柄
m_hFile = 0;
//文件数据在内存中的缓存首地址
m_pCachedData = 0;
//临时堆分配
m_pCachedData = g_pPakHeap->TempAlloc(nAlloc, "CCachedFileRawData::CCachedFileRawData");
}
//返回一个文件buff,以字节为单位
m_pFileBuffer = new char[nFileSize];
m_pFileBuffer= gEnv->pCryPak->FGetCachedFileData(m_hFile, nFileSize);
//加载文件的内存块
[CGFLoader.cpp] bool CLoaderCGF::LoadChunks(bool bJustGeometry)
//加载材质
[CGFLoader.cpp] CMaterialCGF* CLoaderCGF::LoadMaterialFromChunk(int nChunkId)
//获取CGF文件的入口,主要是chunk块在内存中的地址(可能会有很多chunk),所以开始需要读出他们的地址
ChunkFile::GetChunkTableEntries_0x746(&f, m_chunks);
//内存阅读器
[ChunkFileReaders.cpp] MemoryReader::Read(void* buffer, size_t size)
//读取CGF的文件头和全部chunk信息
[ChunkFileReaders.cpp]static const char* GetChunkTableEntries_0x746_Tpl(IReader* pReader, TListRef& chunks)
CGF文件分析
[ChunkFileComponents.h] struct FileHeader_0x746 (CGF文件头标识共占16个字节)
{
内存:[00000000h:43 72 43 68 46 07 00 00 0D 00 00 00 10 00 00 00;CrChF...........]
文件开头4个字节是 signature "CrCh"是CGF文件标识;
紧接着的4个字节是 version,根据windows是小端存储其表示的值为 0X00000746;
后面的4个字节是块数量 chunkCount 0X0000000D,表示有13个块
最后4个字节是 chunkTableOffset 块表的偏移地址0x00000010,也就是在接着的下一个字节处
本标识的结束处是 0000000F,下一个字节地址也就是00000010
}
[ChunkFileComponents.h] struct ChunkTableEntry_0x746 (CGF文件块表的标识共占16个字节)
{
内存:[00000010h:13 10 00 00 00 00 00 00 48 00 00 00 E0 00 00 00;........H...?.. ]
前2个字节是type 1013,1013代表ChunkType_SourceInfo。具体看ChunkTypes定义,
还有ChunkType_Mesh、ChunkType_Light等等
接着的2个字节是version 0000
接着的4个字节是id 00000000(主要用于查找,是唯一标识)
接着的4个字节是size 00000048H,数据占0x48(72)个字节
接着的4个字节是offseInFile 真实数据在文件里的偏移 0x000000E0
因为上面已经标识有13个Chunk,所以现在要从块表开始向后读取13*16(208)个字节的chunks信息
}
[ReadOnlyChunkFile.cpp] 143行
/*获取每一个chunk数据的首地址*/
for (size_t i = 0; i < m_chunks.size(); ++i)
{
ChunkDesc& cd = m_chunks[i];
cd.data = m_pFileBuffer + cd.fileOffset;
}
std::sort(m_chunks.begin(), m_chunks.end(), IChunkFile::ChunkDesc::LessId);根据ID从小到大排序
/*加载chunk数据*/
不同类型chunk的详细加载信息
[CGFLoader.cpp] bool CLoaderCGF::LoadChunks(bool bJustGeometry)
//chunk的类型为:ChunkType_ExportFlags,CGF导出的标记(具体有什么自己去源码看)
[CryHeaders.h](该头文件包含全部不同类型的chunk结构描述) struct EXPORT_FLAGS_CHUNK_DESC
//chunk的类型为:ChunkType_MtlName的chunk,也就是材质,其结构描述为:struct MTL_NAME_CHUNK_DESC_0802
[CGFLoader.cpp]LoadMaterialNameChunk(IChunkFile::ChunkDesc* pChunkDesc)
//具体加载类型为ChunkType_Node,也就是节点,主要描述各类ID和变换矩阵存放的结构,其结构描述为NODE_CHUNK_DESC_0824
[CGFLoader.cpp]bool CLoaderCGF::LoadNodeChunk(IChunkFile::ChunkDesc* pChunkDesc, bool bJustGeometry)
//具体加载类型为ChunkType_Mesh,也就是mesh,主要描述具体看MESH_CHUNK_DESC_0801结构,顶点,法线,UV全部在该函数加载
[CGFLoader.cpp]bool CLoaderCGF::LoadCompiledMeshChunk(CNodeCGF* pNode, IChunkFile::ChunkDesc* pChunkDesc)
{
将mesh的所有数据赋值到pNode保存,如下代码
Vec3 bboxMin, bboxMax;
memcpy(&bboxMin, &chunk.bboxMin, sizeof(bboxMin));
memcpy(&bboxMax, &chunk.bboxMax, sizeof(bboxMax));
pNode->meshInfo.nVerts = chunk.nVerts;
pNode->meshInfo.nIndices = chunk.nIndices;
pNode->meshInfo.nSubsets = chunk.nSubsets;
pNode->meshInfo.bboxMin = bboxMin;
pNode->meshInfo.bboxMax = bboxMax;
pNode->meshInfo.fGeometricMean = chunk.geometricMeanFaceArea;
pNode->nPhysicalizeFlags = chunk.nFlags2;
LoadPhysicsDataChunk(pNode, nPhysGeomType, chunk.nPhysicsDataChunkId[nPhysGeomType]);
std::unique_ptr<CMesh> pMesh(new CMesh());
CMesh& mesh = *(pMesh.get());
mesh.m_bbox = AABB(bboxMin, bboxMax);
LoadMeshSubsetsChunk(mesh, pSubsetChunkDesc, globalBonesPerSubset)
// Read position stream.
ok = ok && LoadStreamChunk<Vec3, Vec3f16>.....
// Read normals stream.
ok = ok && LoadStreamChunk<Vec3>(mesh, chunk, CGF_STREAM_NORMALS, CMesh::NORMALS)
// Read Texture coordinates stream.
ok = ok && LoadStreamChunk<SMeshTexCoord>(mesh, chunk, CGF_STREAM_TEXCOORDS, CMesh::TEXCOORDS);
// Read indices stream.
ok = ok && LoadIndexStreamChunk(mesh, chunk);
// Read colors stream.
ok = ok && LoadStreamChunk<SMeshColor>(mesh, chunk, CGF_STREAM_COLORS, CMesh::COLORS_0);
ok = ok && LoadStreamChunk<SMeshColor>(mesh, chunk, CGF_STREAM_COLORS2, CMesh::COLORS_1);
}
//加载类型为ChunkType_MeshPhysicsData,描述物理数据的类型等信息,主要结构描述看MESH_PHYSICS_DATA_CHUNK_DESC_0800(EPhysicsGeomType CryHeaders.h)
[CGFLoader.cpp]bool CLoaderCGF::LoadPhysicsDataChunk(CNodeCGF* pNode, int nPhysGeomType, int nChunkId)
//加载类型为ChunkType_DataStream(顶点、索引、UV等具体的数据都是DataStream)
//描述数据的标记,尺寸,类型等信息,结构体描述为:STREAM_DATA_CHUNK_DESC_0800(ECgfStreamType CryHeaders.h)
[CGFLoader.cpp] bool LoadStreamDataChunk(int nChunkId, void*& pStreamData, int& nStreamType, int& nCount, int& nElemSize, bool& bSwapEndianness);
//将DataStream的内存信息拷贝到对应的变量下(vertex、UV、normal、color等)
[IIndexedMesh.h]void SetStreamData(int stream, void* pStream, int nNewCount)