Bones

The skeleton. mstudiobone_t is reached from studiohdr_t.boneindex, one per bone, and almost everything else in the format refers back to it: hitboxes, attachments, meshes and animations all name bones by index into this array.

The tree is implicit. There is no child list; each bone carries a parent index instead, and -1 marks a root. Parents always precede their children, which is what lets the engine build world transforms in a single forward pass.

The compression scales live here, not on the animation. posscale and rotscale are the per-axis multipliers used to decode the compressed animation values in mstudioanim_t. A bone’s animated position is value * posscale[axis], offset from pos unless the animation is a delta. Read an animation without its bones and the numbers mean nothing.

Procedural bones. When proctype is non-zero, procindex points at one of four different structures – mstudioaxisinterpbone_t, mstudioquatinterpbone_t, mstudioaimatbone_t or mstudiojigglebone_t – and the engine computes the bone at runtime rather than reading it from an animation. procindex == 0 means none. Note that STUDIO_PROC_AIMATBONE and STUDIO_PROC_AIMATATTACH are two different proctypes sharing one structure, differing only in what aim refers to.

physicsbone is not an offset, despite studiobyteswap.cpp tagging it DEFINE_INDEX. It is an index into the physics model’s bone list. The datadesc is wrong here in the same way it is wrong about m_nBoneFlexDriverCount.

mstudiolinearbone_t is the same skeleton again, transposed. Reached from studiohdr2_t, it is a struct of arrays holding what mstudiobone_t holds as an array of structs – every bone’s pos in one run, every quat in the next, and so on. The engine walks it when it wants one field of every bone and would rather not stride 216 bytes to get each one. It is redundant data: a writer must keep it in step with the bones or the engine will animate from one and draw from the other.

mstudiosrcbonetransform_t records how a bone was transformed at compile time, so studiomdl can map animations authored against a different skeleton onto this one. mstudioboneflexdriver_t is the reverse of a flex: a bone driving a flex controller, so that a jaw bone can move a face.

struct valvemdl.structs.bone.mstudiojigglebone_t
flags(int)<parsed from long>

a combination of the JIGGLE_* flags

length(float)<parsed from float>

how far from bone base, along bone, is tip

tipMass(float)<parsed from float>
yawStiffness(float)<parsed from float>
yawDamping(float)<parsed from float>
pitchStiffness(float)<parsed from float>
pitchDamping(float)<parsed from float>
alongStiffness(float)<parsed from float>
alongDamping(float)<parsed from float>
angleLimit(float)<parsed from float>

maximum deflection of tip in radians

minYaw(float)<parsed from float>
maxYaw(float)<parsed from float>
yawFriction(float)<parsed from float>
yawBounce(float)<parsed from float>
minPitch(float)<parsed from float>
maxPitch(float)<parsed from float>
pitchFriction(float)<parsed from float>
pitchBounce(float)<parsed from float>
baseMass(float)<parsed from float>
baseStiffness(float)<parsed from float>
baseDamping(float)<parsed from float>
baseMinLeft(float)<parsed from float>
baseMaxLeft(float)<parsed from float>
baseLeftFriction(float)<parsed from float>
baseMinUp(float)<parsed from float>
baseMaxUp(float)<parsed from float>
baseUpFriction(float)<parsed from float>
baseMinForward(float)<parsed from float>
baseMaxForward(float)<parsed from float>
baseForwardFriction(float)<parsed from float>
boingImpactSpeed(float)<parsed from float>
boingImpactAngle(float)<parsed from float>
boingDampingRate(float)<parsed from float>
boingFrequency(float)<parsed from float>
boingAmplitude(float)<parsed from float>
struct valvemdl.structs.bone.mstudioaimatbone_t
parent(int)<parsed from long>
aim(int)<parsed from long>

the bone or attachment to aim at, depending on proctype

aimvector(Vector)
upvector(Vector)
basepos(Vector)
struct valvemdl.structs.bone.mstudioquatinterpbone_t
control(int)<parsed from long>

local transformation to check

numtriggers(int)<parsed from long>
triggerindex(int)<parsed from long>

offset to an array of mstudioquatinterpinfo_t

struct valvemdl.structs.bone.mstudioquatinterpinfo_t
inv_tolerance(float)<parsed from float>

inverse of the tolerance of triggers

trigger(Quaternion)

angle to match

pos(Vector)

new position

quat(Quaternion)

new angle

struct valvemdl.structs.bone.mstudioaxisinterpbone_t
control(int)<parsed from long>

local transformation of this bone used to calc 3 point blend

axis(int)<parsed from long>

axis to check

pos(Vector[6])

