Problems importing 3D models

Hi there,

did anyone already face problems with glb importing as 3D Models?

I create my glb file in Python through pyvista/vtk/pygltflib. However, when I import it in Cesium Ion, the only option that seems to work is 3D Models (import as 3D tiles).


Also, when importing as 3D Models (import as 3D tiles) to Cesium Ion, it does not come with any orientation, and thus I cannot try it in SandCastle, because it simply does not work.

An example of a glb file is here.
model_MAG_IGRF_grid_v0_0_3_1_try02_ravel_C.glb (40.6 KB)

And why do I want to import it as 3D Models?

  1. Because the model comes in an upward orientation instead of its original one (horizontal, as a map) as 3D Models (import as 3D tiles). And when we try to rotate it in Cesium Ion, it gets the wrong position. And positioning is crucial for our case since we are working with accurate georeferenced data.
  2. Also, we are having trouble rotating the 3D Models (import as 3D tiles), which is a 3DTileset. The origin of the model (as depicted in the Cesium Ion Editor is not centered at the model but at its edge.
  3. And we need to do that without Cesium Ion editor, through an automated process (lines of codes, therefore).

In an attempt to rotate this model, I adapted this example to adhere to rotation, not only translation.

The complete code is below.

var viewer = new Cesium.Viewer("cesiumContainer", {
  shadows: true,

viewer.scene.globe.depthTestAgainstTerrain = true;

var viewModel = {
  height: 0,


var toolbar = document.getElementById("toolbar");
Cesium.knockout.applyBindings(viewModel, toolbar);

var tileset = new Cesium.Cesium3DTileset({
  url: "../SampleData/Cesium3DTiles/Tilesets/Tileset/tileset.json",

  .then(function (tileset) {
      new Cesium.HeadingPitchRange(
        tileset.boundingSphere.radius * 2.0
  .otherwise(function (error) {

  .getObservable(viewModel, "height")
  .subscribe(function (height) {
    height = Number(height);
    if (isNaN(height)) {

    var cartographic = Cesium.Cartographic.fromCartesian(
    var surface = Cesium.Cartesian3.fromRadians(
    var offset = Cesium.Cartesian3.fromRadians(
    var translation = Cesium.Cartesian3.subtract(
      new Cesium.Cartesian3()
    var rotation = Cesium.Matrix3.fromRotationY(
      new Cesium.Matrix3()
    // tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
    tileset.modelMatrix = Cesium.Matrix4.fromRotationTranslation(rotation,translation);

  • I added var rotation = Cesium.Matrix3.fromRotationY - but could be X or Z: none worked for angle values different than 0.
  • I changed
    tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
    tileset.modelMatrix = Cesium.Matrix4.fromRotationTranslation(rotation,translation);
    to make sense.

The model simply vanishes.

Hey @leomiquelutti,

The easiest way to do this would be to upload your model using the REST API and pass in the longitude, latitude, and height for the origin of your 3D model. This will (1) make it appear in exactly the right location, and (2) when you rotate it with the Cesium ion tool it should rotate from the correct origin.

The REST API tutorial here describes how to set this up and has a link to a full example: The example uploads a CityGML, yours would be a type “3D_MODEL” which allows you to pass a “position”.

Hi @omar thanks for the answer.

Is it possible to test it from Sandcastle?

The method I suggested is done at upload time, not at runtime. So you’ll need to run it as a NodeJS script locally (or use anything to call the REST API, like Python etc)

Hi @omar,

we did uploaded our models thorugh the REST API. Thanks for the tip!

However, we could not place them correctly. They changed coordinates after importing. The coordinates in Cesium Ion Editor are different than the one we defined during the importing process. And we did use EPSG:4326 CRS. Any ideas?

@leomiquelutti can you share the asset ID so we can look into it?

Hi @omar,

it can be both 150867 or 150858. Thanks! :raised_hands:t2:

Hi @omar,

did you have the chance to look at our models? Any news?

Hi @leomiquelutti,

I had a look to the model and it seems all the vertices are in UTM coordinates. Cesium ion expects models to be in local coordinates ( So you’ll need to regenerate the models using local coords or unprojecting those positions.

To get the model to appear at those UTM positions if you were using Cesiumjs you would need to convert them to lat/lon/height. And then, to position something directly in the globe:

Cartesian3.fromDegrees(lat, lon, height)

Hope it helps.

P.S: While reviewing this we noticed that using local coordinates is not explicitely said in the BIM/CAD import documentation (, so thank you for raising this issue, as it helps to improve our documentation.

Hi @jtorres,

thanks for the help. Could you please show me how did you notice this? Where/what are those coordinates values from my model? I have been a hard time trying to track in my code (with strong 3rd-party dependency) how these values are set.

Hi @leomiquelutti,

I just converted to OBJ to be able to inspect visually the value of each vertex. But you can do the same by exporting to gltf and check min/max for position accessor or export to other text formats. Also you could import in blender to check where are the vertex positions.

Hope it helps

1 Like

Hi @omar and @jtorres,

I am confused about what do local coordinates mean.

  • The example you inspected (ID 150858) was in UTM.
  • Assets 155511 and 155515 are in WGS84

None works. Could you, or anyone, please help me get through this?

@leomiquelutti you would need to center your model geometry around the (0, 0, 0), or just have it be close to that origin. You can confirm this by opening your model in a tool like Blender and see that it is close to the origin.

You can then pass in the lat/lon/height of where that (0, 0, 0) point is on the globe with the REST API.

For example, if you had a building model that looked like this: image

You would want to place it using the REST API at a height of 9 meters above from the ground height at that location for it to appear correctly on the ground.

Hi @omar,

so, instead of building a whole model with real coordinates (be it UTM or WGS84), I should:

  • change them all for something with a (0,0,0) somewhere inside my model (maybe the center)
  • than provide a lat/lon/height, where my (0,0,0) will be placed upon?

Are local coordinates in meters?

1 Like

@leomiquelutti yes this is all correct. We have this now listed in this doc page:

  • Your model must use local coordinates (the geometry must be centered around the origin).
  • Units are assumed to be in meters.

If there’s anything that can improve that page we’re happy to hear that feedback.

I do want to add that supporting models in UTM/WGS84/other coordinate systems is something we’d like to support. I have a feature request for this documented here: Support tiling 3D models with different coordinate systems.


Hi @omar and @jtorres,

we imported it in the right place. Thanks!

However, we need to apply heading = -90 to get the right orientation in the Cesium Ion Editor.

We tried to rotate it with the following code:

var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
var heading = -Cesium.Math.PI_OVER_TWO;
var pitch = 0.0;
var roll = 0.0;
var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
var transform = Cesium.Transforms.headingPitchRollToFixedFrame(center, hpr);

The model goes out of the globe. Any ideas? Could you please help us?

If the origin of the model is not in the geometry center then rotating it will move it.

Have you tried rotating it before uploading it to Cesium ion? It looks like the ion REST API only supports a position, but the other way to handle this would be if you could pass a rotation to apply before tiling (which would be the same as rotating the model from its geometry origin before uploading it).