# Unable to load b3dm tileset to scene

**URL:** <https://community.cesium.com/t/unable-to-load-b3dm-tileset-to-scene/26414>\
**Category:** CesiumJS\
**Created:** [August 29, 2023, 1:12pm UTC](https://community.cesium.com/t/unable-to-load-b3dm-tileset-to-scene/26414 "2023-08-29T13:12:53Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![BingoStar](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/bingostar/32/15663_2.png) [@BingoStar](https://community.cesium.com/u/BingoStar)\
**Post date:** [August 29, 2023, 1:12pm UTC](https://community.cesium.com/t/unable-to-load-b3dm-tileset-to-scene/26414/1 "2023-08-29T13:12:53Z")

</div>

I have a b3dm object, that is available by [this](http://84.252.138.255/files/digitaltwineditor/models/ee38fd54-4dbb-4dd7-b10e-270475bc5707/15c23b48-75da-4606-b7ae-1632baaa0d8f/model.b3dm) link. I created this b3dm by `3d-tiles-tools`. I also have a `tileset.json` with such contents:

```auto
{
             "asset": {
                "version": "1.0"
             },
            "geometricError": 0,
            "root": {
                "boundingVolume": {
                  "region": [43.970149521784, 56.260414816738106, 43.9702, 56.26042, 0, 10]
               },
              "geometricError": 0,
              "refine": "ADD",
              "content": {
                "uri": "model.b3dm"
              }
          }
   }

```

This tileset is available by this [link](http://84.252.138.255/files/digitaltwineditor/models/ee38fd54-4dbb-4dd7-b10e-270475bc5707/15c23b48-75da-4606-b7ae-1632baaa0d8f/tileset.json)

I try to add my model to the scene like so:

```auto
 const tileset = await Cesium.Cesium3DTileset.fromUrl("/files/digitaltwineditor/models/ee38fd54-4dbb-4dd7-b10e-270475bc5707/15c23b48-75da-4606-b7ae-1632baaa0d8f/tileset.json", {
      debugShowBoundingVolume: true,
      debugShowGeometricError: true,
      debugShowUrl: true,
      debugWireframe: true
    });
    viewer.scene.primitives.add(tileset);

```

But it does not work. I see no model rendered. I expect to see this model somewhere near these WGS84 coordinates `43.970149521784, 56.260414816738106`. But there is just white screen.

The initial model was in `glb` format - it is just a simple tree. You can see this model attached to the topic.  
[849af735-3acc-4978-a70c-97580db543fa.glb](https://community.cesium.com/uploads/short-url/11HlPTCGKuMxfvkkX4u2GdZsx8k.glb) (178.7 KB)

Finally, the whole reproducible example is running at this [link](http://84.252.138.255/Cesium/index.html?guid=ee38fd54-4dbb-4dd7-b10e-270475bc5707)

What is wrong with that and how can I fix it? Many thanks in advance!

---

<div class="post-metadata">

**Author:** ![BingoStar](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/bingostar/32/15663_2.png) [@BingoStar](https://community.cesium.com/u/BingoStar)\
**Post date:** [August 29, 2023, 1:56pm UTC](https://community.cesium.com/t/unable-to-load-b3dm-tileset-to-scene/26414/2 "2023-08-29T13:56:43Z")

</div>

I switched on OSM imagery and see that I’m somewhere in Ocean. It seems like `region` property is defined incorrectly. Probably it should be in `EPSG:3857`

---

<div class="post-metadata">

**Author:** ![Marco13](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/marco13/32/2757_2.png) [@Marco13](https://community.cesium.com/u/Marco13)\
**Post date:** [August 29, 2023, 1:58pm UTC](https://community.cesium.com/t/unable-to-load-b3dm-tileset-to-scene/26414/3 "2023-08-29T13:58:01Z")

</div>

The `tileset.json` contains

```auto
"boundingVolume": {
  "region": [43.970149521784, 56.260414816738106, 43.9702, 56.26042, 0, 10]
},

```

and you said

> I expect to see this model somewhere near these WGS84 coordinates `43.970149521784, 56.260414816738106` .

It might be that there is a misunderstanding: The `boundingVolume` of a tile does not **place** the tile into that bounding volume. Instead, it is supposed to _describe_ where the tile data actually **is**.

The bounding box of the given B3DM file is

```auto
min: (-6.922408011098346,-0.46230301526023787,-8.381912988737927)
max: (8.597974959256684,16.36762981635396,7.617428474129772)

```

meaning that this B3DM is placed “at the origin”, and this is totally unrelated to the bounding region that is given in the tileset.

* * *

Details of the answer now depend on the actual goal:

- Should that B3DM be displayed at a certain position on the globe? Then you can specify a [tile transform](https://github.com/CesiumGS/3d-tiles/tree/main/specification#core-tile-transforms) for the tile that refers to this B3DM
- Do you want to place the tileset at a certain position on the globe? Then you only have to set the proper bounding volume in the `tileset.json`, and can set the location of the tileset at runtime.

* * *

I think that the second one is a bit more versatile, but depending on the application case, you might want to have the B3DM geolocated directly. However, if the goal is the second one, then you could use the following as your `tileset.json` file:

```auto
{
  "asset": {
    "version": "1.1"
  },
  "geometricError": 4096,
  "root": {
    "boundingVolume": {
      "box": [
        0.8377834740791688,
        0.38224225730407735,
        7.952663400546863,
        7.760191485177515,
        0,
        0,
        0,
        -7.9996707314338495,
        0,
        0,
        0,
        8.4149664158071
      ]
    },
    "geometricError": 512,
    "content": {
      "uri": "model.b3dm"
    },
    "refine": "ADD"
  }
}

```

Then you can create a tileset from that, and set it to the desired location, by setting its `modelMatrix`, as shown in this sandcastle:

```auto
const viewer = new Cesium.Viewer("cesiumContainer");

// Create the tileset in the viewer
const tileset = viewer.scene.primitives.add(
  await Cesium.Cesium3DTileset.fromUrl(
    "http://localhost:8003/tileset.json", {
    debugShowBoundingVolume: true,
  })
);

// Move the tileset to a certain position on the globe
const transform = Cesium.Transforms.eastNorthUpToFixedFrame(
  Cesium.Cartesian3.fromDegrees(43.970149521784, 56.260414816738106, 0)
);
tileset.modelMatrix = transform;

// Zoom to the tileset, with a small offset so that
// it is fully visible
const offset = new Cesium.HeadingPitchRange(
  Cesium.Math.toRadians(-22.5),
  Cesium.Math.toRadians(-22.5),
  60.0
);
viewer.zoomTo(tileset, offset);

```

The result will then be this:

![Cesium Single B3DM](https://global.discourse-cdn.com/cesium/original/2X/4/45aac076499358e8a78492bcbb1bc4c088770257.png)

* * *

An aside:

The `tileset.json` that was shown above was created with the `3d-tiles-tools`. There is a [pull request in the `3d-tiles-tools`](https://github.com/CesiumGS/3d-tiles-tools/pull/51) that was merged yesterday, which contains a `createTilesetJson` function. This function is **not** yet part of the release, but if you check out this branch locally, then you can use this function to create a `tileset.json` file for your `model.b3dm` file like this:

```auto
npx ts-node ./src/main.ts createTilesetJson -i C:\data\model.b3dm -o C:\data\tileset.json

```

---

<div class="post-metadata">

**Author:** ![BingoStar](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/bingostar/32/15663_2.png) [@BingoStar](https://community.cesium.com/u/BingoStar)\
**Post date:** [August 29, 2023, 2:15pm UTC](https://community.cesium.com/t/unable-to-load-b3dm-tileset-to-scene/26414/4 "2023-08-29T14:15:11Z")

</div>

@Marco13 Thank you, Marco for such a detailed answer! I’m relatively new to all these concepts and have some extra question. You say, that the given B3DM have some specific bounding box and provide some values for min and max. The question is - how can I read myself these values? Is there any reader for this format? And the second question refers to the main goal of the topic. I guess, my main goal is not to position the model at runtime, since I do not expect cesium to be the sole client of my data. I mean to say, that I need the position to be hard coded inside tileset.json file. So, is it possible to implement through `tile transform`? Are there any examples with model instances positioned to some known coordinates in WGS84? Many-many thanks in advance!

---

<div class="post-metadata">

**Author:** ![BingoStar](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/bingostar/32/15663_2.png) [@BingoStar](https://community.cesium.com/u/BingoStar)\
**Post date:** [August 29, 2023, 2:48pm UTC](https://community.cesium.com/t/unable-to-load-b3dm-tileset-to-scene/26414/5 "2023-08-29T14:48:09Z")

</div>

@Marco13. I updated my [example](http://84.252.138.255/Cesium/index.html?guid=ee38fd54-4dbb-4dd7-b10e-270475bc5707). I know use transorm function and it seems to work exactly like it should. At the same time the result looks very strange now.

 ![tree](https://global.discourse-cdn.com/cesium/original/2X/a/a6246d88a946da7c21e26159e86dd64ee1266b41.jpeg)

It does not look like a tree in your screen. What may be wrong? Thanks!

---

<div class="post-metadata">

**Author:** ![Marco13](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/marco13/32/2757_2.png) [@Marco13](https://community.cesium.com/u/Marco13)\
**Post date:** [August 29, 2023, 3:03pm UTC](https://community.cesium.com/t/unable-to-load-b3dm-tileset-to-scene/26414/6 "2023-08-29T15:03:33Z")

</div>

> [@BingoStar](#):
>
> You say, that the given B3DM have some specific bounding box and provide some values for min and max. The question is - how can I read myself these values? Is there any reader for this format?

You mentioned earlier

> The initial model was in `glb` format - it is just a simple tree.

and you created a B3DM file from that. It may be worth mentioning that when you use **3D Tiles 1.1** (instead of 1.0), then you can also use `.GLB` files directly. So you could create the corresponding `tileset.json` like this:

```auto
{
  "asset": {
    "version": "1.1" // When you use "1.1" here....
  },
...
    "content": {
      "uri": "model.glb" // ... then you can use the GLB file here
    },
...
  }
}

```

Beyond that, there are different tools for dealing with B3DM data, but I’m not aware of any tool that can _directly_ compute the bounding box of a B3DM. One … solution (although a bit cumbersome) is

- Extract the `.GLB` file from the B3DM, e.g. with the `3d-tiles-tools` `b3dmToGlb` command
  - (You don’t have to do this, because you already have the GLB file!)

- Drop the GLB file into [https://gltf.report/](https://gltf.report/)
- Select the “Metadata” tab

This will show be bounding box, as in the lower right of this screenshot:

 ![Cesium GLB from B3DM bounds](https://global.discourse-cdn.com/cesium/original/2X/9/973ffe7118ec5148edbad4f9bdcc4ffcd87bbbbd.png)

When you have this bounding box, with its `(min,max)` points, then you still have to convert this into the `boundingVolume.box` representation of 3D Tiles. There, you need the `(center,halfAxes)` representation (because it can not only represent an Axis-Aligned bounding box, but also an _Oriented_ bounding box). The conversion from the `(min,max)` representation into the representation that is used for 3D Tiles is… simple, but … currently, also has to be done manually. The computation is shown, for example, in [this code snippet](https://github.com/CesiumGS/3d-tiles-samples/tree/main/1.1/BoundingBoxTests#conversion).

All this is indeed a bit cumbersome. I think that some of this could be simplified. One idea would be to add this information as part of the output of the [`analyze`](https://github.com/CesiumGS/3d-tiles-tools#analyze) command of the 3D Tiles Tools.

I opened [Extend `analyze` command to include bounding volume information · Issue #59 · CesiumGS/3d-tiles-tools · GitHub](https://github.com/CesiumGS/3d-tiles-tools/issues/59) for this. This could be a low-hanging fruit, be implemented with reasonable effort, and might be useful for various sorts of analysis and debugging.

* * *

> I guess, my main goal is not to position the model at runtime […] I mean to say, that I need the position to be hard coded inside tileset.json file.

> … it seems to work exactly like it should. At the same time the result looks very strange now.

This looks like … the `transform` matrix is wrong.

One way of determining the right matrix could be:

- Create the transform with the `eastNorthUpToFixedFrame` function (for a given position)
- Print this as a flat array to the console

For example, the output of this

```auto
const transform = Cesium.Transforms.eastNorthUpToFixedFrame(
  Cesium.Cartesian3.fromDegrees(43.970149521784, 56.260414816738106, 0)
);
console.log(Cesium.Matrix4.pack(transform, new Array(16), 0));

```

will be

```code
-0.6942835079850745,0.7197016121559956,0,0,-0.5984826914710163,-0.5773457436868751,0.5554190852466787,0,0.399736011074243,0.3856183109069252,0.831570586146325,0,2555492.883786797,2465239.113014277,5280601.728445846,1

```

And you can just copy+paste this into the `transform` array of the root tile.

The final `tileset.json` should then be

```auto
{
  "asset": {
    "version": "1.1"
  },
  "geometricError": 4096,
  "root": {
    "transform": [
        -0.6942835079850745,0.7197016121559956,0,0,
        -0.5984826914710163,-0.5773457436868751,0.5554190852466787,0,
        0.399736011074243,0.3856183109069252,0.831570586146325,0,
        2555492.883786797,2465239.113014277,5280601.728445846,1
    ],
    "boundingVolume": {
      "box": [
        0.8377834740791688,
        0.38224225730407735,
        7.952663400546863,
        7.760191485177515,
        0,
        0,
        0,
        -7.9996707314338495,
        0,
        0,
        0,
        8.4149664158071
      ]
    },
    "geometricError": 512,
    "content": {
      "uri": "model.b3dm"
    },
    "refine": "ADD"
  }
}

```

This can be loaded with this sandcastle:

```auto
const viewer = new Cesium.Viewer("cesiumContainer");

// Create the tileset in the viewer
const tileset = viewer.scene.primitives.add(
  await Cesium.Cesium3DTileset.fromUrl(
    "http://localhost:8003/tileset.json", {
    debugShowBoundingVolume: true,
  })
);

// Zoom to the tileset, with a small offset so that
// it is fully visible
const offset = new Cesium.HeadingPitchRange(
  Cesium.Math.toRadians(-22.5),
  Cesium.Math.toRadians(-22.5),
  60.0
);
viewer.zoomTo(tileset, offset);

```

And it will display the tree at the same location as before, but _without_ setting the `modelMatrix` at runtime.

---

<div class="post-metadata">

**Author:** ![BingoStar](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/bingostar/32/15663_2.png) [@BingoStar](https://community.cesium.com/u/BingoStar)\
**Post date:** [August 29, 2023, 3:06pm UTC](https://community.cesium.com/t/unable-to-load-b3dm-tileset-to-scene/26414/7 "2023-08-29T15:06:43Z")

</div>

Thank you, sir! You really saved my day!
