Jump to content

MDX: Difference between revisions

From Kingpin Wiki
Created page with "<p style="text-align: center;" align="center"><span style="font-size: x-large;"><strong>.mdx File Format Specification</strong></span></p> <p style="text-align: center;" align="center"><span style="font-size: medium;"><strong>by <a href="mailto:tical@kingpin.com"> TiCaL</a></strong></span></p> <p align="left"><span style="color: #436888; font-size: large;">INTRO</span></p> <p>This page will try and give some sort of technical documentation on the Kingpin model format (.m..."
 
No edit summary
Line 1: Line 1:
<p style="text-align: center;" align="center"><span style="font-size: x-large;"><strong>.mdx File Format Specification</strong></span></p>
<center>'''<big>.mdx File Format Specification</big>'''</center>
<p style="text-align: center;" align="center"><span style="font-size: medium;"><strong>by <a href="mailto:tical@kingpin.com"> TiCaL</a></strong></span></p>
<center>'''by''' [mailto:tical@kingpin.com '''TiCaL''']</center>
<p align="left"><span style="color: #436888; font-size: large;">INTRO</span></p>
 
<p>This page will try and give some sort of technical documentation on the Kingpin model format (.mdx).</p>
== INTRO ==
<p>These specs can be used freely for whatever you want. I only ask that people <a href="mailto:tical@kingpin.com">send me</a> corrections, suggestions, etc.</p>
This page will try and give some sort of technical documentation on the Kingpin model format (.mdx).
<p align="left">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).</p>
 
<p align="left"><span style="color: #436888; font-size: large;">HEADER</span></p>
These specs can be used freely for whatever you want. I only ask that people [mailto:tical@kingpin.com send me] corrections, suggestions, etc.
<p align="left">The header comes right at the start of the file. The information in the header is needed to load different parts of the model.</p>
 
<p align="left"><code>typedef struct <br /> { <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_magic">magic</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_version">version</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_skinWidth">skinWidth</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_skinHeight">skinHeight</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_frameSize">frameSize</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_numSkins">numSkins</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_numVertices">numVertices</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_numTriangles">numTriangles</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_numGlCommands">numGlCommands</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_numFrames">numFrames</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_numSfxDefines">numSfxDefines</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_numSubObjects">numSfxEntries</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_numSubObjects">numSubObjects</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_offsetSkins">offsetSkins</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_offsetTriangles">offsetTriangles</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_offsetFrames">offsetFrames</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_offsetGlCommands">offsetGlCommands</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_offsetSfxDefines">offsetVertexInfo</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_numSubObjects">offsetSfxDefines</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_numSubObjects">offsetSfxEntries</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_offsetBBoxFrames">offsetBBoxFrames</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_offsetDummyEnd">offsetDummyEnd</a>; <br /> &nbsp;&nbsp;&nbsp;int <a href="#model_offsetDummyEnd">offsetEnd</a>; <br /> } model_t;</code></p>
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).
<p align="left"><a name="model_magic"></a>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' &lt;&lt; 8) + ('P' &lt;&lt; 16) + ('X' &lt;&lt; 24)).</p>
 
<p align="left"><a target="_top" name="model_version"></a> int version: Version number of the file. Always 4.</p>
== HEADER ==
<p align="left"><a target="_top" name="model_skinWidth"></a> int skinWidth: Width of the skin(s) in pixels.</p>
The header comes right at the start of the file. The information in the header is needed to load different parts of the model.
<p align="left"><a target="_top" name="model_skinHeight"></a> int skinHeight: Height of the skin(s) in pixels.</p>
 
