diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index 896767ac..ab4ba7d1 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -1152,13 +1152,20 @@ void EditorApp::exportZone(const std::string& outputDir) { std::unordered_set uniq; for (auto& t : TextureExporter::collectUsedTextures(terrain_)) uniq.insert(std::move(t)); std::unordered_set visitedM2; + std::unordered_set visitedWMO; auto addM2Tex = [&](const std::string& m2Path) { if (m2Path.empty() || !visitedM2.insert(m2Path).second) return; for (auto& t : TextureExporter::collectM2Textures(assetManager_.get(), m2Path)) uniq.insert(std::move(t)); }; + auto addWMOTex = [&](const std::string& wmoPath) { + if (wmoPath.empty() || !visitedWMO.insert(wmoPath).second) return; + for (auto& t : TextureExporter::collectWMOTextures(assetManager_.get(), wmoPath)) + uniq.insert(std::move(t)); + }; for (const auto& obj : objectPlacer_.getObjects()) { if (obj.type == PlaceableType::M2) addM2Tex(obj.path); + else if (obj.type == PlaceableType::WMO) addWMOTex(obj.path); } for (const auto& npc : npcSpawner_.getSpawns()) addM2Tex(npc.modelPath); @@ -1167,7 +1174,8 @@ void EditorApp::exportZone(const std::string& outputDir) { if (!usedTextures.empty()) { int exported = TextureExporter::exportTexturesAsPng( assetManager_.get(), usedTextures, base + "/textures"); - LOG_INFO("Exported ", exported, " textures as PNG (terrain + ", visitedM2.size(), " M2s)"); + LOG_INFO("Exported ", exported, " textures as PNG (terrain + ", + visitedM2.size(), " M2s + ", visitedWMO.size(), " WMOs)"); } } diff --git a/tools/editor/texture_exporter.cpp b/tools/editor/texture_exporter.cpp index d4fa6a2a..45470241 100644 --- a/tools/editor/texture_exporter.cpp +++ b/tools/editor/texture_exporter.cpp @@ -2,6 +2,7 @@ #include "pipeline/asset_manager.hpp" #include "pipeline/blp_loader.hpp" #include "pipeline/m2_loader.hpp" +#include "pipeline/wmo_loader.hpp" #include "core/logger.hpp" #include #include @@ -45,6 +46,38 @@ std::vector TextureExporter::collectM2Textures(pipeline::AssetManag return out; } +std::vector TextureExporter::collectWMOTextures(pipeline::AssetManager* am, + const std::string& wmoPath) { + std::vector out; + if (!am || wmoPath.empty()) return out; + + auto rootData = am->readFile(wmoPath); + if (rootData.empty()) return out; + auto wmo = pipeline::WMOLoader::load(rootData); + + // Load group files so any group-only texture references are populated too. + std::string base = wmoPath; + if (base.size() > 4) base = base.substr(0, base.size() - 4); + for (uint32_t gi = 0; gi < wmo.nGroups; gi++) { + char suffix[16]; + std::snprintf(suffix, sizeof(suffix), "_%03u.wmo", gi); + auto gd = am->readFile(base + suffix); + if (!gd.empty()) pipeline::WMOLoader::loadGroup(gd, wmo, gi); + } + + std::unordered_set unique; + for (const auto& tex : wmo.textures) { + if (tex.empty()) continue; + std::string p = tex; + std::transform(p.begin(), p.end(), p.begin(), + [](unsigned char c) { return std::tolower(c); }); + unique.insert(p); + } + out.assign(unique.begin(), unique.end()); + std::sort(out.begin(), out.end()); + return out; +} + int TextureExporter::exportTexturesAsPng(pipeline::AssetManager* am, const std::vector& texturePaths, const std::string& outputDir) { diff --git a/tools/editor/texture_exporter.hpp b/tools/editor/texture_exporter.hpp index d8e44cc8..bac55148 100644 --- a/tools/editor/texture_exporter.hpp +++ b/tools/editor/texture_exporter.hpp @@ -20,6 +20,11 @@ public: static std::vector collectM2Textures(pipeline::AssetManager* am, const std::string& m2Path); + // Collect all texture paths referenced by a WMO root file. + // Includes textures used by every group (loads root + group files via `am`). + static std::vector collectWMOTextures(pipeline::AssetManager* am, + const std::string& wmoPath); + // Export all used textures as PNG to an output directory // Returns count of successfully exported textures static int exportTexturesAsPng(pipeline::AssetManager* am,