diff --git a/include/pipeline/wowee_model.hpp b/include/pipeline/wowee_model.hpp index a0bba030..487eda21 100644 --- a/include/pipeline/wowee_model.hpp +++ b/include/pipeline/wowee_model.hpp @@ -90,7 +90,12 @@ public: // Convenience: try loading .wom from the standard editor // search paths (custom_zones/models/, output/models/). Returns valid model on hit. - static WoweeModel tryLoadByGamePath(const std::string& gamePath); + // `extraPrefixes` are tried before the defaults — pass per-zone roots like + // {"output//models/", "custom_zones//models/"} when the caller + // knows the active zone. + static WoweeModel tryLoadByGamePath( + const std::string& gamePath, + const std::vector& extraPrefixes = {}); }; } // namespace pipeline diff --git a/src/pipeline/wowee_model.cpp b/src/pipeline/wowee_model.cpp index 922f7606..91d3695c 100644 --- a/src/pipeline/wowee_model.cpp +++ b/src/pipeline/wowee_model.cpp @@ -500,17 +500,26 @@ M2Model WoweeModelLoader::toM2(const WoweeModel& wom) { return m; } -WoweeModel WoweeModelLoader::tryLoadByGamePath(const std::string& gamePath) { +WoweeModel WoweeModelLoader::tryLoadByGamePath( + const std::string& gamePath, + const std::vector& extraPrefixes) { std::string base = gamePath; auto dot = base.rfind('.'); if (dot != std::string::npos) base = base.substr(0, dot); std::replace(base.begin(), base.end(), '\\', '/'); - for (const char* prefix : {"custom_zones/models/", "output/models/"}) { - std::string full = std::string(prefix) + base; + auto tryPrefix = [&](const std::string& prefix) -> WoweeModel { + std::string full = prefix + base; if (exists(full)) { auto wom = load(full); if (wom.isValid()) return wom; } + return {}; + }; + for (const auto& p : extraPrefixes) { + if (auto w = tryPrefix(p); w.isValid()) return w; + } + for (const char* p : {"custom_zones/models/", "output/models/"}) { + if (auto w = tryPrefix(p); w.isValid()) return w; } return {}; } diff --git a/src/rendering/terrain_manager.cpp b/src/rendering/terrain_manager.cpp index 5ea6b940..37e84bda 100644 --- a/src/rendering/terrain_manager.cpp +++ b/src/rendering/terrain_manager.cpp @@ -482,25 +482,22 @@ std::shared_ptr TerrainManager::prepareTile(int x, int y) { } // Check for WOM open format first (custom zone models) - std::string womBase = m2Path; - auto womDot = womBase.rfind('.'); - if (womDot != std::string::npos) womBase = womBase.substr(0, womDot); - // Check custom_zones and output directories - std::vector womPrefixes = {"custom_zones/models/", "output/" + mapName + "/models/"}; - for (const std::string& prefix : womPrefixes) { - std::string womPath = prefix + womBase; - std::replace(womPath.begin(), womPath.end(), '\\', '/'); - if (pipeline::WoweeModelLoader::exists(womPath)) { - auto wom = pipeline::WoweeModelLoader::load(womPath); - if (wom.isValid()) { - // toM2() handles WOM1/WOM2/WOM3 (multi-batch + materials) - auto m2Model = pipeline::WoweeModelLoader::toM2(wom); - pending->m2Models.push_back({modelId, std::move(m2Model), {}}); - preparedModelIds.insert(modelId); - LOG_INFO("Loaded WOM model: ", womPath, " (v", wom.version, - ", ", wom.batches.size(), " batches)"); - return true; - } + // Try open WOM format first via shared helper. Per-zone prefixes are + // checked before the global fallback so a zone export overrides a + // generic custom asset of the same name. + { + std::vector extraPrefixes = { + "output/" + mapName + "/models/", + "custom_zones/" + mapName + "/models/", + }; + auto wom = pipeline::WoweeModelLoader::tryLoadByGamePath(m2Path, extraPrefixes); + if (wom.isValid()) { + auto m2Model = pipeline::WoweeModelLoader::toM2(wom); + pending->m2Models.push_back({modelId, std::move(m2Model), {}}); + preparedModelIds.insert(modelId); + LOG_INFO("Loaded WOM model: ", m2Path, " (v", wom.version, + ", ", wom.batches.size(), " batches)"); + return true; } }