Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial orientation of S2 globe is too sensitive to changes in the boundingVolume value? #11488

Open
pmaxarr opened this issue Aug 29, 2023 · 7 comments

Comments

@pmaxarr
Copy link

pmaxarr commented Aug 29, 2023

Recently we had a problem that two versions of our internal S2 globe was displayed with different initial orientation by Cesium. After investigating we realized that the boundingVolume of the two version had a small decimal difference (7th decimal of PI).

Cesium seems to be sensitive to the exact value of the boundingVolume when opening our internal globe and selecting what S2 face to display as initial orientation.
Cesium is zooming to different S2 faces when opening our internal globe depending on the root boundingVolume value defined in the tileset.json file .
If the root boundingVolume is as follows then Africa (face 0) shown:
"root": {
"boundingVolume": {
"region": [
-3.141592653589793,
-1.5707963267948966,
3.141592653589793,
1.5707963267948966,
-10881.995181892067,
8805.044281111099
]
},
here -3.141592653589793 is identical to the definition of PI.

If the root boundingVolume is as follows then Antarctica (face 5) is shown:
"boundingVolume": {
"region": [
-3.141592618820418,
-1.5707962986969973,
3.14159264862274,
1.5707962837028416,
-10881.995181892067,
8805.044281111099
]
},

The difference of the bounding volume is:

-3.14159265358979 - -3.141592618820418
-3.47693718261155e-08

Is this as expected? Is it required that the boundingVolume must be identical to PI to orient the globe to display Africa (face 0) as its initial orientation?

We were suprised that our small difference in boundingVolume resulted in the dispaly of different initial S2 faces (Africa/Antarctica).

@pmaxarr pmaxarr changed the title Intial orientation of S2 globe is too sensitive to changes in the boundingVolume value? Initial orientation of S2 globe is too sensitive to changes in the boundingVolume value? Aug 31, 2023
@ggetz
Copy link
Contributor

ggetz commented Sep 1, 2023

Hi @pmaxarr, thanks for the repot. Would you mind including a brief code snippet or sandcastle example that shows how you are constructing your scene?

By default, the home view is oriented relative your timezone.

If you are zooming to a bounding sphere, I would not expect such a small difference in the bounding region to cause a difference in orientation.

@ggetz ggetz added the needs feedback On hold until additional info is supplied label Sep 1, 2023
@javagl
Copy link
Contributor

javagl commented Sep 6, 2023

Attached is a file with two tilesets, tilesetA.json and tilesetB.json, which each just define the bounding volume as described in the first comment. It also contains a sandcastle for loading them and switching between them with a dropdown menu.

S2OrientationIssue.zip

Cesium S2 Orientation

From a quick debug pass, the root cause can (probably) be tracked to this check in the Transforms.js

The bounding spheres of the tilesets are computed as follows
tileset A:
center: Cartesian3 { x: 0, y: 0, z: 0 }
radius: 11050175.466421327
tileset B:
center: Cartesian3 {x: 1.862645149230957e-9, y: 2.7755575615628914e-17, z: -1.862645149230957e-9}
radius: 11050175.466421323

The center that is used as the origin there is obviously EPSILON14-equal to (0,0,0) in the first case, causing ~"some default orientation" (looking at Africa) to be used.

In the second case, the center is not EPSILON14-equal to (0,0,0). It will try to compute some orientation from that, in this line, ending up with east=(-2.7755575615628914e-17, 1.862645149230957e-9, 0) there. These numbers are small enough to consider the initial orientation as ~"random noise".


Focussing on the problem description:

Finding a solution that does not involve discontinuities (in a mathematical sense) could be difficult. When zooming to a sphere with its center at (0.0,,0,0), there is no sensible orientation - so some default has to be used. When the center is at (1.0, 0,0), then there is an orientation. That orientation may be completely different from the default. What about (0.1...)? What about (0.01...)? What about (0.0000001...)? One could try to come up with something that interpolates between the "default" orientation and the "computed" interpolation, based on the distance of the center to the origin (maybe "weighted" by the inverse radius of the sphere). But making sure that it is robust and always yields "nice" results could be tricky...

@pmaxarr
Copy link
Author

pmaxarr commented Sep 7, 2023

Thank you for the explanation @ggetz and @javagl, I believe I understand the problem now.
We will try to avoid bounding spheres that triggers this behavior.

@javagl
Copy link
Contributor

javagl commented Sep 8, 2023

Trying to avoid that problem at the producing side could also be tricky. The fact that the values in the bounding region may be the result of some computation (possibly involving deg-to-rad conversions and conversions between CRSes) might make certain rounding errors inevitable. The way how exactly the values are used for computing the initial oprientation is not specified (it might work as expected for a given set of values, because of the EPSILON14-range, but would not work if it was changed to EPSILON15).

One way to imagine the problem could be: Place the bounding shere at the origin, (x=0,y=0,z=0). Now pick it with the mouse, and drag it down the z-axis, slowly, always updating the camera so that it always matches the initial configuration that would be achieved with the zoomTo function. Right now, it would start with the view that looks at Africa. At a certain point (say (x=0,y=0, z=0.00....001)), it will "snap" to the view that looks at Antarctica. Some requirements for the fix would be:

  1. At z=0.0, it should look at Africa (the default)
  2. At z=earthRadius, it should definitely look at Antarctica.
  3. There should be no "snapping" between these values,( i.e. no discontinuity for any z=0...earthRadius)

Maybe some maybe some solution can be derived from that...

@ggetz ggetz added type - bug category - 3d tiles 3D Tiles as Terrain and removed needs feedback On hold until additional info is supplied labels Sep 8, 2023
@javagl
Copy link
Contributor

javagl commented Mar 10, 2024

For what users can observe here, the problem is that the viewer.zoomTo(tileset) call that is commonly used to center the camera on a tileset will eventually call camera.lookAt(target, offset), with the target being the center of the bounding sphere of the tileset. The implementation of lookAt tries to compute the "east north up (ENU)" matrix for that center/target, which leads into the special cases in the Transforms class that I pointed out earlier. Whether or not these "special cases" are triggered solely depends on some EPSILON14-thresholds. Now, the center of a tileset that describes the whole earth is ... at the center of the earth, i.e. at (0,0,0), and the differences in the tilesets in this issue have just been whether that EPSILON14-case was triggered or not.

So the issue is....

  • one cannot compute the ENU matrix from (0,0,0).
  • one can compute the ENU matrix from (1,1,1)
  • whether or not one can compute a sensible ENU matrix from (1e-10, 1e-10, 1e-10) is up for debate. I'd say "no", but one can just perform the computations and return the result nevertheless...

In terms of solving this, the question boils down to

What is the orientation that should be used when calling lookAt?

This unfolds into several considerations and follow-up questions. One high-level question (although only remotely related to this particular issue) is: Should this depend on whether the scene.globe is present (and whether it is shown) or not?

I could imagine that this should be taken into account. Imagine you have a glTF model that has a size of 10x10x10, load it with globe: false, and you want to lookAt(1,1,1). It will use some orientation that is computed from ENU at a random place on the surface of the earth. Pretty unexpected, I guess. However, one could just say: "You have to use lookAtTransform in this case, and pass in the matrix that you want". More generally, the camera orientation seems to depend on whether the globe is present or not (in subtle and hard to articulate ways), so maybe we don't want to go down that path for now.


I do have an approach, implemented locally, but just as an experiment. The outline is: When calling lookAt, then

  • compute the "default" orientation (that is used when looking at (0,0,0))
  • compute the ENU orientation for the actual target (which may or may not be correct)
  • interpolate between the two, with a weight that depends on the distance of the target to (0,0,0)

That "weight" is currently computed as max(1.0, distance/ellipsoidRadius). This means that

  • for targets very close to the center of the earth, the default orientation will be used
  • for targets near the surface of the earth, the computed orientation will be used

It seems to fix the issue for the given tilesets, but feels a bit shady: What does the ellipsoid have to do with all that? Imagine that the distance is 0.5*radius (i.e. somewhere inside the earth). Then it will use an orientation that is just in-between the "default" orientation and the orientation that it would have if it was at the surface of the earth for this location, which could be hard to justify. One could just set the weight to be max(1.0, distance), and I actually played with that, via if (globe==false) ellipsoid=UNIT_SPHERE, but all this involves guesses about what users/callers might expect from this function...

@javagl
Copy link
Contributor

javagl commented Mar 12, 2024

The overarching question for fixing this issue is: Under which conditions should the zoomTo/lookAt/... and related family of functions even try to compute an "orientation" from a bounding sphere? And the details are subtle, and still have to be sorted out internally.

In the meantime, one way of just avoiding the issue could be to not use lookAt or zoomTo. Instead, one coud use the Camera#lookAtTransform function, which does not try to compute an orientation, but takes the orientation directly as a matrix instead.

Applied to a tileset, this could be

function zoomToTilesetFixed(targetTileset) {
  const boundingSphere = targetTileset.boundingSphere;
  const transform = Cesium.Matrix4.fromTranslation(boundingSphere.center);
  const offset = new Cesium.Cartesian3(boundingSphere.radius, 0, 0);
  viewer.camera.lookAtTransform(transform, offset);
}

Which means that instead of calling
viewer.zoomTo(currentTileset);
one could just call
zoomToTilesetFixed(currentTileset);

@javagl
Copy link
Contributor

javagl commented Mar 14, 2024

In addition to the workaround that was described above: Considering that it is known (at the source) that this tileset is supposed to represent the whole globe, one wokaround could also be to not compute the bounding region, but just insert the (known) fixed bounding region (without the epsilon-deviations) into the tileset JSON. Of course, this would be very specific and a bit quirky, and a more general solution has to be found on the CesiumJS side, but maybe the problem could be avoided for this specific case, at least.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants