Table of Contents

ArmA 1 MLOD / P3DM

ArmA 1 Forum, ArmA 1 Home, ArmA 1 Config, ArmA 1 Tools, ArmA 1 File Formats, ArmA 1 Missions, ArmA 1 3D Modeling, ArmA 1 Terrain, ArmA 1 Texturing, ArmA 1 Scripting

ArmA 1 aka Armed Assault (ArmA)

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.

#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…

  1. Denoting the selectness and
  2. Denoting per point weighting.

Any value other than zero indicates that the point is not only part of the selection but also has a weighting value.

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.

<Named Selection>

struct structTag_Selection
{
	byte Active; //eg. Always \x01
	asciiz "<Any Ascii Characters>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.