diff --git a/include/pipeline/asset_manager.hpp b/include/pipeline/asset_manager.hpp index f2285ddb..fbcb7bcc 100644 --- a/include/pipeline/asset_manager.hpp +++ b/include/pipeline/asset_manager.hpp @@ -147,6 +147,16 @@ public: */ size_t purgeExtractedAssets(); + /** + * Resolve a normalized WoW path to its on-disk location. Checks the + * override directory first, then the manifest, then the base-fallback + * manifest. Public so callers (e.g. terrain_manager probing for + * sidecar files like .whm/.wot/.woc next to a .adt) can locate the + * extracted file's directory without reading it. + * @return absolute or relative fs path, or "" if not found + */ + std::string resolveFile(const std::string& normalizedPath) const; + private: bool initialized = false; std::string dataPath; @@ -162,11 +172,7 @@ private: std::string baseFallbackDataPath_; AssetManifest baseFallbackManifest_; - /** - * Resolve filesystem path: check override dir first, then base manifest. - * Returns empty string if not found. - */ - std::string resolveFile(const std::string& normalizedPath) const; + // (resolveFile moved to public — declaration above.) // Guards fileCache, dbcCache, fileCacheTotalBytes, fileCacheAccessCounter, and // fileCacheBudget. Shared lock for read-only cache lookups (readFile cache hit, diff --git a/src/rendering/terrain_manager.cpp b/src/rendering/terrain_manager.cpp index 6f9b95c3..612b7e55 100644 --- a/src/rendering/terrain_manager.cpp +++ b/src/rendering/terrain_manager.cpp @@ -368,6 +368,37 @@ std::shared_ptr TerrainManager::prepareTile(int x, int y) { } } + // Try WHM/WOT sidecar from the asset tree (asset_extract --emit-terrain + // writes one alongside the ADT). This lets the runtime use the open + // format without copying anything into custom_zones/. + if (!loadedFromWot) { + std::string adtPath = getADTPath(coord); + std::string adtFsPath = assetManager->resolveFile(adtPath); + if (!adtFsPath.empty() && adtFsPath.size() >= 4) { + std::string sidecarBase = adtFsPath.substr(0, adtFsPath.size() - 4); + if (pipeline::WoweeTerrainLoader::exists(sidecarBase) && + pipeline::WoweeTerrainLoader::load(sidecarBase, *terrainPtr)) { + loadedFromWot = true; + LOG_INFO("Loaded asset-tree WHM/WOT sidecar: ", sidecarBase); + if (pipeline::WoweeCollisionBuilder::exists(sidecarBase)) { + auto woc = pipeline::WoweeCollisionBuilder::load(sidecarBase + ".woc"); + if (woc.isValid()) { + CollisionData cd; + cd.triangles.reserve(woc.triangles.size()); + for (const auto& t : woc.triangles) + cd.triangles.push_back({t.v0, t.v1, t.v2, t.flags}); + cd.boundsMin = woc.bounds.min; + cd.boundsMax = woc.bounds.max; + cd.loaded = true; + collisionTiles_[tileKey(coord.x, coord.y)] = std::move(cd); + LOG_INFO("Loaded sidecar WOC collision: ", + woc.triangles.size(), " triangles"); + } + } + } + } + } + // Fall back to ADT format if (!loadedFromWot) { std::string adtPath = getADTPath(coord);