The geometry of an MD3 model is in its surfaces. Each surface is one mesh drawn with
one shader, such as a head or a weapon’s body, and holds its vertices for every frame.
The first surface starts at the header’s ofsSurfaces; each surface’s
ofsEnd leads to the next. All offsets inside a surface are counted from the
start of that surface. This page follows qfiles.h and the loader in the
Quake III Arena source.
Surface header
108 bytes (md3Surface_t).
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | char[4] | ident | IDP3 as written by the tools; the engine does not check it |
| 4 | 64 | char[64] | name | Surface name, which skin files use to give it a shader |
| 68 | 4 | int32 | flags | No flags are defined; the engine does not read it |
| 72 | 4 | int32 | numFrames | Frames, the same as the model’s |
| 76 | 4 | int32 | numShaders | Shaders, at most 256 |
| 80 | 4 | int32 | numVerts | Vertices in each frame, at most 4096 (1000 in Quake III’s renderer) |
| 84 | 4 | int32 | numTriangles | Triangles, at most 8192 (2000 in Quake III’s renderer) |
| 88 | 4 | int32 | ofsTriangles | Offset of the triangles |
| 92 | 4 | int32 | ofsShaders | Offset of the shaders |
| 96 | 4 | int32 | ofsSt | Offset of the texture coordinates |
| 100 | 4 | int32 | ofsXyzNormals | Offset of the vertices, numVerts × numFrames of them |
| 104 | 4 | int32 | ofsEnd | Size of the surface: the next surface starts here |
The loader makes the name lower case and, if its second-to-last character is
_, cuts the last two characters, so head_1 is matched as
head. The source calls this a crutch for its model tool, which added such
endings. id’s tools write the parts after the header in the order shaders, triangles,
texture coordinates, vertices.
Shaders
numShaders entries of 68 bytes at ofsShaders
(md3Shader_t).
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 64 | char[64] | name | Shader or image path, such as models/players/sarge/band.tga |
| 64 | 4 | int32 | shaderIndex | Filled in by the engine when it loads the model; the stored value means nothing |
A skin file, when the game uses one, overrides these by surface name. Otherwise the
engine draws the surface with shader number skinNum modulo
numShaders, and with its default shader if there are none.
Triangles
numTriangles entries of 12 bytes at ofsTriangles
(md3Triangle_t).
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 12 | int32[3] | indexes | Three vertex numbers within this surface, counted from 0 |
Unlike MD2, a vertex has one texture coordinate, so a vertex on a seam is stored twice.
Texture coordinates
numVerts entries of 8 bytes at ofsSt
(md3St_t), one per vertex and shared by every frame.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | float32 | st[0] | Horizontal position, 0 to 1 across the image |
| 4 | 4 | float32 | st[1] | Vertical position, 0 to 1 across the image |
Vertices
numVerts × numFrames entries of 8 bytes at
ofsXyzNormals (md3XyzNormal_t): every vertex of frame 0,
then every vertex of frame 1, and so on. Vertex v of frame f is
entry f × numVerts + v.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 6 | int16[3] | xyz | Position x, y, z in units of 1/64 (MD3_XYZ_SCALE), so −512 to just under 512 |
| 6 | 1 | uint8 | longitude | Low byte of normal: the angle from straight up |
| 7 | 1 | uint8 | latitude | High byte of normal: the angle around the vertical axis |
Decoding the normal
Each byte is an angle in 256ths of a full turn: lat = latitude × 2π ÷ 256 and lng = longitude × 2π ÷ 256. The renderer then computes:
x = cos(lat) × sin(lng)
y = sin(lat) × sin(lng)
z = cos(lng)
tr_surface.c.The encoder in id’s tools (NormalToLatLong) writes straight up as
longitude 0, latitude 0, and straight down as longitude 128, latitude 0. Other
directions are encoded from atan2(y, x) and acos(z) scaled by
255/360 per degree rather than 256/360, so a decoded normal can differ from the original
by a fraction of a step.
Worked example: the eight bytes 40 00 80 FF 00 01 40 40 give
x = 64 ÷ 64 = 1.0, y = −128 ÷ 64 = −2.0 and z = 256 ÷ 64 = 4.0. The normal’s longitude is
0x40 = 64, a quarter turn, and its latitude is also 64, so the normal is
cos 90° × sin 90° = 0, sin 90° × sin 90° = 1 and cos 90° = 0: (0, 1, 0).