Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Camera Roll #3011

Draft
wants to merge 33 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0afd99b
pitch working up to but not including 90 deg
NathanMOlson Feb 12, 2024
aa02572
use max furthestDistance if pitched above the horizon
NathanMOlson Feb 12, 2024
0cb061d
variable FOV seems to work
NathanMOlson Feb 12, 2024
ac8fbc2
set camera position, not map center postion. Pitches above 90 work, b…
NathanMOlson Feb 12, 2024
c5f18db
fixes for pitch = 90
NathanMOlson Feb 14, 2024
b50ee05
fix trig issue affecting near nadir
NathanMOlson Feb 14, 2024
414b0e8
add settable camera altitude
NathanMOlson Feb 14, 2024
d17706a
twist working, but there's wreckage in camera.cpp
NathanMOlson Feb 14, 2024
92a4b4d
toward full twist support
NathanMOlson Feb 14, 2024
14c4404
Merge branch 'main' into roll (probably doesn't compile)
NathanMOlson Nov 12, 2024
f737b2f
compiles
NathanMOlson Nov 12, 2024
bf5745a
rename twist -> roll
NathanMOlson Nov 12, 2024
a3b7478
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 12, 2024
eb06001
fix command line abbreviations
NathanMOlson Nov 12, 2024
d32dc72
clarify altitude
NathanMOlson Nov 12, 2024
ef274ec
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 12, 2024
1c4a24e
fix compile
NathanMOlson Nov 12, 2024
b98ea0a
fix altitude interpretation
NathanMOlson Nov 12, 2024
1c195c8
Add comments
NathanMOlson Nov 12, 2024
f641468
better naming
NathanMOlson Nov 12, 2024
e39788d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 12, 2024
fd9fa00
fov handling improvements
NathanMOlson Nov 12, 2024
9421f10
Merge branch 'roll' of github.com:NathanMOlson/maplibre-native into roll
NathanMOlson Nov 12, 2024
92b6017
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 12, 2024
c001e50
formatting
NathanMOlson Nov 12, 2024
ee93d55
cleanup
NathanMOlson Nov 12, 2024
9b5370f
Merge branch 'main' into roll
NathanMOlson Nov 12, 2024
fb7ffe7
cleanup
NathanMOlson Nov 13, 2024
c150fd6
restore bounds limits, and interpolate center altitude in easeTo() an…
NathanMOlson Nov 13, 2024
8d06c07
interpolate fov in easeTo and flyTo, and limit FOV
NathanMOlson Nov 13, 2024
8b8b63c
interpolate roll
NathanMOlson Nov 13, 2024
0e2f441
Merge branch 'main' into roll
louwers Nov 13, 2024
4b6c340
Merge branch 'main' into roll
NathanMOlson Nov 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion bin/render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ int main(int argc, char* argv[]) {

args::ValueFlag<double> lonValue(argumentParser, "degrees", "Longitude", {'x', "lon"});
args::ValueFlag<double> latValue(argumentParser, "degrees", "Latitude", {'y', "lat"});
args::ValueFlag<double> altValue(argumentParser, "degrees", "Altitude", {'A', "alt"});
args::ValueFlag<double> fovValue(argumentParser, "degrees", "FOV", {'f', "fov"});
args::ValueFlag<double> bearingValue(argumentParser, "degrees", "Bearing", {'b', "bearing"});
args::ValueFlag<double> pitchValue(argumentParser, "degrees", "Pitch", {'p', "pitch"});
args::ValueFlag<double> rollValue(argumentParser, "degrees", "Roll", {'R', "roll"});
args::ValueFlag<uint32_t> widthValue(argumentParser, "pixels", "Image width", {'w', "width"});
args::ValueFlag<uint32_t> heightValue(argumentParser, "pixels", "Image height", {'h', "height"});

Expand All @@ -55,9 +58,12 @@ int main(int argc, char* argv[]) {

const double lat = latValue ? args::get(latValue) : 0;
const double lon = lonValue ? args::get(lonValue) : 0;
const double alt = altValue ? args::get(altValue) : 0;
const double zoom = zoomValue ? args::get(zoomValue) : 0;
const double fov = fovValue ? args::get(fovValue) : 37;
const double bearing = bearingValue ? args::get(bearingValue) : 0;
const double pitch = pitchValue ? args::get(pitchValue) : 0;
const double roll = rollValue ? args::get(rollValue) : 0;
const double pixelRatio = pixelRatioValue ? args::get(pixelRatioValue) : 1;

const uint32_t width = widthValue ? args::get(widthValue) : 512;
Expand Down Expand Up @@ -97,7 +103,14 @@ int main(int argc, char* argv[]) {
}

map.getStyle().loadURL(style);
map.jumpTo(CameraOptions().withCenter(LatLng{lat, lon}).withZoom(zoom).withBearing(bearing).withPitch(pitch));
map.jumpTo(CameraOptions()
.withCenter(LatLng{lat, lon})
.withCenterAltitude(alt)
.withZoom(zoom)
.withBearing(bearing)
.withPitch(pitch)
.withRoll(roll)
.withFov(fov));

if (debug) {
map.setDebug(debug ? mbgl::MapDebugOptions::TileBorders | mbgl::MapDebugOptions::ParseStatus
Expand Down
31 changes: 26 additions & 5 deletions include/mbgl/map/camera.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ struct CameraOptions {
center = o;
return *this;
}
CameraOptions& withCenterAltitude(const std::optional<double>& o) {
centerAltitude = o;
return *this;
}
CameraOptions& withPadding(const std::optional<EdgeInsets>& p) {
padding = p;
return *this;
Expand All @@ -41,10 +45,21 @@ struct CameraOptions {
pitch = o;
return *this;
}
CameraOptions& withRoll(const std::optional<double>& o) {
roll = o;
return *this;
}
CameraOptions& withFov(const std::optional<double>& o) {
fov = o;
return *this;
}

/** Coordinate at the center of the map. */
std::optional<LatLng> center;

/** Altitude of the center of the map, in meters above sea level. */
std::optional<double> centerAltitude;

/** Padding around the interior of the view that affects the frame of
reference for `center`. */
std::optional<EdgeInsets> padding;
Expand All @@ -63,11 +78,18 @@ struct CameraOptions {
/** Pitch toward the horizon measured in degrees , with 0 deg resulting in a
two-dimensional map. */
std::optional<double> pitch;

/** Camera roll, measured in degrees. */
std::optional<double> roll;

/** Camera vertical field of view, measured in degrees. */
std::optional<double> fov;
};

constexpr bool operator==(const CameraOptions& a, const CameraOptions& b) {
return a.center == b.center && a.padding == b.padding && a.anchor == b.anchor && a.zoom == b.zoom &&
a.bearing == b.bearing && a.pitch == b.pitch;
a.bearing == b.bearing && a.pitch == b.pitch && a.roll == b.roll && a.fov == b.fov &&
a.centerAltitude == b.centerAltitude;
}

constexpr bool operator!=(const CameraOptions& a, const CameraOptions& b) {
Expand Down Expand Up @@ -134,7 +156,6 @@ struct FreeCameraOptions {
0, 0]

Orientation can be set freely but certain constraints still apply
- Orientation must be representable with only pitch and bearing.
- Pitch has an upper limit */
std::optional<vec4> orientation = std::nullopt;

Expand All @@ -151,9 +172,9 @@ struct FreeCameraOptions {
bearing can't be deduced from the viewing direction */
void lookAtPoint(const LatLng& location, const std::optional<vec3>& upVector = std::nullopt) noexcept;

/** Helper function for setting the orientation of the camera as a pitch and
a bearing. Both values are in degrees */
void setPitchBearing(double pitch, double bearing) noexcept;
/** Helper function for setting the orientation of the camera as a roll, pitch, and
a bearing. All values are in degrees */
void setRollPitchBearing(double roll, double pitch, double bearing) noexcept;
};

} // namespace mbgl
2 changes: 1 addition & 1 deletion include/mbgl/util/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ constexpr double LATITUDE_MAX = 85.051128779806604;
constexpr double LONGITUDE_MAX = 180;
constexpr double DEGREES_MAX = 360;
constexpr double PITCH_MIN = 0.0;
constexpr double PITCH_MAX = M_PI / 3;
constexpr double PITCH_MAX = M_PI;
constexpr double MIN_ZOOM = 0.0;
constexpr double MAX_ZOOM = 25.5;
constexpr float MIN_ZOOM_F = MIN_ZOOM;
Expand Down
58 changes: 48 additions & 10 deletions src/mbgl/map/transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ void Transform::resize(const Size size) {
double scale{state.getScale()};
double x{state.getX()};
double y{state.getY()};
double z{state.getZ()};
state.constrain(scale, x, y);
state.setProperties(TransformStateProperties().withScale(scale).withX(x).withY(y));
state.setProperties(TransformStateProperties().withScale(scale).withX(x).withY(y).withZ(z));

observer.onCameraDidChange(MapObserver::CameraChangeMode::Immediate);
}
Expand Down Expand Up @@ -99,6 +100,9 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
double zoom = camera.zoom.value_or(getZoom());
double bearing = camera.bearing ? util::deg2rad(-*camera.bearing) : getBearing();
double pitch = camera.pitch ? util::deg2rad(*camera.pitch) : getPitch();
double fov = camera.fov ? util::deg2rad(*camera.fov) : getFieldOfView();
double centerAlt = camera.centerAltitude.value_or(state.getCenterAltitude());
double roll = camera.roll ? util::deg2rad(*camera.roll) : getRoll();

if (std::isnan(zoom) || std::isnan(bearing) || std::isnan(pitch)) {
if (animation.transitionFinishFn) {
Expand All @@ -120,13 +124,15 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
startLatLng.unwrapForShortestPath(latLng);
}
}
const double startCenterAlt = state.getCenterAltitude();

const Point<double> startPoint = Projection::project(startLatLng, state.getScale());
const Point<double> endPoint = Projection::project(latLng, state.getScale());

// Constrain camera options.
zoom = util::clamp(zoom, state.getMinZoom(), state.getMaxZoom());
pitch = util::clamp(pitch, state.getMinPitch(), state.getMaxPitch());
fov = util::clamp(fov, state.getMinFieldOfView(), state.getMaxFieldOfView());

// Minimize rotation by taking the shorter path around the circle.
bearing = _normalizeAngle(bearing, state.getBearing());
Expand All @@ -135,6 +141,8 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
const double startZoom = state.getZoom();
const double startBearing = state.getBearing();
const double startPitch = state.getPitch();
const double startRoll = state.getRoll();
const double startFov = state.getFieldOfView();
state.setProperties(TransformStateProperties()
.withPanningInProgress(unwrappedLatLng != startLatLng)
.withScalingInProgress(zoom != startZoom)
Expand All @@ -149,6 +157,7 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
LatLng frameLatLng = Projection::unproject(framePoint, state.zoomScale(startZoom));
double frameZoom = util::interpolate(startZoom, zoom, t);
state.setLatLngZoom(frameLatLng, frameZoom);
state.setCenterAltitude(util::interpolate(startCenterAlt, centerAlt, t));
if (bearing != startBearing) {
state.setBearing(util::wrap(util::interpolate(startBearing, bearing, t), -pi, pi));
}
Expand All @@ -160,9 +169,14 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
util::interpolate(startEdgeInsets.bottom(), padding.bottom(), t),
util::interpolate(startEdgeInsets.right(), padding.right(), t)});
}
double maxPitch = getMaxPitchForEdgeInsets(state.getEdgeInsets());
if (pitch != startPitch || maxPitch < startPitch) {
state.setPitch(std::min(maxPitch, util::interpolate(startPitch, pitch, t)));
if (pitch != startPitch) {
state.setPitch(util::interpolate(startPitch, pitch, t));
}
if (roll != startRoll) {
state.setPitch(util::interpolate(startRoll, roll, t));
}
if (fov != startFov) {
state.setFieldOfView(util::interpolate(startFov, fov, t));
}
},
duration);
Expand All @@ -179,9 +193,12 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
void Transform::flyTo(const CameraOptions& camera, const AnimationOptions& animation, bool linearZoomInterpolation) {
const EdgeInsets& padding = camera.padding.value_or(state.getEdgeInsets());
const LatLng& latLng = camera.center.value_or(getLatLng(LatLng::Unwrapped)).wrapped();
const double centerAlt = camera.centerAltitude.value_or(state.getCenterAltitude());
double zoom = camera.zoom.value_or(getZoom());
double bearing = camera.bearing ? util::deg2rad(-*camera.bearing) : getBearing();
double pitch = camera.pitch ? util::deg2rad(*camera.pitch) : getPitch();
double roll = camera.roll ? util::deg2rad(*camera.roll) : getRoll();
double fov = camera.fov ? util::deg2rad(*camera.fov) : getFieldOfView();

if (std::isnan(zoom) || std::isnan(bearing) || std::isnan(pitch) || state.getSize().isEmpty()) {
if (animation.transitionFinishFn) {
Expand All @@ -193,20 +210,24 @@ void Transform::flyTo(const CameraOptions& camera, const AnimationOptions& anima
// Determine endpoints.
LatLng startLatLng = getLatLng(LatLng::Unwrapped).wrapped();
startLatLng.unwrapForShortestPath(latLng);
const double startCenterAlt = state.getCenterAltitude();

const Point<double> startPoint = Projection::project(startLatLng, state.getScale());
const Point<double> endPoint = Projection::project(latLng, state.getScale());

// Constrain camera options.
zoom = util::clamp(zoom, state.getMinZoom(), state.getMaxZoom());
pitch = util::clamp(pitch, state.getMinPitch(), state.getMaxPitch());
fov = util::clamp(fov, state.getMinFieldOfView(), state.getMaxFieldOfView());

// Minimize rotation by taking the shorter path around the circle.
bearing = _normalizeAngle(bearing, state.getBearing());
state.setBearing(_normalizeAngle(state.getBearing(), bearing));
const double startZoom = state.scaleZoom(state.getScale());
const double startBearing = state.getBearing();
const double startPitch = state.getPitch();
const double startRoll = state.getRoll();
const double startFov = state.getFieldOfView();

/// w₀: Initial visible span, measured in pixels at the initial scale.
/// Known henceforth as a <i>screenful</i>.
Expand Down Expand Up @@ -320,6 +341,7 @@ void Transform::flyTo(const CameraOptions& camera, const AnimationOptions& anima
// Convert to geographic coordinates and set the new viewpoint.
LatLng frameLatLng = Projection::unproject(framePoint, startScale);
state.setLatLngZoom(frameLatLng, frameZoom);
state.setCenterAltitude(util::interpolate(startCenterAlt, centerAlt, us));
if (bearing != startBearing) {
state.setBearing(util::wrap(util::interpolate(startBearing, bearing, k), -pi, pi));
}
Expand All @@ -331,10 +353,15 @@ void Transform::flyTo(const CameraOptions& camera, const AnimationOptions& anima
util::interpolate(startEdgeInsets.bottom(), padding.bottom(), k),
util::interpolate(startEdgeInsets.right(), padding.right(), k)});
}
double maxPitch = getMaxPitchForEdgeInsets(state.getEdgeInsets());

if (pitch != startPitch || maxPitch < startPitch) {
state.setPitch(std::min(maxPitch, util::interpolate(startPitch, pitch, k)));
if (pitch != startPitch) {
state.setPitch(util::interpolate(startPitch, pitch, k));
}
if (roll != startRoll) {
state.setPitch(util::interpolate(startRoll, roll, k));
}
if (fov != startFov) {
state.setFieldOfView(util::interpolate(startFov, fov, k));
}
},
duration);
Expand Down Expand Up @@ -431,15 +458,24 @@ double Transform::getPitch() const {
return state.getPitch();
}

double Transform::getRoll() const {
return state.getRoll();
}

double Transform::getFieldOfView() const {
return state.getFieldOfView();
}

// MARK: - North Orientation

void Transform::setNorthOrientation(NorthOrientation orientation) {
state.setNorthOrientation(orientation);
double scale{state.getScale()};
double x{state.getX()};
double y{state.getY()};
double z{state.getZ()};
state.constrain(scale, x, y);
state.setProperties(TransformStateProperties().withScale(scale).withX(x).withY(y));
state.setProperties(TransformStateProperties().withScale(scale).withX(x).withY(y).withZ(z));
}

NorthOrientation Transform::getNorthOrientation() const {
Expand All @@ -453,8 +489,9 @@ void Transform::setConstrainMode(mbgl::ConstrainMode mode) {
double scale{state.getScale()};
double x{state.getX()};
double y{state.getY()};
double z{state.getZ()};
state.constrain(scale, x, y);
state.setProperties(TransformStateProperties().withScale(scale).withX(x).withY(y));
state.setProperties(TransformStateProperties().withScale(scale).withX(x).withY(y).withZ(z));
}

ConstrainMode Transform::getConstrainMode() const {
Expand Down Expand Up @@ -646,7 +683,8 @@ double Transform::getMaxPitchForEdgeInsets(const EdgeInsets& insets) const {
// 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 tangentOfFovAboveCenterAngle = 1.03 * (0.5 + centerOffsetY / height) * 2.0 *
tan(getFieldOfView() / 2.0);
const double fovAboveCenter = std::atan(tangentOfFovAboveCenterAngle);
return pi * 0.5 - fovAboveCenter;
// e.g. Maximum pitch of 60 degrees is when perspective center's offset from
Expand Down
2 changes: 2 additions & 0 deletions src/mbgl/map/transform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ class Transform : private util::noncopyable {
// Pitch

double getPitch() const;
double getRoll() const;
double getFieldOfView() const;

// North Orientation
void setNorthOrientation(NorthOrientation);
Expand Down
Loading
Loading