CesiumGS / 3d-tiles-tools

Apache License 2.0
280 stars 41 forks source link

Hello developer, I would like to ask a question about the location of tiles #120

Open jingyangking opened 4 months ago

jingyangking commented 4 months ago

this is the situation where I directly loaded tiles without making any modifications: image

you can see that the position of the building is vertical.Here is a demonstration of my model after making positional changes in the code: image image You can see that the positions of some subcomponents have been misplaced,How can I perform the correct position transformation of tiles?Thank you!

jingyangking commented 4 months ago

this is my tiles file two_ifctileset.zip

javagl commented 4 months ago

This is not really an issue of the 3d-tiles-tools. The issue tracker should not be used for general support questions. If you want to report a bug in the 3d-tiles-tools, or request a featue, then you can open an issue here. But if you have a general question, then you could ask at the Cesium Community Forum. There, more people will see your question, and more people will have a chance to answer. And if you ask such a question, then you should include more information. For example: I can see this line scene1.rotate... in the screenshot. But I don't know which framework/library/application you are actually using for rendering the tileset.


However, I did have a short look at the tileset that you provided. And I have to add a disclaimer here: I have to make a few guesses, due to the lack of technical background information. But the problem is very likely caused by the fact that the tileset has a structure that is ... not ideal for "runtime manipulations".

The tileset consists of a single root node/tile. And it has several child tiles. (Each child tile refers to one B3DM file). The child tiles do have a transform matrix that contains a huge translation component. This translation component should probably move the tile data to a certain position on the globe. But the problem is that this translation has to be taken into account when you want to rotate the tileset. Depending on the exact renderer that you are using (and depending on what scene1.rotate... is doing, exactly), the result of applying a rotation may not be what you expect.

I tried to illustrate this here:

Cesium Geolocation Structure


The question now is: How can this be solved?

And there is probably no easy, one-fits-all solution. But for the specific tileset that you attached, there may be a way to accomplish the desired goal. Since the structure of the tileset is very simple, you can easily create a new one that has a structure that makes this kind of manipulation easier.

You can use the createTilesetJson command to create a new tileset JSON file from the given B3DM files. When you run npx 3d-tiles-tools createTilesetJson -i C:\two_ifctileset -o C:\two_ifctileset\tileset-new.json then it will generate the tileset-new.json that is attached here:

tileset-new.json

This tileset will be located at the origin. This means that it will not include any tile transform matrices. This means that

  1. you have full control over the rotation of the tileset
  2. but you are also responsible for putting the tileset at the desired position on the globe

The following is a sandcastle that shows how this can be done:

const viewer = new Cesium.Viewer("cesiumContainer");

const tileset = viewer.scene.primitives.add(
  await Cesium.Cesium3DTileset.fromUrl(
    "http://localhost:8003/tileset-new.json", {
    debugShowBoundingVolume: true,
  })
);

// Move the tileset to a certain position on the globe
const transform = Cesium.Transforms.eastNorthUpToFixedFrame(
  Cesium.Cartesian3.fromDegrees(-75.152408, 39.946975, 20)
);
const scale = 1.0;
const modelMatrix = Cesium.Matrix4.multiplyByUniformScale(
  transform,
  scale,
  new Cesium.Matrix4()
);
tileset.modelMatrix = modelMatrix;

// Zoom to the tileset, with a small offset so that
// it is fully visible
const offset = new Cesium.HeadingPitchRange(
  Cesium.Math.toRadians(-22.5),
  Cesium.Math.toRadians(-22.5),
  560.0
);
viewer.zoomTo(tileset, offset);

The result will be the tileset, rendered at the given cartographic position:

Cesium Geolocation Result

jingyangking commented 4 months ago

Thank you for your reply! I think I will listen carefully to your suggestions. For the issue of correcting the position of tiles, I think the root cause of the problem may be the inconsistency of the coordinate axes applied when creating the tile set. I will study this and would like to know if you have any good suggestions? Thank you!

javagl commented 4 months ago

the inconsistency of the coordinate axes applied

I assume that refers to the question of which axis is the 'up' axis. And yes, this can be confusing. Every tool and every 3D format has its own convention. For example, glTF (which is contained in B3DM) defines the Y-axis as 'up' (at least, in version 2.0). In 3D Tiles, the Z-axis is the 'up' axis. And there are further details of the conventions that may cause confusion. Some of that is summarized at https://github.com/CesiumGS/3d-tiles/tree/main/specification#y-up-to-z-up , but ... regardless of how clearly it is specified, it will always lead to bugs.

However, in the level of the tileset itself, changing the 'rotation' could be simple. In a CesiumJS Sandcastle, you can set the tileset.modelMatrix (as shown in the example above), and this can contain the required rotation. But in the tileset that you provided, this is more difficult, because it contains the translation components in the child tiles.

Do you happen to know which tool was used for creating this tileset?

jingyangking commented 4 months ago

Yes, I used the py3dtiles tool to create tiles. Fortunately, I have found the problem of dealing with tiles with incorrect orientation. I added a corresponding matrix to the root node of tiles. json and set the corresponding rotation vector, and the incorrect orientation of tiles actually became correct. Although I am not sure why this operation can solve the problem perfectly, I still want to understand the principle behind it. If you know, I hope you can tell me. Thank you! image image

javagl commented 4 months ago

The screenshot that you showed already seems to be from the tileset-new.json that was created with the createTilesetJson command. And there, none of the tiles contains a transform. So you can insert the transform 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, ... that applies the rotation for the up-axis conversion. And the important point is: This can be done without affecting the transforms of the child tiles, and without affecting the bounding volumes. If the chilld tiles contained a transform matrix, then inserting the axis-conversion transform would affect the position of the tile content in the way that I tried to illustrate in the picture above.

jingyangking commented 4 months ago

Sorry, I didn't use the createTilesetJson command to perform a tileset.json update. I just added a matrix for the root node on top of the original tileset.json, and didn't do anything else. So, I'm curious why adding a matrix allows me to rotate and transform it as a whole. Here is the modified JSON tileset.zip

javagl commented 4 months ago

I see. I was confused by the screenshot: It shows a boundingVolume for the root that is different from the bounding volume of the root of the tileset that you just posted:

    "geometricError": 500.0,
    "root": {
        "boundingVolume": {
            "box": [
                486144.9223339863,
                3554992.1194912135,
                10.550217400223502,
                48.79999904060969,
                0.0,
                0.0,
                0.0,
                110.17518057441339,
                0.0,
                0.0,
                0.0,
                29.849785207338435
            ]
        },
...

In general, you have to be a bit careful when "manually" editing a tileset JSON file: It can easily happen that the tileset becomes "inconsistent" in terms of the bounding volumes and transforms.

In this case, adding a transform to the root tile should be "safe" in many ways: It originally did not have a transform (meaning that the transform defaulted to the identity matrix), and when you assign a new transform only to the root, then no bounding volumes or other transforms have to be updated.

But two things are still raising some questions:

I assume that you are using https://github.com/NASA-AMMOS/3DTilesRendererJS for rendering the tileset, is that correct? (Maybe I'll have a short look at this, and what rotateX might be doing different than just applying a rotation to the tileset...)

jingyangking commented 4 months ago

I'm very sorry, I forgot something. After adding the matrix to tileset.json, I don't need to manipulate the position of the model in the scene. I just need to modify the newly added matrix to change the position of the model at will. Therefore, the previous rotateX is no longer used (if it is necessary to use it, there will still be a problem of position confusion). I do use 3dtilesrenderjs, which makes it easy for me to load tiles in Threejs. Thank you for your reply!