Spaces:
Sleeping
Sleeping
File size: 5,689 Bytes
fcf3b03 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | // @flow
import Texture from './texture';
import Color from '../style-spec/util/color';
import DepthMode from '../gl/depth_mode';
import StencilMode from '../gl/stencil_mode';
import ColorMode from '../gl/color_mode';
import CullFaceMode from '../gl/cull_face_mode';
import {
heatmapUniformValues,
heatmapTextureUniformValues
} from './program/heatmap_program';
import type Painter 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 default drawHeatmap;
function drawHeatmap(painter: Painter, sourceCache: SourceCache, layer: HeatmapStyleLayer, coords: Array<OverscaledTileID>) {
if (layer.paint.get('heatmap-opacity') === 0) {
return;
}
if (painter.renderPass === 'offscreen') {
const context = painter.context;
const gl = context.gl;
// Allow kernels to be drawn across boundaries, so that
// large kernels are not clipped to tiles
const stencilMode = StencilMode.disabled;
// Turn on additive blending for kernels, which is a key aspect of kernel density estimation formula
const colorMode = new ColorMode([gl.ONE, gl.ONE], Color.transparent, [true, true, true, true]);
bindFramebuffer(context, painter, layer);
context.clear({color: Color.transparent});
for (let i = 0; i < coords.length; i++) {
const coord = coords[i];
// Skip tiles that have uncovered parents to avoid flickering; we don't need
// to use complex tile masking here because the change between zoom levels is subtle,
// so it's fine to simply render the parent until all its 4 children are loaded
if (sourceCache.hasRenderableParent(coord)) continue;
const tile = sourceCache.getTile(coord);
const bucket: ?HeatmapBucket = (tile.getBucket(layer): any);
if (!bucket) continue;
const programConfiguration = bucket.programConfigurations.get(layer.id);
const program = painter.useProgram('heatmap', programConfiguration);
const {zoom} = painter.transform;
program.draw(context, gl.TRIANGLES, DepthMode.disabled, stencilMode, colorMode, CullFaceMode.disabled,
heatmapUniformValues(coord.posMatrix,
tile, zoom, layer.paint.get('heatmap-intensity')),
layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer,
bucket.segments, layer.paint, painter.transform.zoom,
programConfiguration);
}
context.viewport.set([0, 0, painter.width, painter.height]);
} else if (painter.renderPass === 'translucent') {
painter.context.setColorMode(painter.colorModeForRenderPass());
renderTextureToMap(painter, layer);
}
}
function bindFramebuffer(context, painter, layer) {
const gl = context.gl;
context.activeTexture.set(gl.TEXTURE1);
// Use a 4x downscaled screen texture for better performance
context.viewport.set([0, 0, painter.width / 4, painter.height / 4]);
let fbo = layer.heatmapFbo;
if (!fbo) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
fbo = layer.heatmapFbo = context.createFramebuffer(painter.width / 4, painter.height / 4, false);
bindTextureToFramebuffer(context, painter, texture, fbo);
} else {
gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
context.bindFramebuffer.set(fbo.framebuffer);
}
}
function bindTextureToFramebuffer(context, painter, texture, fbo) {
const gl = context.gl;
// Use the higher precision half-float texture where available (producing much smoother looking heatmaps);
// Otherwise, fall back to a low precision texture
const internalFormat = context.extRenderToTextureHalfFloat ? context.extTextureHalfFloat.HALF_FLOAT_OES : gl.UNSIGNED_BYTE;
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, painter.width / 4, painter.height / 4, 0, gl.RGBA, internalFormat, null);
fbo.colorAttachment.set(texture);
}
function renderTextureToMap(painter, layer) {
const context = painter.context;
const gl = context.gl;
// Here we bind two different textures from which we'll sample in drawing
// heatmaps: the kernel texture, prepared in the offscreen pass, and a
// color ramp texture.
const fbo = layer.heatmapFbo;
if (!fbo) return;
context.activeTexture.set(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
context.activeTexture.set(gl.TEXTURE1);
let colorRampTexture = layer.colorRampTexture;
if (!colorRampTexture) {
colorRampTexture = layer.colorRampTexture = new Texture(context, layer.colorRamp, gl.RGBA);
}
colorRampTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
painter.useProgram('heatmapTexture').draw(context, gl.TRIANGLES,
DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled,
heatmapTextureUniformValues(painter, layer, 0, 1),
layer.id, painter.viewportBuffer, painter.quadTriangleIndexBuffer,
painter.viewportSegments, layer.paint, painter.transform.zoom);
}
|