Combine DeviceOrientationControls with custom touchmove rotation on Y axis

Hi!
I would like to combine DeviceOrientationControls with a custom logic that allow user to rotate camera around Y axis using touch move gestures. Basically I would like to achieve the same behavior (for touch devices) of this app: https://showroom.littleworkshop.fr

I’ve implemented a simple logic which seems to work ok for the touch gestures and temporarily disable the DeviceOrientationControls in case of a touchmove event (otherwise the camera orientation can’t change):

    camera.position.set(0, 0, 0)
    camera.rotation.set(0, 0, 0)
    const controls = new THREE.DeviceOrientationControls(camera)

    let lastPageX
    let deltaPageX
    let isTouchMove = false

    document.addEventListener("touchstart", e => {
        isTouchMove = false
        lastPageX = e.pageX
    }, false)


    document.addEventListener("touchmove", e => {
        isTouchMove = true
        controls.enabled = false
        deltaPageX = e.pageX - lastPageX
        lastPageX = e.pageX
        // console.log(deltaPageX)
        camera.rotation.y += deltaPageX * .007
    }, false)

    document.addEventListener("touchend", e => {
        isTouchMove = false
        controls.enabled = true
    }, false)

    // in the animate function
   function animate () {
       controls.update()
       // [...]
       requestAnimationFrame(animate) 
   }

My problem is that as soon as the touchmove event has finished I need to re-enable DeviceOrientationControls. But doing that the camera rotation switch back to the orientation calculated from the device orientation, it does not get the new rotation (from the touch gesture) as the new “starting point”.

Any idea how to solve this problem?

It unfortunately doesn’t look like DeviceOrientationControls has a lot of it’s internals exposed to be able to do this. It will directly translate the deviceorientation into a camera rotation with no way to specify some offset value (like you’re trying to use with your end drag position).

The easiest solution would be to, every time after you do controls.update(), apply a quaternion offset on your camera. Something like:

    let offsetRotation = new THREE.Quaternion()
    document.addEventListener("touchmove", e => {
        //...
        offsetRotation.premultiply(
            new THREE.Quaternion().setFromAxisAngle(
                new THREE.Vector3(0,1,0), 
                deltaPageX * 0.007));
        //It would be better to convert deltaPageX into a normalized device coordinate value and
        //then use that to multiply by some unit of 2*Math.PI to better control how much rotation
        //occurs instead of tweaking a magic number
    }, false)

    // in the animate function
   function animate () {
       if(!isTouchMove) {
           controls.update();
       }
       camera.quaternion.premultiply(offsetRotation); //Rotate by the rotation stored in offsetRotation
       requestAnimationFrame(animate) 
   }

this won’t work perfectly, as it when you’re doing a touchMove, it won’t start from the camera’s last quaternion value but it’s not a stretch to add it. This should get you closer though.

1 Like

Hi @Cobertos, I’ve tried your solution but it seems it shows the same incorrect behaviour as mine. It just fall back on the previous rotation once the touchmove gesture has finished.

I’ve attached a simple project for testing. Any other ideas?touchmove_controls.zip (507.5 KB)