====== MLOD / P3DM ====== Although the overall structure of a P3DM MLOD model file is similar to SP3X MLOD model files there are some notable differences. Namely, Materials, Multiple UVSets and Animations. **Legend** ^ Type ^ Description ^ | byte | 8 bit (1 byte) | | short | 16 bit signed short (2 bytes) | | int | 32 bit signed integer (4 bytes) | | float | 32 bit signed single precision floating point value (4 bytes) | | asciiz | Null terminated (0x00) variable length ascii string | ====== File Format ====== The following is a mix of pseudo-code and structure references that could be used to describe the file format of MLOD P3DM model files. It may or may not be accurate. MLOD_P3DM { structHeader Header; structLOD[] Lods; } ====== Structures ====== **structHeader** struct structHeader { char[4] Filetype; //eg. MLOD int Version; //eg. 0x0101 0000 = 257 int NoOfLods; } **structLOD** struct structLOD { char[4] LODType; //eg. P3DM int MajorVersion; //eg. 0x1C00 0000 = 28 int MinorVersion; //eg. 0x0001 0000 = 256 int NoOfPoints; int NoOfNormals; int NoOfFaces; int Unknown; //Probably 'Model Flags' - Unused. structPoint[NoOfPoints] Points; structNormal[NoOfNormals] Normals; structFace[NoOfFaces] Faces; char[4] Tagg; //eg. Always 'TAGG' structTag[] Tags; //eg. Always, minimum of #UVSet# & #EndOfFile# tags exists. float Resolution; } **structPoint** struct structPoint { float x; float y; float z; int PointFlags; } **structNormal** struct structNormal { float x; float y; float z; } **structFace** struct structFace { int NoOfVerts; structVertex[4] Vertices; int FaceFlags; asciiz Texture; asciiz Material; } NB: Irrespective whether a Face has 3 or 4 vertices. There are always 4 vertex points described. In the case of a triangle the 4th vertex will have all zero entries. **structVertex** struct structVertex { int PointIndex; int NormalIndex; float U; float V; } **structEdge** struct structEdge { int 1st_PointIndex; int 2nd_PointIndex; } NB: This structure is only relevant in "#SharpEdges#" tag set. **structTag** struct structTag { byte Active; //eg. Always \x01 asciiz TagName; int NoOfBytes; ArbitraryData[NoOfBytes] TagData; //If NoOfBytes == Zero then Non-existent } Processing of a TAGG section of a P3DM MLOD file is dependable on the type of tag being processed. A tag will always be in the following format. The actual structure of the "ArbitraryData" is dependable on the tag type. * Currently there are 8 specific tags used by P3DM MLOD models. * There can be a variable amount of additional tags for each "Named Selection" in a given LOD. **#EndOfFile#** struct structTag_EndOfFile { byte Active; //eg. Always \x01 asciiz "#EndOfFile#\x00"; int NoOfBytes; //eg. Always \x00000000 = Zero } **#Selected#** struct structTag_SelectedWeighted { byte Active; //eg. Always \x01 asciiz "#Selected#\x00"; int NoOfBytes; //eg. Will always be NoOfPoints + NoOfFaces byte[NoOfPoints] SelectedWeightedPoints; byte[NoOfFaces] SelectedFaces; } NB: The byte array's indicate a zero-based offset into the corresponding Points & Faces structures. Each byte in the SelectedWeightedPoints array denotes a value 0 to 255 (0 to FF hex). And, serves a dual purpose in... - Denoting the selectness and - Denoting per point weighting. * Zero (0) or 0x00 hex indicates the corresponding point in the LODs Points array is not selected. Any value other than zero indicates that the point is not only part of the selection but also has a weighting value. * One (1) or 0x01 hex indicates the corresponding point in the LODs Points array is selected and has 100% weighting. Every value between 2 and 255 (0x02 to 0xFF hex) indicates the percentage weight of the corresponding point and that it is part of the selection. Two (2) being almost 100% weighted and 255 being almost 0% weighted. Following is some appropriate pseudo-code to illustrate. **byte2weight** float32 byte2weight = (256 - weight)/255; if (byte2weight > 1.0) then byte2weight = 0; Where "weight" is a unit8 (single byte) in the range... 0 >= weight <= 255 or 0x00 >= weight <= 0xFF. **weight2byte** unit8 weight2byte = round(256 - (255 x weight),0); if (weight2byte == 256) then weight2byte = 0; Where "weight" is a float32 in the range... 0.0 >= weight <= 1.0. **#SharpEdges#** struct structTag_SharpEdges { byte Active; //eg. Always \x01 asciiz "#SharpEdges#\x00"; int NoOfBytes; structEdge[NoOfBytes / 8] Edges; } **#Mass#** struct structTag_Mass { byte Active; //eg. Always \x01 asciiz "#Mass#\x00"; int NoOfBytes; float[NoOfPoints] Mass; } NB: #Mass# tag is only present in the "Geometry LOD". **#Property#** struct structTag_Property { byte Active; //eg. Always \x01 asciiz "#Property#\x00"; int NoOfBytes; //eg. Always \x8000 0000 = 128 char[64] Name; //eg. lodnoshadow char[64] Value; //eg. 1 } **#UVSet#** struct structTag_UVSet { byte Active; //eg. Always \x01 asciiz "#UVSet#\x00"; int NoOfBytes; //eg. (NoOfTris*24)+(NoOfQuads*32)+4 structUV[NoOfFaces] UVSet; } struct structUV { structPointUV[NoOfVerts] FaceUVs; } struct structPointUV { float U; float V; } NB: There can be as many as 8 Distinct UVSets per LOD. **#Lock#** struct structTag_Lock { byte Active; //eg. Always \x01 asciiz "#Lock#\x00"; int NoOfBytes; //eg. Will always be NoOfPoints + NoOfFaces byte[NoOfPoints] LockedPoints; //eg. \x01 = true, \x00 = false byte[NoOfFaces] LockedFaces; //eg. \x01 = true, \x00 = false } NB: The byte array's indicate a zero-based offset into the corresponding Points & Faces structures. **#Animation#** struct structTag_Animation { byte Active; //eg. Always \x01 asciiz "#Animation#\x00"; int NoOfBytes; //eg. Will always be (NoOfPoints*12)+4 float FrameTime; structXYZ[NoOfPoints] FramePoints; } struct structXYZ { float x; float y; float z; } NB: There will be 1 "#Animation#" chunk per Frame. This can result in VERY large P3DM MLOD models. A model with three thousand (3,000) odd frames (eg. ActsPercMstpSnonWnonDnon_DancingStefan.rtm) will be approx. 200 Megabytes on disk. This type of data format for Animation is commonly known as a "Point Cache" or an "MDD Point Cache". It describes the exact location of every point in the LOD in 3D "Model Space" for each frame. **** struct structTag_Selection { byte Active; //eg. Always \x01 asciiz "x00"; int NoOfBytes; //eg. Will always be NoOfPoints + NoOfFaces byte[NoOfPoints] SelectedPoints; //eg. \x01 = true, \x00 = false byte[NoOfFaces] SelectedFaces; //eg. \x01 = true, \x00 = false } NB: The byte array's indicate a zero-based offset into the corresponding Points & Faces structures.