A studio model’s vertices are attached to bones, and the bones form a tree. These
tables, found through the header, describe
the skeleton and the points the game can query on it. The layouts are from
studio.h in the Half-Life SDK (version 10).
Bones
numbones records of 112 bytes at boneindex
(mstudiobone_t). A bone’s six degrees of freedom are always in the order
x, y, z position, then x, y, z rotation.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 32 | char[32] | name | Bone name, such as Bip01 Spine |
| 32 | 4 | int32 | parent | Parent bone number, or −1 for a root bone. A parent always comes before its children |
| 36 | 4 | int32 | flags | Bone flags; see below |
| 40 | 24 | int32[6] | bonecontroller | For each degree of freedom, the bone controller that moves it, or −1 for none |
| 64 | 24 | float32[6] | value | Default position (x, y, z, relative to the parent) and rotation (Euler angles in radians) |
| 88 | 24 | float32[6] | scale | Multiplier for the animation values of each degree of freedom |
Bone flags
studio.h defines these values, but Valve’s studiomdl
writes 0, and the SDK’s own comment on the field is “??”.
| Value | Name | Meaning |
|---|---|---|
| 0x0001 | STUDIO_HAS_NORMALS | Normals are attached to the bone |
| 0x0002 | STUDIO_HAS_VERTICES | Vertices are attached to the bone |
| 0x0004 | STUDIO_HAS_BBOX | The bone has a hit box |
| 0x0008 | STUDIO_HAS_CHROME | Some of the bone’s vertices use a chrome texture |
Bone controllers
numbonecontrollers records of 24 bytes at
bonecontrollerindex (mstudiobonecontroller_t), at most 8. A
controller lets the game turn or move one bone directly, for a turret’s aim or a
mouth.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | int32 | bone | The bone it moves |
| 4 | 4 | int32 | type | Which degree of freedom: one motion flag from STUDIO_X to STUDIO_ZR, optionally with STUDIO_RLOOP |
| 8 | 4 | float32 | start | Value at controller setting 0: units for a position, degrees for a rotation |
| 12 | 4 | float32 | end | Value at controller setting 255 |
| 16 | 4 | int32 | rest | Controller setting, 0 to 255, at rest |
| 20 | 4 | int32 | index | Which of the entity’s controllers drives it: 0 to 3, or 4 for the mouth, which the game opens as a character speaks |
The game stores each controller as a byte. For a normal controller the value is
start + (byte ÷ 255) × (end − start). With
STUDIO_RLOOP the byte is an angle in 256ths of a turn added to
start, and the renderer blends across the shorter way round. The mouth
controller uses the speaking level ÷ 64, at most 1, in the same way.
Motion flags
These flags name degrees of freedom. Bone controllers use the first six; sequences use the rest to describe how the model moves through the world.
| Value | Name | Meaning |
|---|---|---|
| 0x0001 | STUDIO_X | Position x |
| 0x0002 | STUDIO_Y | Position y |
| 0x0004 | STUDIO_Z | Position z |
| 0x0008 | STUDIO_XR | Rotation about x |
| 0x0010 | STUDIO_YR | Rotation about y |
| 0x0020 | STUDIO_ZR | Rotation about z |
| 0x0040 | STUDIO_LX | Linear movement along x over the sequence |
| 0x0080 | STUDIO_LY | Linear movement along y |
| 0x0100 | STUDIO_LZ | Linear movement along z |
| 0x0200 | STUDIO_AX | Automatic movement along x |
| 0x0400 | STUDIO_AY | Automatic movement along y |
| 0x0800 | STUDIO_AZ | Automatic movement along z |
| 0x1000 | STUDIO_AXR | Automatic rotation about x |
| 0x2000 | STUDIO_AYR | Automatic rotation about y |
| 0x4000 | STUDIO_AZR | Automatic rotation about z |
| 0x7FFF | STUDIO_TYPES | Mask of all the flags above |
| 0x8000 | STUDIO_RLOOP | A controller that wraps round the shortest way |
Hit boxes
numhitboxes records of 32 bytes at hitboxindex
(mstudiobbox_t). Each box moves with its bone and is used to test where a
shot hits.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | int32 | bone | The bone it follows |
| 4 | 4 | int32 | group | Hit group; see below |
| 8 | 12 | float32[3] | bbmin | Lower corner, in the bone’s space |
| 20 | 12 | float32[3] | bbmax | Upper corner |
The format leaves the meaning of a group to the game. Half-Life’s game code
(monsters.h) uses these:
| Group | Name |
|---|---|
| 0 | HITGROUP_GENERIC |
| 1 | HITGROUP_HEAD |
| 2 | HITGROUP_CHEST |
| 3 | HITGROUP_STOMACH |
| 4 | HITGROUP_LEFTARM |
| 5 | HITGROUP_RIGHTARM |
| 6 | HITGROUP_LEFTLEG |
| 7 | HITGROUP_RIGHTLEG |
Attachments
numattachments records of 88 bytes at attachmentindex
(mstudioattachment_t): points fixed to a bone, such as a gun’s muzzle,
that the game can ask for, numbered from 0.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 32 | char[32] | name | Name; studiomdl leaves it empty, since the game uses the number |
| 32 | 4 | int32 | type | Not used; 0 |
| 36 | 4 | int32 | bone | The bone it is fixed to |
| 40 | 12 | float32[3] | org | Position in the bone’s space |
| 52 | 36 | float32[3][3] | vectors | Direction vectors; not used, and left 0 by studiomdl |