Gamepad Camera Rotation Issue

Hi All,

I’m pretty new to babylon.js and learning all the features. I’m in the process of building a space scene with Planets, a ship, asteroid fields etc. All is going well apart from an issue with the camera now I’ve hooked it up to my gamepad.

The problem I am having is, once I have pressed button 5 or 6 and rotated the ship 90 degrees to the left or right using camera.rotation.z, the issue happens where camera.rotation.x and camera.rotation.y are now not relative to the camera rotation, they are still relative to the world.

So when I then use my left stick to rotate left for example, the ship rotates up instead of left and vice versa if I rotate right the ship rotates down.

I solved this on strafing Left, Right, Up, Down using addInPlace:
camera.cameraDirection.addInPlace(camera.getDirection(BABYLON.Vector3.Right()).scale(Math.abs(gamepad.rightStick.x / 100)));

However there doesn’t seem to be an equivalent to use once the camera has rotated around the Z axis. camera.rotation.x is always relative to the world and not the camera. What do i need to add to this to make X relative to the camera or at least when i push left to rotate left it goes left and not up?

I’m sure i’m missing something simple here, but i’m lost and going round in circles and nothing works.

Any help is much appreciated.

I don’t have a playground at the moment as I’ve just signed up, but ill create one if needed.

Thanks in advance.

