Is it not possible for LOD to work when the content of a tile is another tileset.json

I am currently experimenting with having different models within the contents of a specific tile, at different positions and at the same time being able to be put through the LOD refinement process of “ADD” or “REPLACE.

This is because I want to avoid making the adjustments for each model in that tile when exporting the model, and have all the models involved be exported with it at origin in model space.

So, I am wrapping each model within it’s own tileset.json to position and orientate them, but I realized that as long as I have a .json within the content section of the tileset, the LOD refinement “REPLACE” or “ADD” no longer works, and the childrens will always be rendered together with the content.

Am I missing something? or is it intentional for this to happen?

(In which rendering engine is this happening? CesiumJS, Cesium For Unreal, or Cesium For Unity…? I’d probably move the thread to the respective section)

There might be some special handling or behavior for tiles that refer to external tilesets. Could it be possible to attach one of the tilesets as a ZIP file here? (If it’s not too large, and if it can be shared). Otherwise, a more detailed description of the actual tileset structure could help - maybe a test data set can be created for those who will investigate this in the respective rendering engine.

Hi Marco13,

Thank you for the respond.

This is happening on CesiumJS rendering engine.

For more context, I am trying to create a simple editor to position and orientate 3D models and export them as tileset.jsons. All the models given to me are positioned at origin of their own model space.

That is why I initially planned to have each model wrapped in a single tile, to be linked by the content section of a parent tile. I assumed this would allow me to have multiple models in a single tile level, each with their own transform matrix, and at the same time able to use LOD on multiple models.

The attached are the tileset.json and models that I was testing with, I apologize if the structure I am using is wrong, and for the weird file names :sweat_smile: .

highrise.glb_TileSetLayer.Json is the entry tileset (The Parent/Root)
While the other 3 JSONs are the content and children tiles.
The GLB folder is placed in the same directory as the JSON files.

GLB.zip (8.5 MB)

TestTilesetsJSON.zip (2.4 KB)

The idea is that we could have multiple models wrapped individually in their own external tiles and linked in the content section of “PARENT” so that we could control their(models) positions with each of their transform matrix and at the same time able to use refinement methods such as “REPLACE” and “ADD” that would refine away the whole content of the “PARENT” Tile.

But only through the testing i realized that the refinement methods does not work correctly when the content section contains any external tileset.json, it would work correctly when the content section only has .glb(s) files. Both the content and children tiles are being rendered at the same time always.

So, from here I thought that it was because the external tiles linked in Content section was treated as its own tileset (new hierarchy), so I tried some “funky” method to mimic the behavior of REPLACE and ADD refinement.

Additional Information:————————————————————————————————————————————

I was somewhat able to get the method for REPLACE to work, by adding a child in the external tileset with no models, which it would replace the main content with nothing, mimicking the REPLACE refinement at certain zoom levels. (The attached is an example of what i tried to do)

highrise.glb.zip (621 Bytes)

For “ADD” refinement I tried doing the opposite, having an empty model content, and replaced with a child that has the actual content.

Ultimately both “funky” methods gave non ideal results (it is the wrong usage of 3DTile anyways :sweat_smile:). With the REPLACEMENT method still having both the children and parent content rendering at the same time, just that with the content disappearing when zoomed in, and for some reason the content is being culled away when the children tile exits the camera’s frustrum but the content is still within.

As for the “funky” ADD method, the result was that the added tileset being repeatedly added and rendered through the globe. (Sorry I have lost the example for this case)

————————————————————————————————————————————

After which, I went to consult the documentation on 3D Tile specification and saw this:

Which unfortunately I had missed the phrase during my initial research

  • Cannot have any children; tile.children shall be omitted

However, cesiumJS seems to still be taking the children of the tile into account, and having weird behaviors instead of omitting the children section of the tile.

Does this mean that it is still possible to achieve what i am trying to do ? As in, it is intended for the children of a tileset to be processed a certain way when there are external tiles in content section? Or it is just not meant to be like that and not intended for the children to be included at all when there are external tilesets in content section. (I understand to a certain extend it is also to prevent any circular inclusions)

