Do you use Quarternions? [Fixed!]

If so, pls help.

I’ve been working for a while on a game that revolves around a 6-DOF pawn perched at the top of a spring, essentially, a hovering vehicle. Everything has been working relatively fine until I started adding the finer details like rolling while strafing / steering, pitch control and yaw rotation.

I’m experiencing some odd rotation issues which I suspect are linked to the way I’m doing rotations. Although everything has been playing nicely until now, the introduction of rolling and pitching is giving me a few headaches. I’m currently using a combination of Matrix rotation/un-rotation and also FRotators, the latter of which is giving me the trouble. I’m calculating these additional vectors then rotating them by the crafts rotation, which at different angles gives me rotation around the Z-axis, something I really really don’t want. I suspect this is due to using Euler rotation, and It seems I’ll have to switch to using Quarternions instead.

Thing is, I’m not really sure where to start. Most YouTube tutorials explain the math more than they explain how to put them into practice, and I suspect they are not as intuitive as Euler rotations, so does anybody have a good place to start with Quarternions in Unreal, and how to use them?

Well, I had to figure them into my FirstPerson Space Prototype and even though I used them in Unity and Ogre3d before, they still give me some headache everytime.

The single best thing that helped me was this picture. Quaternions are nothing else but vectors that have a rotation around their normal. Visualizing them this way always helps me alot.

I usually just handle rotation for x,y,z as separat matrix objects, then when the final rotation is required, i convert each to a quaternion, and add the quaternions together.
The problem usually appear when adding rotation values to each other, or when certain angels come close to one of the major axis. That’s at least the feeling I get.

I rely on quaternions, since I am working on a space game with full 6 DOF as well as other projects, I even need it just to do aim offsets that replicate over network without doing weird twirls periodically!

**FQuat::Slerp **is your friend! Spherical Interpolation!

Slerp is the only way I can ensure networked replicated rotations for all proxies interpolate correctly!

You can just use FQuats like you would Rotators, by converting



FQuat SomeQuat
FRotator = SomeQuat.Rotator()

FRotator SomeRot
FQuat = SomeRot.Quaternion()


Summary: you can define your quats as the familiar FRotator and then convert, but do all operations on the quaternions, and just convert the result back to FRotator as needed

or many UE4 functions will accept FQuat directly, so you dont have to convert back.

like FTransform::SetRotation()

Rama

Hi,

With FRotator you are using euler-angles (Pich/Roll/Yaw) for the representation of a rotation which can lead to the “gimbal lock problem”.

Quaternions (FQuat) and rotation matrices (FRotationMatrix) are not having those issues, although the math can be difficult to understand.
The math behind them is not really important, though, since you are never confronted with the details during daily use.

Especially quaternions will cause a lot of headaches if you try to understand them :).
It also won’t really help to understand the basic concepts of 3d-space-conversions either.

If you want a user-editable rotation solution, euler angles are best, because it is in a user-readable format and can be set externally.
If you want to avoid gimbal lock, rotating your orientation by cumulating matrices or quaternions **internally **is a must if you rotate around more than 1 axis.

Using quaternions is the fastest (only 4*4 multiplications) and also most beautiful way of cumulating rotations.

See for example:

FQuat::Slerp for the win :slight_smile:

Good luck :slight_smile:

Thanks for the help Rama and , I feel like I may have to understand the math behind Quats to make the most out of them in this case. Here’s my scenario.

