feat(extensions): TerrainExtension GlobeView support#10251
feat(extensions): TerrainExtension GlobeView support#10251charlieforward9 wants to merge 16 commits into
Conversation
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| // Convert bounds to the common space, as [minX, minY, width, height] | ||
| // Pack bounds as [minX, minY, width, height] | ||
| bounds: bounds | ||
| ? [ |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
felixpalmer
left a comment
There was a problem hiding this comment.
@charlieforward9 I've done a pass a tidied a few things:
- Neater
terrain_globe_to_mercatorequations - More succinct comments
- Revert removal of
commonOriginoffset 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!
|
Confirmed locally: the TerrainExtension example loads tiles with the Mapbox token, and the View selector is now in the example controls. CI is green. |
felixpalmer
left a comment
There was a problem hiding this comment.
Tested with new globe controls, works nicely
…xtension-globe # Conflicts: # modules/geo-layers/src/terrain-layer/terrain-layer.ts
Summary
Makes
_TerrainExtensiondraped rendering work onGlobeViewas well asMapView.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
GlobeViewthat space is globe cartesian, which is not a stable 2D sampling basis for those textures.Change
Validation
TerrainExtensionandTerrainLayersource remains unchanged.TerrainExtensionandTerrainLayersource renders draped layers and terrain preview correctly.test/modules/extensions/terrain,test/modules/geo-layers/terrain-layer.spec.ts, andtest/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.