fix(camera): keep the zoom ceiling fixed while tracking a target

While the camera tracks an entity or looks at a point, Cesium measures
zoom distance from the target, not the Earth. Raising the ceiling to the
camera's altitude then lifted it with every outward step, so a tracked
camera could zoom out without limit. The guard now follows the altitude
only for an untracked 3D camera and otherwise keeps its 40,000 km
ceiling.
This commit is contained in:
Sameh Khamis committed 2026-11-03 09:29:22 -08:00
1 parent 9e3786716d
commit cc3d61a960
2 files changed
+27 -1

No files matched your search

+6 -1
View File
@@ -111,7 +111,12 @@ export function installZoomOutGuard(
const followCeiling = () => { const followCeiling = () => {
if (!ownsCeiling || controller.maximumZoomDistance !== maximumZoomDistance) if (!ownsCeiling || controller.maximumZoomDistance !== maximumZoomDistance)
return; return;
const height = camera.positionCartographic?.height; // While tracking, Cesium measures zoom from the target, not the Earth, so
// the camera's altitude would lift the ceiling with every outward step.
const untracked =
scene.mode === Cesium.SceneMode.SCENE3D &&
Cesium.Matrix4.equals(camera.transform, Cesium.Matrix4.IDENTITY);
const height = untracked ? camera.positionCartographic?.height : undefined;
maximumZoomDistance = maximumZoomDistance =
height > MAXIMUM_ZOOM_DISTANCE_M ? height : MAXIMUM_ZOOM_DISTANCE_M; height > MAXIMUM_ZOOM_DISTANCE_M ? height : MAXIMUM_ZOOM_DISTANCE_M;
controller.maximumZoomDistance = maximumZoomDistance; controller.maximumZoomDistance = maximumZoomDistance;
+21
View File
@@ -741,3 +741,24 @@ test('a share-link view restored above the ceiling is honoured by later frames',
s.dispose(); s.dispose();
} }
}); });
test('a tracked camera cannot climb past the ceiling from its target', () => {
const s = realControllerScene(1_000_000);
try {
s.frame();
// In look-at mode Cesium measures zoom from the target, not the Earth.
s.camera.lookAt(
Cesium.Cartesian3.fromDegrees(0, 0, 35_786_000),
new Cesium.HeadingPitchRange(0, -0.5, 5_000_000),
);
for (let i = 0; i < 20; i++) {
s.wheel(100);
s.frame();
}
const range = Cesium.Cartesian3.magnitude(s.camera.position);
assert.ok(range <= MAXIMUM_ZOOM_DISTANCE_M + 1_000, `${range} m`);
assert.equal(s.controller.maximumZoomDistance, MAXIMUM_ZOOM_DISTANCE_M);
} finally {
s.dispose();
}
});