Posting the source code for LookRotation for those who need it .

Posting it here as it took me a bit of trouble to figure this out . Seriously , why doesn’t the engine have this already ?



MyLookRotation(FVector lookAt, FVector upDirection)
{


FVector forward = lookAt;
FVector up = upDirection;


forward = forward.GetSafeNormal();
up = up - (forward * FVector::DotProduct(up, forward));
up = up.GetSafeNormal();

///////////////////////





FVector vector = forward.GetSafeNormal();
FVector vector2 = FVector::CrossProduct(up, vector);
FVector vector3 = FVector::CrossProduct(vector, vector2);
float m00 = vector2.X;
float m01 = vector2.Y;
float m02 = vector2.Z;
float m10 = vector3.X;
float m11 = vector3.Y;
float m12 = vector3.Z;
float m20 = vector.X;
float m21 = vector.Y;
float m22 = vector.Z;


float num8 = (m00 + m11) + m22;
FQuat quaternion = FQuat();
if (num8 > 0.0f)
{
    float num = (float)FMath::Sqrt(num8 + 1.0f);
    quaternion.W = num * 0.5f;
    num = 0.5f / num;
    quaternion.X = (m12 - m21) * num;
    quaternion.Y = (m20 - m02) * num;
    quaternion.Z = (m01 - m10) * num;
    return FRotator(quaternion);
}
if ((m00 >= m11) && (m00 >= m22))
{
    float num7 = (float)FMath::Sqrt(((1.0f + m00) - m11) - m22);
    float num4 = 0.5f / num7;
    quaternion.X = 0.5f * num7;
    quaternion.Y = (m01 + m10) * num4;
    quaternion.Z = (m02 + m20) * num4;
    quaternion.W = (m12 - m21) * num4;
    return FRotator(quaternion);
}
if (m11 > m22)
{
    float num6 = (float)FMath::Sqrt(((1.0f + m11) - m00) - m22);
    float num3 = 0.5f / num6;
    quaternion.X = (m10 + m01) * num3;
    quaternion.Y = 0.5f * num6;
    quaternion.Z = (m21 + m12) * num3;
    quaternion.W = (m20 - m02) * num3;
    return FRotator(quaternion);
}
float num5 = (float)FMath::Sqrt(((1.0f + m22) - m00) - m11);
float num2 = 0.5f / num5;
quaternion.X = (m20 + m02) * num2;
quaternion.Y = (m21 + m12) * num2;
quaternion.Z = 0.5f * num5;
quaternion.W = (m01 - m10) * num2;


return FRotator(quaternion);


}


1 Like

Is this like the “Find Look at Rotation”?

It does. Find Look at Rotation should do what you want. Or you can just do:



FVector newForward = Target - Source;
newForward.Normalize();

const FVector WorldUp(0.0f, 0.0f, 1.0f);

FVector newRight = FVector::Cross(toTarget, WorldUp);
FVector newUp = FVector::Cross(newRight, newForward);

return FTransform(newForward, newRight, newUp, Source);


LookAtRotation and LookRotation are different . You would get more explanation of the difference if you check the unity forums .
https://answers.unity.com/questions/…krotation.html

Basically lookrotation creates a rotation using a forward and upward direction as input alone , while lookat explicitly points towards a target location . LookAtRotation has the problem of “flipping” the rotator when the target pass through the source instead of sticking to a specific direction when needed (a specific problem in my case) . LookRotation will make sure the rotation doesn’t have the flipping problem as stated above .

1 Like

    //return normalized rotator
    static FORCEINLINE FTransform QuatLookXatLocalDirection(const FTransform& LookAtFromTransform, const FVector& LookAtLocalDirection)
    {
        const FQuat Q = FQuat::FindBetweenNormals(FVector(1, 0, 0), LookAtLocalDirection);
        return FTransform((LookAtFromTransform.GetRotation() * Q).GetNormalized(), LookAtFromTransform.GetLocation(), LookAtFromTransform.GetScale3D());
    }

    static FORCEINLINE FTransform QuatLookXatDirection(const FTransform& LookAtFromTransform, const FVector& LookAtDirection)
    {
        const FVector LookAtLocalDirection = (LookAtFromTransform.Inverse().TransformVector(LookAtDirection)).GetSafeNormal();
        return QuatLookXatLocalDirection(LookAtFromTransform, LookAtLocalDirection);
    }

    //return normalized rotator
    static FORCEINLINE FTransform QuatLookXatLocation(const FTransform& LookAtFromTransform, const FVector& LookAtTarget)
    {
        if (!LookAtFromTransform.GetLocation().Equals(LookAtTarget, 0.01f))
        {
            const FVector LookAtLocalDirection = (LookAtFromTransform.Inverse().TransformPositionNoScale(LookAtTarget)).GetSafeNormal();
            return QuatLookXatLocalDirection(LookAtFromTransform, LookAtLocalDirection);
        }
        else
        {
            return LookAtFromTransform;
        }
    }

