A python library to read and write .mdl files, the model format of the Source
engine, along with their .vvd and .vtx companions.
ValveMDL is about serialization and nothing else. It will read a model apart and put it back together; it will not transform meshes, render anything, or convert to other formats.
ValveMDL reads and writes all three files. Across a stock Team Fortress 2 install -- 9,858 models spanning format versions 44 to 48 -- a model read apart and written back is byte-for-byte identical to what studiomdl produced, with a single exception caused by orphaned data in one shipped file. See the documentation for the structure by structure detail.
https://pysourcesdk.github.io/ValveMDL/
The docs describe the mdl format itself as much as they describe the library. If you never intend to write a line of python, the data structures section is still the reference you came for.
pip install valvemdlfrom valvemdl import Mdl
mdl = Mdl('models/props_farm/ambulance.mdl')
mdl.version # 48
mdl.name # 'ambulance.mdl'
mdl.header.numbones # 3A model is really a set of files. An mdl contains no vertices and no triangles;
those live in the .vvd and .vtx next to it, tied together by a checksum.
Opening an mdl notices which of them are present but does not read them until
you ask:
mdl.sidecar_paths # {'.vvd': '...', '.dx90.vtx': '...', ...}
mdl.vvd # parsed now, cached after
mdl.get_vtx('.sw.vtx')Team Fortress 2 ships mdl versions 44, 45, 46, 47 and 48 -- five variants of the
same format, in the same game. Version 48 accounts for about 91% of them.
studiohdr_t is unchanged across that whole range.
ValveMDL is evidence driven: a structure is supported once it has been read out of real files and cross-checked against the Source SDK, and not before. Gaps are left as gaps rather than guessed at.
Issues and pull requests are welcome. Be pythonic, document and test your code. That's all.
One extra rule applies here, because ValveMDL is as much a format reference as it is a library: do not guess. Every field, flag and version rule should trace back either to the Source SDK or to bytes observed in real model files. If something is unknown, say that it is unknown.