mapbox / mapbox-gl-js

Interactive, thoroughly customizable maps in the browser, powered by vector tiles and WebGL
https://docs.mapbox.com/mapbox-gl-js/
Other
11.22k stars 2.23k forks source link

Camera elevation functionality #10745

Open arthur-clifford opened 3 years ago

arthur-clifford commented 3 years ago

Motivation

3D terrain is awesome and moving aroudn on the map is generally a nice experience, until you run into a mountain at which point the only way to rise above it is to un-pitch the map zoom out then re-pitch the map.

It would be nice to be able leverage the new elevation querying tech to maintain to maintain a minimum camera elevation above the terrain.

Design Alternatives

Consider a combination of the following If not through the elevation model it would at least be nice to have a keyboard commands to zoom out from unpitched map while pitched. That is, vertical lift controls. Perhaps ctrl/option + [Up arrow]/[Down arrow] could increase/decrease camera elevation when pitched.

elevationToZoom and/or zoomToElevation functions

Used to get the meter equivalent of an elevation to set the camera to (when unpitched) or to set the camera's current elevation.

The thing to keep in mind is that camera elevation is a function of zoom-level or fraction of zoom-level when the map is unpitched and some relationship with meters likely based on the meter-to-pixel ratio for each whole zoom level. Once the camera elevation is set, and pitch is applied "zoom" is no longer elevation related.

Note, these would have other applications such as being able to style make layers available based on camera elevation rather than zoom levels.

minHoverHeight property for the camera

Would be a meter distance above ground that the camera will always guarantee to be at or above. If the camera is pointing at a mountain and the user uses up arrow to move toward the mountain as the user reaches the mountain the camera will vertically rise to stay above the terrain so the user can 'fly over' the mountain. In this mode, after flying over the mountain the camera height would maintain.

hoverHeight property

Fix the camera at a meter height above ground such that the camera will rise and fall with the terrain. Flying over a mount means that on the other side of the mountain the camera will lower as the user descends the slope of the mountain.

followPxDistance

When pitched programatically setting map center will actually center the map at specified point and move it followPxDistance "up"/away from center. (after rotation is applied).

Note that rotating the map would essentially require subtracting the followPxDistance from current map center, then rotating the map, then reapplying the follow distance to map center.

The advantage of this in conjunction with hoverHeight or minHoverHeight would be that the camera would rise or fall ahead of the map center reaching the mountain. This is also reproducing UnrealEngine notion of having the camera follow a character/sprite by a distance so you can have a 3d object that is moving through the game space being the main character that the camera follows.

DEM elevation API demo re-imagined

You might re-imagine the current elevation api demo to where the marker with popup showing elevation is actually map center and that the camera is set to have a followPxdistance and and hoverHeight. If the map is recentered for each step in the animation of the marker going along the hill path, the camera would follows the marker up the mountainside while displaying elevation data.

Design

The followDistance is probably a bit more than is really required, but something to be considered for ... eventually. The fact is that this is not for creating games per se.

At a minimum API functions to raise or lower the camera elevation are needed to make naving the map tolerable with mountainous terrains enabled.

On top of those APIs optional lift controls would be useful, if they don't exist already.

The hoverHeight and minHoverHeight functions I believe would make for smoother experiences unless you want to allow for full 360 camera rotation to where you are looking up mountains in order to zoom in to go up the mountain. If the zoomToElevation and elevationToZoom functions were in place these functions would be trivial to implement its basically a function of unpitching the map applying a change in camera elevation then re-pitching the map (between renderings) as the camera moves around in the map space.

The complexity of the implementation is in computing the elevation for the camera. If that is too expensive then nothing based on that operation is going to be any less expensive. If it can be done efficiently then I can only see benefits for navigation and any elevation based operations.

Mock-Up

cameraOptions = map.getFreeCameraOptions() cameraOptions .minHoverHeight = 1; // don't let camera 's elevation be lower than 1 meter above current map-center's elevation. map.setFreeCameraOptions(cameraOptions);