Can someone post a whole script so I can just copy/paste it, please? I`m really need it

Just wondering but what are the use cases where LookAtRotation vs. LookRotation would be useful?

How does this even get implemented though?

You also can, and probably should, use FRotationMatrix::MakeFromXZ. I ported it over to Unity and it seems to have the exact same effect.

P.S the original post’s LookRotation function might have its axes in the wrong order. Unity’s XYZ is right, up, forward, whilst Unreal’s is forward, right, up. Below is the UNREAL C++ code that works for me.

FQuat FQuatUtils::LookRotation(const FVector& lookAt, const FVector& upDirection)
{
	// https://forums.unrealengine.com/t/posting-the-source-code-for-lookrotation-for-those-who-need-it/111568

	FVector forward = lookAt;
	FVector up = upDirection;
	
	forward = forward.GetSafeNormal();
	// cross product of two non-perpendicular unit vectors can create a non-unit vector
	up = up - (forward * FVector::DotProduct(up, forward));
	up = up.GetSafeNormal();
	
	///////////////////////
	
	
	
	FVector vector = forward.GetSafeNormal();
	FVector vector2 = FVector::CrossProduct(up, vector);
	FVector vector3 = FVector::CrossProduct(vector, vector2);
	float m00 = vector2.Y;
	float m01 = vector2.Z;
	float m02 = vector2.X;
	float m10 = vector3.Y;
	float m11 = vector3.Z;
	float m12 = vector3.X;
	float m20 = vector.Y;
	float m21 = vector.Z;
	float m22 = vector.X;
	
	
	float num8 = (m00 + m11) + m22;
	FQuat quaternion = FQuat();
	if (num8 > 0.0f)
	{
		float num = (float)FMath::Sqrt(num8 + 1.0f);
		quaternion.W = num * 0.5f;
		num = 0.5f / num;
		quaternion.Y = (m12 - m21) * num;
		quaternion.Z = (m20 - m02) * num;
		quaternion.X = (m01 - m10) * num;
		return quaternion;
	}
	if ((m00 >= m11) && (m00 >= m22))
	{
		float num7 = (float)FMath::Sqrt(((1.0f + m00) - m11) - m22);
		float num4 = 0.5f / num7;
		quaternion.Y = 0.5f * num7;
		quaternion.Z = (m01 + m10) * num4;
		quaternion.X = (m02 + m20) * num4;
		quaternion.W = (m12 - m21) * num4;
		return quaternion;
	}
	if (m11 > m22)
	{
		float num6 = (float)FMath::Sqrt(((1.0f + m11) - m00) - m22);
		float num3 = 0.5f / num6;
		quaternion.Y = (m10 + m01) * num3;
		quaternion.Z = 0.5f * num6;
		quaternion.X = (m21 + m12) * num3;
		quaternion.W = (m20 - m02) * num3;
		return quaternion;
	}
	float num5 = (float)FMath::Sqrt(((1.0f + m22) - m00) - m11);
	float num2 = 0.5f / num5;
	quaternion.Y = (m20 + m02) * num2;
	quaternion.Z = (m21 + m12) * num2;
	quaternion.X = 0.5f * num5;
	quaternion.W = (m01 - m10) * num2;
	
	return (quaternion);

}

The UNITY C# code of the ported MakeFromXZ function is below.

Quaternion MakeFromXZ(Vector3 XAxis, Vector3 ZAxis)
{
    Vector3 NewX = XAxis.normalized;
    Vector3 Norm = ZAxis.normalized;

    // if they're almost same, we need to find arbitrary vector
    if (Mathf.Approximately(Mathf.Abs(Vector3.Dot(NewX,Norm)), 1.0f))
    {
        // make sure we don't ever pick the same as NewX
        Norm = (Mathf.Abs(NewX.z) < (1.0f - 0.0001f)) ? Vector3.up : Vector3.forward;
    }

    Vector3 NewY = (Vector3.Cross(Norm, NewX)).normalized;
    Vector3 NewZ = Vector3.Cross(NewX, NewY);

    return ortho3x3ToQuat(ref NewY, ref NewZ, ref NewX);
}

static Quaternion ortho3x3ToQuat(ref Vector3 c0, ref Vector3 c1, ref Vector3 c2)
{
    if (c0.x + c1.y + c2.z > 0f) return quat(3, 1f + c0.x + c1.y + c2.z, c1.z - c2.y, c2.x - c0.z, c0.y - c1.x);
    if (c0.x >= c1.y && c0.x >= c2.z) return quat(0, 1f + c0.x - c1.y - c2.z, c0.y + c1.x, c0.z + c2.x, c1.z - c2.y);
    if (c1.y > c2.z) return quat(1, 1f - c0.x + c1.y - c2.z, c1.x + c0.y, c2.y + c1.z, c2.x - c0.z);
    return quat(2, 1f - c0.x - c1.y + c2.z, c2.x + c0.z, c2.y + c1.z, c0.y - c1.x);
}

static Quaternion quat(int index, float t, float a, float b, float c)
{
    var s = .5f / Mathf.Sqrt(t);
    return index switch
    {
        3 => new(a * s, b * s, c * s, t * s),
        2 => new(a * s, b * s, t * s, c * s),
        1 => new(a * s, t * s, b * s, c * s),
        _ => new(t * s, a * s, b * s, c * s)
    };
}