feat(extract): emit WHM+WOT+WOC for ADT terrain tiles

Final piece of the open-format emit pipeline:
  --emit-terrain  foo.adt → foo.whm + foo.wot + foo.woc

With this, --emit-open now produces a fully open-format zone
alongside every Blizzard MPQ extraction:
  BLP  → PNG       (textures)
  DBC  → JSON      (data tables)
  M2   → WOM       (models, with skin merge)
  WMO  → WOB       (buildings, with group merge)
  ADT  → WHM/WOT   (terrain heights + metadata)
       → WOC       (collision mesh derived from heights)

Originals stay on disk and indexed by manifest.json so private
servers continue to load proprietary formats; wowee runtime/editor
read the open formats directly. One extraction now feeds both
audiences with no separate conversion pass.

Implementation:
- Inline WHM+WOT writer in open_format_emitter.cpp (mirrors the
  editor's WoweeTerrain::exportOpen but without the PNG-preview /
  normal-map deps so the extractor stays editor-independent).
- Tile coords (x,y) parsed from <map>_<x>_<y>.adt filename.
- Collision mesh derived via WoweeCollisionBuilder::fromTerrain
  (terrain triangles only — WMO collision overlays would need
  asset manager and aren't worth the extractor complexity).
This commit is contained in:
Kelsi 2026-05-06 10:36:14 -07:00
parent e6ace7cce5
commit d4c69a2b46
6 changed files with 182 additions and 4 deletions

View file

@ -979,11 +979,12 @@ bool Extractor::run(const Options& opts) {
// Open-format emission: walk the extracted tree and write
// wowee-format side-files (PNG / JSON DBC) next to each .blp/.dbc.
// Originals are left untouched so private servers continue to work.
if (opts.emitPng || opts.emitJsonDbc || opts.emitWom || opts.emitWob) {
if (opts.emitPng || opts.emitJsonDbc || opts.emitWom || opts.emitWob ||
opts.emitTerrain) {
std::cout << "Emitting wowee open-format side-files...\n";
OpenFormatStats ofs;
emitOpenFormats(effectiveOutputDir, opts.emitPng, opts.emitJsonDbc,
opts.emitWom, opts.emitWob, ofs);
opts.emitWom, opts.emitWob, opts.emitTerrain, ofs);
if (opts.emitPng) {
std::cout << " PNG (BLP→PNG) : " << ofs.pngOk << " ok";
if (ofs.pngFail) std::cout << ", " << ofs.pngFail << " failed";
@ -1004,6 +1005,11 @@ bool Extractor::run(const Options& opts) {
if (ofs.wobFail) std::cout << ", " << ofs.wobFail << " failed";
std::cout << "\n";
}
if (opts.emitTerrain) {
std::cout << " WHM/WOT/WOC (ADT) : " << ofs.whmOk << " ok";
if (ofs.whmFail) std::cout << ", " << ofs.whmFail << " failed";
std::cout << "\n";
}
}
// Cache WoW.exe for Warden MEM_CHECK responses