POD格式分析学习

1. POD文件基本结构和读取方法

POD是powvr sdk提供的一种场景文件,包含了scene tree常用的对象。

POD是二进制文件,通过导出插件PVRGenPOD或者Collada2POD工具从dae转换得到。通过分析学习POD格式以及pvr sdk装载POD的代码,可以了解3d引擎场景模型动画文件的一种二进制实现方式,作为自己设计文件格式的参考。并且POD格式特别针对Imagination自家的GPU做了优化,值得研究。

POD文件按照block的方式组织,block可以嵌套block。每个block由一对marker包裹。marker包含两个32位值。marker分为start marker和end marker。

marker的第一个32位值是block name, 如果是end marker,name的最高位(bit31)置1。这个name是一个整形常量值,在sdk里面有定义。

marker的第二个32位值是block size。如果是end marker,这个值就为0。

POD文件的读取就是从读取start block开始,根据相应的name嵌套读取。处理完一个block循环处理下一个。如果没有子block, end marker可只读不处理。如果有子block,end marker就可以作为该block处理完毕的标志,从而让读取逻辑返回到上一个层次处理。例如,读取scene的子block light时,在读取代码中:

case ePODFileLight | PVRTMODELPOD_TAG_END:            return true;


block在POD文件中会按照一定的顺序出现,基本的信息和必须要先知道的内容会在前面出现,通过跟踪读取代码,第一个block是ePODFileVersion,用于验证是否是一个合法的POD文件,第二个block是ePODFileExpOpt,包含选项,正常读取scene时不读取。

第三个block就是ePODFileScene。读取这个block需要嵌套读取其内部的子block。如:ePODFileColourBackground, ePODFileColourAmbient,ePODFileNumCamera等。具体见sdk的PVRTModelPOD.cpp中的ReadScene函数。


文件读取方式上,SDK采取了一次性将文件所有内容读取到内存buffer中,然后通过移动指针访问的方式,如果生成的对象需要内存空间则在读取时分配,最后全部读取完毕后再释放整个buffer。这种方式只进行一次文件io,速度较快,只是需要多占用内存。但只要读取时内存不到峰值影响不大,总体来说这种方式还是不错的。其实sdk设计的很灵活,文件读取和释放函数都可以由使用者传入。文件读取的接口使用CSource接口。具体实现使用了CSourceStream。CSourceStream又使用了CPVRTResourceFile,支持文件读取和memroy读取。


2. POD 文件层block

读取POD的第一个层次,和文件验证历史选项等有关,暂且称为文件层,包含如下block:

    ePODFileVersion                = 1000,
    ePODFileScene,
    ePODFileExpOpt,
    ePODFileHistory,
    ePODFileEndiannessMisMatch  = -402456576,

注意ePODFileEndiannessMisMatch 是用来检测平台的endianness是否和POD文件兼容,这不是一个block。其实他是1000(0x03E8)的32位big endian值:0xE8030000。

在一些引擎里面如果endianness不兼容,可能不做任何处理,可能会做swap。PVR SDK采取一个中间路线。


2. ePODFileScene的内容

POD是按照常用的scene tree的方式组织scene的,从SDK可以看到ePODFileScene block中包含以下子block:

    ePODFileColourBackground    = 2000,
    ePODFileColourAmbient,
    ePODFileNumCamera,
    ePODFileNumLight,
    ePODFileNumMesh,
    ePODFileNumNode,
    ePODFileNumMeshNode,
    ePODFileNumTexture,
    ePODFileNumMaterial,
    ePODFileNumFrame,
    ePODFileCamera,        // Will come multiple times
    ePODFileLight,        // Will come multiple times
    ePODFileMesh,        // Will come multiple times
    ePODFileNode,        // Will come multiple times
    ePODFileTexture,    // Will come multiple times
    ePODFileMaterial,    // Will come multiple times
    ePODFileFlags,
    ePODFileFPS,
    ePODFileUserData,

其中camera, light, mesh, node, texture, material这些block都可能有多个。注意同类型的多个block并没有再增加一个层次,而是和其他类型block在同一个层次,因此他们的顺序都不一定需要连续,只不过总数量必须和NumXXX的block指定的相符合。并且NumXXX的这些block在文件中的位置也是在前面。


3. Mesh的结构

sdk里面的SPODMesh结构包含了mesh,skinmesh以及法线贴图需要的所有顶点数据。对应的block是ePODFileMesh,读取函数是ReadMesh。读取方式和其他block类似,不过针对顶点数据的特点设计了一个ReadCPODData方法去读取CPODData结构。CPODData包含:

    EPVRTDataType    eType;        /*!< Type of data stored */
    PVRTuint32        n;            /*!< Number of values per vertex */
    PVRTuint32        nStride;    /*!< Distance in bytes from one array entry to the next */
    PVRTuint8        *pData;        /*!< Actual data (array of values); if mesh is interleaved, this is an OFFSET from pInterleaved */

Imagination不愧是是搞移动设备GPU的,这个结构简直就是为OpenGLES设计的。其中pData如果mesh使用独立的分量数组时是数据地址,如果使用Interleaved数组时则作为数据偏移。

mesh包含的子block有:

    ePODFileMeshNumVtx            = 6000,
    ePODFileMeshNumFaces,
    ePODFileMeshNumUVW,
    ePODFileMeshFaces,
    ePODFileMeshStripLength,
    ePODFileMeshNumStrips,
    ePODFileMeshVtx,
    ePODFileMeshNor, //法线
    ePODFileMeshTan, //切线
    ePODFileMeshBin, //次法线
    ePODFileMeshUVW,            // Will come multiple times
    ePODFileMeshVtxCol,
    ePODFileMeshBoneIdx,
    ePODFileMeshBoneWeight,
    ePODFileMeshInterleaved,
    ePODFileMeshBoneBatches,
    ePODFileMeshBoneBatchBoneCnts,
    ePODFileMeshBoneBatchOffsets,
    ePODFileMeshBoneBatchBoneMax,
    ePODFileMeshBoneBatchCnt,
    ePODFileMeshUnpackMatrix,

从这儿就可以看出mesh的组成结构。



你可能感兴趣的:(POD格式分析学习)