Is there anyway to set control rotation with a quaternion?

I’m trying to have 6DoF with my control rotation. I keep running into gimbal lock. My world is round and the players feet should always point towards the planets center. When I try to add in the mouse input X rotation I believe it is causing it. I keep reading everywhere that keeping rotations as quaternions will eliminate this but I don’t see how I can when SetControlRotation only takes a rotator.

1 Like

FRotators are not Euler internally. They let you modify the rotation of a character with Euler because it’s intuitive as opposed to entering Quaternion values directly. You can calculate your quaternion and assign it to your rotator.

FRotator NewRot(YourCalculatedQuaternion);
SetControlRotation(NewRot);

This next part I’m writing off the top of my head so there might be mistakes, but to calculate that quaternion assuming the player is standing on a sphere I would try something like the following:

If VecUp is the ‘up’ vector at the point on the sphere the player is standing, then:

VecUp = PlayerActor.GetActorLocation() - SphereActor.GetActorLocation;
VecUp.Normalize();
FQuat YourCalculatedQuaternion = FQuat::FindBetween(FVector::UpVector, VecUp);

That would put him in the correct orientation (feet on ground, head towards the sky), then to spin him in place to the desired facing:

FQuat LocalSpin(FVector::UpVector, MyYaw);
YourCalculatedQuaternion *= LocalSpin;

You would keep track of what ‘Yaw’ or spin round his center he has at the time.

I haven’t used fquat::findbetween yet so I’m hopeful. I was using makefromzx which could have been the problem. I had setup the yaw part though, and did the fquat * fquat part. It still produced gimbal lock, but maybe this new way of getting the correct vertical orientation will add in better. I’ll check it out. Thank you!

Yes, I replied to one of your threads where you ask about your method. I recommended trying to change your line:

orgRot.ConcatenateRotation(addInRot);

to

orgRot.SetRotation(addInRot * orgRot.GetRotation());

The method I describe here might also work. Just thought a slight modification to what you already had would be a quick test.

Haha, yeah it would have been a quick test but I spent today going through this response I received. It required me to use a pawn, and movement component instead of character and CMP. So far it has been worth it because I have it working using this method. Its rough but it appears to avoid gimbal lock.

The link

Okay I I put the Character and CharacterMovementComponent back in play (they have so much nice functionality built into them already). I tried to apply what you wrote above

FQuat upRot(FQuat::FindBetween(FVector::UpVector, GetCapsuleComponent()->GetComponentLocation().GetSafeNormal()));
	FQuat yawRot(FVector::UpVector, mouseX);
	Controller->SetControlRotation((upRot * yawRot).Rotator());

This appears to point me in the right vertical direction but adding in the mouse X rotation results in twitching (like it’s trying to Yaw but not being allowed to). And I just noticed that when I walk my character over to the axis poles of the world - the character still flips over on his head.

Finally!!! The solution was to set the capsule upvector rotation first using MakeFromZX. Then using the capsules AddLocalRotation (which the control rotation doesn’t have) I add in the mouse X input rotation. Then finally set the control rotation using the capsule rotations. No Gimbal Lock. I had to set bUseControllerRotation (Pitch, Roll, And Yaw) to False first.

    //Initialize component...
    bUseControllerRotationPitch = false;
    bUseControllerRotationRoll = false;
    bUseControllerRotationYaw = false;
    
    //Tick Function...
 //####### Adjusted UpVector for the capsule
    GetCapsuleComponent()->SetRelativeRotation(FRotationMatrix::MakeFromZX(GetCapsuleComponent()-       >GetComponentLocation(), GetCapsuleComponent()->GetForwardVector()).ToQuat());
//####### Add in mouse X input rotation
    GetCapsuleComponent()->AddLocalRotation(FRotator(0, mouseX, 0));
//####### Set the control rotation using the capsules rotation
    Controller->SetControlRotation(GetCapsuleComponent()->GetComponentRotation());

I’m having trouble with gimbal lock and it seems you’ve grappled with this several times before. Your solution above doesn’t work for me, but I’m trying to set rotation of an actor, not component. I assume the capsule component is the root component of your character actor so why are you not just adjusting the actor rotation? Maybe your answer will help me better understand what I’m doing wrong. Thanks in advance.

Yeah I’ve definitely gone the rounds with it. To be honest I’ve adjusted this so many times I’ve almost forgot my motivations between the different versions. In my setup I’m trying to point the feet of my character capsule towards the world center. This is done because the world is round. I had already gone through the Character Movement Component and retro fitted it to allow for spherical movement. Looking at my current code I’ve simplified it even further than the above answer. Perhaps it will help.

Capsule->SetRelativeRotation(FRotationMatrix::MakeFromZX(Capsule->GetComponentLocation(), inVec).ToQuat());

