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.
This commit is contained in:
Kelsi 2026-02-08 20:20:37 -08:00
parent d49b69a3a8
commit b48802855b
3 changed files with 13 additions and 7 deletions

View file

@ -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

View file

@ -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<int>(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<int>(std::ceil(moveDist / stepSize))));
glm::vec3 stepPos = startPos;
glm::vec3 stepDelta = (desiredPos - startPos) / static_cast<float>(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

View file

@ -1906,7 +1906,7 @@ std::optional<float> 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 dont re-collide immediately
const float MAX_PUSH = 0.08f; // cap per triangle contact (tune 0.100.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;