@@ -7,16 +7,27 @@ import {DepthMode} from '../gl/depth_mode';
77import { CullFaceMode } from '../gl/cull_face_mode' ;
88import { rasterUniformValues } from './program/raster_program' ;
99import { EXTENT } from '../data/extent' ;
10- import { coveringZoomLevel } from '../geo/projection/covering_tiles ' ;
10+ import { FadingDirections } from '../source/tile ' ;
1111import Point from '@mapbox/point-geometry' ;
1212
1313import type { Painter , RenderOptions } from './painter' ;
1414import type { SourceCache } from '../source/source_cache' ;
1515import type { RasterStyleLayer } from '../style/style_layer/raster_style_layer' ;
1616import type { OverscaledTileID } from '../source/tile_id' ;
17- import type { IReadonlyTransform } from '../geo/transform_interface' ;
1817import type { Tile } from '../source/tile' ;
19- import type { Terrain } from './terrain' ;
18+
19+ type FadeProperties = {
20+ parentTile : Tile ;
21+ parentScaleBy : number ;
22+ parentTopLeft : [ number , number ] ;
23+ fadeValues : FadeValues ;
24+ } ;
25+
26+ type FadeValues = {
27+ tileOpacity : number ;
28+ parentTileOpacity ?: number ;
29+ fadeMix : { opacity : number ; mix : number } ;
30+ } ;
2031
2132const cornerCoords = [
2233 new Point ( 0 , 0 ) ,
@@ -83,37 +94,32 @@ function drawTiles(
8394
8495 const colorMode = painter . colorModeForRenderPass ( ) ;
8596 const align = ! painter . options . moving ;
97+ const rasterOpacity = layer . paint . get ( 'raster-opacity' ) ;
98+ const rasterResampling = layer . paint . get ( 'raster-resampling' ) ;
99+ const fadeDuration = layer . paint . get ( 'raster-fade-duration' ) ;
100+ const isTerrain = ! ! painter . style . map . terrain ;
86101
87102 // Draw all tiles
88103 for ( const coord of coords ) {
89104 // Set the lower zoom level to sublayer 0, and higher zoom levels to higher sublayers
90105 // Use gl.LESS to prevent double drawing in areas where tiles overlap.
91106 const depthMode = painter . getDepthModeForSublayer ( coord . overscaledZ - minTileZ ,
92- layer . paint . get ( 'raster-opacity' ) === 1 ? DepthMode . ReadWrite : DepthMode . ReadOnly , gl . LESS ) ;
107+ rasterOpacity === 1 ? DepthMode . ReadWrite : DepthMode . ReadOnly , gl . LESS ) ;
93108
94109 const tile = sourceCache . getTile ( coord ) ;
110+ const textureFilter = rasterResampling === 'nearest' ? gl . NEAREST : gl . LINEAR ;
95111
96- tile . registerFadeDuration ( layer . paint . get ( 'raster-fade-duration' ) ) ;
97-
98- const parentTile = sourceCache . findLoadedParent ( coord , 0 ) ;
99- const siblingTile = sourceCache . findLoadedSibling ( coord ) ;
100- // Prefer parent tile if present
101- const fadeTileReference = parentTile || siblingTile || null ;
102- const fade = getFadeValues ( tile , fadeTileReference , sourceCache , layer , painter . transform , painter . style . map . terrain ) ;
103-
104- let parentScaleBy , parentTL ;
105-
106- const textureFilter = layer . paint . get ( 'raster-resampling' ) === 'nearest' ? gl . NEAREST : gl . LINEAR ;
107-
112+ // create and bind first texture
108113 context . activeTexture . set ( gl . TEXTURE0 ) ;
109114 tile . texture . bind ( textureFilter , gl . CLAMP_TO_EDGE , gl . LINEAR_MIPMAP_NEAREST ) ;
110115
116+ // create second texture - use either the current tile or fade tile to bind second texture below
111117 context . activeTexture . set ( gl . TEXTURE1 ) ;
112-
118+ const { parentTile, parentScaleBy, parentTopLeft, fadeValues} = getFadeProperties ( tile , sourceCache , fadeDuration , isTerrain ) ;
119+ tile . fadeOpacity = fadeValues . tileOpacity ;
113120 if ( parentTile ) {
121+ parentTile . fadeOpacity = fadeValues . parentTileOpacity ;
114122 parentTile . texture . bind ( textureFilter , gl . CLAMP_TO_EDGE , gl . LINEAR_MIPMAP_NEAREST ) ;
115- parentScaleBy = Math . pow ( 2 , parentTile . tileID . overscaledZ - tile . tileID . overscaledZ ) ;
116- parentTL = [ tile . tileID . canonical . x * parentScaleBy % 1 , tile . tileID . canonical . y * parentScaleBy % 1 ] ;
117123 } else {
118124 tile . texture . bind ( textureFilter , gl . CLAMP_TO_EDGE , gl . LINEAR_MIPMAP_NEAREST ) ;
119125 }
@@ -127,10 +133,9 @@ function drawTiles(
127133
128134 const terrainData = painter . style . map . terrain && painter . style . map . terrain . getTerrainData ( coord ) ;
129135 const projectionData = transform . getProjectionData ( { overscaledTileID : coord , aligned : align , applyGlobeMatrix : ! isRenderingToTexture , applyTerrainMatrix : true } ) ;
130- const uniformValues = rasterUniformValues ( parentTL || [ 0 , 0 ] , parentScaleBy || 1 , fade , layer , corners ) ;
136+ const uniformValues = rasterUniformValues ( parentTopLeft , parentScaleBy , fadeValues . fadeMix , layer , corners ) ;
131137
132138 const mesh = projection . getMeshFromTileID ( context , coord . canonical , useBorder , allowPoles , 'raster' ) ;
133-
134139 const stencilMode = stencilModes ? stencilModes [ coord . overscaledZ ] : StencilMode . disabled ;
135140
136141 program . draw ( context , gl . TRIANGLES , depthMode , stencilMode , colorMode , flipCullfaceMode ? CullFaceMode . frontCCW : CullFaceMode . backCCW ,
@@ -139,46 +144,79 @@ function drawTiles(
139144 }
140145}
141146
142- function getFadeValues ( tile : Tile , parentTile : Tile , sourceCache : SourceCache , layer : RasterStyleLayer , transform : IReadonlyTransform , terrain : Terrain ) {
143- const fadeDuration = layer . paint . get ( 'raster-fade-duration' ) ;
147+ /**
148+ * Get fade properties for current tile - either cross-fading or self-fading properties.
149+ */
150+ function getFadeProperties ( tile : Tile , sourceCache : SourceCache , fadeDuration : number , isTerrain : boolean ) : FadeProperties {
151+ const defaults : FadeProperties = {
152+ parentTile : null ,
153+ parentScaleBy : 1 ,
154+ parentTopLeft : [ 0 , 0 ] ,
155+ fadeValues : { tileOpacity : 1 , parentTileOpacity : 1 , fadeMix : { opacity : 1 , mix : 0 } }
156+ } ;
157+
158+ if ( fadeDuration === 0 || isTerrain ) return defaults ;
159+
160+ // cross-fade with parent first if available
161+ if ( tile . fadingParentID ) {
162+ const parentTile = sourceCache . _getLoadedTile ( tile . fadingParentID ) ;
163+ if ( ! parentTile ) return defaults ;
164+
165+ const parentScaleBy = Math . pow ( 2 , parentTile . tileID . overscaledZ - tile . tileID . overscaledZ ) ;
166+ const parentTopLeft : [ number , number ] = [
167+ ( tile . tileID . canonical . x * parentScaleBy ) % 1 ,
168+ ( tile . tileID . canonical . y * parentScaleBy ) % 1
169+ ] ;
170+
171+ const fadeValues = getCrossFadeValues ( tile , parentTile , fadeDuration ) ;
172+ return { parentTile, parentScaleBy, parentTopLeft, fadeValues} ;
173+ }
144174
145- if ( ! terrain && fadeDuration > 0 ) {
146- const now = browser . now ( ) ;
147- const sinceTile = ( now - tile . timeAdded ) / fadeDuration ;
148- const sinceParent = parentTile ? ( now - parentTile . timeAdded ) / fadeDuration : - 1 ;
175+ // self-fade for edge tiles
176+ if ( tile . selfFading ) {
177+ const fadeValues = getSelfFadeValues ( tile , fadeDuration ) ;
178+ return { parentTile : null , parentScaleBy : 1 , parentTopLeft : [ 0 , 0 ] , fadeValues} ;
179+ }
149180
150- const source = sourceCache . getSource ( ) ;
151- const idealZ = coveringZoomLevel ( transform , {
152- tileSize : source . tileSize ,
153- roundZoom : source . roundZoom
154- } ) ;
181+ return defaults ;
182+ }
155183
156- // if no parent or parent is older, fade in; if parent is younger, fade out
157- const fadeIn = ! parentTile || Math . abs ( parentTile . tileID . overscaledZ - idealZ ) > Math . abs ( tile . tileID . overscaledZ - idealZ ) ;
184+ /**
185+ * Cross-fade values for a base tile with a parent tile (for zooming in/out)
186+ */
187+ function getCrossFadeValues ( tile : Tile , parentTile : Tile , fadeDuration : number ) : FadeValues {
188+ const now = browser . now ( ) ;
158189
159- const childOpacity = ( fadeIn && tile . refreshedUponExpiration ) ? 1 : clamp ( fadeIn ? sinceTile : 1 - sinceParent , 0 , 1 ) ;
190+ const timeSinceTile = ( now - tile . timeAdded ) / fadeDuration ;
191+ const timeSinceParent = ( now - parentTile . timeAdded ) / fadeDuration ;
160192
161- // we don't crossfade tiles that were just refreshed upon expiring:
162- // once they're old enough to pass the crossfading threshold
163- // (fadeDuration), unset the `refreshedUponExpiration` flag so we don't
164- // incorrectly fail to crossfade them when zooming
165- if ( tile . refreshedUponExpiration && sinceTile >= 1 ) tile . refreshedUponExpiration = false ;
193+ // get fading opacity based on current fade direction
194+ const doFadeIn = ( tile . fadingDirection === FadingDirections . Incoming ) ;
195+ const opacity1 = clamp ( timeSinceTile , 0 , 1 ) ;
196+ const opacity2 = clamp ( 1 - timeSinceParent , 0 , 1 ) ;
166197
167- if ( parentTile ) {
168- return {
169- opacity : 1 ,
170- mix : 1 - childOpacity
171- } ;
172- } else {
173- return {
174- opacity : childOpacity ,
175- mix : 0
176- } ;
177- }
178- } else {
179- return {
180- opacity : 1 ,
181- mix : 0
182- } ;
183- }
198+ const tileOpacity = doFadeIn ? opacity1 : opacity2 ;
199+ const parentTileOpacity = doFadeIn ? opacity2 : opacity1 ;
200+ const fadeMix = {
201+ opacity : 1 ,
202+ mix : 1 - tileOpacity
203+ } ;
204+
205+ return { tileOpacity, parentTileOpacity, fadeMix} ;
206+ }
207+
208+ /**
209+ * Simple fade-in values for tile without a parent (i.e. edge tiles)
210+ */
211+ function getSelfFadeValues ( tile : Tile , fadeDuration : number ) : FadeValues {
212+ const now = browser . now ( ) ;
213+
214+ const timeSinceTile = ( now - tile . timeAdded ) / fadeDuration ;
215+ const tileOpacity = clamp ( timeSinceTile , 0 , 1 ) ;
216+ const fadeMix = {
217+ opacity : tileOpacity ,
218+ mix : 0
219+ } ;
220+
221+ return { tileOpacity, fadeMix} ;
184222}
0 commit comments