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
\$\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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.