A sequence is one animation, such as idle1 or run: a number
of frames, each giving every bone’s position and rotation. Sequences are listed in the
main file; their bone data lives in sequence groups. The layouts are from
studio.h in the Half-Life SDK (version 10), and the decoding from the
client’s StudioModelRenderer.cpp.
Sequence groups
numseqgroups records of 104 bytes at seqgroupindex
(mstudioseqgroup_t). Group 0 is the main file itself; group
n is the file name0n.mdl (see
companion files).
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 32 | char[32] | label | Name, such as default |
| 32 | 64 | char[64] | name | File name of the group |
| 96 | 4 | int32 | unused1 | Formerly a cache pointer; ignore |
| 100 | 4 | int32 | unused2 | Formerly a data pointer; ignore |
Sequences
numseq records of 176 bytes at seqindex
(mstudioseqdesc_t).
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 32 | char[32] | label | Sequence name |
| 32 | 4 | float32 | fps | Frames per second |
| 36 | 4 | int32 | flags | 0x0001 STUDIO_LOOPING: the sequence repeats; no other flag is defined |
| 40 | 4 | int32 | activity | Activity number the game asks for, such as walking or dying; numbers belong to the game code (activity.h in Half-Life) |
| 44 | 4 | int32 | actweight | Weight for choosing at random among sequences with the same activity |
| 48 | 4 | int32 | numevents | Events (76 bytes each) |
| 52 | 4 | int32 | eventindex | Offset of the events, from the start of the main file |
| 56 | 4 | int32 | numframes | Frames in the sequence |
| 60 | 4 | int32 | numpivots | Foot pivots (20 bytes each) |
| 64 | 4 | int32 | pivotindex | Offset of the pivots |
| 68 | 4 | int32 | motiontype | Motion flags saying how the root moves, such as STUDIO_LX |
| 72 | 4 | int32 | motionbone | Bone the motion is measured on; studiomdl writes 0 |
| 76 | 12 | float32[3] | linearmovement | Distance the model travels over the whole sequence |
| 88 | 4 | int32 | automoveposindex | Not written by studiomdl; 0 |
| 92 | 4 | int32 | automoveangleindex | Not written; 0 |
| 96 | 12 | float32[3] | bbmin | Bounding box of the sequence, lower corner |
| 108 | 12 | float32[3] | bbmax | Upper corner |
| 120 | 4 | int32 | numblends | Copies of the animation to blend between: 1, 2, or 4 for two directions |
| 124 | 4 | int32 | animindex | Offset of the animation, from the start of the sequence group’s file |
| 128 | 8 | int32[2] | blendtype | Motion flag each blend direction varies, such as STUDIO_XR for aiming up and down |
| 136 | 8 | float32[2] | blendstart | Value at blend setting 0 |
| 144 | 8 | float32[2] | blendend | Value at blend setting 255 |
| 152 | 4 | int32 | blendparent | Not written; 0 |
| 156 | 4 | int32 | seqgroup | Sequence group holding the animation |
| 160 | 4 | int32 | entrynode | Transition node at the start, or 0 |
| 164 | 4 | int32 | exitnode | Transition node at the end, or 0 |
| 168 | 4 | int32 | nodeflags | Non-zero: the sequence may also be played backwards, from exit node to entry node |
| 172 | 4 | int32 | nextseq | Not written; 0 |
The transition graph uses the node fields.
Events
numevents records of 76 bytes at the sequence’s eventindex
(mstudioevent_t). An event fires when playback reaches its frame, for a
footstep sound, a muzzle flash or a hit.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | int32 | frame | Frame on which it fires |
| 4 | 4 | int32 | event | Event number; the game code defines what each number does |
| 8 | 4 | int32 | type | Not written by studiomdl; 0 |
| 12 | 64 | char[64] | options | Text for the event, such as a sound file name |
Pivots
numpivots records of 20 bytes at pivotindex
(mstudiopivot_t): points a foot stays planted on for a span of
frames.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 12 | float32[3] | org | Pivot point |
| 12 | 4 | int32 | start | First frame |
| 16 | 4 | int32 | end | Last frame |
Animation data
At animindex, in the file of the sequence’s group, are
numblends × numbones records of 12 bytes
(mstudioanim_t): all the bones for blend 0, then all for blend 1, and so
on.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 12 | uint16[6] | offset | For x, y, z, x rotation, y rotation, z rotation: where that channel’s values start, counted from the start of this record; 0 means the channel does not move |
A channel with offset 0 keeps the bone’s default value in every frame.
Otherwise its frames are run-length coded in 16-bit slots
(mstudioanimvalue_t). Each run starts with a slot read as two bytes,
valid then total, followed by valid slots read as
signed 16-bit values:
- The run covers
totalframes. - Its first
validframes take the values in order. - The remaining
total−validframes repeat the last value. - The next run starts right after the last value,
valid+ 1 slots on.
A raw value r of channel j becomes
value[j] + r × scale[j], using the bone’s fields.
Rotations are Euler angles in radians. The renderer then adds any bone controller’s
adjustment and blends between neighbouring frames.
Worked example: a channel whose data is 03 05 0A 00 14 00 1E 00 is one
run with valid 3 and total 5. Frames 0, 1 and 2 have the raw
values 10, 20 and 30, and frames 3 and 4 keep 30. With value 0 and
scale 0.5 the positions are 5, 10, 15, 15 and 15.
Blends
With two blends the renderer mixes blend 0 and blend 1 by the entity’s first blending byte ÷ 255. With four, it mixes blends 0 and 1, and 2 and 3, by the first byte, then the two results by the second.