Search code examples
rotationthree.jsquaternions

How to derive "standard" rotations from three.js when using quaternions?


Newbie stackoverflow participant, newbie 3D programmer, and far from a math wiz... so I'll try to frame this question as clearly as I can, hoping it makes sense, and hoping for an answer that's not a mile over my head.

I've written a very cool app using three.js that lets the user fly through 3D space and explore a solar system. The flight model is loosely based on the Fly.Controller example/extension in the three.js package which taught me to use quaternions for keeping all the axis rotations sensible relative to each other. The flying part all works great.

Here's my dilemma: When using quaternions, how do I deduce the "normal" (I don't know what else to call it) rotation values to determine which direction I am facing? When using quaternions, the "rotation" structure inside the camera object stays at 0,0,0. So, while I can freely fly through space at any angle, I can't figure out how to determine what direction I'm actually facing. Is there a built in three.js function, or other easy way to convert this?

I've found some similar, confusing, pointers on the web, but nothing I can decipher and put to use in three.js. Thanks.


Solution

  • Thanks for the quick response - it wasn't exactly what I was looking for, but I probably didn't know how to ask the question clearly. My specific use-case was that I wanted to draw a 2D map the represented the relative positions of all the objects in my 3D scene, but I wanted to rotate the objects in the map based on the yaw of the camera in the 3D scene - so I needed to know the "angle" that the quaternion-based camera was facing so that I could offset the rotations of the 2D objects on the map accordingly. Seems to work pretty well. I just wish there didn't have to be so many calculations, but at least Javascript is fast.

    // Pass the obj.quaternion that you want to convert here:
    //*********************************************************
    function quatToEuler (q1) {
        var pitchYawRoll = new THREE.Vector3();
         sqw = q1.w*q1.w;
         sqx = q1.x*q1.x;
         sqy = q1.y*q1.y;
         sqz = q1.z*q1.z;
         unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
         test = q1.x*q1.y + q1.z*q1.w;
        if (test > 0.499*unit) { // singularity at north pole
            heading = 2 * Math.atan2(q1.x,q1.w);
            attitude = Math.PI/2;
            bank = 0;
            return;
        }
        if (test < -0.499*unit) { // singularity at south pole
            heading = -2 * Math.atan2(q1.x,q1.w);
            attitude = -Math.PI/2;
            bank = 0;
            return;
        }
        else {
            heading = Math.atan2(2*q1.y*q1.w-2*q1.x*q1.z , sqx - sqy - sqz + sqw);
            attitude = Math.asin(2*test/unit);
            bank = Math.atan2(2*q1.x*q1.w-2*q1.y*q1.z , -sqx + sqy - sqz + sqw)
        }
        pitchYawRoll.z = Math.floor(attitude * 1000) / 1000;
        pitchYawRoll.y = Math.floor(heading * 1000) / 1000;
        pitchYawRoll.x = Math.floor(bank * 1000) / 1000;
    
        return pitchYawRoll;
    }        
    
    // Then, if I want the specific yaw (rotation around y), I pass the results of
    // pitchYawRoll.y into the following to get back the angle in radians which is
    // what can be set to the object's rotation.
    
    //*********************************************************
    function eulerToAngle(rot) {
        var ca = 0;
        if (rot > 0)
            { ca = (Math.PI*2) - rot; } 
        else 
            { ca = -rot }
    
        return (ca / ((Math.PI*2)/360));  // camera angle radians converted to degrees
    }