Shifting orientation of a scene only on mobile

I’m using three.js and DeviceMotion events to make a 3d gallery, where camera rotation on mobile is determined by device rotation (alpha, beta, gamma). Everything works as intended, except I want to change the “base” state from phone laying face up in portrait to phone being held up in portrait.

Here’s a very crude drawing of what I mean:

And here’s my code:

if (isMobile && !/iPad|Macintosh/i.test(navigator.userAgent)) {
            camera.position.set(0, 0, 0);

            // Mobile device controls (using device orientation)
            window.addEventListener("deviceorientation", (event) => {
                const alpha = (event.alpha * Math.PI) / 180;
                const beta = (event.beta * Math.PI) / 180;
                const gamma = (event.gamma * Math.PI) / 180;

                var euler = new THREE.Euler(beta, gamma, alpha, "YXZ"); // ' is the order of rotations

                // console.log(
                //     `DeviceOrientationEvent: alpha ${alpha}, beta ${beta}, gamma ${gamma}`
                // );

                var quaternion = new THREE.Quaternion();
                quaternion.setFromEuler(euler);
                camera.setRotationFromQuaternion(quaternion);
            });

            // Disable OrbitControls 
            controls.enabled = false;
        }

I’ve tried applying a shift to the alpha/beta/gamma values, as well as rotating the whole scene, but both seem to break the rotation. I apologize if the solution is really simple, I’m not the best with geometry :smile:

1 Like

Hello @nandanimal ,

Please provide files & more details to check it more precisely please DM with details.

Feel free to reach out:

jack@technogiq(dot)com
Skype: live:.cid.a0f06a69cf1c6478

Looking forward to hearing from you.

Regards,
Jack Smith

Maybe something like that ^ ?

1 Like

So I see that you disabled the orbit controls. Idk why you have orbit controls in the first place if you’re simply using event listeners for device orientation to position the camera and orientation.

Maybe I don’t understand enough…but try removing/ messing with the orbit controls.

If you don’t really want to do that just keep whatever code you have right now, and just change the entire scenes rotation.

I wouldn’t consider myself a master at Three Js, but I do a bit of OpenGl graphics programming, and in that case, you move the scene not the camera.

More info would be appreciated. Sorry if I wasn’t helpful. Hope you figure it out. Thanks!

Keep problem solving! -Jackson

Here is an implementation which achieves what you are trying to. This is tested and works for my app multi.noclip.org


  updateCameraOrientation() {
    // Pull orientation data from window.orientationGlobal if available
    if (
      window.orientationGlobal &&
      typeof window.orientationGlobal === 'object'
    ) {
      Sensors.orientationData.alpha =
        parseFloat(window.orientationGlobal.alpha) || 0; // 0..360 degrees
      Sensors.orientationData.beta =
        parseFloat(window.orientationGlobal.beta) || 0; // -180..180 degrees
      Sensors.orientationData.gamma =
        parseFloat(window.orientationGlobal.gamma) || 0; // -90..90 degrees
    }
  
    // Access orientation data directly from Sensors.orientationData
    const alphaDeg = Sensors.orientationData.alpha || 0; // 0..360 degrees
    const betaDeg = Sensors.orientationData.beta || 0; // -180..180 degrees
    const gammaDeg = Sensors.orientationData.gamma || 0; // -90..90 degrees
  
    // Fix decimal places for UI display
    const alphaConstraint = alphaDeg.toFixed(2);
    const betaConstraint = betaDeg.toFixed(2);
    const gammaConstraint = gammaDeg.toFixed(2);
  
    // Update UI fields with orientation data
    UI.updateField('Orientation_a', alphaConstraint);
    UI.updateField('Orientation_b', betaConstraint);
    UI.updateField('Orientation_g', gammaConstraint);
  
    // Optional: Replace alerts with console logs for debugging
    console.log(
      `Orientation Data - Alpha: ${alphaDeg}, Beta: ${betaDeg}, Gamma: ${gammaDeg}`
    );
  
    // Check if compass data is available and accurate
    const hasCompass =
      Sensors.orientationData.webkitCompassHeading !== undefined &&
      Sensors.orientationData.webkitCompassAccuracy !== undefined &&
      Math.abs(Sensors.orientationData.webkitCompassAccuracy) <= 10; // Adjust threshold as needed
  
    let yawDeg;
  
    if (hasCompass) {
      // Use compass heading as yaw
      yawDeg = Sensors.orientationData.webkitCompassHeading;
      console.log(`Using compass heading for yaw: ${yawDeg} degrees`);
    } else {
      // Fallback: Calculate yaw using alpha
      yawDeg = alphaDeg;
      console.log(`Using alpha for yaw: ${yawDeg} degrees`);
    }
  
    // Convert degrees to radians
    const yawRad = THREE.MathUtils.degToRad(yawDeg);
    const pitchRad = THREE.MathUtils.degToRad(betaDeg);
    const rollRad = THREE.MathUtils.degToRad(gammaDeg);
  
    // Determine the screen orientation (0, 90, 180, 270 degrees)
    const screenOrientationDeg = window.orientation || 0;
    const screenOrientationRad = THREE.MathUtils.degToRad(screenOrientationDeg);
  
    // Adjust yaw based on screen orientation
    const adjustedYawRad = yawRad - screenOrientationRad;
  
    // Create quaternions for each rotation
    const quaternionYaw = new THREE.Quaternion().setFromAxisAngle(
      new THREE.Vector3(0, 1, 0), // Y-axis
      adjustedYawRad
    );
    const quaternionPitch = new THREE.Quaternion().setFromAxisAngle(
      new THREE.Vector3(1, 0, 0), // X-axis
      pitchRad
    );
    const quaternionRoll = new THREE.Quaternion().setFromAxisAngle(
      new THREE.Vector3(0, 0, 1), // Z-axis
      -rollRad
    );
  
    // Combine the quaternions: Yaw * Pitch * Roll
    const deviceQuaternion = new THREE.Quaternion()
      .multiply(quaternionYaw)
      .multiply(quaternionPitch)
      .multiply(quaternionRoll);
  
    // Reference Quaternion: Rotate -90 degrees around X-axis to align device frame with Three.js frame
    const referenceQuaternion = new THREE.Quaternion().setFromEuler(
      new THREE.Euler(-Math.PI / 2, 0, 0, 'YXZ') // -90 degrees around X-axis
    );
  
    // Combine Device Quaternion with Reference Quaternion
    const finalQuaternion = deviceQuaternion.multiply(referenceQuaternion);
  
    // Apply the final quaternion to the camera
    this.camera.quaternion.copy(finalQuaternion);
  
    // Optional: Log final quaternion for debugging
    console.log(`Final Quaternion: ${finalQuaternion.x}, ${finalQuaternion.y}, ${finalQuaternion.z}, ${finalQuaternion.w}`);
  }

godspeed :saluting_face: