@luma.gl/engine 9.1.0-alpha.2 → 9.1.0-beta.1
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/key-frames.js +1 -0
- package/dist/animation/key-frames.js.map +1 -0
- package/dist/animation/timeline.js +1 -0
- package/dist/animation/timeline.js.map +1 -0
- package/dist/animation-loop/animation-loop-template.js +1 -0
- package/dist/animation-loop/animation-loop-template.js.map +1 -0
- package/dist/animation-loop/animation-loop.d.ts +2 -0
- package/dist/animation-loop/animation-loop.d.ts.map +1 -1
- package/dist/animation-loop/animation-loop.js +24 -6
- package/dist/animation-loop/animation-loop.js.map +1 -0
- package/dist/animation-loop/animation-props.js +1 -0
- package/dist/animation-loop/animation-props.js.map +1 -0
- package/dist/animation-loop/make-animation-loop.d.ts +5 -1
- package/dist/animation-loop/make-animation-loop.d.ts.map +1 -1
- package/dist/animation-loop/make-animation-loop.js +3 -1
- package/dist/animation-loop/make-animation-loop.js.map +1 -0
- package/dist/animation-loop/request-animation-frame.d.ts +4 -2
- package/dist/animation-loop/request-animation-frame.d.ts.map +1 -1
- package/dist/animation-loop/request-animation-frame.js +5 -3
- package/dist/animation-loop/request-animation-frame.js.map +1 -0
- package/dist/application-utils/load-file.d.ts +1 -1
- package/dist/application-utils/load-file.d.ts.map +1 -1
- package/dist/application-utils/load-file.js +2 -2
- package/dist/application-utils/load-file.js.map +1 -0
- package/dist/application-utils/random.js +1 -0
- package/dist/application-utils/random.js.map +1 -0
- package/dist/async-texture/async-texture.d.ts +14 -2
- package/dist/async-texture/async-texture.d.ts.map +1 -1
- package/dist/async-texture/async-texture.js +40 -3
- package/dist/async-texture/async-texture.js.map +1 -0
- package/dist/compute/buffer-transform.d.ts +41 -0
- package/dist/compute/buffer-transform.d.ts.map +1 -0
- package/dist/{transform → compute}/buffer-transform.js +19 -12
- package/dist/compute/buffer-transform.js.map +1 -0
- package/dist/{computation.d.ts → compute/computation.d.ts} +3 -3
- package/dist/compute/computation.d.ts.map +1 -0
- package/dist/{computation.js → compute/computation.js} +7 -8
- package/dist/compute/computation.js.map +1 -0
- package/dist/compute/swap.d.ts +48 -0
- package/dist/compute/swap.d.ts.map +1 -0
- package/dist/compute/swap.js +91 -0
- package/dist/compute/swap.js.map +1 -0
- package/dist/{transform → compute}/texture-transform.d.ts +0 -6
- package/dist/compute/texture-transform.d.ts.map +1 -0
- package/dist/{transform → compute}/texture-transform.js +10 -15
- package/dist/compute/texture-transform.js.map +1 -0
- package/dist/debug/copy-texture-to-image.js +1 -0
- package/dist/debug/copy-texture-to-image.js.map +1 -0
- package/dist/debug/debug-framebuffer.js +2 -1
- package/dist/debug/debug-framebuffer.js.map +1 -0
- package/dist/debug/debug-shader-layout.js +2 -1
- package/dist/debug/debug-shader-layout.js.map +1 -0
- package/dist/debug/pixel-data-utils.js +1 -0
- package/dist/debug/pixel-data-utils.js.map +1 -0
- package/dist/dist.dev.js +2952 -5816
- package/dist/dist.min.js +422 -91
- package/dist/{lib → factories}/pipeline-factory.d.ts +11 -1
- package/dist/factories/pipeline-factory.d.ts.map +1 -0
- package/dist/factories/pipeline-factory.js +181 -0
- package/dist/factories/pipeline-factory.js.map +1 -0
- package/dist/{lib → factories}/shader-factory.d.ts +5 -1
- package/dist/factories/shader-factory.d.ts.map +1 -0
- package/dist/{lib → factories}/shader-factory.js +39 -4
- package/dist/factories/shader-factory.js.map +1 -0
- package/dist/geometries/cone-geometry.js +1 -0
- package/dist/geometries/cone-geometry.js.map +1 -0
- package/dist/geometries/cube-geometry.js +1 -0
- package/dist/geometries/cube-geometry.js.map +1 -0
- package/dist/geometries/cylinder-geometry.js +1 -0
- package/dist/geometries/cylinder-geometry.js.map +1 -0
- package/dist/geometries/ico-sphere-geometry.js +1 -0
- package/dist/geometries/ico-sphere-geometry.js.map +1 -0
- package/dist/geometries/plane-geometry.js +1 -0
- package/dist/geometries/plane-geometry.js.map +1 -0
- package/dist/geometries/sphere-geometry.js +1 -0
- package/dist/geometries/sphere-geometry.js.map +1 -0
- package/dist/geometries/truncated-cone-geometry.js +1 -0
- package/dist/geometries/truncated-cone-geometry.js.map +1 -0
- package/dist/geometry/geometry-table.js +1 -0
- package/dist/geometry/geometry-table.js.map +1 -0
- package/dist/geometry/geometry-utils.js +1 -0
- package/dist/geometry/geometry-utils.js.map +1 -0
- package/dist/geometry/geometry.js +1 -0
- package/dist/geometry/geometry.js.map +1 -0
- package/dist/geometry/gpu-geometry.js +1 -0
- package/dist/geometry/gpu-geometry.js.map +1 -0
- package/dist/geometry/gpu-table.js +1 -0
- package/dist/geometry/gpu-table.js.map +1 -0
- package/dist/index.cjs +1440 -217
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +23 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -9
- package/dist/index.js.map +1 -0
- package/dist/model/model.d.ts +11 -10
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +92 -69
- package/dist/model/model.js.map +1 -0
- package/dist/model/split-uniforms-and-bindings.d.ts +1 -1
- package/dist/model/split-uniforms-and-bindings.d.ts.map +1 -1
- package/dist/model/split-uniforms-and-bindings.js +2 -1
- package/dist/model/split-uniforms-and-bindings.js.map +1 -0
- package/dist/models/billboard-texture-model.d.ts +23 -0
- package/dist/models/billboard-texture-model.d.ts.map +1 -0
- package/dist/models/billboard-texture-model.js +78 -0
- package/dist/models/billboard-texture-model.js.map +1 -0
- package/dist/models/billboard-texture-module.d.ts +10 -0
- package/dist/models/billboard-texture-module.d.ts.map +1 -0
- package/dist/models/billboard-texture-module.js +37 -0
- package/dist/models/billboard-texture-module.js.map +1 -0
- package/dist/{lib → models}/clip-space.d.ts +3 -1
- package/dist/models/clip-space.d.ts.map +1 -0
- package/dist/models/clip-space.js +77 -0
- package/dist/models/clip-space.js.map +1 -0
- package/dist/modules/picking/color-picking.d.ts +28 -0
- package/dist/modules/picking/color-picking.d.ts.map +1 -0
- package/dist/modules/picking/color-picking.js +177 -0
- package/dist/modules/picking/color-picking.js.map +1 -0
- package/dist/modules/picking/index-picking.d.ts +32 -0
- package/dist/modules/picking/index-picking.d.ts.map +1 -0
- package/dist/modules/picking/index-picking.js +148 -0
- package/dist/modules/picking/index-picking.js.map +1 -0
- package/dist/modules/picking/legacy-picking-manager.d.ts +27 -0
- package/dist/modules/picking/legacy-picking-manager.d.ts.map +1 -0
- package/dist/modules/picking/legacy-picking-manager.js +76 -0
- package/dist/modules/picking/legacy-picking-manager.js.map +1 -0
- package/dist/modules/picking/picking-manager.d.ts +45 -0
- package/dist/modules/picking/picking-manager.d.ts.map +1 -0
- package/dist/modules/picking/picking-manager.js +101 -0
- package/dist/modules/picking/picking-manager.js.map +1 -0
- package/dist/modules/picking/picking-uniforms.d.ts +79 -0
- package/dist/modules/picking/picking-uniforms.d.ts.map +1 -0
- package/dist/modules/picking/picking-uniforms.js +109 -0
- package/dist/modules/picking/picking-uniforms.js.map +1 -0
- package/dist/passes/get-fragment-shader.d.ts +12 -0
- package/dist/passes/get-fragment-shader.d.ts.map +1 -0
- package/dist/passes/get-fragment-shader.js +117 -0
- package/dist/passes/get-fragment-shader.js.map +1 -0
- package/dist/passes/shader-pass-renderer.d.ts +63 -0
- package/dist/passes/shader-pass-renderer.d.ts.map +1 -0
- package/dist/passes/shader-pass-renderer.js +197 -0
- package/dist/passes/shader-pass-renderer.js.map +1 -0
- package/dist/scenegraph/group-node.js +1 -0
- package/dist/scenegraph/group-node.js.map +1 -0
- package/dist/scenegraph/model-node.js +1 -0
- package/dist/scenegraph/model-node.js.map +1 -0
- package/dist/scenegraph/scenegraph-node.js +1 -0
- package/dist/scenegraph/scenegraph-node.js.map +1 -0
- package/dist/shader-inputs.d.ts +8 -19
- package/dist/shader-inputs.d.ts.map +1 -1
- package/dist/shader-inputs.js +28 -18
- package/dist/shader-inputs.js.map +1 -0
- package/dist/utils/deep-equal.js +1 -0
- package/dist/utils/deep-equal.js.map +1 -0
- package/dist/utils/uid.js +1 -0
- package/dist/utils/uid.js.map +1 -0
- package/package.json +6 -6
- package/src/animation-loop/animation-loop.ts +27 -6
- package/src/animation-loop/make-animation-loop.ts +8 -3
- package/src/animation-loop/request-animation-frame.ts +4 -3
- package/src/application-utils/load-file.ts +2 -4
- package/src/async-texture/async-texture.ts +48 -10
- package/src/{transform → compute}/buffer-transform.ts +30 -14
- package/src/{computation.ts → compute/computation.ts} +14 -8
- package/src/compute/swap.ts +116 -0
- package/src/{transform → compute}/texture-transform.ts +9 -18
- package/src/debug/debug-framebuffer.ts +1 -1
- package/src/debug/debug-shader-layout.ts +1 -1
- package/src/factories/pipeline-factory.ts +222 -0
- package/src/{lib → factories}/shader-factory.ts +41 -5
- package/src/index.ts +35 -16
- package/src/model/model.ts +132 -77
- package/src/model/split-uniforms-and-bindings.ts +4 -4
- package/src/models/billboard-texture-model.ts +98 -0
- package/src/models/billboard-texture-module.ts +49 -0
- package/src/models/clip-space.ts +88 -0
- package/src/modules/picking/README.md +88 -0
- package/src/modules/picking/color-picking.ts +190 -0
- package/src/modules/picking/index-picking.ts +156 -0
- package/src/modules/picking/legacy-picking-manager.ts +99 -0
- package/src/modules/picking/picking-manager.ts +137 -0
- package/src/modules/picking/picking-uniforms.ts +179 -0
- package/src/passes/get-fragment-shader.ts +129 -0
- package/src/passes/shader-pass-renderer.ts +252 -0
- package/src/shader-inputs.ts +44 -59
- package/dist/computation.d.ts.map +0 -1
- package/dist/lib/clip-space.d.ts.map +0 -1
- package/dist/lib/clip-space.js +0 -46
- package/dist/lib/pipeline-factory.d.ts.map +0 -1
- package/dist/lib/pipeline-factory.js +0 -98
- package/dist/lib/shader-factory.d.ts.map +0 -1
- package/dist/transform/buffer-transform.d.ts +0 -35
- package/dist/transform/buffer-transform.d.ts.map +0 -1
- package/dist/transform/texture-transform.d.ts.map +0 -1
- package/src/lib/clip-space.ts +0 -53
- package/src/lib/pipeline-factory.ts +0 -126
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Shader Module: Picking
|
|
2
|
+
|
|
3
|
+
Provides support for color-coding-based picking and highlighting. In particular, supports picking a specific instance in an instanced draw call and highlighting an instance based on its picking color, and correspondingly, supports picking and highlighting groups of primitives with the same picking color in non-instanced draw-calls
|
|
4
|
+
|
|
5
|
+
Color based picking lets the application draw a primitive with a color that can later be used to index this specific primitive.
|
|
6
|
+
|
|
7
|
+
Highlighting allows application to specify a picking color corresponding to an object that need to be highlighted and the highlight color to be used.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
In your vertex shader, your inform the picking module what object we are currently rendering by supplying a picking color, perhaps from an attribute.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
in vec3 aPickingColor;
|
|
15
|
+
main() {
|
|
16
|
+
picking_setColor(aPickingColor);
|
|
17
|
+
...
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
In your fragment shader, you simply apply (call) the `picking_filterColor` filter function at the very end of the shader. This will return the normal color, or the highlight color, or the picking color, as appropriate.
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
main() {
|
|
25
|
+
fragColor = ...
|
|
26
|
+
fragColor = picking_filterColor(color);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
If highlighting is not needed, you simply apply (call) the `picking_filterPickingColor` filter function at the very end of the shader. This will return the normal color or the picking color, as appropriate.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
main() {
|
|
34
|
+
fragColor = ...
|
|
35
|
+
fragColor = picking_filterPickingColor(fragColor);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
If additional filters need to be applied on the non-picking color (vertex or highlight color) you can use above functions in following order.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
main() {
|
|
43
|
+
fragColor = ...
|
|
44
|
+
fragColor = picking_filterHighlightColor(fragColor);
|
|
45
|
+
... apply any filters on fragColor ...
|
|
46
|
+
fragColor = picking_filterPickingColor(fragColor);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## JavaScript Functions
|
|
51
|
+
|
|
52
|
+
### getUniforms
|
|
53
|
+
|
|
54
|
+
`getUniforms` takes an object with key/value pairs, returns an object with key/value pairs representing the uniforms that the `picking` module shaders need.
|
|
55
|
+
|
|
56
|
+
`getUniforms(opts)`
|
|
57
|
+
opts can contain following keys:
|
|
58
|
+
|
|
59
|
+
- `pickingSelectedColorValid` (_boolean_) - When true current instance picking color is ignored, hence no instance is highlighted.
|
|
60
|
+
- `pickingSelectedColor` (_array_) - Picking color of the currently selected instance.
|
|
61
|
+
- `pickingHighlightColor` (_array_)- Color used to highlight the currently selected instance.
|
|
62
|
+
- `pickingActive`=`false` (_boolean_) - When true, renders the picking colors instead of the normal colors. Normally only used with an off-screen framebuffer during picking. Default value is `false`.
|
|
63
|
+
|
|
64
|
+
Note that the selected item will be rendered using `pickingHighlightColor`, if blending is enabled for the draw, alpha channel can be used to control the blending result.
|
|
65
|
+
|
|
66
|
+
## Vertex Shader Functions
|
|
67
|
+
|
|
68
|
+
### `void picking_setPickingColor(vec3)`
|
|
69
|
+
|
|
70
|
+
Sets the color that will be returned by the fragment shader if color based picking is enabled. Typically set from a `pickingColor` uniform or a `pickingColors` attribute (e.g. when using instanced rendering, to identify the actual instance that was picked).
|
|
71
|
+
|
|
72
|
+
## Fragment Shader Functions
|
|
73
|
+
|
|
74
|
+
### picking_filterPickingColor
|
|
75
|
+
|
|
76
|
+
If picking active, returns the current vertex's picking color set by `picking_setPickingColor`, otherwise returns its argument unmodified.
|
|
77
|
+
|
|
78
|
+
`vec4 picking_filterPickingColor(vec4 color)`
|
|
79
|
+
|
|
80
|
+
### picking_filterHighlightColor
|
|
81
|
+
|
|
82
|
+
Returns picking highlight color if the pixel belongs to currently selected model, otherwise returns its argument unmodified.
|
|
83
|
+
|
|
84
|
+
`vec4 picking_filterHighlightColor(vec4 color)`
|
|
85
|
+
|
|
86
|
+
## Remarks
|
|
87
|
+
|
|
88
|
+
- It is strongly recommended that `picking_filterPickingColor` is called last in a fragment shader, as the picking color (returned when picking is enabled) must not be modified in any way (and alpha must remain 1) or picking results will not be correct.
|
|
@@ -0,0 +1,190 @@
|
|
|
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 {PickingProps, PickingUniforms, PickingBindings} from './picking-uniforms';
|
|
8
|
+
import {pickingUniforms, GLSL_UNIFORMS, WGSL_UNIFORMS} from './picking-uniforms';
|
|
9
|
+
|
|
10
|
+
const source = /* wgsl */ `\
|
|
11
|
+
${WGSL_UNIFORMS}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const vs = /* glsl */ `\
|
|
15
|
+
${GLSL_UNIFORMS}
|
|
16
|
+
out vec4 picking_vRGBcolor_Avalid;
|
|
17
|
+
|
|
18
|
+
// Normalize unsigned byte color to 0-1 range
|
|
19
|
+
vec3 picking_normalizeColor(vec3 color) {
|
|
20
|
+
return picking.useFloatColors > 0.5 ? color : color / 255.0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Normalize unsigned byte color to 0-1 range
|
|
24
|
+
vec4 picking_normalizeColor(vec4 color) {
|
|
25
|
+
return picking.useFloatColors > 0.5 ? color : color / 255.0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
bool picking_isColorZero(vec3 color) {
|
|
29
|
+
return dot(color, vec3(1.0)) < 0.00001;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
bool picking_isColorValid(vec3 color) {
|
|
33
|
+
return dot(color, vec3(1.0)) > 0.00001;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check if this vertex is highlighted
|
|
37
|
+
bool isVertexHighlighted(vec3 vertexColor) {
|
|
38
|
+
vec3 highlightedObjectColor = picking_normalizeColor(picking.highlightedObjectColor);
|
|
39
|
+
return
|
|
40
|
+
bool(picking.isHighlightActive) && picking_isColorZero(abs(vertexColor - highlightedObjectColor));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Set the current picking color
|
|
44
|
+
void picking_setPickingColor(vec3 pickingColor) {
|
|
45
|
+
pickingColor = picking_normalizeColor(pickingColor);
|
|
46
|
+
|
|
47
|
+
if (bool(picking.isActive)) {
|
|
48
|
+
// Use alpha as the validity flag. If pickingColor is [0, 0, 0] fragment is non-pickable
|
|
49
|
+
picking_vRGBcolor_Avalid.a = float(picking_isColorValid(pickingColor));
|
|
50
|
+
|
|
51
|
+
if (!bool(picking.isAttribute)) {
|
|
52
|
+
// Stores the picking color so that the fragment shader can render it during picking
|
|
53
|
+
picking_vRGBcolor_Avalid.rgb = pickingColor;
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
// Do the comparison with selected item color in vertex shader as it should mean fewer compares
|
|
57
|
+
picking_vRGBcolor_Avalid.a = float(isVertexHighlighted(pickingColor));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
void picking_setObjectIndex(uint objectIndex) {
|
|
62
|
+
if (bool(picking.isActive)) {
|
|
63
|
+
uint index = objectIndex;
|
|
64
|
+
if (picking.indexMode == PICKING_INDEX_MODE_INSTANCE) {
|
|
65
|
+
index = uint(gl_InstanceID);
|
|
66
|
+
}
|
|
67
|
+
picking_vRGBcolor_Avalid.r = float(index % 255) / 255.0;
|
|
68
|
+
picking_vRGBcolor_Avalid.g = float((index / 255) % 255) / 255.0;
|
|
69
|
+
picking_vRGBcolor_Avalid.b = float((index / 255 / 255) %255) / 255.0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
void picking_setPickingAttribute(float value) {
|
|
74
|
+
if (bool(picking.isAttribute)) {
|
|
75
|
+
picking_vRGBcolor_Avalid.r = value;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
void picking_setPickingAttribute(vec2 value) {
|
|
80
|
+
if (bool(picking.isAttribute)) {
|
|
81
|
+
picking_vRGBcolor_Avalid.rg = value;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void picking_setPickingAttribute(vec3 value) {
|
|
86
|
+
if (bool(picking.isAttribute)) {
|
|
87
|
+
picking_vRGBcolor_Avalid.rgb = value;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
const fs = /* glsl */ `\
|
|
93
|
+
${GLSL_UNIFORMS}
|
|
94
|
+
|
|
95
|
+
in vec4 picking_vRGBcolor_Avalid;
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* Returns highlight color if this item is selected.
|
|
99
|
+
*/
|
|
100
|
+
vec4 picking_filterHighlightColor(vec4 color) {
|
|
101
|
+
// If we are still picking, we don't highlight
|
|
102
|
+
if (picking.isActive > 0.5) {
|
|
103
|
+
return color;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
bool selected = bool(picking_vRGBcolor_Avalid.a);
|
|
107
|
+
|
|
108
|
+
if (selected) {
|
|
109
|
+
// Blend in highlight color based on its alpha value
|
|
110
|
+
float highLightAlpha = picking.highlightColor.a;
|
|
111
|
+
float blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
|
|
112
|
+
float highLightRatio = highLightAlpha / blendedAlpha;
|
|
113
|
+
|
|
114
|
+
vec3 blendedRGB = mix(color.rgb, picking.highlightColor.rgb, highLightRatio);
|
|
115
|
+
return vec4(blendedRGB, blendedAlpha);
|
|
116
|
+
} else {
|
|
117
|
+
return color;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/*
|
|
122
|
+
* Returns picking color if picking enabled else unmodified argument.
|
|
123
|
+
*/
|
|
124
|
+
vec4 picking_filterPickingColor(vec4 color) {
|
|
125
|
+
if (bool(picking.isActive)) {
|
|
126
|
+
if (picking_vRGBcolor_Avalid.a == 0.0) {
|
|
127
|
+
discard;
|
|
128
|
+
}
|
|
129
|
+
return picking_vRGBcolor_Avalid;
|
|
130
|
+
}
|
|
131
|
+
return color;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* Returns picking color if picking is enabled if not
|
|
136
|
+
* highlight color if this item is selected, otherwise unmodified argument.
|
|
137
|
+
*/
|
|
138
|
+
vec4 picking_filterColor(vec4 color) {
|
|
139
|
+
vec4 highlightColor = picking_filterHighlightColor(color);
|
|
140
|
+
return picking_filterPickingColor(highlightColor);
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Provides support for color-coding-based picking and highlighting.
|
|
146
|
+
* In particular, supports picking a specific instance in an instanced
|
|
147
|
+
* draw call and highlighting an instance based on its picking color,
|
|
148
|
+
* and correspondingly, supports picking and highlighting groups of
|
|
149
|
+
* primitives with the same picking color in non-instanced draw-calls
|
|
150
|
+
*/
|
|
151
|
+
export const picking = {
|
|
152
|
+
...pickingUniforms,
|
|
153
|
+
name: 'picking',
|
|
154
|
+
source,
|
|
155
|
+
vs,
|
|
156
|
+
fs
|
|
157
|
+
} as const satisfies ShaderModule<PickingProps, PickingUniforms, PickingBindings>;
|
|
158
|
+
|
|
159
|
+
// function getUniforms(opts: PickingProps = {}, prevUniforms?: PickingUniforms): PickingUniforms {
|
|
160
|
+
// const uniforms = {} as PickingUniforms;
|
|
161
|
+
|
|
162
|
+
// if (opts.highlightedObjectColor === undefined) {
|
|
163
|
+
// // Unless highlightedObjectColor explicitly null or set, do not update state
|
|
164
|
+
// } else if (opts.highlightedObjectColor === null) {
|
|
165
|
+
// uniforms.isHighlightActive = false;
|
|
166
|
+
// } else {
|
|
167
|
+
// uniforms.isHighlightActive = true;
|
|
168
|
+
// const highlightedObjectColor = opts.highlightedObjectColor.slice(0, 3);
|
|
169
|
+
// uniforms.highlightedObjectColor = highlightedObjectColor;
|
|
170
|
+
// }
|
|
171
|
+
|
|
172
|
+
// if (opts.highlightColor) {
|
|
173
|
+
// const color = Array.from(opts.highlightColor, x => x / 255);
|
|
174
|
+
// if (!Number.isFinite(color[3])) {
|
|
175
|
+
// color[3] = 1;
|
|
176
|
+
// }
|
|
177
|
+
// uniforms.highlightColor = color;
|
|
178
|
+
// }
|
|
179
|
+
|
|
180
|
+
// if (opts.isActive !== undefined) {
|
|
181
|
+
// uniforms.isActive = Boolean(opts.isActive);
|
|
182
|
+
// uniforms.isAttribute = Boolean(opts.isAttribute);
|
|
183
|
+
// }
|
|
184
|
+
|
|
185
|
+
// if (opts.useFloatColors !== undefined) {
|
|
186
|
+
// uniforms.useFloatColors = Boolean(opts.useFloatColors);
|
|
187
|
+
// }
|
|
188
|
+
|
|
189
|
+
// return uniforms;
|
|
190
|
+
// }
|
|
@@ -0,0 +1,156 @@
|
|
|
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, GLSL_UNIFORMS, WGSL_UNIFORMS, INVALID_INDEX} from './picking-uniforms';
|
|
9
|
+
|
|
10
|
+
// SHADERS
|
|
11
|
+
|
|
12
|
+
const source = /* wgsl */ `\
|
|
13
|
+
${WGSL_UNIFORMS}
|
|
14
|
+
|
|
15
|
+
const INDEX_PICKING_MODE_INSTANCE = 0;
|
|
16
|
+
const INDEX_PICKING_MODE_CUSTOM = 1;
|
|
17
|
+
const INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
|
|
18
|
+
|
|
19
|
+
struct indexPickingFragmentInputs = {
|
|
20
|
+
objectIndex: int32;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let indexPickingFragmentInputs: indexPickingFragmentInputs;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Vertex shaders should call this function to set the object index.
|
|
27
|
+
* If using instance or vertex mode, argument will be ignored, 0 can be supplied.
|
|
28
|
+
*/
|
|
29
|
+
fn picking_setObjectIndex(objectIndex: int32) {
|
|
30
|
+
switch (picking.indexMode) {
|
|
31
|
+
case INDEX_PICKING_MODE_INSTANCE, default: {
|
|
32
|
+
picking_objectIndex = instance_index;
|
|
33
|
+
};
|
|
34
|
+
case INDEX_PICKING_MODE_CUSTOM: {
|
|
35
|
+
picking_objectIndex = objectIndex;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
const vs = /* glsl */ `\
|
|
43
|
+
${GLSL_UNIFORMS}
|
|
44
|
+
|
|
45
|
+
const int INDEX_PICKING_MODE_INSTANCE = 0;
|
|
46
|
+
const int INDEX_PICKING_MODE_CUSTOM = 1;
|
|
47
|
+
|
|
48
|
+
const int INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
|
|
49
|
+
|
|
50
|
+
flat out int picking_objectIndex;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Vertex shaders should call this function to set the object index.
|
|
54
|
+
* If using instance or vertex mode, argument will be ignored, 0 can be supplied.
|
|
55
|
+
*/
|
|
56
|
+
void picking_setObjectIndex(int objectIndex) {
|
|
57
|
+
switch (picking.indexMode) {
|
|
58
|
+
case INDEX_PICKING_MODE_INSTANCE:
|
|
59
|
+
picking_objectIndex = gl_InstanceID;
|
|
60
|
+
break;
|
|
61
|
+
case INDEX_PICKING_MODE_CUSTOM:
|
|
62
|
+
picking_objectIndex = objectIndex;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
const fs = /* glsl */ `\
|
|
69
|
+
${GLSL_UNIFORMS}
|
|
70
|
+
|
|
71
|
+
const int INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
|
|
72
|
+
|
|
73
|
+
flat in int picking_objectIndex;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if this vertex is highlighted (part of the selected batch and object)
|
|
77
|
+
*/
|
|
78
|
+
bool picking_isFragmentHighlighted() {
|
|
79
|
+
return
|
|
80
|
+
bool(picking.isHighlightActive) &&
|
|
81
|
+
picking.highlightedBatchIndex == picking.batchIndex &&
|
|
82
|
+
picking.highlightedObjectIndex == picking_objectIndex
|
|
83
|
+
;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Returns highlight color if this item is selected.
|
|
88
|
+
*/
|
|
89
|
+
vec4 picking_filterHighlightColor(vec4 color) {
|
|
90
|
+
// If we are still picking, we don't highlight
|
|
91
|
+
if (bool(picking.isActive)) {
|
|
92
|
+
return color;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// If we are not highlighted, return color as is
|
|
96
|
+
if (!picking_isFragmentHighlighted()) {
|
|
97
|
+
return color;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Blend in highlight color based on its alpha value
|
|
101
|
+
float highLightAlpha = picking.highlightColor.a;
|
|
102
|
+
float blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha);
|
|
103
|
+
float highLightRatio = highLightAlpha / blendedAlpha;
|
|
104
|
+
|
|
105
|
+
vec3 blendedRGB = mix(color.rgb, picking.highlightColor.rgb, highLightRatio);
|
|
106
|
+
return vec4(blendedRGB, blendedAlpha);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* Returns picking color if picking enabled else unmodified argument.
|
|
111
|
+
*/
|
|
112
|
+
ivec4 picking_getPickingColor() {
|
|
113
|
+
// Assumes that colorAttachment0 is rg32int
|
|
114
|
+
// TODO? - we could render indices into a second color attachment and not mess with fragColor
|
|
115
|
+
return ivec4(picking_objectIndex, picking.batchIndex, 0u, 0u);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
vec4 picking_filterPickingColor(vec4 color) {
|
|
119
|
+
if (bool(picking.isActive)) {
|
|
120
|
+
if (picking_objectIndex == INDEX_PICKING_INVALID_INDEX) {
|
|
121
|
+
discard;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return color;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
* Returns picking color if picking is enabled if not
|
|
129
|
+
* highlight color if this item is selected, otherwise unmodified argument.
|
|
130
|
+
*/
|
|
131
|
+
vec4 picking_filterColor(vec4 color) {
|
|
132
|
+
vec4 outColor = color;
|
|
133
|
+
outColor = picking_filterHighlightColor(outColor);
|
|
134
|
+
outColor = picking_filterPickingColor(outColor);
|
|
135
|
+
return outColor;
|
|
136
|
+
}
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Provides support for color-based picking and highlighting.
|
|
141
|
+
*
|
|
142
|
+
* In particular, supports picking a specific instance in an instanced
|
|
143
|
+
* draw call and highlighting an instance based on its picking color,
|
|
144
|
+
* and correspondingly, supports picking and highlighting groups of
|
|
145
|
+
* primitives with the same picking color in non-instanced draw-calls
|
|
146
|
+
*
|
|
147
|
+
* @note Color based picking has the significant advantage in that it can be added to any
|
|
148
|
+
* existing shader without requiring any additional picking logic.
|
|
149
|
+
*/
|
|
150
|
+
export const picking = {
|
|
151
|
+
...pickingUniforms,
|
|
152
|
+
name: 'picking',
|
|
153
|
+
source,
|
|
154
|
+
vs,
|
|
155
|
+
fs
|
|
156
|
+
} as const satisfies ShaderModule<PickingProps, PickingUniforms, PickingBindings>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// luma.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Device, Framebuffer} from '@luma.gl/core';
|
|
6
|
+
import {picking} from '@luma.gl/shadertools';
|
|
7
|
+
import {ShaderInputs} from '../../shader-inputs';
|
|
8
|
+
import {NumberArray3} from '@math.gl/types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Helper class for using the legacy picking module
|
|
12
|
+
*/
|
|
13
|
+
export class LegacyPickingManager {
|
|
14
|
+
device: Device;
|
|
15
|
+
framebuffer: Framebuffer | null = null;
|
|
16
|
+
shaderInputs: ShaderInputs<{picking: typeof picking.props}>;
|
|
17
|
+
|
|
18
|
+
constructor(device: Device, shaderInputs: ShaderInputs) {
|
|
19
|
+
this.device = device;
|
|
20
|
+
this.shaderInputs = shaderInputs as ShaderInputs<{picking: typeof picking.props}>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
destroy() {
|
|
24
|
+
this.framebuffer?.destroy();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getFramebuffer() {
|
|
28
|
+
if (!this.framebuffer) {
|
|
29
|
+
this.framebuffer = this.device.createFramebuffer({
|
|
30
|
+
colorAttachments: ['rgba8unorm'],
|
|
31
|
+
depthStencilAttachment: 'depth24plus'
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return this.framebuffer;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Clear highlighted / picked object */
|
|
38
|
+
clearPickState() {
|
|
39
|
+
this.shaderInputs.setProps({picking: {highlightedObjectColor: null}});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Prepare for rendering picking colors */
|
|
43
|
+
beginRenderPass() {
|
|
44
|
+
const framebuffer = this.getFramebuffer();
|
|
45
|
+
framebuffer.resize(this.device.getCanvasContext().getPixelSize());
|
|
46
|
+
|
|
47
|
+
this.shaderInputs.setProps({picking: {isActive: true}});
|
|
48
|
+
|
|
49
|
+
const pickingPass = this.device.beginRenderPass({
|
|
50
|
+
framebuffer,
|
|
51
|
+
clearColor: [0, 0, 0, 0],
|
|
52
|
+
clearDepth: 1
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return pickingPass;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
updatePickState(mousePosition: [number, number]) {
|
|
59
|
+
const framebuffer = this.getFramebuffer();
|
|
60
|
+
|
|
61
|
+
// use the center pixel location in device pixel range
|
|
62
|
+
const [pickX, pickY] = this.getPickPosition(mousePosition);
|
|
63
|
+
|
|
64
|
+
// Read back
|
|
65
|
+
const color255 = this.device.readPixelsToArrayWebGL(framebuffer, {
|
|
66
|
+
sourceX: pickX,
|
|
67
|
+
sourceY: pickY,
|
|
68
|
+
sourceWidth: 1,
|
|
69
|
+
sourceHeight: 1
|
|
70
|
+
});
|
|
71
|
+
// console.log(color255);
|
|
72
|
+
|
|
73
|
+
// Check if we have
|
|
74
|
+
let highlightedObjectColor: NumberArray3 | null = [...color255].map(
|
|
75
|
+
x => x / 255
|
|
76
|
+
) as NumberArray3;
|
|
77
|
+
const isHighlightActive =
|
|
78
|
+
highlightedObjectColor[0] + highlightedObjectColor[1] + highlightedObjectColor[2] > 0;
|
|
79
|
+
|
|
80
|
+
if (!isHighlightActive) {
|
|
81
|
+
highlightedObjectColor = null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.shaderInputs.setProps({
|
|
85
|
+
picking: {isActive: false, highlightedObjectColor}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get pick position in device pixel range
|
|
91
|
+
* use the center pixel location in device pixel range
|
|
92
|
+
*/
|
|
93
|
+
getPickPosition(mousePosition: number[]): [number, number] {
|
|
94
|
+
const devicePixels = this.device.getCanvasContext().cssToDevicePixels(mousePosition);
|
|
95
|
+
const pickX = devicePixels.x + Math.floor(devicePixels.width / 2);
|
|
96
|
+
const pickY = devicePixels.y + Math.floor(devicePixels.height / 2);
|
|
97
|
+
return [pickX, pickY];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// luma.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Device, Framebuffer} from '@luma.gl/core';
|
|
6
|
+
import {ShaderInputs} from '../../shader-inputs';
|
|
7
|
+
import {pickingUniforms, INVALID_INDEX} from './picking-uniforms';
|
|
8
|
+
// import {picking} from './color-picking';
|
|
9
|
+
|
|
10
|
+
/** Information about picked object */
|
|
11
|
+
export type PickInfo = {
|
|
12
|
+
batchIndex: number | null;
|
|
13
|
+
objectIndex: number | null;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type PickingManagerProps = {
|
|
17
|
+
/** Shader Inputs from models to pick */
|
|
18
|
+
shaderInputs?: ShaderInputs<{picking: typeof pickingUniforms.props}>;
|
|
19
|
+
/** Callback */
|
|
20
|
+
onObjectPicked?: (info: PickInfo) => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Helper class for using the new picking module
|
|
25
|
+
* @todo Port to WebGPU
|
|
26
|
+
* @todo Support multiple models
|
|
27
|
+
* @todo Switching picking module
|
|
28
|
+
*/
|
|
29
|
+
export class PickingManager {
|
|
30
|
+
device: Device;
|
|
31
|
+
props: Required<PickingManagerProps>;
|
|
32
|
+
/** Info from latest pick operation */
|
|
33
|
+
pickInfo: PickInfo = {batchIndex: null, objectIndex: null};
|
|
34
|
+
/** Framebuffer used for picking */
|
|
35
|
+
framebuffer: Framebuffer | null = null;
|
|
36
|
+
|
|
37
|
+
static defaultProps: Required<PickingManagerProps> = {
|
|
38
|
+
shaderInputs: undefined!,
|
|
39
|
+
onObjectPicked: () => {}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
constructor(device: Device, props: PickingManagerProps) {
|
|
43
|
+
this.device = device;
|
|
44
|
+
this.props = {...PickingManager.defaultProps, ...props};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
destroy() {
|
|
48
|
+
this.framebuffer?.destroy();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// TODO - Ask for a cached framebuffer? a Framebuffer factory?
|
|
52
|
+
getFramebuffer() {
|
|
53
|
+
if (!this.framebuffer) {
|
|
54
|
+
this.framebuffer = this.device.createFramebuffer({
|
|
55
|
+
colorAttachments: ['rgba8unorm', 'rg32sint'],
|
|
56
|
+
depthStencilAttachment: 'depth24plus'
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return this.framebuffer;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Clear highlighted / picked object */
|
|
63
|
+
clearPickState() {
|
|
64
|
+
this.props.shaderInputs.setProps({picking: {highlightedObjectIndex: null}});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Prepare for rendering picking colors */
|
|
68
|
+
beginRenderPass() {
|
|
69
|
+
const framebuffer = this.getFramebuffer();
|
|
70
|
+
framebuffer.resize(this.device.getDefaultCanvasContext().getPixelSize());
|
|
71
|
+
|
|
72
|
+
this.props.shaderInputs?.setProps({picking: {isActive: true}});
|
|
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
|
+
});
|
|
79
|
+
|
|
80
|
+
return pickingPass;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
getPickInfo(mousePosition: [number, number]): PickInfo | null {
|
|
84
|
+
const framebuffer = this.getFramebuffer();
|
|
85
|
+
|
|
86
|
+
// use the center pixel location in device pixel range
|
|
87
|
+
const [pickX, pickY] = this.getPickPosition(mousePosition);
|
|
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) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const pickInfo: PickInfo = {
|
|
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
|
+
) {
|
|
111
|
+
this.pickInfo = pickInfo;
|
|
112
|
+
this.props.onObjectPicked(pickInfo);
|
|
113
|
+
// console.log(`Object ${pickInfo.objectIndex} in batch ${pickInfo.batchIndex} was picked`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.props.shaderInputs?.setProps({
|
|
117
|
+
picking: {
|
|
118
|
+
isActive: false,
|
|
119
|
+
highlightedBatchIndex: pickInfo.batchIndex,
|
|
120
|
+
highlightedObjectIndex: pickInfo.objectIndex
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return this.pickInfo;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get pick position in device pixel range
|
|
129
|
+
* use the center pixel location in device pixel range
|
|
130
|
+
*/
|
|
131
|
+
getPickPosition(mousePosition: number[]): [number, number] {
|
|
132
|
+
const devicePixels = this.device.getDefaultCanvasContext().cssToDevicePixels(mousePosition);
|
|
133
|
+
const pickX = devicePixels.x + Math.floor(devicePixels.width / 2);
|
|
134
|
+
const pickY = devicePixels.y + Math.floor(devicePixels.height / 2);
|
|
135
|
+
return [pickX, pickY];
|
|
136
|
+
}
|
|
137
|
+
}
|