How to set tileset.json transform correctly?

Hi everyone,

I’m facing some difficulties to create 3D tiles from another file format.

What i’m trying to achieve :

I have a collection of photos and a json file describing the position and orientations of the camera for each photo. The goal is to convert the json into 3d tiles where each photo is represented by a geometry (roughly a pyramid)

What I have done so far :

tileset.json, gltf and bin files are generated from the json file. These files describe a pyramid and its instances, each pyramid has a translation and a rotation and the vertices coordinates are expressed in a local system (the center of the dataset).

What I don’t understand :

I’m really confused about what I should set in the root “transform”. It seems obvious the translation part should be the center of the dataset in ECEF, but what about the rotation?

Here is what I see when I open the tileset file with CesiumJS :

The geometries should be parallel to the ground

Expected positions :

I have tried to use eastNorthUpToFixedFrame function from CesiumNative and put the result in root transform, but it does not work. On the other hand, it looks better when adding a 90 degrees rotation around X axis

That is where I’m getting confused. According to the documentation, cesium already rotates the whole scene to trasnform from a Y-up norm (gltf) to a Z-Up norm (cesium). Moreover, I thought the purpose of eastNorthUpToFixedFrame is to orient the geometry based on its position on earth. For instance to prevent building to “tilt” since without any rotation in the root transform they would have the same orientation no matter their position on earth ( a sphere)

So the questions are : am I missing something? How to set the root transform in tileset.json

Yeah, that can be a bit tricky and confusing.

According to the documentation, cesium already rotates the whole scene to trasnform from a Y-up norm (gltf) to a Z-Up norm (cesium).

I assume that this refers to the glTF transforms paragraph from the specification, or the ‘y-up to z-up’ section there. But this only refers to the glTF itself, and not to any placement that is done to this glTF based on the tile.transform of the tileset JSON.

In general, the eastNorthUpToFixedFrame is the function that you’re looking for. It generates a matrix that can directly be set as the tileset.root.transform to place “the whole tileset” at the respective position on the globe, including the proper orientation. Specifically, with this code

    const cartographic = Cartographic.fromDegrees(/* your values here */);
    const cartesian = Cartographic.toCartesian(cartographic);
    const enuMatrix = Transforms.eastNorthUpToFixedFrame(cartesian);
    const transform = Matrix4.toArray(enuMatrix);

The resulting transform can be set as the root.transform in the tileset JSON. (This is basically what is done in the createTilesetJson command of the 3d-tiles-tools, by calling this function)

each pyramid has a translation and a rotation.

This might be where some coordinate system issue could come in. If these translations are stored as part of the transform of the tiles, then they already have to be in z-up orientation. Maybe you applied them with y-up?

If there is some example code, or maybe examples of the input (camera positions and orientations) and output (tileset JSON), it could be easier to find out what might be wrong.


BTW: What you are doing there sounds similar to what has been considered as a feature of the 3d-tiles-tools via Extend tileset creation function to assign multiple positions · Issue #130 · CesiumGS/3d-tiles-tools · GitHub and Consider convenience functions to create instanced models · Issue #84 · CesiumGS/3d-tiles-tools · GitHub. It could be relatively low-hanging fruit in terms of implementation effort, but there are a few degrees of freedom for how the input data (instance positions) could be provided, and it hasn’t yet been decided how exactly that could be offered.

Hi Marco, thanks for your help

Here is an example of camera position (ecef) & rotation :

“Center” : {

"x" : 4357878.92150379438,

“y” : 499377.057726634433,

“z” : 4615903.61287616752

},

“Rotation” : {

“omega” : -3.03613405076508958,

“phi” : -0.72010804749420354,

“kappa” : 0.204569662598000079

},

For now, i’m not using the rotation. I will use it when the tileset is oriented as expected

Instances translations in the .bin file :

translations.push_back(pose.center() - center);

where “pose.center()” is “Center” from the json file, and “center” is the tileset center position (another ecef position)

Then

auto vect = Eigen::Vector3d(sceneCenter.x(), sceneCenter.y(), sceneCenter.z());
auto matrix = eastNorthUpToFixedFrame(vect);
for (int r = 0; r < 4; r++)
        for (int c = 0; c < 4; c++)
            root.transform[r + 4 * c] = matrix(r, c)

I get this matrix :

[-0.11385067290190085,0.993497873314173,0.0,0.0,-0.7225013006308046,-0.08279560677358744,0.6863940253861402,0.0,0.6819310044766846,0.07814642166605644,0.7272298411879223,0.0,4357691.866584947,499373.13872130145,4616051.714314801,1.0]

In euler :

Yaw (Z) –99.0°
Pitch (Y) –43.3°
Roll (X) +6.1°

The dataset is located in western switzerland, so the pitch and roll look good, but the result does not look good at all

Here, I’m very confused : eastNorthUpToFixedFrame matrix does not work, but a simple 90 degrees rotation on X without eastNorthUpToFixedFrame does the trick

I’ll start with another disclaimer: It may be necessary to iterate on that a bit more. There are some very subtle details that can decide on whether something is “right” or “wrong”, and it’s sometimes difficult to find out what caused a result to be “wrong”.

But there already are some important details in your answer. For example, you initially said

a json file describing the position and orientations of the camera

I may have silently assumed it to be a cartographic position. But the JSON that you posted indicates that these are cartesian (ECEF) positions. Additionally, the fact that you use the difference (pose.center() - center) for the individual translations is relevant here.

Based on that new information, I’d say that eastNorthUpToFixedFrame will indeed not be required. It sounds like root.transform simply has to be a translation matrix, with the translation just being the sceneCenter (i.e. that “center position” of the tileset that you mentioned). This should work when the translations that are computed as
translations.push_back(pose.center() - center);
are then the translations that are inserted as the translations of the child tiles of the root tile.