From b48802855ba2220df82bd45bbfad0d656ebb87b6 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 8 Feb 2026 20:20:37 -0800 Subject: [PATCH] Tighten WMO collision detection when inside buildings When inside a WMO, use: - Smaller sweep step size (0.20 vs 0.35) for more frequent collision checks - Tighter player radius (0.45 vs 0.50) for less claustrophobic corridors - Stronger push response (0.12 vs 0.08 max) for more responsive walls Prevents clipping through walls in tight indoor spaces while keeping outdoor movement smooth. --- include/rendering/wmo_renderer.hpp | 3 ++- src/rendering/camera_controller.cpp | 7 +++++-- src/rendering/wmo_renderer.cpp | 10 ++++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/rendering/wmo_renderer.hpp b/include/rendering/wmo_renderer.hpp index 3e5d7e88..70871a78 100644 --- a/include/rendering/wmo_renderer.hpp +++ b/include/rendering/wmo_renderer.hpp @@ -201,9 +201,10 @@ public: * @param from Starting position * @param to Desired position * @param adjustedPos Output adjusted position (pushed away from walls) + * @param insideWMO If true, use tighter collision for indoor precision * @return true if collision occurred */ - bool checkWallCollision(const glm::vec3& from, const glm::vec3& to, glm::vec3& adjustedPos) const; + bool checkWallCollision(const glm::vec3& from, const glm::vec3& to, glm::vec3& adjustedPos, bool insideWMO = false) const; /** * Check if a position is inside any WMO diff --git a/src/rendering/camera_controller.cpp b/src/rendering/camera_controller.cpp index b96c5650..95bd8832 100644 --- a/src/rendering/camera_controller.cpp +++ b/src/rendering/camera_controller.cpp @@ -444,13 +444,16 @@ void CameraController::update(float deltaTime) { // Sweep collisions in small steps to reduce tunneling through thin walls/floors. // Skip entirely when stationary to avoid wasting collision calls. + // Use tighter steps when inside WMO for more precise collision. { glm::vec3 startPos = *followTarget; glm::vec3 desiredPos = targetPos; float moveDist = glm::length(desiredPos - startPos); if (moveDist > 0.01f) { - int sweepSteps = std::max(1, std::min(5, static_cast(std::ceil(moveDist / 0.35f)))); + // Smaller step size when inside buildings for tighter collision + float stepSize = cachedInsideWMO ? 0.20f : 0.35f; + int sweepSteps = std::max(1, std::min(8, static_cast(std::ceil(moveDist / stepSize)))); glm::vec3 stepPos = startPos; glm::vec3 stepDelta = (desiredPos - startPos) / static_cast(sweepSteps); @@ -459,7 +462,7 @@ void CameraController::update(float deltaTime) { if (wmoRenderer) { glm::vec3 adjusted; - if (wmoRenderer->checkWallCollision(stepPos, candidate, adjusted)) { + if (wmoRenderer->checkWallCollision(stepPos, candidate, adjusted, cachedInsideWMO)) { candidate.x = adjusted.x; candidate.y = adjusted.y; // Accept upward Z correction (ramps), reject downward diff --git a/src/rendering/wmo_renderer.cpp b/src/rendering/wmo_renderer.cpp index ad437d81..aabd084b 100644 --- a/src/rendering/wmo_renderer.cpp +++ b/src/rendering/wmo_renderer.cpp @@ -1906,7 +1906,7 @@ std::optional WMORenderer::getFloorHeight(float glX, float glY, float glZ return bestFloor; } -bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to, glm::vec3& adjustedPos) const { +bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to, glm::vec3& adjustedPos, bool insideWMO) const { QueryTimer timer(&queryTimeMs, &queryCallCount); adjustedPos = to; bool blocked = false; @@ -1916,7 +1916,8 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to, if (moveDist < 0.001f) return false; // Player collision parameters — WoW-style horizontal cylinder - const float PLAYER_RADIUS = 0.50f; // Horizontal cylinder radius + // Tighter radius when inside for more responsive indoor collision + const float PLAYER_RADIUS = insideWMO ? 0.45f : 0.50f; const float PLAYER_HEIGHT = 2.0f; // Cylinder height for Z bounds const float MAX_STEP_HEIGHT = 1.0f; // Step-up threshold @@ -2067,8 +2068,9 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to, float absNz = std::abs(normal.z); if (absNz >= 0.45f) continue; - const float SKIN = 0.005f; // small separation so we don’t re-collide immediately - const float MAX_PUSH = 0.08f; // cap per triangle contact (tune 0.10–0.25) + const float SKIN = 0.005f; // small separation so we don't re-collide immediately + // Stronger push when inside WMO for more responsive indoor collision + const float MAX_PUSH = insideWMO ? 0.12f : 0.08f; float penetration = (PLAYER_RADIUS - horizDist); float pushDist = glm::clamp(penetration + SKIN, 0.0f, MAX_PUSH); glm::vec2 pushDir2;