Set camera lookAt from a heading, tilt and roll (moving from Google Earth to Cesiumjs)

On my attempt to port my project (youbeq) from Google Earth to Cesiumjs I've encountered some difficulties that are preventing me from moving forward.

You can check out a part of what is already running on Cesium: http://dev.youbeq.com/Apps/Sandcastle/gallery/FollowModelCameras.html

If you check that example you can see that I have a vehicle you can control and you can change cameras as well as look left/right/back.

In Google Earth you can control the camera position and orientation by setting the LatLngAlt (for position) and Heading, Tilt and Roll (for orientation), in Cesiumjs it is a completely different setup and I can't seem to find the correct way to accomplish the same behaviour. What I was doing was pretty simple, given the current vehicle position I would find the camera position according to an offset for the xyz axis, and set the HTR of that particular camera.

From the LatLngAlt I already have I can position the camera, what I can't seem to get right is setting the orientation of the camera given the HTR I have. How can I rotate the camera correctly?

I tried adapting the code from here https://github.com/AnalyticalGraphicsInc/cesium/issues/1133 but that didn't work that well.
This is what I have right now, it just positions the camera and attempts to set the heading:

// code snippet ---------->
var position = Cesium.Cartesian3.fromDegrees(lng, lat, cameraAlt, ellipsoid, new Cesium.Cartesian3());

var transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
camera.transform = transform;

var yDir = Cesium.Matrix4.multiplyByPointAsVector(bodyModel.modelMatrix, Cesium.Cartesian3.UNIT_Y, new Cesium.Cartesian3());
Cesium.Matrix4.multiplyByPointAsVector(this.camera.inverseTransform, yDir, yDir);
Cesium.Cartesian3.negate(yDir, yDir);

var rZ = Cesium.Matrix3.fromRotationZ(getVehicleHeading() - cameraHeading, new Cesium.Matrix3());

var rotatedZ = new Cesium.Cartesian3();
Cesium.Matrix3.multiplyByVector(rZ, yDir, rotatedZ);

Cesium.Cartesian3.add(yDir, rotatedZ, yDir);

camera.lookAt(
                    yDir,
                    Cesium.Cartesian3.ZERO,
                    Cesium.Cartesian3.UNIT_Z
);
// <---------- code snippet

If anyone can give me some help in finding the correct way to accomplish this type of movement it would be greatly appreciated.

If you want to position the camera at some distance along the model’s y-axis, you probably want to multiply by some offset distance. Your yDir looks correct, but if you use it as the eye position argument to lookAt it will only be 1 meter away from bodyModel’s center (assuming that the model is at the centered around the origin in model coordinates). I would add this:

Cesium.Cartesian3.normalize(yDir, yDir); // yDir’s magnitude might not be exactly 1 after the rotation

var eyePos = Cesium.Cartesian3.multiplyByScalar(yDir, distanceFromModel, new Cesium.Cartesian3());

If you want to set the heading and tilt after the camera position is set, try:

var transform = camera.transform;

camera.setTransform(Cesium.Matrix4.IDENTITY);

camera.heading = heading;

camera.tilt = tilt;

camera.setTransform(transform);

This last part setting the heading and tilt may not be exactly what you want. If it is, I’ll explain what’s going on with setTransform. If not, let me know and I’ll try to better understand what you are trying to do.

The lat, lng, alt that I use to find the position already has the distance offset taken into account.

With that approach I can correctly replicate what I had on the GE Plugin, but now I'm missing the roll. I need the Camera to have the same roll (or almost the same) as the vehicle it's following. How could I apply a roll to the camera?

So what is going on with setTransform?

Quinta-feira, 18 de Setembro de 2014 18:26:40 UTC+1, Daniel Bagnell escreveu:

There is a transform property and setTransform method on the Camera. Changing the transform property only changes a matrix internally. The camera position and orientation are then interpreted in that reference frame. Its assumed that the user will set them after changing transform either directly or through something like lookAt. The setTransform method changes the transform matrix and it also changes the position and orientation of the camera. Essentially, it multiplies the position and orientation by the current transform and then the inverse of the new transform.

The heading and and tilt properties only make sense if the camera is in world coordinates (transform is set to the identity matrix). So the first setTransform transforms the position and orientation to world coordinates, then we apply the heading and tilt, and, finally, revert the position and orientation back to the desired transform.

We should add a roll property similar to heading and tilt. Have you tried camera.twist(angle)? That could be called roll. It rotates the orientation vectors around the camera’s direction. You might have to keep track of the current roll and modify the angle parameter based on the current roll angle and new roll angle.

I see, thanks for the info :slight_smile:

Didn't notice that function before, with it I can correctly set the roll. I guess since I'm resetting the camera transform I don't need to keep track of the Δroll.

Thanks for the help.

Quinta-feira, 18 de Setembro de 2014 20:07:12 UTC+1, Daniel Bagnell escreveu:

Didn't notice before, but I was checking the tilt of the camera and it only accepts values from 0 to PI*0.5, and only allows me to look down. If I'm flying around with an airplane I want the camera to always have the plane in sight no matter the pitch. How can I achieve that?

Quinta-feira, 18 de Setembro de 2014 18:26:40 UTC+1, Daniel Bagnell escreveu:

You can see the implementation for tilt here. You can remove the clamp to [0, pi / 2]:

var angle = tiltAngle - camera.tilt;

camera.look(camera.right, angle);

I see, that way it works as expected.
What was the reason for clamping the value in the set implementation?

The tilt angle was clamped because we didn’t want users to look up at the sky (mouse interaction only worked when the globe could be picked) or to have “upside down” views. We recently added the ability to interact with the sky and the clamping should really be done at the app level if that behavior is desired (e.g. for a navigation widget). I’ll submit a pull request removing the clamp.

Hi Andre,

For the heading, tilt and roll, starting in version 1.6 you should be able to do:

camera.setView({

heading : cameraHeading,

pitch : cameraTilt,

roll : cameraRoll

});

For details see https://groups.google.com/forum/#!topic/cesium-dev/2-Fn_8yeurY.

Thanks,

Dan