Skip to content

Commit 8a58785

Browse files
underootwoodroof
authored andcommitted
Fix qRF and collision debug for globe-to-mercator transition (internal-2594)
Co-authored-by: Danil Ilinykh <danil.ilinykh@mapbox.com>
1 parent ab6757f commit 8a58785

File tree

13 files changed

+295
-48
lines changed

13 files changed

+295
-48
lines changed

src/render/draw_collision_debug.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import DepthMode from '../gl/depth_mode';
22
import StencilMode from '../gl/stencil_mode';
33
import CullFaceMode from '../gl/cull_face_mode';
4-
import {collisionUniformValues, collisionCircleUniformValues} from './program/collision_program';
4+
import {collisionUniformValues, collisionCircleUniformValues, type CollisionDebugDefinesType} from './program/collision_program';
55
import {QuadTriangleArray, CollisionCircleLayoutArray} from '../data/array_types';
66
import {collisionCircleLayout} from '../data/bucket/symbol_attributes';
77
import SegmentVector from '../data/segment';
88
import {mat4} from 'gl-matrix';
99
import {getCollisionDebugTileProjectionMatrix} from '../geo/projection/projection_util';
10+
import {globeToMercatorTransition} from '../geo/projection/globe_util';
11+
import {mercatorXfromLng, mercatorYfromLat} from '../geo/mercator_coordinate';
1012

1113
import type VertexBuffer from '../gl/vertex_buffer';
1214
import type IndexBuffer from '../gl/index_buffer';
1315
import type Painter from './painter';
1416
import type SourceCache from '../source/source_cache';
15-
import type {TypedStyleLayer} from '../style/style_layer/typed_style_layer';
1617
import type {OverscaledTileID} from '../source/tile_id';
1718
import type SymbolBucket from '../data/bucket/symbol_bucket';
1819
import type Projection from '../geo/projection/projection';
20+
import type SymbolStyleLayer from '../style/style_layer/symbol_style_layer';
1921

2022
export default drawCollisionDebug;
2123

