diff --git a/src/rendering/camera_controller.cpp b/src/rendering/camera_controller.cpp index 08f7c751..0605ff51 100644 --- a/src/rendering/camera_controller.cpp +++ b/src/rendering/camera_controller.cpp @@ -27,7 +27,7 @@ std::optional selectReachableFloor(const std::optional& terrainH, if (wmoH && *wmoH <= refZ + maxStepUp) reachWmo = wmoH; // Avoid snapping up to higher WMO floors when entering buildings. - if (reachTerrain && reachWmo && *reachWmo > refZ + 2.0f) { + if (reachTerrain && reachWmo && *reachWmo > refZ + 3.5f) { return reachTerrain; } @@ -479,6 +479,11 @@ void CameraController::update(float deltaTime) { if (!walkable) { candidate.x = adjusted.x; candidate.y = adjusted.y; + // Snap Z to floor at adjusted position to prevent fall-through + auto adjFloor = wmoRenderer->getFloorHeight(adjusted.x, adjusted.y, feetZ + 2.5f); + if (adjFloor && *adjFloor >= feetZ - 1.0f && *adjFloor <= feetZ + 1.6f) { + candidate.z = *adjFloor; + } } else if (floorH && *floorH > candidate.z) { // Snap Z to ramp surface so subsequent sweep // steps measure feetZ from the ramp, not the @@ -611,8 +616,6 @@ void CameraController::update(float deltaTime) { // Skip entirely while swimming — the swim floor clamp handles vertical bounds. if (!swimming) { float stepUpBudget = grounded ? 1.6f : 1.2f; - bool firstPerson = (!thirdPerson) || (currentDistance < 0.6f); - // 1. Center-only sample for terrain/WMO floor selection. // Using only the center prevents tunnel entrances from snapping // to terrain when offset samples miss the WMO floor geometry. @@ -624,7 +627,7 @@ void CameraController::update(float deltaTime) { terrainH = terrainManager->getHeightAt(targetPos.x, targetPos.y); } float wmoProbeZ = std::max(targetPos.z, lastGroundZ) + stepUpBudget + 0.5f; - if (wmoRenderer && !firstPerson) { + if (wmoRenderer) { wmoH = wmoRenderer->getFloorHeight(targetPos.x, targetPos.y, wmoProbeZ); } groundH = selectReachableFloor(terrainH, wmoH, targetPos.z, stepUpBudget); diff --git a/src/rendering/wmo_renderer.cpp b/src/rendering/wmo_renderer.cpp index bce2dcd7..950768c2 100644 --- a/src/rendering/wmo_renderer.cpp +++ b/src/rendering/wmo_renderer.cpp @@ -1859,10 +1859,10 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to, const auto& indices = group.collisionIndices; // Use spatial grid: query range covering the movement segment + player radius - float rangeMinX = std::min(localFrom.x, localTo.x) - PLAYER_RADIUS - 1.0f; - float rangeMinY = std::min(localFrom.y, localTo.y) - PLAYER_RADIUS - 1.0f; - float rangeMaxX = std::max(localFrom.x, localTo.x) + PLAYER_RADIUS + 1.0f; - float rangeMaxY = std::max(localFrom.y, localTo.y) + PLAYER_RADIUS + 1.0f; + float rangeMinX = std::min(localFrom.x, localTo.x) - PLAYER_RADIUS - 2.5f; + float rangeMinY = std::min(localFrom.y, localTo.y) - PLAYER_RADIUS - 2.5f; + float rangeMaxX = std::max(localFrom.x, localTo.x) + PLAYER_RADIUS + 2.5f; + float rangeMaxY = std::max(localFrom.y, localTo.y) + PLAYER_RADIUS + 2.5f; group.getTrianglesInRange(rangeMinX, rangeMinY, rangeMaxX, rangeMaxY, wallTriScratch); for (uint32_t triStart : wallTriScratch) { @@ -1913,7 +1913,7 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to, glm::vec3 hitPoint = localFrom + (localTo - localFrom) * tHit; glm::vec3 hitClosest = closestPointOnTriangle(hitPoint, v0, v1, v2); float hitErrSq = glm::dot(hitClosest - hitPoint, hitClosest - hitPoint); - if (hitErrSq <= 0.04f * 0.04f) { + if (hitErrSq <= 0.5f * 0.5f) { float side = fromDist > 0.0f ? 1.0f : -1.0f; glm::vec3 safeLocal = hitPoint + normal * side * (PLAYER_RADIUS + 0.05f); glm::vec3 safeWorld = glm::vec3(instance.modelMatrix * glm::vec4(safeLocal, 1.0f));