CesiumGS / 3d-tiles

Specification for streaming massive heterogeneous 3D geospatial datasets :earth_americas:
2.12k stars 467 forks source link

Semantic clarification with Bounding Volumes and ImplicitTiling #777

Open abwood opened 1 month ago

abwood commented 1 month ago

While implementing support for tile semantics (link), I encountered some unexpected behavior with ImplicitTiling and the use of the TILE_BOUNDING_BOX semantic. In particular, if the TILE_BOUNDING_BOX semantic is used on the root tile node of a implicitTileset, should child tiles use this tight fitting bounding box for subdivision, or the original bounding box as defined in the tileset.json?

My initial thought was that the bounding box defined in the TILE_BOUNDING_BOX semantic should be used when calculating all implicit child bounding volumes, in order to ensure that parent bounding volumes fully contain all child bounding volumes (spatial coherence).

However, from my observations it appears that the tiling tools and Cesium expect that implicit child bounding volumes are derived from the original bounding volume defined in the tileset.json (without the TILE_BOUNDING_BOX semantic).

Here is an example of the AGI_Headquarters tileset built with implicitTiling, with debugShowBoundingVolume set to true on the tileset. Zoomed out you can see the tilesetBounds shown and the TILE_BOUNDING_BOX shown around tile root tile node (gray box). The TILE_BOUNDING_BOX is not defined on any of the implicit tiles (no propertyTable).

image

Zooming in you can see child bounding volumes appear to be subdivided based on the original, larger bounding volume defined on the tileset.json: image

In the spec, I see the following defined for how one should handle missing semantic data:

If property values are missing, either because the property is omitted or the property table contains noData values, the original tile properties are used, such as those explicitly defined in tileset JSON or implicitly computed from subdivision schemes in Implicit Tiling.

I'm not sure there's clear guidance on how we should handle implicit children in this case. While the TILE_BOUNDING_BOX is missing on the implicit children, their instructions are to subdivide the bounds defined on he root tile node which happens to be redefined using the TILE_BOUNDING_BOX semantic. Is the intent that they should ignore the updated bounding volume on the root tile node?

javagl commented 1 month ago

I might have to re-read that and let it sink in (and maybe try it out on my own), but from the description and my current understanding, I'd also say that the implicit child bounding volumes should be derived from the bounding volume that is defined via the semantics, and not from the 'original' one.

So for me, right now, this rather sounds like a bug in CesiumJS, but that remains to be confirmed.

One potential detail that may be part of investigating this: One should check what exactly the "debug bounding voume visualiztion" is operating on. Very roughly: Maybe it's only the visualization that is wrong, and maybe the other internal mechanisms (like culling and whatnot) do use the one that is derived from the semantics. (Unlikely, but to be checked)

abwood commented 1 month ago

Thanks for the reply @javagl. In our implementation, I see that if we compute the bounding volumes from the semantic, the volumes are incorrect for the implicit children. I believe that for all of this to work, the tiling pipeline would also need to correctly subdivide the geometry to match these new child bounding volumes that would have been defined from the tighter parent TILE_BOUNDING_BOX semantic. Also possible that there's some other bug in our implementation which has lead me to this conclusion.

Given that the TILE_BOUNDING_BOX semantic is not required for implicitTiling to work, I can understand why Cesium and the tiling tools chose to require implicit bounds be derived from the original (unmodified) tileset.json bounds, but it does look like it breaks a few assumptions, such as the requirement for spatial coherence.

abwood commented 1 month ago

To clarify, here's the AGI Headquarters, root content in the implicitTileset. We see the tight bounding volume around this single root tile: image

And when I zoom in, the availability of the tileset indicates that the geometry is available in the bottom half of the octree (as it was defined with the original bounding volume). If we subdivide the TILE_BOUNDING_BOX defined on the root of the implicitTileset, we end up with child bounds that do not contain the geometry (see bounds clipping into the buildings).

image

javagl commented 1 month ago

That wasn't obvious in the first image. And yes, given the subdivision rule for implicit octrees, this is expected, which would mean that 1. either each implicit child tile would need its own TILE_BOUNDING_BOX (to avoid the automatic subdivision), or 2. the TILE_BOUNDING_BOX of the implicit root tile would have to take the subsequent subdivision into account.

All this raises some questions about the consistency of the data. (Where does this come from? Did you manually add the TILE_BOUNDING_BOX semantics in an existing one?)

lilleyse commented 1 month ago

It sounds like the spec needs to be clearer, but the intent is that each tile's bounding volume is implicitly derived from the tile bounding volume in tileset.json unless it is overridden by a semantic. So TILE_BOUNDING_BOX has no affect on subsequent subdivision.

javagl commented 1 month ago

This raises a few questions. Usually, the TILE_BOUNDING_BOX will be used for defining a smaller bounding volume. This means that each tile will have to define the TILE_BOUNDING_BOX (otherwise it would violate the spatial coherence).

So the affected section may be Implicit Tiling -> Subdivision Rules, which currently says

Implicit tiling only requires defining the subdivision scheme, refinement strategy, bounding volume, and geometric error at the implicit root tile. For descendant tiles, these properties are computed automatically, based on the following rules:

boundingVolume | Divided into four or eight parts depending on the subdivisionScheme

The wording of "divided into ... eight parts" suggests that it is derived from the parent tile. (One could argue that the 'Implementation Note' there suggests that it does have to be computed from the root tile, but that refers rather to the numerial issues for the other case).

abwood commented 1 month ago

Just to follow up, I worry that this approach also conflicts with how we handle the TILE_TRANSFORM semantic; if a TILETRANSFORM is specified on the root node of an implicitTileset, then one would expect all children in the scenegraph to also be impacted by this transform. That approach is counter to what is discussed above where the override is ignored for the sake of implicit tiling. I believe @javagl's suggestion is the only way to address these issues. I would expand this to include all relevant TILE semantics:

Any TILE_XXX semantic defined on the root tile of an implicit tileset that overrides a property used by the implicitTiling scheme (geometric error, transform, bounding volume, minHeight, maxHeight, etc) MUST define the semantics on all implicit children.

abwood commented 1 month ago

All this raises some questions about the consistency of the data. (Where does this come from? Did you manually add the TILE_BOUNDING_BOX semantics in an existing one?)

Missed this question; this sample tileset was generated using Cesium ion.

javagl commented 1 month ago

We might have to check internally to see whether there is any inconsistency (or even just "unexpected behavior") in the ion output.

But I do not (yet) fully understand the connection tot he TILE_TRANSFORM here:

if a TILE_TRANSFORM is specified on the root node of an implicitTileset, then one would expect all children in the scenegraph to also be impacted by this transform. That approach is counter to what is discussed above where the override is ignored for the sake of implicit tiling.

I have to admit that I might have a hard time (visually) imagining the effect of the TILE_TRANFORM for each possible case. But there is a difference of which I think that it is crucial here. Namely: The implicit tiling does not describe any way of how a the transform of an implicit child tile is derived from the transform of its parent.

So while the TILE_TRANFORM will affect the children (e.g. by transforming their content accordingly), I don't see any potential inconsistency or conflict here.

(Maybe I'll have to think that through with an artificial example, and maybe even pen+paper, to fully understand it...)