I perform a bunch of operations on my vehices angular velocity in order to calculate what it should be. ‘Alpha’ is my Angular Acceleration, which I modify then add to the current velocity here’s an example. Note: you don’t have to read it all through and understand it, it’s just a small snippet that shows the way I manipulate the value.



        Omega = UpdatedComponent->GetPhysicsAngularVelocity();		// Set Omega Initial Value

        // --- Add a damping force to the crafts rotation in X and Y  ----------------------------------------------
	Alpha.X = -HoverEngineProperties.AlphaDamp * Omega.X;
	Alpha.Y = -HoverEngineProperties.AlphaDamp * Omega.Y;

        // --- Roll slightly when the craft strafes ----------------------------------------------
	FVector RollStrafe = UpdatedComponent->GetForwardVector() * (ScaledTrack * -HoverEngineProperties.RollStrafe * Straf);
	FVector RollVector = FRotator(UpdatedComponent->GetComponentRotation()).RotateVector(FVector(ScaledTrack * -HoverEngineProperties.RollStrafe * Straf, 0.f, 0.f));
	RollVector.Z = 0.f; // Force-Override This To Remove Noise. Above Calculation Might Need Some Work!
	Alpha += RollVector;

	// --- Pitch With Thrusting & Pitch Value ----------------------------------------------
 	FVector AimPitch = FVector::ZeroVector;
	AimPitch.Y = (HoverEngineProperties.AlphaTrack * HoverEngineProperties.PitchPitch * InputStates.PitchValue) + (ScaledTrack * HoverEngineProperties.PitchThrust * InputStates.ThrustValue);
  	FVector WSAimPitch = FRotator(UpdatedComponent->GetComponentRotation()).RotateVector(AimPitch);
	WSAimPitch.Z = 0.f;  // Force-Override This To Remove Noise. Above Calculation Might Need Some Work!
 	Alpha += WSAimPitch;

        // --- APPLY FINAL VALUES TO UPDATED COMPONENT ------------------------------------------
	Omega += Alpha * TimeStep;
        UpdatedComponent->SetPhysicsAngularVelocity(Omega);


So, this combined with other modifications to the ‘Accel’ value give me strange rotational problems. Not Gimbal Lock, but something else. You’ll notice I have to set RollVector.Z and WSAImPitch.Z to zero, otherwise my vehicle starts experiencing rotation around the Z-axis. Would a Quarternion save me this problem and prevent my having to somewhat hackily nuke out the .Z component of the Vector? If so, what parts do I need to turn into Quats?

EDIT: Ooooo I got a badge :smiley:

In your example, you are controling the velocities around your axes and not the result-orientation, therefore quaternions can’t be used to control it :slight_smile:

If you modify the angular velocity of the physic-object directly, and rotate around the x- and y-axes, I am not sure why the physicsystem would internally set the z component of the velocity to a value different from zero the next frame.
Perhaps angular friction or dampening leads to this behavior. Perhaps this is normal behavior but I would also not expect it.

Have you tried to control your angular rotation by setting Omega.Z to zero instead of keeping the z-velocity of the last frame ?

How do you initialize/calc alpha.z above ?

Alpha Z is calculated with the following. Basically everything works fine an independently, until Roll and Pitch is introduced. Here’s the Z (Steering) Control:

Once again, you can see ‘Steer Roll’ also has to be nuked in the .Z axis.



	// --- Yaw And Roll With Steering Control ----------------------------------------------
	float SteerVal = 0.f;
	float TurnRate = HoverEngineProperties.OmegaSpin + (HoverEngineProperties.OmegaTurn - HoverEngineProperties.OmegaSpin) * ControlMag;
	if (TurnRate > 0.f)
	{
		SteerVal = HoverEngineProperties.AlphaSteer * FMath::Clamp(InputStates.SteerValue - Omega.Z / TurnRate, -1.0f, 1.0f);
	}
	else
	{
		SteerVal = 0.f;
	}
	FVector SteerYaw = SteerVal * UpdatedComponent->GetUpVector();

	// --- Apply Locally-Rotated Yaw And Roll When Steering --------------------------------
	FVector SteerRoll = FRotator(UpdatedComponent->GetComponentRotation()).RotateVector(FVector(FMath::Clamp(InputStates.SteerValue, -1.0f, 1.0f) * ScaledTrack * -HoverEngineProperties.RollSteer, 0.f, 0.f));
	SteerRoll.Z = 0.f; // Force-Override This To Remove Noise. Above Calculation Might Need Some Work!
	Alpha += SteerRoll + SteerYaw;


Easy Quaternion Slerp For Rotators

Feel free to add this to your static function library!

I just encountered a case, just now, where FMath::RInterpTo does not work and simply jitters, just by passing the rotators into Slerp and converting the output I resolved the matter and can continue with other matters.

Note unlike RInterpTo, Slerp uses alpha, so its always a % of the remaining interpolation amount.

0.1 is a great starting point while testing for your ideal alpha.

Rotator Slerp Function For You



//Rama's ease-of-use Slerp wrapper. Lets you pass in rotators and uses power of Slerp to interpolate rotations correctly.
// use this where RInterpTo does not work correctly. 

