FQuat::FindBetween yields weird results

Hello.

I am using on a gravity system and for camera to work, I need to determine the rotation amount between forward and right vectors so I can add this amount to the character. It is working well, except for the Y axis gravity. Just to test, I assumed I am looking at X and thus -Z is my right vector. Just not to get affected by the random rotation amounts, I can this:

FQuat::FindBetween(FVector(1,0,0),FVector(0,0,-1));

and the result is -90 pitch, rest zero, as it should be.

However, when I fetch the same vector values from my calculations and use FindBetween with them, even though values are extremely similar, I get some crazy amounts.

This is the code block I am using:

   FRotator DestinationRotation = FQuat::FindBetween(GetActorUpVector(), GravityDirection*-1).Rotator().Add(GetActorRotation().Pitch, GetActorRotation().Yaw, GetActorRotation().Roll);

	FVector Test1(0.9999, -0.000001, -0.0003);
	FVector Test2(-0.03, 0.0009, -0.99);
	
	FQuat Findbetween = FQuat::FindBetween(Test1, Test2); 
	FRotator Test(Findbetween);
	PrintRotator(Test, "Test", 7); // Yields -88, 178, -178

	PrintRotator(DestinationRotation, "DestinationRotation", 5);
	FVector TargetForwardVector(UKismetMathLibrary::GetForwardVector(DestinationRotation));
	FVector TargetRightVector(UKismetMathLibrary::GetRightVector(DestinationRotation));
	PrintVector(TargetForwardVector, "DestinationForward", 3);
	PrintVector(TargetRightVector, "DestinationRight", 4);

	FRotator RotationOfTurn(FQuat::FindBetween(TargetForwardVector, TargetRightVector));
	PrintRotator(RotationOfTurn, "RotationOfTurn", 6);

Printer code in case there might be a mistake:

void ACryOnCharacter::PrintRotator(const FRotator &Rotator, FString Name, int32 Permanumber)
{
	GEngine->AddOnScreenDebugMessage(Permanumber, 50.f, FColor::Yellow, Name + "Pitch: " + FString::SanitizeFloat(Rotator.Pitch) + " Yaw: " + FString::SanitizeFloat(Rotator.Yaw) + " Roll: " + FString::SanitizeFloat(Rotator.Roll));
}

Print log: http://imgur.com/a/aWUDP

The approach I am following is:
I linetrace to a wall, get the normal and set the gravity to the - of it, works alright. I use FindBetween with actor up vector and the normal to determine the angle change of my character and apply it during tick, which also works alright. But to determine my turning rotation, I find the characters future rotation (From the previous sentence) get Up and Right vectors from it and use FindBetween again to find the 90 degree angle between the two vectors. The funny thing is, it only occurs when Y axis is the gravitation axis and FindBetween should never yield any angle greater than 90 degrees when used on up and forward vectors.

Comparing the results with the output log, everything goes well, until

FRotator RotationOfTurn(FQuat::FindBetween(TargetForwardVector, TargetRightVector));

Also, is there an easier way to figure out my turning angle for the character? A more efficient math like finding a rotator just directly from a normal?

Hey Naocan-

I tried testing FQuat::FindBetween() however the results I’m getting are not consistent with what you’ve reproted. Specifically, when I print the result of FQuat::FindBetween(FVector(1,0,0),FVector(0,0,-1)); to my veiewport, the result is (X=0 Y=0.707106769 Z=0 W=0.707106769) rather than (-90, 0, 0). Can you confirm what you see when you use the following code:

GEngine->AddOnScreenDebugMessage(-1, 4.f, FColor::Yellow, FQuat::FindBetween(FVector(1,0,0), FVector(0,0,-1)).ToString());

For your question of another way to find a rotator from a normal, I would suggest experimenting with KismetMathLibrary::RotatorFromAxisAndAngle(FVector Axis, float Angle);.

Hello ,

Yes I the same amount after using it but -90,0,0 is the Rotator amount of that.

GEngine->AddOnScreenDebugMessage(-1, 4.f, FColor::Yellow, FQuat::FindBetween(FVector(1, 0, 0), FVector(0, 0, -1)).ToString()); // (X=0 Y=0.707106769 Z=0 W=0.707106769)
GEngine->AddOnScreenDebugMessage(-1, 4.f, FColor::Yellow, FQuat::FindBetween(FVector(1, 0, 0), FVector(0, 0, -1)).Rotator().ToString()); // (P=-90, Y=0, R=0)

Edit: Wrong code paste

Another finding:
Instead of using

FRotator DestinationRotation = FQuat::FindBetween(GetActorUpVector(), GravityDirection*-1).Rotator().Add(GetActorRotation().Pitch, GetActorRotation().Yaw, GetActorRotation().Roll);

I used:

FRotator DestinationRotation = UKismetMathLibrary::MakeRotFromZ(GravityDirection*-1);

that gives slightly better results as I dont need the actual rotation, only what would be my relative rotation change for the mouse turn. Works even better than the previous, however http://imgur.com/a/pvH11

As you can see, RotationOfTurn is

FQuat::FindBetween(FVector(1, 0, 0), FVector(0, 0, -1)).Rotator()

almost, with that 0.000004 on DestinationForward.Y yet my RotationOfTurn is -90,90,-90

I thought about GimbalLock here, so that might be the only culprit but it happens the same on the wall of the opposite side (Where Gravity is +Y axis instead of -Y)

I solved it simply by using AddActorLocalRotation instead of all my calculations. Not marking it as resolved yet in case there might be a problem with the function itself.

Ahh, I didn’t understand at first that you were reporting the rotation after calling FindBetween(). What appears to be happening is FQuat::FindBetween first calls

const float NormAB = FMath::Sqrt(A.SizeSquared() * B.SizeSquared());

Where A and B are the two vectors being passed in. NormAB is then used to calculate

float W = NormAB + FVector::DotProduct(A, B);

When your vectors are (1,0,0) and (0,0,-1), W works out to be 1. However with the Test vectors that you mentioned, W works out to be closer to ~0.96066.

This is where things get interesting, as the resulting FQuat that is created:

FQuat Result = FQuat(A.Y * B.Z - A.Z * B.Y  ,  A.Z * B.X - A.X * B.Z  ,  A.X * B.Y - A.Y * B.X  ,  W);

is then normalized ( Result.Normalize()). This is where the value of W plays a large role. When W = 1, your original vectors give a result of -90 pitch rotation as you mentioned. If you use these lines of code rather than calling FindBetween() - and set W to a hard value of 1 - then the Test vectors work out to a rotation of (P -89.4165, Y 5.1778, R -5.1266).

Conversely, if you set W = 0.96066, then the vectors (1,0,0) (0,0,-1) works out to (P -87.7011, Y 180.0, R 180.0). It appears the math in the Normalize function is running into floating point / rounding error based on the value of W. I’m glad to hear that you were able to find a solution that works for you, however it seems to me that the FindBetween function is functioning as intended.

Cheers