@@ -29,11 +31,20 @@ type TileBatch = {
2931

3032
let quadTriangles: QuadTriangleArray | null | undefined;
3133

32-
function drawCollisionDebug(painter: Painter, sourceCache: SourceCache, layer: TypedStyleLayer, coords: Array<OverscaledTileID>, translate: [number, number], translateAnchor: 'map' | 'viewport', isText: boolean) {
34+
function drawCollisionDebug(painter: Painter, sourceCache: SourceCache, layer: SymbolStyleLayer, coords: Array<OverscaledTileID>, translate: [number, number], translateAnchor: 'map' | 'viewport', isText: boolean) {
3335
const context = painter.context;
3436
const gl = context.gl;
3537
const tr = painter.transform;
36-
const program = painter.getOrCreateProgram('collisionBox');
38+
const mercatorCenter: [number, number] = [
39+
mercatorXfromLng(tr.center.lng),
40+
mercatorYfromLat(tr.center.lat)
41+
];
42+
const symbolPlacement = layer.layout.get('symbol-placement');
43+
const textVariableAnchor = layer.layout.get('text-variable-anchor');
44+
const iconRotateWithMap = layer.layout.get('icon-rotation-alignment') === 'map';
45+
const textRotateWithMap = layer.layout.get('text-rotation-alignment') === 'map';
46+
47+
const alongLine = symbolPlacement !== 'point';
3748
const tileBatches: Array<TileBatch> = [];
3849
let circleCount = 0;
3950
let circleOffset = 0;
@@ -44,7 +55,26 @@ function drawCollisionDebug(painter: Painter, sourceCache: SourceCache, layer: T
4455
const bucket = tile.getBucket(layer) as SymbolBucket;
4556
if (!bucket) continue;
4657

58+
const invMatrix = bucket.getProjection().createInversionMatrix(tr, coord.canonical);
59+
const defines: CollisionDebugDefinesType[] = [];
4760
const tileMatrix = getCollisionDebugTileProjectionMatrix(coord, bucket, tr);
61+
const isIconPlacedAlongLine = !isText && iconRotateWithMap && alongLine;
62+
const isTextPlacedAlongLine = isText && textRotateWithMap && alongLine;
63+
const hasVariableAnchors = textVariableAnchor && bucket.hasTextData();
64+
const updateIconTextFit = bucket.hasIconTextFit() && hasVariableAnchors && bucket.hasIconData();
65+
const projectedPosOnLabelSpace = isIconPlacedAlongLine || isTextPlacedAlongLine || (isText && hasVariableAnchors) || updateIconTextFit;
66+
const bucketIsGlobeProjection = bucket.projection.name === 'globe';
67+
const globeToMercator = bucketIsGlobeProjection ? globeToMercatorTransition(tr.zoom) : 0.0;
68+
69+
if (bucketIsGlobeProjection) {
70+
defines.push('PROJECTION_GLOBE_VIEW');
71+
72+
if (projectedPosOnLabelSpace) {
73+
defines.push('PROJECTED_POS_ON_VIEWPORT');
74+
}
75+
}
76+
77+
const program = painter.getOrCreateProgram('collisionBox', {defines});
4878

4979
let posMatrix = tileMatrix;
5080
if (translate[0] !== 0 || translate[1] !== 0) {
@@ -76,11 +106,12 @@ function drawCollisionDebug(painter: Painter, sourceCache: SourceCache, layer: T
76106
}
77107
if (!buffers) continue;
78108
if (painter.terrain) painter.terrain.setupElevationDraw(tile, program);
109+
const tileId: [number, number, number] = bucketIsGlobeProjection ? [coord.canonical.x, coord.canonical.y, 1 << coord.canonical.z] : [0, 0, 0];
79110
program.draw(painter, gl.LINES,
80111
DepthMode.disabled, StencilMode.disabled,
81112
painter.colorModeForRenderPass(),
82113
CullFaceMode.disabled,
83-
collisionUniformValues(posMatrix, tr, tile, bucket.getProjection()),
114+
collisionUniformValues(posMatrix, invMatrix, tr, globeToMercator, mercatorCenter, tile, tileId, bucket.getProjection()),
84115
layer.id, buffers.layoutVertexBuffer, buffers.indexBuffer,
85116
buffers.segments, null, tr.zoom, null,
86117
[buffers.collisionVertexBuffer, buffers.collisionVertexBufferExt]);

src/render/draw_symbol.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import SegmentVector from '../data/segment';
44
import * as symbolProjection from '../symbol/projection';
55
import {mat4, vec3, vec4} from 'gl-matrix';
66
import {clamp} from '../util/util';
7-
const identityMat4 = mat4.create();
87
import StencilMode from '../gl/stencil_mode';
98
import DepthMode from '../gl/depth_mode';
109
import CullFaceMode from '../gl/cull_face_mode';
@@ -66,6 +65,8 @@ type SymbolTileRenderState = {
6665

6766
type Alignment = 'auto' | 'map' | 'viewport';
6867

68+
const identityMat4 = mat4.create();
69+
6970
function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: SymbolStyleLayer, coords: Array<OverscaledTileID>, variableOffsets: Partial<Record<CrossTileID, VariableOffset>>) {
7071
if (painter.renderPass !== 'translucent') return;
7172

src/render/program/collision_program.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
import {Uniform1f, Uniform2f, UniformMatrix4f} from '../uniform_binding';
22
import EXTENT from '../../style-spec/data/extent';
3+
import {Uniform3f, type UniformValues} from '../uniform_binding';
34

45
import type {mat4} from 'gl-matrix';
56
import type Context from '../../gl/context';
6-
import type {UniformValues} from '../uniform_binding';
77
import type Transform from '../../geo/transform';
88
import type Tile from '../../source/tile';
99
import type Projection from '../../geo/projection/projection';
1010

11+
export type CollisionDebugDefinesType =
12+
| 'PROJECTION_GLOBE_VIEW'
13+
| 'PROJECTED_POS_ON_VIEWPORT';
14+
1115
export type CollisionUniformsType = {
1216
['u_matrix']: UniformMatrix4f;
17+
['u_inv_rot_matrix']: UniformMatrix4f;
1318
['u_camera_to_center_distance']: Uniform1f;
1419
['u_extrude_scale']: Uniform2f;
20+
['u_zoom_transition']: Uniform1f
21+
['u_merc_center']: Uniform2f
22+
['u_tile_id']: Uniform3f
1523
};
1624

1725
export type CollisionCircleUniformsType = {
@@ -23,8 +31,12 @@ export type CollisionCircleUniformsType = {
2331

2432
const collisionUniforms = (context: Context): CollisionUniformsType => ({
2533
'u_matrix': new UniformMatrix4f(context),
34+
'u_inv_rot_matrix': new UniformMatrix4f(context),
2635
'u_camera_to_center_distance': new Uniform1f(context),
27-
'u_extrude_scale': new Uniform2f(context)
36+
'u_extrude_scale': new Uniform2f(context),
37+
'u_zoom_transition': new Uniform1f(context),
38+
'u_merc_center': new Uniform2f(context),
39+
'u_tile_id': new Uniform3f(context),
2840
});
2941

3042
const collisionCircleUniforms = (context: Context): CollisionCircleUniformsType => ({
@@ -36,17 +48,25 @@ const collisionCircleUniforms = (context: Context): CollisionCircleUniformsType
3648

3749
const collisionUniformValues = (
3850
matrix: mat4,
51+
invMatrix: mat4,
3952
transform: Transform,
53+
globeToMercator: number,
54+
mercatorCenter: [number, number],
4055
tile: Tile,
56+
tileId: [number, number, number],
4157
projection: Projection,
4258
): UniformValues<CollisionUniformsType> => {
4359
const pixelRatio = EXTENT / tile.tileSize;
4460

4561
return {
4662
'u_matrix': matrix as Float32Array,
63+
'u_inv_rot_matrix': invMatrix as Float32Array,
4764
'u_camera_to_center_distance': transform.getCameraToCenterDistance(projection),
4865
'u_extrude_scale': [transform.pixelsToGLUnits[0] / pixelRatio,
49-
transform.pixelsToGLUnits[1] / pixelRatio]
66+
transform.pixelsToGLUnits[1] / pixelRatio],
67+
'u_zoom_transition': globeToMercator,
68+
'u_tile_id': tileId,
69+
'u_merc_center': mercatorCenter
5070
};
5171
};
5272

src/render/program/program_uniforms.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {fillExtrusionDepthUniforms, fillExtrusionUniforms, fillExtrusionPatternU
22
import {fillUniforms, fillPatternUniforms, fillOutlineUniforms, fillOutlinePatternUniforms, elevatedStructuresDepthUniforms, elevatedStructuresUniforms, elevatedStructuresDepthReconstructUniforms} from './fill_program';
33
import {buildingUniforms, buildingBloomUniforms, buildingDepthUniforms, type BuildingDefinesType} from '../../../3d-style/render/program/building_program';
44
import {circleUniforms} from './circle_program';
5-
import {collisionUniforms, collisionCircleUniforms} from './collision_program';
5+
import {collisionUniforms, collisionCircleUniforms, type CollisionDebugDefinesType} from './collision_program';
66
import {debugUniforms} from './debug_program';
77
import {clippingMaskUniforms} from './clipping_mask_program';
88
import {heatmapUniforms, heatmapTextureUniforms} from './heatmap_program';
@@ -59,6 +59,7 @@ export type DynamicDefinesType =
5959
| GlobalDefinesType
6060
| CircleDefinesType
6161
| SymbolDefinesType
62+
| CollisionDebugDefinesType
6263
| LineDefinesType
6364
| FillDefinesType
6465
| FillExtrusionDefinesType

src/shaders/collision_box.vertex.glsl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,28 @@ uniform mat4 u_matrix;
1414
uniform vec2 u_extrude_scale;
1515
uniform float u_camera_to_center_distance;
1616

17+
#ifdef PROJECTION_GLOBE_VIEW
18+
uniform vec3 u_tile_id;
19+
uniform mat4 u_inv_rot_matrix;
20+
uniform vec2 u_merc_center;
21+
uniform float u_zoom_transition;
22+
#endif
23+
1724
out float v_placed;
1825
out float v_notUsed;
1926

2027
void main() {
2128
float feature_elevation = a_elevation_from_sea.x + a_auto_z_offset;
2229
float terrain_elevation = (a_elevation_from_sea.y == 1.0 ? 0.0 : elevation(a_anchor_pos));
23-
vec4 projectedPoint = u_matrix * vec4(a_pos + elevationVector(a_anchor_pos) * (feature_elevation + terrain_elevation), 1);
30+
vec3 proj_pos = a_pos + elevationVector(a_anchor_pos) * (feature_elevation + terrain_elevation);
31+
#ifdef PROJECTION_GLOBE_VIEW
32+
#ifndef PROJECTED_POS_ON_VIEWPORT
33+
vec3 globe_pos = proj_pos;
34+
vec3 mercator_pos = mercator_tile_position(u_inv_rot_matrix, a_anchor_pos, u_tile_id, u_merc_center);
35+
proj_pos = mix_globe_mercator(globe_pos, mercator_pos, u_zoom_transition);
36+
#endif
37+
#endif
38+
vec4 projectedPoint = u_matrix * vec4(proj_pos, 1);
2439

2540
highp float camera_to_anchor_distance = projectedPoint.w;
2641
highp float collision_perspective_ratio = clamp(

src/style/query_geometry.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ export class QueryGeometry {
4545

4646
isAboveHorizon: boolean;
4747

48-
constructor(screenBounds: Point[], cameraPoint: Point, aboveHorizon: boolean, transform: Transform) {
48+
constructor(screenBounds: Point[], aboveHorizon: boolean, transform: Transform) {
4949
this.screenBounds = screenBounds;
50-
this.cameraPoint = cameraPoint;
50+
this.cameraPoint = transform.getCameraPoint();
5151
this._screenRaycastCache = {};
5252
this._cameraRaycastCache = {};
5353
this.isAboveHorizon = aboveHorizon;
@@ -80,7 +80,7 @@ export class QueryGeometry {
8080
aboveHorizon = polygonizeBounds(tl, br).every((p) => transform.isPointAboveHorizon(p)) && transform.isPointAboveHorizon(center);
8181
}
8282

83-
return new QueryGeometry(screenGeometry, transform.getCameraPoint(), aboveHorizon, transform);
83+
return new QueryGeometry(screenGeometry, aboveHorizon, transform);
8484
}
8585

8686
/**

src/symbol/collision_index.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ import Point from '@mapbox/point-geometry';
22
import PathInterpolator from './path_interpolator';
33
import * as intersectionTests from '../util/intersection_tests';
44
import Grid from './grid_index';
5-
import {mat4, vec4} from 'gl-matrix';
5+
import {mat4, vec2, vec4} from 'gl-matrix';
66
import ONE_EM from '../symbol/one_em';
77
import {FOG_SYMBOL_CLIPPING_THRESHOLD, getFogOpacityAtTileCoord} from '../style/fog_helpers';
88
import assert from 'assert';
99
import * as symbolProjection from '../symbol/projection';
10-
import {degToRad} from '../util/util';
10+
import {degToRad, wrap} from '../util/util';
1111
import {clipLines} from '../util/line_clipping';
12+
import EXTENT from '../style-spec/data/extent';
13+
import {number as mix} from '../style-spec/util/interpolate';
14+
import {globeToMercatorTransition} from '../geo/projection/globe_util';
1215

1316
import type {OverscaledTileID} from '../source/tile_id';
1417
import type {vec3} from 'gl-matrix';
@@ -92,6 +95,9 @@ class CollisionIndex {
9295
bucket: SymbolBucket,
9396
scale: number,
9497
collisionBox: SingleCollisionBox,
98+
mercatorCenter: [number, number],
99+
invMatrix: mat4,
100+
projectedPosOnLabelSpace: boolean,
95101
shift: Point,
96102
allowOverlap: boolean,
97103
textPixelRatio: number,
@@ -101,9 +107,11 @@ class CollisionIndex {
101107
): PlacedCollisionBox {
102108
assert(!this.transform.elevation || collisionBox.elevation !== undefined);
103109

104-
let anchorX = collisionBox.projectedAnchorX;
105-
let anchorY = collisionBox.projectedAnchorY;
106-
let anchorZ = collisionBox.projectedAnchorZ;
110+
let projectedAnchorX = collisionBox.projectedAnchorX;
111+
let projectedAnchorY = collisionBox.projectedAnchorY;
112+
let projectedAnchorZ = collisionBox.projectedAnchorZ;
113+
const anchorX = collisionBox.tileAnchorX;
114+
const anchorY = collisionBox.tileAnchorY;
107115

108116
// Apply elevation vector to the anchor point
109117
const elevation = collisionBox.elevation;
@@ -113,13 +121,36 @@ class CollisionIndex {
113121
const [ux, uy, uz] = projection.upVector(tileID.canonical, collisionBox.tileAnchorX, collisionBox.tileAnchorY);
114122
const upScale = projection.upVectorScale(tileID.canonical, this.transform.center.lat, this.transform.worldSize).metersToTile;
115123

116-
anchorX += ux * elevation * upScale;
117-
anchorY += uy * elevation * upScale;
118-
anchorZ += uz * elevation * upScale;
124+
projectedAnchorX += ux * elevation * upScale;
125+
projectedAnchorY += uy * elevation * upScale;
126+
projectedAnchorZ += uz * elevation * upScale;
127+
}
128+
129+
const bucketIsGlobeProjection = bucket.projection.name === 'globe';
130+
const globeToMercator = bucket.projection.name === 'globe' ? globeToMercatorTransition(this.transform.zoom) : 0.0;
131+
const isGlobeToMercatorTransition = globeToMercator < 1;
132+
133+
if (tileID && bucketIsGlobeProjection && isGlobeToMercatorTransition && !projectedPosOnLabelSpace) {
134+
const tilesCount = 1 << tileID.canonical.z;
135+
const mercator = vec2.fromValues(anchorX, anchorY);
136+
vec2.scale(mercator, mercator, 1 / EXTENT);
137+
vec2.add(mercator, mercator, vec2.fromValues(tileID.canonical.x, tileID.canonical.y));
138+
vec2.scale(mercator, mercator, 1 / tilesCount);
139+
vec2.sub(mercator, mercator, vec2.fromValues(mercatorCenter[0], mercatorCenter[1]));
140+
mercator[0] = wrap(mercator[0], -0.5, 0.5);
141+
142+
vec2.scale(mercator, mercator, EXTENT);
143+
144+
const mercatorPosition = vec4.fromValues(mercator[0], mercator[1], EXTENT / (2.0 * Math.PI), 1.0);
145+
vec4.transformMat4(mercatorPosition, mercatorPosition, invMatrix);
146+
147+
projectedAnchorX = mix(projectedAnchorX, mercatorPosition[0], globeToMercator);
148+
projectedAnchorY = mix(projectedAnchorY, mercatorPosition[1], globeToMercator);
149+
projectedAnchorZ = mix(projectedAnchorZ, mercatorPosition[2], globeToMercator);
119150
}
120151

121152
const checkOcclusion = projection.name === 'globe' || !!elevation || this.transform.pitch > 0;
122-
const projectedPoint = this.projectAndGetPerspectiveRatio(posMatrix, anchorX, anchorY, anchorZ, collisionBox.tileID, checkOcclusion, projection);
153+
const projectedPoint = this.projectAndGetPerspectiveRatio(posMatrix, projectedAnchorX, projectedAnchorY, projectedAnchorZ, collisionBox.tileID, checkOcclusion, projection);
123154

124155
const tileToViewport = textPixelRatio * projectedPoint.perspectiveRatio;
125156
const tlX = (collisionBox.x1 * scale + shift.x - collisionBox.padding) * tileToViewport + projectedPoint.point.x;

0 commit comments

Comments
 (0)