Are there other attempts or others doing something like this before ? and how would you go about trying to have different models at different positions in a single tile level (without adjusting the models in model space), and have them be part of a LOD process too?

I hope the information i provided are relevant, and again i apologize if the question i am asking is a little weird, as i am getting quite confused and need some guidance on this to set me back on track.

At the moment I am changing the way my editor works to restrict any children in a tileset if the content has external tilesets, in case there really is no work arounds having external tilesets and LOD refinements.

I’ll probably have to allocate a bit more time to read through that information again, and to take a closer look at the data that you provided. Only a short hint about the last part for now:

Which unfortunately I had missed the phrase during my initial research

Cannot have any children; tile.children shall be omitted

However, cesiumJS seems to still be taking the children of the tile into account, and having weird behaviors instead of omitting the children section of the tile.

CesiumJS does probably not explicitly check if the tileset follows the rules that are described in the specification. When a tile has a content that is an external tileset, and that tile has children (in violation of the specification), then CesiumJS may still render… “something”, but the behavior is not clear, and not specified. (It may be the “weird” behavior that you refer to…)

Does this mean that it is still possible to achieve what i am trying to do ?

I probably did not yet fully understand the intended structure and goals. There should be some flexibility in terms of the structure. For example, when you say

That is why I initially planned to have each model wrapped in a single tile, to be linked by the content section of a parent tile.

This should be possible. The “root tileset” could have one tile (its root tile). This root tile could have, three children. Each of these children could have a content. And each of these contents could be an external tileset.

Right now, one of the points that are “most unclear” for me is: What is the content that is contained in the “root” tileset and that should be replaced with something from the external tilesets?

Thank you very much, Marco13, for your response.

I apologize for my confusing explanation, I do not really know how to clearly put it into words :sweat_smile:.

I understood clearly and you have answered my question regarding the “weird” behaviors that are happening when i have children and external tilesets in content of the same tileset. Thank you.

Moving on to the intended structure and goals for what I was trying to achieve.

In general, am I right to say that in a tileset the contents follows strictly the transformation matrix that was defined in the tileset?

  • Tileset:
    • root:
      • TransformMatrix
      • BoundingVolume
      • Content/Contents
      • GeometricError
      • Refine
      • Children

If let say we have 3 .glb model files under the Contents section, they will be applied with the transform matrix defined in the root section of the tileset.

I probably did not yet fully understand the intended structure and goals.

So allow me to try to explain more about the goal. The goal is to be able to have multiple .glb model files in contents, but not at the same position (not strictly following the Tileset’s transform matrix).
For example a small town, and in that town we have about 3 clusters of buildings, and in each cluster there are about 5 buildings each.

Figure. 1

Instead of building a LOD tree for each of the buildings in all 3 clusters, I was given a few “common” building model (a low detail template model of a building that could represent any kind of building) and need to build a low definition representation of the cluster when the camera is zoomed out.

So for example when I am zoomed out away from this 3 clusters, I could have 3 “common” building models each representing 1 cluster. All of which in 1 tileset. (example in Figure. 2)

Figure. 2

And when zoomed in closer (not very close), we could refine the previous tileset (Figure. 2) into 3 separate cluster children tileset. (example in Figure. 3)

Figure. 3 (The Diamond shapes are “Common” Building Models)

And if we zoom in even further into the a cluster, the cluster tileset will have children that links to the tileset for each building and refine “REPLACE” to those children. So in our example case, each cluster tileset will have 5 children (Tileset) and each children is 1 building in that cluster.
Each building will further have their own LOD models and tree. (example Figure. 4)

Figure. 4 (each building in the clusters are a tilesets with their own LOD, not “common” building model)

So for Figure. 2 Tileset, we have 3 Models at different positions, but have to be refined together, into 3 external Tilesets (1 for each cluster):

Tileset:

  • Root:
    • Transform: …
    • BoundingVolume: …
    • Contents: ( Not strictly 1 Common Model for 1 cluster, could have more )
      • Common Model (.glb)
      • Common Model (.glb)
      • Common Model (.glb)
    • Refine: “REPLACE”
    • Children:
      • Cluster 1 Tileset
      • Cluster 2 Tileset
      • Cluster 3 Tileset

And the pattern is similar for Figure. 3 Tileset as well (Cluster 1/2/3 Tileset).

Cluster 1/2/3 Tileset:

  • Root:
    • Transform: …
    • BoundingVolume: …
    • Contents: ( 3 Models just for example )
      • Common Model (.glb)
      • Common Model (.glb)
      • Common Model (.glb)
    • Refine: “REPLACE”
    • Children:
      • Building 1 Tileset
      • Building 2 Tileset
      • Building 3 Tileset
      • Building 4 Tileset
      • Building 5 Tileset

But here is the reason or the problem to why I have the goal and even attempted to wrap each model in the content into their own tileset, because the “common” models provided to me are individual building models that are at origin in their respective model space, and I would like to avoid making new models or putting the “common” models together to make a new low LOD model for each town, and each cluster.

That was why I was trying to wrap the “common” Models in the content into their own individual tilesets to allow each glb model in the content to have their own transform matrix.
Now I know that it is not allowed if the tileset has childrens.

Cannot have any children; tile.children shall be omitted

So with that my other question is that is it possible for us to define a transform matrix for each model in the content ?

  • Root:
    • Transform: …
    • BoundingVolume: …
    • Contents:
    • {
      Common Model (.glb)
      Transform Matrix
      },
    • {
      Common Model (.glb)
      Transform Matrix
      },
    • {
      Common Model (.glb)
      Transform Matrix
      }
    • Refine: “REPLACE”
    • Children:

If it is not possible, I understand and I would think this topic could be marked resolved. I will change the way my editor is handling the contents for each tileset.

Ultimately, I am not crafting the Tileset.Json manually, I am making a simple 3DTile editor to make things simpler for my other team mates to make the 3DTile Tileset.Json(s) in the future. That is why I am trying to catch the different edge cases as well as seeing if is possible to do certain unusual things.

In general, am I right to say that in a tileset the contents follows strictly the transformation matrix that was defined in the tileset?

Yes. Each content is transformed with the matrix that is computed by multiplying all transform matrices from the root, up to the tile that contains the content.

For example a small town, and in that town we have about 3 clusters of buildings, and in each cluster there are about 5 buildings each.

What you described here sounds like a plain, straightforward way of how LOD can be applied (and is supposed to be applied) in 3D Tiles.

So with that my other question is that is it possible for us to define a transform matrix for each model in the content ?

No, this is not possible

But it seems like there is some misunderstanding about the transform or how a tileset can be structured. For example: I don’t understand why you want to use external tilesets at all. (Note that external tilesets … do raise some questions. They offer a lot of flexibility, but … here, I don’t see a reason to use them).

As mentioned above: Each content is transformed with the matrix that is computed by multiplying all transform matrices from the root, up to the tile that contains the content.

(Also note that each tile can have a transform. And it is not even necessary for a tile to have a content You can insert a tile just to have a place to put a transform…)

However, trying to be more specific:

Based on your description, I created an example tileset. This tileset just contains four “unit cubes”. These are the “buildings” in one of the “clusters” that you mentioned.

Note that each tile that refers to such a unit cube refers to the exact same GLB file! I think that this is important. The difference between these tiles is only that they all define different transform matrices. These transforms contain translations, (0,0,0), (0,2,0), (2,0,0), and (2,2,0). This causes the exact same model to be rendered at 4 different positions.

The root node (above these children) refers to a different GLB file. This GLB file is just a “block” with a size of 3x1x3 (that I created for this example).

The tileset uses REPLACE refinement. So when rendering it in CesiumJS and zooming in and out, this is the effect:

Cesium LOD example

You could add another level of detail if you wanted, with finer representations of the model. (And yes, you could replace the whole model with an external tileset that contains a complex building).

