Quickstart ========== Installation ------------ .. code-block:: bash pip install valvemdl Reading a model --------------- .. code-block:: python from valvemdl import Mdl mdl = Mdl('models/props_farm/ambulance.mdl') mdl.version # 48 mdl.name # 'ambulance.mdl' mdl.checksum # ties the mdl to its vvd and vtx The whole :any:`studiohdr_t` is available as ``mdl.header``, and :any:`studiohdr2_t` as ``mdl.header2`` when the model has one: .. code-block:: python mdl.header.numbones mdl.header.mass mdl.header.flags.STUDIOHDR_FLAGS_STATIC_PROP A model is a set of files ------------------------- An mdl on its own contains no vertices and no triangles. Those live in the ``.vvd`` and ``.vtx`` files sitting next to it, tied together by a checksum. Opening an mdl notices which of those files are present but does not read them. They are parsed the first time you ask for them, and cached afterwards: .. code-block:: python mdl.sidecar_paths # {'.vvd': '...', '.dx90.vtx': '...', ...} mdl.vvd # parsed now mdl.vvd # same object, not reparsed studiomdl compiles one vtx per graphics backend. ``mdl.vtx`` gives you the ``dx90`` one, which is what Team Fortress 2 loads; the others are reachable explicitly: .. code-block:: python mdl.get_vtx('.sw.vtx') Not every model has sidecars. Animation-only models carry no geometry at all, and asking for their vertices raises: .. code-block:: python from valvemdl import MdlSidecarMissingError try: mdl.vvd except MdlSidecarMissingError: pass Checking a model over --------------------- :py:meth:`validate` checks the invariants the format promises -- that the header's ``length`` really is the size of the file, that the version is one we have seen, and that the sidecars' checksums agree with the mdl's. It reports rather than raises, because a model that fails one of these is usually the interesting one: .. code-block:: python for problem in mdl.validate(): print(problem) Writing it back --------------- Each of the three files knows how to write itself: .. code-block:: python mdl.save('out.mdl') # or mdl.save() to overwrite the original mdl.vvd.save('out.vvd') mdl.vtx.save('out.vtx') The bytes are rebuilt from the object graph, not copied from the file, so what lands on disk reflects the model as it now is. Across every model in a stock Team Fortress 2 install this rebuild is byte-for-byte identical to what studiomdl wrote -- the one exception being a single file with a jigglebone that nothing points at, which the object graph cannot reach and so cannot reproduce. :py:meth:`coverage` reports how much of a file |proj_name| can account for, split into bytes regenerated from structures, bytes copied at a measured extent (the compressed animation streams), and bytes nothing wrote: .. code-block:: python mdl.coverage() # {'struct': ..., 'opaque': ..., 'differ': 0, ...} mdl.vvd.coverage() # a vvd is all struct: no compressed regions