Local to Global Coordinates

I’m creating a naval warfare game that uses CesiumJS to show the 3d battlefield. I’m trying to launch weapons and aircraft from ships. But, right now, they launch from the centre of the ship.

I’d like to able to translate local model coordinates to pick a point on the ship (flight deck or a weapons launcher) where the aircraft or weapons would be launched. This would be similar to computing the local emitter model matrix when we create particle systems.

Can you give me some advice or point me to a resource where I can figure out how to translate the local coordinates back to global coordinates, which would be the point where the subunits (aircraft/weapon) would be created, taxi to, and finally, launch from?

Thank you!

Hi Luan,

If you multiply the model’s modelMatrix by the local coordinates that should give you the global coordinates. If that doesn’t work for whatever reason, try using model._computedMatrix instead.

Also note that the glTF model is probably y-up, so you’ll want to define the local coordinates as y-up.

1 Like

Thanks for all your help Sean!

I am currently facing an issue where the direction of the gltf model is different from the local coordinates
How to define local coordinates as y up?

Hi @lvfeijian ,

Thanks for your post. Just to confirm, are you working in CesiumJS?

If yes, the best approach may be to convert the glTF model coordinate system from Y-up to Z-up. You can achieve this by rotating the model by 90 degrees around the X-axis (or whichever axis you need to match the glTF model to Cesium’s Z-up coordinate system).

Here is some sample code demonstrating how to do this.

const rotationMatrix = Cesium.Matrix4.fromRotationTranslation(
    Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(90)) // Rotate 90 degrees around the X-axis
  );
  model.modelMatrix = Cesium.Matrix4.multiply(model.modelMatrix, rotationMatrix, new Cesium.Matrix4());

Does this approach help solve your problem? Please let me know if not and I would be happy to try and further assist.

Best,
Luke

@Luke_McKinstry
Hello, developer.
I’m glad to receive your reply
I am using Cesium, and I used your code to convert the coordinate system of the gltfF model from y up to z up. But there are still some issues.
Let me first describe my requirements to you. I used Cesium to render the GLTF model, and I hope to find the position coordinates of a component of the model when clicking on it. But I found that the coordinates I was looking for were incorrect, because of a problem with the coordinate system
Here is my code

<template>
  <div>
    <div id="cesiumContainer"></div>

  </div>
</template>

<script setup lang="ts">
import { onMounted, ref, reactive, watch,nextTick,computed } from "vue";
import { lngLatHeightFromCartesian } from '@/lib/cesiumUtils'

