0

I'm using jsc3d to load and display some 3d objects on a canvas. The viewer has already a built-in feature that allows to rotate the "view coordinates" (correct me if i'm wrong) about the Y axis by dragging the mouse.

The rotation is performed through a classic rotation matrix, and finally the trasformation matrix is multiplied by this rotation matrix.

The totation about the Y axis is calculated in a way that resembles a circular movement around the whole scene of loaded objects:

JSC3D.Matrix3x4.prototype.rotateAboutYAxis = function(angle) {
    if(angle != 0) {
        angle *= Math.PI / 180;
        var c = Math.cos(angle);
        var s = Math.sin(angle);

        var m00 = c * this.m00 + s * this.m20;
        var m01 = c * this.m01 + s * this.m21;
        var m02 = c * this.m02 + s * this.m22;
        var m03 = c * this.m03 + s * this.m23;
        var m20 = c * this.m20 - s * this.m00;
        var m21 = c * this.m21 - s * this.m01;
        var m22 = c * this.m22 - s * this.m02;
        var m23 = c * this.m23 - s * this.m03;

        this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m03 = m03;
        this.m20 = m20; this.m21 = m21; this.m22 = m22; this.m23 = m23;
    }
};

Now, dragging the mouse will apply this rotation about the Y axis on the whole world, like on the left side in the picture below. Is there a way, to apply a rotation about the Up vector to keep it in the initial position, like it appear on the right side?

enter image description here

I tried something like that:

var rotY = (x - viewer.mouseX) * 360 / viewer.canvas.height;       
var rotMat = new JSC3D.Matrix3x4; // identity
rotMat.rotateAboutYAxis(rotY);
viewer.rotMatrix.multiply(rotMat);

but it has no effect.

What operations shall be applied to my rotation matrix to achieve a rotation about the Up vector?

Sample: https://jsfiddle.net/4xzjnnar/1/

deblocker
  • 7,629
  • 2
  • 24
  • 59

1 Answers1

0

This 3D library has already some built-in functions to allow scene rotation about X,Y,and Z axis, so there is no need to implement new matrix operations for that, we can use the existing functions rotateAboutXAyis, rotateAboutYAxis and rotateAboutZAxis, which apply an in-place matrix multiplication of the desired rotation angle in degrees.

The scene in JSC3D is transformed by a 3x4 matrix where the rotation is stored in the first 3 values of each row. After applying a scene rotation and/or translation, applying a subsequent rotation about the Up vector, is a problem of calculate a rotation about an arbitrary axis.

A very clean and didactic explanation how to solve this problem is described here: http://ami.ektf.hu/uploads/papers/finalpdf/AMI_40_from175to186.pdf

  1. Translate the P 0 (x 0 ,y 0 ,z 0 ) axis point to the origin of the coordinate system.
  2. Perform appropriate rotations to make the axis of rotation coincident with z-coordinate axis.
  3. Rotate about the z-axis by the angle θ.
  4. Perform the inverse of the combined rotation transformation.
  5. Perform the inverse of the translation.

Now, its easy to write a function for that, because we use the functions already available in JSC3D (translation part is omitted here).

JSC3D.Viewer.prototype.rotateAboutUpVector = function(angle) {
    angle %= 360;

    /* pitch, counter-clockwise rotation about the Y axis */

    var degX = this.rpy[0], degZ = this.rpy[2];
    this.rotMatrix.rotateAboutXAxis(-degX);
    this.rotMatrix.rotateAboutZAxis(-degZ);
    this.rotMatrix.rotateAboutYAxis(angle);
    this.rotMatrix.rotateAboutZAxis(degZ);
    this.rotMatrix.rotateAboutXAxis(degX);

}

Because all above mentioned functions are using degrees, we need to get back the actual Euler angles from the rotation matrix (simplified):

JSC3D.Viewer.prototype.calcRollPitchYaw = function() {
    var m = this.rotMatrix;
    var radians = 180 / Math.PI;

    var angleX = Math.atan2(-m.m12, m.m22) * radians;
    var angleY = Math.asin(m.m01) * radians;
    var angleZ = Math.atan2(-m.m01, m.m00) * radians;

    this.rpy[0] = angleX;
    this.rpy[1] = angleY;
    this.rpy[2] = angleZ;
}

The tricky part here, is that we need always to get back the current rotation angles, as they results from the applied rotations, so a separate function must be used to store the current Euler angles every time that a rotation is applied to the scene.

For that, we can use a very simple structure:

JSC3D.Viewer.prototype.rpy = [0, 0, 0];

This will be the final result:

enter image description here

deblocker
  • 7,629
  • 2
  • 24
  • 59