feat(core): GlobeView pitch, bearing, and cursor-anchored zoom#10249
feat(core): GlobeView pitch, bearing, and cursor-anchored zoom#10249charlieforward9 wants to merge 7 commits into
Conversation
| coord = vec3.lerp([], coord0, coord1, t); | ||
| const discriminant = lt * lt - dSqr; | ||
|
|
||
| if (discriminant < 0) { |
There was a problem hiding this comment.
This is a neat idea, but I'm not sure it leads to intuitive behavior, see other comment about keeping old behavior
There was a problem hiding this comment.
Removed the new opt-in API surface here. GlobeViewport#unproject now keeps the existing always-return-a-coordinate behavior, while controller code uses isPointOnGlobe when it specifically needs a true globe hit. We can revisit an explicit opt-in API in a follow-up if there is a broader need.
Visual check is clean: local globe app render, drag, wheel zoom, and off-globe drag all behaved as expected.
|
@felixpalmer thank you for the review - I agree with your reinstatement and pan behavior |
c123fa7 to
9d31831
Compare
| startPanOnGlobe?: boolean; | ||
| }; | ||
|
|
||
| function unprojectOnGlobe( |
There was a problem hiding this comment.
maybe create a globe-projection.ts or globe-projection-utils.ts?
class GlobeProjection {
project()
unproject()
getViewMatrix()
..
}or would this be part of GlobeViewport?
There was a problem hiding this comment.
I checked the existing viewport pattern and kept this in GlobeViewport for now. Core viewport-specific projection logic generally lives on the viewport class, while WebMercator delegates to math.gl. Pulling out only globe projection would be a one-off; extracting projection logic feels better as a broader refactor across projection types.
| coord = vec3.lerp([], coord0, coord1, t); | ||
| const discriminant = lt * lt - dSqr; | ||
|
|
||
| if (discriminant < 0) { |
| const GlobeViewState = new GlobeController({} as any).ControllerState; | ||
|
|
||
| // Bearing should be normalized to [-180, 180] | ||
| let viewState = new GlobeViewState({ |
There was a problem hiding this comment.
IIRC, view state is normally a pure object, not a class?
There was a problem hiding this comment.
This test file exercises controller-state classes directly for Map, Globe, Orbit, Orthographic, and FirstPerson state constraints. The public view state is still a plain object; this is testing the internal ControllerState normalization path consistently with the rest of the file.
Add camera tilt (pitch) and rotation (bearing) to GlobeView, matching the Google Maps/Earth interaction model. Uses a lookAt-based view matrix that composes pitch and bearing rotations while keeping the target lng/lat at screen center. Scroll-wheel zoom now targets the mouse cursor position instead of screen center (matching MapView). Pan reuses MapState's grabbed-lng/lat logic so the point under the cursor stays under the cursor through the drag — previously GlobeState's delta-pan produced wrong on-screen speed and yanked the center at zoom > 12 where WebMercatorViewport takes over rendering. Unproject for rays that miss the globe surface returns a plausible fallback rather than null. Changes: - GlobeViewport: lookAt view matrix with pitch/bearing, cursor-anchored zoom, ray/sphere miss fallback - GlobeController: dragRotate/touchRotate, pitch/bearing constraints, inherits pan from MapState - GlobeView: pitch/bearing/minPitch/maxPitch view state props - Tests for pitch/bearing transitions and viewport math
484ba62 to
225a52c
Compare
225a52c to
30297f2
Compare
Summary
Brings
GlobeViewto feature parity withMapViewfor camera interaction. Three related changes:GlobeViewnow acceptspitch,bearing,minPitch,maxPitchview-state props.GlobeViewportcomposes them into alookAt-based view matrix that keeps the target lng/lat fixed at screen center through tilt and rotation, matching the Google Maps/Earth interaction model.GlobeControllerwires updragRotateandtouchRotateand enforces the pitch constraints.MapView. When the cursor is outside the globe, zoom falls back to center zoom instead of anchoring to an inferred ray-miss point.Includes a ray/sphere-miss fallback in
GlobeViewport#unprojectfor callers that need a surface point, while controller pan/zoom anchoring only uses true globe intersections.Why
Pitch + bearing + zoom-toward-cursor are the camera affordances users already expect from MapView. Without them, GlobeView-based apps can't offer the same 3D tilt/orbit UX that MapView has supported for years, which makes it hard to build a projection-toggling app where the camera feels the same on both projections.
Test plan
test/modules/core/viewports/globe-viewport.spec.tsfor pitch/bearing viewport math and globe hit detectiontest/modules/core/controllers/view-states.spec.tsfor pitch/bearing transitions and off-globe delta-spin pan behaviortest/apps/globe/app.jsupdated with sliders to exercise the new view-state props interactivelyCompanion PRs
This PR is standalone. Two follow-up PRs build on it to bring terrain rendering to GlobeView: