Description
When an OrbitView with dragMode: 'pan' is used together with TerrainExtension, panning works correctly when clicking on the terrain layer directly, but causes runaway (exponentially growing) camera movement when the click lands on a layer that uses TerrainExtension with terrainDrawMode: 'offset', e.g. an EditableGeoJsonLayer over a point-cloud layer.
Root cause
OrbitController._unproject3D calls deck.pickObject({ unproject3D: true }) to determine the 3D world position that will be used as the pan pivot (startPanPosition in OrbitState). The depth for the unproject3D coordinate is resolved in DeckPicker._getDepthLayers:
// packages/core/src/lib/deck-picker.ts
_getDepthLayers(pickInfo, pickableLayers, unproject3D) {
if (!unproject3D || !this.depthFBO) return [];
const { pickedLayer } = pickInfo;
const isDraped = pickedLayer?.state?.terrainDrawMode === 'drape';
if (pickedLayer && !isDraped) {
return [pickedLayer]; // <-- re-renders pickedLayer for depth
}
// For draped layers or no pick: use terrain layers
return pickableLayers.filter(l => l.props.operation.includes('terrain'));
}
When the picked layer uses terrainDrawMode: 'offset', isDraped is false, so [pickedLayer] is returned. The picker re-renders that layer in a dedicated depth pass to extract the Z value. However, in this depth pass the terrain heightmap texture (needed to apply the terrain offset in the vertex shader) is not reliably available, so the geometry is evaluated at its raw data Z (= 0). The resulting startPanPosition is [x, y, 0] instead of [x, y, terrain_z].
Workaround
Subclass OrbitController to ignore non-terrain picks. This fixes it in my setup.
class TerrainAwareOrbitController extends OrbitController {
protected _unproject3D = (pos: number[]): number[] | null => {
if (!this.pickPosition) return null;
const { x, y } = this.props as { x: number; y: number };
const pickResult = this.pickPosition(x + pos[0], y + pos[1]);
// Only accept picks from layers explicitly marked pickable:'3d'
// (the terrain layer); fall back to viewport.unproject at target depth.
if (
pickResult?.coordinate &&
(pickResult as any).layer?.props.pickable === '3d'
) {
return pickResult.coordinate;
}
return null;
};
}
class TerrainOrbitView extends OrbitView {
get ControllerType() { return TerrainAwareOrbitController; }
}
Flavors
Expected Behavior
Panning should feel the same regardless of which pickable layer the pointer lands on. The 3D pan pivot should always resolve to the terrain surface.
Steps to Reproduce
OrbitView with controller: { dragMode: 'pan' }
PointCloudLayer with operation: 'terrain+draw' and pickable: '3d' — defines the terrain surface
- A second layer (e.g.
EditableGeoJsonLayer) with extensions: [new TerrainExtension()] and terrainDrawMode: 'offset' rendered on top
Environment
- Framework version: 9.3.0
- Browser: Chrome
- OS: Mac
Logs
No response
Description
When an
OrbitViewwithdragMode: 'pan'is used together withTerrainExtension, panning works correctly when clicking on the terrain layer directly, but causes runaway (exponentially growing) camera movement when the click lands on a layer that usesTerrainExtensionwithterrainDrawMode: 'offset', e.g. anEditableGeoJsonLayerover a point-cloud layer.Root cause
OrbitController._unproject3Dcallsdeck.pickObject({ unproject3D: true })to determine the 3D world position that will be used as the pan pivot (startPanPositioninOrbitState). The depth for theunproject3Dcoordinate is resolved inDeckPicker._getDepthLayers:When the picked layer uses
terrainDrawMode: 'offset',isDrapedisfalse, so[pickedLayer]is returned. The picker re-renders that layer in a dedicated depth pass to extract the Z value. However, in this depth pass the terrain heightmap texture (needed to apply the terrain offset in the vertex shader) is not reliably available, so the geometry is evaluated at its raw data Z (= 0). The resultingstartPanPositionis[x, y, 0]instead of[x, y, terrain_z].Workaround
Subclass
OrbitControllerto ignore non-terrain picks. This fixes it in my setup.Flavors
Expected Behavior
Panning should feel the same regardless of which pickable layer the pointer lands on. The 3D pan pivot should always resolve to the terrain surface.
Steps to Reproduce
OrbitViewwithcontroller: { dragMode: 'pan' }PointCloudLayerwithoperation: 'terrain+draw'andpickable: '3d'— defines the terrain surfaceEditableGeoJsonLayer) withextensions: [new TerrainExtension()]andterrainDrawMode: 'offset'rendered on topEnvironment
Logs
No response