Sequences¶
A sequence is what the game actually plays.
studiohdr_t.localseqindexreaches an array ofmstudioseqdesc_t, and each one names the animations it is built from, how to blend them, what events fire during it, and what it is allowed to transition to. Animations are the raw material; sequences are the thing an entity asks for by name.Sequences reference animations through a grid.
animindexindexpoints at ashort[groupsize[1]][groupsize[0]]of animation indices, and the twoparamindexvalues name the pose parameters that pick a cell. A one dimensional blend hasgroupsizeof{n, 1}; a two dimensional one – a movement sequence blended over speed and direction, say – fills the grid. The SDK’sanim(x, y)clamps both axes rather than trusting the caller.paramindex needs two caveats before it means anything.
-1marks an axis as unused, but agroupsizeof0means there is no axis at all, and in that case studiomdl leavesparamindexat0rather than-1– a value that looks like “pose parameter 0” and is not. More importantly, the index is into the model’s merged pose parameter list, not its local one. A model that borrows from another via$includemodelcan name a pose parameter it does not itself declare: the ninec_*_armsviewmodels in Team Fortress 2 each havenumlocalposeparameters == 0and sequences pointing at parameters that live inc_*_animations.mdl. The SDK resolves this throughGetSharedPoseParameterand the virtual model. ValveMDL does not build virtual models, so on a model with includes these indices are simply not ours to interpret.posekeyindex has a bug in the SDK, and the bug is the format. The accessor is:
inline float *pPoseKey( int iParam, int iAnim ) const { return (float *)(((byte *)this) + posekeyindex) + iParam * groupsize[0] + iAnim; }
iParam * groupsize[0]uses the first axis’s stride for both axes, which is only right wheniParamis 0. studiomdl writes what this reads, so a writer that “fixes” the stride produces a file the engine misreads. The comment two lines up admits the shape is wrong: “FIXME: make this 2D instead of 2x1D arrays”.weightlistindex is a float per bone, letting a sequence apply to only part of the skeleton – an upper body gesture over a running lower body.
Events are cues, not effects. Each
mstudioevent_tnames a cycle and a thing to do at it: play a sound, spawn a particle, land a footstep.NEW_EVENT_STYLE(1<<10) intypedistinguishes events named by the string atszeventindexfrom older ones identified by theeventinteger.optionsis an inline 64 byte buffer rather than an offset.numikrules here is a count only. The rules themselves hang off the animdescs, not the sequence. 21.85% of animdescs have some.
- struct valvemdl.structs.seq.mstudioseqdesc_t¶
-
baseptr
(<parsed from long>¶int) NEGATIVE offset back to the
studiohdr_t
-
eventindex
(<parsed from long>¶int) offset to an array of
mstudioevent_t
-
animindexindex
(<parsed from long>¶int) offset to a short[groupsize[1]][groupsize[0]] grid of animation indices
-
autolayerindex
(<parsed from long>¶int) offset to an array of
mstudioautolayer_t
-
weightlistindex
(<parsed from long>¶int) offset to a float[numbones]; how much this sequence affects each bone
-
posekeyindex
(<parsed from long>¶int) offset to a float[2][groupsize[n]]; see the note about pPoseKey
-
iklockindex
(<parsed from long>¶int) offset to an array of
mstudioiklock_t
-
cycleposeindex
(<parsed from long>¶int) offset to a local pose parameter used to cycle through animations
-
activitymodifierindex
(<parsed from long>¶int) offset to an array of
mstudioactivitymodifier_t
-
baseptr
- struct valvemdl.structs.seq.mstudioactivitymodifier_t¶
- struct valvemdl.structs.seq.mstudioiklock_t¶
- struct valvemdl.structs.seq.mstudioautolayer_t¶