Gimble lock with Matrix to Quaternion to Rotation conversion from Matrix source data

Hi
So we have been pulling our hair out here when dealing with rotation matrix issues from a live data feed.
It seems no matter what the source is, gimbal lock always occurs.
The source data is a rotation matrix, I’m converting it to a Quaternion and then to a rotation matrix.

When applying it to an object in Unreal live with a Set Actor Relative Rotation.
Does anyone have any tips on how to avoid this? It seems that it always ends up back to a form of Euler rotation :S

Issue is evident when doing consecutive 90 deg rotations.

Thanks :slight_smile:

As you said, the key is going to be staying out of Euler rotation. Why are you converting it from a Quat back into a rotational matrix? Once you have your rotation as a Quat it should be all good. SetActorRelativeRotation appears to only use the Quat contained within FRotator, I don’t see it converting things back to Euler angles.

You may also want to sanitize any rotational matrices when they first come in (i.e. make sure all the axis are orthogonal). UE4 has a special matrix for rotational matrices called FRotationMatrix (check RotationMatrix.h) which has some helper methods for constructing a FRotationMatrix and keeping things orthogonal.

thanks for the reply.
So currently my source data is already in rotation matrix form from my external app.
I’ve converted this and confirmed the values are correct when converting to Quat->Euler but I am experiencing major Gimbal issues.

Is there a way I could apply the rotation data without converting to Euler?
It seems SetActorRelativeRotation only accepts a FRotator, which is the source of my problem :S

Are you doing this in blueprints or C++?

C++. The data is being streamed in to a custom struct containing the matrix data among other things. The result is passed back to unreal via Blueprint.

I tried a few methods of converting the data,
Mostly I’m seeing issues with flipping around the axis of the poles. Am trying to solve with other peoples methods but am having somewhat limited success:

    // This worked but exhibits flipping around the poles:
    //
    FVector InX(A, B, C);
    	FVector InY(E, F, G);
    	FVector InZ(I, J, K);
    	FVector InW(M, N, O);
    
    	FMatrix matrix(InX, InY, InZ, InW);
    	FQuat ToQuat(matrix);
    	FRotator myRot(ToQuat);
    
    	return myRot;
    }
    
    // This one didn't work well:
    //
    FRotator ATransformations::GetRotatorFromSlerp(class AActor* pitch, class AActor* yaw, class AActor* roll)
    {
    	FQuat pitchQ = pitch->GetActorQuat();
    	FQuat yawQ = yaw->GetActorQuat();
    	FQuat rollQ = roll->GetActorQuat();
    
    	FQuat pitchrollQ = FQuat::Slerp(pitchQ, rollQ, 0.5);
    	FQuat finalQ = FQuat::Slerp(pitchrollQ, yawQ, 0.5);
    
    	FRotator finalRotation(pitchrollQ);
    
    	return finalRotation;

I should say that this second method was after I applied the data (as FRotator) to 3 items in the scene at the same position. The idea was then to average all of their rotation positions and create a new FRotator Needless to say it failed :stuck_out_tongue:

Give this a shot:

// Given a vector, an axis, and a vector perpendicular to that axis - grab the angle between the vector and the axis, in degrees.
float getDegreesRelativeToAxis(FVector const & vec, FVector const & axis, FVector const& perpendicularAxis)
{
	check (vec.IsNormalized() && axis.IsNormalized() && perpendicularAxis.IsNormalized());
	
	float theta = FVector::DotProduct(vec, axis);
	float angleInDegress = FMath::RadiansToDegrees(FMath::Acos(theta));
	
	if (FVector::DotProduct(vec, perpedicularAxis) < 0.0f)
	{
		angleInDegrees = 360.0f - angleInDegrees;
	}
	
	return angleInDegrees;
}

FRotator constructRotator(FVector const& yawVector, FVector const& pitchVector, FVector const& rollVector)
{
	const FVector unitX(1.0f, 0.0f, 0.0f);
	const FVector unitY(0.0f, 1.0f, 0.0f);
	const FVector unitZ(0.0f, 0.0f, 1.0f);
	
	yawVector.Normalize();
	pitchVector.Normalize();
	rollVector.Normalize();
	
	float yawDegrees = getDegreesRelativeToAxis(yawVector, unitX, unitZ); // Rotation around Z (Up)
	float pitchDegrees = getDegreesRelativeToAxis(pitchVector, unitY, unitX);// Rotation around X (Left/Right)
	float rollDegrees = getDegreesRelativeToAxis(rollVector, unitZ, unitY); // Rotation around Y (Forward/Back)
	
	return FRotator(yawDegrees, pitchDegrees, rollDegrees);
}