@deck.gl/layers
Version:
deck.gl core layers
129 lines (128 loc) • 4.87 kB
JavaScript
// Copyright (c) 2015 - 2017 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, project32, gouraudLighting, picking, UNIT } from '@deck.gl/core';
import GL from '@luma.gl/constants';
import { Model, Geometry } from '@luma.gl/core';
import vs from './point-cloud-layer-vertex.glsl';
import fs from './point-cloud-layer-fragment.glsl';
const DEFAULT_COLOR = [0, 0, 0, 255];
const DEFAULT_NORMAL = [0, 0, 1];
const defaultProps = {
sizeUnits: 'pixels',
pointSize: { type: 'number', min: 0, value: 10 },
getPosition: { type: 'accessor', value: x => x.position },
getNormal: { type: 'accessor', value: DEFAULT_NORMAL },
getColor: { type: 'accessor', value: DEFAULT_COLOR },
material: true,
// Depreated
radiusPixels: { deprecatedFor: 'pointSize' }
};
// support loaders.gl point cloud format
function normalizeData(data) {
const { header, attributes } = data;
if (!header || !attributes) {
return;
}
data.length = header.vertexCount;
if (attributes.POSITION) {
attributes.instancePositions = attributes.POSITION;
}
if (attributes.NORMAL) {
attributes.instanceNormals = attributes.NORMAL;
}
if (attributes.COLOR_0) {
attributes.instanceColors = attributes.COLOR_0;
}
}
/** The Point Cloud Layer takes in points with 3d positions, normals and colors and renders them as spheres with a certain radius. */
export default class PointCloudLayer extends Layer {
getShaders() {
return super.getShaders({ vs, fs, modules: [project32, gouraudLighting, picking] });
}
initializeState() {
this.getAttributeManager().addInstanced({
instancePositions: {
size: 3,
type: GL.DOUBLE,
fp64: this.use64bitPositions(),
transition: true,
accessor: 'getPosition'
},
instanceNormals: {
size: 3,
transition: true,
accessor: 'getNormal',
defaultValue: DEFAULT_NORMAL
},
instanceColors: {
size: this.props.colorFormat.length,
type: GL.UNSIGNED_BYTE,
normalized: true,
transition: true,
accessor: 'getColor',
defaultValue: DEFAULT_COLOR
}
});
}
updateState(params) {
const { changeFlags, props } = params;
super.updateState(params);
if (changeFlags.extensionsChanged) {
const { gl } = this.context;
this.state.model?.delete();
this.state.model = this._getModel(gl);
this.getAttributeManager().invalidateAll();
}
if (changeFlags.dataChanged) {
normalizeData(props.data);
}
}
draw({ uniforms }) {
const { pointSize, sizeUnits } = this.props;
this.state.model
.setUniforms(uniforms)
.setUniforms({
sizeUnits: UNIT[sizeUnits],
radiusPixels: pointSize
})
.draw();
}
_getModel(gl) {
// a triangle that minimally cover the unit circle
const positions = [];
for (let i = 0; i < 3; i++) {
const angle = (i / 3) * Math.PI * 2;
positions.push(Math.cos(angle) * 2, Math.sin(angle) * 2, 0);
}
return new Model(gl, {
...this.getShaders(),
id: this.props.id,
geometry: new Geometry({
drawMode: GL.TRIANGLES,
attributes: {
positions: new Float32Array(positions)
}
}),
isInstanced: true
});
}
}
PointCloudLayer.layerName = 'PointCloudLayer';
PointCloudLayer.defaultProps = defaultProps;