The visible model is a set of body parts, each a choice of models, each made of meshes
that share one texture. Vertices are stored in the space of the bone they belong to.
The layouts are from studio.h in the Half-Life SDK (version 10); offsets
are from the start of the file that holds the table.
Textures
numtextures records of 80 bytes at textureindex
(mstudiotexture_t). When the main file has none, they are in the
T.mdl companion file.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 64 | char[64] | name | Source image name, such as DM_Base.bmp |
| 64 | 4 | int32 | flags | Texture flags; see below |
| 68 | 4 | int32 | width | Width in pixels |
| 72 | 4 | int32 | height | Height in pixels |
| 76 | 4 | int32 | index | Offset of the pixel data |
At index are width × height bytes, one palette
index per pixel, row by row from the top, followed straight away by the texture’s own
palette: 256 colours of 3 bytes, red, green, blue (768 bytes). Each texture has its
own palette.
Texture flags
| Value | Name | Meaning |
|---|---|---|
| 0x0001 | STUDIO_NF_FLATSHADE | Flat shading |
| 0x0002 | STUDIO_NF_CHROME | Chrome: texture coordinates come from the normals, for a shiny reflective look. studiomdl sets it when the name contains “chrome” |
| 0x0004 | STUDIO_NF_FULLBRIGHT | Drawn at full brightness, ignoring lighting |
| 0x0008 | STUDIO_NF_NOMIPS | No mipmaps |
| 0x0010 | STUDIO_NF_ALPHA | Alpha; the SDK gives no further description |
| 0x0020 | STUDIO_NF_ADDITIVE | Drawn additively, for glows; set with $texrendermode … additive |
| 0x0040 | STUDIO_NF_MASKED | Palette index 255 is transparent; set with $texrendermode … masked |
Skin families
At skinindex is a table of 16-bit texture numbers,
numskinfamilies rows of numskinref each. A mesh names a slot
(its skinref), and the entity’s skin number picks the row, so a model can
swap whole sets of textures, such as a scientist’s different faces.
Body parts
numbodyparts records of 76 bytes at bodypartindex
(mstudiobodyparts_t). Each body part shows exactly one of its models, such
as a head with or without a helmet.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 64 | char[64] | name | Body part name |
| 64 | 4 | int32 | nummodels | Choices in this part |
| 68 | 4 | int32 | base | Product of the nummodels of all earlier parts |
| 72 | 4 | int32 | modelindex | Offset of this part’s models |
The entity’s single body number encodes every part’s choice: part
p shows model (body ÷ base) mod
nummodels, with whole-number division.
Models
nummodels records of 112 bytes at the body part’s
modelindex (mstudiomodel_t).
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 64 | char[64] | name | Model name |
| 64 | 4 | int32 | type | Not written by studiomdl; 0 |
| 68 | 4 | float32 | boundingradius | Radius around the model |
| 72 | 4 | int32 | nummesh | Meshes (20 bytes each) |
| 76 | 4 | int32 | meshindex | Offset of the meshes |
| 80 | 4 | int32 | numverts | Unique vertices |
| 84 | 4 | int32 | vertinfoindex | Offset of one byte per vertex: the bone it belongs to |
| 88 | 4 | int32 | vertindex | Offset of the vertex positions, 3 float32 each, in their bone’s space |
| 92 | 4 | int32 | numnorms | Unique normals |
| 96 | 4 | int32 | norminfoindex | Offset of one byte per normal: the bone it belongs to |
| 100 | 4 | int32 | normindex | Offset of the normals, 3 float32 each, in their bone’s space |
| 104 | 4 | int32 | numgroups | Deformation groups; not written, 0 |
| 108 | 4 | int32 | groupindex | Not written, 0 |
To place a vertex, transform its position by the current matrix of the bone named in its info byte. Normals turn with their bones in the same way.
Meshes
nummesh records of 20 bytes at meshindex
(mstudiomesh_t).
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | int32 | numtris | Triangles in the mesh |
| 4 | 4 | int32 | triindex | Offset of the triangle commands |
| 8 | 4 | int32 | skinref | Texture slot in the current skin family |
| 12 | 4 | int32 | numnorms | Normals the mesh uses |
| 16 | 4 | int32 | normindex | Not written by studiomdl; 0 |
Triangle commands
At triindex is a list of 16-bit commands, as in Quake II’s
MD2:
| First value | Meaning | Followed by |
|---|---|---|
| Positive n | A triangle strip | n vertices |
| Negative n | A triangle fan | −n vertices |
| 0 | End of the list | Nothing |
Each vertex in a command is four int16 values, 8 bytes:
| Offset | Size | Type | Meaning |
|---|---|---|---|
| 0 | 2 | int16 | Vertex number in the model |
| 2 | 2 | int16 | Normal number in the model |
| 4 | 2 | int16 | s, in texture pixels; divide by the texture’s width |
| 6 | 2 | int16 | t, in texture pixels; divide by the texture’s height |
FD FF fan of 3 vertices (−3)
05 00 05 00 10 00 20 00 vertex 5, normal 5, s 16, t 32
... two more vertices
00 00 end of the list