Vtx structures

The optimized mesh file, produced per graphics backend. An Mdl carries no triangle indices; they all live here, already arranged into strips and stripgroups for the hardware.

studiomdl emits one Vtx per backend, distinguished only by file extension: .dx90.vtx, .dx80.vtx and .sw.vtx. Team Fortress 2 loads dx90.

The tree mirrors the mdl. FileHeader_t reaches BodyPartHeader_t reaches ModelHeader_t reaches ModelLODHeader_t reaches MeshHeader_t reaches StripGroupHeader_t reaches StripHeader_t. The bodypart / model / mesh levels correspond one to one with the mdl’s, which is what lets the two files describe the same geometry from two angles. What the vtx adds is everything below a mesh: the strips and stripgroups the hardware actually draws, and the index buffer that turns the vvd’s vertices into triangles.

Packing. Vtx is the one byte-packed family in the studio formats. optimize.h wraps the whole OptimizedModel namespace in #pragma pack(1), so unlike Mdl and Vvd there is no padding between fields and structures do not sit on 4 byte boundaries. StripHeader_t is 27 bytes, not the 32 its field types would suggest, and MaterialReplacementHeader_t is 6, not 8. (This costs ValveMDL nothing: construct concatenates fields without alignment, so it byte-packs by nature. The trap is only for a reader who assumes alignment.)

No magic number. FileHeader_t opens directly on version; there is no signature to identify a Vtx by. The only integrity check available is checkSum, which must match the one in the Mdl and the Vvd.

Its offsets are struct-relative, like the mdl’s and unlike the vvd’s.

struct valvemdl.structs.vtx.MaterialReplacementListHeader_t
numReplacements(int)<parsed from long>
replacementOffset(int)<parsed from long>

offset to an array of MaterialReplacementHeader_t

struct valvemdl.structs.vtx.MaterialReplacementHeader_t
materialID(int)<parsed from short>
replacementMaterialNameOffset(int)<parsed from long>

offset to a string; note this 6 byte struct is why the pack(1) matters

struct valvemdl.structs.vtx.BoneStateChangeHeader_t
hardwareID(int)<parsed from long>
newBoneID(int)<parsed from long>
struct valvemdl.structs.vtx.Vertex_t
boneWeightIndex(int[3])<parsed from unsigned char>

indexes the owning mesh vertex bone weights

numBones(int)<parsed from unsigned char>
origMeshVertID(int)<parsed from unsigned short>

indexes back into the mdl mesh vertex range this belongs to

boneID(int[3])<parsed from signed char>

hardware bone indices for hw skinning, global bone indices for sw

struct valvemdl.structs.vtx.StripHeader_t
numIndices(int)<parsed from long>
indexOffset(int)<parsed from long>

where this strip starts in the stripgroup index array

numVerts(int)<parsed from long>
vertOffset(int)<parsed from long>

where this strip starts in the stripgroup vertex array

numBones(int)<parsed from short>

the maximum bones any vertex in this strip uses

flags(int)<parsed from unsigned char>
Represents a flags Enum
0x1STRIP_IS_TRILIST
0x2STRIP_IS_TRISTRIP
numBoneStateChanges(int)<parsed from long>
boneStateChangeOffset(int)<parsed from long>

offset to an array of BoneStateChangeHeader_t

struct valvemdl.structs.vtx.StripGroupHeader_t
numVerts(int)<parsed from long>
vertOffset(int)<parsed from long>

offset to an array of Vertex_t; the strips index into this

numIndices(int)<parsed from long>
indexOffset(int)<parsed from long>

offset to an array of unsigned short; the index buffer

numStrips(int)<parsed from long>
stripOffset(int)<parsed from long>

offset to an array of StripHeader_t

flags(int)<parsed from unsigned char>
Represents a flags Enum
0x1STRIPGROUP_IS_FLEXED
0x2STRIPGROUP_IS_HWSKINNED
0x3STRIPGROUP_IS_DELTA_FLEXED
0x4STRIPGROUP_SUPPRESS_HW_MORPH
struct valvemdl.structs.vtx.MeshHeader_t
numStripGroups(int)<parsed from long>
stripGroupHeaderOffset(int)<parsed from long>

offset to an array of StripGroupHeader_t

flags(int)<parsed from unsigned char>
Represents a flags Enum
0x1MESH_IS_TEETH
0x2MESH_IS_EYES
struct valvemdl.structs.vtx.ModelLODHeader_t
numMeshes(int)<parsed from long>
meshOffset(int)<parsed from long>

offset to an array of MeshHeader_t

switchPoint(float)<parsed from float>

the distance at which the engine drops to this lod

struct valvemdl.structs.vtx.ModelHeader_t
numLODs(int)<parsed from long>

also in FileHeader_t

lodOffset(int)<parsed from long>

offset to an array of ModelLODHeader_t

struct valvemdl.structs.vtx.BodyPartHeader_t
numModels(int)<parsed from long>
modelOffset(int)<parsed from long>

offset to an array of ModelHeader_t

struct valvemdl.structs.vtx.FileHeader_t
version(int)<parsed from long>

OPTIMIZED_MODEL_FILE_VERSION; note there is no id field preceding this

vertCacheSize(int)<parsed from long>

hardware params that affect how the model is to be optimized

maxBonesPerStrip(int)<parsed from unsigned short>
maxBonesPerTri(int)<parsed from unsigned short>
maxBonesPerVert(int)<parsed from long>
checkSum(int)<parsed from long>

must match studiohdr_t.checksum

numLODs(int)<parsed from long>

also specified in ModelHeader_t, and should match

materialReplacementListOffset(int)<parsed from long>

offset to an array of MaterialReplacementListHeader_t, one per lod

numBodyParts(int)<parsed from long>
bodyPartOffset(int)<parsed from long>

offset to an array of BodyPartHeader_t