gamepadManager.onGamepadConnectedObservable.add((gamepad, state) => {
gamepad.onButtonDownObservable.add((button, state) => {
//BUTTON PRESSED

                if (button == 5) {                        
                   camera.rotation.z += -0.01;                    
                }
                if (button == 4) {                       
                   camera.rotation.z += 0.01;                 
                }

            })
            scene.registerBeforeRender(function () {
                if (gamepad instanceof BABYLON.DualShockPad) {
                                            
                    if (gamepad.rightStick.y > 0.1 || gamepad.rightStick.y < -0.1) {
                        
                        setVal("control_thrusterXstate", (parseFloat(checkVal("control_thrusterXstate")) + gamepad.rightStick.y / (5000 / (parseFloat(checkVal("control_afterburner"))))));                            
                    }

                    if (gamepad.rightStick.x > 0.1) {                           
                        camera.cameraDirection.addInPlace(camera.getDirection(BABYLON.Vector3.Right()).scale(Math.abs(gamepad.rightStick.x / 100)));                            
                    }

                    if (gamepad.rightStick.x < -0.1) {                            
                        camera.cameraDirection.addInPlace(camera.getDirection(BABYLON.Vector3.Left()).scale(Math.abs(gamepad.rightStick.x / 100)));                                                        
                    }
                    
                    ****THIS IS WHERE I HAVE THE ISSUE
                    if (gamepad.leftStick.y > 0.2 || gamepad.leftStick.y < -0.2) {                                                                                  
                        camera.rotation.x += (gamepad.leftStick.y / 500) * -1; 
                    }

                    if (gamepad.leftStick.x > 0.1 || gamepad.leftStick.x < -0.1) {                            
                        camera.rotation.y += (gamepad.leftStick.x / 500);                            
                    }
                                            
                }
            });

Hello! It is a bit hard to say without a playground, but it sounds like you’re getting gimbal lock issues? This can happen when rotating a model by using euler angles (which is what rotation uses)


To avoid these issues, you can use quaternions instead (Babylon has methods for converting Quaternions to Euler angles and vice versa so no need to do the calculations yourself)

Hi Carol,

Thanks this does make a bit more sense, but i’m not sure where to start to correct it.

I’ve created a playground here so you can see it. If you rotate when it initially loads the Z rotation is 0 so everything is ok. However once you have rolled left or right the controls are skewed.

To recreate, roll to the left or right 90 degrees and use the left controls to move in a direction and you will see the camera follows the world X,Y,Z not the current camera orientation. When i rotate the camera on the Z axis, i always want rotation to be equivalent to the current camera Z rotation not the world, so left is left and right is right.

The right stick works as expected because its using the following methods to move Left, Right, Up or Down so Vector3.Left is calculated.

camera.cameraDirection.addInPlace(camera.getDirection(BABYLON.Vector3.Left()))

Is there an equivalent method for rotate up, down, left right? for example:

camera.cameraRotation.addInPlace(camera.getRotation(BABYLON.Vector3.Left()))

I know the above doesn’t exist, but is there something similar which will achieve the same?

I’m sure i’m missing something simple but i dont know what. :slight_smile:

Here are some images of what ive got so far:

Sorry, I think I misunderstood your issue then. Just to clarify, what you want is something like this?


So, you start like (1), where pressing the “right” button takes you to the camera -Z (which is the right), and pressing the roll and then the “right” button will now take you in the rotated camera -Z direction.

I’ll also tag @PolygonalSun here who knows a lot about cameras.

Basically yes. The camera is the view out of the front window of the ship, so I want left to always be left when I’m doing a rotation and right to be right.

Roll will always be Z so that’s fine, but when I rotate left it needs to account for the roll position I’m currently in and calculate left.

Thanks for your help so far.

1 Like

If you want to move relative to the camera space, then you add the camera’s position to the result of camera.getDirection. If you want to move relative to world space, then you just add the camera position: Babylon.js Playground (babylonjs.com)

For the camera rotation, I’d recommend trying the following:

let currentRotation;

if (gamepad.leftStick.y > 0.2 || gamepad.leftStick.y < -0.2) {
    // If we detect vertical movement, first get the current rotation, with respect to the camera's Yaw, Pitch, and Roll
    currentRotation = BABYLON.Quaternion.RotationYawPitchRoll(camera.rotation.y, camera.rotation.x, camera.rotation.z);
    // Next, create your rotation change quaternion
    let rotationChange = BABYLON.Quaternion.RotationAxis(BABYLON.Axis.X, ((gamepad.leftStick.y / 500) * -1));
    // Finally, apply your change to the currentRotation quaternion
    currentRotation.multiplyInPlace(rotationChange);
}

if (currentRotation) {
    currentRotation.toEulerAnglesToRef(camera.rotation); 
}

This code is essentially a slightly modified form of the rotation code that we use for mouse rotation. You can see this in action in this updated form of your PG: Camera Rotation Issue | Babylon.js Playground (babylonjs.com). Is this closer to what you were looking for?

1 Like

Hi Poly,

This is perfect, thank you. Works exactly as expected.

I would have never worked that out myself :slight_smile:

Thanks
Simon

1 Like

@PolygonalSun,

I’m a Babylon.js newbie and would appreciate it at your convenience you could add a few more comments to explain what is happening in the snippet you wrote (especially the currentRotation.multiplyInPlace() and currentRotation.toEulerAnglesToRef(camera.rotation) functions)?

I’m using the snippet in some projects I’m working on, and it works well, but I’d like to understand what it actually does since seems a little different than then steps described in the Babylon.js documentation on rotation.

Also, your same snippet can be used for mesh and camera rotations, correct?

Thanks :slight_smile:

Hey @SugarRayLua,

This is one of many ways to perform a rotation and technically, this way of handling the rotation could also be used with meshes.

With the lines below, we’re first going to take our rotation angles and convert it to a quaternion. This helps us to avoid gimble lock issues. We’re then taking the value of our joystick (0 to 1) and dividing it by 500 (think of this as a sort of sensitivity value, the higher it is, the less it moves) and apply that to our desired axis of rotation to get a Quaternion.

currentRotation = BABYLON.Quaternion.RotationYawPitchRoll(camera.rotation.y, camera.rotation.x, camera.rotation.z);
// Next, create your rotation change quaternion
let rotationChange = BABYLON.Quaternion.RotationAxis(BABYLON.Axis.X, ((gamepad.leftStick.y / 500) * -1));

Because we’re trying to add rotationChange to currentRotation to create a single rotation quaternion, we’ll need to multiply them together.

// Finally, apply your change to the currentRotation quaternion
currentRotation.multiplyInPlace(rotationChange);

With this last line, we are converting our rotation quaternion back to a Euler angle vector. We’re doing this only so that we can use the camera’s existing rotation vector. We can take our quaternion and just plug it in directly to the camera’s rotationQuaternion variable but if that variable is defined, your camera will continue to work with quaternions. This isn’t an issue but if you don’t want to perform any other rotations with quaternions, just convert it with this line.

currentRotation.toEulerAnglesToRef(camera.rotation);
1 Like

Absolutely perfect explanation, thanks, @PolygonalSun!

I saved your reply as an example for future reference and will share with other Babylon.js users.

Have a great rest of your week and upcoming weekend. :blush:

@PolygonalSun,

A follow up question on the snippet you posted above. When I initially tested it out on a simple mesh (e.g. the “detached house” mesh in the Babylon.js assets) it seemed to work great; however, when I tested it out on a more complex mesh the camera seemed to get off-center after multiple complex combinations of movements (horizon no longer levels with movement). However, when I use the default Babylon VirtualCamera with its default right joystick control, the camera is not off-center. I tried to figure out what might therefore be different between the snippet you showed and Babylon.js’s virtual joystick.

Babylon.js’s virtual joystick control of the camera is here:

Correct?

If so, I’m surprised that the control of rotation in that code seems to come only from this one line in that code:

camera.cameraRotation = camera.cameraRotation.addVector3(this._rightjoystick.deltaPosition);

That one line seemed too simple to control rotation by itself. I tried though using that one line code to control the camera’s rotation, and it didn’t work (camera kept spinning when I moved the right manual joystick I made in one direction)

Am I misunderstanding something? Is there more code to control the right joystick? Does that one line code depend on other code to control the camera’s rotation?

I appreciate any further insight you could provide on this matter at your convenience.

Thanks!