The most “difficult” thing here is to get the transform values right. For example, if you now added a transform in the root tile, then the transform values of the child tiles might have to compensate for that. And in all cases, you’ll have to get the bounding volumes right, which might really be the most tricky part.

But maybe the example helps to illustrate the approach. It contains the GLB files and the tileset JSON, as well as a Sandcastle for quick tests.

Cesium Forum 42979 LOD.zip (7.6 KB)

Hi Marco13, Thank you for your clear and concise response, also Thank you for your time to make that example.

Sorry for the late response from me.

I understood the basic usage of 3DTile and their LOD process of REPLACE and ADD, as well as their use of instancing techniques to render the same model (GLB) at multiple positions.

The most “difficult” thing here is to get the transform values right. For example, if you now added a transform in the root tile, then the transform values of the child tiles might have to compensate for that. And in all cases, you’ll have to get the bounding volumes right, which might really be the most tricky part.

As for this part, there are no worries I believe, I have a simple algorithm that helps to calculate the transform matrix of each Child/Content(if external tile) with respect to the parent’s transform matrix, as well as having the bounding volume calculated in their own local space. I have tested it and it seems okay for this.

Allow me to take the example you provided with the 4 unit cubes as individual buildings in a cluster, and the 3x1x3 block as the parent (low detail LOD). lets assume that the 3x1x3 block is a .glb (Generated.glb) in the content section of the root node, and each of the 4 unit cube is within the children section of the root node (in their own Tilesets that defines their own transform matrix and possible further LOD refinement; while using the same .glb (unitCubeTextured.glb) file)

And, with the refine method of REPLACE the 3x1x3 block (in content) would be replaced by the 4 children tilesets (unit cubes) when zoomed in. I understand this use case of 3DTile, and have already implemented the export for this kind of export of 3DTile in my editor.

So the question is, for example at the root node level, instead of only 1 3x1x3 block (Generated.glb) we would want to have 2 of them side by side, both using the same .glb file, which means they would require different transform matrix?
And, also to allow the LOD process, where when we zoom in, both the 3x1x3 blocks would be refined away by REPLACE, and the 4 unit cubes will render.

May I ask how would you make the tileset.json(s) for this situation ?
(assuming we are to use the same Generated.glb file for both the 3x1x3 blocks. And, we are to exclude the method of separating the two 3x1x3 block into two tilesets, each taking 2 unit cube as their refinement LOD)

EXTRAS:

Here is just an screenshot of what I have been working on, to make a simple editor that would help my team who might not be familiar with 3DTile specifications or any of the available tools, to position their models and export the tileset.json(s).

The visualization in the picture also shows our example situation and the question I am asking, whereas the content of a tileset is 2 glb models at different position, and allowing LOD refinement to 4 other children.

If there are no clear cut way of doing it, I will just remove the ability to adjust the model’s transformation when it is part of a tileset’s content section from my editor. :smiley: :smiley:

So the question is, for example at the root node level, instead of only 1 3x1x3 block (Generated.glb) we would want to have 2 of them side by side, both using the same .glb file, which means they would require different transform matrix?
And, also to allow the LOD process, where when we zoom in, both the 3x1x3 blocks would be refined away by REPLACE, and the 4 unit cubes will render.

I agree that this sounds like a reasonable requirement. And the original intention to use an external tileset to combine multiple GLBs (with different transforms) into a single content (and still let it be refined by children) sounds reasonable as well. It’s a pity that this is disallowed by the specification.

(And I wonder why it is disallowed. It might have reasons in the implementation that I don’t have on the radar. It may also be that the intention to use the same (“identical”) GLB with different transforms was considered to be an unusual case. Admittedly, it sounds like an artificial example, but can imagine that this would be “nice to have” for a generic editor that allows “assembling” models into tilesets…)

May I ask how would you make the tileset.json(s) for this situation ?

Unfortunately, I don’t have a really good answer here. I just tried some “workaround”:

  • The root has two children
  • These children both contain the 3x1x3-block, with different transforms
  • One of these children has an empty child node (so that it “disappears” when it is refined)
  • The other child has the 4 unit-cube children that should replace both of the original 3x1x3-blocks

