Skip to content

fix(geo-layers): TerrainLayer GlobeView support#10250

Merged
felixpalmer merged 16 commits into
masterfrom
cr/feat/terrain-layer-globe-grid
May 15, 2026
Merged

fix(geo-layers): TerrainLayer GlobeView support#10250
felixpalmer merged 16 commits into
masterfrom
cr/feat/terrain-layer-globe-grid

Conversation

@charlieforward9
Copy link
Copy Markdown
Collaborator

@charlieforward9 charlieforward9 commented Apr 19, 2026

Summary

Makes tiled TerrainLayer meshes render correctly in GlobeView by using COORDINATE_SYSTEM.LNGLAT when the active viewport is globe-backed.

This keeps the existing @loaders.gl/terrain mesh generation path intact. The only layer behavior change is coordinate-system selection at render time plus a projection-mode update trigger so cached terrain tiles are rebuilt when switching between MapView and GlobeView.

Also defaults the existing terrain website example to GlobeView, keeps the current pitch/bearing values in place, increases the default zoom from 11.5 to 12.5, and enables Deck-level back-face culling with parameters={{cull: true}}.

Why

GlobeViewport.projectFlat is identity, so terrain tile bounds are already in lng/lat degrees under GlobeView. Rendering those meshes as CARTESIAN puts them in the wrong coordinate frame. LNGLAT matches the generated vertex positions and makes the terrain visible on the globe.

Test plan

  • yarn vitest run --project headless test/modules/geo-layers/terrain-layer.spec.ts test/modules/geo-layers/terrain-layer-loading.spec.ts
  • Existing examples/website/terrain renders by default in GlobeView at zoom 12.5

Companion PRs

  • feat(core): GlobeView pitch, bearing, and cursor-anchored zoom #10249 improves GlobeView camera/controller behavior, including pitch/bearing support in globe. Until that lands, the example defaults to GlobeView for coverage but the interaction UX is not expected to feel pleasant. This PR is still useful to land independently because it fixes the TerrainLayer render coordinate system.

Add a grid tesselator option to TerrainLayer and route the rendered mesh
through the correct coordinate system for each projection. Three pieces:

1. `tesselator: 'grid'` emits a fixed-resolution lng/lat/elev mesh in-process
   (no worker), keeping the same cached mesh valid on both MapView and
   GlobeView. The default `'auto'` path continues to use Martini/Delatin via
   `@loaders.gl/terrain`. Grid rows are uniform in Mercator-y so they line
   up with heightmap rows at any latitude (no polar sampling warp).

   Two quality touches on the grid path:
   - Elevation samples are clamped to [-500, 9000]m. Terrain-RGB encodes into
     24 bits across three channels, so one corrupt pixel decodes to millions
     of meters and renders as a specular star.
   - Edge-vertex skirts are clamped to ≤1% of the in-tile grid step so the
     seam slope stays imperceptible at any zoom (default 8m skirt over a
     ~0.6m grid step at z=21 was a visible cliff).

2. renderSubLayers picks the coordinate system per path: LNGLAT for the grid
   tesselator (vertices are lng/lat) and on GlobeView for the legacy path
   (GlobeViewport.projectFlat is identity so bounds land in lng/lat degrees),
   CARTESIAN on MapView for the legacy path (bounds in Mercator world units).
   Fixes the MapView→GlobeView case where legacy cartesian meshes landed
   inside the sphere and were invisible.

3. Default material changes from `true` (PBR with specular) to a matte
   configuration. Specular on terrain meshes catches as star-shaped glints
   on skirt edges and elevation discontinuities — a matte surface reads as
   a fluid landscape.

Also adds horizon culling in the tile-2d traversal used by the internal
TileLayer. The frustum test accepts tiles on the far hemisphere of the
globe; without a horizon check, low-zoom traversal fans out across the
entire globe. A point on the sphere is visible from camera C iff
dot(P, C) > |P|² — apply to the tile-vs-camera closest point (with lng
wrap so the clamp is angular, not Euclidean).

Docs updated for the new `tesselator` and `gridSize` props.
Copy link
Copy Markdown
Collaborator

@felixpalmer felixpalmer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is doing 2 separate things: fixing TerrainLayer in GlobeView and adding a tesselator. Could you just restrict it to the first fix? I think we should consider tesselation in general (see my other comment)

Comment thread modules/geo-layers/src/terrain-layer/grid-terrain-mesh.ts Outdated
Comment thread modules/geo-layers/src/terrain-layer/terrain-layer.ts Outdated
@charlieforward9 charlieforward9 changed the title feat(geo-layers): TerrainLayer GlobeView + grid tesselator fix(geo-layers): TerrainLayer GlobeView support Apr 23, 2026

This comment has been minimized.

@charlieforward9 charlieforward9 force-pushed the cr/feat/terrain-layer-globe-grid branch from 50bee70 to 41ae4a2 Compare April 23, 2026 05:08
Copy link
Copy Markdown
Collaborator Author

Updated the example to default to GlobeView. One caveat for review: the UX is not expected to feel pleasant yet because current GlobeView interaction does not support pitch/bearing properly. #10249 should address that controller/camera behavior; this PR is still worth landing independently for the TerrainLayer render-coordinate fix.

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 28, 2026

Coverage Status

coverage: 83.438% (+0.02%) from 83.416% — cr/feat/terrain-layer-globe-grid into master

@felixpalmer felixpalmer merged commit 2ff156c into master May 15, 2026
3 checks passed
@felixpalmer felixpalmer deleted the cr/feat/terrain-layer-globe-grid branch May 15, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] TerrainLayer zooming, TerrainExtension draping [Bug] Cannot dynamically alternate between terrain ON/OFF

3 participants