Vvd structures

The vertex file. An Mdl contains no vertex positions whatsoever; they all live here, in a flat pool shared by every mesh in the model.

Unlike the Mdl, the offsets in vertexFileHeader_t are relative to the start of the file, not to the containing structure. The SDK accessors read vertexDataStart + (byte *)this where this is the header, and the header is the file.

Lod sorting. When numFixups is non-zero the vertex pool is sorted by LOD rather than by mesh, and the fixup table has to be replayed to re-establish mesh order before mstudiomodel_t.vertexindex means anything at all. Each vertexFileFixup_t names a run of vertices and the LOD it belongs to; applying them in order rebuilds the pool a mesh reader can index into.

Tangents live in their own parallel pool rather than inside the vertex, which is what keeps mstudiovertex_t at exactly 48 bytes.

struct valvemdl.structs.vvd.vertexFileHeader_t
id(bytes[4], constant=b'IDSV')

magic number; MODEL_VERTEX_FILE_ID

version(int)<parsed from long>
checksum(int)<parsed from long>

must match studiohdr_t.checksum

numLODs(int)<parsed from long>
numLODVertexes(int[8])<parsed from long>

vertex count per lod, MAX_NUM_LODS entries

numFixups(int)<parsed from long>
fixupTableStart(int)<parsed from long>

offset from the start of the file to the fixup table

vertexDataStart(int)<parsed from long>

offset from the start of the file to the vertex pool; 0 means absent

tangentDataStart(int)<parsed from long>

offset from the start of the file to the tangent pool; 0 means absent

struct valvemdl.structs.vvd.vertexFileFixup_t
lod(int)<parsed from long>

used to skip culled root lod

sourceVertexID(int)<parsed from long>

absolute index from start of vertex/tangent blocks

numVertexes(int)<parsed from long>
struct valvemdl.structs.vvd.mstudiovertex_t
m_BoneWeights(mstudioboneweight_t)
m_vecPosition(Vector)
m_vecNormal(Vector)
m_vecTexCoord(Vector2D)
struct valvemdl.structs.vvd.mstudioboneweight_t
weight(float[3])<parsed from float>
bone(int[3])<parsed from signed char>
numbones(int)<parsed from unsigned char>