How to drive an Actor using ECEF position and orientation

I’m trying to visualize a drone descending from 100km orbit down to the surface of the Moon using Unreal Engine with the Cesium Moon.

The drone is currently represented by a Camera Actor, and I’m driving it using UDP socket data that provides:

ECEF position
ECEF orientation (quaternion)

I configured the CesiumGeoreference with:

Origin Latitude = 0
Origin Longitude = 0
Origin Height = 0

Then I send the following data:

ECEFPosition = [1837400, 0, 0] % meters
ECEFRotation = [0, 0, 0, 1] % identity quaternion

and apply it in the Camera Tick function via:

const FMatrix ActorToECEF = FQuatRotationTranslationMatrix(socket_data.ECEFRotation,
socket_data.ECEFPosition);

GlobeAnchor->SetActorToEarthCenteredEarthFixedMatrix(ActorToECEF);

What I observe in the editor (in the Details panel)
CameraActor Location = [0, 0, 10000000]
→ This matches expectation (~100 km altitude in the local ENU frame)
CameraActor Rotation = [0, -90, 90]
→ This is unexpected
What I expected (since the quaternion is identity):

Actor +X aligns with +ECEF X
Actor +Y aligns with +ECEF Y (I mean right-handed Actor +Y)
Actor +Z aligns with +ECEF Z

However, the Actor appears rotated relative to the ECEF frame (see attached image: large axes = ECEF frame, small axes = Actor body frame (left-handed)).

Is there an example in Cesium for Unreal that demonstrates driving an Actor directly using ECEF position and ECEF orientation (quaternion)?

Any guidance on how Cesium maps ECEF frames into the Unreal actor coordinate frame—or what transformation step I might be missing—would be greatly appreciated.

Hey @pilgrimstranger,

I believe the reason position working but rotation is wrong is because, if I remember correctly, ECEF is right-handed and Unreal Engine is left-handed, so there is no purely rotational mapping between the two. An identity quaternion in ECEF doesn’t correspond to identity in Unreal’s display, which is why you’re seeing [0, -90, 90] even though the quaternion you sent was [0, 0, 0, 1]. That rotation is actually the Unreal-coordinate representation of “axes aligned with ECEF” at that position on the Moon’s surface.

There are two existing threads that work through this exact problem:

The key solution from the first thread: you need to multiply your ECEF rotation against ComputeEarthCenteredEarthFixedToUnrealTransformation(), but you cannot call RemoveScaling() on the result. The scale in that matrix encodes the reflection that handles the left-to-right-hand conversion. Then extract the quaternion using the scaled axes:


const FMatrix ECEFToUnreal = CesiumGeoreference->ComputeEarthCenteredEarthFixedToUnrealTransformation();
const FMatrix FinalRotation = socket_data.ECEFRotation.ToMatrix() * ECEFToUnreal;
const FVector XAxis = FinalRotation.GetScaledAxis(EAxis::X);
const FVector YAxis = FinalRotation.GetScaledAxis(EAxis::Y);
const FQuat UnrealQuat = FRotationMatrix::MakeFromXY(XAxis, YAxis).ToQuat();

Let me know if the above gets you closer!

Thank you, Darcy! The first thread provided the solution that resolved my issue.