@deck.gl/core 9.2.11 → 9.3.0-alpha.2

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 (152) hide show
  1. package/debug.min.js +1 -1
  2. package/dist/controllers/controller.d.ts +10 -0
  3. package/dist/controllers/controller.d.ts.map +1 -1
  4. package/dist/controllers/controller.js +15 -0
  5. package/dist/controllers/controller.js.map +1 -1
  6. package/dist/controllers/first-person-controller.d.ts +3 -2
  7. package/dist/controllers/first-person-controller.d.ts.map +1 -1
  8. package/dist/controllers/first-person-controller.js +13 -5
  9. package/dist/controllers/first-person-controller.js.map +1 -1
  10. package/dist/controllers/globe-controller.d.ts +1 -0
  11. package/dist/controllers/globe-controller.d.ts.map +1 -1
  12. package/dist/controllers/globe-controller.js +66 -5
  13. package/dist/controllers/globe-controller.js.map +1 -1
  14. package/dist/controllers/map-controller.d.ts +21 -3
  15. package/dist/controllers/map-controller.d.ts.map +1 -1
  16. package/dist/controllers/map-controller.js +139 -25
  17. package/dist/controllers/map-controller.js.map +1 -1
  18. package/dist/controllers/orbit-controller.d.ts +12 -4
  19. package/dist/controllers/orbit-controller.d.ts.map +1 -1
  20. package/dist/controllers/orbit-controller.js +118 -10
  21. package/dist/controllers/orbit-controller.js.map +1 -1
  22. package/dist/controllers/orthographic-controller.d.ts +117 -9
  23. package/dist/controllers/orthographic-controller.d.ts.map +1 -1
  24. package/dist/controllers/orthographic-controller.js +302 -37
  25. package/dist/controllers/orthographic-controller.js.map +1 -1
  26. package/dist/controllers/view-state.d.ts +4 -1
  27. package/dist/controllers/view-state.d.ts.map +1 -1
  28. package/dist/controllers/view-state.js +2 -1
  29. package/dist/controllers/view-state.js.map +1 -1
  30. package/dist/debug/loggers.d.ts.map +1 -1
  31. package/dist/debug/loggers.js +1 -4
  32. package/dist/debug/loggers.js.map +1 -1
  33. package/dist/dist.dev.js +7585 -10714
  34. package/dist/effects/lighting/lighting-effect.d.ts +1 -0
  35. package/dist/effects/lighting/lighting-effect.d.ts.map +1 -1
  36. package/dist/effects/lighting/lighting-effect.js +14 -5
  37. package/dist/effects/lighting/lighting-effect.js.map +1 -1
  38. package/dist/index.cjs +812 -120
  39. package/dist/index.cjs.map +4 -4
  40. package/dist/lib/attribute/attribute-manager.d.ts.map +1 -1
  41. package/dist/lib/attribute/attribute-manager.js +2 -0
  42. package/dist/lib/attribute/attribute-manager.js.map +1 -1
  43. package/dist/lib/attribute/data-column.js +2 -2
  44. package/dist/lib/attribute/data-column.js.map +1 -1
  45. package/dist/lib/attribute/gl-utils.d.ts +1 -1
  46. package/dist/lib/attribute/gl-utils.d.ts.map +1 -1
  47. package/dist/lib/attribute/gl-utils.js +4 -0
  48. package/dist/lib/attribute/gl-utils.js.map +1 -1
  49. package/dist/lib/deck-picker.d.ts +14 -1
  50. package/dist/lib/deck-picker.d.ts.map +1 -1
  51. package/dist/lib/deck-picker.js +43 -11
  52. package/dist/lib/deck-picker.js.map +1 -1
  53. package/dist/lib/deck-renderer.d.ts +6 -1
  54. package/dist/lib/deck-renderer.d.ts.map +1 -1
  55. package/dist/lib/deck-renderer.js +14 -2
  56. package/dist/lib/deck-renderer.js.map +1 -1
  57. package/dist/lib/deck.d.ts +10 -0
  58. package/dist/lib/deck.d.ts.map +1 -1
  59. package/dist/lib/deck.js +29 -11
  60. package/dist/lib/deck.js.map +1 -1
  61. package/dist/lib/init.js +2 -2
  62. package/dist/lib/init.js.map +1 -1
  63. package/dist/lib/layer.d.ts.map +1 -1
  64. package/dist/lib/layer.js +7 -3
  65. package/dist/lib/layer.js.map +1 -1
  66. package/dist/lib/view-manager.d.ts +4 -0
  67. package/dist/lib/view-manager.d.ts.map +1 -1
  68. package/dist/lib/view-manager.js +6 -1
  69. package/dist/lib/view-manager.js.map +1 -1
  70. package/dist/lib/widget.d.ts +4 -0
  71. package/dist/lib/widget.d.ts.map +1 -1
  72. package/dist/lib/widget.js +11 -0
  73. package/dist/lib/widget.js.map +1 -1
  74. package/dist/passes/draw-layers-pass.d.ts +2 -0
  75. package/dist/passes/draw-layers-pass.d.ts.map +1 -1
  76. package/dist/passes/draw-layers-pass.js +3 -0
  77. package/dist/passes/draw-layers-pass.js.map +1 -1
  78. package/dist/passes/layers-pass.d.ts +2 -1
  79. package/dist/passes/layers-pass.d.ts.map +1 -1
  80. package/dist/passes/layers-pass.js +7 -3
  81. package/dist/passes/layers-pass.js.map +1 -1
  82. package/dist/passes/pick-layers-pass.d.ts +6 -3
  83. package/dist/passes/pick-layers-pass.d.ts.map +1 -1
  84. package/dist/passes/pick-layers-pass.js +12 -4
  85. package/dist/passes/pick-layers-pass.js.map +1 -1
  86. package/dist/shaderlib/project/project.glsl.d.ts.map +1 -1
  87. package/dist/shaderlib/project/project.glsl.js +3 -0
  88. package/dist/shaderlib/project/project.glsl.js.map +1 -1
  89. package/dist/utils/deep-merge.d.ts +5 -0
  90. package/dist/utils/deep-merge.d.ts.map +1 -0
  91. package/dist/utils/deep-merge.js +31 -0
  92. package/dist/utils/deep-merge.js.map +1 -0
  93. package/dist/utils/math-utils.d.ts +4 -0
  94. package/dist/utils/math-utils.d.ts.map +1 -1
  95. package/dist/utils/math-utils.js +8 -0
  96. package/dist/utils/math-utils.js.map +1 -1
  97. package/dist/utils/texture.d.ts.map +1 -1
  98. package/dist/utils/texture.js +3 -1
  99. package/dist/utils/texture.js.map +1 -1
  100. package/dist/viewports/globe-viewport.d.ts +1 -0
  101. package/dist/viewports/globe-viewport.d.ts.map +1 -1
  102. package/dist/viewports/globe-viewport.js +1 -1
  103. package/dist/viewports/globe-viewport.js.map +1 -1
  104. package/dist/viewports/orbit-viewport.d.ts.map +1 -1
  105. package/dist/viewports/orbit-viewport.js +7 -2
  106. package/dist/viewports/orbit-viewport.js.map +1 -1
  107. package/dist/viewports/orthographic-viewport.d.ts +8 -2
  108. package/dist/viewports/orthographic-viewport.d.ts.map +1 -1
  109. package/dist/viewports/orthographic-viewport.js.map +1 -1
  110. package/dist/viewports/web-mercator-viewport.d.ts +5 -0
  111. package/dist/viewports/web-mercator-viewport.d.ts.map +1 -1
  112. package/dist/viewports/web-mercator-viewport.js +9 -0
  113. package/dist/viewports/web-mercator-viewport.js.map +1 -1
  114. package/dist/views/orthographic-view.d.ts +38 -4
  115. package/dist/views/orthographic-view.d.ts.map +1 -1
  116. package/dist/views/orthographic-view.js.map +1 -1
  117. package/dist/views/view.d.ts.map +1 -1
  118. package/dist/views/view.js +2 -8
  119. package/dist/views/view.js.map +1 -1
  120. package/dist.min.js +226 -154
  121. package/package.json +9 -9
  122. package/src/controllers/controller.ts +25 -2
  123. package/src/controllers/first-person-controller.ts +18 -8
  124. package/src/controllers/globe-controller.ts +89 -5
  125. package/src/controllers/map-controller.ts +174 -32
  126. package/src/controllers/orbit-controller.ts +147 -13
  127. package/src/controllers/orthographic-controller.ts +417 -41
  128. package/src/controllers/view-state.ts +10 -3
  129. package/src/debug/loggers.ts +1 -5
  130. package/src/effects/lighting/lighting-effect.ts +20 -8
  131. package/src/lib/attribute/attribute-manager.ts +1 -0
  132. package/src/lib/attribute/data-column.ts +3 -3
  133. package/src/lib/attribute/gl-utils.ts +5 -1
  134. package/src/lib/deck-picker.ts +47 -12
  135. package/src/lib/deck-renderer.ts +17 -3
  136. package/src/lib/deck.ts +39 -11
  137. package/src/lib/layer.ts +7 -3
  138. package/src/lib/view-manager.ts +9 -1
  139. package/src/lib/widget.ts +14 -0
  140. package/src/passes/draw-layers-pass.ts +5 -0
  141. package/src/passes/layers-pass.ts +9 -4
  142. package/src/passes/pick-layers-pass.ts +18 -6
  143. package/src/shaderlib/project/project.glsl.ts +3 -0
  144. package/src/utils/deep-merge.ts +33 -0
  145. package/src/utils/math-utils.ts +12 -0
  146. package/src/utils/texture.ts +3 -1
  147. package/src/viewports/globe-viewport.ts +1 -1
  148. package/src/viewports/orbit-viewport.ts +8 -2
  149. package/src/viewports/orthographic-viewport.ts +8 -2
  150. package/src/viewports/web-mercator-viewport.ts +10 -0
  151. package/src/views/orthographic-view.ts +38 -4
  152. package/src/views/view.ts +2 -8