// 模型初始化位置
let modelPosition = reactive({
  lng: 113.57148,
  lat: 22.76804,
  height: 0
})
// header pitch和roll的值,单位是度
const viewModel = {
  header: 0,
  pitch: 0,
  roll: -90
}
onMounted(() => {
  init()
  clickGo()
})
async function init(){
  const position = Cesium.Cartesian3.fromDegrees(
    modelPosition.lng,
    modelPosition.lat,
    modelPosition.height
  );
  const viewer = new Cesium.Viewer("cesiumContainer", {
    shouldAnimate: true
  });
  window.viewer = viewer;

  const url = "/api/draco/无标题.glb";
  const model = viewer.scene.primitives.add(
    await Cesium.Model.fromGltfAsync({
      url: url,
      modelMatrix: Cesium.Transforms.headingPitchRollToFixedFrame(
        position,
        new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(viewModel.header), Cesium.Math.toRadians(viewModel.pitch), Cesium.Math.toRadians(viewModel.roll)),
      ),
    }),
  );
  window.model = model
  console.log(model.modelMatrix,'model.modelMatrix');

  const rotationMatrix = Cesium.Matrix4.fromRotationTranslation(
    Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(90)) // Rotate 90 degrees around the X-axis
  );
  model.modelMatrix = Cesium.Matrix4.multiply(model.modelMatrix, rotationMatrix, new Cesium.Matrix4());
  console.log(model.modelMatrix,'model.modelMatrix');

  model.readyEvent.addEventListener(() => {
    viewer.camera.flyToBoundingSphere(model.boundingSphere, {
      duration: 0.0,
    });

  })
}
// 点击获取位置
function clickGo() {
  const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

  handler.setInputAction(function (movement) {

    const modelMatrix = model.modelMatrix; // 获取模型的4×4变换矩阵

    const pick = viewer.scene.pick(movement.position);
    pick.detail.node.show = false
    console.log(pick.detail.node.transform);
    var resultMatrix = new Cesium.Matrix4();
    Cesium.Matrix4.multiply(modelMatrix,pick.detail.node.transform,resultMatrix)

    const position = new Cesium.Cartesian3();
    Cesium.Matrix4.getTranslation(resultMatrix,position)
    console.log(position,'position',lngLatHeightFromCartesian(position));

    var entity = viewer.entities.add({
      position: position,
      ellipsoid: {
          radii: new Cesium.Cartesian3(1,1,1), // 球体的半径
          material: Cesium.Color.RED.withAlpha(0.5) // 球体的颜色和透明度
      }
    });

  }, Cesium.ScreenSpaceEventType.LEFT_CLICK);


}
</script>```

@lvfeijian,

Thanks for your follow up post. Is it possible for you to share sample code demonstrating your issue using our sandcastle tool https://sandcastle.cesium.com/?

Having a working example in sandcastle that reproduces the issue would make it easier for us to understand it and help debug.

Please let us know.
Thanks,
Luke

I’m hesitating to post this response here, because I cannot really explain it (although I should). But I think that this is what you are looking for…

Cesium Forum 6560 position picking from glTF

And here is the sandcastle:

I cannot explain that rotationMatrix there. I thought that it was related to the “axis correction matrix”, but didn’t do the math with pen and paper yet - maybe someone can quickly chime in here.


An aside: You are currently using a roll of -90 to compute the model matrix, and then essentially cancelling this out with the rotation matrix. That may just be an artifact of the attempt to handle the coordinate transforms. So I think that this part could be omitted, and the sandcastle could be simplified to this:

@Marco13 Thank you very much for your reply
Yes, your effect is exactly what I want.
Actually, I have already implemented this feature before asking you. I don’t know what knowledge I should learn to understand this problem
Can you give me some advice,very thanks

I have read some materials to understand this knowledge. Actually, it’s about converting the gltf coordinate system to the cesium coordinate system。

Yes, this matrix is handling the coordinate system conversion between 3D Tiles and glTF.

I now checked this again: The rotationMatrix from the sandcastle that I posted is indeed equal to the “axis correction matrix” that is applied internally: For upAxis=Y and forwardAxis=Z, the matrix will be
m = Y_UP_TO_Z_UP * Z_UP_TO_X_UP

When printing this (as a flat 16-element array), then it will be
0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,1

The rotationMatrix that I defined in the sandcastle was
rotationMatrix = X_UP_TO_Y_UP * Y_UP_TO_Z_UP
but this happens to be the same as
rotationMatrix = Y_UP_TO_Z_UP * Z_UP_TO_X_UP (i.e. the axis correction matrix)

Both of them are
0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,1

Given that all these axis conversion matrices are just rotations about +/- 90 degrees, there are many cases where the multiplication of two different conversion matrices yield the same result. But as I said: I didn’t go through the math with pen and paper here.


An aside, regarding the screenshot that you posted: You probably noticed the difference:
0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,1 ← the matrix from the screenshot
0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,1 ← the axis correction matrix
But you are using that matrix in a different way: You are multiplying this with the node.transform and the model matrix. The model matrix here contains an additional axis conversion, because it involves a HeadingPitchRoll with a roll of -90 degrees. And the node.transform could contain another rotation. So it’s really difficult to understand what is actually happening there, and it’s diffucult to ensure that this works for all node.transform matrices and all model matrices.

I think that the approach from the last sandcastle that I posted should be relatively easy, and I think that it should work in all cases. In a real application, I would probably use
rotationMatrix = Y_UP_TO_Z_UP * Z_UP_TO_X_UP
and write a short comment about the reason for this (maybe even with a link to the “axis correction matrix” code), just to avoid confusion for future readers.

Well… maybe not to avoid confusion. These axis conversions always are confusing :laughing: But maybe it helps to reduce the confusion :slightly_smiling_face:

@Marco13
Yes, using YUP_TOZ-UP * Z-UP_TO_X_UP is easier to understand. Just compare it with the gltf and cesium coordinate systems in the picture above to see why.
I also support using your writing style. I calculated the model matrix myself because I didn’t know it could be written this way before, but this may require some knowledge of matrices
Thank you very much for your suggestion