Cesium3DTileset modelMatrix reset error
What happened?
I applied the transformation matrix to Cesium3DTileset and reapplied the initial value, but an error occurred.
Reproduction steps
- Create Cesium3DTeilset
- Clone the original modelMatrix
- Apply any transformation matrix
- Apply original modelMatrix ---> Error occurs after
I applied the above process to Sandcastle. If you wait a while after running, an error will occur.
Sandcastle example
https://sandcastle.cesium.com/#c=tVTfb9MwEP5XrIiHFAUnA/aydRWom8TDEGgrPEVibnJNDY5d2ZeWber/zsV2tmzrHnmomvPd9333y66Mdsi2EnZg2RnTsGNzcLJr+U9/lpZJ5e250SikBlsmGbsvNWNuLWqzcycMbQdZqfeT01KXWgEylAocINlob0N0PCINsRMSB5Xw9+F8Edx8ZU37w6q0hzBWJpzn16LdKDgXKPIn0XnEPHzkUYP/dkaXSU/Rp8RiedxVoIFvrGwlyi04Luo6jZgDgY0yS+A1bHC9AIefGyrf4QKspQ+qoy97jLozpl2YmHmkzYI1ausXELXUzXeJ1fpK6AYigLGCF9nw/a7gxw/GUNbSdLrHXm/WYIFbIuoce8ve8yKEToaS96wSxM9SStbYSZhARaM2CrgyTXpz0TuYMj6ZQeGEvbn3iP2NZ+nHOYhLTU0Tyrf6kmBQ9+272ILGS+mQGmbTVacrlEanI0VklTIafrUCrfxLbYt9+Ortj9x7hynw1tSggisbdy1Gp5Mwp0gsLJrGis1aVo/E89GpX6f+gFxCPx3N83bSzJG2+2Fpgobr7EpU8JTes33w5FfUQKFdpB5nRH3WjcSuhuyQU+DYV4QRjpXNahXuy38VPipeCKMl3j7M6IPqrltSSIVROuQZ6WK7Xq79Iz6dPBd8bTX6MhePyaSjxAL8wNYQS6DzAf0vz1lY9jlF+XcLcCFbMB2mtKhns7Crr7GNt9dz7jN2XBSFvyCT0yRLpg5vFcyG2/pJthtjkXX0itHrhUCvl6Da82VX/SH6yrmQPWPTfAyd1nLLZH124MWlLIRz5Fl1Sl3LOyiT2TSn+BfQeKO/bcEqcduHrY9ml+GQcz7NyTyMRGPUUthnzP8A
Environment
Browser: Chrome CesiumJS Version: 1.117 Operating System: window10
(Edit: This was also posted in the forum at https://community.cesium.com/t/cesium3d-tileset-model-matrix-reset-error/32477 )
- When the transform is modified, then this will cause a call to
Cesium3DTile.updateTransform, which updates the bounding volume of the tile by callingcreateBoundingVolume - When the tile defines a
regionin its bounding volume header, it will create the tile bounding volume by callingcreateRegion-
Important: It will pass in the current bounding volume of the tile as the
resultparameter, to be filled with the data from thisregion
-
Important: It will pass in the current bounding volume of the tile as the
- The
createRegionfunction tries to fill theresultparameter, assuming that it is aTileBoundingRegion -
But... when the transform changed, then the
createRegionwill not return aTileBoundingRegion. Instead, it will return aTileOrientedBoundingBox, by callingcreateBoxFromTransformedRegion
So to summarize: Changing the transform (by setting the modelMatrix) will convert all TileBoundingRegion into TileOrientedBoundingBox instances. Changing the transform again will trigger the update process, which find the region in the tile, and then assumes that the tile bounding volume is a TileBoundingRegion, even though it was previously changed to be a TileOrientedBoundingBox.
There could be some ... "pragmatic" ... fixes for this, involving some if (result instanceof TileOrientedBoundingBox) doNotTryToFillItWithRegionData(), but the details are to be sorted out...
This could be a one-line fix in the createRegion function in Cesium3DTile. We already check if the transform has been changed, and if so, return a TileOrientedBoundingBox instead of a TileBoundingRegion.
So this:
if (
!Matrix4.equalsEpsilon(transform, initialTransform, CesiumMath.EPSILON8)
) {
return createBoxFromTransformedRegion(
could become this:
if (
!Matrix4.equalsEpsilon(transform, initialTransform, CesiumMath.EPSILON8) ||
result instanceof TileOrientedBoundingBox
) {
return createBoxFromTransformedRegion(
I am part of the JTC Flagship program and would like to work on this issue please!
@slimkevo Sounds good!
@ggetz Hello, I wanted some feedback on whether this is an appropriate way to update and reload a 3D tileset in Cesium.
const viewer = new Cesium.Viewer("cesiumContainer", {
shadows: true,
});
let tileset;
try {
tileset = await Cesium.Cesium3DTileset.fromUrl(
"../SampleData/Cesium3DTiles/Tilesets/Tileset/tileset.json"
);
viewer.scene.primitives.add(tileset);
viewer.scene.globe.depthTestAgainstTerrain = true;
viewer.zoomTo(
tileset,
new Cesium.HeadingPitchRange(
0.0,
-0.5,
tileset.boundingSphere.radius * 2.0
)
);
} catch (error) {
console.log(`Error loading tileset: ${error}`);
}
tileset.initialTilesLoaded.addEventListener(function() {
const clone_matrix = Cesium.Matrix4.clone(tileset.modelMatrix, new Cesium.Matrix4());
const cartographic = Cesium.Cartographic.fromCartesian(
tileset.boundingSphere.center
);
const surface = Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
0.0
);
const offset = Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
100
);
const translation = Cesium.Cartesian3.subtract(
offset,
surface,
new Cesium.Cartesian3()
);
const matrix = Cesium.Matrix4.fromTranslation(translation);
tileset.modelMatrix = matrix;
setTimeout(async () => {
// Remove the tileset from the scene
viewer.scene.primitives.remove(tileset);
// Create a new instance of the tileset
tileset = await Cesium.Cesium3DTileset.fromUrl(
"../SampleData/Cesium3DTiles/Tilesets/Tileset/tileset.json"
);
// Update the modelMatrix
tileset.modelMatrix = clone_matrix;
// Add the new tileset to the scene
viewer.scene.primitives.add(tileset);
}, 5000);
});
Could creating a new instance of the tileset every time lead to performance issues?
Hi @slimkevo, is this code snippet intended to reproduce the issue, or to solve the issue?
I would generally not recommend recreating the tileset unless it's for debugging purposes only. Re-creating the tileset could lead to performance impacts and shouldn't be necessary for updating the modelMatrix property.