Solved

.MDL File Spec

Posted on 1997-08-26
4
319 Views
Last Modified: 2012-06-21
Does anyone know where I can get the specifications for the .mdl "model" files used by Quake?
0
Comment
Question by:IsaacW
  • 3
4 Comments
 
LVL 4

Accepted Solution

by:
md041797 earned 200 total points
Comment Utility
A Model file contains:

   1.A skin texture, that describes the color of the skin and clothes of the creature, or whatever it can be wearing.
   2.A list of skin vertices, that are just the position of vertices on the skin texture.
   3.A list of triangles, the describe the general shape of the model.
   4.A list of animation frames.
     Each frame holds a list of the 3D vertices and the index of the precalculated vertex normal.

4.3.0 Alias Model Header

Here is the format of the .MDL file header:

typedef struct
{ long id;                     // 0x4F504449 = "IDPO"
  long version;                // Version = 3
  vec3_t scale;                // Scale factor, for x,y,z
  vec3_t origin;               // Model origin: point for x=0,y=0,z=0
  scalar_t radius;             // Model radius, maybe useless now.
  vec3_t offsets;              // (?)Inte
0
 
LVL 4

Expert Comment

by:md041797
Comment Utility
This got cut off.  reposting...
0
 
LVL 4

Expert Comment

by:md041797
Comment Utility
Find more info at http://www.cybernet.dk/users/jensh/quake/qkspec31.html#MD1


A Model file contains:

   1.A skin texture, that describes the color of the skin and clothes of the creature, or whatever it can be wearing.
   2.A list of skin vertices, that are just the position of vertices on the skin texture.
   3.A list of triangles, the describe the general shape of the model.
   4.A list of animation frames.
     Each frame holds a list of the 3D vertices and the index of the precalculated vertex normal.

4.3.0 Alias Model Header

Here is the format of the .MDL file header:

typedef struct
{ long id;                     // 0x4F504449 = "IDPO"
  long version;                // Version = 3
  vec3_t scale;                // Scale factor, for x,y,z
  vec3_t origin;               // Model origin: point for x=0,y=0,z=0
  scalar_t radius;             // Model radius, maybe useless now.
  vec3_t offsets;              // (?)Integer offsets
  long numskins ;              // the number of skin textures
  long skinwidth;              // Width of skin texture
                               //           must be multiple of 8
  long skinheight;             // Height of skin texture
                               //           must be multiple of 8
  long numverts;               // Number of vertices
  long numtris;                // Number of triangles surfaces
  long numframes;              // Number of frames
  long unknown;                // 0
} mdl_t;

The size of this header is 0x4C bytes (76).

4.3.1 Alias Model Skin

This is simply a flat picture, stored as a collection of 8-bit color indexes.

At offset baseskin = 0x4C in the .MDL file you will find:

typedef struct
{ long        unknown;         // Always 0
  u_char skin[skinwidth*skinheight]; // the skin picture
} skin_t skins[numskins];      // numskins pictures

There might be more than on skin texture, as indicated by numskins. They all have the same size.

It is suspected that color index 0xFF represents a transparent area, like in level textures.

Note that the skin pictures are a bit particular: as a matter of fact, they are not made of one piece, but of at least two pieces: one for the front of the model,
the other for the back of the model.

Actually, there may be as many pieces as there are independent parts in the model, and even more for special animations.

Note that the back skin of a given sprite part must be on the same height, but translated width/2, relatively to the front skin part. The back skin part must
also be inverted along the vertical axis.

This design is used to allow the correct rendering of a seamless skin texture, using Skin Vertices with onseam == 1, on the skin border.

4.3.2 Alias Model Skin Vertices

A .MDL file is made of a list of vertices. To each of these vertices corresponds a 3D position, a normal, and a position on the skin picture, for texture
mapping.

The list of skin vertices indicates only the position on texture picture, not the 3D position. That's because for a given vertex, the position on skin is constant,
while the position in 3D space varies with the animation.

The list of skin vertices is made of these structures:

typedef struct
{ long onseam;                 // 0 or 1
  long s;                      // position, horizontally
                               //  in range [0,skinwidth[
  long t;                      // position, vertically
                               //  in range [0,skinheight[
} stvert_t;

onseam is a boolean, and if non zero it means that the vertex is on the boundary between the skin part that is applied on the front of the sprite, and the skin
part that is applied on the back of the sprites (i.e. on the edge).

s and t are (X,Y) position on the skin picture.

At offset baseverts = baseskin + (4 + skinwidth * skinheight) * numskins in the .MDL file, you will find:

stvert_t vertices[numverts];

4.3.3 Alias Model Triangles

An Alias Model is made of a set of triangle facets, with vertices at the boundaries.

Only vertices index are stored in triangles. the normal vector of the surface is reconstituted from the vertex position.

Here is the structure of triangles:

typedef struct
{ long facesfront;             // boolean
  long vertices[3];            // Index of 3 triangle vertices
                               // in range [0,numverts[
} itriangle_t;

The boolean facesfront indicates if the triangle is part of the front or the back skin. 1 means that it is on the front skin, 0 means that it is on the back skin,
so any skin vertex that is on seam must have its horizontal value increased by skinwidth/2, so as to find its correct position on the back skin.

Note that the index of a given vertex is the same in the skin vertex table and in the frame table.

At offset basetri = baseverts + numverts * sizeof(stvert_t) in the .MDL file, you will find:

itriangle_t triangles[numtris];

4.3.4 Alias Model Frames

An Alias Model contains a set of animation frames, which can be used in relation with the behavior of the modeled entity, so as to display it in various
postures (walking, attacking, spreading its guts all over the place...).

The frame vertices

Each frame vertex is defined by a 3D position and a normal for each of the vertices in the model.

typedef struct
{ u_char packedposition[3];    // X,Y,Z coordinate, packed on 0-255
  u_char lightnormalindex;     // index of the vertex normal
} trivertx_t;

To get the real X coordinate, from the packed coordinates, multiply the X coordinate by the X scaling factor, and add the X origin. Both the scaling factor
and the origin can be found in the Model Header.

The currently suggested formula for calculating positions is:

vec3_t real[i] = ( scale[i] *  packedposition[i] ) + origin[i]

Where scale, and origin can be found as vectors in the Model Header.

The Light Normal Indexes

The lightnormalindex field is used to represent the vertex normal vector. The normal vector of a given vertex is the average of the normal vectors of all
the faces that contain this vertex.

This information is necessary to calculate the Gouraud shading of the faces, but actually a crude estimation of the actual vertex normal is sufficient. That's why,
to save space and to reduce the number of computations needed, it has been chosen to approximate each vertex normal.

The ordinary values of lightnormalindex are comprised between 0 and 161, and directly map into the index of one of the 162 precalculated normal
vectors that can be found in Appendix B.

However, value 255 of lightnormalindex is a bit special, in the sense that a vertex taged with that value will always look dark, whatever the light level
around.

Experiments show that value 255 should be used for dull object, while values from 162 to 254 could be used for bright object, however it could just be an
artifact, don't rely on it.

The frames

The beginning of the frames can be found in the .MDL file, at offset baseframes = basetri + numtris * sizeof(itriangle_t);.

The size of each frames is sizeframe = 0xC + numverts * trivertx_t;.

The frame fram can be found in the .MDL file at offset baseframe = baseframes + sizeframe * fram, with fram in the range [0,numframes[.

Each frame is a structure made of a header and an array:

typedef struct
{ long       unknown;          // Always zero
  trivertx_t min;              // minimum values of X,Y,Z
  trivertx_t max;              // maximum values of X,Y,Z
  trivertx_t frame[numverts];  // array of vertices
} frame_t;

The number of vertices is numverts, and to each of the vertex declared here corresponds a Skin Vertex with the same index.

The frame header contains two vertex definitions, min and max, that define a bounding box around the whole frame: all the other vertices must be inside that
bounding box.

However, that bounding box is only used for collision detection, so if you make it smaller than it should be the model will still display fine, but you can get very
close to it before hitting it.

To get the floating point values corresponding to min and max, treat them as if they were ordinary vertex positions.

Unknown fields

The first filed of the header is always zero, and there's no explanation for it. It cannot be a time stamp, since frame animations is in fact coded in the Code
lump.

The lightnormalindex of min and max have irrelevant values, and are apparently not used. They only pad the structure to 4 bytes.

0
 

Author Comment

by:IsaacW
Comment Utility
Thanks a lot, could you now just send me that info by e-mail to waldroni@lr.net?  That would be appreciated
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now