The ‘3D Tiles’ category is probably the right place for this sort of question. But as you said: The question is extensive, and the answers to questions about “Best Practices” basically always start with “It depends…”.
The first bullet point list already contains some points that will affect what exactly the ‘best’ solution could be. But there still are many degrees of freedom and options. I’ll try to address these, with the huge disclaimers that…
- metadata handling in 3D Tiles and glTF is a ~“relatively new” feature, and “best practices” are still evolving
- parts of my response can only consist of further questions about technical details
- in some cases, I can only “brainstorm” about options, but not necessarily say which option is ‘better’ for the specific use case
Some high-level questions:
What is the total data size of all the geometry? (Roughly: The sum of the sizes of all glTF assets)
The main point here is whether it makes sense to try and load all the data at once. If the data is in the range of ~“a few dozen MB”, then it should be possible to just display that. When it reaches the ~“three-digit MB range”, one might have to think about possible approaches for LOD or lazy loading. (All that also depends on the machine that this is supposed to be running on, but a ballpark estimate could be good).
How exactly is the metadata currently stored?
It sounds like this was only stored in the extras
field of glTF nodes. The extras
field contains “application-specific data”. This is not a structured, reliable way of transporting metadata, particularly when the glTF asset itself is only one small building block (sic) of a larger tileset. While it is theoretically possible to brutally drill a hole through all ‘private’ fields of a picked element and access the extras
of the glTF (as in const id = picked?.content?._model?._loader?._gltfJson?.extras["exampleId"];
), this is not recommended, for obvious reasons.
There are some glTF extensions for handling metadata, in a more structured form:
KHR_xmp_json_ld
(not directly supported by CesiumJS)EXT_mesh_features
(a proposal from Cesium, to identify parts of glTF assets)EXT_structural_metadata
(also a proposal by Cesium, to associate parts of glTF assets with metadata)
One relevant question here is: Where is the metadata coming from, and how is the metadata currently assigned to the elements of the glTF asset?
Depending on how exactly the data is structured, it might not even be necessary to assign metadata to elements of a glTF asset. It might be sufficient to associate the metadata to the “glTF asset as a whole” (namely, to the tile that refers to that asset via its content.uri
). This is related to some of the other points:
Also we are not completely sure if we should use a single tileset for all buildings or one tileset per building. Any recommendations on that?
From the description until now, it sounds like a single tileset should be sufficient (unless it’s far larger than I’d assume).
Can certain floors of a building be hidden using CesiumJS, when they are all within one glTF file or should we opt for one floor per glTF?
It is possible to show/hode certain parts of one glTF asset, based on its associated metadata. Some details are given in the Styling and Filtering 3D Tiles tutorial.
Right now we are exporting our buildings as one glTF. Though we are unsure if it would make our life easier exporting each floor of a building as one glTF and stacking them in the tileset.
Addressing this (and summarizing some of the previous points):
The broader question here is that about the overall structure of the data and the metadata, also within the tileset, including the question about the structure and granularity of the glTF assets.
Options here are, very roughly speaking:
- You could try to create “fine-grained” glTF assets, and combine these (many) glTF assets with one tileset JSON
- You could put many elements into one glTF asset, and identify the sub-components by assigning IDs to them
More technically, with some trade-offs:
For 1., the “fine-grained” approach: You could try to store each ‘identifiable thing’ as one glTF asset. This would be one glTF asset for every “thing” that has an ID and associated metadata. One advantage could be that it could be easier to reflect a hierarchical structure: When you have a tile hierarchy like this (pseudocode/JSON)
root {
buildings: {
buildingA: {
// If it's necessary to be very fine-grained:
roomA0: {
"wallA0_N": "wallA0_N.gltf",
"wallA0_S": "wallA0_S.gltf",
"wallA0_E": "wallA0_E.gltf",
"wallA0_W": "wallA0_W.gltf"
},
roomA1: "roomA1.gltf",
roomA2: "roomA2.gltf",
},
buildingB: {
roomB0: "roomB0.gltf",
roomB1: "roomB1.gltf",
}
}
}
then it could be relatively (!) easy to offer options like: “Show/Hide Building A” and “Show/Hide Room 1 of Building B”, or “Give me the (metadata) color of the northern wall of room 0 of building A”.
You could assign metadata to these elements, as 3D Tiles Metadata, directly in the tileset JSON. Possible drawbacks could be: When it is really a lot of fine-grained data, then this could cause an overhead. But if you have roughly ~“50 buildings, with 5 floors, with 10 rooms per floor, with 1 KB of metadata each”, this would still only be in the range of 2.5MB, which shouldn’t be prohibitively large.
For 2., the coarse-grained approach: There could be an advantage due to the lower number of requests that have to go out (because there are fewer glTF assets referred to from the tileset JSON), and that the metadata could be stored in a more compact (binary) form. A disadvantage could be that it is technically more difficult to structure and assign this metadata: You’ll have to use EXT_mesh_features
to assign IDs to elements of the glTF asset. And you’d have to use EXT_structural_metadata
to associate metadata with these IDs (in binary form, not as easily editable as JSON).
For both cases, there’s the question of how the metadata is put into the right place (regardless of whether it’s in JSON or binary form, in the tileset or in the glTF assets). But that depends on where exactly the metadata is coming from, and how the tileset JSON is supposed to be created from the source data (geometry and metadata).
We will have some buildings with basements containing rooms we also need to show. Is there a way in CesiumJS to properly show these when they are laying underneath the base layer map’s surface? E.g. by hiding the layer’s area which overlaps with the basement and would otherwise cover it?
A starting point for that could be the Terrain Clipping Planes sandcastle.