mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-06 09:03:52 +00:00
Placed WMO buildings reference textures (walls, floors, decorations) that were not being exported alongside the WOB files. Added collectWMOTextures() which loads the root WMO + all group files and gathers every texture path, then folds these into the same PNG export pass that handles terrain and M2 textures. Exported zones now have every texture they need across all model types.
122 lines
4.4 KiB
C++
122 lines
4.4 KiB
C++
#include "texture_exporter.hpp"
|
|
#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 <filesystem>
|
|
#include <algorithm>
|
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
#include "stb_image_write.h"
|
|
|
|
namespace wowee {
|
|
namespace editor {
|
|
|
|
std::vector<std::string> TextureExporter::collectUsedTextures(const pipeline::ADTTerrain& terrain) {
|
|
std::unordered_set<std::string> unique;
|
|
for (const auto& tex : terrain.textures)
|
|
unique.insert(tex);
|
|
std::vector<std::string> result(unique.begin(), unique.end());
|
|
std::sort(result.begin(), result.end());
|
|
return result;
|
|
}
|
|
|
|
std::vector<std::string> TextureExporter::collectM2Textures(pipeline::AssetManager* am,
|
|
const std::string& m2Path) {
|
|
std::vector<std::string> 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<std::string> 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;
|
|
}
|
|
|
|
std::vector<std::string> TextureExporter::collectWMOTextures(pipeline::AssetManager* am,
|
|
const std::string& wmoPath) {
|
|
std::vector<std::string> 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<std::string> 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<std::string>& texturePaths,
|
|
const std::string& outputDir) {
|
|
namespace fs = std::filesystem;
|
|
int exported = 0;
|
|
|
|
for (const auto& texPath : texturePaths) {
|
|
auto blpImage = am->loadTexture(texPath);
|
|
if (!blpImage.isValid()) {
|
|
LOG_WARNING("Texture not found or invalid: ", texPath);
|
|
continue;
|
|
}
|
|
|
|
// Build output path: replace backslashes, change .blp to .png
|
|
std::string outPath = texPath;
|
|
std::replace(outPath.begin(), outPath.end(), '\\', '/');
|
|
// Lowercase
|
|
std::transform(outPath.begin(), outPath.end(), outPath.begin(),
|
|
[](unsigned char c) { return std::tolower(c); });
|
|
// Change extension
|
|
auto dotPos = outPath.rfind('.');
|
|
if (dotPos != std::string::npos)
|
|
outPath = outPath.substr(0, dotPos) + ".png";
|
|
|
|
std::string fullPath = outputDir + "/" + outPath;
|
|
fs::create_directories(fs::path(fullPath).parent_path());
|
|
|
|
// Write RGBA data as PNG
|
|
if (stbi_write_png(fullPath.c_str(), blpImage.width, blpImage.height, 4,
|
|
blpImage.data.data(), blpImage.width * 4)) {
|
|
exported++;
|
|
} else {
|
|
LOG_WARNING("Failed to write PNG: ", fullPath);
|
|
}
|
|
}
|
|
|
|
LOG_INFO("Exported ", exported, "/", texturePaths.size(), " textures as PNG to ", outputDir);
|
|
return exported;
|
|
}
|
|
|
|
} // namespace editor
|
|
} // namespace wowee
|