Search code examples
aframe

In aframe two multiple rotations looks identical


I've been building something in aframe (using the current master build from github as of 23rd March 2018) and have noticed that there are two sets of rotations that are identical that I don't think should be.

  • A model looks the same with its rotation attribute "270 90 90" and "270 180 0".

  • Similarly - setting the rotation attribute to "270 270 90" and "270 0 0" look the same.

I created a little demo to show this here - http://marcamillian.com/VR/rotationIssue.html.

Is this a bug or am I mis-understanding something?


=== Further information ===

I came across this when trying to add rotating animations a model from "270 90 0" along its roll axis and yaw axis and not getting the same motion on each.

After checking all of my functions I started setting the attribute on the model directly and getting the model looking the same for different rotation positions.


Solution

  • This is a pretty common problem in 3D computer graphics. Welcome to the gimbal lock problem! It is basically an issue you run into when trying to set rotations using "Euler" angles, especially when rotating in multiples of 90 degrees. If you are going to rotate like this try to only rotate by two axes (e.g. x and y) instead of all 3 (x,y,z).

    More information here on gimbal lock: https://www.youtube.com/watch?v=zc8b2Jo7mno&feature=youtu.be

    An even better practise is to rotate using quaternions instead. I will also note that it is best practise to use components to make sure A-Frame is available to modify, or not as recommended a "loaded" event listener on a-scene (see this SO question for more info. How to detect when a scene is loaded in A-Frame?):

    //listen for scene load s0 we know Aframe and Threejs are around to access. A snipped of your code, modified slightly for some direction ...
    document.querySelector('a-scene').addEventListener('loaded', function () {
                //AFRAME loaded
                const scene = document.querySelector('a-scene');
                const arrowElem = scene.querySelector(".shape-container");
    
                //now set your click listener
                setButton.addEventListener('click',function(){
                    let rotationValue = `${inputPitch.valueAsNumber} ${inputYaw.valueAsNumber} ${inputRoll.valueAsNumber}`
                    //arrowElem.setAttribute('rotation', rotationValue); //your way
    
                    //this doesn't work well ...
                    //arrowElem.object3D.rotation.set(    THREE.Math.degToRad(inputPitch.valueAsNumber), 
                                                        THREE.Math.degToRad(inputYaw.valueAsNumber), 
                                                        THREE.Math.degToRad(inputRoll.valueAsNumber)
                                                    );
                    //consider how you can use quaternions instead
                    let quaternion = new THREE.Quaternion();
                    quaternion.setFromAxisAngle( new THREE.Vector3( 0, 1, 0 ), Math.PI / 2 ); //might have to change your logic to better use this functionality ...
    
                })
            });