Inverse kinematics

The structures that let the engine plant a foot on uneven ground rather than letting it hover, and keep a hand on a weapon that has moved.

studiohdr_t.ikchainindex reaches the mstudioikchain_t list – the chains a model has, typically one per leg. Each chain is a short list of mstudioiklink_t, naming the bones it runs through and the direction the knee bends.

The rules live on the animations, not the chains, and not the sequences. mstudioikrule_t hangs off mstudioanimdesc_t.ikruleindex, and says what a chain should do during that animation. mstudioseqdesc_t.numikrules is a count with no array behind it. 21.85% of animdescs in Team Fortress 2 carry at least one rule, so this is a common path rather than an exotic one.

Error terms come in two forms. ikerrorindex reaches uncompressed mstudioikerror_t; compressedikerrorindex reaches a mstudiocompressedikerror_t, which stores six scales and six offsets into the same animvalue streams the bone animation uses. Only one is present.

pError is frame biased. The accessor indexes (i - iStart), not i: a rule that begins partway through an animation stores only the frames it covers.

mstudiolocalhierarchy_t is the neighbouring idea: it lets an animation temporarily reparent a bone – a hand to a steering wheel for the duration of a sequence – rather than solving for it.

struct valvemdl.structs.ik.mstudiolocalhierarchy_t
iBone(int)<parsed from long>
iNewParent(int)<parsed from long>

the bone to temporarily reparent iBone to

start(float)<parsed from float>
peak(float)<parsed from float>
tail(float)<parsed from float>
end(float)<parsed from float>
iStart(int)<parsed from long>
localanimindex(int)<parsed from long>
unused(int[4])<parsed from long>
struct valvemdl.structs.ik.mstudioikrule_t
index(int)<parsed from long>
type(int)<parsed from long>

one of the IK constants; IK_SELF, IK_WORLD, IK_GROUND, IK_RELEASE, IK_ATTACHMENT, IK_UNLATCH

chain(int)<parsed from long>

which mstudioikchain_t this rule drives

bone(int)<parsed from long>
slot(int)<parsed from long>
height(float)<parsed from float>
radius(float)<parsed from float>
floor(float)<parsed from float>
pos(Vector)
q(Quaternion)
compressedikerrorindex(int)<parsed from long>

offset to a mstudiocompressedikerror_t

unused2(int)<parsed from long>
iStart(int)<parsed from long>

the first frame this rule covers; pError indexes (i - iStart)

ikerrorindex(int)<parsed from long>

offset to an array of mstudioikerror_t

start(float)<parsed from float>
peak(float)<parsed from float>
tail(float)<parsed from float>
end(float)<parsed from float>
unused3(float)<parsed from float>
contact(float)<parsed from float>
drop(float)<parsed from float>
top(float)<parsed from float>
unused6(int)<parsed from long>
unused7(int)<parsed from long>
unused8(int)<parsed from long>
szattachmentindex(int)<parsed from long>

offset to a string; the attachment name for IK_ATTACHMENT

unused(int[7])<parsed from long>
struct valvemdl.structs.ik.mstudiocompressedikerror_t
scale(float[6])<parsed from float>
offset(int[6])<parsed from short>

one offset per channel to an mstudioanimvalue_t stream; the same encoding the bone animation uses

struct valvemdl.structs.ik.mstudioikerror_t
pos(Vector)
q(Quaternion)
struct valvemdl.structs.ik.mstudioikchain_t
sznameindex(int)<parsed from long>

offset to a string

linktype(int)<parsed from long>
linkindex(int)<parsed from long>

offset to an array of mstudioiklink_t

the direction the joint bends, so the solver does not pick the wrong one