rendering: re-enable WMO camera collision with asymmetric smoothing

Previously disabled because the per-frame raycast caused erratic zoom
snapping at doorway transitions.  Re-enable using an asymmetrically-
smoothed collision limit: pull-in reacts quickly (τ≈60 ms) to prevent
the camera from ever visibly clipping through walls, while recovery is
slow (τ≈400 ms) so walking through a doorway zooms back out gradually
instead of snapping.

Uses wmoRenderer->raycastBoundingBoxes() which already has strict wall
filters (|normal.z|<0.20, surface-alignment check, ±0.9 height band)
to ignore floors, ramps, and arch geometry.
This commit is contained in:
Kelsi 2026-03-10 09:13:31 -07:00
parent c622e547c9
commit b2dccca58c
2 changed files with 29 additions and 4 deletions

View file

@ -1316,12 +1316,36 @@ void CameraController::update(float deltaTime) {
}
}
// ===== Camera collision (sphere sweep approximation) =====
// Find max safe distance using raycast + sphere radius
// ===== Camera collision (WMO raycast) =====
// Cast a ray from the pivot toward the camera direction to find the
// nearest WMO wall. Uses asymmetric smoothing: pull-in is fast (so
// the camera never visibly clips through a wall) but recovery is slow
// (so passing through a doorway doesn't cause a zoom-out snap).
collisionDistance = currentDistance;
// WMO/M2 camera collision disabled — was pulling camera through
// geometry at doorway transitions and causing erratic zoom behaviour.
if (wmoRenderer && currentDistance > MIN_DISTANCE) {
float rawHitDist = wmoRenderer->raycastBoundingBoxes(pivot, camDir, currentDistance);
// rawHitDist == currentDistance means no hit (function returns maxDistance on miss)
float rawLimit = (rawHitDist < currentDistance)
? std::max(MIN_DISTANCE, rawHitDist - CAM_SPHERE_RADIUS - CAM_EPSILON)
: currentDistance;
// Initialise smoothed state on first use.
if (smoothedCollisionDist_ < 0.0f) {
smoothedCollisionDist_ = rawLimit;
}
// Asymmetric smoothing:
// • Pull-in: τ ≈ 60 ms — react quickly to prevent clipping
// • Recover: τ ≈ 400 ms — zoom out slowly after leaving geometry
const float tau = (rawLimit < smoothedCollisionDist_) ? 0.06f : 0.40f;
float alpha = 1.0f - std::exp(-deltaTime / tau);
smoothedCollisionDist_ += (rawLimit - smoothedCollisionDist_) * alpha;
collisionDistance = std::min(collisionDistance, smoothedCollisionDist_);
} else {
smoothedCollisionDist_ = -1.0f; // Reset when wmoRenderer unavailable
}
// Camera collision: terrain-only floor clamping
auto getTerrainFloorAt = [&](float x, float y) -> std::optional<float> {