From 35127d3e965bb7f8f0ec596c8e9af20922d08e3d Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Tue, 25 Apr 2017 18:46:59 -0700 Subject: [PATCH] Make pitched collision detection more conservative: When doing collision detection, assume all labels are scaled the same as the least scaled label in the tile. --- src/data/bucket/symbol_bucket.js | 4 ++-- src/render/draw_collision_debug.js | 3 +-- src/render/draw_symbol.js | 5 +++-- src/shaders/symbol_icon.vertex.glsl | 4 +++- src/shaders/symbol_sdf.vertex.glsl | 11 +++++++++-- src/symbol/collision_tile.js | 5 ++++- 6 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/data/bucket/symbol_bucket.js b/src/data/bucket/symbol_bucket.js index 7aee3cce190..141d9c1670c 100644 --- a/src/data/bucket/symbol_bucket.js +++ b/src/data/bucket/symbol_bucket.js @@ -597,7 +597,7 @@ class SymbolBucket { // Insert final placement into collision tree and add glyphs/icons to buffers if (hasText) { - collisionTile.insertCollisionFeature(textCollisionFeature, glyphScale, layout['text-ignore-placement']); + collisionTile.insertCollisionFeature(textCollisionFeature, glyphScale, layout['text-ignore-placement'], layer.getLayoutValue('text-pitch-scale')); if (glyphScale <= maxScale) { const textSizeData = getSizeVertexData(layer, this.zoom, @@ -619,7 +619,7 @@ class SymbolBucket { } if (hasIcon) { - collisionTile.insertCollisionFeature(iconCollisionFeature, iconScale, layout['icon-ignore-placement']); + collisionTile.insertCollisionFeature(iconCollisionFeature, iconScale, layout['icon-ignore-placement'], layer.getLayoutValue('icon-pitch-scale')); if (iconScale <= maxScale) { const iconSizeData = getSizeVertexData( layer, diff --git a/src/render/draw_collision_debug.js b/src/render/draw_collision_debug.js index f07cbc47b4f..5370aa3a59c 100644 --- a/src/render/draw_collision_debug.js +++ b/src/render/draw_collision_debug.js @@ -27,8 +27,7 @@ function drawCollisionDebug(painter, sourceCache, layer, coords) { gl.uniform1f(program.u_collision_y_stretch, tile.collisionTile.yStretch); gl.uniform1f(program.u_pitch, painter.transform.pitch / 360 * 2 * Math.PI); gl.uniform1f(program.u_camera_to_center_distance, painter.transform.cameraToCenterDistance); - // TODO: Hook up to `text-pitch-scale` and `icon-pitch-scale` properties - gl.uniform1f(program.u_pitch_scale, 0.0); + gl.uniform1f(program.u_pitch_scale, tile.collisionTile.minimumPitchScaling); for (const segment of buffers.segments) { segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, null, segment.vertexOffset); diff --git a/src/render/draw_symbol.js b/src/render/draw_symbol.js index 86292510073..f7880a872b9 100644 --- a/src/render/draw_symbol.js +++ b/src/render/draw_symbol.js @@ -93,7 +93,7 @@ function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate program = painter.useProgram(isSDF ? 'symbolSDF' : 'symbolIcon', programConfiguration); programConfiguration.setUniforms(gl, program, layer, {zoom: painter.transform.zoom}); - setSymbolDrawState(program, painter, layer, coord.z, isText, isSDF, rotateWithMap, pitchWithMap, bucket.fontstack, bucket.iconsNeedLinear, sizeData); + setSymbolDrawState(program, painter, layer, coord.z, isText, isSDF, rotateWithMap, pitchWithMap, bucket.fontstack, bucket.iconsNeedLinear, sizeData, tile.collisionTile.minimumPitchScaling); } painter.enableTileClippingMask(coord); @@ -112,7 +112,7 @@ function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate if (!depthOn) gl.enable(gl.DEPTH_TEST); } -function setSymbolDrawState(program, painter, layer, tileZoom, isText, isSDF, rotateWithMap, pitchWithMap, fontstack, iconsNeedLinear, sizeData) { +function setSymbolDrawState(program, painter, layer, tileZoom, isText, isSDF, rotateWithMap, pitchWithMap, fontstack, iconsNeedLinear, sizeData, minimumPitchScaling) { const gl = painter.gl; const tr = painter.transform; @@ -196,6 +196,7 @@ function setSymbolDrawState(program, painter, layer, tileZoom, isText, isSDF, ro } gl.uniform1f(program.u_camera_to_center_distance, tr.cameraToCenterDistance); gl.uniform1f(program.u_pitch_scale, layer.getLayoutValue(isText ? 'text-pitch-scale' : 'icon-pitch-scale')); + gl.uniform1f(program.u_collision_pitch_scale, minimumPitchScaling); } function drawTileSymbols(program, programConfiguration, painter, layer, tile, buffers, isText, isSDF, pitchWithMap) { diff --git a/src/shaders/symbol_icon.vertex.glsl b/src/shaders/symbol_icon.vertex.glsl index 6221b506197..6618445dcbc 100644 --- a/src/shaders/symbol_icon.vertex.glsl +++ b/src/shaders/symbol_icon.vertex.glsl @@ -11,6 +11,7 @@ uniform mediump float u_layout_size; // used when size is feature constant uniform mediump float u_camera_to_center_distance; uniform mediump float u_pitch; uniform mediump float u_pitch_scale; +uniform mediump float u_collision_pitch_scale; uniform mediump float u_collision_y_stretch; #pragma mapbox: define lowp float opacity @@ -91,7 +92,8 @@ void main() { // See comments in symbol_sdf.vertex highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch)); highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch); + highp float collision_perspective_ratio = 1.0 + (1.0 - u_collision_pitch_scale)*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); - highp float perspective_zoom_adjust = log2(perspective_ratio * collision_adjustment)*10.0 / 255.0; + highp float perspective_zoom_adjust = log2(collision_perspective_ratio * collision_adjustment)*10.0 / 255.0; v_fade_tex = vec2((a_labelminzoom / 255.0) + perspective_zoom_adjust, 0.0); } diff --git a/src/shaders/symbol_sdf.vertex.glsl b/src/shaders/symbol_sdf.vertex.glsl index 2bf8d6f020b..3fde238f9cb 100644 --- a/src/shaders/symbol_sdf.vertex.glsl +++ b/src/shaders/symbol_sdf.vertex.glsl @@ -37,6 +37,7 @@ uniform mediump float u_bearing; uniform mediump float u_aspect_ratio; uniform mediump float u_camera_to_center_distance; uniform mediump float u_pitch_scale; +uniform mediump float u_collision_pitch_scale; uniform mediump float u_collision_y_stretch; uniform vec2 u_extrude_scale; @@ -170,7 +171,13 @@ void main() { // Never make the adjustment less than 1.0: instead of allowing collisions on the x-axis, be conservative on // the y-axis. highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch); - - highp float perspective_zoom_adjust = log2(perspective_ratio * collision_adjustment)*10.0 / 255.0; + // "collision_perspective_ratio" is based on the smallest pitch scaling of any label on the tile, instead of + // just the pitch scaling for this label. This is a conservative approximation that makes sure we don't collide + // with an unscaled (large) label even though this particular label is highly scaled (and thus small). + // Note that this same conservatism doesn't apply to the minzoom/maxzoom for line-following glyphs: those + // zoom bounds are independent of other labels on the tile. + highp float collision_perspective_ratio = 1.0 + (1.0 - u_collision_pitch_scale)*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); + + highp float perspective_zoom_adjust = log2(collision_perspective_ratio * collision_adjustment)*10.0 / 255.0; v_fade_tex = vec2((a_labelminzoom / 255.0) + perspective_zoom_adjust, 0.0); } diff --git a/src/symbol/collision_tile.js b/src/symbol/collision_tile.js index 86b9f6e4590..19024685d5f 100644 --- a/src/symbol/collision_tile.js +++ b/src/symbol/collision_tile.js @@ -33,6 +33,8 @@ class CollisionTile { this.angle = angle; this.pitch = pitch; + this.minimumPitchScaling = 1; + const sin = Math.sin(angle), cos = Math.cos(angle); this.rotationMatrix = [cos, -sin, sin, cos]; @@ -314,7 +316,8 @@ class CollisionTile { * @param {number} minPlacementScale * @private */ - insertCollisionFeature(collisionFeature, minPlacementScale, ignorePlacement) { + insertCollisionFeature(collisionFeature, minPlacementScale, ignorePlacement, pitchScaling) { + this.minimumPitchScaling = Math.min(pitchScaling, this.minimumPitchScaling); const grid = ignorePlacement ? this.ignoredGrid : this.grid; const collisionBoxArray = this.collisionBoxArray;