Search code examples
three.jsrotationquaternions

How to rotate Object3d around X,Y & Z axis without having the axes to flip or cause gimbal lock?


Probably threejs's rotation question no 9000 , i simply want to have 3 UI buttons to incrementally rotate an object 90 degrees in either x,y & z direction with every click on each, how hard could it be ?

Using regular rotation methods which uses euler will cause gimbal lock, and using quanternions the axes will flip randomly in which z-axis will be y-axis at one point or another.

please have a look at my working demo : http://grutex.com/webgl/demos/help/

Edit: here is the rotation functions part :

        function z_rotate(){

        var startAngle = 0;
        var start = {angle: startAngle};
        var end = {angle: startAngle + 90};
        var lastAngle=0;                                
        var tween = new TWEEN.Tween(start)
          .to(end, 400)
          .easing( TWEEN.Easing.Quadratic.Out )
          .onUpdate(function(){
              startAngle=this.angle;    
              my_object.rotateOnAxis(new THREE.Vector3(0,0,1),degreeToRadians(startAngle-lastAngle));
              lastAngle=startAngle;                     
           })
          .start(); 


    }

    function x_rotate(){

        var startAngle = 0;
        var start = {angle: startAngle};
        var end = {angle: startAngle + 90};
        var lastAngle=0;                                
        var tween = new TWEEN.Tween(start)
          .to(end, 400)
          .easing( TWEEN.Easing.Quadratic.Out )
          .onUpdate(function(){
              startAngle=this.angle;
              my_object.rotateOnAxis(new THREE.Vector3(1,0,0),degreeToRadians(startAngle-lastAngle));
              lastAngle=startAngle;                     
           })
          .start();
    }

    function y_rotate(){
        var startAngle = 0;
        var start = {angle: startAngle};
        var end = {angle: startAngle + 90};
        var lastAngle=0;                                
        var tween = new TWEEN.Tween(start)
          .to(end, 400)
          .easing( TWEEN.Easing.Quadratic.Out )
          .onUpdate(function(){
              startAngle=this.angle;
              my_object.rotateOnAxis(new THREE.Vector3(0,1,0),degreeToRadians(startAngle-lastAngle));
              lastAngle=startAngle;                     
           })
          .start();

    }

Solution

  • As @WestLangley pointed out, i should attach and detach the 3d object to a parent pivot point (THREE.Object3D), attaching the 3d object to the pivot point before the tween start and detaching the pivot point after tween completion and resetting its rotation to (0,0,0)

    @WestLangley answer : Three.js: Adding and Removing Children of Rotated Objects

    New modified rotation function :

    var axis_x = new THREE.Vector3( 1, 0, 0 ).normalize();
    
        function x_rotate(){
            pivot_attach();
            var startAngle = 0;
            var start = {angle: degreeToRadians(startAngle)};
            var end = {angle: degreeToRadians(startAngle + 90)};
            var lastAngle=0;                                
            var tween = new TWEEN.Tween(start)
              .to(end, 400)
              .easing( TWEEN.Easing.Quadratic.Out )
              .onUpdate(function(){
                  startAngle=this.angle;
                 pivot.rotateOnAxis(axis_x,startAngle-lastAngle);
                  lastAngle=startAngle;                     
               })
              .start().onComplete(pivot_detach);
        }
    
    function pivot_attach() {
    
        pivot.updateMatrixWorld();
        THREE.SceneUtils.attach( my_object, scene, pivot );
    
    }
    
    function pivot_detach() {
    
        pivot.updateMatrixWorld();
        my_object.updateMatrixWorld(); // if not done by the renderer
    
        THREE.SceneUtils.detach( my_object, pivot, scene );
        pivot.rotation.set( 0, 0, 0 );
    
    }
    

    and don't forget to add the pivot to the scene "scene.add(pivot);"