MDX
.mdx File Format Specification
by TiCaL
INTRO
This page will try and give some sort of technical documentation on the Kingpin model format (.mdx).
These specs can be used freely for whatever you want. I only ask that people send me corrections, suggestions, etc.
Kingpin models are stored in files with the .mdx extension. A single mdx file contains the model's geometry, frame information, skin filename(s), skin texture mapping, and bounding box max's and min's. The file is little-endian (Intel byte ordering).
HEADER
The header comes right at the start of the file. The information in the header is needed to load different parts of the model.
typedef struct
{
int magic;
int version;
int skinWidth;
int skinHeight;
int frameSize;
int numSkins;
int numVertices;
int numTriangles;
int numGlCommands;
int numFrames;
int numSfxDefines;
int numSfxEntries;
int numSubObjects;
int offsetSkins;
int offsetTriangles;
int offsetFrames;
int offsetGlCommands;
int offsetVertexInfo;
int offsetSfxDefines;
int offsetSfxEntries;
int offsetBBoxFrames;
int offsetDummyEnd;
int offsetEnd;
} model_t;
- magic: A "magic number" used to identify the file. The magic number is 1481655369 in decimal (0x58504449 in hexadecimal). The magic number is equal to the int "IDPX" (id polygon Xatrix), which is formed by ('I' + ('D' << 8) + ('P' << 16) + ('X' << 24)).
- version: Version number of the file. Always 4.
- skinWidth: Width of the skin(s) in pixels.
- skinHeight: Height of the skin(s) in pixels.
- frameSize: Size of each frame in bytes.
- numSkins: Number of skins associated with this model.
- numVertices: Number of vertices in each frame.
- numTriangles: Number of triangles in each frame.
- numGlCommands: Number of dwords (4 bytes) in the GL command list.
- numFrames: Number of frames.
- numSfxDefines: Number of SFX definitions.
- numSfxEntries: Number of SFX entries.
- numSubObjects: Number of subobjects in the `.mdx` file.
- offsetSkins: Offset, in bytes from the start of the file, to the list of skin names.
- offsetTriangles: Offset, in bytes from the start of the file, to the list of triangles.
- offsetFrames: Offset, in bytes from the start of the file, to the list of frames.
- offsetGlCommands: Offset, in bytes from the start of the file, to the GL command list.
- offsetVertexInfo: Offset, in bytes from the start of the file, to the vertex info list.
- offsetSfxDefines: Offset, in bytes from the start of the file, to the SFX definition list.
- offsetSfxEntries: Offset, in bytes from the start of the file, to the SFX entry list.
- offsetBBoxFrames: Offset, in bytes from the start of the file, to the bounding box frames list.
- offsetDummyEnd: Offset, in bytes from the start of the file, to the end (same as offsetEnd).
- offsetEnd: Offset, in bytes from the start of the file, to the end (size of the file).
FRAMES
Each frame contains the positions in 3D space for each vertex of each triangle that makes up the model. Kingpin, Quake 2 (and Quake) models contain only triangles.
typedef struct
{
byte vertex[3];
byte lightNormalIndex;
} triangleVertex_t;
- vertex[3]: The three bytes represent the x, y, and z coordinates of this vertex. This is not the "real" vertex coordinate. This is a scaled version of the coordinate, scaled so that each of the three numbers fit within one byte. To scale the vertex back to the "real" coordinate, multiply each of the bytes by their respective float scale in the frame_t structure, and then add the respective float translation. This provides the vertex coordinate relative to the model's origin, which is at the origin, (0, 0, 0).
- lightNormalIndex: This is an index into a table of normals kept by Quake2. The table can be obtained from this zip file, released by id Software, containing the source code of tools used for Quake2.
typedef struct
{
float scale[3];
float translate[3];
char name[16];
triangleVertex_t vertices[1];
} frame_t;
- scale[3]: A scale used by the vertex member of triangleVertex_t.
- translate[3]: A translation used by the vertex member of triangleVertex_t.
- name[16]: The name of the frame.
- vertices[1]: An array of numVertices triangleVertex_t structures.
frame_t is a variable-sized structure; however, all frame_t structures within the same file will have the same size (numVertices in the header).
TRIANGLES
Kingpin models are made up of only triangles. At offsetTriangles in the file is an array of triangle_t structures. The array contains numTriangles structures.
typedef struct
{
short vertexIndices[3];
short textureIndices[3];
} triangle_t;
- vertexIndices[3]: These three shorts are indices into the array of vertices in each frame. In other words, the number of triangles in a `.mdx` file is fixed, and each triangle is always made of the same three indices into each frame's array of vertices. So, in each frame, the triangles themselves stay intact, their vertices are just moved around.
- textureIndices[3]: These three shorts are indices into the array of texture coordinates.
SKINS
There is an array of numSkins skin names stored at offsetSkins in the file. Each skin name is a char[64]. The name is a path to the skin, relative to the base game directory (e.g., main for Kingpin). The skin files are compressed and uncompressed 24-bit TGA files.
GL COMMANDS
At offsetGlCommands bytes into the file, there is the gl command list, which is made up of a series of numGlCommands int's and float's, organized into groups. Each group starts with an int (TrisTypeNum).
- If it is positive, it is followed by another int (SubObjectID) and that (first int) many glCommandVertex_t structures, which form a triangle strip.
- If it is negative, it is followed by another int (SubObjectID) and -x glCommandVertex_t structures, which form a triangle fan.
- A 0 indicates the end of the list.
The list is an optimized way of issuing commands when rendering with OpenGL.
NOTE: If there are more sub-objects than 1, then this set of `glCommandVertex_t` structs continues after the first set of GL commands.
typedef struct
{
float s, t;
int vertexIndex;
} glCommandVertex_t;
typedef struct
{
int TrisTypeNum;
int SubObjectID;
glCommandVertex_t GLCmdVerts[TrisTypeNum];
} glGLCommands_t;
- s, t: These two floats are used to map a vertex onto a skin. The horizontal axis position is given by s, and the vertical axis position is given by t. The range for s and for t is 0.0 to 1.0. Note that the ranges are different than in the textureCoordinate_t structure. They are stored as floats here because that's the way Kingpin passes them to OpenGL.
- SubObjectID: Integer possibly specifying the sub-object's ID.
- vertexIndex: Index into the array of vertices stored in each frame.
VERTEX INFO
At offsetVertexInfo bytes into the file, there is the vertex info list, which is made up of a series of numVertices int's, all of which are grouped by the sub-object they belong to. If a vertex is in sub-object 1 then its marked as 1, if its in sub-object 2 then its marked as 2 etc etc. First all the vertices from the first sub-object are marked then the rest or the vertices from other sub-objects follow. For example if the first sub-object has 4 vertices and the second sub-object has 2 vertices the HEX code in the mdx would look like this:
0100 0000 0100 0000 0100 0000 0100 0000 0200 0000 0200 0000
This shows there are 4 one's and 2 two's integers specifying that the first for vertices are part of sub-object 1 and that the last 2 vertices are part of sub-object 2. Lets say there are 4 sub-objects who all have 2 vertices each. Then the HEX code would look like this.
0100 0000 0100 0000 0200 0000 0200 0000 0300 0000 0300 0000 0400 0000 0400 0000
typedef struct
{
int Object[numVertexInObject];
} SubObjectsVerts_t;
- Object[numVertexInObject]: is an array of int's where numVertexInObject is the number of vertices in the sub-object. All the values (int's) in one array should all have the same value which corresponds to the sub-object number.
typedef struct
{
SubObjectsVerts_t VertexInfo[numSubObjects];
} VertexInfo_t;
- VertexInfo[numSubObjects]: is an array of SubObjectsVerts_t structs defined above. The number of structs in the VertexInfo array corresponds to the numSubObjects.
I'm not sure if there is an easier way to define this structure....i have a complicated mind ;)
SFX DEFINITION LIST
At offsetSfxDefines bytes into the file, there is the sfx definition list, which is made up of a series of int's and float's. If numSfxDefines is zero then offsetSfxDefines just points to the same structure as offsetBBoxFrames. The following sfx defines struct contains info on how the sfx will look/behave/move/appear/fall/size etc etc.
typedef struct
{
int type;
int flags;
int velocity_type;
int velocity_speed_up;
float gravity;
int spawn_interval;
float random_spawn_interval;
float start_alpha;
float end_alpha;
float fadein_time;
float lifetime;
float random_time_scale;
float start_width;
float end_width;
float start_height;
float end_height;
float random_size_scale;
} Define_t;
typedef struct
{
Define_t SfxDefintions[numSfxDefines];
} SfxDefine_t;
There is usually only one sfx definition per mdx file (I still haven't seen any with more than one so the above structure (Define_t) can be used instead of SfxDefine_t. Again I'm not sure if there is an easier way to define this structure....i have a complicated mind ;)
SFX ENTRY
At offsetSfxEntries bytes into the file, there is the sfx entry list, which is made up of a series of int's and one array of char's. If numSfxEntries is zero then offsetSfxEntries just points to the same structure as offsetBBoxFrames. One mdx fie can only have one or no sfx entries and its structure would look like something like this.
typedef struct
{
int index; // Vertex to show SFX on
int define_no;
int vertexindex;
char SfxFrames[1024];
} SfxEntry_t;
- SfxFrames[1024]: contains 512 bits/64 bytes which correspond to 512 animation frames maximum. If a bit in those 512 bits is set to 1 the sfx will be shown on that frame. If the bit is set to 0 the sfx will not be shown. For example if you wanted to show the sfx in all 512 animation frames you would set all the 512 bits to 1. If you don't want to show any sfx then you would set all 512 bits to zero. Simple. The hard bit is encoding and decoding the char array so that we know the frame range for which the sfx will be shown for. This is not neccessary for importing mdx files but it is essential for exporting them.
BOUNDING BOX LIST
At offsetBBoxFrames bytes into the file, there is the bounding box list, which is made up of a series of float's grouped together in sub-object order for each animation frame. So lets say an mdx file has 2 sub-objects and 5 animation frames, this would mean that first part of the list would contain 5 BBox structures (one for each frame) for the first sub-object followed by another 5 BBox structures for the second sub-object. This was done because Xatrix wished to implement locational damage for their player models so that their limbs could be shot off (very realistic effects). Ok so each sub-object has its own bounding box for each frame and its MinX, MinY, MinZ & MaxX, MaxY, MaxZ are stored for each bounding box. The structure would look something like this.
typedef struct
{
float MinX;
float MinY;
float MinZ;
float MaxX;
float MaxY;
float MaxZ;
} BBox_t;
typedef struct
{
BBox_t BoxFrames[numFrames];
} BFrames_t;
typedef struct
{
BFrames_t Boxes[numSubObjects];
} BBoxFrames_t;
Again this is not neccesary for importing but essential for exporting and again I'm not sure if there is an easier way to define this structure....i have a complicated mind ;)
MAXIMUMS
Kingpin has some pre-defined limits, so that dynamic memory does not need to be used. You can use these to your advantage to speed up loading if you want.
Kingpin is a trademark of Xatrix/Gray Matter. All trademarks are properties of their respective owners.