feat(editor): auto-convert placed M2 objects to WOM on zone export

- Zone export now converts all placed M2 models to WOM open format
- Deduplicates: each unique M2 path converted only once
- WOM files saved to output/MapName/models/ with original path structure
- Uses WoweeModelLoader::fromM2() for M2→WOM conversion
- Combined with PNG texture export, zones can now be fully distributed
  without any Blizzard proprietary files

Full open format export pipeline:
  Terrain: ADT→WOT/WHM | Textures: BLP→PNG | Data: DBC→JSON
  Models: M2→WOM | Buildings: WMO→WOB (manual) | Map: WDT→zone.json
This commit is contained in:
Kelsi 2026-05-05 10:31:51 -07:00
parent 71c3eb0fe6
commit 5e2cb6fb3c

View file

@ -5,6 +5,7 @@
#include "wowee_terrain.hpp"
#include "texture_exporter.hpp"
#include "dbc_exporter.hpp"
#include "pipeline/wowee_model.hpp"
#include "core/coordinates.hpp"
#include "rendering/vk_context.hpp"
#include "pipeline/adt_loader.hpp"
@ -16,6 +17,7 @@
#include <algorithm>
#include <chrono>
#include <sstream>
#include <unordered_set>
namespace wowee {
namespace editor {
@ -733,6 +735,26 @@ void EditorApp::exportZone(const std::string& outputDir) {
objectPlacer_.saveToFile(objPath);
}
// Convert placed M2 objects to WOM open format
if (objectPlacer_.objectCount() > 0) {
std::unordered_set<std::string> convertedModels;
for (const auto& obj : objectPlacer_.getObjects()) {
if (obj.type == PlaceableType::M2 && !convertedModels.count(obj.path)) {
auto wom = pipeline::WoweeModelLoader::fromM2(obj.path, assetManager_.get());
if (wom.isValid()) {
std::string womPath = obj.path;
std::replace(womPath.begin(), womPath.end(), '\\', '/');
auto dot = womPath.rfind('.');
if (dot != std::string::npos) womPath = womPath.substr(0, dot);
pipeline::WoweeModelLoader::save(wom, base + "/models/" + womPath);
convertedModels.insert(obj.path);
}
}
}
if (!convertedModels.empty())
LOG_INFO("Converted ", convertedModels.size(), " M2 models to WOM");
}
// Export used textures as PNG (open format replacement for BLP)
auto usedTextures = TextureExporter::collectUsedTextures(terrain_);
if (!usedTextures.empty()) {