Model Transforms

I have been provided with a number of 3D aircraft models in OBJ format. I used the online converter to convert the files to GLTF v2 format which seemed to work well. However, when I display the aircraft models in Cesium the models are either pointing nose up or nose down and either left or right wing towards the assigned heading (based on which transform I use).

I don’t have any experience (or understanding) related to using the transforms. I’ve tried all 4 of the built in transforms (eastNorthUpToFixedFrame, northEastDownToFixedFrame, etc.), but none of them provide the desired results. For instance here’s what I get with northWestUpToFixedFrame…


The models are all oriented in the same manner. So is there a method I can use to “transform” these models when I load them into Cesium so that their noses are pointing toward the assigned heading and they are oriented “as expected”?

I really appreciate any help with this!


I’ve been reading every post I can find on modifying a model’s orientation. I just have no experience here, and so the posts I’ve found don’t make sense to me.

It seems I need a method to rotate/transform heading by -90 degrees and pitch by -90 degrees. Is there a document somewhere that explains how to modify the orientation of a 3D model? Of course a simple example would be awesome.

If you’re loading it as an entity you should just be able to set its orientation using new Cesium.HeadingPitchRoll.

Here’s a quick Sandcastle example. Is that what you’re looking for?

Thank you for the response. But I don’t think I can make use of this method since I am loading the model via Cesium.Model.fromGLTF as follows:

var modelMatrix = Cesium.Transforms.northEastDownToFixedFrame(Cesium.Cartesian3.fromDegrees(0,0,0));

    this.Model = Cesium.Model.fromGltf({

        url: "resources/models/" + strModelId + "-hr.gltf",


incrementallyLoadTextures: false,



colorBlendMode: Cesium.ColorBlendMode.MIX,

colorBlendAmount: 0.0,

        cacheKey: Math.random()     



I’ve tried to use a HPR transform to modify the orientation as follows but this did not work:

    var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);

var heading = 0.0;

var pitch = Cesium.Math.toRadians(30.0);

var roll = 0.0;

var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);

var transform = Cesium.Transforms.headingPitchRollToFixedFrame(center, hpr);

