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;

int 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)).

int version: Version number of the file. Always 4.

int skinWidth: Width of the skin(s) in pixels.

int skinHeight: Height of the skin(s) in pixels.

int frameSize: Size of each frame in bytes.

int numSkins: Number of skins associated with this model.

int numVertices: Number of vertices in each frame.

int numTriangles: Number of triangles in each frame.

int numGlCommands: Number of dwords (4 bytes) in the gl command list.

int numFrames: Number of frames.

int numSfxDefines: Number of sfx definitions.

int numSfxEntries: Number of sfx entries.

int numSubObjects: Number of subobjects in mdx.

int offsetSkins: Offset, in bytes from the start of the file, to the list of skin names.

int offsetTriangles: Offset, in bytes from the start of the file, to the list of triangles.

int offsetFrames: Offset, in bytes from the start of the file, to the list of frames.

int offsetGlCommands: Offset, in bytes from the start of the file, to the gl command list.

int offsetVertexInfo: Offset, in bytes from the start of the file, to the vertex info list.

int offsetSfxDefines: Offset, in bytes from the start of the file, to the sfx defintion list.

int offsetSfxEntries: Offset, in bytes from the start of the file, to the sfx entry list.

int offsetBBoxFrames: Offset, in bytes from the start of the file, to the bounding box frames list.

int offsetDummyEnd: Offset, in bytes from the start of the file, to the end (same as offsetEnd).

int 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.

typdef struct
{
   byte vertex[3];
   byte lightNormalIndex;
} triangleVertex_t;

byte 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, you need to first multiply each of the bytes by their respective float scale in the frame_t structure, and then add the respective float translation, also in the frame_t structure. This will give you the vertex coordinate relative to the model's origin, which is at the origin, (0, 0, 0).

byte lightNormalIndex: This is an index into a table of normals kept by Quake2. To get the table, you need to download this zip file (1.7 MB), released by id, that has the source code to all of the tools they used for quake2.

typedef struct
{
   float scale[3];
   float translate[3];
   char name[16];
   triangleVertex_t vertices[1];
} frame_t;

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)

float scale[3]: This is a scale used by the vertex member of the triangleVertex_t structure.

float translate[3]: This is a translation used by the vertex member of the triangleVertex_t structure.

char name[16]: This is a name for the frame.

triangleVertex_t vertices[1]: An array of numVertices triangleVertex_t structures.

TRIANGLES

Kingpin models are made up of only triangles. At offsetTriangles in the file is an array of triangle_t structures. The array has numTriangles structures in it.

typedef struct
{
   short vertexIndices[3];
   short textureIndices[3];
} triangle_t;

short vertexIndices: These three shorts are indices into the array of vertices in each frames. In other words, the number of triangles in a md2 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.

short textureIndices: These three shorts are indices into the array of texture coordinates.

SKINS

There is an array of numSkins skin names stored at offsetSkins into the file. Each skin name is a char[64]. The name is really a path to the skin, relative to the base game directory (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 is 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 ;

float 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.

int SubObjectID: Integer possibly specifying the sub objects id.    
int 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;          //What sfx will be used
  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;       //On what vertex to show the sfx on
  int define_no;
  int vertexindex;
  char SfxFrames[64];
} SfxEntry_t;

Char array SfxFrames 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 trademark of Xatrix/Gray Matter.
All trademarks used are properties of their respective owners.


FREDZ | Thursday 27 December 2018 - 16:00
These icons link to social bookmarking sites where readers can share and discover new web pages.
  • email
  • Facebook
  • Google
  • PrintFriendly