Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling Proper Rotation of Cannon Body Based on Quaternion?

This one is bugging me quite a bit. I'm trying to achieve rotation of a Cannon.Body based on the mouse input. By using the (Cannon) Three FPS example to demonstrate, you can see what the issue is.

https://codepen.io/Raggar/pen/EggaZP https://github.com/RaggarDK/Baby/blob/baby/pl.js

When you run the code and enable pointerlockcontrols by clicking on the "click to play" area and press W for 1 second to get the sphere into the view of the camera, you'll see that the sphere moves according to the WASD keys by applying velocity. If you move the mouse, the quaternion is applied to the Body, and the proper velocity is calculated. Now turn 180 degrees, and the rotation on the X axis is now negated somehow. When moving the mouse up, the sphere rotates down.

How would one fix such issue? Maybe I'm doing something wrong elsewhere, that might mess with the quaternion?

Maybe I should mention, in the playercontroller(pl.js), I'm applying the rotation to the sphereBody, instead of the yaw- and pitchObjects.

Relevant code from pl.js (Line 49):

var onMouseMove = function ( event ) {

    if ( scope.enabled === false ) return;

    var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
    var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;


    cannonBody.rotation.y -= movementX * 0.002;
    cannonBody.rotation.x -= movementY * 0.002;

    cannonBody.rotation.x = Math.max( - PI_2, Math.min( PI_2, cannonBody.rotation.x ) );





    //console.log(cannonBody.rotation);
};

And (Line 174):

    euler.x = cannonBody.rotation.x;
    euler.y = cannonBody.rotation.y;
    euler.order = "XYZ";
    quat.setFromEuler(euler);
    inputVelocity.applyQuaternion(quat);
    cannonBody.quaternion.copy(quat);
    velocity.x = inputVelocity.x;
    velocity.z = inputVelocity.z;

Inside the animate() function, codepen (Line 305): testballMesh.position.copy(sphereBody.position); testballMesh.quaternion.copy(sphereBody.quaternion);

like image 970
Matt DeVenge Avatar asked Sep 18 '16 17:09

Matt DeVenge


1 Answers

The problem is the way you assign angles to and from the Quaternions. The quaternion x,y,z,w properties are not directly compatible with angles, so you need to convert.

This is how to set the angle around a given axis for a CANNON.Quaternion:

var axis = new CANNON.Vec3(1,0,0);
var angle = Math.PI / 3;
body.quaternion.setFromAxisAngle(axis, angle);

Extracting the Euler angles from quaternions is probably not be the best way to attack the second part of the problem. You could instead just store the rotation around X and Y axes when the user moves the mouse:

// Declare variables outside the mouse handler
var angleX=0, angleY=0;

// Inside the handler:
angleY -= movementX * 0.002;
angleX -= movementY * 0.002;
angleX = Math.max( - PI_2, Math.min( PI_2, angleX ) );

And then to get the rotation as a quaternion, use two quaternions separately (one for X angle and one for Y) and then combine them to one:

var quatX = new CANNON.Quaternion();
var quatY = new CANNON.Quaternion();
quatX.setFromAxisAngle(new CANNON.Vec3(1,0,0), angleX);
quatY.setFromAxisAngle(new CANNON.Vec3(0,1,0), angleY);
var quaternion = quatY.mult(quatX);
quaternion.normalize();

To apply the quaternion to your velocity vector:

var rotatedVelocity = quaternion.vmult(inputVelocity);

Pro tip: don't use Euler angles if you can avoid them. They usually cause more problems than they solve.

like image 177
schteppe Avatar answered Oct 15 '22 14:10

schteppe