World models (WMO)¶
A WMO (World Model Object) is WoW's format for large static models —
buildings, caves, whole cities. On disk it is a root file plus one group
file per interior/exterior piece (Model_000.wmo …), each an IFF-style chunk
stream. wowlib models the whole thing as one versioned entity: a
WMO<version> holds the typed root plus every group, and reading through the
filesystem gateway pulls the group files automatically.
Reads are byte-perfect round-trips: writing an unmodified WMO reproduces the original bytes exactly, so a targeted edit never churns the rest of the file.
Reading a WMO from the client¶
import wowlib
from wowlib.formats import wmo
model = wmo.WMO.for_version(wowlib.Expansion.Wotlk)
model.read(fs, wowlib.FileKey("World/wmo/Dungeon/AZ_Subway/Subway.wmo"))
for_version returns the concrete class covering that era
(WMOVanillaToWotlk here — eras with identical layouts share one class),
so everything after this line is fully typed.
using WoWLib;
// Typed per-era factory (compile-time concrete class) — or the dynamic
// twin, WMO.ForVersion(Expansion.Wotlk), whose family-base result reads
// and writes the same way (see Writing version-agnostic code).
using var model = WoWLib.Formats.WMO.WMO.Era.Wotlk();
model.Read(fs, new FileKey("World/wmo/Dungeon/AZ_Subway/Subway.wmo"));
Walking the data¶
The root carries the model-wide tables (materials, textures, doodad sets,
portals); each group carries its geometry in a body. Every vector is a
live view of the underlying C++ storage — mutations write through, and
numeric geometry arrays are zero-copy.
Buffers instead of a client¶
You do not need an opened client — the entities also speak plain bytes.
In Python, read/write take any bytes-like or file-like source, with the
group streams passed alongside the root:
model = wmo.WMO.for_version(wowlib.Expansion.Wotlk)
model.read(root_bytes, [group0_bytes, group1_bytes])
import io
sink, groups = io.BytesIO(), [io.BytesIO() for _ in model.groups]
model.write(sink, groups)
Converting between versions¶
convert() re-targets a model to another era's layout — chunks gain or lose
fields according to the target's schema:
For code that should run on any era — driven by whatever client is open — see Writing version-agnostic code.
The exact per-field version coverage is documented with expansion badges on the WMO root and WMO group pages; the C++ signatures live in the C++ reference.