Document last updated 2011 Jul 11.

Much of the information was extracted from the header files provided in the Q3AToolsSource package from Id Software, Inc.


I make no claims as to the accuracy of the information provided within. I have made attempts to be as accurate as possible, but the information herein is still provided "as-is". I am not affiliated with Id Software, Inc., nor RSA Data Security, Inc. Copyrights and trademarks are under the control of their respective holders.

The following information are educated guesses based on my experience with the MD3 format and Blender 3D modeller. At this time, I don't know of any MD4 file that works in Quake 3.


MD4 is a newer 3D data format that appeared in Quake 3 (PR 1.29?). MD4 uses "bones animation" (aka "skeletal animation", "skeletal deformation", ???), unlike the explicit vertex listings in MD3 (the 3D version of animated cels). Potential advantages over MD3 include smaller data files and the potential for more complex animation. Potential disadvantages include longer load time and/or requiring more processing power. These particular disadvantages become a moot point with GHz consumer computers.

A separate file format is used to describe maps, the environment of the game: a source MAP file that compiles into a BSP file. This document does not cover the MAP nor BSP file formats.

The MD4 file format from Id Software, Inc., should not be confused with Message Digest 4 algorithm from RSA Data Security, Inc., also called MD4. One is a file format, the other is an algorithm ("math formula"), but they are, unfortunately, both called "MD4". To add to the confusion, the Quake series uses the MD4 algorithm (slightly modified?) as a checksum algorithm for network error-checking and pak file integrity (including the MD4 files).

To clarify, this document covers the MD4 file format, not the MD4 algorithm.

This document does not cover the older MD3 file format.

The Quake series was developed and run on IA32 (x86) machines, using C. The file format shows many remnants of x86-isms and C-isms (expected byte order, word sizes, data type names, etc.). Some of these isms spill over into this document.

The MD4 format is presented here from a larger scope to smaller ones.

Data type indicator:

U8char8-bit unsigned octet (character).
S16shortlittle-endian signed 16-bit integer.
S32intlittle-endian signed 32-bit integer.
F32floatIEEE-754 32-bit floating-point.
VEC3vec3_ttriplet of F32 in sequence (read 4 octets, make float, read 4, make float, read 4, make float), describing a 3-space vector.
*[]indicates sequential repeat count (homogenous aggregation, array, vector), as in "U8 * 16" to mean a 16-character array (i.e. character string).
-file/array offset of which to make special note.
!aggregate complex data that should be described elsewhere.


-MD4_STARToffset of MD4 object. Usually 0, but not guaranteed.
S32IDENTMagic number. As a string of 4 char, reads "IDP4", as unsigned little-endian 1384147059 (0x52806873), as unsigned big endian 1936228434 (0x73688052).
S32VERSIONMD4 version number, latest known is 1, but use the constant MD4_VERSION
U8 * MAX_QPATHNAMEMD4 name, usually its pathname in the PK3. ASCII character string, NUL-terminated (C-style). Current value of MAX_QPATH is 64.
S32NUM_FRAMESNumber of Frame objects. Current value of MD3_MAX_FRAMES is 1024.
S32NUM_BONESNumber of Bone objects. Current value of MD4_MAX_BONES is 128.
S32OFS_FRAMESRelative offset from start of MD4 object where Frame objects start.
S32NUM_LODSNumber of LOD (Level of Detail) surfaces. Current value of MD3_MAX_LODS is 4.
S32OFS_LODSRelative offset from start of MD4 where LOD objects start. Written sequentially.
S32OFS_EOFRelative offset from start of MD4 of the end of the MD4 object.
!(Frame)The array of Frame objects, use OFS_FRAMES.
!(LOD)The array of LOD objects, use OFS_TAGS.
-MD4_ENDEnd of MD4 object. Should match OFS_EOF.


(member of MD4)
VEC3MIN_BOUNDSFirst corner of the bounding box. The bounding box encloses all Surface objects for all LOD objects for this frame (that is, encloses the maximum possible volume occupied in this frame).
VEC3MAX_BOUNDSSecond corner of the bounding box.
VEC3LOCAL_ORIGINMidpoint of bounds, for bounding sphere (sphere cull?)
F32RADIUSRadius of bounding (culling?) sphere.
U8 * 16NAMEName of Frame. ASCII character string, NUL-terminated (C-style).
!(Bone)List of Bone objects. Number of Bone object is determined by NUM_BONES in the MD4 header. This lists all the bones used in this animation frame, and lists their parameters (position/rotation) for this frame.