//Try using 0.1 for the alpha for your initial test to get a sense of the interp speed.
//~~~~~~~~~~~~~~~~~~~~
//Quaternion Spherical Lerp
//~~~~~~~~~~~~~~~~~~~~
static FORCEINLINE FRotator Slerp(const FRotator& A, const FRotator& B, const float Alpha)
{
	return FQuat::Slerp(A.Quaternion(), B.Quaternion(),Alpha).Rotator();
}


How do you initialize alpha ?
Alpha.z is different from zero because of “FVector SteerYaw = SteerVal * UpdatedComponent->GetUpVector();”
Why not clearing out Omega.z, if you do not want rotation around the z axis at all ?

If you want to use quaternions, you would have to calculate the final rotation of the actor and set it with AActor::SetActorRotation.
AActor::SetActorRotation accepts only a FRotator, so you would have to convert your final rotation back into a FRotator before you set the rotation.

If you are setting the rotation directly, the physic object can lose penetration infos from last frame, which will not look as accurate as if you control the angular velocities, which can be a problem if you rotate arbitrary shaped dynamic bodies against each other, but is normally not a problem for dynamic vs static intersections.

In my opinion, if the z clearing works, why would you change anything?

Hi Rama, thanks for the info although that’s still not a way I can use the Quaternions at the moment.

My issue is that my vectors just do not seem to rotate as I think they should. I’ve tried rotating the vector by Quarternions and that doesn’t seem to be the issue, and even altering the vector based on the directional vectors of the component (and the Quaternion too). Yet for some reason, when adding Pitch Rotation, I get a small amount of Yaw rotation too which just builds up.

Here’s the simplified code:



PitchAccel = FVector(0.f, ((HoverEngineProperties.AlphaTrack * HoverEngineProperties.PitchPitch * InputStates.PitchValue) + (ScaledTrack * HoverEngineProperties.PitchThrust * InputStates.ThrustValue)), 0.f);
AngularAccel += QuaternionRotation.RotateVector(PitchAccel);

UpdatedComponent->SetPhysicsAngularVelocity(AngularAccel * DeltaSeconds);



This is the result. Am I just doing something fundamentally wrong here? I just end up spinning on Z when manipulating the pitch value, which just makes no sense at all. Notice the debug strings on the top left.

https://www.youtube.com/watch?v=8qbn-rHC0ZY

Hey , sorry I missed your message first time around:

Alpha is initialized every frame to FVector::ZeroVector. I don’t set Omega.Z to zero because at the end of the frame, I do UpdatedComponent->SetPhysicsAngularVelocity(Omega);. I want to keep the existing omega.Z value in case of collisions etc, otherwise I’ll smash into something and won’t respond to it.

I also figured out why I shouldn’t be clearing the Z vector for each part. Imagine I reach a rotation in mid-air where the side of my craft is parallel to the floor. At this point I may want to pitch up or down still, but I won’t be able to since Z will be forced to zero all the time, and I’ll have to wait until I hit the ground and the auto-levelling system takes over.

The most sensible method for me here is to adjust the ‘Alpha’ value based on the axes of the craft, it’s just bizarre that it doesn’t seem to work the way it should.

Hey TheJamsh,

I hope you will solve that %§"$ rotation problem :). I must admit that I did not have a look at how you are calculating AngularAccel exactly, but made some assumptions of how the spacecraft should move after just setting x/y angular velocities.
The video is very helpful :).

Have you tried that ? As far as I know you are only losing your angular momentum. The physic system should solve for free collisions nevertheless.
Do you really need a perfect physic behavior for the stated situation or is it just hypothetical?
Because tuning a physic system to emulate “real live behavior” is the major work, I would not code anything which is not really needed.
(Your problem is an advanced one, so to say)

But that answer is probably not very helpful.

There was an article somewhere flying around explaining the different ways to control a game-object with the various physic-methods, but I can currently not find it.

Another way of seeing things:

  1. You are setting the “wished” velocities of your physic object
  2. The physic system “tries” to execute your forces as good as it can, so that there are minimal or no intersections next frame

This is part of the problem. You will never have perfect control of your actor if you are using physic.

Staying in physics-land:
would it not be best to have some sort of attached spring which backrotates your ship to your desired pitch angle?

See for example Constraint Angular Velocity Drive - World Creation - Epic Developer Community Forums

But, I wonder if that is not an even more complicated solution to your problem.

I actually have the back-rotating thing built in, but I want it to align only to the surface normal when it’s at regular altitude, but if it jumps off of a ramp or cliff, I want it to be able to spin as if it’s a 6-DOF object, but it just falls to the ground.

