Skip to main content

MD3 (.md3)


Overview


MD3 is the 3D data format used in Quake 3: Arena and derivative games (Q3 mods, Return to Castle Wolfenstein, Jedi Knights 2, etc.). The file format is used to describe 3D objects in the game that move and interact with players, other objects, and/or the environment. Animation is recorded by describing the position of every vertex in the model for each frame of animation. This style of animation may also be known as "mesh deformation", "vertex animation", ???.

The Quake series was developed and run on IA32 (x86) machines, using C. The file format shows many evidences 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 MD3 format is presented here from a larger scope to smaller ones.

note

While Doom 3 itself never used .md3 during its development, the engine supports loading MD3 files, though its usage its not advise because of some issues and crashes, though some source ports has fixed this issue, such source ports are:

Data type indicator

Datatypename / purposeDescription
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 → float; read 4 → float; read 4 → float), representing a 3D vector.
*[]Indicates sequential repeat count (homogenous aggregation, array, vector), e.g., "U8 * 16" = 16-octet array.
--File/array offset to note specially.
!!Aggregate complex data that should be described elsewhere.

MD3

Datatypename / purposeDescription
-MD3_STARTOffset of MD3 object; usually 0 but not guaranteed.
S32IDENTMagic number; as 4-char string "IDP3"; little-endian 1367369843 (0x51806873); big-endian 1936228433 (0x73688051).
S32VERSIONMD3 version number; latest known 15; use constant MD3_VERSION.
U8 * MAX_QPATHNAMEMD3 name, usually pathname in PK3; ASCII NUL-terminated string; MAX_QPATH = 64.
S32FLAGSUnknown/unused field.
S32NUM_FRAMESNumber of Frame objects; maximum MD3_MAX_FRAMES (1024).
S32NUM_TAGSNumber of Tag objects; maximum MD3_MAX_TAGS (16).
S32NUM_SURFACESNumber of Surface objects; maximum MD3_MAX_SURFACES (32).
S32NUM_SKINSNumber of Skin objects; typically unused (artifact from MD2).
S32OFS_FRAMESRelative offset from MD3 start to Frame objects; Frames are stored sequentially.
S32OFS_TAGSRelative offset from MD3 start to Tag objects; Tags are stored sequentially.
S32OFS_SURFACESRelative offset from MD3 start to Surface objects; Surfaces are stored sequentially.
S32OFS_EOFRelative offset from MD3 start to end of MD3 object; no offset for Skins.
!(Frame)Aggregate: array of Frame objects; usually follows header but use OFS_FRAMES.
!(Tag)Aggregate: array of Tag objects; usually after Frames but use OFS_TAGS.
!(Surface)Aggregate: array of Surface objects; usually after Tags but use OFS_SURFACES.
-MD3_ENDEnd of MD3 object; should match MD3_START.

Frame

(member of MD3)

Datatypename / purposeDescription
VEC3MIN_BOUNDSFirst corner of the bounding box.
VEC3MAX_BOUNDSSecond corner of the bounding box.
VEC3LOCAL_ORIGINLocal origin, usually 0,0,0.
F32RADIUSRadius of bounding sphere.
U8 * 16NAMEName of Frame; ASCII NUL-terminated string.

Tag

(member of MD3)

Datatypename / purposeDescription
U8 * MAX_QPATHNAMEName of Tag; ASCII NUL-terminated string; MAX_QPATH = 64.
VEC3ORIGINCoordinates of Tag object.
VEC3 * 3AXISOrientation of Tag object (three column vectors).

Surface

(member of MD3)