this.Model = Cesium.Model.fromGltf({

url: “resources/models/” + strModelId + “-hr.gltf”,


incrementallyLoadTextures: false,



colorBlendMode: Cesium.ColorBlendMode.MIX,

colorBlendAmount: 0.0,

cacheKey: Math.random()



Based on other posts I’m assuming I’m going to need to use Cesium.Matrix4.Multiply with a modified transform somehow… but I haven’t figured it out.

Your second example is almost correct!

This should be a working Sandcastle of rotating the primitive using a model matrix.


Thank you for the response and the example. I am assuming that the code in the readyPromise only has to do with positioning the camera after the model is loaded. So it appears the code that rotates the model is

var hpRoll = new Cesium.HeadingPitchRoll(Math.PI/2, Math.PI/2, 0);

var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 5000.0);

var fixedFrameTransform = Cesium.Transforms.localFrameToFixedFrameGenerator(‘north’, ‘west’);

var planePrimitive = scene.primitives.add(Cesium.Model.fromGltf({

  url : '',

  modelMatrix : Cesium.Transforms.headingPitchRollToFixedFrame(position, hpRoll, Cesium.Ellipsoid.WGS84, fixedFrameTransform),

  minimumPixelSize : 128



When I implement this code it does not change the rotation of my models. The aircraft models are still rotated with nose down and left wing pointed toward what should be the heading.

Are there any other switches/settings that I need to adjust?

My code now looks like this:

var hpr = new Cesium.HeadingPitchRoll(-90.0

  • Cesium.Math.RADIANS_PER_DEGREE, -90.0 * Cesium.Math.RADIANS_PER_DEGREE, 0.0);

var position =
Cesium.Cartesian3.fromDegrees(0, 0, 0);

var fixedFrameTransform =
Cesium.Transforms.localFrameToFixedFrameGenerator(‘north’, ‘west’);

var modelMatrix =
Cesium.Transforms.headingPitchRollToFixedFrame(position, hpr,
Cesium.Ellipsoid.WGS84, fixedFrameTransform);

this.Model = Cesium.Model.fromGltf({

url: “resources/models/”

  • strModelId + “-hr.gltf”,

modelMatrix: modelMatrix,

incrementallyLoadTextures: false,

scale: this.ModelSize,

minimumPixelSize: 5.0,


colorBlendAmount: 0.0,

cacheKey: Math.random()



I tried to point the Sandcastle example to one of the models I’m working with, but it seems I’m not able to do that. The model is currently (temporarily) located at if you would like to test with it.

I really wish I understood more about the matrices and transforms. It seems like this must be possible and I must be doing something wrong.

Thanks for your help with this…


Thanks for sharing your model Rob!

So it doesn’t work in Sandcastle just because the URL to your model is HTTP whereas Sandcastle is running in HTTPS.

I just tried it locally, and here’s what it looks like with

And with new Cesium.HeadingPitchRoll(Math.PI/2, Math.PI/2, 0)

If it’s not changing your model at all, I wonder if there’s something else overriding the transform. The code you have initializes the model. Are you then adding it to the scene afterwards? Does anything else happen to it?

Well I shouldn’t make assumptions… You are right. I didn’t realize that the code we use to position the models had been modified at some point.

Below is the code that is being used to position the models. This code has been used for previous models that do not have the rotational problem I’ve described in this thread. I would like to replace these old models with the new models (that unfortunately have this rotational issue).

I have been experimenting with it to try to get the model rotations corrected, but so far I have not been able to come up with the correct rotation. Do you have suggestions on how I should modify or replace this code?

var position = Cesium.Cartesian3.fromDegrees(lng, lat, alt, scene.globe.ellipsoid, new Cesium.Cartesian3());

var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(position);

var currentTranslation = new Cesium.Cartesian3();

var currentRotation = new Cesium.Matrix3();

Cesium.Matrix4.getRotation(modelMatrix, currentRotation);

position = Cesium.Cartesian3.fromDegrees(lng, lat, alt, scene.globe.ellipsoid, new Cesium.Cartesian3());

modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(position);

Cesium.Matrix4.getTranslation(modelMatrix, currentTranslation);

var rotateQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_Z, -heading);

var turnQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_X, -pitch);

var rollQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_Y, -roll);

var finalQuat = Cesium.Quaternion.multiply(rotateQuat, turnQuat, new Cesium.Quaternion());

Cesium.Quaternion.multiply(finalQuat, rollQuat, finalQuat);

var rM = new Cesium.Matrix3();

Cesium.Matrix3.fromQuaternion(finalQuat, rM);

Cesium.Matrix3.multiply(currentRotation, rM, currentRotation);







So that code is actually a pretty good example of updating a model rotation. The steps are:

  1. Construct the quaternions with the angle you want to rotate for each axis you want to rotate around.
  2. Combine all the quaternions into one by multiplying them together.
  3. Construct a rotation matrix from the quaternions.
  4. Multiply the model matrix by this rotation matrix.
    So you can see how that code constructs 3 quaternions for heading,pitch and roll. If you change those values you should be able to rotate however you like.

Another way if you are just given a model with a precomputed modelMatrix, and you want to apply an additional rotation, you would just follow the steps I outlined above:

// Construct the quaternions for the rotation along each axis

// A quaternion encodes a rotation around an axis by a given angle

var rotateQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_Z, -Math.PI/2);

var turnQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_X, -Math.PI/2);

// Combine all our quaternions

// Careful! The order you multiple them in matters.

var finalQuat = Cesium.Quaternion.multiply(turnQuat,rotateQuat, new Cesium.Quaternion());

// Now we construct a rotation matrix out of our quaternion

var rM = new Cesium.Matrix3();

Cesium.Matrix3.fromQuaternion(finalQuat, rM);

// And multiply the model matrix by this rotation matrix

Cesium.Matrix4.multiplyByMatrix3(Model.modelMatrix, rM, Model.modelMatrix);


Here I modified the example from before to apply this additional rotation after 5 sections.


Thank you very much for the detailed response and explanation. I’m very hopeful that your explanation will allow me to work through this issue!

My first attempt was to simply leave the code we’ve been using for positioning the model and add the additional rotation you described at the end. This definitely changes the models but results in the models being upside down and tails pointing in the direction of the heading as seen here (the green wall is a trail/curtain following the aircraft):


I want to ask about the descriptions you used for the quaternions. You called one “rotateQuat” which was from the UNIT_Z axis and the other “turnQuat” from the UNIT_X axis. How do these relate to heading, pitch, roll? Is the “rotateQuat” (UNIT_Z) the heading and “turnQuat” (UNIT_X) the pitch?

The code we’re currently using is assigning heading to UNIT_Z, pitch to UNIT_X and roll to UNIT_Y.

Thanks for all your help with this!

I’m glad my response was helpful!

The names for the quaternions in my code were just copied from the example you posted. Since this is applying an additional rotation it’ll be relative the current orientation. I would experiment with each axis individually to see which rotations you need (and note the order you’re combining them since applying rotation A followed by B is not the same as B followed by A).

So I received an email from Gabby with another approach for modifying the modelMatrix. She thought that I should be able to only adjust the heading, but that did not work. Instead I had to apply adjustments to both heading and pitch… and the value I had to use to adjust the heading doesn’t make sense to me. However, I have been able to get the models rotated “correctly” using the following code. I have added the following code as an additional step following the code I posted earlier that we have been using to position the models.

var heading2 = Cesium.Math.toRadians(-270);

var pitch2 = Cesium.Math.toRadians(-90);

var hpr = new Cesium.HeadingPitchRoll(heading2, pitch2, 0);

var rotation = Cesium.Matrix3.fromHeadingPitchRoll(hpr);

Cesium.Matrix4.multiplyByMatrix3(model.modelMatrix, rotation, model.modelMatrix);


Thank you very much for all of the help with this! I am slowly gaining some insights into the transforms and translations and matrices, etc.