// @flow import type Painter from './painter'; import type SourceCache from '../source/source_cache'; import type StyleLayer from '../style/style_layer'; import type {OverscaledTileID} from '../source/tile_id'; import type SymbolBucket from '../data/bucket/symbol_bucket'; import DepthMode from '../gl/depth_mode'; import StencilMode from '../gl/stencil_mode'; import CullFaceMode from '../gl/cull_face_mode'; import {collisionUniformValues, collisionCircleUniformValues} from './program/collision_program'; import {QuadTriangleArray, CollisionCircleLayoutArray} from '../data/array_types'; import {collisionCircleLayout} from '../data/bucket/symbol_attributes'; import SegmentVector from '../data/segment'; import {mat4} from 'gl-matrix'; import VertexBuffer from '../gl/vertex_buffer'; import IndexBuffer from '../gl/index_buffer'; export default drawCollisionDebug; type TileBatch = { circleArray: Array, circleOffset: number, transform: mat4, invTransform: mat4 }; let quadTriangles: ?QuadTriangleArray; function drawCollisionDebug(painter: Painter, sourceCache: SourceCache, layer: StyleLayer, coords: Array, translate: [number, number], translateAnchor: 'map' | 'viewport', isText: boolean) { const context = painter.context; const gl = context.gl; const program = painter.useProgram('collisionBox'); const tileBatches: Array = []; let circleCount = 0; let circleOffset = 0; for (let i = 0; i < coords.length; i++) { const coord = coords[i]; const tile = sourceCache.getTile(coord); const bucket: ?SymbolBucket = (tile.getBucket(layer): any); if (!bucket) continue; let posMatrix = coord.posMatrix; if (translate[0] !== 0 || translate[1] !== 0) { posMatrix = painter.translatePosMatrix(coord.posMatrix, tile, translate, translateAnchor); } const buffers = isText ? bucket.textCollisionBox : bucket.iconCollisionBox; // Get collision circle data of this bucket const circleArray: Array = bucket.collisionCircleArray; if (circleArray.length > 0) { // We need to know the projection matrix that was used for projecting collision circles to the screen. // This might vary between buckets as the symbol placement is a continous process. This matrix is // required for transforming points from previous screen space to the current one const invTransform = mat4.create(); const transform = posMatrix; mat4.mul(invTransform, bucket.placementInvProjMatrix, painter.transform.glCoordMatrix); mat4.mul(invTransform, invTransform, bucket.placementViewportMatrix); tileBatches.push({ circleArray, circleOffset, transform, invTransform }); circleCount += circleArray.length / 4; // 4 values per circle circleOffset = circleCount; } if (!buffers) continue; program.draw(context, gl.LINES, DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled, collisionUniformValues( posMatrix, painter.transform, tile), layer.id, buffers.layoutVertexBuffer, buffers.indexBuffer, buffers.segments, null, painter.transform.zoom, null, null, buffers.collisionVertexBuffer); } if (!isText || !tileBatches.length) { return; } // Render collision circles const circleProgram = painter.useProgram('collisionCircle'); // Construct vertex data const vertexData = new CollisionCircleLayoutArray(); vertexData.resize(circleCount * 4); vertexData._trim(); let vertexOffset = 0; for (const batch of tileBatches) { for (let i = 0; i < batch.circleArray.length / 4; i++) { const circleIdx = i * 4; const x = batch.circleArray[circleIdx + 0]; const y = batch.circleArray[circleIdx + 1]; const radius = batch.circleArray[circleIdx + 2]; const collision = batch.circleArray[circleIdx + 3]; // 4 floats per vertex, 4 vertices per quad vertexData.emplace(vertexOffset++, x, y, radius, collision, 0); vertexData.emplace(vertexOffset++, x, y, radius, collision, 1); vertexData.emplace(vertexOffset++, x, y, radius, collision, 2); vertexData.emplace(vertexOffset++, x, y, radius, collision, 3); } } if (!quadTriangles || quadTriangles.length < circleCount * 2) { quadTriangles = createQuadTriangles(circleCount); } const indexBuffer: IndexBuffer = context.createIndexBuffer(quadTriangles, true); const vertexBuffer: VertexBuffer = context.createVertexBuffer(vertexData, collisionCircleLayout.members, true); // Render batches for (const batch of tileBatches) { const uniforms = collisionCircleUniformValues( batch.transform, batch.invTransform, painter.transform ); circleProgram.draw( context, gl.TRIANGLES, DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled, uniforms, layer.id, vertexBuffer, indexBuffer, SegmentVector.simpleSegment(0, batch.circleOffset * 2, batch.circleArray.length, batch.circleArray.length / 2), null, painter.transform.zoom, null, null, null); } vertexBuffer.destroy(); indexBuffer.destroy(); } function createQuadTriangles(quadCount: number): QuadTriangleArray { const triCount = quadCount * 2; const array = new QuadTriangleArray(); array.resize(triCount); array._trim(); // Two triangles and 4 vertices per quad. for (let i = 0; i < triCount; i++) { const idx = i * 6; array.uint16[idx + 0] = i * 4 + 0; array.uint16[idx + 1] = i * 4 + 1; array.uint16[idx + 2] = i * 4 + 2; array.uint16[idx + 3] = i * 4 + 2; array.uint16[idx + 4] = i * 4 + 3; array.uint16[idx + 5] = i * 4 + 0; } return array; }