From 341c07d41234144fdcaf5c78cbca65e47e6aa119 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 08:53:12 -0700 Subject: [PATCH] feat(editor): add --regen-collision CLI for batch WOC rebuild Walks a zone directory recursively, finds every WHM file, and rebuilds the matching WOC. Useful after batch terrain edits when you want to refresh collision for many tiles in one shot. Reports per-tile triangle counts and exits 1 if any rebuild failed. --- tools/editor/main.cpp | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 2781de37..5e969c35 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -32,6 +32,7 @@ static void printUsage(const char* argv0) { std::printf(" --list-zones List discovered custom zones and exit\n"); std::printf(" --scaffold-zone [tx ty] Create a blank zone in custom_zones// and exit\n"); std::printf(" --build-woc Generate a WOC collision mesh from WHM/WOT and exit\n"); + std::printf(" --regen-collision Rebuild every WOC under a zone dir and exit\n"); std::printf(" --export-png Render heightmap, normal-map, and zone-map PNG previews\n"); std::printf(" --validate Score zone open-format completeness and exit\n"); std::printf(" --zone-summary One-shot validate + creature/object/quest counts and exit\n"); @@ -64,7 +65,7 @@ int main(int argc, char* argv[]) { "--info-creatures", "--info-objects", "--info-quests", "--info-wcp", "--list-wcp", "--unpack-wcp", "--pack-wcp", "--validate", "--zone-summary", - "--scaffold-zone", "--build-woc", "--export-png", + "--scaffold-zone", "--build-woc", "--regen-collision", "--export-png", "--convert-m2", "--convert-wmo", }; for (int i = 1; i < argc; i++) { @@ -510,6 +511,42 @@ int main(int argc, char* argv[]) { wowee::editor::WoweeTerrain::exportZoneMap(terrain, base + "_zone.png", 512); std::printf("Exported PNGs: %s_{heightmap,normals,zone}.png\n", base.c_str()); return 0; + } else if (std::strcmp(argv[i], "--regen-collision") == 0 && i + 1 < argc) { + // Find all WHM/WOT pairs under a zone dir and rebuild WOC for each. + // Useful after sculpting changes when you want to re-derive + // collision in batch instead of one tile at a time. + std::string zoneDir = argv[++i]; + namespace fs = std::filesystem; + if (!fs::exists(zoneDir)) { + std::fprintf(stderr, "regen-collision: %s does not exist\n", + zoneDir.c_str()); + return 1; + } + int rebuilt = 0, failed = 0; + for (auto& entry : fs::recursive_directory_iterator(zoneDir)) { + if (!entry.is_regular_file()) continue; + if (entry.path().extension() != ".whm") continue; + std::string base = entry.path().string(); + base = base.substr(0, base.size() - 4); // strip .whm + wowee::pipeline::ADTTerrain terrain; + if (!wowee::pipeline::WoweeTerrainLoader::load(base, terrain)) { + std::fprintf(stderr, " FAILED to load: %s\n", base.c_str()); + failed++; + continue; + } + auto col = wowee::pipeline::WoweeCollisionBuilder::fromTerrain(terrain); + std::string outPath = base + ".woc"; + if (wowee::pipeline::WoweeCollisionBuilder::save(col, outPath)) { + std::printf(" WOC rebuilt: %s (%zu triangles)\n", + outPath.c_str(), col.triangles.size()); + rebuilt++; + } else { + std::fprintf(stderr, " FAILED to save: %s\n", outPath.c_str()); + failed++; + } + } + std::printf("regen-collision: %d rebuilt, %d failed\n", rebuilt, failed); + return failed > 0 ? 1 : 0; } else if (std::strcmp(argv[i], "--build-woc") == 0 && i + 1 < argc) { // Generate a WOC collision mesh from a WHM/WOT terrain pair. // Uses terrain triangles only (no WMO overlays); useful as a