Datatypename / purposeDescription
-SURFACE_STARTOffset relative to start of MD3 object.
S32IDENTMagic number "IDP3" (0x51806873 little-endian).
U8 * MAX_QPATHNAMESurface name; ASCII NUL-terminated string; MAX_QPATH = 64.
S32FLAGSFlags (implementation-defined).
S32NUM_FRAMESNumber of animation frames; matches MD3 header.
S32NUM_SHADERSNumber of Shader objects; max MD3_MAX_SHADERS (256).
S32NUM_VERTSNumber of Vertex objects per frame; max MD3_MAX_VERTS (4096).
S32NUM_TRIANGLESNumber of Triangle objects; max MD3_MAX_TRIANGLES (8192).
S32OFS_TRIANGLESOffset from SURFACE_START to Triangle list.
S32OFS_SHADERSOffset from SURFACE_START to Shader list.
S32OFS_STOffset from SURFACE_START to St (texture coord) list.
S32OFS_XYZNORMALOffset from SURFACE_START to Vertex (XYZNormal) list.
S32OFS_ENDOffset from SURFACE_START to end of Surface.
!(Shader)List of Shader objects; use OFS_SHADERS.
!(Triangle)List of Triangle objects; use OFS_TRIANGLES.
!(St)List of TexCoord (St) objects; use OFS_ST.
!(XYZNormal)List of Vertex objects; total count = NUM_FRAMES * NUM_VERTS.
-SURFACE_ENDEnd of Surface; should match OFS_END.

Shader

(member of MD3)

Datatypename / purposeDescription
U8 * MAX_QPATHNAMEPathname of shader in PK3; ASCII NUL-terminated string; MAX_QPATH = 64.
S32SHADER_INDEXShader index number (allocation order implementation-defined).

Triangle

(member of MD3)

Datatypename / purposeDescription
S32 * 3INDEXESThree vertex indices into the Vertex list that form the triangle.

TexCoord

(member of MD3)

Datatypename / purposeDescription
F32 * 2STTexture coordinates (S,T) typically in [0.0 .. 1.0], may wrap.

Vertex

(member of MD3)

Datatypename / purposeDescription
S16XX coordinate scaled by MD3_XYZ_SCALE (multiply by MD3_XYZ_SCALE to recover).
S16YY coordinate scaled by MD3_XYZ_SCALE (multiply by MD3_XYZ_SCALE to recover).
S16ZZ coordinate scaled by MD3_XYZ_SCALE (multiply by MD3_XYZ_SCALE to recover).
S16NORMALEncoded normal vector (see Normals section).

Tags

Tags are volumeless vectors. Tags are primarily used in aligning separate MD3 objects in-game. For example, the Tag object in the railgun model is called 'tag_weapon', and the position (and rotation) of this Tag gets aligned with those of the Tag named 'tag_weapon' in the player model, dragging the rest of the railgun model over with the [railgun's] Tag object. The railgun model follows its Tag positions and rotations, which in turn follows the positions and rotations of the player model Tag object (most noticeable in taunt animation). Tags are also used to line up the torso with the legs, and the head with the torso, and so on.

Normals

Encoding

The encoded normal vector uses a spherical coordinate system. Since the normal vector is, by definition, a length of one, only the angles need to be recorded. Each angle is constrained within [0, 255], so as to fit in one octet. A normal vector encodes into 16 bits. (XXX: more blah)

1514131211109876543210
latlatlatlatlatlatlatlatlnglnglnglnglnglnglnglng

(Code in q3tools/common/mathlib.c:NormalToLatLong)

lng <- atan2 ( y / x) * 255 / (2 * pi)
lat <- acos ( z ) * 255 / (2 * pi)
lng <- lower 8 bits of lng
lat <- lower 8 bits of lat
normal <- (lat shift-left 8) binary-or (lng)

Two special vectors are the ones that point up and point down, as these values for z result in a singularity for acos. The special case of straight-up is:

normal <- 0

And the special case of straight down is:

lat <- 0
lng <- 128
normal <- (lat shift-left 8) binary-or (lng)

or, shorter:

normal <- 32768

Decoding

(Code in q3tools/q3map/misc_model.c:InsertMD3Model)

lat <- ((normal shift-right 8) binary-and 255) * (2 * pi ) / 255
lng <- (normal binary-and 255) * (2 * pi) / 255
x <- cos ( lat ) * sin ( lng )
y <- sin ( lat ) * sin ( lng )
z <- cos ( lng )