From 8517ae3778fe23a1c23a03d97426e4d4f145ca29 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 5 May 2026 10:36:46 -0700 Subject: [PATCH] feat: client loads WOM open models from custom zones automatically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TerrainManager now checks for .wom files before loading M2 models - Searches custom_zones/models/ and output/MapName/models/ directories - Converts WOM vertices/indices to M2Model struct for the renderer - Full pipeline: editor exports M2→WOM → client loads WOM directly - Falls back to standard M2 loading if no WOM found The client can now render custom zone content using entirely open formats: WOT/WHM terrain + WOM models + PNG textures --- src/rendering/terrain_manager.cpp | 38 ++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/rendering/terrain_manager.cpp b/src/rendering/terrain_manager.cpp index 0100d4a5..460f67af 100644 --- a/src/rendering/terrain_manager.cpp +++ b/src/rendering/terrain_manager.cpp @@ -9,6 +9,7 @@ #include "audio/ambient_sound_manager.hpp" #include "core/coordinates.hpp" #include "pipeline/wowee_terrain_loader.hpp" +#include "pipeline/wowee_model.hpp" #include "core/memory_monitor.hpp" #include "core/profiler.hpp" #include "pipeline/asset_manager.hpp" @@ -449,10 +450,45 @@ 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()) { + // Convert WOM to M2Model for the renderer + pipeline::M2Model m2Model; + m2Model.name = wom.name; + m2Model.boundRadius = wom.boundRadius; + m2Model.vertices.reserve(wom.vertices.size()); + for (const auto& v : wom.vertices) { + pipeline::M2Vertex mv; + mv.position = v.position; + mv.normal = v.normal; + mv.texCoords[0] = v.texCoord; + m2Model.vertices.push_back(mv); + } + m2Model.indices.reserve(wom.indices.size()); + for (uint32_t idx : wom.indices) + m2Model.indices.push_back(static_cast(idx)); + + pending->m2Models.push_back({modelId, std::move(m2Model), {}}); + preparedModelIds.insert(modelId); + LOG_INFO("Loaded WOM model: ", womPath); + return true; + } + } + } + std::vector m2Data = assetManager->readFile(m2Path); if (m2Data.empty()) { skippedFileNotFound++; - LOG_WARNING("M2 file not found: ", m2Path); return false; }