Concatenate FQuat rotation resulting in Gimbal Lock?

I have nearly the same scenario described in this post:

Rotate Pawn in full 360 degrees

In fact, I’ve combined a few of the code samples and suggestions from responses to arrive at the scenario described.

From my custom APlayerController subclass:

void AShipController::AddPitchInput(float Rate)
{
	auto PitchRot = FRotator(Rate, 0, 0);
	
	auto Transform = GetActorTransform();
	Transform.ConcatenateRotation(PitchRot.Quaternion());
	Transform.NormalizeRotation();
	SetControlRotation(Transform.Rotator());
	
}

void AShipController::AddYawInput(float Rate)
{
	auto YawRot = FRotator(0, Rate, 0);

	auto Transform = GetActorTransform();
	Transform.ConcatenateRotation(YawRot.Quaternion());
	Transform.NormalizeRotation();
	SetControlRotation(Transform.Rotator());
	
}

void AShipController::AddRollInput(float Rate)
{
	auto RollRot = FRotator(0, 0, Rate);

	auto Transform = GetActorTransform();
	Transform.ConcatenateRotation(RollRot.Quaternion());
	Transform.NormalizeRotation();
	SetControlRotation(Transform.Rotator());
	
}

Here is a video of the results:

Gimbal Lock?

There are some other scaling factors on the pitch and yaw rate (this is why the yaw is slow to begin, but when the aircraft slow down yaw increase), but this can be ignored.

There appears to be no limit on yaw, right? I can turn around completely.

Roll is still locked to a 180 degree range. I was hoping to exceed this limit by overriding my controller classes’ AddRollInput(float) method. I call my pawn’s AddControllerRollInput(Rate) method form a function bound to input.

void SetupPlayerInputComponent(UInputComponent * PlayerInputComponent) {
...
PlayerInputComponent->BindAxis("TurnRate", this, &AShipPawn::TurnAtRate);
...
}

void AShipPawn::RollAtRate(float Rate)
{
	float RollMod = Rate * BaseRollRate * GetWorld()->GetDeltaSeconds();
	AddControllerRollInput(RollMod);
}

I repeat this pattern for the Pitch and Yaw input.

With this I have basically the same limitations as the default controller. My aircraft is limited to 90 degrees pitch and roll in any direction. However my limitation to pitch comes in the form of with what I assume is Gimbal Lock?

I’ve also tried the rotation calculation described in the answer here:

Pawn pitch rotation of 360 degree

I have this code, currently commented out, in my AddInput methods.

//auto Rotation = GetActorTransform().Rotator();
//auto NewRotation = FQuat(RollRot) * FQuat(Rotation);
//NewRotation.Normalize();
//SetControlRotation(NewRotation.Rotator());

This lead to some axis issues, (pitch binding controlled roll until yaw was turned to point ship down roll axis… I can make a video if needed) but perhaps I just applied the wrong vector product?

EDIT:
I decided to try the other “cross product”(?) of FQuat vectors and that turned out closer to what I wanted. By “other” I mean:

 FQuat(Rotation) * FQuat(PitchRot)

vs

FQuat(PitchRot) * FQuat(Rotation);

Here is a video of the code commented out in the op.

Bad cross product

Things to know, I’m only using the pitch and yaw bound inputs in this video. So, to start using pitch up or down was controlling the roll. After I turn 90 degrees to the right pitch binding really controls the pitch.

To try the other cross product (I really hope this is right term) I update my code to the following:

void AShipController::AddPitchInput(float Rate)
{
	auto PitchRot = FRotator(Rate, 0, 0);
	
	//auto Transform = GetActorTransform();
	//Transform.ConcatenateRotation(PitchRot.Quaternion());
	//Transform.NormalizeRotation();
	//SetControlRotation(Transform.Rotator());
	
	auto Rotation = GetActorTransform().Rotator();
	auto NewRotation = FQuat(Rotation) * FQuat(PitchRot);
	NewRotation.Normalize();
	SetControlRotation(NewRotation.Rotator());
}

void AShipController::AddYawInput(float Rate)
{
	auto YawRot = FRotator(0, Rate, 0);
	
	auto Rotation = GetActorTransform().Rotator();
	auto NewRotation = FQuat(Rotation) * FQuat(YawRot);
	NewRotation.Normalize();
	SetControlRotation(NewRotation.Rotator());
}

void AShipController::AddRollInput(float Rate)
{
	auto RollRot = FRotator(0, 0, Rate);
    
	auto Rotation = GetActorTransform().Rotator();
	auto NewRotation = FQuat(Rotation) * FQuat(RollRot)  ;
	NewRotation.Normalize();
	SetControlRotation(NewRotation.Rotator());
}

Here is a video of the updated code:

Still Locking

Immediately the pitch input aligns with the correct axis, and appears to stay consistent while yaw increases/decreases. Roll slowly changes as well though, which I don’t mind so much. Still, pitch over 90 degree causes Gimbal Lock?