Bodyparts, models and meshes¶
The geometry spine:
studiohdr_t.bodypartindexreaches an array ofmstudiobodyparts_t, each of which ownsmstudiomodel_ts, each of which ownsmstudiomesh_tes. It is a three level tree, and none of it contains a single vertex position or triangle index – those live in the Vvd and Vtx respectively. What this tree describes is the shape of the model: which pieces exist, which are swappable, and which material each piece wants.A bodypart is a set of alternatives, not a part. Despite the name, a bodypart holds
nummodelsmutually exclusive options – a head bodypart might hold “head”, “head with hat” and “nothing”. The engine picks one per bodypart using a single packed integer, the bodygroup, decoded withbase:(bodynum / base) % nummodels
baseis the stride studiomdl assigned this bodypart when it packed them all into that one int. A model with no bodyparts at all is not malformed – it is an animation-only model, and there are plenty in Team Fortress 2.A model is not the model.
mstudiomodel_tis one alternative inside one bodypart. Itsnameis stored inline as a fixed 64 byte buffer, likestudiohdr_t.nameand unlike almost every other string in the format.vertexindex and tangentsindex are byte offsets, not counts. They point into the Vvd’s vertex and tangent pools. The SDK gives the game away in
GetGlobalVertexIndex, which divides before using it:i + ( modelptr->vertexindex / sizeof( mstudiovertex_t ) )So the vertex a model starts at is
vertexindex / 48. Treating the field as a vertex count is the classic way to read a model that looks almost right.A mesh’s vertexoffset, on the other hand, is a count – vertices from the start of its own model, not bytes. Two adjacent fields, two different units.
modelindex points backwards.
mstudiomesh_tcarries a negative offset back to the model that owns it, so it is signed and it resolves to something earlier in the file.material is not a texture index. It is a column into the skin table; see
mstudiotexture_t.vertexdata is half runtime junk and half real data.
modelvertexdatais a pointer the engine fills in, zero on disk.numLODVertexesimmediately after it is genuine: it is how many vertices this mesh keeps at each level of detail, andStudio_SetRootLODuses it to cull. So the structure has to be read, but only half of it believed.
- struct valvemdl.structs.mesh.mstudiomesh_t¶
-
material
(<parsed from long>¶int) a column into the skin table, not an index into the texture array
-
modelindex
(<parsed from long>¶int) NEGATIVE offset back to the
mstudiomodel_tthat owns this mesh
-
vertexoffset
(<parsed from long>¶int) where this mesh starts within its model, in VERTICES; unlike
mstudiomodel_t.vertexindex, which is bytes
-
flexindex
(<parsed from long>¶int) offset to an array of
mstudioflex_t
-
vertexdata
(¶mstudio_meshvertexdata_t)
-
material
- struct valvemdl.structs.mesh.mstudiomodel_t¶
-
name
(<parsed from a 64 bytes long padded string>¶string) stored inline as a fixed 64 byte buffer, not as an offset
-
meshindex
(<parsed from long>¶int) offset to an array of
mstudiomesh_t
-
vertexindex
(<parsed from long>¶int) offset into the vvd vertex pool, in BYTES; divide by sizeof(
mstudiovertex_t) for a vertex index
-
eyeballindex
(<parsed from long>¶int) offset to an array of
mstudioeyeball_t
-
vertexdata
(¶mstudio_modelvertexdata_t)
-
name
- struct valvemdl.structs.mesh.mstudio_meshvertexdata_t¶
- struct valvemdl.structs.mesh.mstudio_modelvertexdata_t¶