StructureIn a pseudo-C style structure, a PKG-file looks like this: struct PKG
{
char[4] header = "PKG3" | "PKG2";
PKGFile[] files;
}
union PKGFile
{
PKG2File pkg2file; // If header == "PKG2"
PKG3File pkg3file; // If header == "PKG3"
}
struct PKG2File
{
char[4] header = "FILE";
String name; // Name of this section
PKGFileData data; // Format depends of name, see below
}
struct PKG3File
{
char[4] header = "FILE";
String name; // Name of this section
long length; // Length of this PKGFile in bytes
PKGFileData data; // Format depends of name, see below
}
struct String
{
unsigned byte length; // Number of bytes in this string including
// the string terminator.
char[length - 1] characters; // ASCII characters
char terminator = '\0';
}
PKGFileDataThe PKGFileData is different depending on the name of the PKGFile section. The following section describes the known chunks or files as they are called in MM2 terminology. VL, L, M, HPKGFileData sections whose name ends with one of "VL", "L", "M", "H" define geometry primitives. The name suffixes define the LOD, the level of detail, of the particular part of the object. A PKG-file can have any number of separate parts, some have special significance, espescially for vehicle models. The suffixes themself stand for Very Low, Low, Medium and High respectively. struct PKGFileData
{
long nSections; // Number of sections making up this LOD
long nVerticesTot; // Total number of vertices in this LOD
long nIndiciesTot; // Total number of indicies in this LOD
long nSections2; // Repetition of the number of sections?
// Highly unlikely, but I have no better suggestion at
// this time
long flags; // Flags defining what components are provided with
// each vertex, see below
PKGSection[nSections] sections;
}
struct PKGSection
{
ushort nStrips; // Number of geometry strips in this section
ushort flags; // Unknown flags (Always 0 in MM2 PKGs)
long shaderOffset; // Offset into the shader list of the requested
// paintjob
PKGStrip[nStrips] strips;
}
struct PKGStrip
{
long verticesPerFace; // Determines if the strips are quads or triangles,
// have only seen 3 in this field though
long nVertices; // Number of vertices in this strip
PKGVertex[nVertices] vertices;
long nIndices; // Number of indices making up the geometry strip
ushort[nIndices] indices;
}
struct PKGVertex
{
Vertex3D coordinate; // If flags indicate coordinates
Vector3D normal; // If flags indicate normals
Vertex2D textureCoordinate; // If flags indicate texture coordinates
}
struct Vertex3D
{
float x;
float y;
float z;
}
struct Vector3D
{
float x;
float y;
float z;
}
struct Vertex2D
{
float x;
float y;
}
Description of the bits for the PKGFILEData.flags field:
All other bits are unknown at this time. shadersEach PKGSection of the geometry references a shader, a shader defines the colour of the facets. Each PKG-file has one PKGFileData section containing the definitions of all shaders used in the PKG. Within this section, shaders can be defined in one of two ways, either using floating point colour values or integer colour values. The shader defines ambient, diffuse, specular and emissive colours. The floating point shader also specifies the shininess used to calculate highlights. Each shader can also specify one texture map. The shaders are organized in groups called paint jobs. When an object is placed in game, a certain paint job is requested, either by the INST, PATHSET or PSDL files for monuments and props or by user interaction for player vehicle models. Each paint job holds the same number of shaders, even if a particular shader is identical in several paint jobs. The index defined in the PKGSection is an index into the shader list of each paint job. PKGFileData
{
long shaderType; // Bit 7: Shader type
// Bit 0-6: Number of paint jobs
long shadersPerPaintJob;
PKGShader[Number of paint jobs * shadersPerPaintJob] shaders;
}
union PKGShader
{
FloatPKGShader floatShader; // If type is 0
IntPKGShader intShader; // If type is 1
}
FloatPKGShader
{
String textureName;
Color4f ambient;
Color4f diffuse;
Color4f specular;
Color4f emissive;
float shininess;
}
IntPKGShader
{
String textureName;
Color4d ambient;
Color4d diffuse;
Color4d specular;
Color4d emissive;
}
Color4f
{
float red;
float green;
float blue;
float alpha;
}
Color4d
{
unsigned char red;
unsigned char green;
unsigned char blue;
unsigned char alpha;
}
offsetPKGFileData
{
Vector3D offset; // Have not tested, but could be an offset added to all
// vertices in the object
}
xrefA PKG can reference external objects stored in other PKGs, when rendering a PKG with external references, each object referenced will be rendered too. These objects are possibly breakable, meaning that they can be detached from the main object. PKGFileData
{
long nReferences;
Vector3D xAxis;
Vector3D yAxis;
Vector3D zAxis;
Vector3D origin;
char[] name;
char terminator = 0x00; // Just to end the string
char[] unknown0 = "max";
ushort[] unknown1 = {0, 0, 0, 0, 0, 0, 0};
}
|