mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix transport/WMO diagnostics and terrain WMO dedup lifecycle
This commit is contained in:
parent
514b914068
commit
ff8ffc3bfb
5 changed files with 59 additions and 26 deletions
|
|
@ -60,6 +60,7 @@ struct TerrainTile {
|
|||
|
||||
// Instance IDs for cleanup on unload
|
||||
std::vector<uint32_t> wmoInstanceIds;
|
||||
std::vector<uint32_t> wmoUniqueIds; // For WMO dedup cleanup on unload
|
||||
std::vector<uint32_t> m2InstanceIds;
|
||||
std::vector<uint32_t> doodadUniqueIds; // For dedup cleanup on unload
|
||||
};
|
||||
|
|
@ -93,6 +94,7 @@ struct PendingTile {
|
|||
// Pre-loaded WMO data
|
||||
struct WMOReady {
|
||||
uint32_t modelId;
|
||||
uint32_t uniqueId;
|
||||
pipeline::WMOModel model;
|
||||
glm::vec3 position;
|
||||
glm::vec3 rotation;
|
||||
|
|
|
|||
|
|
@ -1659,14 +1659,14 @@ void Application::setupUICallbacks() {
|
|||
transportManager->registerTransport(guid, wmoInstanceId, pathId, canonicalSpawnPos, entry);
|
||||
} else {
|
||||
pendingTransportMoves_[guid] = PendingTransportMove{x, y, z, orientation};
|
||||
LOG_WARNING("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
||||
" - WMO instance not found (queued move for replay)");
|
||||
LOG_DEBUG("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
||||
" - WMO instance not found yet (queued move for replay)");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
pendingTransportMoves_[guid] = PendingTransportMove{x, y, z, orientation};
|
||||
LOG_WARNING("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
||||
" - entity not found in EntityManager (queued move for replay)");
|
||||
LOG_DEBUG("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
||||
" - entity not found in EntityManager (queued move for replay)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -5137,24 +5137,35 @@ void Application::setupTestTransport() {
|
|||
transportManager->loadPathFromNodes(pathId, harborPath, true, speed);
|
||||
LOG_INFO("Registered transport path ", pathId, " with ", harborPath.size(), " waypoints, speed=", speed);
|
||||
|
||||
// Try to load a transport WMO model
|
||||
// Common transport WMOs: Transportship.wmo (generic ship)
|
||||
std::string transportWmoPath = "Transports\\Transportship\\Transportship.wmo";
|
||||
// Try transport WMOs in manifest-backed paths first.
|
||||
std::vector<std::string> transportCandidates = {
|
||||
"World\\wmo\\transports\\transport_ship\\transportship.wmo",
|
||||
"World\\wmo\\transports\\transport_zeppelin\\transport_zeppelin.wmo",
|
||||
"World\\wmo\\transports\\transport_horde_zeppelin\\Transport_Horde_Zeppelin.wmo",
|
||||
"World\\wmo\\transports\\icebreaker\\Transport_Icebreaker_ship.wmo",
|
||||
// Legacy fallbacks
|
||||
"Transports\\Transportship\\Transportship.wmo",
|
||||
"Transports\\Boat\\Boat.wmo",
|
||||
};
|
||||
|
||||
auto wmoData = assetManager->readFile(transportWmoPath);
|
||||
if (wmoData.empty()) {
|
||||
LOG_WARNING("Could not load transport WMO: ", transportWmoPath);
|
||||
LOG_INFO("Trying alternative: Boat transport");
|
||||
transportWmoPath = "Transports\\Boat\\Boat.wmo";
|
||||
wmoData = assetManager->readFile(transportWmoPath);
|
||||
std::string transportWmoPath;
|
||||
std::vector<uint8_t> wmoData;
|
||||
for (const auto& candidate : transportCandidates) {
|
||||
wmoData = assetManager->readFile(candidate);
|
||||
if (!wmoData.empty()) {
|
||||
transportWmoPath = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (wmoData.empty()) {
|
||||
LOG_WARNING("No transport WMO found - test transport disabled");
|
||||
LOG_INFO("Available transport WMOs are typically in Transports\\ directory");
|
||||
LOG_INFO("Expected under World\\wmo\\transports\\...");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Using transport WMO: ", transportWmoPath);
|
||||
|
||||
// Load WMO model
|
||||
pipeline::WMOModel wmoModel = pipeline::WMOLoader::load(wmoData);
|
||||
LOG_INFO("Transport WMO root loaded: ", transportWmoPath, " nGroups=", wmoModel.nGroups);
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@ void EntityManager::addEntity(uint64_t guid, std::shared_ptr<Entity> entity) {
|
|||
return;
|
||||
}
|
||||
|
||||
const int type = static_cast<int>(entity->getType());
|
||||
entities[guid] = std::move(entity);
|
||||
|
||||
LOG_DEBUG("Added entity: GUID=0x", std::hex, guid, std::dec,
|
||||
", Type=", static_cast<int>(entity->getType()));
|
||||
", Type=", type);
|
||||
}
|
||||
|
||||
void EntityManager::removeEntity(uint64_t guid) {
|
||||
|
|
|
|||
|
|
@ -3307,7 +3307,6 @@ void GameHandler::setOrientation(float orientation) {
|
|||
}
|
||||
|
||||
void GameHandler::handleUpdateObject(network::Packet& packet) {
|
||||
|
||||
UpdateObjectData data;
|
||||
if (!packetParsers_->parseUpdateObject(packet, data)) {
|
||||
LOG_WARNING("Failed to parse SMSG_UPDATE_OBJECT");
|
||||
|
|
@ -10588,7 +10587,10 @@ void GameHandler::saveCharacterConfig() {
|
|||
|
||||
out << "character_guid=" << playerGuid << "\n";
|
||||
out << "gender=" << static_cast<int>(ch->gender) << "\n";
|
||||
out << "use_female_model=" << (ch->useFemaleModel ? 1 : 0) << "\n";
|
||||
// For male/female, derive from gender; only nonbinary has a meaningful separate choice
|
||||
bool saveUseFemaleModel = (ch->gender == Gender::NONBINARY) ? ch->useFemaleModel
|
||||
: (ch->gender == Gender::FEMALE);
|
||||
out << "use_female_model=" << (saveUseFemaleModel ? 1 : 0) << "\n";
|
||||
for (int i = 0; i < ACTION_BAR_SLOTS; i++) {
|
||||
out << "action_bar_" << i << "_type=" << static_cast<int>(actionBar[i].type) << "\n";
|
||||
out << "action_bar_" << i << "_id=" << actionBar[i].id << "\n";
|
||||
|
|
@ -10666,8 +10668,14 @@ void GameHandler::loadCharacterConfig() {
|
|||
for (auto& character : characters) {
|
||||
if (character.guid == playerGuid) {
|
||||
character.gender = static_cast<Gender>(savedGender);
|
||||
if (savedUseFemaleModel >= 0) {
|
||||
character.useFemaleModel = (savedUseFemaleModel != 0);
|
||||
if (character.gender == Gender::NONBINARY) {
|
||||
// Only nonbinary characters have a meaningful body type choice
|
||||
if (savedUseFemaleModel >= 0) {
|
||||
character.useFemaleModel = (savedUseFemaleModel != 0);
|
||||
}
|
||||
} else {
|
||||
// Male/female always use the model matching their gender
|
||||
character.useFemaleModel = (character.gender == Gender::FEMALE);
|
||||
}
|
||||
LOG_INFO("Applied saved gender: ", getGenderName(character.gender),
|
||||
", body type: ", (character.useFemaleModel ? "feminine" : "masculine"));
|
||||
|
|
|
|||
|
|
@ -521,7 +521,10 @@ std::shared_ptr<PendingTile> TerrainManager::prepareTile(int x, int y) {
|
|||
}
|
||||
|
||||
PendingTile::WMOReady ready;
|
||||
ready.modelId = placement.uniqueId;
|
||||
// Cache WMO model uploads by path; placement dedup uses uniqueId separately.
|
||||
ready.modelId = static_cast<uint32_t>(std::hash<std::string>{}(wmoPath));
|
||||
if (ready.modelId == 0) ready.modelId = 1;
|
||||
ready.uniqueId = placement.uniqueId;
|
||||
ready.model = std::move(wmoModel);
|
||||
ready.position = pos;
|
||||
ready.rotation = rot;
|
||||
|
|
@ -647,10 +650,11 @@ void TerrainManager::finalizeTile(const std::shared_ptr<PendingTile>& pending) {
|
|||
std::vector<uint32_t> m2InstanceIds;
|
||||
std::vector<uint32_t> wmoInstanceIds;
|
||||
std::vector<uint32_t> tileUniqueIds;
|
||||
std::vector<uint32_t> tileWmoUniqueIds;
|
||||
|
||||
// Upload M2 models to GPU and create instances
|
||||
if (m2Renderer && assetManager) {
|
||||
if (!m2Renderer->getModelCount()) {
|
||||
if (!m2Renderer->isInitialized()) {
|
||||
m2Renderer->initialize(assetManager);
|
||||
}
|
||||
|
||||
|
|
@ -691,7 +695,7 @@ void TerrainManager::finalizeTile(const std::shared_ptr<PendingTile>& pending) {
|
|||
|
||||
// Upload WMO models to GPU and create instances
|
||||
if (wmoRenderer && assetManager) {
|
||||
if (!wmoRenderer->getModelCount()) {
|
||||
if (!wmoRenderer->isInitialized()) {
|
||||
wmoRenderer->initialize(assetManager);
|
||||
}
|
||||
|
||||
|
|
@ -699,9 +703,9 @@ void TerrainManager::finalizeTile(const std::shared_ptr<PendingTile>& pending) {
|
|||
int loadedLiquids = 0;
|
||||
int skippedWmoDedup = 0;
|
||||
for (auto& wmoReady : pending->wmoModels) {
|
||||
// Deduplicate WMO instances by uniqueId (prevents Stormwind from rendering 16x)
|
||||
// uniqueId is stored in modelId field (see line 522 in prepareTile)
|
||||
if (placedWmoIds.count(wmoReady.modelId)) {
|
||||
// Deduplicate by placement uniqueId when available.
|
||||
// Some ADTs use uniqueId=0, which is not safe for dedup.
|
||||
if (wmoReady.uniqueId != 0 && placedWmoIds.count(wmoReady.uniqueId)) {
|
||||
skippedWmoDedup++;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -710,7 +714,10 @@ void TerrainManager::finalizeTile(const std::shared_ptr<PendingTile>& pending) {
|
|||
uint32_t wmoInstId = wmoRenderer->createInstance(wmoReady.modelId, wmoReady.position, wmoReady.rotation);
|
||||
if (wmoInstId) {
|
||||
wmoInstanceIds.push_back(wmoInstId);
|
||||
placedWmoIds.insert(wmoReady.modelId);
|
||||
if (wmoReady.uniqueId != 0) {
|
||||
placedWmoIds.insert(wmoReady.uniqueId);
|
||||
tileWmoUniqueIds.push_back(wmoReady.uniqueId);
|
||||
}
|
||||
loadedWMOs++;
|
||||
|
||||
// Load WMO liquids (canals, pools, etc.)
|
||||
|
|
@ -773,6 +780,7 @@ void TerrainManager::finalizeTile(const std::shared_ptr<PendingTile>& pending) {
|
|||
tile->loaded = true;
|
||||
tile->m2InstanceIds = std::move(m2InstanceIds);
|
||||
tile->wmoInstanceIds = std::move(wmoInstanceIds);
|
||||
tile->wmoUniqueIds = std::move(tileWmoUniqueIds);
|
||||
tile->doodadUniqueIds = std::move(tileUniqueIds);
|
||||
|
||||
// Calculate world bounds
|
||||
|
|
@ -1018,6 +1026,9 @@ void TerrainManager::unloadTile(int x, int y) {
|
|||
for (uint32_t uid : tile->doodadUniqueIds) {
|
||||
placedDoodadIds.erase(uid);
|
||||
}
|
||||
for (uint32_t uid : tile->wmoUniqueIds) {
|
||||
placedWmoIds.erase(uid);
|
||||
}
|
||||
|
||||
// Remove M2 doodad instances
|
||||
if (m2Renderer) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue