Skip to content

Commit

Permalink
Viewport collision detection:
Browse files Browse the repository at this point in the history
 - Collision detection now runs on the foreground in a global, viewport-aligned CollisionIndex instead of in the background on map-aligned CollisionTiles.
 - Calculating label size at render time means it's no longer necessary to approximate perspective effects
 - Splits symbol_bucket#prepare and symbol_bucket#place into separate files.
 - Background processing of symbol_bucket, including buffer generation, is now completed with 'prepare'
 - 'place' is now called on the foreground in preparation for render calls.
 - Placement now updates dynamic opacity buffers instead of re-generating large static buffers
 - Line label projection code from 'projection.js' is now used during Placement to calculate the extent of line labels
 - Vertical and horizontal versions of glyphs are now generated at same time in the background. At render time, one version will be hidden based on line angle.
 - Rolls back PR #1981: even when icons are rotation-aligned to a line, just use a single collision box for them, instead of trying to approximate the shape of the icon with multiple collision boxes. This makes the behavior similar to point icons with rotation-alignment map, which depend on having a roughly square shape (see issue #4861).
  • Loading branch information
ChrisLoer committed Oct 6, 2017
1 parent 80f104e commit 1e48710
Show file tree
Hide file tree
Showing 26 changed files with 1,733 additions and 1,466 deletions.
924 changes: 318 additions & 606 deletions src/data/bucket/symbol_bucket.js

Large diffs are not rendered by default.

28 changes: 16 additions & 12 deletions src/data/feature_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ const vt = require('@mapbox/vector-tile');
const Protobuf = require('pbf');
const GeoJSONFeature = require('../util/vectortile_to_geojson');
const arraysIntersect = require('../util/util').arraysIntersect;
const TileCoord = require('../source/tile_coord');

import type CollisionTile from '../symbol/collision_tile';
import type TileCoord from '../source/tile_coord';
import type CollisionIndex from '../symbol/collision_index';
import type StyleLayer from '../style/style_layer';
import type {SerializedStructArray} from '../util/struct_array';

Expand All @@ -37,7 +37,9 @@ type QueryParameters = {
params: {
filter: FilterSpecification,
layers: Array<string>,
}
},
tileSourceMaxZoom: number,
collisionBoxArray: any
}