<p align="left"><a target="_top" name="model_frameSize"></a> int frameSize: Size of each <a href="#FRAMES">frame</a> in bytes.</p>
<syntaxhighlight lang="c">
<p align="left"><a target="_top" name="model_numSkins"></a> int numSkins: Number of skins associated with this model.</p>
typedef struct
<p align="left"><a target="_top" name="model_numVertices"></a> int numVertices: Number of <a href="#triangleVertex">vertices</a> in each frame.</p>
{
<p align="left"><a target="_top" name="model_numTriangles"></a> int numTriangles: Number of triangles in each frame.</p>
    int magic;
<p align="left"><a target="_top" name="model_numGlCommands"></a> int numGlCommands: Number of dwords (4 bytes) in the gl command list.</p>
    int version;
<p align="left"><a target="_top" name="model_numFrames"></a> int numFrames: Number of <a href="#FRAMES">frames</a>.</p>
    int skinWidth;
<p align="left"><a name="model_numSfxDefines"></a>int numSfxDefines: Number of sfx definitions.</p>
    int skinHeight;
<p align="left"><a name="model_numSfxEntries"></a>int numSfxEntries: Number of sfx entries.</p>
    int frameSize;
<p align="left"><a name="model_numSubObjects"></a>int numSubObjects: Number of subobjects in mdx.</p>
    int numSkins;
<p align="left"><a target="_top" name="model_offsetSkins"></a> int offsetSkins: Offset, in bytes from the start of the file, to the list of skin names.</p>
    int numVertices;
<p align="left"><a target="_top" name="model_offsetTriangles"></a> int offsetTriangles: Offset, in bytes from the start of the file, to the list of triangles.</p>
    int numTriangles;
<p align="left"><a target="_top" name="model_offsetFrames"></a> int offsetFrames: Offset, in bytes from the start of the file, to the list of <a href="#FRAMES">frames</a>.</p>
    int numGlCommands;
<p align="left"><a target="_top" name="model_offsetGlCommands"></a> int offsetGlCommands: Offset, in bytes from the start of the file, to the <a href="#GLC">gl command list</a>.</p>
    int numFrames;
<p align="left"><a name="model_offsetVertexInfo"></a>int offsetVertexInfo: Offset, in bytes from the start of the file, to the <a href="#VERTEXINFO">vertex info list</a>.</p>
    int numSfxDefines;
<p align="left"><a name="model_offsetSfxDefines"></a>int offsetSfxDefines: Offset, in bytes from the start of the file, to the <a href="#SFXDEFS">sfx defintion list</a>.</p>
    int numSfxEntries;
<p align="left"><a name="model_offsetSfxEntries"></a>int offsetSfxEntries: Offset, in bytes from the start of the file, to the <a href="#SFXENTRY">sfx entry list</a>.</p>
    int numSubObjects;
<p align="left"><a name="model_offsetBBoxFrames"></a>int offsetBBoxFrames: Offset, in bytes from the start of the file, to the <a href="#BBOX">bounding box frames list</a>.</p>
    int offsetSkins;
<p align="left"><a target="_top" name="model_offsetDummyEnd"></a>int offsetDummyEnd: Offset, in bytes from the start of the file, to the end (same as offsetEnd).</p>
    int offsetTriangles;
<p align="left"><a target="_top" name="model_offsetEnd"></a> int offsetEnd: Offset, in bytes from the start of the file, to the end (size of the file).</p>
    int offsetFrames;
<p><a name="FRAMES"></a></p>
    int offsetGlCommands;
<p align="left"><span style="color: #436888; font-size: large;">FRAMES</span></p>
    int offsetVertexInfo;
<p align="left">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.</p>
    int offsetSfxDefines;
<p><a name="triangleVertex"></a> <code> typdef struct<br /> {<br /> &nbsp;&nbsp;&nbsp;byte <a href="#triangleVertex_vertex">vertex</a>[3];<br /> &nbsp;&nbsp;&nbsp;byte <a href="#triangleVertex_lightNormalIndex">lightNormalIndex</a>;<br /> } triangleVertex_t;<br /> </code></p>
    int offsetSfxEntries;
<p align="left"><a target="_top" name="triangleVertex_vertex"></a> 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 <a href="#frame_scale">scale</a> in the <a href="#frame">frame_t</a> structure, and then add the respective float <a href="#frame_translate">translation</a>, also in the <a href="#frame">frame_t</a> structure. This will give you the vertex coordinate relative to the model's origin, which is at the origin, (0, 0, 0).</p>
    int offsetBBoxFrames;
<p align="left"><a target="_top" name="triangleVertex_lightNormalIndex"></a> byte lightNormalIndex: This is an index into a table of normals kept by Quake2. To get the table, you need to download <a href="https://www.quaddicted.com/files/idgames2/idstuff/quake2/source/old/q2source_12_11.zip">this zip file (1.7 MB)</a>, released by <a href="http://www.idsoftware.com">id</a>, that has the source code to all of the tools they used for quake2.</p>
    int offsetDummyEnd;
<p align="left"><code> typedef struct<br /> {<br /> &nbsp;&nbsp;&nbsp;float <a href="#frame_scale">scale</a>[3];<br /> &nbsp;&nbsp;&nbsp;float <a href="#frame_translate">translate</a>[3];<br /> &nbsp;&nbsp;&nbsp;char <a href="#frame_name">name</a>[16];<br /> &nbsp;&nbsp;&nbsp;<a href="#triangleVertex">triangleVertex_t</a> <a href="#frame_vertices">vertices</a>[1];<br /> } frame_t;<br /> </code></p>
    int offsetEnd;
<p align="left">frame_t is a variable sized structure, however all frame_t structures within the same file will have the same size (<a href="#model_numVertices">numVertices</a> in the <a href="#model">header</a>)</p>
} model_t;
<p align="left"><a target="_top" name="frame_scale"></a> float scale[3]: This is a scale used by the <a href="#triangleVertex_vertex">vertex</a> member of the <a href="#triangleVertex">triangleVertex_t</a> structure.</p>
</syntaxhighlight>
<p align="left"><a target="_top" name="frame_translate"></a> float translate[3]: This is a translation used by the <a href="#triangleVertex_vertex">vertex</a> member of the <a href="#triangleVertex">triangleVertex_t</a> structure.</p>
 