(member of Frame)
VEC3LOC?3-space Cartesian coordinate?
VEC3X_AXIS?3-space Cartesian vector for X axis?
VEC3Y_AXIS?3-space Cartesian vector for Y axis?
VEC3Z_AXIS?3-space Cartesian vector for Z axis?


(member of MD4)
I32NUM_SURFACESNumber of Surface objects for this LOD object. Current value of MD3_MAX_SURFACES is 32.
I32OFS_SURFACESOffset of Surface object (XXX: relative to where?)
I32OFS_ENDOffset to end of LOD object (next LOD object follow) (XXX: what about final LOD?)
!(Surface)List of Surface objects.


(member of LOD)
-SURFACE_STARTOffset relative to start of MD4 object.
S32IDENTMagic number. As a string of 4 char, reads "IDP4", as unsigned little-endian 1384147059 (0x52806873), as unsigned big endian 1936228434 (0x73688052).
U8 * MAX_QPATHNAMEName of Surface object. ASCII character string, NUL-terminated (C-style). Current value of MAX_QPATH is 64.
U8 * MAX_QPATHNAMEName of shader to use. ASCII character string, NUL-terminated (C-style). Current value of MAX_QPATH is 64.
I32SHADER_INDEXIndex number of shader. (XXX: how assigned?)
I32OFS_HEADEROffset of header? Relative to what? ID source states this is a negative number.
I32NUM_VERTSNumber of Vertex objects for this Surface object. Current value of MD3_MAX_VERTS is 4096.
I32OFS_VERTSOffset of list of Vertex objects (XXX: relative to where?)
I32NUM_TRIANGLESNumber of Triangle objects for this Surface object. Current value of MD3_MAX_TRIANGLES is 8192.
I32OFS_TRIANGLESOffset of list of Triangle objects (XXX: relatvie to where?)
I32NUM_BONE_REFERENCESNumber of BoneReference objects. This allows the engine to look at only those bones which affect this Surface object, instead of calculating the effects of all Bone objects in the MD4 object.
I32OFS_BONE_REFERENCESOffset of list of BoneReference objects (XXX: relative to where?)
I32OFS_ENDOffset to end of MD4 object (XXX: relative to what?)
!(Triangle)List of Triangle objects, use OFS_TRIANGLES
!(Vertex)List of Vertex objects, use OFS_VERTS
!(BoneReference)List of BoneReference objects, use OFS_BONE_REFERENCES
-SURFACE_ENDEnd of Surface object. Should match OFS_END.


(member of Surface)
I32INDEXIndex into list of Bone objects.


(member of Surface)
S32 * 3INDEXESList of offset values into the list of Vertex objects that constitute the corners of the Triangle object. Vertex numbers are used instead of actual coordinates, as the coordinates are implicit in the Vertex object. (XXX: does order matter?)


(member of Surface)
VEC3VERTEX3-space Cartesian coordinate, left-handed geometry.
VEC3NORMALCartesian normal vector, left-handed geometry.
F32 * 2TEXCOORDTexture coordinate on shader that corresponds to this vertex.
I32NUM_WEIGHTSNumber of bones that influence this Vertex object, that is, number of Weight objects.
!(Weight)List of Weight objects that influence this Vertex object.


(member of Vertex)
I32BONE_INDEXIndex into list of BoneReference objects.
F32BONE_WIDHTHow much this BoneReference object influences the Vertex object, as a multiplier(?).

Notable format changes from MD3

First and foremost, the encoding of the normal vector is eliminated in favor of just using the Cartesian coordinates (yay, no crazy lat/lng calculations!).

Vertex coordinates are not scaled as in MD3, and are now full-blown floating-point values, not 16-bit integers.

LOD (Level Of Detail) sets are mashed into one MD4 object.

Only one shader may be associated with a Surface object. As a result, the list of shaders is eliminated (folded into the Surface object directly), and the texture coordinates are folded directly into the Vertex objects, instead of in a separate list of TexCoords [per shader].

-- PhaethonH (