export type SerializedFeatureIndex = {
Expand All @@ -60,22 +62,22 @@ class FeatureIndex {
rawTileData: ArrayBuffer;
bucketLayerIDs: Array<Array<string>>;

collisionTile: CollisionTile;
vtLayers: {[string]: VectorTileLayer};
sourceLayerCoder: DictionaryCoder;

collisionIndex: CollisionIndex;

static deserialize(serialized: SerializedFeatureIndex,
rawTileData: ArrayBuffer,
collisionTile: CollisionTile) {
rawTileData: ArrayBuffer) {
const coord = serialized.coord;
const self = new FeatureIndex(
serialized.coord,
new TileCoord(coord.z, coord.x, coord.y, coord.w),
serialized.overscaling,
new Grid(serialized.grid),
new FeatureIndexArray(serialized.featureIndexArray));

self.rawTileData = rawTileData;
self.bucketLayerIDs = serialized.bucketLayerIDs;
self.setCollisionTile(collisionTile);

return self;
}
Expand Down Expand Up @@ -113,8 +115,8 @@ class FeatureIndex {
}
}

setCollisionTile(collisionTile: CollisionTile) {
this.collisionTile = collisionTile;
setCollisionIndex(collisionIndex: CollisionIndex) {
this.collisionIndex = collisionIndex;
}

serialize(transferables?: Array<Transferable>): SerializedFeatureIndex {
Expand Down Expand Up @@ -166,9 +168,11 @@ class FeatureIndex {
matching.sort(topDownFeatureComparator);
this.filterMatching(result, matching, this.featureIndexArray, queryGeometry, filter, params.layers, styleLayers, args.bearing, pixelsToTileUnits);

const matchingSymbols = this.collisionTile.queryRenderedSymbols(queryGeometry, args.scale);
const matchingSymbols = this.collisionIndex ?
this.collisionIndex.queryRenderedSymbols(queryGeometry, args.scale, this.coord, args.tileSourceMaxZoom, pixelsToTileUnits, args.collisionBoxArray) :
[];
matchingSymbols.sort();
this.filterMatching(result, matchingSymbols, this.collisionTile.collisionBoxArray, queryGeometry, filter, params.layers, styleLayers, args.bearing, pixelsToTileUnits);
this.filterMatching(result, matchingSymbols, args.collisionBoxArray, queryGeometry, filter, params.layers, styleLayers, args.bearing, pixelsToTileUnits);

return result;
}
Expand Down
2 changes: 2 additions & 0 deletions src/data/program_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export type ProgramInterface = {
layoutAttributes: Array<LayoutAttribute>,
indexArrayType: Class<StructArray>,
dynamicLayoutAttributes?: Array<LayoutAttribute>,
opacityAttributes?: Array<LayoutAttribute>,
collisionAttributes?: Array<LayoutAttribute>,
paintAttributes?: Array<PaintAttribute>,
indexArrayType2?: Class<StructArray>
}
Expand Down
32 changes: 15 additions & 17 deletions src/geo/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class Transform {
_maxZoom: number;
_center: LngLat;
_constraining: boolean;
_posMatrixCache: {[string]: Float32Array};

constructor(minZoom: ?number, maxZoom: ?number, renderWorldCopies: boolean | void) {
this.tileSize = 512; // constant

Expand All @@ -60,6 +62,7 @@ class Transform {
this._fov = 0.6435011087932844;
this._pitch = 0;
this._unmodified = true;
this._posMatrixCache = {};
}

clone(): Transform {
Expand All @@ -74,6 +77,7 @@ class Transform {
clone._fov = this._fov;
clone._pitch = this._pitch;
clone._unmodified = this._unmodified;
clone._calcMatrices();
return clone;
}

Expand Down Expand Up @@ -394,7 +398,14 @@ class Transform {
* @param {TileCoord} tileCoord
* @param {number} maxZoom maximum source zoom to account for overscaling
*/
calculatePosMatrix(tileCoord: TileCoord, maxZoom?: number) {
calculatePosMatrix(tileCoord: TileCoord, maxZoom?: number): Float32Array {
let posMatrixKey = tileCoord.id.toString();
if (maxZoom) {
posMatrixKey += maxZoom.toString();
}
if (this._posMatrixCache[posMatrixKey]) {
return this._posMatrixCache[posMatrixKey];
}
// if z > maxzoom then the tile is actually a overscaled maxzoom tile,
// so calculate the matrix the maxzoom tile would use.
const coord = tileCoord.toCoordinate(maxZoom);
Expand All @@ -405,22 +416,8 @@ class Transform {
mat4.scale(posMatrix, posMatrix, [scale / EXTENT, scale / EXTENT, 1]);
mat4.multiply(posMatrix, this.projMatrix, posMatrix);

return new Float32Array(posMatrix);
}

/**
* Calculate the distance from the center of a tile to the camera
* These distances are in view-space dimensions derived from the size of the
* viewport, similar to this.cameraToCenterDistance
* If the tile is dead-center in the viewport, then cameraToTileDistance == cameraToCenterDistance
*
* @param {Tile} tile
*/
cameraToTileDistance(tile: Object) {
const posMatrix = this.calculatePosMatrix(tile.coord, tile.sourceMaxZoom);
const tileCenter = [tile.tileSize / 2, tile.tileSize / 2, 0, 1];
vec4.transformMat4(tileCenter, tileCenter, posMatrix);
return tileCenter[3];
this._posMatrixCache[posMatrixKey] = new Float32Array(posMatrix);
return this._posMatrixCache[posMatrixKey];
}

_constrain() {
Expand Down Expand Up @@ -536,6 +533,7 @@ class Transform {
if (!m) throw new Error("failed to invert matrix");
this.pixelMatrixInverse = m;

this._posMatrixCache = {};
}
}

Expand Down
20 changes: 17 additions & 3 deletions src/gl/index_buffer.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
// @flow
const assert = require('assert');

import type {TriangleIndexArray, LineIndexArray} from '../data/index_array_type';
import type {SerializedStructArray} from '../util/struct_array';


class IndexBuffer {
gl: WebGLRenderingContext;
buffer: WebGLBuffer;
dynamicDraw: boolean;

constructor(gl: WebGLRenderingContext, array: TriangleIndexArray | LineIndexArray) {
constructor(gl: WebGLRenderingContext, array: TriangleIndexArray | LineIndexArray, dynamicDraw?: boolean) {
this.gl = gl;
this.buffer = gl.createBuffer();
this.dynamicDraw = Boolean(dynamicDraw);

// The bound index buffer is part of vertex array object state. We don't want to
// modify whatever VAO happens to be currently bound, so make sure the default
Expand All @@ -21,14 +26,23 @@ class IndexBuffer {
}

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, array.arrayBuffer, gl.STATIC_DRAW);
delete array.arrayBuffer;
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, array.arrayBuffer, this.dynamicDraw ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);

if (!this.dynamicDraw) {
delete array.arrayBuffer;
}
}

bind() {
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.buffer);
}

updateData(array: SerializedStructArray) {
assert(this.dynamicDraw);
this.bind();
this.gl.bufferSubData(this.gl.ELEMENT_ARRAY_BUFFER, 0, array.arrayBuffer);
}

destroy() {
if (this.buffer) {
this.gl.deleteBuffer(this.buffer);
Expand Down
15 changes: 7 additions & 8 deletions src/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

const browser = require('../util/browser');
const mat4 = require('@mapbox/gl-matrix').mat4;
const FrameHistory = require('./frame_history');
const SourceCache = require('../source/source_cache');
const EXTENT = require('../data/extent');
const pixelsToTileUnits = require('../source/pixels_to_tile_units');
Expand All @@ -12,6 +11,7 @@ const VertexArrayObject = require('./vertex_array_object');
const RasterBoundsArray = require('../data/raster_bounds_array');
const PosArray = require('../data/pos_array');
const {ProgramConfiguration} = require('../data/program_configuration');
const CrossTileSymbolIndex = require('../symbol/cross_tile_symbol_index');
const shaders = require('../shaders');
const Program = require('./program');
const RenderTexture = require('./render_texture');
Expand Down Expand Up @@ -45,7 +45,8 @@ type PainterOptions = {
showOverdrawInspector: boolean,
showTileBoundaries: boolean,
rotating: boolean,
zooming: boolean
zooming: boolean,
collisionFadeDuration: number
}

/**
Expand All @@ -58,7 +59,6 @@ class Painter {
gl: WebGLRenderingContext;
transform: Transform;
_tileTextures: { [number]: Array<Texture> };
frameHistory: FrameHistory;
numSublayers: number;
depthEpsilon: number;
lineWidthRange: [number, number];
Expand Down Expand Up @@ -94,14 +94,13 @@ class Painter {
_showOverdrawInspector: boolean;
cache: { [string]: Program };
currentProgram: Program;
crossTileSymbolIndex: CrossTileSymbolIndex;

constructor(gl: WebGLRenderingContext, transform: Transform) {
this.gl = gl;
this.transform = transform;
this._tileTextures = {};

this.frameHistory = new FrameHistory();

this.setup();

// Within each layer there are multiple distinct z-planes that can be drawn to.
Expand All @@ -113,6 +112,8 @@ class Painter {

this.basicFillProgramConfiguration = ProgramConfiguration.createBasicFill();
this.emptyProgramConfiguration = new ProgramConfiguration();

this.crossTileSymbolIndex = new CrossTileSymbolIndex();
}

/*
Expand Down Expand Up @@ -276,9 +277,7 @@ class Painter {
this.imageManager = style.imageManager;
this.glyphManager = style.glyphManager;

this.frameHistory.record(Date.now(), this.transform.zoom, style.getTransition().duration);

for (const id in this.style.sourceCaches) {
for (const id in style.sourceCaches) {
const sourceCache = this.style.sourceCaches[id];
if (sourceCache.used) {
sourceCache.prepare(this.gl);
Expand Down
7 changes: 5 additions & 2 deletions src/render/program.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ class Program {
indexBuffer: IndexBuffer,
segments: SegmentVector,
configuration: ?ProgramConfiguration,
dynamicLayoutBuffer: ?VertexBuffer) {
dynamicLayoutBuffer: ?VertexBuffer,
dynamicLayoutBuffer2: ?VertexBuffer) {

const primitiveSize = {
[gl.LINES]: 2,
Expand All @@ -107,7 +108,9 @@ class Program {
indexBuffer,
configuration && configuration.paintVertexBuffer,
segment.vertexOffset,
dynamicLayoutBuffer);
dynamicLayoutBuffer,
dynamicLayoutBuffer2
);

gl.drawElements(
drawMode,
Expand Down
28 changes: 24 additions & 4 deletions src/render/vertex_array_object.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class VertexArrayObject {
boundIndexBuffer: ?IndexBuffer;
boundVertexOffset: ?number;
boundDynamicVertexBuffer: ?VertexBuffer;
boundDynamicVertexBuffer2: ?VertexBuffer;
vao: any;
gl: WebGLRenderingContext;

Expand All @@ -32,7 +33,8 @@ class VertexArrayObject {
indexBuffer: ?IndexBuffer,
vertexBuffer2: ?VertexBuffer,
vertexOffset: ?number,
dynamicVertexBuffer: ?VertexBuffer) {
dynamicVertexBuffer: ?VertexBuffer,
dynamicVertexBuffer2: ?VertexBuffer) {

if (gl.extVertexArrayObject === undefined) {
(gl: any).extVertexArrayObject = gl.getExtension("OES_vertex_array_object");
Expand All @@ -45,11 +47,12 @@ class VertexArrayObject {
this.boundVertexBuffer2 !== vertexBuffer2 ||
this.boundIndexBuffer !== indexBuffer ||
this.boundVertexOffset !== vertexOffset ||
this.boundDynamicVertexBuffer !== dynamicVertexBuffer
this.boundDynamicVertexBuffer !== dynamicVertexBuffer ||
this.boundDynamicVertexBuffer2 !== dynamicVertexBuffer2
);

if (!gl.extVertexArrayObject || isFreshBindRequired) {
this.freshBind(gl, program, layoutVertexBuffer, indexBuffer, vertexBuffer2, vertexOffset, dynamicVertexBuffer);
this.freshBind(gl, program, layoutVertexBuffer, indexBuffer, vertexBuffer2, vertexOffset, dynamicVertexBuffer, dynamicVertexBuffer2);
this.gl = gl;
} else {
(gl: any).extVertexArrayObject.bindVertexArrayOES(this.vao);
Expand All @@ -58,6 +61,14 @@ class VertexArrayObject {
// The buffer may have been updated. Rebind to upload data.
dynamicVertexBuffer.bind();
}

if (indexBuffer && indexBuffer.dynamicDraw) {
indexBuffer.bind();
}

if (dynamicVertexBuffer2) {
dynamicVertexBuffer2.bind();
}
}
}

Expand All @@ -67,7 +78,8 @@ class VertexArrayObject {
indexBuffer: ?IndexBuffer,
vertexBuffer2: ?VertexBuffer,
vertexOffset: ?number,
dynamicVertexBuffer: ?VertexBuffer) {
dynamicVertexBuffer: ?VertexBuffer,
dynamicVertexBuffer2: ?VertexBuffer) {
let numPrevAttributes;
const numNextAttributes = program.numAttributes;

Expand All @@ -84,6 +96,7 @@ class VertexArrayObject {
this.boundIndexBuffer = indexBuffer;
this.boundVertexOffset = vertexOffset;
this.boundDynamicVertexBuffer = dynamicVertexBuffer;
this.boundDynamicVertexBuffer2 = dynamicVertexBuffer2;

} else {
numPrevAttributes = (gl: any).currentNumAttributes || 0;
Expand All @@ -105,6 +118,9 @@ class VertexArrayObject {
if (dynamicVertexBuffer) {
dynamicVertexBuffer.enableAttributes(gl, program);
}
if (dynamicVertexBuffer2) {
dynamicVertexBuffer2.enableAttributes(gl, program);
}

layoutVertexBuffer.bind();
layoutVertexBuffer.setVertexAttribPointers(gl, program, vertexOffset);
Expand All @@ -119,6 +135,10 @@ class VertexArrayObject {
if (indexBuffer) {
indexBuffer.bind();
}
if (dynamicVertexBuffer2) {
dynamicVertexBuffer2.bind();
dynamicVertexBuffer2.setVertexAttribPointers(gl, program, vertexOffset);
}

(gl: any).currentNumAttributes = numNextAttributes;
}
Expand Down
Loading

0 comments on commit 1e48710

Please sign in to comment.