Rotate axis X produces weird rotation values

The rotate method produces weird rotation values.

In this example I rotate along X 90 deg

// Same for global or local.
group.rotate(Axis.X, Math.PI / 2, Space.Local);

and this is the result:

+ 90
expected: x: 90 y: 0 z: 0 
actual:   x: 90 y: 0 z: 0

+ 90
expected: x: 180 y: 0   z: 0 
actual:   x: 0   y: 180 z: 180

+ 90
expected: x: 270 y: 0   z: 0 (-90 is also ok it seems to be -180..180, but why 360?)
actual:   x: -90 y: 360 z: 0

+90
expected: x: 0 y: 0                       z: 0 
actual:   x: 0 y: -1.4033418597069754e-14 z: 0 (rounding error?)

and so on ...
  1. Why is rotation not updated automatically when using rotate? It should be, because this is the actual rotation of the object. How to get the actual rotation (normalized) of the object after rotate()? And how to set/get the absolute rotation in world space?

  2. Why 360 deg and not 0 deg?

  3. Why this random-looking values?

:arrow_forward: Playground

const createScene = () => {
    const scene = new BABYLON.Scene(engine);
    const camera = new BABYLON.TargetCamera("camera", new BABYLON.Vector3(0, 5, -10), scene);
    camera.setTarget(BABYLON.Vector3.Zero());
    new BABYLON.HemisphericLight("light", new BABYLON.Vector3(2, 1, -2), scene);

    const group = new BABYLON.TransformNode('group', scene);
    const box1 = BABYLON.MeshBuilder.CreateBox("box1", { width: 1, height: 1, depth: 1 }, scene);
    const box2 = BABYLON.MeshBuilder.CreateBox("box2", { width: 0.5, height: 2, depth: 0.5 }, scene);
    box2.position.y += 0.5;
    box1.parent = group
    box2.parent = group

    // The playground is running without reloading. Previous timer remains active. Therefore clear first.
    window.clearInterval(window._intervalId ?? 0);
    let expectedValue = 0;
    window._intervalId = window.setInterval(() => {
        // Same for local and global space.
        expectedValue = (expectedValue + Math.PI / 2) % (Math.PI * 2)
        group.rotate(BABYLON.Axis.X, Math.PI / 2, BABYLON.Space.Local);
        const euler = group.rotationQuaternion.toEulerAngles()
        group.rotation = new BABYLON.Vector3(euler.x, euler.y, euler.z);
        console.log(
            `expected:`,
            `x: ${rad2deg(expectedValue)}`,
            `y: 0`,
            `z: 0`,
            `\nactual:  `,
            `x: ${rad2deg(group.rotation.x)}`, 
            `y: ${rad2deg(group.rotation.y)}`, 
            `z: ${rad2deg(group.rotation.z)}`
        );
    }, 1000);
    
    return scene;
};

function rad2deg(x) {
    return x * 180 / Math.PI
}

// PLEASE SEE ON CONSOLE!

At the end of the day I want to get and set absolute rotation values. I want to rotate in world space and want to know what rotation actually is.

This is all because Quaternion is the only fully reliable way of rotating (Gimbal lock - Wikipedia) Once working in Quat, converting back to Euler has several corresponding sets which are all correct despite not being the one you expect.

I would recommend to familiarize your self with quats as mixing with Euler would lead to unintuitive results.

1 Like