3d-tiles & Clipping Planes

1. A concise explanation of the problem you’re experiencing.

Previously we have been trimming our 3D-tiles in pre-production, but this is not always practical. We are attempting to implement cesium clipping planes to allow us to define areas to hide but have not been having much luck. Our ultimate aim is to draw a polygon, convert each straight edge into a vertical clipping plane and exclude from display that texture that falls outside the planes. Currently we are just trying to get clipping planes of any definition to work our 3d-tile dataset and regardless of what we try there is zero effect on the 3d-tiles. Below is an example… any help would be much appreciated

2. A minimal code example. If you’ve found a bug, this helps us reproduce and repair it.

tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
url : tile_url,
clippingPlanes : new Cesium.ClippingPlaneCollection({
planes : [
new Cesium.Plane(new Cesium.Cartesian3(1.0, 0.0, 0.0), 0),
new Cesium.Plane(new Cesium.Cartesian3(-1.0, 0.0, 0.0), 0),
new Cesium.Plane(new Cesium.Cartesian3(0.0, 1.0, 0.0), 0),
new Cesium.Plane(new Cesium.Cartesian3(0.0, -1.0, 0.0), 0),
new Cesium.Plane(new Cesium.Cartesian3(0.0, 0.0, -1.0), 0)
],
unionClippingRegions : true
})
}));

tileset.debugShowBoundingVolume = viewModel.debugBoundingVolumesEnabled;
return tileset.readyPromise.then(function() {
let boundingSphere = tileset.boundingSphere;
let radius = boundingSphere.radius;

// viewer.zoomTo(tileset, new Cesium.HeadingPitchRange(0.5, -0.2, radius * 4.0));
tileset.clippingPlanes.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(boundingSphere.center);
tileset.readyPromise.then(function() {
    let boundingSphere = tileset.boundingSphere;
    viewer.camera.viewBoundingSphere(boundingSphere, new Cesium.HeadingPitchRange(0.0, -0.5, boundingSphere.radius * 2));
    viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
});

for (let i = 0; i < clippingPlanes.length; ++i) {
    let plane = clippingPlanes[i];
    let planeEntity = viewer.entities.add({
        position : boundingSphere.center,
        plane : {
            dimensions : new Cesium.Cartesian2(radius  2.5, radius  2.5),
            material : Cesium.Color.WHITE.withAlpha(0.1),
            plane : new Cesium.CallbackProperty(createPlaneUpdateFunction(plane, tileset.modelMatrix), false),
            outline : true,
            outlineColor : Cesium.Color.WHITE
        }
    });

    planeEntities.push(planeEntity);
}
return tileset;

}).otherwise(function(error) {
console.log(error);
});

``

3. Context. Why do you need to do this? We might know a better way to accomplish your goal.

4. The Cesium version you’re using, your operating system and browser.

1.55

One thing to be aware of is that the clipping planes are relative to the tileset’s root transform. So if you’re drawing a polygon on the surface of the Earth, and you want those edges to act as clipping planes, the easiest thing might be to reverse the “world-to-local” transformation for the clipping planes. This thread has an example on this:

https://groups.google.com/d/msg/cesium-dev/rRNeE6vQb7g/dOLhS6LXAwAJ

For your case, can you try changing the Cesium3DTileset constructor too:

tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({ url : tile_url, clippingPlanes : new Cesium.ClippingPlaneCollection({ planes : [ new Cesium.Plane(new Cesium.Cartesian3(1.0, 0.0, 0.0), 0) ], // unionClippingRegions : true }) }));

``

This should clip half the tileset. If it doesn’t, can you show your tileset.json, or at least the root node in it? The clipping planes use the root node’s transform to figure out the relative position for where to clip.

Hi Omar,

Fantastic advice as usual, thanks very much… We have made some progress, however I think we are getting something wrong with the inverse transformation? We can successfully trim the globe based on our polygon edges as seen above, but the tileset disappears entirely with the following code. Below the js snippet is an example tileset.json if it is of any help?

Thanks in advance,
Josh

let pts = positions;

let pointsLength = pts.length - 1;

let clippingPlanes = ;

for (let i = 0; i < pointsLength; ++i) {

let nextIndex = (i + 1) % pointsLength;

let midpoint = Cesium.Cartesian3.add(pts[i], pts[nextIndex], new Cesium.Cartesian3());

midpoint = Cesium.Cartesian3.multiplyByScalar(midpoint, 0.5, midpoint);

let up = Cesium.Cartesian3.normalize(midpoint, new Cesium.Cartesian3());

let right = Cesium.Cartesian3.subtract(pts[nextIndex], midpoint, new Cesium.Cartesian3());

right = Cesium.Cartesian3.normalize(right, right);

let normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3());

normal = Cesium.Cartesian3.normalize(normal, normal);

let originCenteredPlane = new Cesium.Plane(normal, 0.0);

let distance = Cesium.Plane.getPointDistance(originCenteredPlane, midpoint);

clippingPlanes.push(new Cesium.ClippingPlane(normal, distance));

}

console.log(clippingPlanes);

viewer.scene.globe.clippingPlanes = new Cesium.ClippingPlaneCollection({

planes : clippingPlanes,

edgeColor: Cesium.Color.WHITE,

unionClippingRegions : true,

});

tileset.clippingPlanes = new Cesium.ClippingPlaneCollection({

planes : clippingPlanes,

unionClippingRegions : true,

edgeColor: Cesium.Color.WHITE,

modelMatrix: Cesium.Matrix4.inverse(tileset.root.computedTransform, new Cesium.Matrix4())

});

``

{

“asset”: {

“version”: “0.0”,

“gltfUpAxis”: “Y”

},

“geometricError”: 49.7341114741387997,

“root”: {

“boundingVolume”: {

“sphere”: [

-4632209.9893530942499638,

2581120.2956524640321732,

-3532187.1000989843159914,

49.7341114741387642

]

},

“refine”: “REPLACE”,

“geometricError”: 49.7341114741387997,

“children”: [

{

“boundingVolume”: {

“sphere”: [

-4632216.1126385899260640,

2581116.8207813599146903,

-3532179.5933731999248266,

18.6634587549551014

]

},

“geometricError”: 18.6634587549551014,

“children”: [

{

“boundingVolume”: {

“sphere”: [

-4632216.1126385899260640,

2581116.8207813599146903,

-3532179.5933731999248266,

18.6634587549551014

]

},

“geometricError”: 1,

“content”: {

“url”: “Data/Tile_2/Tile_2.json”

}

}

]

},

{

“boundingVolume”: {

“sphere”: [

-4632211.8696897998452187,

2581111.7691548001021147,

-3532189.5037792301736772,

11.4677779945705005

]

},

“geometricError”: 11.4677779945705005,

“children”: [

{

“boundingVolume”: {

“sphere”: [

-4632211.8696897998452187,

2581111.7691548001021147,

-3532189.5037792301736772,

11.4677779945705005

]

},

“geometricError”: 0.5000000000000000,

“content”: {

“url”: “Data/Tile_3/Tile_3.json”

}

}

]

},

{

“boundingVolume”: {

“sphere”: [

-4632203.1482633799314499,

2581123.9984698197804391,

-3532194.5363906701095402,

13.1519037570412998

]

},

“geometricError”: 13.1519037570412998,

“children”: [

{

“boundingVolume”: {

“sphere”: [

-4632203.1482633799314499,

2581123.9984698197804391,

-3532194.5363906701095402,

13.1519037570412998

]

},

“geometricError”: 0.5000000000000000,

“content”: {

“url”: “Data/Tile_5/Tile_5.json”

}

}

]

},

{

“boundingVolume”: {

“sphere”: [

-4632205.6490913899615407,

2581110.9178494098596275,

-3532203.7913987198844552,

16.8360943859069003

]

},

“geometricError”: 16.8360943859069003,

“children”: [

{

“boundingVolume”: {

“sphere”: [

-4632205.6490913899615407,

2581110.9178494098596275,

-3532203.7913987198844552,

16.8360943859069003

]

},

“geometricError”: 0.5000000000000000,

“content”: {

“url”: “Data/Tile_4/Tile_4.json”

}

}

]

},

{

“boundingVolume”: {

“sphere”: [

-4632223.5277511803433299,

2581119.7752239098772407,

-3532165.6262226500548422,

17.6053403882662991

]

},

“geometricError”: 17.6053403882662991,

“children”: [

{

“boundingVolume”: {

“sphere”: [

-4632223.5277511803433299,

2581119.7752239098772407,

-3532165.6262226500548422,

17.6053403882662991

]

},

“geometricError”: 1,

“content”: {

“url”: “Data/Tile_1/Tile_1.json”

}

}

]

},

{

“boundingVolume”: {

“sphere”: [

-4632196.2920495299622416,

2581123.8337095598690212,

-3532207.7433422501198947,

14.1507573095964005

]

},

“geometricError”: 14.1507573095964005,

“children”: [

{

“boundingVolume”: {

“sphere”: [

-4632196.2920495299622416,

2581123.8337095598690212,

-3532207.7433422501198947,

14.1507573095964005

]

},

“geometricError”: 0.5000000000000000,

“content”: {

“url”: “Data/Tile_6/Tile_6.json”

}

}

]

}

]

}

}

``

The tileset disappearing means the clipping planes origin isn’t in the right place, but it’s hard to tell why. Any chance you’re able to recreate this in a Sandcastle you can share?

If not, I would try to debug this by looking at how the clippingPlanesOrigin is computed here:

https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Scene/Cesium3DTileset.js#L816

Another thing to try would be just returning Matrix4.IDENTITY here:

https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Scene/Cesium3DTileset.js#L1241-L1250

Since that should hardcode the clipping origin to the center of the earth so it matches the globe.

Hi Omar,

Thanks again for your advice, however not much luck has been had on this today.. I put together a rough sandcastle example with a model I have made public.. The shortcut "shift+q" will allow you to draw a polygon on the 3d-tiles, left click to place vertices and right click to close the polygon. You will notice the globe clips as intended, but the 3d-tiles disappear.. We wouldn't clip the globe with the final product, it is just to demonstrate that our planes are defined as expected before the inverse transformation.

Regards,
Josh

Ok, so the reason it wasn’t working was because tileset.root.computedTransform was actually identity (the root tile doesn’t have a transform). It gets its location on the surface of the earth from an RTC transform (relative to center).

Since what we really want here is to reverse the clipping plane’s matrix so that its coordinates are relative to the center of the earth, we can just invert the tileset._initialClippingPlanesOriginMatrix:

tileset.clippingPlanes = new Cesium.ClippingPlaneCollection({

planes : clippingPlanes,

unionClippingRegions : true,

edgeColor: Cesium.Color.WHITE,

modelMatrix: Cesium.Matrix4.inverse(tileset._initialClippingPlanesOriginMatrix, new Cesium.Matrix4())

});

``

Sandcastle link.

Unrelated, but I had trouble with the Shift+Q and clicking out a polygon. It seems like the polygon positions aren’t computed correctly? For example, here’s the result of clicking out a polygon on the surface:

The last two positions seem to be way underground when tilting the camera.

Thanks Omar, that is clipping as expected now!

The polygons, and polygon-->plane logic is still very much a work in progress, however I have never had it behave as per your screenshot. Works as expected on my end - see screenshot

Is it possible to have the code or sandcastle example for this one? I ask because because I am dealing with the same problem. The current sandcastle links no longer work.

Probably the best way to get started with this:

A new tutorial has been added recently, that shows the “Clipping Polygons” that have recently been introduced in CesiumJS. The tutorial was announced in New CesiumJS Tutorial: Hide Regions of 3D Tiles or Terrain with Clipping Polygons – Cesium , and can be found here:


Regarding this thread and the (outdated) sandcastles:

The sandcastle links that have been posted here have the form
https://cesiumjs.org/Cesium/Build/Apps/Sandcastle/#c=5VkNb9u4Gf...

It is possible to “revive” some of these, by taking the #c=... part, and attaching it to the proper URL for Sandcastles nowadays. So you can take the #c=... part and create a new sandcastle URL like this:
https://sandcastle.cesium.com/index.html#c=5VkNb9u4Gf...

But… note that these links are pretty old, and there might have been changes in CesiumJS that cause these Sandcastles to no longer work as expected. (For example, the last sandcastle that was posted above is updated here, but it does not seem to do anything useful…)


In addition to the tutorial that was linked above, here are a few Sandcastles that (still work and) might be useful: