How can I change the click-drag behavior on this rotatable globe to be more intuitive?

58 Views Asked by At

I've been messing around with 3D meshes in Phaser 3, and I'm trying to create a basic 3D shape with click/drag-to-rotate behavior, very much like this globe example:

https://codepen.io/cedarcantab/pen/MWBKGeE

However, you'll notice that if you rotate the globe about 90 degrees left or right, then click/drag again and try to rotate vertically, the rotation behavior isn't what you'd intuitively expect. (To be clear, I would expect the front-facing area of the globe to roughly move in the direction of the mouse, similar to click/dragging in Google Earth.)

Any X cursor movement is mapped to rotating the Y axis, and Y cursor movement is mapped to rotating the X axis. This makes sense on your first click/drag with all axes at 0 rotation, but future click/drags can make less sense, depending on your starting position.

As far as I can tell, the Y axis behaves like a global or absolute rotation value: regardless of the globe's rotation state on any other axis, dragging left/right always rotates around an axis that goes from top to bottom. But it seems like the other 2 axes behave like relative rotation values: the X axis begins as a left-to-right axis, but as you rotate around the Y axis, the X axis itself rotates (as does the Z axis).

The rotation code is inside the pointermove event:

mesh.modelRotation.y += pointer.velocity.x * (rotateRate / 800);
mesh.modelRotation.x += pointer.velocity.y * (rotateRate / 600);

I've replaced this simple, 2-line logic with many attempts at improved rotation code, at various points involving rotation matrices, quaternions, splitting the Y cursor movement manually between the X and Z axes depending on current rotation, and I still haven't found the right approach. Here's one attempt where I rotate around an axis I define myself, but the behavior ends up exactly the same:

const dragVector = new Phaser.Math.Vector3(pointer.velocity.x * rotateRate / 800, -pointer.velocity.y * rotateRate / 600),
    vectorZ = new Phaser.Math.Vector3(0, 0, 1),
    rotationAxis = new Phaser.Math.Vector3().crossVectors(dragVector, vectorZ).normalize(),
    rotationMatrix = new Phaser.Math.Matrix4().fromRotationXYTranslation(mesh.modelRotation, mesh.modelPosition, true);

rotationMatrix.makeRotationAxis(rotationAxis, dragVector.length());

const endEuler = new Phaser.Math.Euler().setFromRotationMatrix(rotationMatrix),
    addToRotationX = endEuler.x,
    addToRotationY = endEuler.y,
    addToRotationZ = endEuler.z;

mesh.modelRotation.x += addToRotationX;
mesh.modelRotation.y += addToRotationY;
mesh.modelRotation.z += addToRotationZ;

Any pointers in the right direction appreciated!

Edit: After more research and experimenting, I believe all 3 rotation values in mesh.modelRotation are "extrinsic rotation" values, and they are applied in the (non-standard?) order ZXY.

This is why left/right rotation (around the Y axis) always feels correct: it is applied last around the fixed/global Y axis.

0

There are 0 best solutions below