javascriptthree.jsleap-motion

three.js leap.js - leapControls.js no longer gives expected behaviour with version R66, movement limited to Z axes


so im pretty new to Three.js and leap.js but im creating a small game using both. I have found the perfect controller (https://github.com/openleap/Three.leapControls) created by cabbibo. (im pretty sure it was created using R55 of three.js)

My problem is that with the newest version of three.js, the controller has no errors and the camera rotation works perfectly. But the movement is restricted to the Z axes no matter what direction the camera is facing (where it should move forward in the direction of the camera).

It works as expected with no additional changes other than using three.js R55 so im positive this is my problem.

I have spent days staring at this document attempting to figure this out but it beyond my skills. Are there any three.js wizards out there that could lend a helping hand? or even if you could just point me in the right direction as to where i need to keep staring, it would be appreciated.

Thanks in advance.

Here is the whole controller:

//Global Leap Hand Variable

var hand;


THREE.LeapControls = function ( object, domElement ) {

    this.leapConstants = {

        //makes rotation slower (also dependent on this.lookSpeed)
        rotationConstant:400,

        //when the hand leaves the field
        //this is the amount the rotation will be
        //divided by ever render
        rotationDampening:1.01,

        //Changes the 0 of the Y field
        yAlignment: 250,

        //Changes the 0 of the Z field
        zAlignment: -50,

        //Makes movement faster
        movementConstant:.01,

        //deceleration for when hand leaves field
        movementDampening:1.05,

    }

    this.object = object;
    this.domElement = ( domElement !== undefined ) ? domElement : document;

    // API

    this.mouseLook = true;
    this.lookSpeed = 1;
    this.movementSpeed = 1;
    this.rollSpeed = 1;
    this.maxSpeed = 200
    this.minSpeed = -200;





    this.autoForward = true;

    //Added to smooth camera to a stop
    //this.minSpeed = 0
    this.acceleration = 100
    this.deceleration = 100




    this.constrainVertical = [ -0.9, 0.9 ];

    // disable default target object behavior

    this.object.matrixAutoUpdate = false;

    // internals

    this.forward = new THREE.Vector3( 0, 0, 1 );
    this.roll = 0;

    var xTemp = new THREE.Vector3();
    var yTemp = new THREE.Vector3();
    var zTemp = new THREE.Vector3();
    var rollMatrix = new THREE.Matrix4();


    //Keeping keyboard control functionality
    //as backup
    var doRoll = false, rollDirection = 1, forwardSpeed = 0, sideSpeed = 0, upSpeed = 0;


    var windowHalfX = 0;
    var windowHalfY = 0;

    this.handleResize = function () {

        windowHalfX = window.innerWidth / 2;
        windowHalfY = window.innerHeight / 2;

    };


    //Sets rotation contants
    //so that when the hand is removed from the field, it won't automatically jump to no movement
    this.leapRotateHorizontal = 0
    this.leapRotateVertical = 0

    this.update = function ( delta ) {


        /*

            LEAP MOTION CAMERA MOVEMENT / ROTATION

        */


        //if the hand is within range of leap,
        // use its X & Y to look horizontally and vertically


        if ( window.leapPos.x !=0 && window.leapPos.y !=0) {

          var leapHand = {}

          if(hand){
            //console.log(hand)
            leapHand.x = hand.direction[0]*200 
            leapHand.y = hand.direction[1]*200
          }else{
           // console.log(leapHand)
            leapHand.x = 0
            leapHand.y = 0

          }
            this.leapRotateHorizontal = 
              (window.leapPos.x + leapHand.x)
              /this.leapConstants.rotationConstant

            this.leapRotateVertical = 
            (-( window.leapPos.y-this.leapConstants.yAlignment) - leapHand.y)
            /this.leapConstants.rotationConstant
        }else{
            this.leapRotateHorizontal = this.leapRotateHorizontal/this.leapConstants.rotationDampening
            this.leapRotateVertical = this.leapRotateVertical/this.leapConstants.rotationDampening
        }

        var actualLookSpeed = delta * this.lookSpeed;
        this.rotateHorizontally( actualLookSpeed * this.leapRotateHorizontal);
        this.rotateVertically( actualLookSpeed  * this.leapRotateVertical);


        //If there is only one pointable, use its position in Z space to move the camera
        if(window.leapPos.z!=0 && window.leapPos.stationary ==false){
            this.movementSpeed += -(window.leapPos.z+this.leapConstants.zAlignment)*(this.acceleration/100)
        }else{
            if(this.movementSpeed > 5 || this.movementSpeed < -5 ){
                this.movementSpeed = this.movementSpeed/this.leapConstants.movementDampening
            }else{
                this.movementSpeed = 0
            }

        }




        //If movement is greater then max speed, reduce it to the max speed
        if(this.movementSpeed >= this.maxSpeed){
            this.movementSpeed = this.maxSpeed
        }

        //If movement is greater then max speed, reduce it to the max speed
        if(this.movementSpeed <= this.minSpeed){
            this.movementSpeed = this.minSpeed
        }



        var actualSpeed = delta * this.movementSpeed;
        var forwardOrAuto = ( forwardSpeed > 0 || ( this.autoForward && ! ( forwardSpeed < 0 ) ) ) ? 1 : forwardSpeed;

        this.object.translateZ( -actualSpeed * forwardOrAuto );
        this.object.translateX( actualSpeed * sideSpeed );
        this.object.translateY( actualSpeed * upSpeed );

        this.rollSpeed *= .95
        if(hand){   

          if(Math.abs(hand.palmNormal[0])>=.1){

            this.rollSpeed += hand.palmNormal[0]*5*delta            

          }

        }

        this.roll += this.rollSpeed * delta

        // cap forward up / down

        if( this.forward.y > this.constrainVertical[ 1 ] ) {

            this.forward.y = this.constrainVertical[ 1 ];
            this.forward.normalize();

        } else if( this.forward.y < this.constrainVertical[ 0 ] ) {

            this.forward.y = this.constrainVertical[ 0 ];
            this.forward.normalize();

        }


        // construct unrolled camera matrix

        zTemp.copy( this.forward );
        yTemp.set( 0, 1, 0 );

        xTemp.crossVectors( yTemp, zTemp ).normalize();
        yTemp.crossVectors( zTemp, xTemp ).normalize();

        this.object.matrix.elements[0] = xTemp.x; this.object.matrix.elements[4] = yTemp.x; this.object.matrix.elements[8] = zTemp.x;
        this.object.matrix.elements[1] = xTemp.y; this.object.matrix.elements[5] = yTemp.y; this.object.matrix.elements[9] = zTemp.y;
        this.object.matrix.elements[2] = xTemp.z; this.object.matrix.elements[6] = yTemp.z; this.object.matrix.elements[10] = zTemp.z;

        // calculate roll matrix

        rollMatrix.identity();
        rollMatrix.elements[0] = Math.cos( this.roll ); rollMatrix.elements[4] = -Math.sin( this.roll );
        rollMatrix.elements[1] = Math.sin( this.roll ); rollMatrix.elements[5] =  Math.cos( this.roll );

        // multiply camera with roll

        this.object.matrix.multiply( rollMatrix );
        this.object.matrixWorldNeedsUpdate = true;

        // set position

        this.object.matrix.elements[12] = this.object.position.x;
        this.object.matrix.elements[13] = this.object.position.y;
        this.object.matrix.elements[14] = this.object.position.z;


    };

    this.translateX = function ( distance ) {

        this.object.position.x += this.object.matrix.elements[0] * distance;
        this.object.position.y += this.object.matrix.elements[1] * distance;
        this.object.position.z += this.object.matrix.elements[2] * distance;

    };

    this.translateY = function ( distance ) {

        this.object.position.x += this.object.matrix.elements[4] * distance;
        this.object.position.y += this.object.matrix.elements[5] * distance;
        this.object.position.z += this.object.matrix.elements[6] * distance;

    };

    this.translateZ = function ( distance ) {

        this.object.position.x -= this.object.matrix.elements[8] * distance;
        this.object.position.y -= this.object.matrix.elements[9] * distance;
        this.object.position.z -= this.object.matrix.elements[10] * distance;

    };


    this.rotateHorizontally = function ( amount ) {

        // please note that the amount is NOT degrees, but a scale value

        xTemp.set( this.object.matrix.elements[0], this.object.matrix.elements[1], this.object.matrix.elements[2] );
        xTemp.multiplyScalar( amount );

        this.forward.sub( xTemp );
        this.forward.normalize();

    };

    this.rotateVertically = function ( amount ) {

        // please note that the amount is NOT degrees, but a scale value

        yTemp.set( this.object.matrix.elements[4], this.object.matrix.elements[5], this.object.matrix.elements[6] );
        yTemp.multiplyScalar( amount );

        this.forward.add( yTemp );
        this.forward.normalize();

    };

    function onKeyDown( event ) {

        //event.preventDefault();

        switch ( event.keyCode ) {

            case 38: /*up*/
            case 87: /*W*/ forwardSpeed = 1; break;

            case 37: /*left*/
            case 65: /*A*/ sideSpeed = -1; break;

            case 40: /*down*/
            case 83: /*S*/ forwardSpeed = -1; break;

            case 39: /*right*/
            case 68: /*D*/ sideSpeed = 1; break;

            case 81: /*Q*/ doRoll = true; rollDirection = 1; break;
            case 69: /*E*/ doRoll = true; rollDirection = -1; break;

            case 82: /*R*/ upSpeed = 1; break;
            case 70: /*F*/ upSpeed = -1; break;

        }

    };

    function onKeyUp( event ) {

        switch( event.keyCode ) {

            case 38: /*up*/
            case 87: /*W*/ forwardSpeed = 0; break;

            case 37: /*left*/
            case 65: /*A*/ sideSpeed = 0; break;

            case 40: /*down*/
            case 83: /*S*/ forwardSpeed = 0; break;

            case 39: /*right*/
            case 68: /*D*/ sideSpeed = 0; break;

            case 81: /*Q*/ doRoll = false; break;
            case 69: /*E*/ doRoll = false; break;

            case 82: /*R*/ upSpeed = 0; break;
            case 70: /*F*/ upSpeed = 0; break;

        }

    };

    function onMouseMove( event ) {

        mouseX = ( event.clientX - windowHalfX ) / window.innerWidth;
        mouseY = ( event.clientY - windowHalfY ) / window.innerHeight;

    };

    function onMouseDown ( event ) {

        event.preventDefault();
        event.stopPropagation();

        mouseIsDown = true

        switch ( event.button ) {

            case 0: forwardSpeed = 1; break;
            case 2: forwardSpeed = -1; break;

        }

    };

    function onMouseUp ( event ) {

        event.preventDefault();
        event.stopPropagation();


        mouseIsDown = false

        switch ( event.button ) {

            case 0: forwardSpeed = 1; break;
            case 2: forwardSpeed = -1; break;

        }

    };

    this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );

    this.domElement.addEventListener( 'mousemove', onMouseMove, false );
    this.domElement.addEventListener( 'mousedown', onMouseDown, false );
    this.domElement.addEventListener( 'mouseup', onMouseUp, false );
    this.domElement.addEventListener( 'keydown', onKeyDown, false );
    this.domElement.addEventListener( 'keyup', onKeyUp, false );

    this.handleResize();



};



var leapInit =  function (){

    window.leapPos={
        x:0,
        y:0,
        z:0,
        stationary:false
     }

     Leap.loop(function(frame) {


        if(frame.hands){
        if(frame.hands[0]){

            window.leapPos.x = frame.hands[0].palmPosition[0]
            window.leapPos.y = frame.hands[0].palmPosition[1]
            window.leapPos.z = frame.hands[0].palmPosition[2]

            window.hand = frame.hands[0]

            //if no pointables present, set to  zero
            }else{
                window.leapPos.x = 0
                window.leapPos.y = 0
                window.leapPos.z = 0
                window.hand = undefined
            }

            //If there is a second pointable, set to stationary
            if(frame.pointables.length == 0 ){
                window.leapPos.stationary = true
            }else{
                window.leapPos.stationary = false   
            }

        }
        latestFrame = frame
        animate();
    })



}

Solution

  • I've updated the controls on https://github.com/openleap/Three.leapControls , But you will need to setup the controls in a slightly different way, check out the README for instructions, and let me know if any of it doesn't make sense!

    Happy Leaping!