if it makes it any easier, these are the physics I’m trying to copy (sorry, best video I can find right now)

So it turns out that the extra rotation is caused by combining this regular Pitch/Roll/Yaw rotation (which works fine on it’s own), with the Self-Righting rotation/force. Rotating around the actors local vectors seems 100% fine until the following code and the above code come into play with one another.

Here’s the self-righting code, which simulates a rotational force and applies it directly as velocity. What I find odd, is that the self-righting code does NOTHING in the Z-axis at all, so how this is happening I have no idea. So bloody confused… never doing Physics programming after this again :stuck_out_tongue:



	// --- Transform Alpha To Correct Local Rotation --------------------------------------------------------
	/* Get the Z-UnRotated Cross Product of The Actors Right Vector and the Normal */
	FVector UnrotatedRCrossN = FRotator(FRotationMatrix::MakeFromZX(Normal, FVector::CrossProduct(UpdatedComponent->GetRightVector(), Normal)).Rotator()).UnrotateVector(UpdatedComponent->GetRightVector());
	///* Get the Z-Orientated Rotation Of the Cross Product Of the Z-Unrotated Vector, and the Normal */
	FRotator FinalRotation = FRotationMatrix::MakeFromZX(Normal, FVector::CrossProduct(UnrotatedRCrossN, Normal)).Rotator();
	///* Get the Right and Front Axis Vectors of the Rotation */
	FVector NormRight = FRotationMatrix(FinalRotation).GetScaledAxis(EAxis::Y);
	FVector NormFront = FRotationMatrix(FinalRotation).GetScaledAxis(EAxis::X);

	FVector RightVector = FVector(ScaledTrack * FVector::DotProduct(NormRight, UpdatedComponent->GetUpVector()), ScaledTrack * FVector::DotProduct(NormFront, UpdatedComponent->GetUpVector()), 0.f);
        Alpha.X += RightVector.X;
        Alpha.Y -= RightVector.Y;


Anybody have any ideas what could be happening?

Difficult to say without seeing the complete code.

Some other wild guesses:

You could try to enable sub stepping.
See for example:

You could try to alter the angular damping for the body and see if it makes any difference.

You could use an angular velocity drive, which seems to be the best way to move your actor physically while maintaining gamecontrol with the drive parameters.

You could try to lock the z-rotation with various methods (clear out z, using a 6dof joint with locked z rotation,…) until a collision occurs.

Definitely interesting ideas, I removed all Angular Damping on the body, not tried using an Angular Velocity Drive yet. Bit worried about using sub-stepping since this thing will be among another couple of hundred or so simulated vehicles, will have to check performance there :stuck_out_tongue:

Velocity Drive sounds interesting… might give that a go if I run out of ideas.

I find it also interesting to know why z starts to rotate, but if you can exclude wrong calculations, I guess only a physx guy can explain, why the solvers tend to be inaccurate/unstable with some given input.
Perhaps this is normal behavior as errors accumulate ? Seems to be wrong.
You could perhaps try to find an physx engineer if you have a repro.

Good luck

I FINALLY FIXED IT.

Necro/Posting here because I’m going to cry with glee. The issue was the self-righting code, it basically tried to solve 3D rotation using only 2 Axes. Basically self-inflicted gimbal lock when there was no reason to have any.

New code that works for reference! (Runs faster too probably, no more rotation matrices or rotators, all vectors).



-Redacted- Some Content now NDA


I just did this the other day for reading my phone’s device orientation to aim in space.

It’s a bit weird in UE because UE loves FRotators for everything which is basically Euler and you get the dreaded gimbal lock. I wasn’t able to properly use the PlayerController’s accumulated rotation input since that’s an FRotator.

In Unreal, forward is X, up is Z, and side is Y. Or maybe forward was Y and side was X. I need to look at my code again.

So, to roll, you create a quaternion around the forward pointing axis. The pitch, you create a quaternion around the side axis. The yaw you rotate about the up facing axis.

I created these 3 separate quaternions individually based on the 3 components and then multiplied my current actor rotation by them.

Using the traditional euler won’t work because roll won’t be relative to your current orientation, but relative to if you were looking straight ahead. Basically as if pitch was 0.

Hopefully this isn’t too confusing. I can post the code later. I think people above said basically the same thing as I did.

It may be possible to do it slightly more efficiently with one quat instead of 3 individual quats.