From 08d40583c9e02990b2e022226aea30da48e5c9ec Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 5 Mar 2026 15:12:51 -0800 Subject: [PATCH] Fix MLIQ water parsing, skip interior water, clear movement on teleport - Remove bogus 2-byte skip after materialId in MLIQ parser that shifted all vertex heights and tile flags by 2 bytes (garbage data) - Skip liquid loading for interior WMO groups (flag 0x2000) to prevent indoor water from rendering as outdoor canal water - Clear movement inputs on teleport/portal to prevent auto-running after zone transfer (held keys persist through loading screen) --- src/core/application.cpp | 10 ++++++++++ src/pipeline/wmo_loader.cpp | 4 ---- src/rendering/terrain_manager.cpp | 9 +++++---- src/rendering/water_renderer.cpp | 3 --- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/core/application.cpp b/src/core/application.cpp index 1bdbecf5..8b1faed5 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -970,6 +970,10 @@ void Application::update(float deltaTime) { bool onTransportNow = gameHandler && gameHandler->isOnTransport(); if (worldEntryMovementGraceTimer_ > 0.0f) { worldEntryMovementGraceTimer_ -= deltaTime; + // Clear stale movement from before teleport each frame + // until grace period expires (keys may still be held) + if (renderer && renderer->getCameraController()) + renderer->getCameraController()->clearMovementInputs(); } if (renderer && renderer->getCameraController()) { const bool externallyDrivenMotion = onTaxi || onTransportNow || chargeActive_; @@ -1550,6 +1554,9 @@ void Application::setupUICallbacks() { worldEntryMovementGraceTimer_ = 2.0f; taxiLandingClampTimer_ = 0.0f; lastTaxiFlight_ = false; + // Stop any movement that was active before the teleport + if (renderer->getCameraController()) + renderer->getCameraController()->clearMovementInputs(); return; } @@ -1565,6 +1572,9 @@ void Application::setupUICallbacks() { worldEntryMovementGraceTimer_ = 2.0f; taxiLandingClampTimer_ = 0.0f; lastTaxiFlight_ = false; + // Stop any movement that was active before the teleport + if (renderer && renderer->getCameraController()) + renderer->getCameraController()->clearMovementInputs(); loadOnlineWorldTerrain(mapId, x, y, z); // loadedMapId_ is set inside loadOnlineWorldTerrain (including // any deferred entries it processes), so we must NOT override it here. diff --git a/src/pipeline/wmo_loader.cpp b/src/pipeline/wmo_loader.cpp index 450957e2..e90a79de 100644 --- a/src/pipeline/wmo_loader.cpp +++ b/src/pipeline/wmo_loader.cpp @@ -586,10 +586,6 @@ bool WMOLoader::loadGroup(const std::vector& groupData, group.liquid.basePosition.y = read(groupData, parseOffset); group.liquid.basePosition.z = read(groupData, parseOffset); group.liquid.materialId = read(groupData, parseOffset); - if (parseOffset + sizeof(uint16_t) <= subChunkEnd) { - // Reserved/flags in some WMO liquid variants. - parseOffset += sizeof(uint16_t); - } // Keep parser resilient across minor format variants: // prefer explicit per-vertex floats, otherwise fall back to flat. diff --git a/src/rendering/terrain_manager.cpp b/src/rendering/terrain_manager.cpp index 88d1dd13..ccef1dae 100644 --- a/src/rendering/terrain_manager.cpp +++ b/src/rendering/terrain_manager.cpp @@ -837,10 +837,11 @@ bool TerrainManager::advanceFinalization(FinalizingTile& ft) { modelMatrix = glm::rotate(modelMatrix, wmoReady.rotation.y, glm::vec3(0.0f, 1.0f, 0.0f)); modelMatrix = glm::rotate(modelMatrix, wmoReady.rotation.x, glm::vec3(1.0f, 0.0f, 0.0f)); for (const auto& group : wmoReady.model.groups) { - if (group.liquid.hasLiquid()) { - waterRenderer->loadFromWMO(group.liquid, modelMatrix, wmoInstId); - loadedLiquids++; - } + if (!group.liquid.hasLiquid()) continue; + // Skip interior groups — their liquid is for indoor areas + if (group.flags & 0x2000) continue; + waterRenderer->loadFromWMO(group.liquid, modelMatrix, wmoInstId); + loadedLiquids++; } } } diff --git a/src/rendering/water_renderer.cpp b/src/rendering/water_renderer.cpp index 5d917f5f..f0dfebd0 100644 --- a/src/rendering/water_renderer.cpp +++ b/src/rendering/water_renderer.cpp @@ -875,8 +875,6 @@ void WaterRenderer::loadFromWMO([[maybe_unused]] const pipeline::WMOLiquid& liqu const int vertexCount = gridWidth * gridHeight; // WMO liquid base heights sit ~2 units above the visual waterline. - // Lower them to match surrounding terrain water and prevent clipping - // at bridge edges and walkways. constexpr float WMO_WATER_Z_OFFSET = -1.0f; float adjustedZ = surface.origin.z + WMO_WATER_Z_OFFSET; surface.heights.assign(vertexCount, adjustedZ); @@ -895,7 +893,6 @@ void WaterRenderer::loadFromWMO([[maybe_unused]] const pipeline::WMOLiquid& liqu for (size_t t = 0; t < tileCount; t++) { bool hasLiquid = true; if (t < liquid.flags.size()) { - // In WoW MLIQ format, (flags & 0x0F) == 0x0F means "no liquid" for this tile if ((liquid.flags[t] & 0x0F) == 0x0F) { hasLiquid = false; }