While it works in principle, there still can be cases where one child is refined and the other one is not refined, meaning that there can be configurations where

  • only one of the 3x1x3-blocks is visible
  • or one of the 3x1x3-blocks and the 4 unit cubes are visible

Captured here (slowly zooming to one of these points at the end):

Cesium Forum 42979 LOD workaround

Here’s the test, just for transparency:

Cesium Forum 42979 LOD workaround.zip (7.7 KB)

I’ll ping a few people to see if they have better ideas.


This looks interesting. (I’ve spent some time with libraries for creating 3D Tiles programmatically. They are not public yet, but the possibility to easily create tilesets from GLBs is also one of the primary goals here).

Is this editor publicly available (or will it eventually be published)?

Thank you Marco13 for your response.

It’s a pity that this is disallowed by the specification.
(And I wonder why it is disallowed. It might have reasons in the implementation that I don’t have on the radar. It may also be that the intention to use the same (“identical”) GLB with different transforms was considered to be an unusual case. Admittedly, it sounds like an artificial example, but can imagine that this would be “nice to have” for a generic editor that allows “assembling” models into tilesets…)

Yea I guess it is a pity that its disallowed by the 3DTile specifications. I was trying to build this functionality in the editor from one of the possible use cases which was why the question is so specific to this situation.

  • The root has two children

  • These children both contain the 3x1x3-block, with different transforms

  • One of these children has an empty child node (so that it “disappears” when it is refined)

  • The other child has the 4 unit-cube children that should replace both of the original 3x1x3-blocks

While it works in principle, there still can be cases where one child is refined and the other one is not refined, meaning that there can be configurations where

  • only one of the 3x1x3-blocks is visible

  • or one of the 3x1x3-blocks and the 4 unit cubes are visible

Yes, I understand this method. This was my mitigation plan for if I was not able to find other more “proper” ways to solve this roadblock :rofl: :rofl: and just take the possible flaw that 1 (3x1x3) model would not be refined away.
As, at first I thought my approach of trying have 2 models in the content with different transform matrix was the wrong direction for achieving the goal.

Now, with the information from this discussion, I would say my question has been answered. Will be going with the method above that you mentioned (the mitigation plan) :smiley:, for now, until maybe in the future there is a better way to achieve this.

Thank you so much Marco13 for your time and effort to reply to my weird questions.

Is this editor publicly available (or will it eventually be published)?

At the moment, I do not think so, I am still a Fresh Junior programmer and an intern, and this editor is a personal side project that I am doing to hopefully help better the life of my team mates in my company :rofl:, so I am unsure of what is needed or required to be checked for an application like this to be published or be put available to public.

However, If you are interested you could send me your email, once it is done, I could share it with you privately for you to check it out.

Once again, Thank you so much Marco13 for responding to my questions.

This thread has certainly unveiled some things that I’d consider as reasonable requirements, and that can (apparently) not nicely be accomplished with the current 3D Tiles structures.

(A hint that may be obvious: Of course, you could create a solution that contains two instances of that 3x1x3 block in the root, and that are properly replaced by the children. But only by combining these two instances into a single GLB - which is what you wanted to avoid, and which can be hard to do in a “life/online editor”. It is doable, but not so easily…)

I also asked internally. One colleague suggested something (that doesn’t work either):

  • root
    • 3x1x1 block with ADD refinement
      • 3x1x1 block with REPLACE refinement
        • children (unit cubes)

The idea was that it would ‘ADD’ together the two 3x1x3 blocks, and eventually replace them with the unit cubes. But… the REPLACE only replaces that single tile. It does not replace “everything up to the root”

However, I hope that this is not a too severe limitation for your editor.

(BTW: When this editor is “ready” and can be shared, I might have a look at it if you send it to marco@cesium.com (but cannot make any promises about feedback or so…))