@@ -11,7 +11,8 @@ import {
11
11
  getEmptyPickingInfo,
12
12
  PickingInfo
13
13
  } from './picking/pick-info';
14
-
14
+ import type {RenderStats} from '../passes/layers-pass';
15
+ import type {Stats} from '@probe.gl/stats';
15
16
  import type {Framebuffer} from '@luma.gl/core';
16
17
  import type {FilterContext, Rect} from '../passes/layers-pass';
17
18
  import type Layer from './layer';
@@ -52,6 +53,7 @@ export default class DeckPicker {
52
53
  depthFBO?: Framebuffer;
53
54
  pickLayersPass: PickLayersPass;
54
55
  layerFilter?: (context: FilterContext) => boolean;
56
+ stats?: Stats;
55
57
 
56
58
  /** Identifiers of the previously picked object, for callback tracking and auto highlight */
57
59
  lastPickedInfo: {
@@ -62,8 +64,9 @@ export default class DeckPicker {
62
64
 
63
65
  _pickable: boolean = true;
64
66
 
65
- constructor(device: Device) {
67
+ constructor(device: Device, opts: {stats?: Stats} = {}) {
66
68
  this.device = device;
69
+ this.stats = opts.stats;
67
70
  this.pickLayersPass = new PickLayersPass(device);
68
71
  this.lastPickedInfo = {
69
72
  index: -1,
@@ -281,16 +284,17 @@ export default class DeckPicker {
281
284
  }
282
285
 
283
286
  let z;
284
- if (pickInfo.pickedLayer && unproject3D && this.depthFBO) {
287
+ const depthLayers = this._getDepthLayers(pickInfo, pickableLayers, unproject3D);
288
+ if (depthLayers.length > 0) {
285
289
  const {pickedColors: pickedColors2} = this._drawAndSample(
286
290
  {
287
- layers: [pickInfo.pickedLayer],
291
+ layers: depthLayers,
288
292
  views,
289
293
  viewports,
290
294
  onViewportActive,
291
295
  deviceRect: {
292
- x: pickInfo.pickedX as number,
293
- y: pickInfo.pickedY as number,
296
+ x: pickInfo.pickedX ?? devicePixel[0],
297
+ y: pickInfo.pickedY ?? devicePixel[1],
294
298
  width: 1,
295
299
  height: 1
296
300
  },
@@ -444,16 +448,17 @@ export default class DeckPicker {
444
448
  }
445
449
 
446
450
  let z;
447
- if (pickInfo.pickedLayer && unproject3D && this.depthFBO) {
451
+ const depthLayers = this._getDepthLayers(pickInfo, pickableLayers, unproject3D);
452
+ if (depthLayers.length > 0) {
448
453
  const {pickedColors: pickedColors2} = this._drawAndSample(
449
454
  {
450
- layers: [pickInfo.pickedLayer],
455
+ layers: depthLayers,
451
456
  views,
452
457
  viewports,
453
458
  onViewportActive,
454
459
  deviceRect: {
455
- x: pickInfo.pickedX as number,
456
- y: pickInfo.pickedY as number,
460
+ x: pickInfo.pickedX ?? devicePixel[0],
461
+ y: pickInfo.pickedY ?? devicePixel[1],
457
462
  width: 1,
458
463
  height: 1
459
464
  },
@@ -806,7 +811,8 @@ export default class DeckPicker {
806
811
  }
807
812
  }
808
813
 
809
- const {decodePickingColor} = this.pickLayersPass.render(opts);
814
+ const {decodePickingColor, stats} = this.pickLayersPass.render(opts);
815
+ this._updateStats(stats);
810
816
 
811
817
  // Read from an already rendered picking buffer
812
818
  // Returns an Uint8ClampedArray of picked pixels
@@ -911,7 +917,8 @@ export default class DeckPicker {
911
917
  }
912
918
  }
913
919
 
914
- const {decodePickingColor} = this.pickLayersPass.render(opts);
920
+ const {decodePickingColor, stats} = this.pickLayersPass.render(opts);
921
+ this._updateStats(stats);
915
922
 
916
923
  // Read from an already rendered picking buffer
917
924
  // Returns an Uint8ClampedArray of picked pixels
@@ -928,6 +935,34 @@ export default class DeckPicker {
928
935
  return {pickedColors, decodePickingColor};
929
936
  }
930
937
 
938
+ private _updateStats(source: RenderStats[]) {
939
+ if (!this.stats) return;
940
+ let layersCount = 0;
941
+ for (const {visibleCount} of source) {
942
+ layersCount += visibleCount;
943
+ }
944
+ this.stats.get('Layers picked').addCount(layersCount);
945
+ }
946
+
947
+ /**
948
+ * Determine which layers to use for the depth (pickZ) pass.
949
+ * - If a non-draped layer was picked, use just that layer.
950
+ * - If a draped layer was picked (geometry is at z=0) or no layer was picked
951
+ * (e.g. no-FBO tiles at extreme zoom), fall back to terrain layers.
952
+ */
953
+ _getDepthLayers(pickInfo: PickedPixel, pickableLayers: Layer[], unproject3D?: boolean): Layer[] {
954
+ if (!unproject3D || !this.depthFBO) {
955
+ return [];
956
+ }
957
+ const {pickedLayer} = pickInfo;
958
+ const isDraped = pickedLayer?.state?.terrainDrawMode === 'drape';
959
+ if (pickedLayer && !isDraped) {
960
+ return [pickedLayer];
961
+ }
962
+ // For draped layers or when no layer was picked, use terrain layers for depth
963
+ return pickableLayers.filter(l => l.props.operation.includes('terrain'));
964
+ }
965
+
931
966
  /**
932
967
  * Calculate a picking rect centered on deviceX and deviceY and clipped to device
933
968
  * @returns null if pixel is outside of device
@@ -7,7 +7,8 @@ import {Framebuffer} from '@luma.gl/core';
7
7
  import debug from '../debug/index';
8
8
  import DrawLayersPass from '../passes/draw-layers-pass';
9
9
  import PickLayersPass from '../passes/pick-layers-pass';
10
-
10
+ import type {RenderStats} from '../passes/layers-pass';
11
+ import type {Stats} from '@probe.gl/stats';
11
12
  import type Layer from './layer';
12
13
  import type Viewport from '../viewports/viewport';
13
14
  import type View from '../views/view';
@@ -24,14 +25,16 @@ export default class DeckRenderer {
24
25
  drawPickingColors: boolean;
25
26
  drawLayersPass: DrawLayersPass;
26
27
  pickLayersPass: PickLayersPass;
28
+ stats?: Stats;
27
29
 
28
30
  private renderCount: number;
29
31
  private _needsRedraw: string | false;
30
32
  private renderBuffers: Framebuffer[];
31
33
  private lastPostProcessEffect: string | null;
32
34
 
33
- constructor(device: Device) {
35
+ constructor(device: Device, opts: {stats?: Stats} = {}) {
34
36
  this.device = device;
37
+ this.stats = opts.stats;
35
38
  this.layerFilter = null;
36
39
  this.drawPickingColors = false;
37
40
  this.drawLayersPass = new DrawLayersPass(device);
@@ -88,7 +91,8 @@ export default class DeckRenderer {
88
91
  renderOpts.clearColor = [0, 0, 0, 0];
89
92
  renderOpts.clearCanvas = true;
90
93
  }
91
- const renderStats = layerPass.render({...renderOpts, target: outputBuffer});
94
+ const renderResult = layerPass.render({...renderOpts, target: outputBuffer});
95
+ const renderStats = 'stats' in renderResult ? renderResult.stats : renderResult;
92
96
 
93
97
  if (renderOpts.effects) {
94
98
  if (this.lastPostProcessEffect) {
@@ -102,6 +106,7 @@ export default class DeckRenderer {
102
106
  this.renderCount++;
103
107
 
104
108
  debug(TRACE_RENDER_LAYERS, this, renderStats, opts);
109
+ this._updateStats(renderStats);
105
110
  }
106
111
 
107
112
  needsRedraw(opts: {clearRedrawFlags: boolean} = {clearRedrawFlags: false}): string | false {
@@ -120,6 +125,15 @@ export default class DeckRenderer {
120
125
  renderBuffers.length = 0;
121
126
  }
122
127
 
128
+ private _updateStats(source: RenderStats[]) {
129
+ if (!this.stats) return;
130
+ let layersCount = 0;
131
+ for (const {visibleCount} of source) {
132
+ layersCount += visibleCount;
133
+ }
134
+ this.stats.get('Layers rendered').addCount(layersCount);
135
+ }
136
+
123
137
  private _preRender(effects: Effect[], opts: LayersPassRenderOptions) {
124
138
  this.lastPostProcessEffect = null;
125
139
  opts.preRenderStats = opts.preRenderStats || {};
package/src/lib/deck.ts CHANGED
@@ -55,10 +55,15 @@ const getCursor = ({isDragging}) => (isDragging ? 'grabbing' : 'grab');
55
55
  export type DeckMetrics = {
56
56
  fps: number;
57
57
  setPropsTime: number;
58
+ layersCount: number;
59
+ drawLayersCount: number;
60
+ updateLayersCount: number;
58
61
  updateAttributesTime: number;
62
+ updateAttributesCount: number;
59
63
  framesRedrawn: number;
60
64
  pickTime: number;
61
65
  pickCount: number;
66
+ pickLayersCount: number;
62
67
  gpuTime: number;
63
68
  gpuTimePerFrame: number;
64
69
  cpuTime: number;
@@ -309,10 +314,15 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
309
314
  protected metrics: DeckMetrics = {
310
315
  fps: 0,
311
316
  setPropsTime: 0,
317
+ layersCount: 0,
318
+ drawLayersCount: 0,
319
+ updateLayersCount: 0,
320
+ updateAttributesCount: 0,
312
321
  updateAttributesTime: 0,
313
322
  framesRedrawn: 0,
314
323
  pickTime: 0,
315
324
  pickCount: 0,
325
+ pickLayersCount: 0,
316
326
  gpuTime: 0,
317
327
  gpuTimePerFrame: 0,
318
328
  cpuTime: 0,
@@ -381,13 +391,9 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
381
391
  _cachePipelines: true,
382
392
  ...this.props.deviceProps,
383
393
  onResize: (canvasContext, info) => {
384
- // Manually sync drawing buffer dimensions (canvas is externally managed)
385
- // TODO(v9.3): Use canvasContext.setDrawingBufferSize(width, height) when upgrading to luma 9.3+
394
+ // Sync drawing buffer dimensions with externally-managed canvas
386
395
  const {width, height} = canvasContext.canvas;
387
- // @ts-ignore - accessing public properties to sync state
388
- canvasContext.drawingBufferWidth = width;
389
- // @ts-ignore
390
- canvasContext.drawingBufferHeight = height;
396
+ canvasContext.setDrawingBufferSize(width, height);
391
397
 
392
398
  this._needsRedraw = 'Canvas resized';
393
399
  userOnResize?.(canvasContext, info);
@@ -678,6 +684,15 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
678
684
  return this._pick('pickObjects', 'pickObjects Time', opts);
679
685
  }
680
686
 
687
+ /**
688
+ * Internal method used by controllers to pick 3D position at a screen coordinate
689
+ * @private
690
+ */
691
+ private _pickPositionForController(x: number, y: number): {coordinate?: number[]} | null {
692
+ const pickResult = this.pickObject({x, y, radius: 0, unproject3D: true});
693
+ return pickResult;
694
+ }
695
+
681
696
  /** Experimental
682
697
  * Add a global resource for sharing among layers
683
698
  */
@@ -1072,7 +1087,9 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
1072
1087
  timeline.play();
1073
1088
  this.animationLoop.attachTimeline(timeline);
1074
1089
 
1075
- this.eventManager = new EventManager(this.props.parent || this.canvas, {
1090
+ const eventRoot =
1091
+ this.props.parent?.querySelector<HTMLDivElement>('.deck-events-root') || this.canvas;
1092
+ this.eventManager = new EventManager(eventRoot, {
1076
1093
  touchAction: this.props.touchAction,
1077
1094
  recognizers: Object.keys(RECOGNIZERS).map((eventName: string) => {
1078
1095
  // Resolve recognizer settings
@@ -1101,6 +1118,7 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
1101
1118
  eventManager: this.eventManager,
1102
1119
  onViewStateChange: this._onViewStateChange.bind(this),
1103
1120
  onInteractionStateChange: this._onInteractionStateChange.bind(this),
1121
+ pickPosition: this._pickPositionForController.bind(this),
1104
1122
  views: this._getViews(),
1105
1123
  viewState: this._getViewState(),
1106
1124
  width: this.width,
@@ -1124,13 +1142,17 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
1124
1142
  device: this.device
1125
1143
  });
1126
1144
 
1127
- this.deckRenderer = new DeckRenderer(this.device);
1145
+ this.deckRenderer = new DeckRenderer(this.device, {stats: this.stats});
1146
+
1147
+ this.deckPicker = new DeckPicker(this.device, {stats: this.stats});
1128
1148
 
1129
- this.deckPicker = new DeckPicker(this.device);
1149
+ const widgetParent =
1150
+ this.props.parent?.querySelector<HTMLDivElement>('.deck-widgets-root') ||
1151
+ this.canvas?.parentElement;
1130
1152
 
1131
1153
  this.widgetManager = new WidgetManager({
1132
1154
  deck: this,
1133
- parentElement: this.canvas?.parentElement
1155
+ parentElement: widgetParent
1134
1156
  });
1135
1157
  this.widgetManager.addDefault(new TooltipWidget());
1136
1158
 
@@ -1337,13 +1359,19 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
1337
1359
  stats.get('pickObjects Time').time;
1338
1360
  metrics.pickCount = stats.get('Pick Count').count;
1339
1361
 
1362
+ metrics.layersCount = this.layerManager?.layers.length ?? 0;
1363
+ metrics.drawLayersCount = stats.get('Layers rendered').lastSampleCount;
1364
+ metrics.pickLayersCount = stats.get('Layers picked').lastSampleCount;
1365
+ metrics.updateAttributesCount = stats.get('Layers updated').count;
1366
+ metrics.updateAttributesCount = stats.get('Attributes updated').count;
1367
+
1340
1368
  // Luma stats
1341
1369
  metrics.gpuTime = stats.get('GPU Time').time;
1342
1370
  metrics.cpuTime = stats.get('CPU Time').time;
1343
1371
  metrics.gpuTimePerFrame = stats.get('GPU Time').getAverageTime();
1344
1372
  metrics.cpuTimePerFrame = stats.get('CPU Time').getAverageTime();
1345
1373
 
1346
- const memoryStats = luma.stats.get('Memory Usage');
1374
+ const memoryStats = luma.stats.get('GPU Time and Memory');
1347
1375
  metrics.bufferMemory = memoryStats.get('Buffer Memory').count;
1348
1376
  metrics.textureMemory = memoryStats.get('Texture Memory').count;
1349
1377
  metrics.renderbufferMemory = memoryStats.get('Renderbuffer Memory').count;
package/src/lib/layer.ts CHANGED
@@ -96,9 +96,12 @@ const defaultProps: DefaultProps<LayerProps> = {
96
96
  if (signal) {
97
97
  loadOptions = {
98
98
  ...loadOptions,
99
- fetch: {
100
- ...loadOptions?.fetch,
101
- signal
99
+ core: {
100
+ ...loadOptions?.core,
101
+ fetch: {
102
+ ...loadOptions?.core?.fetch,
103
+ signal
104
+ }
102
105
  }
103
106
  };
104
107
  }
@@ -966,6 +969,7 @@ export default abstract class Layer<PropsT extends {} = {}> extends Component<
966
969
  if (!stateNeedsUpdate) {
967
970
  return;
968
971
  }
972
+ this.context.stats.get('Layer updates').incrementCount();
969
973
 
970
974
  const currentProps = this.props;
971
975
  const context = this.context;
@@ -43,6 +43,7 @@ type ViewManagerProps<ViewsT extends ViewOrViews> = {
43
43
  viewState: ViewStateObject<ViewsT> | null;
44
44
  onViewStateChange?: (params: ViewStateChangeParameters<AnyViewStateOf<ViewsT>>) => void;
45
45
  onInteractionStateChange?: (state: InteractionState) => void;
46
+ pickPosition?: (x: number, y: number) => {coordinate?: number[]} | null;
46
47
  width?: number;
47
48
  height?: number;
48
49
  };
@@ -65,6 +66,7 @@ export default class ViewManager<ViewsT extends View[]> {
65
66
  onViewStateChange?: (params: ViewStateChangeParameters) => void;
66
67
  onInteractionStateChange?: (state: InteractionState) => void;
67
68
  };
69
+ private _pickPosition?: (x: number, y: number) => {coordinate?: number[]} | null;
68
70
 
69
71
  constructor(
70
72
  props: ViewManagerProps<ViewsT> & {
@@ -92,6 +94,7 @@ export default class ViewManager<ViewsT extends View[]> {
92
94
  onViewStateChange: props.onViewStateChange,
93
95
  onInteractionStateChange: props.onInteractionStateChange
94
96
  };
97
+ this._pickPosition = props.pickPosition;
95
98
 
96
99
  Object.seal(this);
97
100
 
@@ -224,6 +227,10 @@ export default class ViewManager<ViewsT extends View[]> {
224
227
  this._setSize(props.width as number, props.height as number);
225
228
  }
226
229
 
230
+ if ('pickPosition' in props) {
231
+ this._pickPosition = props.pickPosition;
232
+ }
233
+
227
234
  // Important: avoid invoking _update() inside itself
228
235
  // Nested updates result in unexpected side effects inside _rebuildViewports()
229
236
  // when using auto control in pure-js
@@ -308,7 +315,8 @@ export default class ViewManager<ViewsT extends View[]> {
308
315
  viewState,
309
316
  width: this.width,
310
317
  height: this.height
311
- })
318
+ }),
319
+ pickPosition: this._pickPosition
312
320
  });
313
321
 
314
322
  return controller;
package/src/lib/widget.ts CHANGED
@@ -98,6 +98,20 @@ export abstract class Widget<
98
98
  }
99
99
  }
100
100
 
101
+ // VIEW STATE HELPERS
102
+
103
+ /** Returns the current view state for the given view */
104
+ protected getViewState(viewId: string): Record<string, unknown> {
105
+ // @ts-ignore viewManager is private
106
+ return this.deck?.viewManager?.getViewState(viewId) || {};
107
+ }
108
+
109
+ /** Updates the view state for the given view */
110
+ protected setViewState(viewId: string, viewState: Record<string, unknown>): void {
111
+ // @ts-ignore Using private method temporary until there's a public one
112
+ this.deck?._onViewStateChange({viewId, viewState, interactionState: {}});
113
+ }
114
+
101
115
  // @note empty method calls have an overhead in V8 but it is very low, ~1ns
102
116
 
103
117
  /**
@@ -3,10 +3,15 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import LayersPass from './layers-pass';
6
+ import type {LayersPassRenderOptions, RenderStats} from './layers-pass';
6
7
 
7
8
  export default class DrawLayersPass extends LayersPass {
8
9
  shouldDrawLayer(layer) {
9
10
  const {operation} = layer.props;
10
11
  return operation.includes('draw') || operation.includes('terrain');
11
12
  }
13
+
14
+ render(options: LayersPassRenderOptions): RenderStats[] {
15
+ return this._render(options);
16
+ }
12
17
  }
@@ -65,9 +65,14 @@ export type RenderStats = {
65
65
  export default class LayersPass extends Pass {
66
66
  _lastRenderIndex: number = -1;
67
67
 
68
- render(options: LayersPassRenderOptions): any {
69
- // @ts-expect-error TODO - assuming WebGL context
70
- const [width, height] = this.device.canvasContext.getDrawingBufferSize();
68
+ render(options: LayersPassRenderOptions): void {
69
+ this._render(options);
70
+ }
71
+
72
+ protected _render(options: LayersPassRenderOptions): RenderStats[] {
73
+ const canvasContext = this.device.canvasContext!;
74
+ const framebuffer = options.target ?? canvasContext.getCurrentFramebuffer();
75
+ const [width, height] = canvasContext.getDrawingBufferSize();
71
76
 
72
77
  // Explicitly specify clearColor and clearDepth, overriding render pass defaults.
73
78
  const clearCanvas = options.clearCanvas ?? true;
@@ -85,7 +90,7 @@ export default class LayersPass extends Pass {
85
90
  }
86
91
 
87
92
  const renderPass = this.device.beginRenderPass({
88
- framebuffer: options.target,
93
+ framebuffer,
89
94
  parameters,
90
95
  clearColor: clearColor as NumberArray4,
91
96
  clearDepth,
@@ -46,13 +46,17 @@ export default class PickLayersPass extends LayersPass {
46
46
  byAlpha: EncodedPickingColors[];
47
47
  } | null = null;
48
48
 
49
- render(props: LayersPassRenderOptions | PickLayersPassRenderOptions) {
49
+ render(props: LayersPassRenderOptions | PickLayersPassRenderOptions): {
50
+ decodePickingColor: PickingColorDecoder | null;
51
+ stats: RenderStats[];
52
+ } {
50
53
  if ('pickingFBO' in props) {
51
54
  // When drawing into an off-screen buffer, use the alpha channel to encode layer index
52
55
  return this._drawPickingBuffer(props);
53
56
  }
54
57
  // When drawing to screen (debug mode), do not use the alpha channel so that result is always visible
55
- return super.render(props);
58
+ const stats = super._render(props);
59
+ return {decodePickingColor: null, stats};
56
60
  }
57
61
 
58
62
  // Private
@@ -70,10 +74,11 @@ export default class PickLayersPass extends LayersPass {
70
74
  effects,
71
75
  pass = 'picking',
72
76
  pickZ,
73
- shaderModuleProps
77
+ shaderModuleProps,
78
+ clearColor
74
79
  }: PickLayersPassRenderOptions): {
75
80
  decodePickingColor: PickingColorDecoder | null;
76
- stats: RenderStats;
81
+ stats: RenderStats[];
77
82
  } {
78
83
  this.pickZ = pickZ;
79
84
  const colorEncoderState = this._resetColorEncoder(pickZ);
@@ -84,7 +89,7 @@ export default class PickLayersPass extends LayersPass {
84
89
  // Note that the callback here is called synchronously.
85
90
  // Set blend mode for picking
86
91
  // always overwrite existing pixel with [r,g,b,layerIndex]
87
- const renderStatus = super.render({
92
+ const renderStatus = super._render({
88
93
  target: pickingFBO,
89
94
  layers,
90
95
  layerFilter,
@@ -96,7 +101,7 @@ export default class PickLayersPass extends LayersPass {
96
101
  pass,
97
102
  isPicking: true,
98
103
  shaderModuleProps,
99
- clearColor: [0, 0, 0, 0],
104
+ clearColor: clearColor ?? [0, 0, 0, 0],
100
105
  colorMask: 0xf,
101
106
  scissorRect
102
107
  });
@@ -145,6 +150,13 @@ export default class PickLayersPass extends LayersPass {
145
150
  pickParameters.blend = true;
146
151
  // TODO: blendColor no longer part of luma.gl API
147
152
  pickParameters.blendColor = encodeColor(this._colorEncoderState, layer, viewport);
153
+ if (operation.includes('terrain') && layer.state?._hasPickingCover) {
154
+ // For terrain+draw layers with a valid cover FBO, the terrain shader outputs the
155
+ // cover FBO pixel which already has correctly encoded alpha from the cover encoder.
156
+ // Use srcFactor 'one' to pass through the cover alpha without double-encoding.
157
+ // Without a cover FBO, keep 'constant' so the layer's own picking colors encode correctly.
158
+ pickParameters.blendAlphaSrcFactor = 'one';
159
+ }
148
160
  } else if (operation.includes('terrain')) {
149
161
  // Pure terrain layers (without 'draw') don't need picking colors
150
162
  pickParameters.blend = false;
@@ -265,6 +265,9 @@ vec2 project_pixel_size_to_clipspace(vec2 pixels) {
265
265
  float project_size_to_pixel(float meters) {
266
266
  return project_size(meters) * project.scale;
267
267
  }
268
+ vec2 project_size_to_pixel(vec2 meters) {
269
+ return project_size(meters) * project.scale;
270
+ }
268
271
  float project_size_to_pixel(float size, int unit) {
269
272
  if (unit == UNIT_METERS) return project_size_to_pixel(size);
270
273
  if (unit == UNIT_COMMON) return size * project.scale;
@@ -0,0 +1,33 @@
1
+ // deck.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ /** Merge two viewstates, except `id`
6
+ * For position arrays such as `target`, only override the components that are defined.
7
+ */
8
+ export function deepMergeViewState<ViewStateT extends Record<string, any>>(
9
+ a: ViewStateT,
10
+ b: ViewStateT
11
+ ): ViewStateT {
12
+ const result = {...a};
13
+ for (const key in b) {
14
+ if (key === 'id') continue;
15
+ if (Array.isArray(result[key]) && Array.isArray(b[key])) {
16
+ result[key] = mergeNumericArray(result[key], b[key]) as any;
17
+ } else {
18
+ result[key] = b[key];
19
+ }
20
+ }
21
+ return result;
22
+ }
23
+
24
+ function mergeNumericArray(target: number[], source: number[]): number[] {
25
+ target = target.slice();
26
+ for (let i = 0; i < source.length; i++) {
27
+ const v = source[i];
28
+ if (Number.isFinite(v)) {
29
+ target[i] = v;
30
+ }
31
+ }
32
+ return target;
33
+ }
@@ -26,6 +26,18 @@ export function getCameraPosition(
26
26
  return [viewMatrixInverse[12], viewMatrixInverse[13], viewMatrixInverse[14]];
27
27
  }
28
28
 
29
+ export function getProjectionParameters(projectionMatrix: Matrix4 | NumericArray): {
30
+ near: number;
31
+ far: number;
32
+ } {
33
+ const m22 = projectionMatrix[10];
34
+ const m23 = projectionMatrix[14];
35
+ return {
36
+ near: m23 / (m22 - 1),
37
+ far: m23 / (m22 + 1)
38
+ };
39
+ }
40
+
29
41
  export type FrustumPlane = {
30
42
  distance: number;
31
43
  normal: Vector3;
@@ -57,7 +57,9 @@ export function createTexture(
57
57
  },
58
58
  mipLevels: device.getMipLevelCount(width, height)
59
59
  });
60
- texture.generateMipmapsWebGL();
60
+ if (device.type === 'webgl') {
61
+ texture.generateMipmapsWebGL();
62
+ }
61
63
 
62
64
  // Track this texture
63
65
  internalTextures[texture.id] = owner;
@@ -13,7 +13,7 @@ import {vec3, vec4} from '@math.gl/core';
13
13
  const DEGREES_TO_RADIANS = Math.PI / 180;
14
14
  const RADIANS_TO_DEGREES = 180 / Math.PI;
15
15
  const EARTH_RADIUS = 6370972;
16
- const GLOBE_RADIUS = 256;
16
+ export const GLOBE_RADIUS = 256;
17
17
 
18
18
  function getDistanceScales() {
19
19
  const unitsPerMeter = GLOBE_RADIUS / EARTH_RADIUS;
@@ -6,6 +6,7 @@ import Viewport from '../viewports/viewport';
6
6
 
7
7
  import {Matrix4} from '@math.gl/core';
8
8
  import {pixelsToWorld, fovyToAltitude} from '@math.gl/web-mercator';
9
+ import {getProjectionParameters} from '../utils/math-utils';
9
10
 
10
11
  const DEGREES_TO_RADIANS = Math.PI / 180;
11
12
 
@@ -150,9 +151,14 @@ export default class OrbitViewport extends Viewport {
150
151
 
151
152
  panByPosition(coords: number[], pixel: number[], startPixel?: number[]): OrbitViewportOptions {
152
153
  const p0 = this.project(coords);
154
+ const {near, far} = getProjectionParameters(this.projectionMatrix);
155
+ const pz = (near * far) / (far - p0[2] * (far - near));
156
+ const centerZ = (near * far) / (far - this.projectedCenter[2] * (far - near));
157
+ const shiftScale = pz / centerZ;
158
+
153
159
  const nextCenter = [
154
- this.width / 2 + p0[0] - pixel[0],
155
- this.height / 2 + p0[1] - pixel[1],
160
+ this.width / 2 + (p0[0] - pixel[0]) * shiftScale,
161
+ this.height / 2 + (p0[1] - pixel[1]) * shiftScale,
156
162
  this.projectedCenter[2]
157
163
  ];
158
164
  return {
@@ -48,6 +48,10 @@ function getProjectionMatrix({
48
48
  });
49
49
  }
50
50
 
51
+ /** independent zoom levels for X and Y axes
52
+ * @deprecated use `zoomX` and `zoomY` instead */
53
+ type Deprecated2DZoom = [number, number];
54
+
51
55
  export type OrthographicViewportOptions = {
52
56
  /** Name of the viewport */
53
57
  id?: string;
@@ -62,8 +66,10 @@ export type OrthographicViewportOptions = {
62
66
  /** The world position at the center of the viewport. Default `[0, 0, 0]`. */
63
67
  target?: [number, number, number] | [number, number];
64
68
  /** The zoom level of the viewport. `zoom: 0` maps one unit distance to one pixel on screen, and increasing `zoom` by `1` scales the same object to twice as large.
65
- * To apply independent zoom levels to the X and Y axes, supply an array `[zoomX, zoomY]`. Default `0`. */
66
- zoom?: number | [number, number];
69
+ * To apply independent zoom levels to the X and Y axes, use `zoomX` and `zoomY`.
70
+ * @default 0
71
+ */
72
+ zoom?: number | Deprecated2DZoom;
67
73
  /** Independent zoom along the X axis. Overrides `zoom`. */
68
74
  zoomX?: number;
69
75
  /** Independent zoom along the Y axis. Overrides `zoom`. */
@@ -285,6 +285,16 @@ export default class WebMercatorViewport extends Viewport {
285
285
  return {longitude, latitude};
286
286
  }
287
287
 
288
+ /**
289
+ * Returns a new longitude and latitude that keeps a 3D world coordinate at a given screen pixel
290
+ * This version handles the z-component (altitude) properly for cameras positioned above ground
291
+ */
292
+ panByPosition3D(coords: number[], pixel: number[]): WebMercatorViewportOptions {
293
+ const targetZ = coords[2] || 0;
294
+ const deltaLngLat = vec2.sub([], coords, this.unproject(pixel, {targetZ}));
295
+ return {longitude: this.longitude + deltaLngLat[0], latitude: this.latitude + deltaLngLat[1]};
296
+ }
297
+
288
298
  getBounds(options: {z?: number} = {}): [number, number, number, number] {
289
299
  // @ts-ignore
290
300
  const corners = getBounds(this, options.z || 0);