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/FollowModel.html
If you check that example you can see that I have a vehicle you can control around the world with different components "attached" to it, like the wheels.
Unfortunately I can't seem to find the correct way to position them according to the position of the vehicle (lat, lng, alt, heading, tilt, roll), and in the example, since the wheels are spinning, the roll affects the other transformations in a bad way.
What I was doing in GE was constructing a Quaternion with the heading, tilt and roll of the vehicle:
// code snippet ----->
// helper object ---->
Quaternion = function (x, y, z, w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
this.mul = function (q) {
var _x = this.w * q.x + this.x * q.w + this.y * q.z - this.z * q.y;
var _y = this.w * q.y + this.y * q.w + this.z * q.x - this.x * q.z;
var _z = this.w * q.z + this.z * q.w + this.x * q.y - this.y * q.x;
var _w = this.w * q.w - this.x * q.x - this.y * q.y - this.z * q.z;
return Quaternion(_x, _y, _z, _w)
};
this.get_eulerAngles = function () {
var h;
var p;
var b;
var sp = -2 * (this.z * this.y - this.w * this.x);
if (Math.abs(sp) > .998) {
p = Math.PI / 2 * sp;
h = Math.atan2(-this.x * this.y + this.w * this.z, .5 - this.z * this.z - this.y * this.y);
b = 0
} else {
p = Math.asin(sp);
h = Math.atan2(this.x * this.y + this.w * this.z, .5 - this.x * this.x - this.z * this.z);
b = Math.atan2(this.x * this.z + this.w * this.y, .5 - this.x * this.x - this.y * this.y)
}
return EulerAngles(h, p, b)
};
};
Quaternion.euler = function (heading, pitch, bank) {
var hq = Quaternion.angleAxis(heading, new Vector3(0, 0, 1));
var pq = Quaternion.angleAxis(pitch, new Vector3(1, 0, 0));
var bq = Quaternion.angleAxis(bank, new Vector3(0, 1, 0));
return bq.mul(pq).mul(hq)
};
Quaternion.angleAxis = function (radian, axis) {
var h = radian / 2;
var s = Math.sin(h);
return Quaternion(s * axis.x, s * axis.y, s * axis.z, Math.cos(h))
};
//<----- helper object
var qVehicle = Quaternion.euler(heading, tilt, roll);
//<----- code snippet
Then I would find the new LatLngAlt of that component according to the offset in the xyz axis and the orientation multiplying the heading, tilt and roll of the component with the Quaternion of the vehicle:
// code snippet ----->
//helper function ---->
getLatLngAlt = function (x, y, z) {
var p = new Vector3(x, y, z);
var m = new Matrix3x3;
m.setRotationY(-vehicleRoll.position); // vehicleRoll contains the current roll value for the vehicle
p.multiply(m);
m.setRotationX(-vehicleTilt.position); // vehicleTilt contains the current tilt value for the vehicle
p.multiply(m);
var angle = -Math.atan2(p.y, p.x) + 1.570796326794897;
var length = lengthVector(p.x, p.y); // returns the vector length
var latLng = vehiclePosition.createOffset(getVehicleHeading() + angle, length); // vehiclePosition contains the current LatLngAlt of the vehicle. CreateOffset will compute the new location according to the direction (heading) and distance (length)
var alt = 0;
alt = vehicleAltitude.position + p.z; // vehicleAltitude contains the current altitude value for the vehicle
return new LatLngAlt(latLng.lat(), latLng.lng(), alt);
};
//<------- helper function
var latLngAlt = getLatLngAlt(x, y, z);
var q1 = Quaternion.angleAxis(wheelHeading, new Math3D.geometry.Vector3(0, 0, 1));
var q2 = Quaternion.angleAxis(wheelTilt+wheelSpeedForward, new Vector3(1, 0, 0));
var e = q2.mul(q1).mul(qVehicle).get_eulerAngles();
var pitch = e.pitch;
var bank = e.bank;
var heading = e.heading;
//<----- code snippet
With this I could position the components and they kept "attached" to the main body. In the Google Earth Plugin everything works by setting LatLngAlt and the heading, tilt and roll.
In Cesium I attempted to accomplish the same with no success, when I try to generate a rotation matrix from the computed Quaternion and apply it to the Model Matrix all hell breaks lose:
// code snippet ----->
//helper function ---->
var euler = function (heading, pitch, bank) {
var hq = new Cesium.Quaternion();
Cesium.Quaternion.fromAxisAngle(new Cesium.Cartesian3(0, 0, 1), heading, hq);
var pq = new Cesium.Quaternion();
Cesium.Quaternion.fromAxisAngle(new Cesium.Cartesian3(1, 0, 0), pitch, pq);
var bq = new Cesium.Quaternion();
Cesium.Quaternion.fromAxisAngle(new Cesium.Cartesian3(0, 1, 0), bank, bq);
var bpq = new Cesium.Quaternion();
Cesium.Quaternion.multiply(bq, pq, bpq);
var bphq = new Cesium.Quaternion();
Cesium.Quaternion.multiply(bpq, hq, bphq);
return bphq;
};
//<------- helper function
var qVehicle = euler(heading, tilt, roll);
var q1 = new Cesium.Quaternion();
Cesium.Quaternion.fromAxisAngle(new Cesium.Cartesian3(0, 0, 1), wheelHeading, q1);
var q2 = new Cesium.Quaternion();
Cesium.Quaternion.fromAxisAngle(new Cesium.Cartesian3(1, 0, 0), wheelTilt+wheelSpeedForward, q2);
var q21 = new Cesium.Quaternion();
Cesium.Quaternion.multiply(q2, q1, q21);
var q21v = new Cesium.Quaternion();
Cesium.Quaternion.multiply(q21, qVehicle, q21v);
var currentTranslation = new Cesium.Cartesian3();
var position = Cesium.Cartesian3.fromDegrees(lng, lat, alt, scene.globe.ellipsoid, new Cesium.Cartesian3());
var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(position);
Cesium.Matrix4.getTranslation(modelMatrix, currentTranslation);
var currentRotation = new Cesium.Matrix3.fromQuaternion(q21v);
Cesium.Matrix4.fromRotationTranslation(
currentRotation,
currentTranslation,
model.modelMatrix
);
//<----- code snippet
and if I apply heading, tilt and roll to the current model matrix rotation I have the issue of the roll messing with the heading and tilt:
// code snippet ----->
//helper function ---->
var rotateMatrix = function(mat, h, t, r){
var mh, mt;
// Heading
if (h === 0) {
mh = Cesium.Matrix3.IDENTITY;
} else {
mh = Cesium.Matrix3.fromRotationZ(-h);
}
// Tilt
if (t !== 0) {
mt = Cesium.Matrix3.fromRotationX(-t);
} else {
// TBD
mt = Cesium.Matrix3.IDENTITY;
}
// Taken from https://github.com/AnalyticalGraphicsInc/cesium/issues/1133
// perform these two rotations so that we can determine the direction vector for roll
Cesium.Matrix3.multiply(mat, mh, mat);
Cesium.Matrix3.multiply(mat, mt, mat);
// Roll (-180 < 0 < 180)
// Roll around the calculated direction axis
if (r !== 0) {
var right = new Cesium.Cartesian3();
Cesium.Matrix3.getColumn(mat, 0, right);
var up = new Cesium.Cartesian3();
Cesium.Matrix3.getColumn(mat, 1, up);
var direction = new Cesium.Cartesian3();
Cesium.Matrix3.getColumn(mat, 2, direction);
var rad = r;
var mr = Cesium.Matrix3.fromQuaternion(Cesium.Quaternion.fromAxisAngle(direction, rad));
Cesium.Matrix3.multiplyByVector(mr, right, right);
Cesium.Matrix3.multiplyByVector(mr, up, up);
Cesium.Matrix3.multiplyByVector(mr, direction, direction);
Cesium.Matrix3.setColumn(mat, 0, right, mat);
Cesium.Matrix3.setColumn(mat, 1, up, mat);
Cesium.Matrix3.setColumn(mat, 2, direction, mat);
}
return mat;
};
//<------- helper function
var currentTranslation = new Cesium.Cartesian3();
var position = Cesium.Cartesian3.fromDegrees(lng, lat, alt, scene.globe.ellipsoid, new Cesium.Cartesian3());
var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(position);
Cesium.Matrix4.getTranslation(modelMatrix, currentTranslation);
var currentRotation = new Cesium.Matrix3();
Cesium.Matrix4.getRotation(initialModelMatrix, currentRotation);
// Apply rotation to model
currentRotation = rotateMatrix(currentRotation, heading, tilt, roll);
Cesium.Matrix4.fromRotationTranslation(
currentRotation,
currentTranslation,
model.modelMatrix
);
//<----- 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.