The trouble is this all ready depends on your characters setup. The above code relies on this vector to point it the direction I want to be looking, so I grab the last movement input vector. I do this because I’ve not moved the camera to third person, and my character points looks and runs in the direction the left analog stick is pointing. So the code above uses the code below to maintain a relative looking direction, while aiming the orientation of the capsule.

FVector lastInVec = GetLastMovementInputVector();

Originally I used a different setup which swiveled the capsule based on Mouse X input, which was added in using the “addLocalRotation”, then I would set my control rotation using the modified capsule rotation. This avoided many problems I was encountering with gimbal lock. If I remember correctly the addLocalRotation function was key to this.

Thanks for your response Thumper. Yeah, I’m trying to do about the same as what you’re doing, except I’ve set up a vehicle pawn with a FloatingPawnMovement component. You said you had already gone through the Character Movement component? Do you remember what you had to change there? All I’m really using the FloatingPawnMovement for is to apply forward acceleration, so I can’t see how it would influence rotation.

	FMatrix TargetRotationMatrix = FRotationMatrix::MakeFromZX(VisiOutHit.ImpactNormal, GetActorForwardVector());
	FQuat TargetRotationQuat = TargetRotationMatrix.ToQuat();
	TargetRotationQuat.Normalize();

	FTransform NewTransform = GetActorTransform();
	FQuat CurrentRotationQuat = NewTransform.GetRotation();
	FQuat InverseCurrentRotationQuat = CurrentRotationQuat.Inverse();

	FQuat LerpedRotationQuat = FQuat::FastLerp(CurrentRotationQuat, TargetRotationQuat, .1f);
	LerpedRotationQuat.Normalize();

	NewTransform.SetRotation(CurrentRotationQuat * InverseCurrentRotationQuat * LerpedRotationQuat);
	NewTransform.NormalizeRotation();
	SetActorTransform(NewTransform);

Like you, I have a spherical “world” and I need the pawn to hug the surface as it moves around. The above code starts to gimbal lock near the equator. I’m not even adding any yaw/mouseX input yet, just trying to avoid the gimbal lock first. And as far as I can tell I’m deriving my rotation values strictly with quaternion operations.

Attached is a screen grab of the real message I wrote. As you can see I went over my character limit, so I posted it in the image form :slight_smile: alt text

Here are those functions in copy pastable form.

//To Orthagonal space
FVector UKoreCharMoveComp::toOrthoSpace(FVector VectorToConvert) const
{
	FTransform compToWorldTrans = CharacterOwner->GetCapsuleComponent()->GetComponentTransform();
	FVector rotatedVector = compToWorldTrans.InverseTransformVectorNoScale(VectorToConvert);
	return rotatedVector;
}
//From Orthagonal space
FVector UKoreCharMoveComp::fromOrthoSpace(FVector VectorToConvert) const
{
	FTransform compToWorldTrans = CharacterOwner->GetCapsuleComponent()->GetComponentTransform();
	FVector rotatedVector = compToWorldTrans.TransformVectorNoScale(VectorToConvert);
	return rotatedVector;
}

Thanks again, this is very useful. Yeah, I want the pawn’s rotation to conform to the topo surface, and feeding in the impact normal as Z for the makeFromZX method seems to work as it should. Now I suspect the problem is in the forward movement, which I was assuming to be calculated relatively by the FloatingPawnMovement component. Thanks for pointing out that this is a bad assumption, I think I see where the forward vector for movement is being converted to world space, which is obviously not what I want. Fortunately the FloatingPawnMovement component is far less complicated than the Character Movement component. I actually think I’ll just write my own forward movement function rather than modifying the UE codebase.

So turns out the problem was not with the rotation, my above code does what it’s supposed to. But I was also adjusting the pawn’s location every tick to keep it at a fixed distance from the topo surface, and of course I was only adjusting the pawn’s Z value to do this. Unfortunately for me it kind of simulated gimbal lock since updating the pawn’s world Z made its movement progressively more erratic as it neared the world “equator”. So, a total red herring. What a waste of time. Thanks a lot for jumping in on this closed thread anyway Thumper, I think your world space conversion functions will come in handy at some point.

Hey there,

I’ve been looking over this post (and other), slowing trying to understand the code and quaternion math. And Thumper you have a lot of comments on this subject!

I think I have a similar problem with rotation, but I’m working with a different camera position, and controller inputs.

I was just hoping one of you could maybe help out on my question:

thanks in advance!

Hello! I am having issues with gimbal lock as well, and as I read the comments y’all posted, I see that you have experience with the matter. My issue will probably seem trivial as it does not involve a sphere or 6dof but rather less (or make it harder in some convoluted way…I hope not hehe).

Here is my post. I would love help on this as I am continuing my months of research to smoothly rotate around only one axis! :smiley:

Thank you for your time!