2

I'm trying to use a gamepad to rotate a camera in Three.js, using first-person-shooter style controls.

The browser detects the gamepad and recognises it's input, but the camera's order of rotation is wrong. When I rotate on the camera's local Y axis, it takes into account the local X rotation too, which is unwanted.

It seems I'm having the same problem as this guy, but his issue was solved using Three.js r54 and I'm using r60. He set camera.eulerOrder = "YXZ"; to get it working, but the current equivalent camera.rotation.order = "YXZ"; doesn't seem to work for me.

I'm aware of Three.js' built-in "FirstPersonControls" class, but it's not suitable for me as it doesn't accept controller input and it would be messy to shoehorn other non-movement controls in there later. I'm also aware of gamepad.js and have no interest in using it.

Can anyone help?

My rotation code:

function pollGamepad()
{
    gamepad = navigator.webkitGetGamepads()[0];

    //Rotation
    if(gamepad.axes[3] > 0.20)
    {
        camera.rotateX(-gamepad.axes[3] * 0.02);
    }
    if(gamepad.axes[3] < -0.20)
    {
        camera.rotateX(-gamepad.axes[3] * 0.02);
    }

    if(gamepad.axes[2] < -0.20)
    {
        camera.rotateY(-gamepad.axes[2] * 0.02);
    }
    if(gamepad.axes[2] > 0.20)
    {
        camera.rotateY(-gamepad.axes[2] * 0.02);
    }
}
Community
  • 1
  • 1
user23709
  • 23
  • 1
  • 3

2 Answers2

2

The approach that PointerLockControls uses is to create a hierarchy of objects: yawObject contains pitchObject contains camera. Then horizontal mouse (or joystick) movement would change the Y-rotation of the yaw object, vertical mouse (or joystick) movement would change the X-rotation of the pitch object, and the camera's rotation would stay fixed at the default (0, 0, -1). This just manually simulates Euler YXZ ordering but it may work better for you. It does create some awkwardness if you need to get the overall rotation.

In a custom controller I wrote recently, I achieved the same result by add()ing the camera to a single parent object and setting the parent object's Euler order to YXZ. I don't remember why this worked better than setting it on the camera directly, but it did.

IceCreamYou
  • 1,852
  • 14
  • 25
  • Thanks Icemonster, this helped a lot. – user23709 Sep 08 '13 at 06:40
  • I just put my camera inside a parent object and applied all translation and yaw to that, any pitch is then applied to the camera itself and that seems to work really well, while keeping everything simple. – user23709 Sep 08 '13 at 06:47
  • THANK YOU!!! I was struggling with rotation my yawObject (since 3 days). I used PointerLockControls, where my camera is parented to an 'anchor' object (a simple object 3D), so that whenever I move my mouse, the object rotates with my camera revolving around it. Then I added key press events for WASD movements, and in a requestAnimationFrame(), I updated my character mesh's positions. So now when I tried to rotate my character mesh along Y while pressing WASD keys, I could not rotate beyond this range (-90 to 90). My character was facing a gimbal lock. So I changed my anchor's euler order – Prav Dec 19 '19 at 02:50
  • Also, in newer versions of three js, the object.eulerOrder is set as object.rotation.order – Prav Dec 19 '19 at 02:52
0

I faced this problem myself and I came up with a simple solution. Always reset camera's x rotation to zero before performing a y rotation. Then, restore x rotation. So:

// Rotate camera Fps style
function rotateCamera(dx,dy){
    //store previous x rotation
    var x = camera.rotation.x;

    //reset camera's x rotation.
    camera.rotateX(-x);

    //rotate camera on y axis
    camera.rotateY(dy);

    //check if we are trying to look to high or too low
    if ( Math.abs( dx + x ) > Math.Pi/2 - 0.05) {
        camera.rotateX(x);
    else
        camera.rotateX(x+dx);

    //reset z rotation. Floating point operations might change z rotation during the above operations.
    camera.rotation.z = 0;
}
Gonzalo
  • 3,674
  • 2
  • 26
  • 28