From 5de0876b1f7f9973aa236746739eb034ed5a93b7 Mon Sep 17 00:00:00 2001 From: Isaac Besora Vilardaga Date: Fri, 15 Nov 2024 10:59:33 +0100 Subject: [PATCH] Refactor usage of implicit parameter to an explicit one when rendering (#5020) * Use explicit parameter instead of implicit one * Add parameter to all draw functions * Add predicates * Don't use default parameter * Move to apply instead of ignore * Move booleans to type * Fixes * Rename RenderFlags to RenderOptions * Rename parameter and change switch * More fixes * More renaming * Add explicit values * Fix tests --- src/geo/projection/globe_transform.test.ts | 12 +-- src/geo/projection/globe_transform.ts | 10 +-- src/geo/projection/mercator_transform.ts | 8 +- src/geo/projection/mercator_utils.ts | 4 +- src/geo/projection/projection_data.ts | 8 +- src/geo/transform_interface.ts | 2 +- src/render/draw_background.ts | 9 +- src/render/draw_circle.ts | 7 +- src/render/draw_collision_debug.ts | 2 +- src/render/draw_custom.test.ts | 5 +- src/render/draw_custom.ts | 7 +- src/render/draw_debug.ts | 2 +- src/render/draw_fill.test.ts | 5 +- src/render/draw_fill.ts | 18 ++-- src/render/draw_fill_extrusion.ts | 16 ++-- src/render/draw_heatmap.ts | 23 +++-- src/render/draw_hillshade.ts | 19 +++-- src/render/draw_line.ts | 12 +-- src/render/draw_raster.ts | 7 +- src/render/draw_sky.ts | 2 +- src/render/draw_symbol.test.ts | 14 +-- src/render/draw_symbol.ts | 14 +-- src/render/draw_terrain.ts | 13 +-- src/render/painter.ts | 85 ++++++++++--------- src/render/program/sky_program.ts | 2 +- src/render/render_to_texture.test.ts | 33 +++---- src/render/render_to_texture.ts | 12 +-- .../style_layer/background_style_layer.ts | 2 + src/style/style_layer/circle_style_layer.ts | 2 + src/style/style_layer/custom_style_layer.ts | 2 + .../style_layer/fill_extrusion_style_layer.ts | 2 + src/style/style_layer/fill_style_layer.ts | 2 + src/style/style_layer/heatmap_style_layer.ts | 2 + .../style_layer/hillshade_style_layer.ts | 2 + src/style/style_layer/line_style_layer.ts | 2 + src/style/style_layer/raster_style_layer.ts | 2 + src/style/style_layer/symbol_style_layer.ts | 2 + test/build/min.test.ts | 2 +- 38 files changed, 211 insertions(+), 162 deletions(-) diff --git a/src/geo/projection/globe_transform.test.ts b/src/geo/projection/globe_transform.test.ts index 0abbda7ed6..a7ad7fc338 100644 --- a/src/geo/projection/globe_transform.test.ts +++ b/src/geo/projection/globe_transform.test.ts @@ -27,7 +27,7 @@ function planeDistance(point: Array, plane: Array) { } function createGlobeTransform(globeProjection: GlobeProjection) { - const globeTransform = new GlobeTransform(globeProjection); + const globeTransform = new GlobeTransform(globeProjection, true); globeTransform.resize(640, 480); globeTransform.setFov(45); return globeTransform; @@ -46,14 +46,14 @@ describe('GlobeTransform', () => { expectToBeCloseToArray(projectionData.tileMercatorCoords, [0.5, 0, 0.5 / EXTENT, 0.5 / EXTENT]); }); - test('Globe transition is not 0 when not ignoring the globe matrix', () => { + test('Globe transition is 0 when not applying the globe matrix', () => { const projectionData = globeTransform.getProjectionData({overscaledTileID: new OverscaledTileID(1, 0, 1, 1, 0)}); - expect(projectionData.projectionTransition).not.toBe(0); + expect(projectionData.projectionTransition).toBe(0); }); - test('Ignoring the globe matrix sets transition to 0', () => { - const projectionData = globeTransform.getProjectionData({overscaledTileID: new OverscaledTileID(1, 0, 1, 1, 0), ignoreGlobeMatrix: true}); - expect(projectionData.projectionTransition).toBe(0); + test('Applying the globe matrix sets transition to something different than 0', () => { + const projectionData = globeTransform.getProjectionData({overscaledTileID: new OverscaledTileID(1, 0, 1, 1, 0), applyGlobeMatrix: true}); + expect(projectionData.projectionTransition).not.toBe(0); }); }); diff --git a/src/geo/projection/globe_transform.ts b/src/geo/projection/globe_transform.ts index 1460a6dde0..3ac8df6f9e 100644 --- a/src/geo/projection/globe_transform.ts +++ b/src/geo/projection/globe_transform.ts @@ -454,8 +454,8 @@ export class GlobeTransform implements ITransform { } getProjectionData(params: ProjectionDataParams): ProjectionData { - const {overscaledTileID, aligned, ignoreTerrainMatrix, ignoreGlobeMatrix} = params; - const data = this._mercatorTransform.getProjectionData({overscaledTileID, aligned, ignoreTerrainMatrix}); + const {overscaledTileID, aligned, applyTerrainMatrix, applyGlobeMatrix} = params; + const data = this._mercatorTransform.getProjectionData({overscaledTileID, aligned, applyTerrainMatrix}); // Set 'projectionMatrix' to actual globe transform if (this.isGlobeRendering) { @@ -463,7 +463,7 @@ export class GlobeTransform implements ITransform { } data.clippingPlane = this._cachedClippingPlane as [number, number, number, number]; - data.projectionTransition = ignoreGlobeMatrix ? 0 : this._globeness; + data.projectionTransition = applyGlobeMatrix ? this._globeness : 0; return data; } @@ -1184,8 +1184,8 @@ export class GlobeTransform implements ITransform { return m; } - getProjectionDataForCustomLayer(): ProjectionData { - const projectionData = this.getProjectionData({overscaledTileID: new OverscaledTileID(0, 0, 0, 0, 0)}); + getProjectionDataForCustomLayer(applyGlobeMatrix: boolean = true): ProjectionData { + const projectionData = this.getProjectionData({overscaledTileID: new OverscaledTileID(0, 0, 0, 0, 0), applyGlobeMatrix}); projectionData.tileMercatorCoords = [0, 0, 1, 1]; // Even though we requested projection data for the mercator base tile which covers the entire mercator range, diff --git a/src/geo/projection/mercator_transform.ts b/src/geo/projection/mercator_transform.ts index 3842e3d2d9..5b163534d7 100644 --- a/src/geo/projection/mercator_transform.ts +++ b/src/geo/projection/mercator_transform.ts @@ -759,9 +759,9 @@ export class MercatorTransform implements ITransform { } getProjectionData(params: ProjectionDataParams): ProjectionData { - const {overscaledTileID, aligned, ignoreTerrainMatrix} = params; + const {overscaledTileID, aligned, applyTerrainMatrix} = params; const matrix = overscaledTileID ? this.calculatePosMatrix(overscaledTileID, aligned) : null; - return getBasicProjectionData(overscaledTileID, matrix, ignoreTerrainMatrix); + return getBasicProjectionData(overscaledTileID, matrix, applyTerrainMatrix); } isLocationOccluded(_: LngLat): boolean { @@ -833,9 +833,9 @@ export class MercatorTransform implements ITransform { return m; } - getProjectionDataForCustomLayer(): ProjectionData { + getProjectionDataForCustomLayer(applyGlobeMatrix: boolean = true): ProjectionData { const tileID = new OverscaledTileID(0, 0, 0, 0, 0); - const projectionData = this.getProjectionData({overscaledTileID: tileID, ignoreTerrainMatrix: true}); + const projectionData = this.getProjectionData({overscaledTileID: tileID, applyGlobeMatrix}); const tileMatrix = calculateTileMatrix(tileID, this.worldSize); mat4.multiply(tileMatrix, this._viewProjMatrix, tileMatrix); diff --git a/src/geo/projection/mercator_utils.ts b/src/geo/projection/mercator_utils.ts index de2a6ab878..bfacebb329 100644 --- a/src/geo/projection/mercator_utils.ts +++ b/src/geo/projection/mercator_utils.ts @@ -94,7 +94,7 @@ export function getMercatorHorizon(transform: {pitch: number; cameraToCenterDist Math.tan(degreesToRadians(maxMercatorHorizonAngle - transform.pitch))); } -export function getBasicProjectionData(overscaledTileID: OverscaledTileID, tilePosMatrix?: mat4, ignoreTerrainMatrix?: boolean): ProjectionData { +export function getBasicProjectionData(overscaledTileID: OverscaledTileID, tilePosMatrix?: mat4, applyTerrainMatrix: boolean = true): ProjectionData { let tileOffsetSize: [number, number, number, number]; if (overscaledTileID) { @@ -110,7 +110,7 @@ export function getBasicProjectionData(overscaledTileID: OverscaledTileID, tileP } let mainMatrix: mat4; - if (overscaledTileID && overscaledTileID.terrainRttPosMatrix && !ignoreTerrainMatrix) { + if (overscaledTileID && overscaledTileID.terrainRttPosMatrix && applyTerrainMatrix) { mainMatrix = overscaledTileID.terrainRttPosMatrix; } else if (tilePosMatrix) { mainMatrix = tilePosMatrix; diff --git a/src/geo/projection/projection_data.ts b/src/geo/projection/projection_data.ts index 0f880c745c..215af751c8 100644 --- a/src/geo/projection/projection_data.ts +++ b/src/geo/projection/projection_data.ts @@ -56,11 +56,11 @@ export type ProjectionDataParams = { */ aligned?: boolean; /** - * Set to true if the terrain matrix should be ignored + * Set to true if the terrain matrix should be applied (i.e. when rendering terrain) */ - ignoreTerrainMatrix?: boolean; + applyTerrainMatrix?: boolean; /** - * Set to true if the globe matrix should be ignored (i.e. when rendering to texture for terrain) + * Set to true if the globe matrix should be applied (i.e. when rendering globe) */ - ignoreGlobeMatrix?: boolean; + applyGlobeMatrix?: boolean; } diff --git a/src/geo/transform_interface.ts b/src/geo/transform_interface.ts index d4788a2392..2feee40c1b 100644 --- a/src/geo/transform_interface.ts +++ b/src/geo/transform_interface.ts @@ -471,7 +471,7 @@ export interface IReadonlyTransform extends ITransformGetters { /** * Return projection data such that coordinates in mercator projection in range 0..1 will get projected to the map correctly. */ - getProjectionDataForCustomLayer(): ProjectionData; + getProjectionDataForCustomLayer(applyGlobeMatrix: boolean): ProjectionData; /** * Returns a tile-specific projection matrix. Used for symbol placement fast-path for mercator transform. diff --git a/src/render/draw_background.ts b/src/render/draw_background.ts index a4fb8499cc..815d49861d 100644 --- a/src/render/draw_background.ts +++ b/src/render/draw_background.ts @@ -6,25 +6,25 @@ import { backgroundPatternUniformValues } from './program/background_program'; -import type {Painter} from './painter'; +import type {Painter, RenderOptions} from './painter'; import type {SourceCache} from '../source/source_cache'; import type {BackgroundStyleLayer} from '../style/style_layer/background_style_layer'; import {OverscaledTileID} from '../source/tile_id'; import {coveringTiles} from '../geo/projection/covering_tiles'; -export function drawBackground(painter: Painter, sourceCache: SourceCache, layer: BackgroundStyleLayer, coords?: Array) { +export function drawBackground(painter: Painter, sourceCache: SourceCache, layer: BackgroundStyleLayer, coords: Array, renderOptions: RenderOptions) { const color = layer.paint.get('background-color'); const opacity = layer.paint.get('background-opacity'); if (opacity === 0) return; + const {isRenderingToTexture} = renderOptions; const context = painter.context; const gl = context.gl; const projection = painter.style.projection; const transform = painter.transform; const tileSize = transform.tileSize; const image = layer.paint.get('background-pattern'); - const globeWithTerrain = painter.style.map.terrain && painter.style.projection.name === 'globe'; if (painter.isPatternMissing(image)) return; @@ -47,7 +47,8 @@ export function drawBackground(painter: Painter, sourceCache: SourceCache, layer for (const tileID of tileIDs) { const projectionData = transform.getProjectionData({ overscaledTileID: tileID, - ignoreGlobeMatrix: globeWithTerrain + applyGlobeMatrix: !isRenderingToTexture, + applyTerrainMatrix: true }); const uniformValues = image ? diff --git a/src/render/draw_circle.ts b/src/render/draw_circle.ts index 63fe26c6e7..04d2ac83e4 100644 --- a/src/render/draw_circle.ts +++ b/src/render/draw_circle.ts @@ -6,7 +6,7 @@ import {circleUniformValues} from './program/circle_program'; import {SegmentVector} from '../data/segment'; import {OverscaledTileID} from '../source/tile_id'; -import type {Painter} from './painter'; +import type {Painter, RenderOptions} from './painter'; import type {SourceCache} from '../source/source_cache'; import type {CircleStyleLayer} from '../style/style_layer/circle_style_layer'; import type {CircleBucket} from '../data/bucket/circle_bucket'; @@ -35,9 +35,10 @@ type SegmentsTileRenderState = { state: TileRenderState; }; -export function drawCircles(painter: Painter, sourceCache: SourceCache, layer: CircleStyleLayer, coords: Array) { +export function drawCircles(painter: Painter, sourceCache: SourceCache, layer: CircleStyleLayer, coords: Array, renderOptions: RenderOptions) { if (painter.renderPass !== 'translucent') return; + const {isRenderingToTexture} = renderOptions; const opacity = layer.paint.get('circle-opacity'); const strokeWidth = layer.paint.get('circle-stroke-width'); const strokeOpacity = layer.paint.get('circle-stroke-opacity'); @@ -80,7 +81,7 @@ export function drawCircles(painter: Painter, sourceCache: SourceCache, layer: C const terrainData = painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord); const uniformValues = circleUniformValues(painter, tile, layer, translateForUniforms, radiusCorrectionFactor); - const projectionData = transform.getProjectionData({overscaledTileID: coord}); + const projectionData = transform.getProjectionData({overscaledTileID: coord, applyGlobeMatrix: !isRenderingToTexture, applyTerrainMatrix: true}); const state: TileRenderState = { programConfiguration, diff --git a/src/render/draw_collision_debug.ts b/src/render/draw_collision_debug.ts index 9da839118c..8ca4a8eb23 100644 --- a/src/render/draw_collision_debug.ts +++ b/src/render/draw_collision_debug.ts @@ -62,7 +62,7 @@ export function drawCollisionDebug(painter: Painter, sourceCache: SourceCache, l CullFaceMode.disabled, collisionUniformValues(painter.transform), painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord), - transform.getProjectionData({overscaledTileID: coord}), + transform.getProjectionData({overscaledTileID: coord, applyGlobeMatrix: true, applyTerrainMatrix: true}), layer.id, buffers.layoutVertexBuffer, buffers.indexBuffer, buffers.segments, null, painter.transform.zoom, null, null, buffers.collisionVertexBuffer); diff --git a/src/render/draw_custom.test.ts b/src/render/draw_custom.test.ts index 47f7217e70..5cc0d0566b 100644 --- a/src/render/draw_custom.test.ts +++ b/src/render/draw_custom.test.ts @@ -1,7 +1,7 @@ import {OverscaledTileID} from '../source/tile_id'; import {SourceCache} from '../source/source_cache'; import {Tile} from '../source/tile'; -import {Painter} from './painter'; +import {Painter, RenderOptions} from './painter'; import type {Map} from '../ui/map'; import {drawCustom} from './draw_custom'; import {CustomStyleLayer} from '../style/style_layer/custom_style_layer'; @@ -60,7 +60,8 @@ describe('drawCustom', () => { }; }, }); - drawCustom(mockPainter, sourceCacheMock, mockLayer); + const renderOptions: RenderOptions = {isRenderingToTexture: false, isRenderingGlobe: false}; + drawCustom(mockPainter, sourceCacheMock, mockLayer, renderOptions); expect(result.gl).toBeDefined(); expect(result.args.farZ).toBeCloseTo(804.8028169246645, 6); expect(result.args.farZ).toBe(mockPainter.transform.farZ); diff --git a/src/render/draw_custom.ts b/src/render/draw_custom.ts index c34ec6b5aa..988c1bdf91 100644 --- a/src/render/draw_custom.ts +++ b/src/render/draw_custom.ts @@ -1,18 +1,19 @@ import {DepthMode} from '../gl/depth_mode'; import {StencilMode} from '../gl/stencil_mode'; -import type {Painter} from './painter'; +import type {Painter, RenderOptions} from './painter'; import type {SourceCache} from '../source/source_cache'; import type {CustomRenderMethodInput, CustomStyleLayer} from '../style/style_layer/custom_style_layer'; -export function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomStyleLayer) { +export function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomStyleLayer, renderOptions: RenderOptions) { + const {isRenderingGlobe} = renderOptions; const context = painter.context; const implementation = layer.implementation; const projection = painter.style.projection; const transform = painter.transform; - const projectionData = transform.getProjectionDataForCustomLayer(); + const projectionData = transform.getProjectionDataForCustomLayer(isRenderingGlobe); const customLayerArgs: CustomRenderMethodInput = { farZ: transform.farZ, diff --git a/src/render/draw_debug.ts b/src/render/draw_debug.ts index 8974d8f9e9..006224a91a 100644 --- a/src/render/draw_debug.ts +++ b/src/render/draw_debug.ts @@ -91,7 +91,7 @@ function drawDebugTile(painter: Painter, sourceCache: SourceCache, coord: Oversc const tileLabel = `${tileIdText} ${tileSizeKb}kB`; drawTextToOverlay(painter, tileLabel); - const projectionData = painter.transform.getProjectionData({overscaledTileID: coord}); + const projectionData = painter.transform.getProjectionData({overscaledTileID: coord, applyGlobeMatrix: true, applyTerrainMatrix: true}); program.draw(context, gl.TRIANGLES, depthMode, stencilMode, ColorMode.alphaBlended, CullFaceMode.disabled, debugUniformValues(Color.transparent, scaleRatio), null, projectionData, id, diff --git a/src/render/draw_fill.test.ts b/src/render/draw_fill.test.ts index aef5b2e811..1b4f6b0311 100644 --- a/src/render/draw_fill.test.ts +++ b/src/render/draw_fill.test.ts @@ -2,7 +2,7 @@ import {mat4} from 'gl-matrix'; import {OverscaledTileID} from '../source/tile_id'; import {SourceCache} from '../source/source_cache'; import {Tile} from '../source/tile'; -import {Painter} from './painter'; +import {Painter, RenderOptions} from './painter'; import {Program} from './program'; import type {ZoomHistory} from '../style/zoom_history'; import type {Map} from '../ui/map'; @@ -38,7 +38,8 @@ describe('drawFill', () => { (sourceCacheMock.getTile as jest.Mock).mockReturnValue(mockTile); sourceCacheMock.map = {showCollisionBoxes: false} as any as Map; - drawFill(painterMock, sourceCacheMock, layer, [mockTile.tileID]); + const renderOptions: RenderOptions = {isRenderingToTexture: false, isRenderingGlobe: false}; + drawFill(painterMock, sourceCacheMock, layer, [mockTile.tileID], renderOptions); // twice: first for fill, second for stroke expect(programMock.draw).toHaveBeenCalledTimes(2); diff --git a/src/render/draw_fill.ts b/src/render/draw_fill.ts index 5c4f336f9e..aa1c2f87a1 100644 --- a/src/render/draw_fill.ts +++ b/src/render/draw_fill.ts @@ -9,7 +9,7 @@ import { fillOutlinePatternUniformValues } from './program/fill_program'; -import type {Painter} from './painter'; +import type {Painter, RenderOptions} from './painter'; import type {SourceCache} from '../source/source_cache'; import type {FillStyleLayer} from '../style/style_layer/fill_style_layer'; import type {FillBucket} from '../data/bucket/fill_bucket'; @@ -18,7 +18,7 @@ import {updatePatternPositionsInProgram} from './update_pattern_positions_in_pro import {StencilMode} from '../gl/stencil_mode'; import {translatePosition} from '../util/util'; -export function drawFill(painter: Painter, sourceCache: SourceCache, layer: FillStyleLayer, coords: Array) { +export function drawFill(painter: Painter, sourceCache: SourceCache, layer: FillStyleLayer, coords: Array, renderOptions: RenderOptions) { const color = layer.paint.get('fill-color'); const opacity = layer.paint.get('fill-opacity'); @@ -26,6 +26,7 @@ export function drawFill(painter: Painter, sourceCache: SourceCache, layer: Fill return; } + const {isRenderingToTexture} = renderOptions; const colorMode = painter.colorModeForRenderPass(); const pattern = layer.paint.get('fill-pattern'); const pass = painter.opaquePassEnabledForLayer() && @@ -37,7 +38,7 @@ export function drawFill(painter: Painter, sourceCache: SourceCache, layer: Fill if (painter.renderPass === pass) { const depthMode = painter.getDepthModeForSublayer( 1, painter.renderPass === 'opaque' ? DepthMode.ReadWrite : DepthMode.ReadOnly); - drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, false); + drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, false, isRenderingToTexture); } // Draw stroke @@ -53,7 +54,7 @@ export function drawFill(painter: Painter, sourceCache: SourceCache, layer: Fill // the (non-antialiased) fill. const depthMode = painter.getDepthModeForSublayer( layer.getPaintProperty('fill-outline-color') ? 2 : 0, DepthMode.ReadOnly); - drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, true); + drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, true, isRenderingToTexture); } } @@ -64,7 +65,8 @@ function drawFillTiles( coords: Array, depthMode: Readonly, colorMode: Readonly, - isOutline: boolean) { + isOutline: boolean, + isRenderingToTexture: boolean) { const gl = painter.context.gl; const fillPropertyName = 'fill-pattern'; const patternProperty = layer.paint.get(fillPropertyName); @@ -106,10 +108,10 @@ function drawFillTiles( updatePatternPositionsInProgram(programConfiguration, fillPropertyName, constantPattern, tile, layer); - const globeWithTerrain = painter.style.map.terrain && painter.style.projection.name === 'globe'; const projectionData = transform.getProjectionData({ overscaledTileID: coord, - ignoreGlobeMatrix: globeWithTerrain + applyGlobeMatrix: !isRenderingToTexture, + applyTerrainMatrix: true }); const translateForUniforms = translatePosition(transform, tile, propertyFillTranslate, propertyFillTranslateAnchor); @@ -149,7 +151,7 @@ function drawFillTiles( // greatly increasing subdivision granularity for both fill layers and stencil masks, at least at tile edges. let stencil: StencilMode; if (painter.renderPass === 'translucent') { - if (globeWithTerrain) { + if (isRenderingToTexture) { const [stencilModes] = painter.stencilConfigForOverlap(coords); stencil = stencilModes[coord.overscaledZ]; } else { diff --git a/src/render/draw_fill_extrusion.ts b/src/render/draw_fill_extrusion.ts index 021778b29b..43542c2fb5 100644 --- a/src/render/draw_fill_extrusion.ts +++ b/src/render/draw_fill_extrusion.ts @@ -7,7 +7,7 @@ import { fillExtrusionPatternUniformValues, } from './program/fill_extrusion_program'; -import type {Painter} from './painter'; +import type {Painter, RenderOptions} from './painter'; import type {SourceCache} from '../source/source_cache'; import type {FillExtrusionStyleLayer} from '../style/style_layer/fill_extrusion_style_layer'; import type {FillExtrusionBucket} from '../data/bucket/fill_extrusion_bucket'; @@ -16,32 +16,33 @@ import type {OverscaledTileID} from '../source/tile_id'; import {updatePatternPositionsInProgram} from './update_pattern_positions_in_program'; import {translatePosition} from '../util/util'; -export function drawFillExtrusion(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLayer, coords: Array) { +export function drawFillExtrusion(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLayer, coords: Array, renderOptions: RenderOptions) { const opacity = layer.paint.get('fill-extrusion-opacity'); if (opacity === 0) { return; } + const {isRenderingToTexture} = renderOptions; if (painter.renderPass === 'translucent') { const depthMode = new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, painter.depthRangeFor3D); if (opacity === 1 && !layer.paint.get('fill-extrusion-pattern').constantOr(1 as any)) { const colorMode = painter.colorModeForRenderPass(); - drawExtrusionTiles(painter, source, layer, coords, depthMode, StencilMode.disabled, colorMode); + drawExtrusionTiles(painter, source, layer, coords, depthMode, StencilMode.disabled, colorMode, isRenderingToTexture); } else { // Draw transparent buildings in two passes so that only the closest surface is drawn. // First draw all the extrusions into only the depth buffer. No colors are drawn. drawExtrusionTiles(painter, source, layer, coords, depthMode, StencilMode.disabled, - ColorMode.disabled); + ColorMode.disabled, isRenderingToTexture); // Then draw all the extrusions a second type, only coloring fragments if they have the // same depth value as the closest fragment in the previous pass. Use the stencil buffer // to prevent the second draw in cases where we have coincident polygons. drawExtrusionTiles(painter, source, layer, coords, depthMode, painter.stencilModeFor3D(), - painter.colorModeForRenderPass()); + painter.colorModeForRenderPass(), isRenderingToTexture); } } } @@ -53,7 +54,8 @@ function drawExtrusionTiles( coords: OverscaledTileID[], depthMode: DepthMode, stencilMode: Readonly, - colorMode: Readonly) { + colorMode: Readonly, + isRenderingToTexture: boolean) { const context = painter.context; const gl = context.gl; const fillPropertyName = 'fill-extrusion-pattern'; @@ -79,7 +81,7 @@ function drawExtrusionTiles( programConfiguration.updatePaintBuffers(crossfade); } - const projectionData = transform.getProjectionData({overscaledTileID: coord}); + const projectionData = transform.getProjectionData({overscaledTileID: coord, applyGlobeMatrix: !isRenderingToTexture, applyTerrainMatrix: true}); updatePatternPositionsInProgram(programConfiguration, fillPropertyName, constantPattern, tile, layer); const translate = translatePosition( diff --git a/src/render/draw_heatmap.ts b/src/render/draw_heatmap.ts index 91e37dd462..0781ae8e8d 100644 --- a/src/render/draw_heatmap.ts +++ b/src/render/draw_heatmap.ts @@ -13,17 +13,18 @@ import { } from './program/heatmap_program'; import {HEATMAP_FULL_RENDER_FBO_KEY} from '../style/style_layer/heatmap_style_layer'; -import type {Painter} from './painter'; +import type {Painter, RenderOptions} from './painter'; import type {SourceCache} from '../source/source_cache'; import type {HeatmapStyleLayer} from '../style/style_layer/heatmap_style_layer'; import type {HeatmapBucket} from '../data/bucket/heatmap_bucket'; import type {OverscaledTileID} from '../source/tile_id'; -export function drawHeatmap(painter: Painter, sourceCache: SourceCache, layer: HeatmapStyleLayer, tileIDs: Array) { +export function drawHeatmap(painter: Painter, sourceCache: SourceCache, layer: HeatmapStyleLayer, tileIDs: Array, renderOptions: RenderOptions) { if (layer.paint.get('heatmap-opacity') === 0) { return; } const context = painter.context; + const {isRenderingToTexture, isRenderingGlobe} = renderOptions; if (painter.style.map.terrain) { for (const coord of tileIDs) { @@ -33,9 +34,9 @@ export function drawHeatmap(painter: Painter, sourceCache: SourceCache, layer: H // so it's fine to simply render the parent until all its 4 children are loaded if (sourceCache.hasRenderableParent(coord)) continue; if (painter.renderPass === 'offscreen') { - prepareHeatmapTerrain(painter, tile, layer, coord); + prepareHeatmapTerrain(painter, tile, layer, coord, isRenderingGlobe); } else if (painter.renderPass === 'translucent') { - renderHeatmapTerrain(painter, layer, coord); + renderHeatmapTerrain(painter, layer, coord, isRenderingToTexture, isRenderingGlobe); } } context.viewport.set([0, 0, painter.width, painter.height]); @@ -79,7 +80,7 @@ function prepareHeatmapFlat(painter: Painter, sourceCache: SourceCache, layer: H const programConfiguration = bucket.programConfigurations.get(layer.id); const program = painter.useProgram('heatmap', programConfiguration); - const projectionData = transform.getProjectionData({overscaledTileID: coord}); + const projectionData = transform.getProjectionData({overscaledTileID: coord, applyGlobeMatrix: true, applyTerrainMatrix: false}); const radiusCorrectionFactor = transform.getCircleRadiusCorrection(); @@ -119,8 +120,7 @@ function renderHeatmapFlat(painter: Painter, layer: HeatmapStyleLayer) { painter.viewportSegments, layer.paint, painter.transform.zoom); } -function prepareHeatmapTerrain(painter: Painter, tile: Tile, layer: HeatmapStyleLayer, coord: OverscaledTileID) { - const isGlobe = painter.style.projection.name === 'globe'; +function prepareHeatmapTerrain(painter: Painter, tile: Tile, layer: HeatmapStyleLayer, coord: OverscaledTileID, isRenderingGlobe: boolean) { const context = painter.context; const gl = context.gl; @@ -144,9 +144,9 @@ function prepareHeatmapTerrain(painter: Painter, tile: Tile, layer: HeatmapStyle context.clear({color: Color.transparent}); const programConfiguration = bucket.programConfigurations.get(layer.id); - const program = painter.useProgram('heatmap', programConfiguration, !isGlobe); + const program = painter.useProgram('heatmap', programConfiguration, !isRenderingGlobe); - const projectionData = painter.transform.getProjectionData({overscaledTileID: tile.tileID}); + const projectionData = painter.transform.getProjectionData({overscaledTileID: tile.tileID, applyGlobeMatrix: true, applyTerrainMatrix: true}); const terrainData = painter.style.map.terrain.getTerrainData(coord); program.draw(context, gl.TRIANGLES, DepthMode.disabled, stencilMode, colorMode, CullFaceMode.disabled, @@ -156,8 +156,7 @@ function prepareHeatmapTerrain(painter: Painter, tile: Tile, layer: HeatmapStyle programConfiguration); } -function renderHeatmapTerrain(painter: Painter, layer: HeatmapStyleLayer, coord: OverscaledTileID) { - const isGlobe = painter.style.projection.name === 'globe'; +function renderHeatmapTerrain(painter: Painter, layer: HeatmapStyleLayer, coord: OverscaledTileID, isRenderingToTexture: boolean, isRenderingGlobe: boolean) { const context = painter.context; const gl = context.gl; const transform = painter.transform; @@ -179,7 +178,7 @@ function renderHeatmapTerrain(painter: Painter, layer: HeatmapStyleLayer, coord: context.activeTexture.set(gl.TEXTURE1); colorRampTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); - const projectionData = transform.getProjectionData({overscaledTileID: coord, ignoreTerrainMatrix: !isGlobe}); + const projectionData = transform.getProjectionData({overscaledTileID: coord, applyTerrainMatrix: isRenderingGlobe, applyGlobeMatrix: !isRenderingToTexture}); painter.useProgram('heatmapTexture').draw(context, gl.TRIANGLES, DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled, diff --git a/src/render/draw_hillshade.ts b/src/render/draw_hillshade.ts index 97238604ca..6f7395cbc2 100644 --- a/src/render/draw_hillshade.ts +++ b/src/render/draw_hillshade.ts @@ -8,14 +8,15 @@ import { hillshadeUniformPrepareValues } from './program/hillshade_program'; -import type {Painter} from './painter'; +import type {Painter, RenderOptions} from './painter'; import type {SourceCache} from '../source/source_cache'; import type {HillshadeStyleLayer} from '../style/style_layer/hillshade_style_layer'; import type {OverscaledTileID} from '../source/tile_id'; -export function drawHillshade(painter: Painter, sourceCache: SourceCache, layer: HillshadeStyleLayer, tileIDs: Array) { +export function drawHillshade(painter: Painter, sourceCache: SourceCache, layer: HillshadeStyleLayer, tileIDs: Array, renderOptions: RenderOptions) { if (painter.renderPass !== 'offscreen' && painter.renderPass !== 'translucent') return; + const {isRenderingToTexture} = renderOptions; const context = painter.context; const projection = painter.style.projection; const useSubdivision = projection.useSubdivision; @@ -33,12 +34,12 @@ export function drawHillshade(painter: Painter, sourceCache: SourceCache, layer: if (useSubdivision) { // Two-pass rendering const [stencilBorderless, stencilBorders, coords] = painter.stencilConfigForOverlapTwoPass(tileIDs); - renderHillshade(painter, sourceCache, layer, coords, stencilBorderless, depthMode, colorMode, false); // draw without borders - renderHillshade(painter, sourceCache, layer, coords, stencilBorders, depthMode, colorMode, true); // draw with borders + renderHillshade(painter, sourceCache, layer, coords, stencilBorderless, depthMode, colorMode, false, isRenderingToTexture); // draw without borders + renderHillshade(painter, sourceCache, layer, coords, stencilBorders, depthMode, colorMode, true, isRenderingToTexture); // draw with borders } else { // Simple rendering const [stencil, coords] = painter.stencilConfigForOverlap(tileIDs); - renderHillshade(painter, sourceCache, layer, coords, stencil, depthMode, colorMode, false); + renderHillshade(painter, sourceCache, layer, coords, stencil, depthMode, colorMode, false, isRenderingToTexture); } } } @@ -51,7 +52,8 @@ function renderHillshade( stencilModes: {[_: number]: Readonly}, depthMode: Readonly, colorMode: Readonly, - useBorder: boolean + useBorder: boolean, + isRenderingToTexture: boolean ) { const projection = painter.style.projection; const context = painter.context; @@ -73,12 +75,11 @@ function renderHillshade( context.activeTexture.set(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get()); - const globeWithTerrain = painter.style.map.terrain && painter.style.projection.name === 'globe'; - const projectionData = transform.getProjectionData({ overscaledTileID: coord, aligned: align, - ignoreGlobeMatrix: globeWithTerrain + applyGlobeMatrix: !isRenderingToTexture, + applyTerrainMatrix: true }); program.draw(context, gl.TRIANGLES, depthMode, stencilModes[coord.overscaledZ], colorMode, CullFaceMode.backCCW, diff --git a/src/render/draw_line.ts b/src/render/draw_line.ts index 1237ecac95..ee30039ce7 100644 --- a/src/render/draw_line.ts +++ b/src/render/draw_line.ts @@ -8,7 +8,7 @@ import { lineGradientUniformValues } from './program/line_program'; -import type {Painter} from './painter'; +import type {Painter, RenderOptions} from './painter'; import type {SourceCache} from '../source/source_cache'; import type {LineStyleLayer} from '../style/style_layer/line_style_layer'; import type {LineBucket} from '../data/bucket/line_bucket'; @@ -17,16 +17,17 @@ import {clamp, nextPowerOfTwo} from '../util/util'; import {renderColorRamp} from '../util/color_ramp'; import {EXTENT} from '../data/extent'; -export function drawLine(painter: Painter, sourceCache: SourceCache, layer: LineStyleLayer, coords: Array) { +export function drawLine(painter: Painter, sourceCache: SourceCache, layer: LineStyleLayer, coords: Array, renderOptions: RenderOptions) { if (painter.renderPass !== 'translucent') return; + const {isRenderingToTexture} = renderOptions; + const opacity = layer.paint.get('line-opacity'); const width = layer.paint.get('line-width'); if (opacity.constantOr(1) === 0 || width.constantOr(1) === 0) return; const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly); const colorMode = painter.colorModeForRenderPass(); - const globeWithTerrain = !!painter.style.map.terrain && painter.style.projection.name === 'globe'; const dasharray = layer.paint.get('line-dasharray'); const patternProperty = layer.paint.get('line-pattern'); @@ -70,7 +71,8 @@ export function drawLine(painter: Painter, sourceCache: SourceCache, layer: Line const projectionData = transform.getProjectionData({ overscaledTileID: coord, - ignoreGlobeMatrix: globeWithTerrain + applyGlobeMatrix: !isRenderingToTexture, + applyTerrainMatrix: true }); const pixelRatio = transform.getPixelScale(); @@ -123,7 +125,7 @@ export function drawLine(painter: Painter, sourceCache: SourceCache, layer: Line } const [stencilModes] = painter.stencilConfigForOverlap(coords); - const stencil = globeWithTerrain ? stencilModes[coord.overscaledZ] : painter.stencilModeForClipping(coord); + const stencil = isRenderingToTexture ? stencilModes[coord.overscaledZ] : painter.stencilModeForClipping(coord); program.draw(context, gl.TRIANGLES, depthMode, stencil, colorMode, CullFaceMode.disabled, uniformValues, terrainData, projectionData, diff --git a/src/render/draw_raster.ts b/src/render/draw_raster.ts index 3fde7be215..d8796f5df2 100644 --- a/src/render/draw_raster.ts +++ b/src/render/draw_raster.ts @@ -10,7 +10,7 @@ import {EXTENT} from '../data/extent'; import {coveringZoomLevel} from '../geo/projection/covering_tiles'; import Point from '@mapbox/point-geometry'; -import type {Painter} from './painter'; +import type {Painter, RenderOptions} from './painter'; import type {SourceCache} from '../source/source_cache'; import type {RasterStyleLayer} from '../style/style_layer/raster_style_layer'; import type {OverscaledTileID} from '../source/tile_id'; @@ -25,11 +25,12 @@ const cornerCoords = [ new Point(0, EXTENT), ]; -export function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterStyleLayer, tileIDs: Array, isRenderingToTexture: boolean = false) { +export function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterStyleLayer, tileIDs: Array, renderOptions: RenderOptions) { if (painter.renderPass !== 'translucent') return; if (layer.paint.get('raster-opacity') === 0) return; if (!tileIDs.length) return; + const {isRenderingToTexture} = renderOptions; const source = sourceCache.getSource(); const projection = painter.style.projection; @@ -125,7 +126,7 @@ function drawTiles( } const terrainData = painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord); - const projectionData = transform.getProjectionData({overscaledTileID: coord, aligned: align, ignoreGlobeMatrix: isRenderingToTexture}); + const projectionData = transform.getProjectionData({overscaledTileID: coord, aligned: align, applyGlobeMatrix: !isRenderingToTexture, applyTerrainMatrix: true}); const uniformValues = rasterUniformValues(parentTL || [0, 0], parentScaleBy || 1, fade, layer, corners); const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, allowPoles, 'raster'); diff --git a/src/render/draw_sky.ts b/src/render/draw_sky.ts index 83b28b5b5f..25578c9ec0 100644 --- a/src/render/draw_sky.ts +++ b/src/render/draw_sky.ts @@ -85,7 +85,7 @@ export function drawAtmosphere(painter: Painter, sky: Sky, light: Light) { const sunPos = getSunPos(light, painter.transform); - const projectionData = transform.getProjectionData({overscaledTileID: null}); + const projectionData = transform.getProjectionData({overscaledTileID: null, applyGlobeMatrix: true, applyTerrainMatrix: true}); const atmosphereBlend = sky.properties.get('atmosphere-blend') * projectionData.projectionTransition; if (atmosphereBlend === 0) { diff --git a/src/render/draw_symbol.test.ts b/src/render/draw_symbol.test.ts index 734edc3e3f..111557c9da 100644 --- a/src/render/draw_symbol.test.ts +++ b/src/render/draw_symbol.test.ts @@ -4,7 +4,7 @@ import {SymbolBucket} from '../data/bucket/symbol_bucket'; import {SourceCache} from '../source/source_cache'; import {Tile} from '../source/tile'; import {SymbolStyleLayer} from '../style/style_layer/symbol_style_layer'; -import {Painter} from './painter'; +import {Painter, RenderOptions} from './painter'; import {Program} from './program'; import {drawSymbols} from './draw_symbol'; import * as symbolProjection from '../symbol/projection'; @@ -49,7 +49,8 @@ describe('drawSymbol', () => { const mockPainter = new Painter(null, null); mockPainter.renderPass = 'opaque'; - drawSymbols(mockPainter, null, null, null, null); + const renderOptions: RenderOptions = {isRenderingToTexture: false, isRenderingGlobe: false}; + drawSymbols(mockPainter, null, null, null, null, renderOptions); expect(mockPainter.colorModeForRenderPass).not.toHaveBeenCalled(); }); @@ -110,7 +111,8 @@ describe('drawSymbol', () => { sourceCacheMock.map = {showCollisionBoxes: false} as any as Map; sourceCacheMock.getTile = (_a) => tile; - drawSymbols(painterMock, sourceCacheMock, layer, [tileId], null); + const renderOptions: RenderOptions = {isRenderingToTexture: false, isRenderingGlobe: false}; + drawSymbols(painterMock, sourceCacheMock, layer, [tileId], null, renderOptions); expect(programMock.draw).toHaveBeenCalledTimes(1); }); @@ -177,7 +179,8 @@ describe('drawSymbol', () => { } as any as Style; const spy = jest.spyOn(symbolProjection, 'updateLineLabels'); - drawSymbols(painterMock, sourceCacheMock, layer, [tileId], null); + const renderOptions: RenderOptions = {isRenderingToTexture: false, isRenderingGlobe: false}; + drawSymbols(painterMock, sourceCacheMock, layer, [tileId], null, renderOptions); expect(spy.mock.calls[0][7]).toBeFalsy(); // rotateToLine === false }); @@ -238,7 +241,8 @@ describe('drawSymbol', () => { (sourceCacheMock.getTile as jest.Mock).mockReturnValue(tile); sourceCacheMock.map = {showCollisionBoxes: false} as any as Map; - drawSymbols(painterMock, sourceCacheMock, layer, [tileId], null); + const renderOptions: RenderOptions = {isRenderingToTexture: false, isRenderingGlobe: false}; + drawSymbols(painterMock, sourceCacheMock, layer, [tileId], null, renderOptions); expect(programMock.draw).toHaveBeenCalledTimes(0); }); diff --git a/src/render/draw_symbol.ts b/src/render/draw_symbol.ts index bef88de127..9c59643aa3 100644 --- a/src/render/draw_symbol.ts +++ b/src/render/draw_symbol.ts @@ -20,7 +20,7 @@ import { symbolTextAndIconUniformValues } from './program/symbol_program'; -import type {Painter} from './painter'; +import type {Painter, RenderOptions} from './painter'; import type {SourceCache} from '../source/source_cache'; import type {SymbolStyleLayer} from '../style/style_layer/symbol_style_layer'; @@ -62,9 +62,10 @@ const identityMat4 = mat4.identity(new Float32Array(16)); export function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: SymbolStyleLayer, coords: Array, variableOffsets: { [_ in CrossTileID]: VariableOffset; -}) { +}, renderOptions: RenderOptions) { if (painter.renderPass !== 'translucent') return; + const {isRenderingToTexture} = renderOptions; // Disable the stencil test so that labels aren't clipped to tile boundaries. const stencilMode = StencilMode.disabled; const colorMode = painter.colorModeForRenderPass(); @@ -89,7 +90,7 @@ export function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: S layer.layout.get('icon-rotation-alignment'), layer.layout.get('icon-pitch-alignment'), layer.layout.get('icon-keep-upright'), - stencilMode, colorMode + stencilMode, colorMode, isRenderingToTexture ); } @@ -100,7 +101,7 @@ export function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: S layer.layout.get('text-rotation-alignment'), layer.layout.get('text-pitch-alignment'), layer.layout.get('text-keep-upright'), - stencilMode, colorMode + stencilMode, colorMode, isRenderingToTexture ); } @@ -303,7 +304,8 @@ function drawLayerSymbols( pitchAlignment: SymbolLayerSpecification['layout']['text-pitch-alignment'], keepUpright: boolean, stencilMode: StencilMode, - colorMode: Readonly) { + colorMode: Readonly, + isRenderingToTexture: boolean) { const context = painter.context; const gl = context.gl; @@ -379,7 +381,7 @@ function drawLayerSymbols( const glCoordMatrixForShader = getGlCoordMatrix(pitchWithMap, rotateWithMap, painter.transform, s); const translation = translatePosition(transform, tile, translate, translateAnchor); - const projectionData = transform.getProjectionData({overscaledTileID: coord}); + const projectionData = transform.getProjectionData({overscaledTileID: coord, applyGlobeMatrix: !isRenderingToTexture, applyTerrainMatrix: true}); const hasVariableAnchors = hasVariablePlacement && bucket.hasTextData(); const updateTextFitIcon = layer.layout.get('icon-text-fit') !== 'none' && diff --git a/src/render/draw_terrain.ts b/src/render/draw_terrain.ts index a81f49244e..fcbe70cc44 100644 --- a/src/render/draw_terrain.ts +++ b/src/render/draw_terrain.ts @@ -1,7 +1,7 @@ import {StencilMode} from '../gl/stencil_mode'; import {DepthMode} from '../gl/depth_mode'; import {terrainUniformValues, terrainDepthUniformValues, terrainCoordsUniformValues} from './program/terrain_program'; -import type {Painter} from './painter'; +import type {Painter, RenderOptions} from './painter'; import type {Tile} from '../source/tile'; import {CullFaceMode} from '../gl/cull_face_mode'; import {Color} from '@maplibre/maplibre-gl-style-spec'; @@ -27,7 +27,7 @@ function drawDepth(painter: Painter, terrain: Terrain) { context.clear({color: Color.transparent, depth: 1}); for (const tile of tiles) { const terrainData = terrain.getTerrainData(tile.tileID); - const projectionData = tr.getProjectionData({overscaledTileID: tile.tileID, ignoreTerrainMatrix: true}); + const projectionData = tr.getProjectionData({overscaledTileID: tile.tileID, applyTerrainMatrix: false, applyGlobeMatrix: true}); const uniformValues = terrainDepthUniformValues(terrain.getMeshFrameDelta(tr.zoom)); program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, 'terrain', mesh.vertexBuffer, mesh.indexBuffer, mesh.segments); } @@ -61,7 +61,7 @@ function drawCoords(painter: Painter, terrain: Terrain) { context.activeTexture.set(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, coords.texture); const uniformValues = terrainCoordsUniformValues(255 - terrain.coordsIndex.length, terrain.getMeshFrameDelta(tr.zoom)); - const projectionData = tr.getProjectionData({overscaledTileID: tile.tileID, ignoreTerrainMatrix: true}); + const projectionData = tr.getProjectionData({overscaledTileID: tile.tileID, applyTerrainMatrix: false, applyGlobeMatrix: true}); program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, 'terrain', mesh.vertexBuffer, mesh.indexBuffer, mesh.segments); terrain.coordsIndex.push(tile.tileID.key); } @@ -69,7 +69,8 @@ function drawCoords(painter: Painter, terrain: Terrain) { context.viewport.set([0, 0, painter.width, painter.height]); } -function drawTerrain(painter: Painter, terrain: Terrain, tiles: Array) { +function drawTerrain(painter: Painter, terrain: Terrain, tiles: Array, renderOptions: RenderOptions) { + const {isRenderingGlobe} = renderOptions; const context = painter.context; const gl = context.gl; const tr = painter.transform; @@ -88,8 +89,8 @@ function drawTerrain(painter: Painter, terrain: Terrain, tiles: Array) { gl.bindTexture(gl.TEXTURE_2D, texture.texture); const eleDelta = terrain.getMeshFrameDelta(tr.zoom); const fogMatrix = tr.calculateFogMatrix(tile.tileID.toUnwrapped()); - const uniformValues = terrainUniformValues(eleDelta, fogMatrix, painter.style.sky, tr.pitch, painter.style.projection?.name === 'globe'); - const projectionData = tr.getProjectionData({overscaledTileID: tile.tileID, ignoreTerrainMatrix: true}); + const uniformValues = terrainUniformValues(eleDelta, fogMatrix, painter.style.sky, tr.pitch, isRenderingGlobe); + const projectionData = tr.getProjectionData({overscaledTileID: tile.tileID, applyTerrainMatrix: false, applyGlobeMatrix: true}); program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, 'terrain', mesh.vertexBuffer, mesh.indexBuffer, mesh.segments); } } diff --git a/src/render/painter.ts b/src/render/painter.ts index 716f5bac97..853375a14f 100644 --- a/src/render/painter.ts +++ b/src/render/painter.ts @@ -49,6 +49,16 @@ import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec'; import type {RenderToTexture} from './render_to_texture'; import type {ProjectionData} from '../geo/projection/projection_data'; import {coveringTiles} from '../geo/projection/covering_tiles'; +import {isSymbolStyleLayer} from '../style/style_layer/symbol_style_layer'; +import {isCircleStyleLayer} from '../style/style_layer/circle_style_layer'; +import {isHeatmapStyleLayer} from '../style/style_layer/heatmap_style_layer'; +import {isLineStyleLayer} from '../style/style_layer/line_style_layer'; +import {isFillStyleLayer} from '../style/style_layer/fill_style_layer'; +import {isFillExtrusionStyleLayer} from '../style/style_layer/fill_extrusion_style_layer'; +import {isHillshadeStyleLayer} from '../style/style_layer/hillshade_style_layer'; +import {isRasterStyleLayer} from '../style/style_layer/raster_style_layer'; +import {isBackgroundStyleLayer} from '../style/style_layer/background_style_layer'; +import {isCustomStyleLayer} from '../style/style_layer/custom_style_layer'; export type RenderPass = 'offscreen' | 'opaque' | 'translucent'; @@ -62,6 +72,11 @@ type PainterOptions = { fadeDuration: number; }; +export type RenderOptions = { + isRenderingToTexture: boolean; + isRenderingGlobe: boolean; +} + /** * @internal * Initialize a new painter object. @@ -298,7 +313,7 @@ export class Painter { const mesh = projection.getMeshFromTileID(this.context, tileID.canonical, useBorders, true, 'stencil'); - const projectionData = transform.getProjectionData({overscaledTileID: tileID}); + const projectionData = transform.getProjectionData({overscaledTileID: tileID, applyGlobeMatrix: true, applyTerrainMatrix: true}); program.draw(context, gl.TRIANGLES, DepthMode.disabled, // Tests will always pass, and ref value will be written to stencil buffer. @@ -328,7 +343,7 @@ export class Painter { const terrainData = this.style.map.terrain && this.style.map.terrain.getTerrainData(tileID); const mesh = projection.getMeshFromTileID(this.context, tileID.canonical, true, true, 'raster'); - const projectionData = transform.getProjectionData({overscaledTileID: tileID}); + const projectionData = transform.getProjectionData({overscaledTileID: tileID, applyGlobeMatrix: true, applyTerrainMatrix: true}); program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, ColorMode.disabled, CullFaceMode.backCCW, null, @@ -474,6 +489,7 @@ export class Painter { const coordsAscending: {[_: string]: Array} = {}; const coordsDescending: {[_: string]: Array} = {}; const coordsDescendingSymbol: {[_: string]: Array} = {}; + const renderOptions: RenderOptions = {isRenderingToTexture: false, isRenderingGlobe: style.projection.name === 'globe'}; for (const id in sourceCaches) { const sourceCache = sourceCaches[id]; @@ -516,7 +532,7 @@ export class Painter { const coords = coordsDescending[layer.source]; if (layer.type !== 'custom' && !coords.length) continue; - this.renderLayer(this, sourceCaches[layer.source], layer, coords); + this.renderLayer(this, sourceCaches[layer.source], layer, coords, renderOptions); } // Execute offscreen GPU tasks of the projection manager @@ -550,7 +566,7 @@ export class Painter { const coords = coordsAscending[layer.source]; this._renderTileClippingMasks(layer, coords, false); - this.renderLayer(this, sourceCache, layer, coords); + this.renderLayer(this, sourceCache, layer, coords, renderOptions); } } @@ -564,13 +580,13 @@ export class Painter { const layer = this.style._layers[layerIds[this.currentLayer]]; const sourceCache = sourceCaches[layer.source]; - if (this.renderToTexture && this.renderToTexture.renderLayer(layer)) continue; + if (this.renderToTexture && this.renderToTexture.renderLayer(layer, renderOptions)) continue; if (!this.opaquePassEnabledForLayer() && !globeDepthRendered) { globeDepthRendered = true; // Render the globe sphere into the depth buffer - but only if globe is enabled and terrain is disabled. // There should be no need for explicitly writing tile depths when terrain is enabled. - if (this.style.projection.name === 'globe' && !this.style.map.terrain) { + if (renderOptions.isRenderingGlobe && !this.style.map.terrain) { this._renderTilesDepthBuffer(); } } @@ -581,11 +597,11 @@ export class Painter { const coords = (layer.type === 'symbol' ? coordsDescendingSymbol : coordsDescending)[layer.source]; this._renderTileClippingMasks(layer, coordsAscending[layer.source], false); - this.renderLayer(this, sourceCache, layer, coords); + this.renderLayer(this, sourceCache, layer, coords, renderOptions); } // Render atmosphere, only for Globe projection - if (this.style.projection.name === 'globe') { + if (renderOptions.isRenderingGlobe) { drawAtmosphere(this, this.style.sky, this.style.light); } @@ -633,42 +649,31 @@ export class Painter { drawCoords(this, this.style.map.terrain); } - renderLayer(painter: Painter, sourceCache: SourceCache, layer: StyleLayer, coords: Array, isRenderingToTexture: boolean = false) { + renderLayer(painter: Painter, sourceCache: SourceCache, layer: StyleLayer, coords: Array, renderOptions: RenderOptions) { if (layer.isHidden(this.transform.zoom)) return; if (layer.type !== 'background' && layer.type !== 'custom' && !(coords || []).length) return; this.id = layer.id; - switch (layer.type) { - case 'symbol': - drawSymbols(painter, sourceCache, layer as any, coords, this.style.placement.variableOffsets); - break; - case 'circle': - drawCircles(painter, sourceCache, layer as any, coords); - break; - case 'heatmap': - drawHeatmap(painter, sourceCache, layer as any, coords); - break; - case 'line': - drawLine(painter, sourceCache, layer as any, coords); - break; - case 'fill': - drawFill(painter, sourceCache, layer as any, coords); - break; - case 'fill-extrusion': - drawFillExtrusion(painter, sourceCache, layer as any, coords); - break; - case 'hillshade': - drawHillshade(painter, sourceCache, layer as any, coords); - break; - case 'raster': - drawRaster(painter, sourceCache, layer as any, coords, isRenderingToTexture); - break; - case 'background': - drawBackground(painter, sourceCache, layer as any, coords); - break; - case 'custom': - drawCustom(painter, sourceCache, layer as any); - break; + if (isSymbolStyleLayer(layer)) { + drawSymbols(painter, sourceCache, layer, coords, this.style.placement.variableOffsets, renderOptions); + } else if (isCircleStyleLayer(layer)) { + drawCircles(painter, sourceCache, layer, coords, renderOptions); + } else if (isHeatmapStyleLayer(layer)) { + drawHeatmap(painter, sourceCache, layer, coords, renderOptions); + } else if (isLineStyleLayer(layer)) { + drawLine(painter, sourceCache, layer, coords, renderOptions); + } else if (isFillStyleLayer(layer)) { + drawFill(painter, sourceCache, layer, coords, renderOptions); + } else if (isFillExtrusionStyleLayer(layer)) { + drawFillExtrusion(painter, sourceCache, layer, coords, renderOptions); + } else if (isHillshadeStyleLayer(layer)) { + drawHillshade(painter, sourceCache, layer, coords, renderOptions); + } else if (isRasterStyleLayer(layer)) { + drawRaster(painter, sourceCache, layer, coords, renderOptions); + } else if (isBackgroundStyleLayer(layer)) { + drawBackground(painter, sourceCache, layer, coords, renderOptions); + } else if (isCustomStyleLayer(layer)) { + drawCustom(painter, sourceCache, layer, renderOptions); } } diff --git a/src/render/program/sky_program.ts b/src/render/program/sky_program.ts index ccf77d2c00..73e9997908 100644 --- a/src/render/program/sky_program.ts +++ b/src/render/program/sky_program.ts @@ -27,7 +27,7 @@ const skyUniformValues = (sky: Sky, transform: IReadonlyTransform, pixelRatio: n const cosRoll = Math.cos(transform.rollInRadians); const sinRoll = Math.sin(transform.rollInRadians); const mercatorHorizon = getMercatorHorizon(transform); - const projectionData = transform.getProjectionData({overscaledTileID: null}); + const projectionData = transform.getProjectionData({overscaledTileID: null, applyGlobeMatrix: true, applyTerrainMatrix: true}); const skyBlend = projectionData.projectionTransition; return { 'u_sky_color': sky.properties.get('sky-color'), diff --git a/src/render/render_to_texture.test.ts b/src/render/render_to_texture.test.ts index 0f807ce680..f01313a5a9 100644 --- a/src/render/render_to_texture.test.ts +++ b/src/render/render_to_texture.test.ts @@ -133,9 +133,10 @@ describe('render to texture', () => { style._order = ['maine-fill', 'maine-symbol']; rtt.prepareForRender(style, 0); layersDrawn = 0; + const renderOptions = {isRenderingToTexture: false, isRenderingGlobe: false}; expect(rtt._renderableLayerIds).toStrictEqual(['maine-fill', 'maine-symbol']); - expect(rtt.renderLayer(fillLayer)).toBeTruthy(); - expect(rtt.renderLayer(symbolLayer)).toBeFalsy(); + expect(rtt.renderLayer(fillLayer, renderOptions)).toBeTruthy(); + expect(rtt.renderLayer(symbolLayer, renderOptions)).toBeFalsy(); expect(layersDrawn).toBe(1); }); @@ -143,14 +144,15 @@ describe('render to texture', () => { style._order = ['maine-background', 'maine-fill', 'maine-raster', 'maine-hillshade', 'maine-symbol', 'maine-line', 'maine-symbol']; rtt.prepareForRender(style, 0); layersDrawn = 0; + const renderOptions = {isRenderingToTexture: false, isRenderingGlobe: false}; expect(rtt._renderableLayerIds).toStrictEqual(['maine-background', 'maine-fill', 'maine-raster', 'maine-hillshade', 'maine-symbol', 'maine-line', 'maine-symbol']); - expect(rtt.renderLayer(backgroundLayer)).toBeTruthy(); - expect(rtt.renderLayer(fillLayer)).toBeTruthy(); - expect(rtt.renderLayer(rasterLayer)).toBeTruthy(); - expect(rtt.renderLayer(hillshadeLayer)).toBeTruthy(); - expect(rtt.renderLayer(symbolLayer)).toBeFalsy(); - expect(rtt.renderLayer(lineLayer)).toBeTruthy(); - expect(rtt.renderLayer(symbolLayer)).toBeFalsy(); + expect(rtt.renderLayer(backgroundLayer, renderOptions)).toBeTruthy(); + expect(rtt.renderLayer(fillLayer, renderOptions)).toBeTruthy(); + expect(rtt.renderLayer(rasterLayer, renderOptions)).toBeTruthy(); + expect(rtt.renderLayer(hillshadeLayer, renderOptions)).toBeTruthy(); + expect(rtt.renderLayer(symbolLayer, renderOptions)).toBeFalsy(); + expect(rtt.renderLayer(lineLayer, renderOptions)).toBeTruthy(); + expect(rtt.renderLayer(symbolLayer, renderOptions)).toBeFalsy(); expect(layersDrawn).toBe(2); }); @@ -158,13 +160,14 @@ describe('render to texture', () => { style._order = ['maine-background', 'maine-symbol', 'maine-hillshade', 'maine-symbol', 'maine-line', 'maine-symbol']; rtt.prepareForRender(style, 0); layersDrawn = 0; + const renderOptions = {isRenderingToTexture: false, isRenderingGlobe: false}; expect(rtt._renderableLayerIds).toStrictEqual(['maine-background', 'maine-symbol', 'maine-hillshade', 'maine-symbol', 'maine-line', 'maine-symbol']); - expect(rtt.renderLayer(backgroundLayer)).toBeTruthy(); - expect(rtt.renderLayer(symbolLayer)).toBeFalsy(); - expect(rtt.renderLayer(hillshadeLayer)).toBeTruthy(); - expect(rtt.renderLayer(symbolLayer)).toBeFalsy(); - expect(rtt.renderLayer(lineLayer)).toBeTruthy(); - expect(rtt.renderLayer(symbolLayer)).toBeFalsy(); + expect(rtt.renderLayer(backgroundLayer, renderOptions)).toBeTruthy(); + expect(rtt.renderLayer(symbolLayer, renderOptions)).toBeFalsy(); + expect(rtt.renderLayer(hillshadeLayer, renderOptions)).toBeTruthy(); + expect(rtt.renderLayer(symbolLayer, renderOptions)).toBeFalsy(); + expect(rtt.renderLayer(lineLayer, renderOptions)).toBeTruthy(); + expect(rtt.renderLayer(symbolLayer, renderOptions)).toBeFalsy(); expect(layersDrawn).toBe(3); }); }); diff --git a/src/render/render_to_texture.ts b/src/render/render_to_texture.ts index 8c4fd96b30..eac254e3b1 100644 --- a/src/render/render_to_texture.ts +++ b/src/render/render_to_texture.ts @@ -1,4 +1,4 @@ -import {Painter} from './painter'; +import {Painter, RenderOptions} from './painter'; import {Tile} from '../source/tile'; import {Color} from '@maplibre/maplibre-gl-style-spec'; import {OverscaledTileID} from '../source/tile_id'; @@ -126,11 +126,13 @@ export class RenderToTexture { * and 'live'-layers (f.e. symbols) it is necessary to create more stacks. For example * a symbol-layer is in between of fill-layers. * @param layer - the layer to render + * @param renderOptions - flags describing how to render the layer * @returns if true layer is rendered to texture, otherwise false */ - renderLayer(layer: StyleLayer): boolean { + renderLayer(layer: StyleLayer, renderOptions: RenderOptions): boolean { if (layer.isHidden(this.painter.transform.zoom)) return false; + const options: RenderOptions = {...renderOptions, isRenderingToTexture: true}; const type = layer.type; const painter = this.painter; const isLastLayer = this._renderableLayerIds[this._renderableLayerIds.length - 1] === layer.id; @@ -153,7 +155,7 @@ export class RenderToTexture { for (const tile of this._renderableTiles) { // if render pool is full draw current tiles to screen and free pool if (this.pool.isFull()) { - drawTerrain(this.painter, this.terrain, this._rttTiles); + drawTerrain(this.painter, this.terrain, this._rttTiles, options); this._rttTiles = []; this.pool.freeAllObjects(); } @@ -180,11 +182,11 @@ export class RenderToTexture { const coords = layer.source ? this._coordsAscending[layer.source][tile.tileID.key] : [tile.tileID]; painter.context.viewport.set([0, 0, obj.fbo.width, obj.fbo.height]); painter._renderTileClippingMasks(layer, coords, true); - painter.renderLayer(painter, painter.style.sourceCaches[layer.source], layer, coords, true); + painter.renderLayer(painter, painter.style.sourceCaches[layer.source], layer, coords, options); if (layer.source) tile.rttCoords[layer.source] = this._coordsAscendingStr[layer.source][tile.tileID.key]; } } - drawTerrain(this.painter, this.terrain, this._rttTiles); + drawTerrain(this.painter, this.terrain, this._rttTiles, options); this._rttTiles = []; this.pool.freeAllObjects(); diff --git a/src/style/style_layer/background_style_layer.ts b/src/style/style_layer/background_style_layer.ts index 412c04a878..1be655cc95 100644 --- a/src/style/style_layer/background_style_layer.ts +++ b/src/style/style_layer/background_style_layer.ts @@ -6,6 +6,8 @@ import {Transitionable, Transitioning, PossiblyEvaluated} from '../properties'; import type {BackgroundPaintProps} from './background_style_layer_properties.g'; import type {LayerSpecification} from '@maplibre/maplibre-gl-style-spec'; +export const isBackgroundStyleLayer = (layer: StyleLayer): layer is BackgroundStyleLayer => layer.type === 'background'; + export class BackgroundStyleLayer extends StyleLayer { _transitionablePaint: Transitionable; _transitioningPaint: Transitioning; diff --git a/src/style/style_layer/circle_style_layer.ts b/src/style/style_layer/circle_style_layer.ts index d5755e34c2..dcb0e812a0 100644 --- a/src/style/style_layer/circle_style_layer.ts +++ b/src/style/style_layer/circle_style_layer.ts @@ -13,6 +13,8 @@ import type {Bucket, BucketParameters} from '../../data/bucket'; import type {CircleLayoutProps, CirclePaintProps} from './circle_style_layer_properties.g'; import type {VectorTileFeature} from '@mapbox/vector-tile'; +export const isCircleStyleLayer = (layer: StyleLayer): layer is CircleStyleLayer => layer.type === 'circle'; + /** * A style layer that defines a circle */ diff --git a/src/style/style_layer/custom_style_layer.ts b/src/style/style_layer/custom_style_layer.ts index 95c41410c8..28fcf05b54 100644 --- a/src/style/style_layer/custom_style_layer.ts +++ b/src/style/style_layer/custom_style_layer.ts @@ -260,6 +260,8 @@ export function validateCustomStyleLayer(layerObject: CustomLayerInterface) { return errors; } +export const isCustomStyleLayer = (layer: StyleLayer): layer is CustomStyleLayer => layer.type === 'custom'; + export class CustomStyleLayer extends StyleLayer { implementation: CustomLayerInterface; diff --git a/src/style/style_layer/fill_extrusion_style_layer.ts b/src/style/style_layer/fill_extrusion_style_layer.ts index 4a5086ee98..adbeeac8bb 100644 --- a/src/style/style_layer/fill_extrusion_style_layer.ts +++ b/src/style/style_layer/fill_extrusion_style_layer.ts @@ -17,6 +17,8 @@ export class Point3D extends Point { z: number; } +export const isFillExtrusionStyleLayer = (layer: StyleLayer): layer is FillExtrusionStyleLayer => layer.type === 'fill-extrusion'; + export class FillExtrusionStyleLayer extends StyleLayer { _transitionablePaint: Transitionable; _transitioningPaint: Transitioning; diff --git a/src/style/style_layer/fill_style_layer.ts b/src/style/style_layer/fill_style_layer.ts index 5b7cff86ae..46296aee26 100644 --- a/src/style/style_layer/fill_style_layer.ts +++ b/src/style/style_layer/fill_style_layer.ts @@ -14,6 +14,8 @@ import type {EvaluationParameters} from '../evaluation_parameters'; import type {IReadonlyTransform} from '../../geo/transform_interface'; import type {VectorTileFeature} from '@mapbox/vector-tile'; +export const isFillStyleLayer = (layer: StyleLayer): layer is FillStyleLayer => layer.type === 'fill'; + export class FillStyleLayer extends StyleLayer { _unevaluatedLayout: Layout; layout: PossiblyEvaluated; diff --git a/src/style/style_layer/heatmap_style_layer.ts b/src/style/style_layer/heatmap_style_layer.ts index cab5240371..a2d3f57aad 100644 --- a/src/style/style_layer/heatmap_style_layer.ts +++ b/src/style/style_layer/heatmap_style_layer.ts @@ -13,6 +13,8 @@ import type {LayerSpecification} from '@maplibre/maplibre-gl-style-spec'; export const HEATMAP_FULL_RENDER_FBO_KEY = 'big-fb'; +export const isHeatmapStyleLayer = (layer: StyleLayer): layer is HeatmapStyleLayer => layer.type === 'heatmap'; + /** * A style layer that defines a heatmap */ diff --git a/src/style/style_layer/hillshade_style_layer.ts b/src/style/style_layer/hillshade_style_layer.ts index 43e841337b..3bf2e78265 100644 --- a/src/style/style_layer/hillshade_style_layer.ts +++ b/src/style/style_layer/hillshade_style_layer.ts @@ -6,6 +6,8 @@ import {Transitionable, Transitioning, PossiblyEvaluated} from '../properties'; import type {HillshadePaintProps} from './hillshade_style_layer_properties.g'; import type {LayerSpecification} from '@maplibre/maplibre-gl-style-spec'; +export const isHillshadeStyleLayer = (layer: StyleLayer): layer is HillshadeStyleLayer => layer.type === 'hillshade'; + export class HillshadeStyleLayer extends StyleLayer { _transitionablePaint: Transitionable; _transitioningPaint: Transitioning; diff --git a/src/style/style_layer/line_style_layer.ts b/src/style/style_layer/line_style_layer.ts index b462a56651..8bfe1cabc5 100644 --- a/src/style/style_layer/line_style_layer.ts +++ b/src/style/style_layer/line_style_layer.ts @@ -37,6 +37,8 @@ export class LineFloorwidthProperty extends DataDrivenProperty { let lineFloorwidthProperty: LineFloorwidthProperty; +export const isLineStyleLayer = (layer: StyleLayer): layer is LineStyleLayer => layer.type === 'line'; + export class LineStyleLayer extends StyleLayer { _unevaluatedLayout: Layout; layout: PossiblyEvaluated; diff --git a/src/style/style_layer/raster_style_layer.ts b/src/style/style_layer/raster_style_layer.ts index 407b11af7d..1135abc78b 100644 --- a/src/style/style_layer/raster_style_layer.ts +++ b/src/style/style_layer/raster_style_layer.ts @@ -6,6 +6,8 @@ import {Transitionable, Transitioning, PossiblyEvaluated} from '../properties'; import type {RasterPaintProps} from './raster_style_layer_properties.g'; import type {LayerSpecification} from '@maplibre/maplibre-gl-style-spec'; +export const isRasterStyleLayer = (layer: StyleLayer): layer is RasterStyleLayer => layer.type === 'raster'; + export class RasterStyleLayer extends StyleLayer { _transitionablePaint: Transitionable; _transitioningPaint: Transitioning; diff --git a/src/style/style_layer/symbol_style_layer.ts b/src/style/style_layer/symbol_style_layer.ts index 04ac67e2a7..01b35d4e3e 100644 --- a/src/style/style_layer/symbol_style_layer.ts +++ b/src/style/style_layer/symbol_style_layer.ts @@ -31,6 +31,8 @@ import type {Expression, Feature, SourceExpression, LayerSpecification} from '@m import type {CanonicalTileID} from '../../source/tile_id'; import {FormatSectionOverride} from '../format_section_override'; +export const isSymbolStyleLayer = (layer: StyleLayer): layer is SymbolStyleLayer => layer.type === 'symbol'; + export class SymbolStyleLayer extends StyleLayer { _unevaluatedLayout: Layout; layout: PossiblyEvaluated; diff --git a/test/build/min.test.ts b/test/build/min.test.ts index b9d88bb3ec..6cffa498c3 100644 --- a/test/build/min.test.ts +++ b/test/build/min.test.ts @@ -37,7 +37,7 @@ describe('test min build', () => { const decreaseQuota = 4096; // feel free to update this value after you've checked that it has changed on purpose :-) - const expectedBytes = 893706; + const expectedBytes = 894730; expect(actualBytes).toBeLessThan(expectedBytes + increaseQuota); expect(actualBytes).toBeGreaterThan(expectedBytes - decreaseQuota);