@deck.gl/core 9.3.0-alpha.3 → 9.3.0-alpha.6

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.
Files changed (88) hide show
  1. package/dist/controllers/terrain-controller.d.ts +7 -6
  2. package/dist/controllers/terrain-controller.d.ts.map +1 -1
  3. package/dist/controllers/terrain-controller.js +58 -39
  4. package/dist/controllers/terrain-controller.js.map +1 -1
  5. package/dist/dist.dev.js +3748 -1377
  6. package/dist/index.cjs +545 -219
  7. package/dist/index.cjs.map +4 -4
  8. package/dist/lib/attribute/gl-utils.d.ts +1 -2
  9. package/dist/lib/attribute/gl-utils.d.ts.map +1 -1
  10. package/dist/lib/attribute/gl-utils.js +2 -2
  11. package/dist/lib/attribute/gl-utils.js.map +1 -1
  12. package/dist/lib/deck-picker.d.ts +3 -2
  13. package/dist/lib/deck-picker.d.ts.map +1 -1
  14. package/dist/lib/deck-picker.js +74 -17
  15. package/dist/lib/deck-picker.js.map +1 -1
  16. package/dist/lib/deck.d.ts +62 -0
  17. package/dist/lib/deck.d.ts.map +1 -1
  18. package/dist/lib/deck.js +219 -77
  19. package/dist/lib/deck.js.map +1 -1
  20. package/dist/lib/init.js +2 -2
  21. package/dist/lib/layer.d.ts.map +1 -1
  22. package/dist/lib/layer.js +60 -9
  23. package/dist/lib/layer.js.map +1 -1
  24. package/dist/lib/view-manager.js +1 -1
  25. package/dist/lib/view-manager.js.map +1 -1
  26. package/dist/passes/layers-pass.d.ts.map +1 -1
  27. package/dist/passes/layers-pass.js +13 -0
  28. package/dist/passes/layers-pass.js.map +1 -1
  29. package/dist/passes/pick-layers-pass.d.ts.map +1 -1
  30. package/dist/passes/pick-layers-pass.js +7 -2
  31. package/dist/passes/pick-layers-pass.js.map +1 -1
  32. package/dist/passes/screen-pass-uniforms.d.ts +1 -1
  33. package/dist/passes/screen-pass-uniforms.js +1 -1
  34. package/dist/shaderlib/color/color.d.ts +1 -4
  35. package/dist/shaderlib/color/color.d.ts.map +1 -1
  36. package/dist/shaderlib/color/color.js +0 -12
  37. package/dist/shaderlib/color/color.js.map +1 -1
  38. package/dist/shaderlib/index.d.ts +1 -2
  39. package/dist/shaderlib/index.d.ts.map +1 -1
  40. package/dist/shaderlib/index.js +1 -2
  41. package/dist/shaderlib/index.js.map +1 -1
  42. package/dist/shaderlib/misc/layer-uniforms.d.ts +3 -2
  43. package/dist/shaderlib/misc/layer-uniforms.d.ts.map +1 -1
  44. package/dist/shaderlib/misc/layer-uniforms.js +10 -1
  45. package/dist/shaderlib/misc/layer-uniforms.js.map +1 -1
  46. package/dist/shaderlib/picking/picking.d.ts +5 -3
  47. package/dist/shaderlib/picking/picking.d.ts.map +1 -1
  48. package/dist/shaderlib/picking/picking.js +29 -0
  49. package/dist/shaderlib/picking/picking.js.map +1 -1
  50. package/dist/shaderlib/project/project.glsl.js +1 -1
  51. package/dist/shaderlib/project/project.wgsl.d.ts.map +1 -1
  52. package/dist/shaderlib/project/project.wgsl.js +4 -6
  53. package/dist/shaderlib/project/project.wgsl.js.map +1 -1
  54. package/dist/shaderlib/shadow/shadow.d.ts +2 -2
  55. package/dist/shaderlib/shadow/shadow.js +1 -1
  56. package/dist/transitions/gpu-interpolation-transition.js +2 -2
  57. package/dist/transitions/gpu-interpolation-transition.js.map +1 -1
  58. package/dist/transitions/gpu-spring-transition.js +1 -1
  59. package/dist/transitions/gpu-transition-utils.d.ts.map +1 -1
  60. package/dist/transitions/gpu-transition-utils.js +3 -4
  61. package/dist/transitions/gpu-transition-utils.js.map +1 -1
  62. package/dist/utils/texture.d.ts.map +1 -1
  63. package/dist/utils/texture.js +3 -0
  64. package/dist/utils/texture.js.map +1 -1
  65. package/dist/utils/typed-array-manager.js.map +1 -1
  66. package/dist.min.js +582 -247
  67. package/package.json +8 -9
  68. package/src/controllers/terrain-controller.ts +60 -51
  69. package/src/lib/attribute/gl-utils.ts +2 -2
  70. package/src/lib/deck-picker.ts +98 -17
  71. package/src/lib/deck.ts +334 -86
  72. package/src/lib/layer.ts +98 -8
  73. package/src/lib/view-manager.ts +1 -1
  74. package/src/passes/layers-pass.ts +21 -1
  75. package/src/passes/pick-layers-pass.ts +6 -2
  76. package/src/passes/screen-pass-uniforms.ts +1 -1
  77. package/src/shaderlib/color/color.ts +0 -12
  78. package/src/shaderlib/index.ts +1 -3
  79. package/src/shaderlib/misc/layer-uniforms.ts +11 -1
  80. package/src/shaderlib/picking/picking.ts +30 -0
  81. package/src/shaderlib/project/project.glsl.ts +1 -1
  82. package/src/shaderlib/project/project.wgsl.ts +4 -6
  83. package/src/shaderlib/shadow/shadow.ts +1 -1
  84. package/src/transitions/gpu-interpolation-transition.ts +2 -2
  85. package/src/transitions/gpu-spring-transition.ts +1 -1
  86. package/src/transitions/gpu-transition-utils.ts +4 -5
  87. package/src/utils/texture.ts +2 -0
  88. package/src/utils/typed-array-manager.ts +3 -3
