@luma.gl/engine 9.2.6 → 9.3.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animation-loop/animation-loop.d.ts +11 -5
- package/dist/animation-loop/animation-loop.d.ts.map +1 -1
- package/dist/animation-loop/animation-loop.js +83 -47
- package/dist/animation-loop/animation-loop.js.map +1 -1
- package/dist/animation-loop/make-animation-loop.js +7 -1
- package/dist/animation-loop/make-animation-loop.js.map +1 -1
- package/dist/animation-loop/request-animation-frame.d.ts.map +1 -1
- package/dist/animation-loop/request-animation-frame.js +23 -6
- package/dist/animation-loop/request-animation-frame.js.map +1 -1
- package/dist/compute/computation.d.ts +3 -7
- package/dist/compute/computation.d.ts.map +1 -1
- package/dist/compute/computation.js +16 -13
- package/dist/compute/computation.js.map +1 -1
- package/dist/compute/swap.d.ts +2 -0
- package/dist/compute/swap.d.ts.map +1 -1
- package/dist/compute/swap.js +10 -5
- package/dist/compute/swap.js.map +1 -1
- package/dist/dist.dev.js +2639 -1290
- package/dist/dist.min.js +325 -210
- package/dist/dynamic-texture/dynamic-texture.d.ts +102 -0
- package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -0
- package/dist/dynamic-texture/dynamic-texture.js +556 -0
- package/dist/dynamic-texture/dynamic-texture.js.map +1 -0
- package/dist/dynamic-texture/texture-data.d.ts +144 -0
- package/dist/dynamic-texture/texture-data.d.ts.map +1 -0
- package/dist/dynamic-texture/texture-data.js +208 -0
- package/dist/dynamic-texture/texture-data.js.map +1 -0
- package/dist/geometries/cone-geometry.d.ts +3 -1
- package/dist/geometries/cone-geometry.d.ts.map +1 -1
- package/dist/geometries/cone-geometry.js.map +1 -1
- package/dist/geometries/cylinder-geometry.d.ts +2 -1
- package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
- package/dist/geometries/cylinder-geometry.js.map +1 -1
- package/dist/geometry/gpu-geometry.d.ts.map +1 -1
- package/dist/geometry/gpu-geometry.js +8 -3
- package/dist/geometry/gpu-geometry.js.map +1 -1
- package/dist/index.cjs +2497 -1212
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +20 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/material/material-factory.d.ts +73 -0
- package/dist/material/material-factory.d.ts.map +1 -0
- package/dist/material/material-factory.js +111 -0
- package/dist/material/material-factory.js.map +1 -0
- package/dist/material/material.d.ts +84 -0
- package/dist/material/material.d.ts.map +1 -0
- package/dist/material/material.js +176 -0
- package/dist/material/material.js.map +1 -0
- package/dist/model/model.d.ts +47 -16
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +113 -47
- package/dist/model/model.js.map +1 -1
- package/dist/model/split-uniforms-and-bindings.d.ts +4 -3
- package/dist/model/split-uniforms-and-bindings.d.ts.map +1 -1
- package/dist/model/split-uniforms-and-bindings.js +2 -2
- package/dist/model/split-uniforms-and-bindings.js.map +1 -1
- package/dist/models/billboard-texture-model.d.ts +8 -5
- package/dist/models/billboard-texture-model.d.ts.map +1 -1
- package/dist/models/billboard-texture-model.js +77 -23
- package/dist/models/billboard-texture-model.js.map +1 -1
- package/dist/models/billboard-texture-module.d.ts +1 -1
- package/dist/models/billboard-texture-module.js +1 -1
- package/dist/models/clip-space.js +7 -7
- package/dist/models/directional-light-model.d.ts +7 -0
- package/dist/models/directional-light-model.d.ts.map +1 -0
- package/dist/models/directional-light-model.js +23 -0
- package/dist/models/directional-light-model.js.map +1 -0
- package/dist/models/light-model-utils.d.ts +69 -0
- package/dist/models/light-model-utils.d.ts.map +1 -0
- package/dist/models/light-model-utils.js +395 -0
- package/dist/models/light-model-utils.js.map +1 -0
- package/dist/models/point-light-model.d.ts +7 -0
- package/dist/models/point-light-model.d.ts.map +1 -0
- package/dist/models/point-light-model.js +22 -0
- package/dist/models/point-light-model.js.map +1 -0
- package/dist/models/spot-light-model.d.ts +7 -0
- package/dist/models/spot-light-model.d.ts.map +1 -0
- package/dist/models/spot-light-model.js +23 -0
- package/dist/models/spot-light-model.js.map +1 -0
- package/dist/modules/picking/color-picking.d.ts +5 -9
- package/dist/modules/picking/color-picking.d.ts.map +1 -1
- package/dist/modules/picking/color-picking.js +122 -115
- package/dist/modules/picking/color-picking.js.map +1 -1
- package/dist/modules/picking/index-picking.d.ts +4 -4
- package/dist/modules/picking/index-picking.d.ts.map +1 -1
- package/dist/modules/picking/index-picking.js +36 -16
- package/dist/modules/picking/index-picking.js.map +1 -1
- package/dist/modules/picking/legacy-color-picking.d.ts +26 -0
- package/dist/modules/picking/legacy-color-picking.d.ts.map +1 -0
- package/dist/modules/picking/legacy-color-picking.js +7 -0
- package/dist/modules/picking/legacy-color-picking.js.map +1 -0
- package/dist/modules/picking/picking-manager.d.ts +29 -3
- package/dist/modules/picking/picking-manager.d.ts.map +1 -1
- package/dist/modules/picking/picking-manager.js +188 -41
- package/dist/modules/picking/picking-manager.js.map +1 -1
- package/dist/modules/picking/picking-uniforms.d.ts +13 -12
- package/dist/modules/picking/picking-uniforms.d.ts.map +1 -1
- package/dist/modules/picking/picking-uniforms.js +27 -14
- package/dist/modules/picking/picking-uniforms.js.map +1 -1
- package/dist/modules/picking/picking.d.ts +25 -0
- package/dist/modules/picking/picking.d.ts.map +1 -0
- package/dist/modules/picking/picking.js +18 -0
- package/dist/modules/picking/picking.js.map +1 -0
- package/dist/passes/get-fragment-shader.js +12 -27
- package/dist/passes/get-fragment-shader.js.map +1 -1
- package/dist/passes/shader-pass-renderer.d.ts +5 -7
- package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
- package/dist/passes/shader-pass-renderer.js +16 -42
- package/dist/passes/shader-pass-renderer.js.map +1 -1
- package/dist/scenegraph/group-node.d.ts +5 -0
- package/dist/scenegraph/group-node.d.ts.map +1 -1
- package/dist/scenegraph/group-node.js +12 -0
- package/dist/scenegraph/group-node.js.map +1 -1
- package/dist/scenegraph/model-node.d.ts +2 -2
- package/dist/scenegraph/model-node.d.ts.map +1 -1
- package/dist/scenegraph/model-node.js.map +1 -1
- package/dist/scenegraph/scenegraph-node.d.ts +1 -1
- package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
- package/dist/scenegraph/scenegraph-node.js +23 -15
- package/dist/scenegraph/scenegraph-node.js.map +1 -1
- package/dist/shader-inputs.d.ts +9 -7
- package/dist/shader-inputs.d.ts.map +1 -1
- package/dist/shader-inputs.js +84 -4
- package/dist/shader-inputs.js.map +1 -1
- package/dist/utils/buffer-layout-order.d.ts.map +1 -1
- package/dist/utils/buffer-layout-order.js +12 -2
- package/dist/utils/buffer-layout-order.js.map +1 -1
- package/dist/utils/shader-module-utils.d.ts +7 -0
- package/dist/utils/shader-module-utils.d.ts.map +1 -0
- package/dist/utils/shader-module-utils.js +46 -0
- package/dist/utils/shader-module-utils.js.map +1 -0
- package/package.json +6 -6
- package/src/animation-loop/animation-loop.ts +89 -50
- package/src/animation-loop/make-animation-loop.ts +13 -5
- package/src/animation-loop/request-animation-frame.ts +32 -6
- package/src/compute/computation.ts +32 -17
- package/src/compute/swap.ts +13 -7
- package/src/dynamic-texture/dynamic-texture.ts +732 -0
- package/src/dynamic-texture/texture-data.ts +336 -0
- package/src/geometries/cone-geometry.ts +6 -1
- package/src/geometries/cylinder-geometry.ts +5 -1
- package/src/geometry/gpu-geometry.ts +8 -3
- package/src/index.ts +38 -8
- package/src/material/material-factory.ts +157 -0
- package/src/material/material.ts +254 -0
- package/src/model/model.ts +158 -67
- package/src/model/split-uniforms-and-bindings.ts +8 -6
- package/src/models/billboard-texture-model.ts +88 -27
- package/src/models/billboard-texture-module.ts +1 -1
- package/src/models/clip-space.ts +7 -7
- package/src/models/directional-light-model.ts +32 -0
- package/src/models/light-model-utils.ts +587 -0
- package/src/models/point-light-model.ts +31 -0
- package/src/models/spot-light-model.ts +32 -0
- package/src/modules/picking/color-picking.ts +123 -122
- package/src/modules/picking/index-picking.ts +36 -16
- package/src/modules/picking/legacy-color-picking.ts +8 -0
- package/src/modules/picking/picking-manager.ts +252 -50
- package/src/modules/picking/picking-uniforms.ts +39 -24
- package/src/modules/picking/picking.ts +22 -0
- package/src/passes/get-fragment-shader.ts +12 -27
- package/src/passes/shader-pass-renderer.ts +25 -48
- package/src/scenegraph/group-node.ts +16 -0
- package/src/scenegraph/model-node.ts +2 -2
- package/src/scenegraph/scenegraph-node.ts +27 -16
- package/src/shader-inputs.ts +165 -15
- package/src/utils/buffer-layout-order.ts +18 -2
- package/src/utils/shader-module-utils.ts +65 -0
- package/dist/async-texture/async-texture.d.ts +0 -166
- package/dist/async-texture/async-texture.d.ts.map +0 -1
- package/dist/async-texture/async-texture.js +0 -386
- package/dist/async-texture/async-texture.js.map +0 -1
- package/dist/factories/pipeline-factory.d.ts +0 -37
- package/dist/factories/pipeline-factory.d.ts.map +0 -1
- package/dist/factories/pipeline-factory.js +0 -181
- package/dist/factories/pipeline-factory.js.map +0 -1
- package/dist/factories/shader-factory.d.ts +0 -22
- package/dist/factories/shader-factory.d.ts.map +0 -1
- package/dist/factories/shader-factory.js +0 -88
- package/dist/factories/shader-factory.js.map +0 -1
- package/src/async-texture/async-texture.ts +0 -551
- package/src/factories/pipeline-factory.ts +0 -224
- package/src/factories/shader-factory.ts +0 -103
- /package/src/{async-texture/texture-setters.ts.disabled → dynamic-texture/texture-data.ts.disabled} +0 -0
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {Device, Framebuffer} from '@luma.gl/core';
|
|
5
|
+
import {Buffer, Device, Framebuffer, Texture} from '@luma.gl/core';
|
|
6
6
|
import {ShaderInputs} from '../../shader-inputs';
|
|
7
7
|
import {pickingUniforms, INVALID_INDEX} from './picking-uniforms';
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
const INDEX_PICKING_ATTACHMENT_INDEX = 1;
|
|
10
|
+
const INDEX_PICKING_CLEAR_COLOR = new Int32Array([INVALID_INDEX, INVALID_INDEX, 0, 0]);
|
|
11
|
+
const COLOR_PICKING_MAX_OBJECT_INDEX = 16777214;
|
|
12
|
+
const COLOR_PICKING_MAX_BATCH_INDEX = 254;
|
|
9
13
|
|
|
10
14
|
/** Information about picked object */
|
|
11
15
|
export type PickInfo = {
|
|
@@ -13,22 +17,81 @@ export type PickInfo = {
|
|
|
13
17
|
objectIndex: number | null;
|
|
14
18
|
};
|
|
15
19
|
|
|
20
|
+
export type PickingMode = 'auto' | 'index' | 'color';
|
|
21
|
+
export type ResolvedPickingMode = Exclude<PickingMode, 'auto'>;
|
|
22
|
+
/** @deprecated Use `PickingMode`. */
|
|
23
|
+
export type PickingBackend = PickingMode;
|
|
24
|
+
/** @deprecated Use `ResolvedPickingMode`. */
|
|
25
|
+
export type ResolvedPickingBackend = ResolvedPickingMode;
|
|
26
|
+
|
|
16
27
|
export type PickingManagerProps = {
|
|
17
|
-
/** Shader
|
|
28
|
+
/** Shader inputs from models to pick */
|
|
18
29
|
shaderInputs?: ShaderInputs<{picking: typeof pickingUniforms.props}>;
|
|
19
30
|
/** Callback */
|
|
20
31
|
onObjectPicked?: (info: PickInfo) => void;
|
|
32
|
+
/** Select a picking mode. Defaults to `color`. Use `auto` to prefer `index` when supported. */
|
|
33
|
+
mode?: PickingMode;
|
|
34
|
+
/** @deprecated Use `mode`. */
|
|
35
|
+
backend?: PickingBackend;
|
|
21
36
|
};
|
|
22
37
|
|
|
38
|
+
export function resolvePickingMode(
|
|
39
|
+
deviceType: Device['type'],
|
|
40
|
+
mode: PickingMode = 'color',
|
|
41
|
+
indexPickingSupported: boolean = deviceType === 'webgpu'
|
|
42
|
+
): ResolvedPickingMode {
|
|
43
|
+
if (mode === 'auto') {
|
|
44
|
+
return indexPickingSupported ? 'index' : 'color';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (mode === 'index' && !indexPickingSupported) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Picking mode "${mode}" requires WebGPU or a WebGL device that supports renderable rg32sint textures.`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return mode;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function supportsIndexPicking(device: Device): boolean {
|
|
57
|
+
return (
|
|
58
|
+
device.type === 'webgpu' ||
|
|
59
|
+
(device.type === 'webgl' && device.isTextureFormatRenderable('rg32sint'))
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** @deprecated Use `resolvePickingMode`. */
|
|
64
|
+
export const resolvePickingBackend = resolvePickingMode;
|
|
65
|
+
|
|
66
|
+
export function decodeIndexPickInfo(pixelData: Int32Array): PickInfo {
|
|
67
|
+
return {
|
|
68
|
+
objectIndex: pixelData[0] === INVALID_INDEX ? null : pixelData[0],
|
|
69
|
+
batchIndex: pixelData[1] === INVALID_INDEX ? null : pixelData[1]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function decodeColorPickInfo(pixelData: Uint8Array): PickInfo {
|
|
74
|
+
const encodedObjectIndex = pixelData[0] + pixelData[1] * 256 + pixelData[2] * 65536;
|
|
75
|
+
if (encodedObjectIndex === 0) {
|
|
76
|
+
return {objectIndex: null, batchIndex: null};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const batchIndex = pixelData[3] > 0 ? pixelData[3] - 1 : 0;
|
|
80
|
+
return {
|
|
81
|
+
objectIndex: encodedObjectIndex - 1,
|
|
82
|
+
batchIndex
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
23
86
|
/**
|
|
24
|
-
* Helper class for using
|
|
25
|
-
* @todo Port to WebGPU
|
|
87
|
+
* Helper class for using object picking with backend-specific readback.
|
|
26
88
|
* @todo Support multiple models
|
|
27
89
|
* @todo Switching picking module
|
|
28
90
|
*/
|
|
29
91
|
export class PickingManager {
|
|
30
92
|
device: Device;
|
|
31
93
|
props: Required<PickingManagerProps>;
|
|
94
|
+
mode: ResolvedPickingMode;
|
|
32
95
|
/** Info from latest pick operation */
|
|
33
96
|
pickInfo: PickInfo = {batchIndex: null, objectIndex: null};
|
|
34
97
|
/** Framebuffer used for picking */
|
|
@@ -36,12 +99,22 @@ export class PickingManager {
|
|
|
36
99
|
|
|
37
100
|
static defaultProps: Required<PickingManagerProps> = {
|
|
38
101
|
shaderInputs: undefined!,
|
|
39
|
-
onObjectPicked: () => {}
|
|
102
|
+
onObjectPicked: () => {},
|
|
103
|
+
mode: 'color',
|
|
104
|
+
backend: 'color'
|
|
40
105
|
};
|
|
41
106
|
|
|
42
107
|
constructor(device: Device, props: PickingManagerProps) {
|
|
43
108
|
this.device = device;
|
|
44
109
|
this.props = {...PickingManager.defaultProps, ...props};
|
|
110
|
+
const requestedMode = props.mode ?? props.backend ?? PickingManager.defaultProps.mode;
|
|
111
|
+
this.props.mode = requestedMode;
|
|
112
|
+
this.props.backend = requestedMode;
|
|
113
|
+
this.mode = resolvePickingMode(
|
|
114
|
+
this.device.type,
|
|
115
|
+
requestedMode,
|
|
116
|
+
supportsIndexPicking(this.device)
|
|
117
|
+
);
|
|
45
118
|
}
|
|
46
119
|
|
|
47
120
|
destroy() {
|
|
@@ -51,17 +124,15 @@ export class PickingManager {
|
|
|
51
124
|
// TODO - Ask for a cached framebuffer? a Framebuffer factory?
|
|
52
125
|
getFramebuffer() {
|
|
53
126
|
if (!this.framebuffer) {
|
|
54
|
-
this.framebuffer =
|
|
55
|
-
|
|
56
|
-
depthStencilAttachment: 'depth24plus'
|
|
57
|
-
});
|
|
127
|
+
this.framebuffer =
|
|
128
|
+
this.mode === 'index' ? this.createIndexFramebuffer() : this.createColorFramebuffer();
|
|
58
129
|
}
|
|
59
130
|
return this.framebuffer;
|
|
60
131
|
}
|
|
61
132
|
|
|
62
133
|
/** Clear highlighted / picked object */
|
|
63
134
|
clearPickState() {
|
|
64
|
-
this.
|
|
135
|
+
this.setPickingProps({highlightedBatchIndex: null, highlightedObjectIndex: null});
|
|
65
136
|
}
|
|
66
137
|
|
|
67
138
|
/** Prepare for rendering picking colors */
|
|
@@ -69,56 +140,38 @@ export class PickingManager {
|
|
|
69
140
|
const framebuffer = this.getFramebuffer();
|
|
70
141
|
framebuffer.resize(this.device.getDefaultCanvasContext().getDevicePixelSize());
|
|
71
142
|
|
|
72
|
-
this.
|
|
73
|
-
|
|
74
|
-
const pickingPass = this.device.beginRenderPass({
|
|
75
|
-
framebuffer,
|
|
76
|
-
clearColors: [new Float32Array([0, 0, 0, 0]), new Int32Array([-1, -1, 0, 0])],
|
|
77
|
-
clearDepth: 1
|
|
78
|
-
});
|
|
143
|
+
this.setPickingProps({isActive: true});
|
|
79
144
|
|
|
80
|
-
return
|
|
145
|
+
return this.mode === 'index'
|
|
146
|
+
? this.device.beginRenderPass({
|
|
147
|
+
framebuffer,
|
|
148
|
+
clearColors: [new Float32Array([0, 0, 0, 0]), INDEX_PICKING_CLEAR_COLOR],
|
|
149
|
+
clearDepth: 1
|
|
150
|
+
})
|
|
151
|
+
: this.device.beginRenderPass({
|
|
152
|
+
framebuffer,
|
|
153
|
+
clearColor: [0, 0, 0, 0],
|
|
154
|
+
clearDepth: 1
|
|
155
|
+
});
|
|
81
156
|
}
|
|
82
157
|
|
|
83
158
|
async updatePickInfo(mousePosition: [number, number]): Promise<PickInfo | null> {
|
|
84
159
|
const framebuffer = this.getFramebuffer();
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// Read back
|
|
90
|
-
const pixelData = this.device.readPixelsToArrayWebGL(framebuffer, {
|
|
91
|
-
sourceX: pickX,
|
|
92
|
-
sourceY: pickY,
|
|
93
|
-
sourceWidth: 1,
|
|
94
|
-
sourceHeight: 1,
|
|
95
|
-
sourceAttachment: 1
|
|
96
|
-
});
|
|
97
|
-
if (!pixelData) {
|
|
160
|
+
const pickPosition = this.getPickPosition(mousePosition);
|
|
161
|
+
const pickInfo = await this.readPickInfo(framebuffer, pickPosition);
|
|
162
|
+
if (!pickInfo) {
|
|
98
163
|
return null;
|
|
99
164
|
}
|
|
100
165
|
|
|
101
|
-
|
|
102
|
-
objectIndex: pixelData[0] === INVALID_INDEX ? null : pixelData[0],
|
|
103
|
-
batchIndex: pixelData[1] === INVALID_INDEX ? null : pixelData[1]
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// Call callback if picked object has changed
|
|
107
|
-
if (
|
|
108
|
-
pickInfo.objectIndex !== this.pickInfo.objectIndex ||
|
|
109
|
-
pickInfo.batchIndex !== this.pickInfo.batchIndex
|
|
110
|
-
) {
|
|
166
|
+
if (this.hasPickInfoChanged(pickInfo)) {
|
|
111
167
|
this.pickInfo = pickInfo;
|
|
112
168
|
this.props.onObjectPicked(pickInfo);
|
|
113
|
-
// console.log(`Object ${pickInfo.objectIndex} in batch ${pickInfo.batchIndex} was picked`)
|
|
114
169
|
}
|
|
115
170
|
|
|
116
|
-
this.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
highlightedObjectIndex: pickInfo.objectIndex
|
|
121
|
-
}
|
|
171
|
+
this.setPickingProps({
|
|
172
|
+
isActive: false,
|
|
173
|
+
highlightedBatchIndex: pickInfo.batchIndex,
|
|
174
|
+
highlightedObjectIndex: pickInfo.objectIndex
|
|
122
175
|
});
|
|
123
176
|
|
|
124
177
|
return this.pickInfo;
|
|
@@ -129,9 +182,158 @@ export class PickingManager {
|
|
|
129
182
|
* use the center pixel location in device pixel range
|
|
130
183
|
*/
|
|
131
184
|
getPickPosition(mousePosition: [number, number]): [number, number] {
|
|
132
|
-
const
|
|
185
|
+
const yInvert = this.device.type !== 'webgpu';
|
|
186
|
+
const devicePixels = this.device
|
|
187
|
+
.getDefaultCanvasContext()
|
|
188
|
+
.cssToDevicePixels(mousePosition, yInvert);
|
|
133
189
|
const pickX = devicePixels.x + Math.floor(devicePixels.width / 2);
|
|
134
190
|
const pickY = devicePixels.y + Math.floor(devicePixels.height / 2);
|
|
135
191
|
return [pickX, pickY];
|
|
136
192
|
}
|
|
193
|
+
|
|
194
|
+
protected createIndexFramebuffer(): Framebuffer {
|
|
195
|
+
const colorTexture = this.device.createTexture({
|
|
196
|
+
format: 'rgba8unorm',
|
|
197
|
+
width: 1,
|
|
198
|
+
height: 1,
|
|
199
|
+
usage: Texture.RENDER_ATTACHMENT
|
|
200
|
+
});
|
|
201
|
+
const pickingTexture = this.device.createTexture({
|
|
202
|
+
format: 'rg32sint',
|
|
203
|
+
width: 1,
|
|
204
|
+
height: 1,
|
|
205
|
+
usage: Texture.RENDER_ATTACHMENT | Texture.COPY_SRC
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return this.device.createFramebuffer({
|
|
209
|
+
colorAttachments: [colorTexture, pickingTexture],
|
|
210
|
+
depthStencilAttachment: 'depth24plus'
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
protected createColorFramebuffer(): Framebuffer {
|
|
215
|
+
const pickingTexture = this.device.createTexture({
|
|
216
|
+
format: 'rgba8unorm',
|
|
217
|
+
width: 1,
|
|
218
|
+
height: 1,
|
|
219
|
+
usage: Texture.RENDER_ATTACHMENT | Texture.COPY_SRC
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return this.device.createFramebuffer({
|
|
223
|
+
colorAttachments: [pickingTexture],
|
|
224
|
+
depthStencilAttachment: 'depth24plus'
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
protected setPickingProps(props: Partial<typeof pickingUniforms.props>): void {
|
|
229
|
+
this.props.shaderInputs?.setProps({picking: props});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
protected async readPickInfo(
|
|
233
|
+
framebuffer: Framebuffer,
|
|
234
|
+
pickPosition: [number, number]
|
|
235
|
+
): Promise<PickInfo | null> {
|
|
236
|
+
return this.mode === 'index'
|
|
237
|
+
? this.readIndexPickInfo(framebuffer, pickPosition)
|
|
238
|
+
: this.readColorPickInfo(framebuffer, pickPosition);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
protected async readIndexPickInfo(
|
|
242
|
+
framebuffer: Framebuffer,
|
|
243
|
+
[pickX, pickY]: [number, number]
|
|
244
|
+
): Promise<PickInfo | null> {
|
|
245
|
+
if (this.device.type === 'webgpu') {
|
|
246
|
+
const pickTexture = framebuffer.colorAttachments[INDEX_PICKING_ATTACHMENT_INDEX]?.texture;
|
|
247
|
+
if (!pickTexture) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const layout = pickTexture.computeMemoryLayout({width: 1, height: 1});
|
|
252
|
+
const readBuffer = this.device.createBuffer({
|
|
253
|
+
byteLength: layout.byteLength,
|
|
254
|
+
usage: Buffer.COPY_DST | Buffer.MAP_READ
|
|
255
|
+
});
|
|
256
|
+
try {
|
|
257
|
+
pickTexture.readBuffer(
|
|
258
|
+
{
|
|
259
|
+
x: pickX,
|
|
260
|
+
y: pickY,
|
|
261
|
+
width: 1,
|
|
262
|
+
height: 1
|
|
263
|
+
},
|
|
264
|
+
readBuffer
|
|
265
|
+
);
|
|
266
|
+
const pickDataView = await readBuffer.readAsync(0, layout.byteLength);
|
|
267
|
+
return decodeIndexPickInfo(new Int32Array(pickDataView.buffer, pickDataView.byteOffset, 2));
|
|
268
|
+
} finally {
|
|
269
|
+
readBuffer.destroy();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const pixelData = this.device.readPixelsToArrayWebGL(framebuffer, {
|
|
274
|
+
sourceX: pickX,
|
|
275
|
+
sourceY: pickY,
|
|
276
|
+
sourceWidth: 1,
|
|
277
|
+
sourceHeight: 1,
|
|
278
|
+
sourceAttachment: INDEX_PICKING_ATTACHMENT_INDEX
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
return pixelData
|
|
282
|
+
? decodeIndexPickInfo(new Int32Array(pixelData.buffer, pixelData.byteOffset, 2))
|
|
283
|
+
: null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
protected async readColorPickInfo(
|
|
287
|
+
framebuffer: Framebuffer,
|
|
288
|
+
[pickX, pickY]: [number, number]
|
|
289
|
+
): Promise<PickInfo | null> {
|
|
290
|
+
if (this.device.type === 'webgpu') {
|
|
291
|
+
const pickTexture = framebuffer.colorAttachments[0]?.texture;
|
|
292
|
+
if (!pickTexture) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const layout = pickTexture.computeMemoryLayout({width: 1, height: 1});
|
|
297
|
+
const readBuffer = this.device.createBuffer({
|
|
298
|
+
byteLength: layout.byteLength,
|
|
299
|
+
usage: Buffer.COPY_DST | Buffer.MAP_READ
|
|
300
|
+
});
|
|
301
|
+
try {
|
|
302
|
+
pickTexture.readBuffer(
|
|
303
|
+
{
|
|
304
|
+
x: pickX,
|
|
305
|
+
y: pickY,
|
|
306
|
+
width: 1,
|
|
307
|
+
height: 1
|
|
308
|
+
},
|
|
309
|
+
readBuffer
|
|
310
|
+
);
|
|
311
|
+
const pickDataView = await readBuffer.readAsync(0, layout.byteLength);
|
|
312
|
+
return decodeColorPickInfo(new Uint8Array(pickDataView.buffer, pickDataView.byteOffset, 4));
|
|
313
|
+
} finally {
|
|
314
|
+
readBuffer.destroy();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const pixelData = this.device.readPixelsToArrayWebGL(framebuffer, {
|
|
319
|
+
sourceX: pickX,
|
|
320
|
+
sourceY: pickY,
|
|
321
|
+
sourceWidth: 1,
|
|
322
|
+
sourceHeight: 1,
|
|
323
|
+
sourceAttachment: 0
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
return pixelData
|
|
327
|
+
? decodeColorPickInfo(new Uint8Array(pixelData.buffer, pixelData.byteOffset, 4))
|
|
328
|
+
: null;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
protected hasPickInfoChanged(pickInfo: PickInfo): boolean {
|
|
332
|
+
return (
|
|
333
|
+
pickInfo.objectIndex !== this.pickInfo.objectIndex ||
|
|
334
|
+
pickInfo.batchIndex !== this.pickInfo.batchIndex
|
|
335
|
+
);
|
|
336
|
+
}
|
|
137
337
|
}
|
|
338
|
+
|
|
339
|
+
export {COLOR_PICKING_MAX_BATCH_INDEX, COLOR_PICKING_MAX_OBJECT_INDEX};
|
|
@@ -9,6 +9,7 @@ import type {ShaderModule} from '@luma.gl/shadertools';
|
|
|
9
9
|
const DEFAULT_HIGHLIGHT_COLOR: NumberArray4 = [0, 1, 1, 1];
|
|
10
10
|
|
|
11
11
|
export const INVALID_INDEX = -1;
|
|
12
|
+
export type PickingPayloadMode = 'instance' | 'attribute';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Props for the picking module, which depending on mode renders picking colors or highlighted item.
|
|
@@ -19,14 +20,14 @@ export const INVALID_INDEX = -1;
|
|
|
19
20
|
export type PickingProps = {
|
|
20
21
|
/** Are we picking? I.e. rendering picking colors? */
|
|
21
22
|
isActive?: boolean;
|
|
22
|
-
/** Whether
|
|
23
|
-
indexMode?:
|
|
24
|
-
/**
|
|
23
|
+
/** Whether the payload is sourced from the builtin instance index or a custom integer attribute */
|
|
24
|
+
indexMode?: PickingPayloadMode;
|
|
25
|
+
/** Identifier of the batch currently being rendered */
|
|
25
26
|
batchIndex?: number;
|
|
26
27
|
|
|
27
|
-
/**
|
|
28
|
+
/** Identifier of the highlighted batch */
|
|
28
29
|
highlightedBatchIndex?: number | null;
|
|
29
|
-
/** Set
|
|
30
|
+
/** Set the highlighted object index, or `null` to explicitly clear **/
|
|
30
31
|
highlightedObjectIndex?: number | null;
|
|
31
32
|
/** Color of visual highlight of "selected" item () */
|
|
32
33
|
highlightColor?: NumberArray4;
|
|
@@ -43,18 +44,18 @@ export type PickingUniforms = {
|
|
|
43
44
|
* When false, renders normal colors, with the exception of selected object which is rendered with highlight
|
|
44
45
|
*/
|
|
45
46
|
isActive: boolean;
|
|
46
|
-
/**
|
|
47
|
+
/** Whether the current payload comes from instance_index or a custom integer attribute */
|
|
47
48
|
indexMode: 0 | 1;
|
|
48
|
-
/**
|
|
49
|
+
/** Identifier of the batch currently being rendered */
|
|
49
50
|
batchIndex: number;
|
|
50
51
|
|
|
51
52
|
/** Do we have a highlighted item? */
|
|
52
53
|
isHighlightActive: boolean;
|
|
53
54
|
/** Color of visual highlight of "selected" item. Note: RGBA components must in the range 0-1 */
|
|
54
55
|
highlightColor: NumberArray4;
|
|
55
|
-
/** Indicates which batch to visually highlight an item in
|
|
56
|
+
/** Indicates which batch to visually highlight an item in */
|
|
56
57
|
highlightedBatchIndex: number;
|
|
57
|
-
/** Indicates which index in the batch to highlight
|
|
58
|
+
/** Indicates which object index in the batch to highlight */
|
|
58
59
|
highlightedObjectIndex: number;
|
|
59
60
|
};
|
|
60
61
|
|
|
@@ -77,7 +78,7 @@ export const GLSL_UNIFORMS = /* glsl */ `\
|
|
|
77
78
|
precision highp float;
|
|
78
79
|
precision highp int;
|
|
79
80
|
|
|
80
|
-
uniform pickingUniforms {
|
|
81
|
+
layout(std140) uniform pickingUniforms {
|
|
81
82
|
int isActive;
|
|
82
83
|
int indexMode;
|
|
83
84
|
int batchIndex;
|
|
@@ -91,15 +92,17 @@ uniform pickingUniforms {
|
|
|
91
92
|
|
|
92
93
|
export const WGSL_UNIFORMS = /* wgsl */ `\
|
|
93
94
|
struct pickingUniforms {
|
|
94
|
-
isActive:
|
|
95
|
-
indexMode:
|
|
96
|
-
batchIndex:
|
|
97
|
-
|
|
98
|
-
isHighlightActive:
|
|
99
|
-
highlightedBatchIndex:
|
|
100
|
-
highlightedObjectIndex:
|
|
101
|
-
highlightColor: vec4<f32
|
|
102
|
-
}
|
|
95
|
+
isActive: i32,
|
|
96
|
+
indexMode: i32,
|
|
97
|
+
batchIndex: i32,
|
|
98
|
+
|
|
99
|
+
isHighlightActive: i32,
|
|
100
|
+
highlightedBatchIndex: i32,
|
|
101
|
+
highlightedObjectIndex: i32,
|
|
102
|
+
highlightColor: vec4<f32>,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
@group(0) @binding(auto) var<uniform> picking: pickingUniforms;
|
|
103
106
|
`;
|
|
104
107
|
|
|
105
108
|
function getUniforms(props: PickingProps = {}, prevUniforms?: PickingUniforms): PickingUniforms {
|
|
@@ -114,7 +117,7 @@ function getUniforms(props: PickingProps = {}, prevUniforms?: PickingUniforms):
|
|
|
114
117
|
case 'instance':
|
|
115
118
|
uniforms.indexMode = 0;
|
|
116
119
|
break;
|
|
117
|
-
case '
|
|
120
|
+
case 'attribute':
|
|
118
121
|
uniforms.indexMode = 1;
|
|
119
122
|
break;
|
|
120
123
|
case undefined:
|
|
@@ -122,9 +125,13 @@ function getUniforms(props: PickingProps = {}, prevUniforms?: PickingUniforms):
|
|
|
122
125
|
break;
|
|
123
126
|
}
|
|
124
127
|
|
|
128
|
+
if (typeof props.batchIndex === 'number') {
|
|
129
|
+
uniforms.batchIndex = props.batchIndex;
|
|
130
|
+
}
|
|
131
|
+
|
|
125
132
|
switch (props.highlightedObjectIndex) {
|
|
126
133
|
case undefined:
|
|
127
|
-
// Unless
|
|
134
|
+
// Unless highlighted payload explicitly null or set, do not update state
|
|
128
135
|
break;
|
|
129
136
|
case null:
|
|
130
137
|
// Clear highlight
|
|
@@ -136,8 +143,16 @@ function getUniforms(props: PickingProps = {}, prevUniforms?: PickingUniforms):
|
|
|
136
143
|
uniforms.highlightedObjectIndex = props.highlightedObjectIndex;
|
|
137
144
|
}
|
|
138
145
|
|
|
139
|
-
|
|
140
|
-
|
|
146
|
+
switch (props.highlightedBatchIndex) {
|
|
147
|
+
case undefined:
|
|
148
|
+
break;
|
|
149
|
+
case null:
|
|
150
|
+
uniforms.isHighlightActive = false;
|
|
151
|
+
uniforms.highlightedBatchIndex = INVALID_INDEX;
|
|
152
|
+
break;
|
|
153
|
+
default:
|
|
154
|
+
uniforms.isHighlightActive = true;
|
|
155
|
+
uniforms.highlightedBatchIndex = props.highlightedBatchIndex;
|
|
141
156
|
}
|
|
142
157
|
|
|
143
158
|
if (props.highlightColor) {
|
|
@@ -169,7 +184,7 @@ export const pickingUniforms = {
|
|
|
169
184
|
isActive: false,
|
|
170
185
|
indexMode: 0,
|
|
171
186
|
batchIndex: 0,
|
|
172
|
-
isHighlightActive:
|
|
187
|
+
isHighlightActive: false,
|
|
173
188
|
highlightedBatchIndex: INVALID_INDEX,
|
|
174
189
|
highlightedObjectIndex: INVALID_INDEX,
|
|
175
190
|
highlightColor: DEFAULT_HIGHLIGHT_COLOR
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// luma.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {ShaderModule} from '@luma.gl/shadertools';
|
|
6
|
+
|
|
7
|
+
import type {PickingBindings, PickingProps, PickingUniforms} from './picking-uniforms';
|
|
8
|
+
import {pickingUniforms} from './picking-uniforms';
|
|
9
|
+
import {picking as colorPicking} from './color-picking';
|
|
10
|
+
import {picking as indexPicking} from './index-picking';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Unified object-picking shader module.
|
|
14
|
+
* Uses color picking on GLSL/WebGL paths and index picking on WGSL/WebGPU paths.
|
|
15
|
+
*/
|
|
16
|
+
export const picking = {
|
|
17
|
+
...pickingUniforms,
|
|
18
|
+
name: 'picking',
|
|
19
|
+
source: indexPicking.source,
|
|
20
|
+
vs: colorPicking.vs,
|
|
21
|
+
fs: colorPicking.fs
|
|
22
|
+
} as const satisfies ShaderModule<PickingProps, PickingUniforms, PickingBindings>;
|
|
@@ -36,22 +36,16 @@ export function getFragmentShaderForRenderPass(options: {
|
|
|
36
36
|
/** Get a filtering WGSL fragment shader */
|
|
37
37
|
function getFilterShaderWGSL(func: string) {
|
|
38
38
|
return /* wgsl */ `\
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@group(0) @binding(1) var texture: texture_2d<f32>;
|
|
42
|
-
@group(0) @binding(2) var sampler: sampler;
|
|
43
|
-
|
|
44
|
-
struct FragmentInputs {
|
|
45
|
-
@location(0) fragUV: vec2f,
|
|
46
|
-
@location(1) fragPosition: vec4f,
|
|
47
|
-
@location(2) fragCoordinate: vec4f
|
|
48
|
-
};
|
|
39
|
+
@group(0) @binding(0) var sourceTexture: texture_2d<f32>;
|
|
40
|
+
@group(0) @binding(2) var sourceTextureSampler: sampler;
|
|
49
41
|
|
|
50
42
|
@fragment
|
|
51
43
|
fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
|
|
52
|
-
let
|
|
53
|
-
|
|
54
|
-
|
|
44
|
+
let texCoord = inputs.coordinate;
|
|
45
|
+
let texSize = vec2f(textureDimensions(sourceTexture));
|
|
46
|
+
|
|
47
|
+
var fragColor = textureSample(sourceTexture, sourceTextureSampler, texCoord);
|
|
48
|
+
fragColor = ${func}(fragColor, texSize, texCoord);
|
|
55
49
|
return fragColor;
|
|
56
50
|
}
|
|
57
51
|
`;
|
|
@@ -60,23 +54,14 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
|
|
|
60
54
|
/** Get a sampling WGSL fragment shader */
|
|
61
55
|
function getSamplerShaderWGSL(func: string) {
|
|
62
56
|
return /* wgsl */ `\
|
|
63
|
-
|
|
64
|
-
@group(0) @binding(
|
|
65
|
-
@group(0) @binding(1) var texture: texture_2d<f32>;
|
|
66
|
-
@group(0) @binding(2) var sampler: sampler;
|
|
67
|
-
|
|
68
|
-
struct FragmentInputs = {
|
|
69
|
-
@location(0) fragUV: vec2f,
|
|
70
|
-
@location(1) fragPosition: vec4f,
|
|
71
|
-
@location(2) fragCoordinate: vec4f
|
|
72
|
-
};
|
|
57
|
+
@group(0) @binding(0) var sourceTexture: texture_2d<f32>;
|
|
58
|
+
@group(0) @binding(2) var sourceTextureSampler: sampler;
|
|
73
59
|
|
|
74
60
|
@fragment
|
|
75
61
|
fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
|
|
76
|
-
let
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return fragColor;
|
|
62
|
+
let texCoord = inputs.coordinate;
|
|
63
|
+
let texSize = vec2f(textureDimensions(sourceTexture));
|
|
64
|
+
return ${func}(sourceTexture, sourceTextureSampler, texSize, texCoord);
|
|
80
65
|
}
|
|
81
66
|
`;
|
|
82
67
|
}
|