UNPKG

@deck.gl/experimental-layers

Version:

Experimental layers for deck.gl

160 lines (141 loc) 5.31 kB
// Copyright (c) 2015 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import {Layer} from '@deck.gl/core'; import GL from 'luma.gl/constants'; import {Model, Geometry, loadTextures} from 'luma.gl'; import BITMAP_VERTEX_SHADER from './bitmap-layer-vertex'; import BITMAP_FRAGMENT_SHADER from './bitmap-layer-fragment'; // Note: needs to match vertex shader const MAX_BITMAPS = 11; const defaultProps = { images: {type: 'array', value: []}, desaturate: {type: 'number', min: 0, max: 1, value: 0}, blendMode: null, // More context: because of the blending mode we're using for ground imagery, // alpha is not effective when blending the bitmap layers with the base map. // Instead we need to manually dim/blend rgb values with a background color. transparentColor: {type: 'color', value: [0, 0, 0, 0]}, tintColor: {type: 'color', value: [255, 255, 255]}, // accessors getCenter: {type: 'accessor', value: x => x.center}, getRotation: {type: 'accessor', value: x => x.rotation} }; /* * @class * @param {object} props * @param {number} props.transparentColor - color to interpret transparency to * @param {number} props.tintColor - color bias */ export default class BitmapLayer extends Layer { initializeState() { const {gl} = this.context; this.setState({model: this.getModel(gl)}); const {attributeManager} = this.state; attributeManager.addInstanced({ instanceCenter: {size: 3, accessor: 'getCenter'}, instanceRotation: {size: 3, accessor: 'getRotation'}, instanceBitmapIndex: {size: 1, update: this.calculateInstanceBitmapIndex} }); } updateState({props, oldProps}) { if (props.images !== oldProps.images) { let changed = !oldProps.images || props.images.length !== oldProps.images.length; if (!changed) { for (let i = 0; i < props.images.length; ++i) { changed = changed || props.images[i] !== oldProps.images[i]; } } if (changed) { this.loadMapImagesToTextures(); } } const {desaturate} = props; this.state.model.setUniforms({desaturate}); } getModel(gl) { // Two triangles making up a square to render the bitmap texture on const verts = [[1, 1, 0], [-1, 1, 0], [1, -1, 0], [-1, 1, 0], [1, -1, 0], [-1, -1, 0]]; const positions = []; const texCoords = []; verts.forEach(vertex => { // geometry: unit square centered on origin positions.push(vertex[0] / 2, vertex[1] / 2, vertex[2] / 2); // texture: unit square with bottom left in origin texCoords.push(vertex[0] / 2 + 0.5, -vertex[1] / 2 + 0.5); }); const model = new Model(gl, { id: this.props.id, vs: BITMAP_VERTEX_SHADER, fs: BITMAP_FRAGMENT_SHADER, shaderCache: this.context.shaderCache, geometry: new Geometry({ drawMode: GL.TRIANGLES, vertexCount: 6, attributes: { positions: new Float32Array(positions), texCoords: new Float32Array(texCoords) } }), isInstanced: true }); return model; } draw({uniforms}) { const {transparentColor, tintColor} = this.props; // TODO fix zFighting // Render the image this.state.model.render( Object.assign({}, uniforms, { transparentColor, tintColor }) ); } loadMapImagesToTextures() { const {model} = this.state; const {images} = this.props; for (let i = 0; i < Math.min(images.length, MAX_BITMAPS); i++) { loadTextures(this.context.gl, { urls: [images[i]] }).then(([texture]) => { return model.setUniforms({[`uBitmap${i}`]: texture}); }); } } getBitmapIndex(point) { const url = point.imageUrl; const idx = Math.max(this.props.images.indexOf(url), 0); return idx >= MAX_BITMAPS ? 0 : idx; } calculateInstanceBitmapIndex(attribute) { const {data} = this.props; const {value, size} = attribute; let i = 0; for (const point of data) { const bitmapIndex = Number.isFinite(point.bitmapIndex) ? point.bitmapIndex : this.getBitmapIndex(point); value[i] = bitmapIndex; i += size; } } } BitmapLayer.layerName = 'BitmapLayer'; BitmapLayer.defaultProps = defaultProps;