diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index dc88a37d..3e037cc7 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -1080,12 +1080,31 @@ void EditorApp::exportZone(const std::string& outputDir) { LOG_INFO("Converted ", convertedWMOs.size(), " WMO buildings to WOB"); } - // Export used textures as PNG (open format replacement for BLP) - auto usedTextures = TextureExporter::collectUsedTextures(terrain_); - if (!usedTextures.empty()) { - int exported = TextureExporter::exportTexturesAsPng( - assetManager_.get(), usedTextures, base + "/textures"); - LOG_INFO("Exported ", exported, " textures as PNG"); + // Export used textures as PNG (open format replacement for BLP). + // Includes terrain textures plus textures referenced by every placed M2/NPC model + // so the exported zone has every texture it needs to render without game data. + std::vector usedTextures; + { + std::unordered_set uniq; + for (auto& t : TextureExporter::collectUsedTextures(terrain_)) uniq.insert(std::move(t)); + std::unordered_set visitedM2; + 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)); + }; + for (const auto& obj : objectPlacer_.getObjects()) { + if (obj.type == PlaceableType::M2) addM2Tex(obj.path); + } + for (const auto& npc : npcSpawner_.getSpawns()) addM2Tex(npc.modelPath); + + usedTextures.assign(uniq.begin(), uniq.end()); + std::sort(usedTextures.begin(), usedTextures.end()); + if (!usedTextures.empty()) { + int exported = TextureExporter::exportTexturesAsPng( + assetManager_.get(), usedTextures, base + "/textures"); + LOG_INFO("Exported ", exported, " textures as PNG (terrain + ", visitedM2.size(), " M2s)"); + } } // Export zone-relevant DBCs as JSON (open format replacement for DBC) diff --git a/tools/editor/texture_exporter.cpp b/tools/editor/texture_exporter.cpp index fd9c7257..d4fa6a2a 100644 --- a/tools/editor/texture_exporter.cpp +++ b/tools/editor/texture_exporter.cpp @@ -1,6 +1,7 @@ #include "texture_exporter.hpp" #include "pipeline/asset_manager.hpp" #include "pipeline/blp_loader.hpp" +#include "pipeline/m2_loader.hpp" #include "core/logger.hpp" #include #include @@ -20,6 +21,30 @@ std::vector TextureExporter::collectUsedTextures(const pipeline::AD return result; } +std::vector TextureExporter::collectM2Textures(pipeline::AssetManager* am, + const std::string& m2Path) { + std::vector out; + if (!am || m2Path.empty()) return out; + + auto data = am->readFile(m2Path); + if (data.empty()) return out; + auto m2 = pipeline::M2Loader::load(data); + // Skin file holds geometry but textures live in the M2 header itself. + // Even if isValid() is false (no skin loaded), the texture list is populated. + + std::unordered_set unique; + for (const auto& tex : m2.textures) { + if (tex.filename.empty()) continue; + std::string p = tex.filename; + 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 f483be3a..d8e44cc8 100644 --- a/tools/editor/texture_exporter.hpp +++ b/tools/editor/texture_exporter.hpp @@ -15,6 +15,11 @@ public: // Collect all texture paths referenced by the terrain static std::vector collectUsedTextures(const pipeline::ADTTerrain& terrain); + // Collect all texture paths referenced by an M2 model (loads the M2 from `am`). + // Returns lowercased game paths (e.g. "creature\\foo\\foo.blp"). Empty if M2 not found. + static std::vector collectM2Textures(pipeline::AssetManager* am, + const std::string& m2Path); + // Export all used textures as PNG to an output directory // Returns count of successfully exported textures static int exportTexturesAsPng(pipeline::AssetManager* am,