(A hint that may be obvious: Of course, you could create a solution that contains two instances of that 3x1x3 block in the root, and that are properly replaced by the children. But only by combining these two instances into a single GLB - which is what you wanted to avoid, and which can be hard to do in a “life/online editor”. It is doable, but not so easily…)

Yea, I avoided modifying GLBs as I wanted the editor to be purely for editing the 3DTiles (keeping the work flow and the purpose of the application simple so my team mates could easily understand it).
Well, at the same time I am kind of lazy and lack the time and knowledge to get the editor be able to edit the model files :sweat_smile:, I assume the research and implementing it would get quite tedious.
(I don’t really look forward to working with the vertex level data of the models :rofl: )

However, I do have a planned feature where I wanted to allow the users to inject metadata into the GLBs using Cesium Extensions, EXT_STRUCTURAL_METADATA and EXT_MESH_FEATURES, for mesh level picking in CesiumJS.
Which may or may not involve modifying vertex level data of the GLB files directly. In previous tests that I have done, I used blender to Tag specific vertices of the GLB with an attribute ID, which then I used a script that defines the table and the metadata to attached onto the vertices with respect to their attribute ID, and export a new GLB with the table and metadata as the 2 Cesium extensions.
The use of this mesh level metadata is because there is a possibility that we are working with LIDAR scans, which are difficult to separate into individual models due to their vertex density (and stuff), thus mesh features for mesh picking :slight_smile:.

So, I think if in the future I were to make some form of vertex editor to tag the attribute ID, I might try to have the editor possibly have the functionality to combine models directly.

However, I hope that this is not a too severe limitation for your editor.

Well, I am building the editor around 3DTile specifications anyways, so I will have to follow the rules :joy:.
It is just a minor limitation as the editor was meant for general users to use it without much knowledge on 3DTile specifications. However, now that I have implemented all the safety checks and warnings to prevent the users from breaking the rules of 3DTile specification, it became a little less intuitive as an editor, and requires the users to at least have some knowledge and deeper understanding about the 3DTile to use the editor properly. But all in all it still capable of functioning with it’s indented goals.

(BTW: When this editor is “ready” and can be shared, I might have a look at it if you send it to marco@cesium.com (but cannot make any promises about feedback or so…))

Alright, I will share the editor project once it is at a ready stage, no worries on the feedback, it is purely for sharing the possibilities that cesium could be use for?

Once again, Thank you so much Marco13 for your responses, it has helped guide me and my thought process greatly.

It’s both an issue of convenience for end-users (i.e. how easy it is to accomplish a certain goal, and (ideally) understand the process while doing it), but also a technical question.

What you described unitl now focuses on the tileset JSON. You can juggle around with the JSON object, and and remove properties, and just dump a console.log(JSON.stringify...) to the console to see what’s up. Dealing with a GLB (with binary data) can be much more challenging.
(Not to mention that all this is likely happening in a browser, and a browser is… “Wahh :cry: I don’t know what a ‘file’ is!”…)

However, all this is possible, of course. You can have a look at https://gltf.report/ : You can drag-and-drop a GLB, and then there’s the <> (Scripts) tab at the left, where you can essentially write code, based on glTF-Transform, which is an unbelievably powerful library for tasks like that. The main point is: You wouldn’t have to deal with the raw data on the level of vertices and buffers.

Once more the pointer to glTF-Transform: It offers the possibility to implement support for extensions. And implementations for EXT_mesh_features and EXT_structural_metadata is already part of the 3d-tiles-tools. The 3D Tiles tools are not (yet) offering a real, public API for that. And they are not (yet) deployed in a way that may be integrated into the browser. But there are some examples at 3d-tiles-tools/demos/gltf-extensions at main · CesiumGS/3d-tiles-tools · GitHub showing how certain metadata-related operations can be implemented with that.

The broader topic about metadata came up occasionally. There’s certainly a lot that can and should be done for better support here. I once opened Tools and processes for assigning and editing IDs and metadata · Issue #801 · CesiumGS/3d-tiles · GitHub (with some links to forum threads) where people asked about that. (In fact, one thread was opened pretty recently).