2
\$\begingroup\$

I created an orbit camera (sometimes called turntable camera; similar to the one with the "use UE3 orbit controls" setting in a static mesh view).

I attached the camera to a USpringArmComponent with a TargetArmLength set to 400. In the tick function, I rotate the arm with this simple method:

        // Simple, clamped version
        FRotator Rotation = CameraSpringArm->GetComponentRotation();
        Rotation.Yaw += CameraInput.X * CameraRotationSpeed;
        Rotation.Pitch = FMath::Clamp(Rotation.Pitch + CameraInput.Y * CameraRotationSpeed, -85.0f, 85.0f);
        CameraSpringArm->SetRelativeRotation(Rotation);

I had to clamp the pitch to hide the gimbal lock problem. But this prevent users to rotate completely around objects. I don't understand why the Z rotation (the yaw) occurs on the world z axis ( FVector::UpVector which is (0, 0, 1)) and not on the local z axis. It turns out that this is exactly what I want.

I tried to solve this gimbal lock problem with this other method:

        // Taken from https://answers.unrealengine.com/questions/232923/how-can-i-avoid-gimbal-lock-in-code.html
        FRotator RotationDelta(CameraInput.Y * CameraRotationSpeed, CameraInput.X * CameraRotationSpeed, 0.f);
        FTransform NewTransform = CameraSpringArm->GetComponentTransform();
        NewTransform.ConcatenateRotation(RotationDelta.Quaternion());
        NewTransform.NormalizeRotation();
        CameraSpringArm->SetWorldTransform(NewTransform);

It works, but this time, the Z rotation (yaw) occurs on the local Z axis. How can I change it to rotate around the world Z axis, and the local Y axis, without gimbal lock?

I tried this hybrid solution, but the gimbal lock is still there:

        // Hybrid
        FRotator RotationDelta(CameraInput.Y * CameraRotationSpeed, 0.f, 0.f);
        FTransform Transform = CameraSpringArm->GetComponentTransform();
        FRotator Rotation = CameraSpringArm->GetComponentRotation();
        Rotation.Yaw += CameraInput.X * CameraRotationSpeed;
        Transform.SetRotation(Rotation.Quaternion());
        Transform.ConcatenateRotation(RotationDelta.Quaternion());
        Transform.NormalizeRotation();
        CameraSpringArm->SetWorldTransform(Transform);

I did solve this problem (a long time ago) in OpenGL using quaternions, so I tried this version:

        // Quaternion
        FRotator Rotator = CameraSpringArm->GetComponentRotation();
        FQuat Quaternion = Rotator.Quaternion();
        // Rotate around the world Z axis:
        Quaternion *= FQuat(FVector::UpVector, FMath::DegreesToRadians(CameraInput.X * CameraRotationSpeed));
        // Rotate around the local Y axis:
        Quaternion *= FQuat(Rotation.RotateVector(FVector::RightVector), FMath::DegreesToRadians(CameraInput.Y * CameraRotationSpeed));
        CameraSpringArm->SetRelativeTransform(Quaternion);

But this does not work. I also tried this:

        // Quaternion + transform
        FTransform Transform = CameraSpringArm->GetComponentTransform();
        Transform.ConcatenateRotation(FQuat(FVector::UpVector, FMath::DegreesToRadians(CameraInput.X * CameraRotationSpeed)));
        Transform.ConcatenateRotation(FQuat(Rotation.RotateVector(FVector::RightVector), FMath::DegreesToRadians(CameraInput.Y * CameraRotationSpeed)));
        Transform.NormalizeRotation();
        CameraSpringArm->SetWorldTransform(Transform);

without success.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

My hybrid solution works!

The gimbal lock was still there because I had a rotation lag on the CameraSpringArm, which does not support 6 DoF.

Removing the camera lag (CameraSpringArm->bEnableCameraRotationLag = false;) solved the problem.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .