Clipping Planes for multiple Tilesets

Is it possible to create a clipping plane (as in this sandcastle), which will affect multiple tilesets simultaneously?

We have dynamically added several Tilesets to the viewer by the user, which for example represent a layer sequence. Each layer is a single tileset. Unfortunately, ClippingPlanes can only be assigned to one Tileset and have no effect on the others. Is there a way to do this in a different way?

What I would do is clone the original clipping plane list and re-assign it to all tilesets as needed. However, since clipping planes are defined relative to the local coordinates of each object they’re attached to, you’ll need to apply an additional transform to reverse that in order to define a clipping plane in a global coordinate system that affects all tilesets in the same way (something like this: 3d-tiles & Clipping Planes).

We do have an open issue here to consider removing the ownership from clipping planes so you can just define them globally as you describe: https://github.com/CesiumGS/cesium/issues/8554

Thanks, it works quite well so far (see attached screencast), except for the recalculation of the matrix.

Here I use the following code, but only the state shown above is reached. Do I perform a wrong calculation?

 let clippingPlanes = new ClippingPlaneCollection({
   planes: [new ClippingPlane(new Cartesian3(0.0, 25.0, 0.0), 0.0)],
   edgeWidth: 1.0,
   unionClippingRegions: true,
   modelMatrix: Matrix4.inverse(tileset._initialClippingPlanesOriginMatrix,
     new Matrix4())
   });

 tileset.clippingPlanes = clippingPlanes;

The clippingPlane will be added to every new Tileset loaded into the viewer.

So in your setup, the red surface and the white surface are two separate 3D Tilesets, right? And they almost have the right outcome, but they are clipped with a slight offset from each other?

I think this slight offset is coming from this line:

modelMatrix: Matrix4.inverse(tileset._initialClippingPlanesOriginMatrix,
     new Matrix4())
   });

It almost looks like they have a different origin. Does each clipping plane use the respective tileset’s _initialClippingPlanesOriginMatrix ? I believe that should be the right configuration. If so, what happens if they share the same model matrix?

Yes, every time a tileset is added to the viewer, the code above is executed, including the _initialClippingPlanesOriginMatrix. When only one tileset is added, the position of the clippingPlane and the entity for moving is identical. If a second tileset is added during the current session, the visible entity is moved to the clipping plane of this new tileset but the clipping plane of the first tileset is not moved. Therefore this offset is visible here.

You mean, what happens if I read out the modelMatrix of the first tileset and then add it to the new tileset instead of the _initialClippingPlanesOriginMatrix?

@omar using the modelMatrix from existent Tilesets shows no changes in the behaviour. I think it just returns a default matrix:
Matrix4 {0: 1, 1: 0, 2: 0, 3: 0, 4: 0, 5: 1, 6: 0, 7: 0, 8: 0, 9: 0, 10: 1, 11: 0, 12: 0, 13: 0, 14: 0, 15: 1}

This was simply determined in the following way:

viewer.scene.primitives._primitives.forEach(primitive => {
  if (primitive instanceof Cesium3DTileset) {
      modelMatrix = primitive._clippingPlanes.modelMatrix;
  }
});

Previously each Tileset used the modelMatrix assigned to it by

modelMatrix: Matrix4.inverse(tileset._initialClippingPlanesOriginMatrix,
     new Matrix4())
   });

But via both ways I have the above offset between the tilesets that are included.

Hi @BaoTran , maybe you can also help with the following problem:
I have provided here a Sandcastle, in which the problem becomes clear.

After setting the camera to the appropriate position (1), a small tileset (blue) is added (2). Here the red plane entity matches the movable clipping plane assigned to the tileset. If another, larger tileset (green) is added now (3), an unexpected offset between the clipping plane, the plane entity and the tilesets is visible.

Actually the clipping planes of all tilesets should match the red plane entity. I also tried the tileset._initialClippingPlanesOriginMatrix (line 67), which is suggested by @omar, without success.

Hi @mholthausen, I think the offset happens because the two clipping planes are placed at two different positions. In the sandcastle example, they are placed at the bounding sphere’s centers of the two tilesets, but those centers don’t match up to align the two planes correctly.

I worked around that by using one of the center of the two tilesets as the global clipping planes position (or the position that is shared by all the clipping planes) and place all the clipping planes at that position. This should match the plane correctly. This is the modified sandcastle example (line 67-69 and 173). Please let me know if it works for your case

1 Like

Thank you @BaoTran ! It looks like it works now!

I have still problems with the correct generation of the dimension for planes on a ClippingPlane.
This sandcastle shows the problem with very deep layers as Tilesets that can be added dynamically (the fact that the clipping planes do not cut the tilesets at the moment when they are moved can be ignored here). It’s about the dimension of the plane. All three Tilesets have the same position except for the z value.

At z-25km the dimension is still okay. But as soon as the layer z-500km is added, the dimension will increase, but not to the expected depth. The same happens when the third layer z-1000km is added. I would have expected the plane entity to capture all three added layers afterwards. But here it ends already far above the second layer.

Changing the matrix for the dimension, e.g. in

dimensions: new Cesium.Cartesian2(
boundingSphere.radius * 2,
boundingSphere.radius * 2.5
),

increases the dimension of the plane, but the deepest layer is still not covered.

Is there a possibility to calculate the dimension for the plane more exactly based on the added Tilesets, because theoretically even deeper tilesets could be added?

Hi @mholthausen, I think the problem is that in my last sandcastle, I only assign the global clippingPlanePosition to the center of the bounding sphere of the first tileset. So when more tilesets are added, the clippingPlanePosition will be off center of the bounding sphere of the entire added tilesets. This is the sandcastle example of the bug. If you add more tilesets, you can see the clipping plane is off center of the bounding sphere.

One possible solution is to update clippingPlanePosition to be at the center of the bounding sphere of all the added tilesets, and adjust the clipping plane dimension to be equal to diameter of the sphere. This is the sandcastle of the fix. The clippingPlanePosition is updated at line 167. Please let me know if it works for your case

Yes, thanks. This has fixed the problem.

I am sorry to activate an old topic, but is there a reason behind the decision to prevent assignment of a single clippingPlaneCollection to multiple tilesets?
I believe that “Clipping Plane Collection should only be assigned to one object” error isn’t rised by the pre-compiled Cesium.js which comes with the node package and assigning the same clippingPlanesCollection to multiple tilesets seems to work just fine.
I’ve only encountered this error after changing the way I import cesium library into the application (switching from precompiled to cesium source files).
You still can manually set clippingPlaneCollection._owner to undefined before assigning it to a new tileset and it seems to work just fine, all tilesets are clipped with a single plane.

@ygb Currently this isn’t possible because of the way that a clipping plane is attached to a tileset’s transform in the code. We have an issue on GitHub tracking this (Omar posted this already above).