I have been using OrbitControls to rotate the camera around the object, and it has worked well. However, I now need to dynamically enable and disable rotation around the Z-axis using mouse events or programmatically. After spending some time playing with camera.up adjustments, I realized that achieving this with OrbitControls is quite challenging. As a result, I started looking into ArcballControls since it provides Z-axis rotation out of the box. However, I encountered the opposite issue: I am now struggling to disable the mentioned rotation. I attempted to conditionally disable the camera.up update as shown below:
//update camera up vector
if (
(this._state == STATE.ROTATE ||
this._state == STATE.ZROTATE ||
this._state == STATE.ANIMATION_ROTATE) &&
!this._disableZAxis
) {
this.camera.up
.copy(this._upState)
.applyQuaternion(this.camera.quaternion);
}
However, this approach resulted in a weird behavior when using diagonal mouse-move rotation. OrbitControls handle this situation better by limiting vertical mouse rotation to 180 degrees. I would like to replicate this behavior in ArcballControls when Z-axis rotation is disabled, however, I am still quite new in this area and I am not sure how to do it. Should I adjust rotationAxis calculation or it is purely rotationMatrix manipulation? How can it be done?
Thank you in advance!
If this helps, the LookAt command automatically cancels out any Z-rotation. So your camera is always rotated level with the horizon (like OrbitControls). So, if there is a way to add that command to your code, you should be set.
Thanks. But the problem is not with Z-axis locking itself since disabling camera.up updates solves this problem, but it is more with Y-axis limiting. It seems that the ArcballControls use Math.tan to calculate camera position and when we cross Y-axis during rotation it starts rotating the camera as well and this is what I am trying to prevent.
Did, you ever find a solution to the issue? Iād be interested to know.
I am seeking something similar also to be able to add keyboard modifiers to conditionally lock the rotation axis. ie press z now only rotations around the Z axis of the camera.
I think I know how to solve it now, but I deprioritized this task, so never got to solving it.
ArcballControls has function called: calculateRotationAxis to calculate the vector to rotate around, then it uses function rotate to calculate transformation matrix and then applyTransformMatrix to actually perform the transformation. If I understand it correctly, to achieve the desired effect, we need to change the logic of calculateRotationAxis. In my case I decided to use quaternions instead of rotationAxis and ended up with some kind of a mixed approach. Also, I decided to keep the full 3D rotation for now.
Hello, I found a solution that I am using with a slider currently but you might be able to modify it. Hope it helps.
function rollCamera(axis, radians, controls) {
//check the axis and set the vector
let vector = new Vector3(0, 0, 1);
if (axis === "Z") {
vector = new Vector3(0, 0, 1);
} else if (axis === "Y") {
vector = new Vector3(0, 1, 0);
} else if (axis === "X") {
vector = new Vector3(1, 0, 0);
} else {
vector = new Vector3(0, 0, 1);
}
if (controls instanceof ArcballControls) {
// Get the vector from the camera to the target (controls.target)
const direction = new THREE.Vector3().subVectors(camera.position, controls.target).normalize();
// Create a quaternion representing the rotation around the Z axis
const quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle(vector, radians);
// Rotate the direction vector
direction.applyQuaternion(quaternion);
// Calculate the new position of the camera
const distance = camera.position.distanceTo(controls.target);
const newPosition = new THREE.Vector3().addVectors(controls.target, direction.multiplyScalar(distance));
// Apply the new position and up vector to the camera
camera.position.copy(newPosition);
camera.up.applyQuaternion(quaternion);
// Look at the target
camera.lookAt(controls.target);
// Update the camera's matrix and the controls
//camera.updateMatrixWorld();
controls.update();
} else if (controls instanceof TrackballControls) {
//store the current camera position
const position = controls.object.position.clone();
// Store the current look at target
const target = controls.target.clone();
// Get the camera's local Z-axis (up direction)
const cameraMatrix = new THREE.Matrix4();
cameraMatrix.lookAt(position, target, controls.object.up);
const cameraLocalZAxis = vector.applyMatrix4(cameraMatrix);
// Rotate the camera around its local Z-axis (up direction)
controls.object.up.applyAxisAngle(cameraLocalZAxis, radians);
// Update the controls
controls.update();
}
}