From ef0b1b45ef5b866469adfe8ef21983e982297b10 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 14 Feb 2026 21:15:28 -0800 Subject: [PATCH] Fix grey WMO curtains by skipping window/sky materials, fix /unstuck - Skip WMO batches with material flags F_SIDN (0x20) or F_WINDOW (0x40) which are transparent sky/window panes that render as grey curtains - Fix /unstuck: always nudge 5 units forward first, then sample floor at destination instead of teleporting back to last safe position (which could be the stuck location itself) --- src/core/application.cpp | 29 ++++++++++------------------- src/rendering/wmo_renderer.cpp | 8 +++++++- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/core/application.cpp b/src/core/application.cpp index 2c601e20..d9add317 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1103,7 +1103,7 @@ void Application::setupUICallbacks() { gameHandler->sendChatMessage(game::ChatType::SAY, cmd.str(), ""); }; - // /unstuck — prefer safe position or nearest floor, avoid blind +Z snaps. + // /unstuck — nudge player forward and snap to floor at destination. gameHandler->setUnstuckCallback([this, sampleBestFloorAt, clearStuckMovement, syncTeleportedPositionToServer, forceServerTeleportCommand]() { if (!renderer || !renderer->getCameraController()) return; worldEntryMovementGraceTimer_ = std::max(worldEntryMovementGraceTimer_, 1.5f); @@ -1115,35 +1115,26 @@ void Application::setupUICallbacks() { if (!ft) return; glm::vec3 pos = *ft; - if (cc->hasLastSafePosition()) { - pos = cc->getLastSafePosition(); - pos.z += 1.5f; - cc->teleportTo(pos); - syncTeleportedPositionToServer(pos); - forceServerTeleportCommand(pos); - clearStuckMovement(); - LOG_INFO("Unstuck: teleported to last safe position"); - return; - } - if (auto floor = sampleBestFloorAt(pos.x, pos.y, pos.z + 60.0f)) { - pos.z = *floor + 0.2f; - } else { - pos.z += 20.0f; - } - - // Nudge forward to break free of collision seams / stuck geometry. + // Always nudge forward first to escape stuck geometry (M2 models, collision seams). if (gameHandler) { float renderYaw = gameHandler->getMovementInfo().orientation + glm::radians(90.0f); pos.x += std::cos(renderYaw) * 5.0f; pos.y += std::sin(renderYaw) * 5.0f; } + // Sample floor at the DESTINATION position (after nudge). + if (auto floor = sampleBestFloorAt(pos.x, pos.y, pos.z + 60.0f)) { + pos.z = *floor + 0.2f; + } else { + pos.z += 20.0f; + } + cc->teleportTo(pos); syncTeleportedPositionToServer(pos); forceServerTeleportCommand(pos); clearStuckMovement(); - LOG_INFO("Unstuck: recovered to sampled floor"); + LOG_INFO("Unstuck: nudged forward and snapped to floor"); }); // /unstuckgy — stronger recovery: safe/home position, then sampled floor fallback. diff --git a/src/rendering/wmo_renderer.cpp b/src/rendering/wmo_renderer.cpp index d97018ba..6097fcd1 100644 --- a/src/rendering/wmo_renderer.cpp +++ b/src/rendering/wmo_renderer.cpp @@ -379,10 +379,16 @@ bool WMORenderer::loadModel(const pipeline::WMOModel& model, uint32_t id) { } bool unlit = false; + uint32_t matFlags = 0; if (batch.materialId < modelData.materialFlags.size()) { - unlit = (modelData.materialFlags[batch.materialId] & 0x01) != 0; + matFlags = modelData.materialFlags[batch.materialId]; + unlit = (matFlags & 0x01) != 0; } + // Skip materials that are sky/window panes (render as grey curtains if drawn opaque) + // 0x20 = F_SIDN (night sky window), 0x40 = F_WINDOW + if (matFlags & 0x60) continue; + // Merge key: texture ID + alphaTest + unlit (unlit batches must not merge with lit) uint64_t key = (static_cast(texId) << 2) | (alphaTest ? 1ULL : 0ULL)