CryEngine CGF模型文件格式解析

主要根据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,数据占0x4872)个字节
     接着的4个字节是offseInFile 真实数据在文件里的偏移 0x000000E0

    因为上面已经标识有13个Chunk,所以现在要从块表开始向后读取13*16208)个字节的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 pMesh(new CMesh());
    CMesh& mesh = *(pMesh.get());
    mesh.m_bbox = AABB(bboxMin, bboxMax);

    LoadMeshSubsetsChunk(mesh, pSubsetChunkDesc, globalBonesPerSubset)

    // Read position stream.
    ok = ok && LoadStreamChunk.....
    // Read normals stream.
    ok = ok && LoadStreamChunk(mesh, chunk, CGF_STREAM_NORMALS, CMesh::NORMALS)
    // Read Texture coordinates stream.
    ok = ok && LoadStreamChunk(mesh, chunk, CGF_STREAM_TEXCOORDS, CMesh::TEXCOORDS);
    // Read indices stream.
    ok = ok && LoadIndexStreamChunk(mesh, chunk);
    // Read colors stream.
    ok = ok && LoadStreamChunk(mesh, chunk, CGF_STREAM_COLORS, CMesh::COLORS_0);
    ok = ok && LoadStreamChunk(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)

你可能感兴趣的:(CryEngine)