Skip to content

feat(extensions): TerrainExtension GlobeView support#10251

Open
charlieforward9 wants to merge 16 commits into
masterfrom
cr/feat/terrain-extension-globe
Open

feat(extensions): TerrainExtension GlobeView support#10251
charlieforward9 wants to merge 16 commits into
masterfrom
cr/feat/terrain-extension-globe

Conversation

@charlieforward9
Copy link
Copy Markdown
Collaborator

@charlieforward9 charlieforward9 commented Apr 19, 2026

Summary

Makes _TerrainExtension draped rendering work on GlobeView as well as MapView.

Why

Terrain cover and height-map textures are 2D render targets. The previous implementation derived their bounds and UVs from the live viewport common space; on GlobeView that space is globe cartesian, which is not a stable 2D sampling basis for those textures.

Change

  • Uses an absolute WebMercator texture basis for terrain cover and height-map FBOs in both map and globe modes.
  • Adds shared projection helpers for Mercator reference bounds.
  • Updates terrain cover and height-map bounds to avoid mixing globe-cartesian viewport bounds with Mercator layer bounds.
  • Converts globe terrain positions directly to Mercator world coordinates in the shader for terrain sampling.
  • Includes only the minimal TerrainLayer/tile traversal preview fixes needed to validate the extension on GlobeView.
  • Adds a MapView/GlobeView toggle to the terrain-extension website example.

Validation

  • MapView with TerrainExtension and TerrainLayer source remains unchanged.
  • GlobeView with TerrainExtension and TerrainLayer source renders draped layers and terrain preview correctly.
  • Projection toggles no longer reuse stale terrain tiles incorrectly.
  • Focused checks cover test/modules/extensions/terrain, test/modules/geo-layers/terrain-layer.spec.ts, and test/modules/geo-layers/tileset-2d/utils.spec.ts.

Merge Notes

Approved by Felix after follow-up cleanup. #10250 remains the broader TerrainLayer globe grid/tessellator work; this PR intentionally keeps only the minimal preview plumbing needed for the extension change.

Make TerrainExtension's draped rendering (height-map and terrain-cover
FBOs) work on GlobeView as well as MapView. The existing implementation
packed layer bounds into the live viewport's common space, which on
GlobeView is sphere cartesian — incomparable with screen-space bounds,
and invalid as a UV basis for the draped sampler.

All bounds are now expressed in ABSOLUTE Web Mercator common space,
regardless of the active projection. The FBO is rendered via a
WebMercatorViewport in both cases, so the cover/height-map texture is
projection-invariant and a projection toggle no longer requires
re-rendering.

Changes:
- projection-utils: new `MERCATOR_REFERENCE_VIEWPORT` plus helpers
  `lngLatToMercatorCommon` and `getMercatorReferenceViewport` so other
  modules can compute bounds in the shared absolute-Mercator basis.
- terrain-cover, height-map-builder: project layer bounds through the
  Mercator reference viewport; on globe, skip the viewport-bounds
  intersection in `getRenderBounds` (which would yield sphere cartesian
  coords) and use full layer bounds.
- shader-module: compute `terrainMercPos` per-fragment by unprojecting
  globe cartesian back to lng/lat and forward-projecting through
  project_mercator_, so USE_HEIGHT_MAP / USE_COVER samples against the
  absolute-Mercator bounds uniform on any projection. The project
  module's helpers are VS-only, so the value is passed via varying;
  terrain meshes are fine enough that interpolation error is negligible.
  Bounds uniform is packed as absolute Mercator (no commonOrigin
  subtract), since the shader computes absolute xy itself.

Example updated with a GlobeView toggle so the terrain-extension demo
exercises both projections (requires TerrainLayer's grid tesselator from
the companion geo-layers PR.
EOF
)
if (terrain.mode == TERRAIN_MODE_USE_HEIGHT_MAP) {
vec3 anchor = geometry.worldPosition;
anchor.z = 0.0;
vec3 anchorCommon = project_position(anchor);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

OK, so if I understand correctly you're basically forcing project_position() to internally take the following branch:

if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR) {

A natural question that comes to mind is that if we are going to hardcode a projection, then why Mercator? Sure it works well for MapView but it doesn't fit the other views.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Perhaps related, I have been feeling that if we extend layers to take a "deformation" when generating geometry, such as a deformation UV grid for BitmapLayer than we could generalize that to support any projection.

Comment thread modules/extensions/src/terrain/shader-module.ts Outdated
// Convert bounds to the common space, as [minX, minY, width, height]
// Pack bounds as [minX, minY, width, height]
bounds: bounds
? [
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I worry this will introduce precision issues

const targetLayer = this.targetLayer;
let shouldRedraw = false;

// Bounds are computed in ABSOLUTE Mercator common space — NOT the live
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we try to use a cartesian space? Seems like a closer match to the spherical coordinates

// Recalculate cached bounds
// Recalculate cached bounds.
// Use a Mercator reference viewport so layer bounds live in ABSOLUTE
// Mercator common space — same rationale as terrain-cover.ts. On
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we add mercator common space as a supported coordinateSystem?
Something like first class support for EPSG: 3857?
Or that is not helpful?

if (terrain.mode == TERRAIN_MODE_USE_HEIGHT_MAP) {
vec3 anchor = geometry.worldPosition;
anchor.z = 0.0;
vec3 anchorCommon = project_position(anchor);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Perhaps related, I have been feeling that if we extend layers to take a "deformation" when generating geometry, such as a deformation UV grid for BitmapLayer than we could generalize that to support any projection.

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.

@charlieforward9 I've done a pass a tidied a few things:

  • Neater terrain_globe_to_mercator equations
  • More succinct comments
  • Revert removal of commonOrigin offset in bounds. This way the existing behavior isn't changed, only globeview support is added

Could you check that I haven't broken anything? Otherwise looks good to merge!

Comment thread examples/website/terrain-extension/app.tsx Outdated
@coveralls
Copy link
Copy Markdown

coveralls commented May 12, 2026

Coverage Status

coverage: 83.402% (+0.01%) from 83.39% — cr/feat/terrain-extension-globe into master

@charlieforward9
Copy link
Copy Markdown
Collaborator Author

Confirmed locally: the TerrainExtension example loads tiles with the Mapbox token, and the View selector is now in the example controls. CI is green.

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.

Tested with new globe controls, works nicely

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

4 participants