cameraOptions = map.getFreeCameraOptions() camera.hoverHeight = 20; // move camera to 20 meters above current map center's elevation map.setFreeCameraOptions(cameraOptions);

cameraOptions = map.getFreeCameraOptions() camera.elevatation = 1000; // unpitch map set zoom to zoom level equivalent of 1000meters and then repitch map. map.setFreeCameraOptions(cameraOptions);

The elevationToZoom and zoomToElevation could be internal functions but if exposed to the API

var elevation = map.zoomToElevation(11); // get the elevation for zoom level 11

var zoom = map.elevationToZoom(1000); // get the fractional zoom level that best matches 1000 meters.

What this looks like to the end user is the camera will follow the terrain rather than bump into mountains.

Concepts

As you roll out the DEM tech there will be an evolution or re-evolution of elevation-based logic that may be explored in web-mapping. The key is to teach people how to avoid running into mountains.

I've used terminology like hoverHeight for the alliteration. But you might look at the nuance between height vs elevation. And the nuance between elevation and distance above ground.

You might think of it like supporting helicopter navigation. I recently played one of the Ghost Recon games and in it you can fly helicopters which have navigation controls similar to mapbox in terms of up down left and right being more about movign the copter forward and back and strafing left and right. The mouse contols rotation but shfit and ctrl control the up and down motion of the copter.

Now that you have pitch and terrain are in place you need a way to rise above the terrain or descend into it without having to leave your current pitch and orientation.

As to new precedents, once you are able to set elevation for the camera you WILL be expected to set elevation for things like markers. It will be a natural outgrowth of elevation model awareness and support. So having billboard like views that hover above the ground (and buldings) and yet below the tops of mountains.

If you support the follow distance notion then you would likely get into people adding a model to use as an avatar/guide to lead a user through a map experience.

Implementation

I've mostly been thinking in terms of the mapbox-gl js . But C++ should look pretty much the same, its just extending the notion of camera options and allowing for zooming based on unpitched map center elevation.

The main edge case I can think of is what to do for touch displays in terms of elevation. Perhaps a three or four finger drag up or down gesture?

rreusser commented 3 years ago

Thanks for the detailed thoughts and ideas, @arthur-clifford! I don't have an immediate detailed response, but we're actively looking at ways to help people better navigate 3D maps, so this is very valuable feedback! 🙇

I've contemplated a first-person view in which you can pivot the camera without moving it, rather than necessarily moving relative to the map.

Also, closely related is this issue: https://github.com/mapbox/mapbox-gl-js/issues/10195

karimnaaji commented 3 years ago

Hey @arthur-clifford thanks for the very thorough and thoughtful design suggestions.

We have been keeping a close eye on these challenges, and so far have focused on providing building blocks to create more advanced camera behaviors at a higher level than within the library. I would say that the largest use-case we aim to support is path/route following camera movement, and not necessarily full free-fly camera.

The main challenges we have noticed are around this common use case:

Concerning (1). I think map.queryTerrainElevation can already get you quite far to achieve the desired result from minHoverHeight and hoverHeight. As you can access the free camera position, immediately query elevation right below it, and adjust its elevation

Fix the camera at a meter height above ground such that the camera will rise and fall with the terrain. Flying over a mount means that on the other side of the mountain the camera will lower as the user descends the slope of the mountain.

An example for this case by doing queryTerrainElevation at the camera actual position (e.g. right below the camera):

https://user-images.githubusercontent.com/7061573/121270386-7932ca80-c876-11eb-8697-fb622fd8e026.mov

minHoverHeight could be done using the same technique (e.g. min(queryTerrainElevation(cameraPosition), minHeightValue)).

Note that by doing that, you might introduce high-frequency changes as the camera will follow the terrain shape quite closely and fast changes in elevation may be disruptive. For that, it may be possible to make it stateful and interpolate between past and new elevation values to have smoother camera positions:

Screen Shot 2021-06-07 at 4 19 05 PM