package/src/lib/deck.ts CHANGED
@@ -18,9 +18,9 @@ import {VERSION} from './init';
18
18
 
19
19
  import {luma} from '@luma.gl/core';
20
20
  import {webgl2Adapter} from '@luma.gl/webgl';
21
+ import {GL} from '@luma.gl/webgl/constants';
21
22
  import {Timeline} from '@luma.gl/engine';
22
23
  import {AnimationLoop} from '@luma.gl/engine';
23
- import {GL} from '@luma.gl/constants';
24
24
  import type {CanvasContextProps, Device, DeviceProps, Framebuffer, Parameters} from '@luma.gl/core';
25
25
  import type {ShaderModule} from '@luma.gl/shadertools';
26
26
 
@@ -81,6 +81,12 @@ type CursorState = {
81
81
  isDragging: boolean;
82
82
  };
83
83
 
84
+ type InternalPickingMode = 'sync' | 'async';
85
+ type PointPickResult = {
86
+ result: PickingInfo[];
87
+ emptyInfo: PickingInfo;
88
+ };
89
+
84
90
  export type DeckProps<ViewsT extends ViewOrViews = null> = {
85
91
  /** Id of this Deck instance */
86
92
  id?: string;
@@ -103,6 +109,10 @@ export type DeckProps<ViewsT extends ViewOrViews = null> = {
103
109
  * @default `0`
104
110
  */
105
111
  pickingRadius?: number;
112
+ /** Selects the internal picking policy used by deck-managed events and controllers.
113
+ * @default `'auto'`
114
+ */
115
+ pickAsync?: InternalPickingMode | 'auto';
106
116
 
107
117
  /** WebGL parameters to be set before each frame is rendered. */
108
118
  parameters?: Parameters;
@@ -233,6 +243,7 @@ const defaultProps: DeckProps = {
233
243
  viewState: null,
234
244
  initialViewState: null,
235
245
  pickingRadius: 0,
246
+ pickAsync: 'auto',
236
247
  layerFilter: null,
237
248
  parameters: {},
238
249
  parent: null,
@@ -333,6 +344,8 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
333
344
  gpuMemory: 0
334
345
  };
335
346
  private _metricsCounter: number = 0;
347
+ private _hoverPickSequence: number = 0;
348
+ private _pointerDownPickSequence: number = 0;
336
349
 
337
350
  private _needsRedraw: false | string = 'Initial render';
338
351
  private _pickRequest: {
@@ -356,6 +369,7 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
356
369
  * This object is reused for subsequent `onClick` and `onDrag*` callbacks.
357
370
  */
358
371
  private _lastPointerDownInfo: PickingInfo | null = null;
372
+ private _lastPointerDownInfoPromise: Promise<PickingInfo> | null = null;
359
373
 
360
374
  constructor(props: DeckProps<ViewsT>) {
361
375
  // @ts-ignore views
@@ -423,7 +437,10 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
423
437
  this.animationLoop?.stop();
424
438
  this.animationLoop?.destroy();
425
439
  this.animationLoop = null;
440
+ this._hoverPickSequence++;
441
+ this._pointerDownPickSequence++;
426
442
  this._lastPointerDownInfo = null;
443
+ this._lastPointerDownInfoPromise = null;
427
444
 
428
445
  this.layerManager?.finalize();
429
446
  this.layerManager = null;
@@ -474,6 +491,7 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
474
491
 
475
492
  // Merge with existing props
476
493
  Object.assign(this.props, props);
494
+ this._validateInternalPickingMode();
477
495
 
478
496
  // Update CSS size of canvas
479
497
  this._setCanvasSize(this.props);
@@ -631,6 +649,47 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
631
649
  }
632
650
 
633
651
  /** Query the object rendered on top at a given point */
652
+ async pickObjectAsync(opts: {
653
+ /** x position in pixels */
654
+ x: number;
655
+ /** y position in pixels */
656
+ y: number;
657
+ /** Radius of tolerance in pixels. Default `0`. */
658
+ radius?: number;
659
+ /** A list of layer ids to query from. If not specified, then all pickable and visible layers are queried. */
660
+ layerIds?: string[];
661
+ /** If `true`, `info.coordinate` will be a 3D point by unprojecting the `x, y` screen coordinates onto the picked geometry. Default `false`. */
662
+ unproject3D?: boolean;
663
+ }): Promise<PickingInfo | null> {
664
+ const infos = (await this._pickAsync('pickObjectAsync', 'pickObject Time', opts)).result;
665
+ return infos.length ? infos[0] : null;
666
+ }
667
+
668
+ /**
669
+ * Query all objects rendered on top within a bounding box
670
+ * @note Caveat: this method performs multiple async GPU queries, so state could potentially change between calls.
671
+ */
672
+ async pickObjectsAsync(opts: {
673
+ /** Left of the bounding box in pixels */
674
+ x: number;
675
+ /** Top of the bounding box in pixels */
676
+ y: number;
677
+ /** Width of the bounding box in pixels. Default `1` */
678
+ width?: number;
679
+ /** Height of the bounding box in pixels. Default `1` */
680
+ height?: number;
681
+ /** A list of layer ids to query from. If not specified, then all pickable and visible layers are queried. */
682
+ layerIds?: string[];
683
+ /** If specified, limits the number of objects that can be returned. */
684
+ maxObjects?: number | null;
685
+ }): Promise<PickingInfo[]> {
686
+ return await this._pickAsync('pickObjectsAsync', 'pickObjects Time', opts);
687
+ }
688
+
689
+ /**
690
+ * Query the object rendered on top at a given point
691
+ * @deprecated WebGL only. Use `pickObjectsAsync` instead
692
+ */
634
693
  pickObject(opts: {
635
694
  /** x position in pixels */
636
695
  x: number;
@@ -647,7 +706,10 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
647
706
  return infos.length ? infos[0] : null;
648
707
  }
649
708
 
650
- /* Query all rendered objects at a given point */
709
+ /**
710
+ * Query all rendered objects at a given point
711
+ * @deprecated WebGL only. Use `pickObjectsAsync` instead
712
+ */
651
713
  pickMultipleObjects(opts: {
652
714
  /** x position in pixels */
653
715
  x: number;
@@ -666,7 +728,10 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
666
728
  return this._pick('pickObject', 'pickMultipleObjects Time', opts).result;
667
729
  }
668
730
 
669
- /* Query all objects rendered on top within a bounding box */
731
+ /**
732
+ * Query all objects rendered on top within a bounding box
733
+ * @deprecated WebGL only. Use `pickObjectsAsync` instead
734
+ */
670
735
  pickObjects(opts: {
671
736
  /** Left of the bounding box in pixels */
672
737
  x: number;
@@ -689,8 +754,12 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
689
754
  * @private
690
755
  */
691
756
  private _pickPositionForController(x: number, y: number): {coordinate?: number[]} | null {
692
- const pickResult = this.pickObject({x, y, radius: 0, unproject3D: true});
693
- return pickResult;
757
+ const internalPickingMode = this._getInternalPickingMode();
758
+ if (internalPickingMode !== 'sync') {
759
+ return null;
760
+ }
761
+
762
+ return this.pickObject({x, y, radius: 0, unproject3D: true});
694
763
  }
695
764
 
696
765
  /** Experimental
@@ -733,6 +802,166 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
733
802
 
734
803
  // Private Methods
735
804
 
805
+ private _resolveInternalPickingMode(): InternalPickingMode {
806
+ const {pickAsync} = this.props;
807
+ const deviceType = this.device?.type || this.props.deviceProps?.type;
808
+
809
+ if (pickAsync === 'auto') {
810
+ return deviceType === 'webgpu' ? 'async' : 'sync';
811
+ }
812
+ if (pickAsync === 'sync' && deviceType === 'webgpu') {
813
+ throw new Error('`pickAsync: "sync"` is not supported when Deck is using a WebGPU device.');
814
+ }
815
+ return pickAsync;
816
+ }
817
+
818
+ private _getInternalPickingMode(): InternalPickingMode | null {
819
+ try {
820
+ return this._resolveInternalPickingMode();
821
+ } catch (error) {
822
+ this.props.onError?.(error as Error);
823
+ return null;
824
+ }
825
+ }
826
+
827
+ private _validateInternalPickingMode(): void {
828
+ this._getInternalPickingMode();
829
+ }
830
+
831
+ private _getFirstPickedInfo({result, emptyInfo}: PointPickResult): PickingInfo {
832
+ return result[0] || emptyInfo;
833
+ }
834
+
835
+ private _shouldUnproject3D(layers = this.layerManager?.getLayers() || []): boolean {
836
+ return layers.some(layer => layer.props.pickable === '3d');
837
+ }
838
+
839
+ private _getPointPickOptions(
840
+ x: number,
841
+ y: number,
842
+ opts: Partial<PickByPointOptions> = {},
843
+ layers = this.layerManager?.getLayers() || []
844
+ ): PickByPointOptions {
845
+ return {
846
+ x,
847
+ y,
848
+ radius: this.props.pickingRadius,
849
+ unproject3D: this._shouldUnproject3D(layers),
850
+ ...opts
851
+ };
852
+ }
853
+
854
+ private _pickPointSync(opts: PickByPointOptions): PointPickResult {
855
+ return this._pick('pickObject', 'pickObject Time', opts);
856
+ }
857
+
858
+ private _pickPointAsync(opts: PickByPointOptions): Promise<PointPickResult> {
859
+ return this._pickAsync('pickObjectAsync', 'pickObject Time', opts);
860
+ }
861
+
862
+ private _getLastPointerDownPickingInfo(
863
+ x: number,
864
+ y: number,
865
+ layers = this.layerManager?.getLayers() || []
866
+ ): PickingInfo {
867
+ return this.deckPicker!.getLastPickedObject(
868
+ {
869
+ x,
870
+ y,
871
+ layers,
872
+ viewports: this.getViewports({x, y})
873
+ },
874
+ this._lastPointerDownInfo
875
+ ) as PickingInfo;
876
+ }
877
+
878
+ private _applyHoverCallbacks(
879
+ {result, emptyInfo}: PointPickResult,
880
+ event: MjolnirPointerEvent
881
+ ): void {
882
+ if (!this.widgetManager) {
883
+ return;
884
+ }
885
+
886
+ this.cursorState.isHovering = result.length > 0;
887
+
888
+ let pickedInfo = emptyInfo;
889
+ let handled = false;
890
+ for (const info of result) {
891
+ pickedInfo = info;
892
+ handled = info.layer?.onHover(info, event) || handled;
893
+ }
894
+ if (!handled) {
895
+ this.props.onHover?.(pickedInfo, event);
896
+ this.widgetManager.onHover(pickedInfo, event);
897
+ }
898
+ }
899
+
900
+ private _dispatchPickingEvent(info: PickingInfo, event: MjolnirGestureEvent): void {
901
+ if (!this.layerManager || !this.widgetManager) {
902
+ return;
903
+ }
904
+
905
+ const eventHandlerProp = EVENT_HANDLERS[event.type];
906
+ if (!eventHandlerProp) {
907
+ return;
908
+ }
909
+
910
+ const {layer} = info;
911
+ const layerHandler = layer && (layer[eventHandlerProp] || layer.props[eventHandlerProp]);
912
+ const rootHandler = this.props[eventHandlerProp];
913
+ let handled = false;
914
+
915
+ if (layerHandler) {
916
+ handled = layerHandler.call(layer, info, event);
917
+ }
918
+ if (!handled) {
919
+ rootHandler?.(info, event);
920
+ this.widgetManager.onEvent(info, event);
921
+ }
922
+ }
923
+
924
+ private _pickAsync(
925
+ method: 'pickObjectAsync',
926
+ statKey: string,
927
+ opts: PickByPointOptions & {layerIds?: string[]}
928
+ ): Promise<{
929
+ result: PickingInfo[];
930
+ emptyInfo: PickingInfo;
931
+ }>;
932
+ private _pickAsync(
933
+ method: 'pickObjectsAsync',
934
+ statKey: string,
935
+ opts: PickByRectOptions & {layerIds?: string[]}
936
+ ): Promise<PickingInfo[]>;
937
+
938
+ private _pickAsync(
939
+ method: 'pickObjectAsync' | 'pickObjectsAsync',
940
+ statKey: string,
941
+ opts: (PickByPointOptions | PickByRectOptions) & {layerIds?: string[]}
942
+ ) {
943
+ assert(this.deckPicker);
944
+
945
+ const {stats} = this;
946
+
947
+ stats.get('Pick Count').incrementCount();
948
+ stats.get(statKey).timeStart();
949
+
950
+ const infos = this.deckPicker[method]({
951
+ // layerManager, viewManager and effectManager are always defined if deckPicker is
952
+ layers: this.layerManager!.getLayers(opts),
953
+ views: this.viewManager!.getViews(),
954
+ viewports: this.getViewports(opts),
955
+ onViewportActive: this.layerManager!.activateViewport,
956
+ effects: this.effectManager!.getEffects(),
957
+ ...opts
958
+ });
959
+
960
+ stats.get(statKey).timeEnd();
961
+
962
+ return infos;
963
+ }
964
+
736
965
  private _pick(
737
966
  method: 'pickObject',
738
967
  statKey: string,
@@ -992,46 +1221,41 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
992
1221
 
993
1222
  /** Actually run picking */
994
1223
  private _pickAndCallback() {
995
- if (this.device?.type === 'webgpu') {
996
- return;
997
- }
998
-
999
1224
  const {_pickRequest} = this;
1000
1225
 
1001
1226
  if (_pickRequest.event) {
1002
- // Check if any layer has pickable: '3d' to enable depth picking for hover
1227
+ const event = _pickRequest.event;
1003
1228
  const layers = this.layerManager?.getLayers() || [];
1004
- const has3DPickableLayers = layers.some(layer => layer.props.pickable === '3d');
1005
- _pickRequest.unproject3D = has3DPickableLayers;
1006
-
1007
- // Perform picking
1008
- const {result, emptyInfo} = this._pick('pickObject', 'pickObject Time', _pickRequest);
1009
- this.cursorState.isHovering = result.length > 0;
1010
-
1011
- // There are 4 possible scenarios:
1012
- // result is [outInfo, pickedInfo] (moved from one pickable layer to another)
1013
- // result is [outInfo] (moved outside of a pickable layer)
1014
- // result is [pickedInfo] (moved into or over a pickable layer)
1015
- // result is [] (nothing is or was picked)
1016
- //
1017
- // `layer.props.onHover` should be called on all affected layers (out/over)
1018
- // `deck.props.onHover` should be called with the picked info if any, or empty info otherwise
1019
- // `deck.props.getTooltip` should be called with the picked info if any, or empty info otherwise
1020
-
1021
- // Execute callbacks
1022
- let pickedInfo = emptyInfo;
1023
- let handled = false;
1024
- for (const info of result) {
1025
- pickedInfo = info;
1026
- handled = info.layer?.onHover(info, _pickRequest.event) || handled;
1229
+ const pickOptions = this._getPointPickOptions(
1230
+ _pickRequest.x,
1231
+ _pickRequest.y,
1232
+ {
1233
+ radius: _pickRequest.radius,
1234
+ mode: _pickRequest.mode
1235
+ },
1236
+ layers
1237
+ );
1238
+ const internalPickingMode = this._getInternalPickingMode();
1239
+ const hoverPickSequence = ++this._hoverPickSequence;
1240
+
1241
+ _pickRequest.event = null;
1242
+
1243
+ if (!internalPickingMode) {
1244
+ return;
1027
1245
  }
1028
- if (!handled) {
1029
- this.props.onHover?.(pickedInfo, _pickRequest.event);
1030
- this.widgetManager!.onHover(pickedInfo, _pickRequest.event);
1246
+
1247
+ if (internalPickingMode === 'sync') {
1248
+ this._applyHoverCallbacks(this._pickPointSync(pickOptions), event);
1249
+ return;
1031
1250
  }
1032
1251
 
1033
- // Clear pending pickRequest
1034
- _pickRequest.event = null;
1252
+ this._pickPointAsync(pickOptions)
1253
+ .then(({result, emptyInfo}) => {
1254
+ if (hoverPickSequence === this._hoverPickSequence) {
1255
+ this._applyHoverCallbacks({result, emptyInfo}, event);
1256
+ }
1257
+ })
1258
+ .catch(error => this.props.onError?.(error));
1035
1259
  }
1036
1260
  }
1037
1261
 
@@ -1044,6 +1268,7 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
1044
1268
 
1045
1269
  private _setDevice(device: Device) {
1046
1270
  this.device = device;
1271
+ this._validateInternalPickingMode();
1047
1272
 
1048
1273
  if (!this.animationLoop) {
1049
1274
  // finalize() has been called
@@ -1231,10 +1456,7 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
1231
1456
  this.layerManager!.updateLayers();
1232
1457
 
1233
1458
  // Perform picking request if any
1234
- // TODO(ibgreen): Picking not yet supported on WebGPU
1235
- if (this.device?.type !== 'webgpu') {
1236
- this._pickAndCallback();
1237
- }
1459
+ this._pickAndCallback();
1238
1460
 
1239
1461
  // Redraw if necessary
1240
1462
  this.redraw();
@@ -1279,61 +1501,87 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
1279
1501
  return;
1280
1502
  }
1281
1503
 
1282
- let info: PickingInfo;
1283
-
1284
- // For click events, check if any layer has pickable: '3d' to determine if we need depth picking
1285
1504
  const layers = this.layerManager.getLayers();
1286
- const has3DPickableLayers = layers.some(layer => layer.props.pickable === '3d');
1505
+ const internalPickingMode = this._getInternalPickingMode();
1287
1506
 
1288
- if (event.type === 'click' && has3DPickableLayers) {
1289
- // Perform a fresh pick to get depth info for 3D coordinates
1290
- const pickResult = this._pick('pickObject', 'pickObject Time', {
1291
- x: pos.x,
1292
- y: pos.y,
1293
- radius: this.props.pickingRadius,
1294
- unproject3D: true
1295
- });
1296
- info = pickResult.result[0] || pickResult.emptyInfo;
1297
- } else {
1298
- // Reuse last picked object for other events
1299
- info = this.deckPicker!.getLastPickedObject(
1300
- {
1301
- x: pos.x,
1302
- y: pos.y,
1303
- layers,
1304
- viewports: this.getViewports(pos)
1305
- },
1306
- this._lastPointerDownInfo
1307
- ) as PickingInfo;
1507
+ if (!internalPickingMode) {
1508
+ return;
1308
1509
  }
1309
1510
 
1310
- const {layer} = info;
1311
- const layerHandler = layer && (layer[eventHandlerProp] || layer.props[eventHandlerProp]);
1312
- const rootHandler = this.props[eventHandlerProp];
1313
- let handled = false;
1314
-
1315
- if (layerHandler) {
1316
- handled = layerHandler.call(layer, info, event);
1317
- }
1318
- if (!handled) {
1319
- rootHandler?.(info, event);
1320
- this.widgetManager!.onEvent(info, event);
1511
+ if (internalPickingMode === 'sync') {
1512
+ const info =
1513
+ event.type === 'click' && this._shouldUnproject3D(layers)
1514
+ ? this._getFirstPickedInfo(
1515
+ this._pickPointSync(
1516
+ this._getPointPickOptions(pos.x, pos.y, {unproject3D: true}, layers)
1517
+ )
1518
+ )
1519
+ : this._getLastPointerDownPickingInfo(pos.x, pos.y, layers);
1520
+
1521
+ this._dispatchPickingEvent(info, event);
1522
+ return;
1321
1523
  }
1524
+
1525
+ const pointerDownInfoPromise =
1526
+ this._lastPointerDownInfoPromise ||
1527
+ Promise.resolve(this._getLastPointerDownPickingInfo(pos.x, pos.y, layers));
1528
+
1529
+ pointerDownInfoPromise
1530
+ .then(info => {
1531
+ this._dispatchPickingEvent(info, event);
1532
+ })
1533
+ .catch(error => this.props.onError?.(error));
1322
1534
  };
1323
1535
 
1324
1536
  /** Internal use only: evnet handler for pointerdown */
1325
1537
  _onPointerDown = (event: MjolnirPointerEvent) => {
1326
- // TODO(ibgreen) Picking not yet supported on WebGPU
1327
- if (this.device?.type === 'webgpu') {
1538
+ const pos = event.offsetCenter;
1539
+ if (!pos) {
1328
1540
  return;
1329
1541
  }
1330
- const pos = event.offsetCenter;
1331
- const pickedInfo = this._pick('pickObject', 'pickObject Time', {
1332
- x: pos.x,
1333
- y: pos.y,
1334
- radius: this.props.pickingRadius
1335
- });
1336
- this._lastPointerDownInfo = pickedInfo.result[0] || pickedInfo.emptyInfo;
1542
+
1543
+ const internalPickingMode = this._getInternalPickingMode();
1544
+ if (!internalPickingMode) {
1545
+ return;
1546
+ }
1547
+
1548
+ const layers = this.layerManager?.getLayers() || [];
1549
+ const pointerDownPickSequence = ++this._pointerDownPickSequence;
1550
+
1551
+ if (internalPickingMode === 'sync') {
1552
+ const pickedInfo = this._pickPointSync({
1553
+ x: pos.x,
1554
+ y: pos.y,
1555
+ radius: this.props.pickingRadius
1556
+ });
1557
+ const info = this._getFirstPickedInfo(pickedInfo);
1558
+ this._lastPointerDownInfo = info;
1559
+ this._lastPointerDownInfoPromise = Promise.resolve(info);
1560
+ return;
1561
+ }
1562
+
1563
+ const pickPromise = this._pickPointAsync(this._getPointPickOptions(pos.x, pos.y, {}, layers))
1564
+ .then(pickResult => this._getFirstPickedInfo(pickResult))
1565
+ .then(info => {
1566
+ if (pointerDownPickSequence === this._pointerDownPickSequence) {
1567
+ this._lastPointerDownInfo = info;
1568
+ }
1569
+ return info;
1570
+ })
1571
+ .catch(error => {
1572
+ this.props.onError?.(error);
1573
+ const fallbackInfo =
1574
+ this.deckPicker && this.viewManager
1575
+ ? this._getLastPointerDownPickingInfo(pos.x, pos.y, layers)
1576
+ : ({} as PickingInfo);
1577
+ if (pointerDownPickSequence === this._pointerDownPickSequence) {
1578
+ this._lastPointerDownInfo = fallbackInfo;
1579
+ }
1580
+ return fallbackInfo;
1581
+ });
1582
+
1583
+ this._lastPointerDownInfo = null;
1584
+ this._lastPointerDownInfoPromise = pickPromise;
1337
1585
  };
1338
1586
 
1339
1587
  private _getFrameStats(): void {
package/src/lib/layer.ts CHANGED
@@ -1071,14 +1071,10 @@ export default abstract class Layer<PropsT extends {} = {}> extends Component<
1071
1071
  context.device.setParametersWebGL({polygonOffset: offsets});
1072
1072
  }
1073
1073
 
1074
- for (const model of this.getModels()) {
1075
- if (model.device.type === 'webgpu') {
1076
- // TODO(ibgreen): model.setParameters currently wipes parameters. Semantics TBD.
1077
- model.setParameters({...model.parameters, ...parameters});
1078
- } else {
1079
- model.setParameters(parameters);
1080
- }
1081
- }
1074
+ const webGPUDrawParameters =
1075
+ context.device instanceof WebGLDevice ? null : splitWebGPUDrawParameters(parameters);
1076
+
1077
+ applyModelParameters(this.getModels(), renderPass, parameters, webGPUDrawParameters);
1082
1078
 
1083
1079
  // Call subclass lifecycle method
1084
1080
  if (context.device instanceof WebGLDevice) {
@@ -1093,6 +1089,9 @@ export default abstract class Layer<PropsT extends {} = {}> extends Component<
1093
1089
  this.draw(opts);
1094
1090
  });
1095
1091
  } else {
1092
+ if (webGPUDrawParameters?.renderPassParameters) {
1093
+ renderPass.setParameters(webGPUDrawParameters.renderPassParameters);
1094
+ }
1096
1095
  const opts: DrawOptions = {renderPass, shaderModuleProps, uniforms, parameters, context};
1097
1096
 
1098
1097
  // extensions
@@ -1344,3 +1343,94 @@ export default abstract class Layer<PropsT extends {} = {}> extends Component<
1344
1343
  this.setNeedsUpdate();
1345
1344
  }
1346
1345
  }
1346
+
1347
+ function splitWebGPUDrawParameters(parameters: LumaParameters): {
1348
+ pipelineParameters: LumaParameters;
1349
+ renderPassParameters?: {blendConstant: [number, number, number, number]};
1350
+ } {
1351
+ const {blendConstant, ...pipelineParameters} = parameters as LumaParameters & {
1352
+ blendConstant?: [number, number, number, number];
1353
+ };
1354
+
1355
+ return blendConstant
1356
+ ? {
1357
+ pipelineParameters,
1358
+ renderPassParameters: {blendConstant}
1359
+ }
1360
+ : {pipelineParameters};
1361
+ }
1362
+
1363
+ function applyModelParameters(
1364
+ models: Model[],
1365
+ renderPass: RenderPass,
1366
+ parameters: LumaParameters,
1367
+ webGPUDrawParameters: ReturnType<typeof splitWebGPUDrawParameters> | null
1368
+ ): void {
1369
+ for (const model of models) {
1370
+ if (model.device.type === 'webgpu') {
1371
+ syncModelAttachmentFormats(model, renderPass);
1372
+ // TODO(ibgreen): model.setParameters currently wipes parameters. Semantics TBD.
1373
+ model.setParameters({
1374
+ ...model.parameters,
1375
+ ...webGPUDrawParameters?.pipelineParameters
1376
+ });
1377
+ } else {
1378
+ model.setParameters(parameters);
1379
+ }
1380
+ }
1381
+ }
1382
+
1383
+ function syncModelAttachmentFormats(model: Model, renderPass: RenderPass): void {
1384
+ const framebuffer =
1385
+ renderPass.props.framebuffer ||
1386
+ ((
1387
+ renderPass as RenderPass & {
1388
+ framebuffer?: {
1389
+ colorAttachments: Array<{texture: {format: string}}>;
1390
+ depthStencilAttachment: {texture: {format: string}} | null;
1391
+ };
1392
+ }
1393
+ ).framebuffer ??
1394
+ null);
1395
+
1396
+ if (!framebuffer) {
1397
+ return;
1398
+ }
1399
+
1400
+ const colorAttachmentFormats = framebuffer.colorAttachments.map(
1401
+ attachment => attachment?.texture?.format ?? null
1402
+ );
1403
+ const depthStencilAttachmentFormat = framebuffer.depthStencilAttachment?.texture?.format;
1404
+
1405
+ const modelWithProps = model as unknown as {
1406
+ props: {
1407
+ colorAttachmentFormats?: (string | null)[];
1408
+ depthStencilAttachmentFormat?: string;
1409
+ };
1410
+ _setPipelineNeedsUpdate(reason: string): void;
1411
+ };
1412
+
1413
+ if (
1414
+ !equalAttachmentFormats(modelWithProps.props.colorAttachmentFormats, colorAttachmentFormats) ||
1415
+ modelWithProps.props.depthStencilAttachmentFormat !== depthStencilAttachmentFormat
1416
+ ) {
1417
+ modelWithProps.props.colorAttachmentFormats = colorAttachmentFormats;
1418
+ modelWithProps.props.depthStencilAttachmentFormat = depthStencilAttachmentFormat;
1419
+ modelWithProps._setPipelineNeedsUpdate('attachment formats');
1420
+ }
1421
+ }
1422
+
1423
+ function equalAttachmentFormats(left?: (string | null)[], right?: (string | null)[]): boolean {
1424
+ if (left === right) {
1425
+ return true;
1426
+ }
1427
+ if (!left || !right || left.length !== right.length) {
1428
+ return false;
1429
+ }
1430
+ for (let i = 0; i < left.length; i++) {
1431
+ if (left[i] !== right[i]) {
1432
+ return false;
1433
+ }
1434
+ }
1435
+ return true;
1436
+ }
@@ -181,7 +181,7 @@ export default class ViewManager<ViewsT extends View[]> {
181
181
  typeof viewOrViewId === 'string' ? this.getView(viewOrViewId) : viewOrViewId;
182
182
  // Backward compatibility: view state for single view
183
183
  const viewState = (view && this.viewState[view.getViewStateId()]) || this.viewState;
184
- return view ? view.filterViewState(viewState) : viewState;
184
+ return (view ? view.filterViewState(viewState) : viewState) as AnyViewStateOf<ViewsT>;
185
185
  }
186
186
 
187
187
  getViewport(viewId: string): Viewport | undefined {