MJOLNIR Tag Editor changelog
MJOLNIR Tag Editorv0.20.0

Levels and Nanite meshes as glTF, textures as DDS and TIFF

A mission's Unreal geometry exports as glTF, a Nanite mesh shows and exports at full detail, a texture exports as DDS or TIFF, and tag references poke into the running game.

Until now a Nanite mesh showed as the reduced fallback the cook kept beside its cluster pages, a texture left the editor only as a PNG of one mip, and a mesh did not leave at all. The pages now decode, and both kinds of asset go out in the forms the tools on the other side expect.

Added

  • Nanite geometry. A static or skeletal mesh that ships as Nanite cluster pages — nearly every SM_ asset, and the vehicles' skeletal hulls — is decoded from those pages and shown at full detail, welded on position, normal and UV, one section per material. The header says Nanite, full detail instead of the LOD number. The decoder is a port of CUE4Parse's reader (Apache-2.0, credited in NOTICE), checked against the install: every one of the first 1,500 shipped static meshes decodes with no vertex outside its cluster's box and a triangle count within a tenth of what the builder was given. The warthog's skeletal mesh, which held only its suspension classically, comes out whole at 197k triangles.

  • DDS export. The Export… button on a texture offers DDS beside PNG: the cooked pixel format — BC1, BC3, BC5, BC7, BC6H, the uncompressed layouts — with the whole mip chain, no re-encoding. A virtual texture's Morton-tiled mips are put back into linear block images on the way, so the file opens like any other DDS. Checked against a shipped 4096² virtual texture: the reassembled blocks decode to the same pixels the tile path shows.

  • TIFF export. The same button offers an uncompressed 8-bit RGBA TIFF of the shown mip, for tools that would rather not read PNG.

  • glTF export. The mesh viewer gained export .glb…: every LOD, one primitive per material slot named after it, positions, normals and the first UV set, converted to metres and +Y up. A skeletal mesh comes out in its rest pose with its bones as a node hierarchy.

  • Level export. The World view of a scenario gained export level geometry…: pick a folder and every level package of the mission — the persistent level and its World Partition cells — is read for its placed static meshes, and each cell that places anything is written as one .glb with a node per placement at its world transform, plus a manifest.json of counts and skips. Instanced components (foliage, scree, rocks — the bulk of every cell) are expanded from their per-instance matrices; a Blueprint actor's mesh comes through its template; attach chains compose. A30 comes to 576 cells, 1.8 million placements and about a gigabyte in 40 seconds. Hierarchical-LOD proxies, hidden components, landscape, child actors and Niagara are counted in the manifest rather than placed.

  • String ids are checked before a bake. An edit that sets a string id the game's registry does not contain is refused when you test or export, with the field named, instead of a warning you could miss — the game rejects the whole tag over it. The registry as the game held it in mission A30 ships with the editor; a new checkbox under Try it, allow unregistered string ids, bakes anyway when you mean it. A registered name set fresh still gets a note, since another mission may not have registered it yet.

  • String ids poke live. Poking a string-id field of the root element writes the id the running game gave that name, read from its registry; a name the game has not registered is refused before anything is written.

  • Tag references poke live. With live mode on, retyping a reference field points it somewhere else in the running game straight away: the target is looked up in the game's own table of loaded tags and the field is written the way the loader writes one — the group, an offset to the target's path, the length, and the handle. It works on a reference inside a block element as readily as one in the root, so an assault rifle can be made to fire banshee bolts by retyping barrels[0].projectile. The target has to be a tag the game has already loaded, since the handle is a slot in that table; anything else is refused with what it would have needed, and clearing a reference sets it to none.

  • Live mode on an unmeasured build. When a game update moves the engine's tables, live mode derives the eight addresses from four anchors in the tag module's code instead of falling back to the sweep — the same anchors reproduce the measured CU4 profile exactly.

Known limits

  • Materials in a .glb are named slots with a plain grey look; the textures are not bundled. Skeletal meshes are not skinned: the cooked buffers this reader decodes carry no weights.
  • Weapons and most Covenant vehicles keep a one-triangle placeholder in both their classic buffers and their Nanite pages; their bodies are assembled at runtime from other assets, so the viewer still says so rather than showing the triangle.
  • A biome tree or plant decodes wider than its fallback: the fallback is the wind-proxy shape, the Nanite mesh the full crown.
  • A level export places each mesh's classic fallback LOD; the full Nanite geometry would multiply the size. Landscape heightfields, Blueprint components whose class the usmap does not describe, and Blueprint template components the cell never instanced are reported, not placed. The files share nothing: a mesh used in forty cells is written forty times.