@@ -149,6 +149,7 @@ class Transform {
149149 _nearZ: number ;
150150 _farZ: number ;
151151 _mercatorScaleRatio: number ;
152+ _isCameraConstrained: boolean ;
152153
153154 constructor ( minZoom : ?number , maxZoom : ?number , minPitch : ?number , maxPitch : ?number , renderWorldCopies : boolean | void , projection ?: ?ProjectionSpecification , bounds : ?LngLatBounds ) {
154155 this . tileSize = 512 ; // constant
@@ -225,13 +226,14 @@ class Transform {
225226 this . _updateCameraOnTerrain ( ) ;
226227 this . _calcMatrices ( ) ;
227228 }
228- updateElevation ( constrainCameraOverTerrain : boolean ) { // On render, no need for higher granularity on update reasons.
229+
230+ updateElevation ( constrainCameraOverTerrain : boolean , adaptCameraAltitude : boolean = false ) {
229231 const centerAltitudeChanged = this . _elevation && this . _elevation . exaggeration ( ) !== this . _centerAltitudeValidForExaggeration ;
230232 if ( this . _seaLevelZoom == null || centerAltitudeChanged ) {
231233 this . _updateCameraOnTerrain ( ) ;
232234 }
233235 if ( constrainCameraOverTerrain || centerAltitudeChanged ) {
234- this . _constrainCameraAltitude ( ) ;
236+ this . _constrainCamera ( adaptCameraAltitude ) ;
235237 }
236238 this . _calcMatrices ( ) ;
237239 }
@@ -1620,7 +1622,6 @@ class Transform {
16201622 }
16211623
16221624 recenterOnTerrain ( ) {
1623-
16241625 if ( ! this . _elevation || this . projection . name === 'globe' )
16251626 return ;
16261627
@@ -1659,41 +1660,41 @@ class Transform {
16591660 }
16601661 }
16611662
1662- _constrainCameraAltitude ( ) {
1663+ _constrainCamera ( adaptCameraAltitude : boolean = false ) {
16631664 if ( ! this . _elevation )
16641665 return ;
16651666
16661667 const elevation : Elevation = this . _elevation ;
1667- this . _updateCameraState ( ) ;
16681668
16691669 // Find uncompensated camera position for elevation sampling.
16701670 // The default camera position might have been compensated by the active projection model.
16711671 const mercPixelsPerMeter = mercatorZfromAltitude ( 1 , this . _center . lat ) * this . worldSize ;
16721672 const pos = this . _computeCameraPosition ( mercPixelsPerMeter ) ;
1673-
16741673 const elevationAtCamera = elevation . getAtPointOrZero ( new MercatorCoordinate ( ...pos ) ) ;
1675- const minHeight = this . _minimumHeightOverTerrain ( ) * Math . cos ( degToRad ( this . _maxPitch ) ) ;
16761674 const terrainElevation = this . pixelsPerMeter / this . worldSize * elevationAtCamera ;
1677- const cameraHeight = this . _camera . position [ 2 ] - terrainElevation ;
1678-
1679- if ( cameraHeight < minHeight ) {
1680- const center = this . locationCoordinate ( this . _center , this . _centerAltitude ) ;
1681- const cameraToCenter = [ center . x - pos [ 0 ] , center . y - pos [ 1 ] , center . z - pos [ 2 ] ] ;
1682- const prevDistToCamera = vec3 . length ( cameraToCenter ) ;
1683-
1684- // Adjust the camera vector so that the camera is placed above the terrain.
1685- // Distance between the camera and the center point is kept constant.
1686- cameraToCenter [ 2 ] -= ( minHeight - cameraHeight ) / this . _pixelsPerMercatorPixel ;
1687-
1688- const newDistToCamera = vec3 . length ( cameraToCenter ) ;
1689- if ( newDistToCamera === 0 )
1690- return ;
1691-
1692- vec3 . scale ( cameraToCenter , cameraToCenter , prevDistToCamera / newDistToCamera * this . _pixelsPerMercatorPixel ) ;
1693- this . _camera . position = [ center . x - cameraToCenter [ 0 ] , center . y - cameraToCenter [ 1 ] , center . z * this . _pixelsPerMercatorPixel - cameraToCenter [ 2 ] ] ;
1694-
1695- this . _camera . orientation = orientationFromFrame ( cameraToCenter , this . _camera . up ( ) ) ;
1696- this . _updateStateFromCamera ( ) ;
1675+ const minHeight = this . _minimumHeightOverTerrain ( ) ;
1676+ const cameraHeight = pos [ 2 ] - terrainElevation ;
1677+
1678+ if ( cameraHeight <= minHeight ) {
1679+ if ( cameraHeight < 0 || adaptCameraAltitude ) {
1680+ const center = this . locationCoordinate ( this . _center , this . _centerAltitude ) ;
1681+ const cameraToCenter = [ pos [ 0 ] , pos [ 1 ] , center . z - pos [ 2 ] ] ;
1682+
1683+ const prevDistToCamera = vec3 . length ( cameraToCenter ) ;
1684+ // Adjust the camera vector so that the camera is placed above the terrain.
1685+ // Distance between the camera and the center point is kept constant.
1686+ cameraToCenter [ 2 ] -= ( minHeight - cameraHeight ) / this . _pixelsPerMercatorPixel ;
1687+ const newDistToCamera = vec3 . length ( cameraToCenter ) ;
1688+
1689+ if ( newDistToCamera === 0 )
1690+ return ;
1691+
1692+ vec3 . scale ( cameraToCenter , cameraToCenter , prevDistToCamera / newDistToCamera * this . _pixelsPerMercatorPixel ) ;
1693+ this . _camera . position = [ pos [ 0 ] , pos [ 1 ] , center . z * this . _pixelsPerMercatorPixel - cameraToCenter [ 2 ] ] ;
1694+ this . _updateStateFromCamera ( ) ;
1695+ } else {
1696+ this . _isCameraConstrained = true ;
1697+ }
16971698 }
16981699 }
16991700
@@ -1754,7 +1755,7 @@ class Transform {
17541755 this . zoom += this . scaleZoom ( s ) ;
17551756 }
17561757
1757- this . _constrainCameraAltitude ( ) ;
1758+ this . _constrainCamera ( ) ;
17581759 this . _unmodified = unmodified ;
17591760 this . _constraining = false ;
17601761 }
@@ -2041,10 +2042,10 @@ class Transform {
20412042
20422043 _minimumHeightOverTerrain ( ) : number {
20432044 // Determine minimum height for the camera over the terrain related to current zoom.
2044- // Values above than 2 allow max-pitch camera closer to e.g. top of the hill, exposing
2045+ // Values above 4 allow camera closer to e.g. top of the hill, exposing
20452046 // drape raster overscale artifacts or cut terrain (see under it) as it gets clipped on
20462047 // near plane. Returned value is in mercator coordinates.
2047- const MAX_DRAPE_OVERZOOM = 2 ;
2048+ const MAX_DRAPE_OVERZOOM = 4 ;
20482049 const zoom = Math . min ( ( this . _seaLevelZoom != null ? this . _seaLevelZoom : this . _zoom ) + MAX_DRAPE_OVERZOOM , this . _maxZoom ) ;
20492050 return this . _mercatorZfromZoom ( zoom ) ;
20502051 }
0 commit comments