<p align="left"><a target="_top" name="frame_name"></a> char name[16]: This is a name for the frame.</p>
* '''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)).
<p align="left"><a target="_top" name="frame_vertices"></a> <a href="#triangleVertex">triangleVertex_t</a> vertices[1]: An array of <a href="#model_numVertices">numVertices</a> <a href="#triangleVertex">triangleVertex_t</a> structures.</p>
 
<p align="left"><a name="TRIANGLES"></a></p>
* '''version''': Version number of the file. Always 4.
<p align="left"><span style="color: #436888; font-size: large;">TRIANGLES</span></p>
 
<p align="left">Kingpin models are made up of only triangles. At <a href="#model_offsetTriangles">offsetTriangles</a> in the file is an array of <a href="#triangle">triangle_t</a> structures. The array has <a href="#model_numTriangles">numTriangles</a> structures in it.</p>
* '''skinWidth''': Width of the skin(s) in pixels.
<p align="left"><a name="triangle"></a></p>
 
<p align="left"><code> typedef struct<br /> {<br /> &nbsp;&nbsp;&nbsp;short <a href="#triangle_vertexIndices">vertexIndices</a>[3];<br /> &nbsp;&nbsp;&nbsp;short <a href="#triangle_textureIndices">textureIndices</a>[3];<br /> } triangle_t;<br /> </code></p>
* '''skinHeight''': Height of the skin(s) in pixels.
<p align="left"><a target="_top" name="triangle_vertexIndices"></a> short vertexIndices: These three shorts are indices into the array of <a href="#frame_vertices">vertices</a> in each <a href="#frame">frames</a>. 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.</p>
 
<p align="left"><a target="_top" name="triangle_textureIndices"></a> short textureIndices: These three shorts are indices into the array of <a href="#textureCoordinate">texture coordinates</a>.</p>
* '''frameSize''': Size of each [[#FRAMES|frame]] in bytes.
<p align="left"><a name="SKINS"></a></p>
 
<p align="left"><span style="color: #436888; font-size: large;">SKINS</span></p>
* '''numSkins''': Number of skins associated with this model.
<p align="left"><a name="SKINS"></a></p>
 
<p align="left">There is an array of <a href="#model_numSkins">numSkins</a> skin names stored at <a href="#model_offsetSkins">offsetSkins</a> 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.</p>
* '''numVertices''': Number of [[#triangleVertex|vertices]] in each frame.
<p align="left"><a name="SKINS"></a></p>
 
<p align="left"><span style="color: #436888; font-size: large;"><a name="GLC"></a>GL COMMANDS</span></p>
* '''numTriangles''': Number of triangles in each frame.
<p align="left"><a name="SKINS"></a></p>
 
<p><a name="GL_COMMANDS"></a>At <a href="#model_offsetGlCommands"> offsetGlCommands</a> bytes into the file, there is the gl command list, which is made up of a series of <a href="#model_numGlCommands">numGlCommands</a> int's and float's, organized into groups. Each group starts with an int <span style="font-family: Times New Roman;">(</span>TrisTypeNum). If it is positive, it is followed by another int (SubObjectID) and that (first int) many <a href="#glCommandVertex">glCommandVertex_t</a> structures, which form a triangle strip. If it is negative, it is followed by another int (SubObjectID) and -x <a href="#glCommandVertex">glCommandVertex_t</a> 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 <a href="http://www.opengl.org">OpenGl</a>. NOTE: if there is more sub-objects than 1 then this set of glCommandVertex_t structs continues after the first set of gl commands. <a name="glCommandVertex"></a></p>
* '''numGlCommands''': Number of dwords (4 bytes) in the gl command list.
<p align="left"><a name="SKINS"></a></p>
 
<p align="left"><code> typedef struct<br /> {<br /> &nbsp;&nbsp;&nbsp;float <a href="#glCommandVertex_st">s</a>, <a href="#glCommandVertex_st">t</a>;<br /> &nbsp;&nbsp;&nbsp;int <a href="#glCommandVertex_vertexIndex">vertexIndex</a>;<br /> } glCommandVertex_t;</code></p>
* '''numFrames''': Number of [[#FRAMES|frames]].
<p align="left"><a name="triangle"></a></p>
 
<p align="left"><code> typedef struct<br /> {<br /> &nbsp;&nbsp;&nbsp;int TrisTypeNum;<br /> &nbsp;&nbsp;&nbsp;int SubObjectID;<br /> &nbsp;&nbsp;&nbsp;glCommandVertex_t GLCmdVerts[TrisTypeNum];<br /> } glGLCommands_t ;</code></p>
* '''numSfxDefines''': Number of SFX definitions.
<p align="left"><a target="_top" name="glCommandVertex_t"></a> 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 <a href="#textureCoordinate">textureCoordinate_t</a><a target="_top" name="glCommandVertex_t"></a> structure. They are stored as floats here because that's the way Kingpin passes them to <a href="http://www.opengl.org">OpenGl</a><a target="_top" name="glCommandVertex_t"></a>.</p>
 
<p align="left"><a target="_top" name="glCommandVertex_vertexIndex"></a> int SubObjectID: Integer possibly specifying the sub objects id. &nbsp;&nbsp;&nbsp;<br />int vertexIndex: Index into the array of <a href="#frame_vertices">vertices</a><a target="_top" name="glCommandVertex_vertexIndex"></a> stored in each <a href="#frame">frame</a><a target="_top" name="glCommandVertex_vertexIndex"></a>.</p>
* '''numSfxEntries''': Number of SFX entries.
<p align="left">&nbsp;<span style="color: #436888; font-size: large;"><a name="VERTEXINFO"></a>VERTEX INFO</span></p>
 
<p>At offsetVertexInfo bytes into the file, there is the vertex info list, which is made up of a series of <a href="#model_numVertices">numVertices</a> 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:</p>
* '''numSubObjects''': Number of subobjects in the `.mdx` file.
<p style="font-size: x-small;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0100 0000 0100 0000 0100 0000 0100 0000 0200 0000 0200 0000</span></p>
 
<p>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.</p>
* '''offsetSkins''': Offset, in bytes from the start of the file, to the list of skin names.
<p style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0100 0000 0100 0000 0200 0000 0200 0000 0300 0000 0300 0000 0400 0000 0400 0000</p>
 
<p align="left"><code> typedef struct<br /> {<br /> &nbsp; int Object[numVertexInObject];<br /> } SubObjectsVerts_t;</code></p>
* '''offsetTriangles''': Offset, in bytes from the start of the file, to the list of triangles.
<p align="left">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.</p>
 
<p align="left"><code> typedef struct<br /> {<br /> &nbsp; SubObjectsVerts_t VertexInfo[numSubObjects];<br /> } VertexInfo_t;</code></p>
* '''offsetFrames''': Offset, in bytes from the start of the file, to the list of [[#FRAMES|frames]].
<p align="left">VertexInfo[numSubObjects] is an array of SubObjectsVerts_t structs defined above. The number of structs in the VertexInfo array corresponds to the numSubObjects.</p>
 
<p align="left">I'm not sure if there is an easier way to define this structure....i have a complicated mind ;)</p>
* '''offsetGlCommands''': Offset, in bytes from the start of the file, to the [[#GL_COMMANDS|GL command list]].
<p align="left"><span style="color: #436888; font-size: large;"><a name="SFXDEFS"></a>SFX DEFINITION LIST</span></p>
 
<p>At offsetSfxDefines bytes into the file, there is the sfx definition list, which is made up of a series of&nbsp; 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.</p>
* '''offsetVertexInfo''': Offset, in bytes from the start of the file, to the [[#VERTEXINFO|vertex info list]].
<p align="left"><code> typedef struct<br /> {<br /> &nbsp; int type;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //What sfx will be used<br /> &nbsp; int flags;<br /> &nbsp; int velocity_type;<br /> &nbsp; int velocity_speed_up;<br /> &nbsp; float gravity;<br /> &nbsp; int spawn_interval;<br /> &nbsp; float random_spawn_interval;<br /> &nbsp; float start_alpha;<br /> &nbsp; float end_alpha;<br /> &nbsp; float fadein_time;<br /> &nbsp; float lifetime;<br /> &nbsp; float random_time_scale;<br /> &nbsp; float start_width;<br /> &nbsp; float end_width;<br /> &nbsp; float start_height;<br /> &nbsp; float end_height;<br /> &nbsp; float random_size_scale;<br /> } Define_t;</code></p>
 
<p align="left"><code> typedef struct<br /> {<br /> &nbsp; Define_t SfxDefintions[numSfxDefines];<br /> } SfxDefine_t;</code></p>
* '''offsetSfxDefines''': Offset, in bytes from the start of the file, to the [[#SFXDEFS|SFX definition list]].
<p align="left">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 ;)</p>
 
<p align="left"><span style="color: #436888; font-size: large;"><a name="SFXENTRY"></a>SFX ENTRY</span></p>
* '''offsetSfxEntries''': Offset, in bytes from the start of the file, to the [[#SFXENTRY|SFX entry list]].
<p align="left">At offsetSfxEntries bytes into the file, there is the sfx entry list, which is made up of a series of&nbsp; 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.</p>
 
<p align="left"><code> typedef struct<br /> {<br /> &nbsp; int index;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //On what vertex to show the sfx on<br /> &nbsp; int define_no;<br /> &nbsp; int vertexindex;<br /> &nbsp; char SfxFrames[64];<br /> } SfxEntry_t;</code></p>
* '''offsetBBoxFrames''': Offset, in bytes from the start of the file, to the [[#BBOX|bounding box frames list]].
<p align="left">Char array SfxFrames contains 512 bits/64 bytes which correspond to 512 animation frames <a href="#BBOX">maximum</a>. 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.</p>
 
<p align="left"><span style="color: #436888; font-size: large;"><a name="BBOX"></a>BOUNDING BOX LIST</span></p>
* '''offsetDummyEnd''': Offset, in bytes from the start of the file, to the end (same as offsetEnd).
<p align="left">At offsetBBoxFrames bytes into the file, there is the bounding box list, which is made up of a series of&nbsp;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 &amp; MaxX, MaxY, MaxZ are stored for each bounding box. The structure would look something like this.</p>
 
<p align="left"><code> typedef struct<br /> {<br /> &nbsp; float MinX;<br /> &nbsp; float MinY;<br /> &nbsp; float MinZ;<br /> &nbsp; float MaxX;<br /> &nbsp; float MaxY;<br /> &nbsp; float MaxZ;<br /> } BBox_t;</code></p>
* '''offsetEnd''': Offset, in bytes from the start of the file, to the end (size of the file).
<p align="left"><code> typedef struct<br /> {<br /> &nbsp; BBox_t BoxFrames[numFrames];<br /> } BFrames_t;</code></p>
 
<p align="left"><code> typedef struct<br /> {<br /> &nbsp; BFrames_t Boxes[numSubObjects];<br /> } BBoxFrames_t;</code></p>
== FRAMES ==
<p align="left">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 ;)</p>
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.
<p align="left"><span style="color: #436888; font-size: large;"><a name="MAXIMUM"></a>MAXIMUMS</span></p>
 
<p align="left">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.</p>
<syntaxhighlight lang="c">
<ul>
typedef struct
<li><a href="#TRIANGLES">Triangles</a>: 4096</li>
{
<li><a href="#triangleVertex">Vertices</a>: 2048</li>
    byte vertex[3];
<li><a href="#FRAMES">Frames</a>: 512</li>
    byte lightNormalIndex;
<li><a href="#SKINS">Skins</a>: 32</li>
} triangleVertex_t;
</ul>
</syntaxhighlight>
<p align="left"><span>Kingpin is trademark of <a href="http://www.gmistudios.com/"> Xatrix/Gray Matter</a>.<br /> All trademarks used are properties of their respective owners. </span></p>
 
<p align="left"><a name="triangle"></a></p>
* '''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 [https://www.quaddicted.com/files/idgames2/idstuff/quake2/source/old/q2source_12_11.zip this zip file], released by [http://www.idsoftware.com id Software], containing the source code of tools used for Quake2.
 
<syntaxhighlight lang="c">
typedef struct
{
    float scale[3];
    float translate[3];
    char name[16];
    triangleVertex_t vertices[1];
} frame_t;
</syntaxhighlight>
 
* '''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|header]]).
 
== TRIANGLES ==
Kingpin models are made up of only triangles. At [[#model_offsetTriangles|offsetTriangles]] in the file is an array of triangle_t structures. The array contains numTriangles structures.
 
<syntaxhighlight lang="c">
typedef struct
{
    short vertexIndices[3];
    short textureIndices[3];
} triangle_t;
</syntaxhighlight>
 
* '''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 [[#textureCoordinate|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 [[#model_offsetGlCommands|offsetGlCommands]] bytes into the file, there is the GL command list, which is made up of a series of [[#model_numGlCommands|numGlCommands]] ints and floats, organized into groups. Each group starts with an int (TrisTypeNum).  
* If positive, it is followed by another int (SubObjectID) and that (first int) many [[#glCommandVertex|glCommandVertex_t]] structures, which form a triangle strip.
* If negative, it is followed by another int (SubObjectID) and -x [[#glCommandVertex|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 [http://www.opengl.org 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.
 
<syntaxhighlight lang="c">
typedef struct
{
    float s, t;
    int vertexIndex;
} glCommandVertex_t;
</syntaxhighlight>
 
<syntaxhighlight lang="c">
typedef struct
{
    int TrisTypeNum;
    int SubObjectID;
    glCommandVertex_t GLCmdVerts[TrisTypeNum];
} glGLCommands_t;
</syntaxhighlight>
 
* '''s, t''': These two floats are used to map a vertex onto a skin. The range for '''s''' and '''t''' is 0.0 to 1.0. Note that the ranges differ from those in the [[#textureCoordinate|textureCoordinate_t]] structure. They are stored as floats because that's how Kingpin passes them to OpenGL.
* '''SubObjectID''': Integer possibly specifying the sub-object's ID.
* '''vertexIndex''': Index into the array of [[#frame_vertices|vertices]] stored in each [[#frame|frame]].
 
== VERTEX INFO ==
At [[#model_offsetVertexInfo|offsetVertexInfo]] bytes into the file, there is the vertex info list, which is made up of a series of [[#model_numVertices|numVertices]] ints, all grouped by the sub-object they belong to.
 
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'''
 
<syntaxhighlight lang="c">
typedef struct
{
    int Object[numVertexInObject];
} SubObjectsVerts_t;
</syntaxhighlight>
 
<syntaxhighlight lang="c">
typedef struct
{
    SubObjectsVerts_t VertexInfo[numSubObjects];
} VertexInfo_t;
</syntaxhighlight>
 
The `VertexInfo[numSubObjects]` array groups vertex info for each sub-object. All values in an array correspond to the sub-object number.
 
== SFX DEFINITION LIST ==
At [[#model_offsetSfxDefines|offsetSfxDefines]] bytes into the file, there is the SFX definition list, which is made up of a series of ints and floats. If `numSfxDefines` is zero, the structure points to `offsetBBoxFrames`.  
 
<syntaxhighlight lang="c">
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;
</syntaxhighlight>
 
== SFX ENTRY ==
At [[#model_offsetSfxEntries|offsetSfxEntries]] bytes into the file, there is the SFX entry list. An MDX file can have only one or no SFX entries.
 
<syntaxhighlight lang="c">
typedef struct
{
    int index;            // Vertex to show SFX on
    int define_no;
    int vertexindex;
    char SfxFrames[64];
} SfxEntry_t;
</syntaxhighlight>
 
`SfxFrames[64]` contains 512 bits/64 bytes representing animation frames. A bit set to 1 indicates the SFX is shown on that frame; a bit set to 0 means it's not shown.
 
== BOUNDING BOX LIST ==
At [[#model_offsetBBoxFrames|offsetBBoxFrames]] bytes into the file, there is the bounding box list. Each sub-object has its own bounding box for each frame, which stores its MinX, MinY, MinZ, MaxX, MaxY, and MaxZ.
 
<syntaxhighlight lang="c">
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;
</syntaxhighlight>
 
== MAXIMUMS ==
Kingpin has pre-defined limits to avoid dynamic memory use:
* [[#TRIANGLES|Triangles]]: 4096
* [[#triangleVertex|Vertices]]: 2048
* [[#FRAMES|Frames]]: 512
* [[#SKINS|Skins]]: 32
 
Kingpin is a trademark of [http://www.gmistudios.com/ Xatrix/Gray Matter]. All trademarks are properties of their respective owners.

Revision as of 00:56, 21 March 2025

.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.
  • 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.
  • offsetSfxEntries: Offset, in bytes from the start of the file, to the SFX entry 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.

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 ints and floats, organized into groups. Each group starts with an int (TrisTypeNum).

  • If positive, it is followed by another int (SubObjectID) and that (first int) many glCommandVertex_t structures, which form a triangle strip.
  • If 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 range for s and t is 0.0 to 1.0. Note that the ranges differ from those in the textureCoordinate_t structure. They are stored as floats because that's how 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 ints, all grouped by the sub-object they belong to.

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

typedef struct
{
    int Object[numVertexInObject];
} SubObjectsVerts_t;
typedef struct
{
    SubObjectsVerts_t VertexInfo[numSubObjects];
} VertexInfo_t;

The `VertexInfo[numSubObjects]` array groups vertex info for each sub-object. All values in an array correspond to the sub-object number.

SFX DEFINITION LIST

At offsetSfxDefines bytes into the file, there is the SFX definition list, which is made up of a series of ints and floats. If `numSfxDefines` is zero, the structure points to `offsetBBoxFrames`.

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;

SFX ENTRY

At offsetSfxEntries bytes into the file, there is the SFX entry list. An MDX file can have only one or no SFX entries.

typedef struct
{
    int index;            // Vertex to show SFX on
    int define_no;
    int vertexindex;
    char SfxFrames[64];
} SfxEntry_t;

`SfxFrames[64]` contains 512 bits/64 bytes representing animation frames. A bit set to 1 indicates the SFX is shown on that frame; a bit set to 0 means it's not shown.

BOUNDING BOX LIST

At offsetBBoxFrames bytes into the file, there is the bounding box list. Each sub-object has its own bounding box for each frame, which stores its MinX, MinY, MinZ, MaxX, MaxY, and MaxZ.

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;

MAXIMUMS

Kingpin has pre-defined limits to avoid dynamic memory use:

Kingpin is a trademark of Xatrix/Gray Matter. All trademarks are properties of their respective owners.