UNPKG

@deck.gl/layers

Version:

deck.gl core layers

133 lines 4.78 kB
// deck.gl // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors import { Layer, color, project32, picking, UNIT } from '@deck.gl/core'; import { Model, Geometry } from '@luma.gl/engine'; import { gouraudMaterial } from '@luma.gl/shadertools'; import { pointCloudUniforms } from "./point-cloud-layer-uniforms.js"; import vs from "./point-cloud-layer-vertex.glsl.js"; import fs from "./point-cloud-layer-fragment.glsl.js"; import source from "./point-cloud-layer.wgsl.js"; const DEFAULT_COLOR = [0, 0, 0, 255]; const DEFAULT_NORMAL = [0, 0, 1]; const defaultProps = { sizeUnits: 'pixels', pointSize: { type: 'number', min: 0, value: 10 }, // point radius in pixels 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) { const { size, value } = attributes.COLOR_0; attributes.instanceColors = { size, type: 'unorm8', value }; } } /** Render a point cloud with 3D positions, normals and colors. */ class PointCloudLayer extends Layer { getShaders() { return super.getShaders({ vs, fs, source, modules: [project32, color, gouraudMaterial, picking, pointCloudUniforms] }); } initializeState() { this.getAttributeManager().addInstanced({ instancePositions: { size: 3, type: 'float64', fp64: this.use64bitPositions(), transition: true, accessor: 'getPosition' }, instanceNormals: { size: 3, transition: true, accessor: 'getNormal', defaultValue: DEFAULT_NORMAL }, instanceColors: { size: this.props.colorFormat.length, type: 'unorm8', transition: true, accessor: 'getColor', defaultValue: DEFAULT_COLOR } }); } updateState(params) { const { changeFlags, props } = params; super.updateState(params); if (changeFlags.extensionsChanged) { this.state.model?.destroy(); this.state.model = this._getModel(); this.getAttributeManager().invalidateAll(); } if (changeFlags.dataChanged) { normalizeData(props.data); } } draw({ uniforms }) { const { pointSize, sizeUnits } = this.props; const model = this.state.model; const pointCloudProps = { sizeUnits: UNIT[sizeUnits], radiusPixels: pointSize }; model.shaderInputs.setProps({ pointCloud: pointCloudProps }); if (this.context.device.type === 'webgpu') { // @ts-expect-error TODO - this line was needed during WebGPU port model.instanceCount = this.props.data.length; } model.draw(this.context.renderPass); } _getModel() { // TODO(ibgreen): WebGPU complication: Matching attachment state of the renderpass requires including a depth buffer const parameters = this.context.device.type === 'webgpu' ? { depthWriteEnabled: true, depthCompare: 'less-equal' } : undefined; // 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(this.context.device, { ...this.getShaders(), id: this.props.id, bufferLayout: this.getAttributeManager().getBufferLayouts(), geometry: new Geometry({ topology: 'triangle-list', attributes: { positions: new Float32Array(positions) } }), parameters, isInstanced: true }); } } PointCloudLayer.layerName = 'PointCloudLayer'; PointCloudLayer.defaultProps = defaultProps; export default PointCloudLayer; //# sourceMappingURL=point-cloud-layer.js.map