Build Total War: WARHAMMER III campaign maps without BOB.
AtlasWH3 compiles a campaign map from your Assembly Kit sources (the Terry project, its layers and maps) in one run, then writes it into your mod's pack. It does everything BOB's campaign actions do, plus the AI pathfinding data the game would otherwise have to build, and it runs headless from the command line as well as from a window.
- One run instead of ten actions. A full IEE build takes about 5 minutes on a 4-core machine. A BOB reprocess of the same map takes about 20, with a pack round trip between some of its actions.
- No workarounds. The devastated map needs no fake second campaign. The camera height map builds (BOB's action fails). Nothing has to be packed and installed halfway through.
- Checked against BOB. Most outputs are byte-identical to BOB's on the test maps, and the full IEE build has been played in game.
| Step | Writes | Replaces |
|---|---|---|
heightmaps |
full_height_map.dds, full_logic_map.compressed_map, shroud_heights.dds, lf_normal.dds |
Campaign Heightmap, Campaign Shroud Heights |
tile_list |
tile_list.bin, tile_mask.dds, patch_mask.dds |
Tilemap |
trees |
trees.campaign_tree_list |
Campaign Trees |
global_map |
global_map\ (blend, texture arrays, tile list) |
Global Tilemap, Campaign Global Blendmap |
masks |
colour overlays, corruption, snow and event area masks | Color Overlay, Corruption / Snow / Event Area Mask |
rivers |
models\river_* |
Terry file (river export) |
global_props |
global_props.bin, global_props_sound.bin |
Terry file (props export) |
environment |
environment_collection.xml |
Terry file |
devastation_pieces |
pieces\event_*, event_tiles, event_trees, event_vfx, for the map and its devastated version |
Devastation pieces |
lookup |
*_lookup.tga / .dds, _minimap.tga |
Convert lookup texture |
camera_heightmap |
camera_heightmap.png |
Generate Camera Height Map |
hlp_spd |
hlp_data.esf, spd_data.esf |
the game's own generation during a startpos build |
Independent steps run in parallel. Every step reads the Terry project and the game and mod packs directly, so nothing has to be installed between steps.
How close to BOB: the logic map, shroud heights, tile list, masks, piece textures and river models (but for two
bytes BOB leaves uninitialised) are byte-identical to BOB's on IEE and The Old World. Trees are byte-identical on Old World; on IEE every tree has the
same id, position and rotation, and all but 23 of 253,903 heights agree within 1e-3. global_props.bin has the same
objects in the same bodies; BOB's own record order changes from run to run, so it is compared by content.
hlp_data.esf and spd_data.esf match the game's own files byte for byte on IEE (on Old World one value differs,
where the game's pathfinder has a bug). Each step's record, with every remaining difference explained, is in
docs/native_campaign_build.md.
Where it differs on purpose: patch_mask.dds covers the whole map (BOB's misses a band in the north). Props go
into quadtree cells by their models' real bounds (BOB uses a 2 × 2 × 2 cube for any model not loose in the kit).
Region lookups can move regions past the 1024 palette entries the game reads. Cultures come from the packs'
prefab_types, so a mod's own cultures get their own buckets (BOB read the Assembly Kit's vanilla table). Props get their
regions from map_data.esf, the file the game reads. full_height_map.dds uses AtlasWH3's own BC6H encoder, with
about half the error of BOB's.
- Windows 10 or 11, x64.
- Total War: WARHAMMER III and its Assembly Kit, with your map's Terry project in
raw_data\terrain\campaigns\<map>. - Your campaign's
.pack, set as the project's pack. The build reads CAIME'smap_data.esfandpathfinding.ppdfrom itscampaign_maps\<map>\, and the mod's DB tables, models and textures; then it writes the compiled map into it. (A vanilla map needs none: the build makes a new pack.) - CAIME's region lookup
.bmp, in the Assembly Kit'sraw_data\EmpireDesignData\campaign_maps\<map>(a copy CAIME writes toworking_datais not read). - Optional: the map's devastated project, the one the DB names (
campaigns.terrain_location_devastate), for the devastated pieces.
Download the zip from the Releases page and unzip it
anywhere. It needs no .NET install. Run AtlasWH3.exe for the app, or AtlasWH3.Cli.exe from a terminal.
The download is unsigned, so Windows SmartScreen may warn on the first start (More info › Run anyway).
The build writes into your mod's pack in the game's data folder, keeping one copy of it as it was
(%LocalAppData%\AtlasWH3\output\pack_backups). The compiled files go to %LocalAppData%\AtlasWH3\output\compiled\<map>;
the Assembly Kit's working_data is not touched.
- Settings: check the game and Assembly Kit folders (found automatically on a Steam install).
- Start page: pick the map from the kit (Project: No project).
- Build (Ctrl+B): New project for <map>…, save it, pick the mod pack the map ships in (or Cancel for a pack of its own), then Build all (F7).
- Start page: pick the project under Project. The editors then open on its map, mod packs and tile map source.
The Build window shows each step's progress, time and notes, and keeps a log of every build in
%LocalAppData%\AtlasWH3\output\build_logs.
AtlasWH3.Cli new-project iee.atlaswh3 --map cr_combi_expanded_map_1 --pack "D:\...\data\!cr_immortal_empires_expanded.pack"
AtlasWH3.Cli build --project iee.atlaswh3
--segments validate,compilebuilds without writing the pack.--steps tile_list,global_mapcompiles only those steps.--jsonprints the report as JSON.AtlasWH3.Cli helplists every command.
A project (.atlaswh3, JSON) holds one map's build: what the Terry project doesn't. This one is IEE's: the build
writes into the mod's own pack in the game's data folder. Settings left out keep their defaults; the Assembly Kit and
the game are the ones in Settings (or the CLI's --ak).
{
"name": "IEE",
"map": "cr_combi_expanded_map_1",
"build": {
"packFile": "{game}\\!cr_immortal_empires_expanded.pack"
}
}- Segments: a build runs Validate, Compile and Pack, in that order, and stops at the first failure.
customStepsadds your own commands before or after any of them (a startpos build, a script). - Tokens:
{project},{game}(the data folder),{ak},{map},{out}and{pack}expand in paths. Relative paths are relative to the project file. - Pack: writes the files this build made into
packFileand keeps everything else in it (DB, scripts, battle maps, CAIME's files); river models and pieces the build no longer makes are dropped. A pack that doesn't exist yet is created. It refuses while the game runs, writes beside the pack and swaps it in, and first keeps one copy of the pack in%LocalAppData%\AtlasWH3\output\pack_backups("backupPack": falseturns that off).piecesPackFile(optional) takes the event-area pieces instead, for a campaign that ships them in a pack of their own (Old World).modPackslists other packs the map reads (only read). Projects made before this keep working: theirpacksandinstallbecomepackFileon load. - Map settings:
acceptTileMap(tile-map check codes to build through),patchMask(fitted, orvanillafor BOB's),lookupLast(regions to move past palette entry 1024),keepOutsideQuadtree(truekeeps objects placed past the map's edge, in the quadtree cell nearest to them; BOB drops them),zeroShroudWestEdge(truesets shroud_heights.dds to 0 over the map's westmost 10 units). - Output:
output(or the CLI's--out) compiles into another folder; by default it is%LocalAppData%\AtlasWH3\output\compiled\<map>.
startpos.esfis not built: it still comes from the game (an RPFM startpos build works as a custom step).- CAIME's files are inputs, not outputs.
- The full build has been played in game on IEE. Other maps, Old World included, are checked against BOB's output.
- The Scene editor, Tile map and Campaign battles windows are a preview: they come from Atlas3K and are only partly moved to WH3.
The full list is in KNOWN_ISSUES.md.
docs/native_campaign_build.md: each step, what it reads and writes, and its parity with BOB.docs/end_to_end.md: the IEE build, from the kit into the game, and the in-game checks.docs/hlp_spd.md: the AI pathfinding files.docs/bob_wh3.md: what WH3's BOB runs headless, and what it reads and writes.docs/atlaswh3_plan.md: the plan, its decisions and phases.docs/atlas3k/: Atlas3K's Three Kingdoms notes, kept for their method.research/: the scripts behind the parity work (Ghidra, Frida traces of BOB, byte-level diffs).
dotnet build AtlasWH3.slnx -c Release
dotnet test src/AtlasWH3.Tests --filter "FullyQualifiedName~BuildProjectTests"
powershell -ExecutionPolicy Bypass -File tools\publish.ps1
- SDK: .NET 10 (
global.jsontakes any 10.0 feature band from 10.0.100). - Publish:
publish.ps1writes the self-contained download todist\AtlasWH3-<version>\and zips it. - Tests: the full suite takes about 16 minutes, so filter to the classes you touch. Tests that need the game or kit data skip when it is missing.
| Project | Contents |
|---|---|
AtlasWH3.Formats |
Readers and writers: packs, DB, Terry, map.hex, tiles, BMD v27, ESF, DDS / BC6H, RMV2 |
AtlasWH3.Core |
The build steps, projects and packing, the editors' model |
AtlasWH3.Cli |
AtlasWH3.Cli.exe |
AtlasWH3.App |
AtlasWH3.exe (WPF, Direct3D 11) |
AtlasWH3.Tests |
xUnit |
Parity fixtures: the data tests run on two maps, IEE (cr_combi_expanded_map_1) and Old World
(cr_oldworld_map_1). Freeze them right after a BOB run, so the tests keep comparing those sources with those BOB
outputs while the maps change:
AtlasWH3.Cli fixture-freeze iee --maps cr_combi_expanded_map_1,cr_combi_expanded_map_devastate_1 --packs !cr_immortal_empires_expanded.pack
AtlasWH3.Cli fixture-freeze oldworld --maps cr_oldworld_map_1,cr_oldworld_map_devastate_1 --packs !cr_oldworld_campaign.pack,!cr_oldworld_campaign_devastate.pack
AtlasWH3.Cli fixture-check
A fixture lives in atlaswh3_fixtures beside the kit, as hard links (no extra disk space). Without one, the tests
read the live kit. Add --replace to freeze a fixture again.
- WH3_visual_map_decompiler does the reverse: it turns a compiled WH3 campaign map back into a Terry project. AtlasWH3 reuses its format readers, and the two together give a round trip.
- CampaignMapToolkit (CAIME) makes the campaign AI map files AtlasWH3 takes as input. Its code is under a non-commercial licence, so none of it is copied here.
- RPFM: AtlasWH3 reads its
schema_wh3.ronfor DB layouts when it is installed.
AtlasWH3 is a fork of Atlas3K, the same tooling for Total War: THREE KINGDOMS. The two games share an engine, and Atlas3K's pack, PNG, hex grid, tree and Terry code, and its method of matching BOB byte for byte, are the base this was built on. Atlas3K's own README is at the fork point.
MIT, as Atlas3K. The libraries it uses keep their own licences; see THIRD_PARTY_NOTICES.md.
Not affiliated with Creative Assembly or SEGA. Total War: WARHAMMER III and its Assembly Kit are their property. AtlasWH3 ships none of the game's data.