Skip to content

Conversation

Beilinson
Copy link
Contributor

@Beilinson Beilinson commented Oct 10, 2025

Description

When a 3D tileset is added to the map, height clamping of the camera to the scene takes about 25% of total frame time, mostly due to Model.pick. This PR proposes a new flag on the ScreenSpaceCameraController, that will allows users to continue clamping entities to the 3D tileset without also sampling the camera height off the 3D tileset every frame.

This is useful when 3D tiles are used in conjuction and closely map to a regular TerrainProvider, so entities can use HeightReference.CLAMP_TO_GROUND and clamp to both terrain and 3D tiles, while the camera can choose to clamp only to the terrain using HeightReference.CLAMP_TO_TERRAIN.

This is important, as unlike entities which in the future we can improve their clamping performance, every frame that the camera moves it must sample the 3D tiles as well. The current API is insufficient in providing users with a solution to this problem, as they are either forced to disable collision entirely (including against terrain) on the camera, or disable clamping to 3D tilesets entirely (including for entities).

Example

import * as Cesium from "cesium";

const widget = new Cesium.CesiumWidget("cesiumContainer", {
  terrain: Cesium.Terrain.fromWorldTerrain(),
});
const scene = widget.scene;

scene.camera.setView({
  destination: new Cesium.Cartesian3(
    1216411.0748779264,
    -4736313.10747583,
    4081359.5125561724,
  ),
  orientation: new Cesium.HeadingPitchRoll(
    4.239925103568368,
    -0.4911293834802475,
    6.279849292088564,
  ),
  endTransform: Cesium.Matrix4.IDENTITY,
});

const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(40866);
tileset.enableCollision = true;
scene.primitives.add(tileset);

// This billboard will clamp to the 3D tileset
widget.entities.add({
  billboard: {
    image: "../images/facility.gif",
    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  },
  position: new Cesium.Cartesian3(
    1216390.063324395,
    -4736314.814479433,
    4081341.9787972216,
  ),
});

const screenSpaceCameraController = widget.scene.screenSpaceCameraController;
// Previously, we could only disable collision detection entirely
screenSpaceCameraController.enableCollisionDetection = false;
// Now, we can pick and choose:
screenSpaceCameraController.collisionHeightReference = HeightReference.CLAMP_TO_TERRAIN; // The camera will only collide against terrain

Author checklist

  • I have submitted a Contributor License Agreement
  • I have added my name to CONTRIBUTORS.md
  • I have updated CHANGES.md with a short summary of my change
  • I have added or updated unit tests to ensure consistent code coverage
  • I have updated the inline documentation, and included code examples where relevant
  • I have performed a self-review of my code

@github-actions
Copy link

Thank you for the pull request, @Beilinson!

✅ We can confirm we have a CLA on file for you.

@Beilinson Beilinson mentioned this pull request Oct 10, 2025
6 tasks
@javagl
Copy link
Contributor

javagl commented Oct 16, 2025

This change is a follow-up from the part at #12968 (comment)

It looks like the repeated 'getHeight' calls are actually causing a performance issue that was reported internally.

During the investigation of the latter, I noticed that the callback wrapper at

const callbackWrapper = (clampedCartographic) => {
is _always called with undefined as it parameter. This means that all the defined checks there are useless, and it is always calling this.getHeight. I assume that the intention of these defined checks was to avoid re-computing the height multiple times, and that the undefined parameter causes the same height to be computed again and again (and this is very costly).

I have not yet analyzed this in detail. That updateHeight function and the web of callbacks that are created and passed around there is... *sigh* ... I'll need more time to reverse engineer that. I also have not yet cross-checked this with the changes in this PR. But the point is: Maybe the low performance (that is addressed here) comes (mainly) from that undefined/callback thingy...?

@Beilinson
Copy link
Contributor Author

Beilinson commented Oct 16, 2025

Hey @javagl,
I'm not only concerned here about repeated calls while the camera is static, but even the calls when the camera moves.

The current available apis do not provide support for the following setup:

  1. Billboards and such clamp to 3d tiles
  2. The camera only collides against terrain (to avoid expensive collision tests against 3D tiles every time the camera moves)

This Pr introduces such an api by specifying a collisionHeightReference property, which explicitly allows the developer to choose against which content the camera does collision checks.

The current api allows us only to either entirely disable collision (including against terrain), or entirely disable collision against 3D tilesets, but that includes height clamping for billboards and such.

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.

2 participants