[core] Limit pitch based on edge insets. Fix max Z calculation in getProjMatrix.

Patch partly fixes #15163 in a way that it doesn't allow loading tens of thousands of tiles and attempt to show area above horizon:

Limit pitch based on edge insets. It is not too bad - current limit of 60 degrees stays active until center of perspective is moved towards the bottom, to 84% of screen height. The plan is to split removal of 60 degrees limit to follow up patch.

Fix max Z calculation in getProjMatrix. TransformState::getProjMatrix calculation of farZ was complex with possibility to lead to negative z values. Replacing it with simpler, precise calculation:
furthestDistance = cameraToCenterDistance / (1 - tanFovAboveCenter * std::tan(getPitch()));

TransformState::getProjMatrix calculation of farZ was an aproximation. Replacing it with simpler, but precise calculation.

Related to: #15163
This commit is contained in:
Aleksandar Stojiljkovic
2019-07-23 17:19:52 +03:00
parent 375c3131af
commit 73d9f26d03
7 changed files with 78 additions and 18 deletions

View File

@@ -138,9 +138,6 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
if (bearing != startBearing) {
state.bearing = util::wrap(util::interpolate(startBearing, bearing, t), -M_PI, M_PI);
}
if (pitch != startPitch) {
state.pitch = util::interpolate(startPitch, pitch, t);
}
if (padding != startEdgeInsets) {
// Interpolate edge insets
state.edgeInsets = {
@@ -150,6 +147,10 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
util::interpolate(startEdgeInsets.right(), padding.right(), t)
};
}
auto maxPitch = getMaxPitchForEdgeInsets(state.edgeInsets);
if (pitch != startPitch || maxPitch < startPitch) {
state.pitch = std::min(maxPitch, util::interpolate(startPitch, pitch, t));
}
}, duration);
}
@@ -302,9 +303,6 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
if (bearing != startBearing) {
state.bearing = util::wrap(util::interpolate(startBearing, bearing, k), -M_PI, M_PI);
}
if (pitch != startPitch) {
state.pitch = util::interpolate(startPitch, pitch, k);
}
if (padding != startEdgeInsets) {
// Interpolate edge insets
state.edgeInsets = {
@@ -314,6 +312,10 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
util::interpolate(startEdgeInsets.right(), padding.right(), us)
};
}
auto maxPitch = getMaxPitchForEdgeInsets(state.edgeInsets);
if (pitch != startPitch || maxPitch < startPitch) {
state.pitch = std::min(maxPitch, util::interpolate(startPitch, pitch, k));
}
}, duration);
}
@@ -576,4 +578,21 @@ LatLng Transform::screenCoordinateToLatLng(const ScreenCoordinate& point, LatLng
return state.screenCoordinateToLatLng(flippedPoint, wrapMode);
}
double Transform::getMaxPitchForEdgeInsets(const EdgeInsets &insets) const
{
double centerOffsetY = 0.5 * (insets.top() - insets.bottom()); // See TransformState::getCenterOffset.
const auto height = state.size.height;
assert(height);
// For details, see description at https://github.com/mapbox/mapbox-gl-native/pull/15195
// The definition of half of TransformState::fov with no inset, is: fov = arctan((height / 2) / (height * 1.5)).
// We use half of fov, as it is field of view above perspective center.
// With inset, this angle changes and tangentOfFovAboveCenterAngle = (h/2 + centerOffsetY) / (height * 1.5).
// 1.03 is a bit extra added to prevent parallel ground to viewport clipping plane.
const double tangentOfFovAboveCenterAngle = 1.03 * (height / 2.0 + centerOffsetY) / (1.5 * height);
const double fovAboveCenter = std::atan(tangentOfFovAboveCenterAngle);
return M_PI * 0.5 - fovAboveCenter;
// e.g. Maximum pitch of 60 degrees is when perspective center's offset from the top is 84% of screen height.
}
} // namespace mbgl