@deck.gl/core
Version:
deck.gl core library
139 lines • 5.25 kB
JavaScript
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import LayersPass from "./layers-pass.js";
import log from "../utils/log.js";
const PICKING_BLENDING = {
blendColorOperation: 'add',
blendColorSrcFactor: 'one',
blendColorDstFactor: 'zero',
blendAlphaOperation: 'add',
blendAlphaSrcFactor: 'constant-alpha',
blendAlphaDstFactor: 'zero'
};
export default class PickLayersPass extends LayersPass {
constructor() {
super(...arguments);
this._colorEncoderState = null;
}
render(props) {
if ('pickingFBO' in props) {
// When drawing into an off-screen buffer, use the alpha channel to encode layer index
return this._drawPickingBuffer(props);
}
// When drawing to screen (debug mode), do not use the alpha channel so that result is always visible
return super.render(props);
}
// Private
// Draws list of layers and viewports into the picking buffer
// Note: does not sample the buffer, that has to be done by the caller
_drawPickingBuffer({ layers, layerFilter, views, viewports, onViewportActive, pickingFBO, deviceRect: { x, y, width, height }, cullRect, effects, pass = 'picking', pickZ, shaderModuleProps }) {
this.pickZ = pickZ;
const colorEncoderState = this._resetColorEncoder(pickZ);
const scissorRect = [x, y, width, height];
// Make sure we clear scissor test and fbo bindings in case of exceptions
// We are only interested in one pixel, no need to render anything else
// Note that the callback here is called synchronously.
// Set blend mode for picking
// always overwrite existing pixel with [r,g,b,layerIndex]
const renderStatus = super.render({
target: pickingFBO,
layers,
layerFilter,
views,
viewports,
onViewportActive,
cullRect,
effects: effects?.filter(e => e.useInPicking),
pass,
isPicking: true,
shaderModuleProps,
clearColor: [0, 0, 0, 0],
colorMask: 0xf,
scissorRect
});
// Clear the temp field
this._colorEncoderState = null;
const decodePickingColor = colorEncoderState && decodeColor.bind(null, colorEncoderState);
return { decodePickingColor, stats: renderStatus };
}
shouldDrawLayer(layer) {
const { pickable, operation } = layer.props;
return ((pickable && operation.includes('draw')) ||
operation.includes('terrain') ||
operation.includes('mask'));
}
getShaderModuleProps(layer, effects, otherShaderModuleProps) {
return {
picking: {
isActive: 1,
isAttribute: this.pickZ
},
lighting: { enabled: false }
};
}
getLayerParameters(layer, layerIndex, viewport) {
// TODO use Parameters type
const pickParameters = {
...layer.props.parameters
};
const { pickable, operation } = layer.props;
if (!this._colorEncoderState || operation.includes('terrain')) {
pickParameters.blend = false;
}
else if (pickable && operation.includes('draw')) {
Object.assign(pickParameters, PICKING_BLENDING);
pickParameters.blend = true;
// TODO: blendColor no longer part of luma.gl API
pickParameters.blendColor = encodeColor(this._colorEncoderState, layer, viewport);
}
return pickParameters;
}
_resetColorEncoder(pickZ) {
// Track encoded layer indices
this._colorEncoderState = pickZ
? null
: {
byLayer: new Map(),
byAlpha: []
};
// Temporarily store it on the instance so that it can be accessed by this.getLayerParameters
return this._colorEncoderState;
}
}
// Assign an unique alpha value for each pickable layer and track the encoding in the cache object
// Returns normalized blend color
function encodeColor(encoded, layer, viewport) {
const { byLayer, byAlpha } = encoded;
let a;
// Encode layerIndex in the alpha channel
// TODO - combine small layers to better utilize the picking color space
let entry = byLayer.get(layer);
if (entry) {
entry.viewports.push(viewport);
a = entry.a;
}
else {
a = byLayer.size + 1;
if (a <= 255) {
entry = { a, layer, viewports: [viewport] };
byLayer.set(layer, entry);
byAlpha[a] = entry;
}
else {
log.warn('Too many pickable layers, only picking the first 255')();
a = 0;
}
}
return [0, 0, 0, a / 255];
}
// Given a picked color, retrieve the corresponding layer and viewports from cache
function decodeColor(encoded, pickedColor) {
const entry = encoded.byAlpha[pickedColor[3]];
return (entry && {
pickedLayer: entry.layer,
pickedViewports: entry.viewports,
pickedObjectIndex: entry.layer.decodePickingColor(pickedColor)
});
}
//# sourceMappingURL=pick-layers-pass.js.map