X+, X-, Y+, Y-, Z+, Z-

quat(Quaternion[6])
struct valvemdl.structs.bone.mstudiobonecontroller_t
bone(int)<parsed from long>

-1 for none

type(int)<parsed from long>

a combination of the STUDIO_X/Y/Z/XR/YR/ZR motion flags

start(float)<parsed from float>
end(float)<parsed from float>
rest(int)<parsed from long>

byte index value at rest

inputfield(int)<parsed from long>

0-3 user set controller, 4 mouth

unused(int[8])<parsed from long>
struct valvemdl.structs.bone.mstudioboneflexdriver_t
m_nBoneIndex(int)<parsed from long>

the bone doing the driving; not an offset, despite the datadesc

m_nControlCount(int)<parsed from long>
m_nControlIndex(int)<parsed from long>

offset to an array of mstudioboneflexdrivercontrol_t; an offset, despite the datadesc calling it a float

unused(int[3])<parsed from long>
struct valvemdl.structs.bone.mstudioboneflexdrivercontrol_t
m_nBoneComponent(int)<parsed from long>

which component of the bone drives the controller; not an offset, despite the datadesc

m_nFlexControllerIndex(int)<parsed from long>
m_flMin(float)<parsed from float>

not an offset, despite the datadesc

m_flMax(float)<parsed from float>

not an offset, despite the datadesc

struct valvemdl.structs.bone.mstudiosrcbonetransform_t
sznameindex(int)<parsed from long>

offset to a string

pretransform(float[4][3])<parsed from float>
posttransform(float[4][3])<parsed from float>
struct valvemdl.structs.bone.mstudiolinearbone_t
numbones(int)<parsed from long>
flagsindex(int)<parsed from long>

offset to an int per bone

parentindex(int)<parsed from long>

offset to an int per bone

posindex(int)<parsed from long>

offset to a Vector per bone

quatindex(int)<parsed from long>

offset to a Quaternion per bone

rotindex(int)<parsed from long>

offset to a RadianEuler per bone

posetoboneindex(int)<parsed from long>

offset to a matrix3x4_t per bone

posscaleindex(int)<parsed from long>

offset to a Vector per bone

rotscaleindex(int)<parsed from long>

offset to a Vector per bone

qalignmentindex(int)<parsed from long>

offset to a Quaternion per bone

unused(int[6])<parsed from long>
struct valvemdl.structs.bone.mstudiobone_t
sznameindex(int)<parsed from long>

offset to a string

parent(int)<parsed from long>

index of the parent bone, -1 for a root

bonecontroller(int[6])<parsed from long>

index into studiohdr_t bone controllers per axis, -1 for none

pos(Vector)

default position

quat(Quaternion)

default orientation

rot(RadianEuler)

default orientation again, as euler angles

posscale(Vector)

per axis multiplier used to decode compressed animation positions

rotscale(Vector)

per axis multiplier used to decode compressed animation rotations

poseToBone(float[4][3])<parsed from float>
qAlignment(Quaternion)
flags(int)<parsed from unsigned long>
Represents a flags Enum
0x1BONE_PHYSICALLY_SIMULATED
0x2BONE_PHYSICS_PROCEDURAL
0x3BONE_ALWAYS_PROCEDURAL
0x4BONE_SCREEN_ALIGN_SPHERE
0x5BONE_SCREEN_ALIGN_CYLINDER
0x9BONE_USED_BY_HITBOX
0xaBONE_USED_BY_ATTACHMENT
0xbBONE_USED_BY_VERTEX_LOD0
0xcBONE_USED_BY_VERTEX_LOD1
0xdBONE_USED_BY_VERTEX_LOD2
0xeBONE_USED_BY_VERTEX_LOD3
0xfBONE_USED_BY_VERTEX_LOD4
0x10BONE_USED_BY_VERTEX_LOD5
0x11BONE_USED_BY_VERTEX_LOD6
0x12BONE_USED_BY_VERTEX_LOD7
0x13BONE_USED_BY_BONE_MERGE
0x15BONE_FIXED_ALIGNMENT
0x16BONE_HAS_SAVEFRAME_POS
0x17BONE_HAS_SAVEFRAME_ROT
proctype(int)<parsed from long>

one of the STUDIO_PROC_* constants, 0 for none

procindex(int)<parsed from long>

offset to the procedural rule named by proctype; 0 means none

physicsbone(int)<parsed from long>

index into the physics model bone list; not an offset, despite the datadesc

surfacepropidx(int)<parsed from long>

offset to a string

contents(int)<parsed from long>

BSP contents flags

unused(int[8])<parsed from long>