I decided to take a stab at this. My approach: I think it’s easier to reason about if you think of it as constructing the decal’s desired coordinate frame from its constituent axis vectors, rather than manipulating Euler rotations.

Let’s start with the “forward” vector since that’s the most obvious. It looks like a DecalActor projects in the direction of its +X axis. Therefore you want the decal’s +X axis to be a vector that opposes the normal of the surface you’re projecting onto.

Then you want to determine which of the object’s local axes most closely corresponds to either your player’s view up or view right axis. This is an arbitrary choice, and the remaining axis can be found as the cross product of the two known axes.

I came up with something like this:

```
FMatrix hitActorToWorld;
if (hit.Actor.IsValid()) {
hitActorToWorld = hit.Actor->ActorToWorld().ToMatrixNoScale();
} else {
hitActorToWorld = FMatrix::Identity;
}
FVector hitActorX, hitActorY, hitActorZ;
hitActorToWorld.GetUnitAxes(hitActorX, hitActorY, hitActorZ);
const FVector cameraUp = viewRotation.RotateVector(FVector(0, 0, 1));
const float upDotActorX = cameraUp | hitActorX;
const float upDotActorY = cameraUp | hitActorY;
const float upDotActorZ = cameraUp | hitActorZ;
EAxis::Type principalActorAxis;
if (FMath::Abs(upDotActorX) > FMath::Abs(upDotActorY)) {
if (FMath::Abs(upDotActorX) > FMath::Abs(upDotActorZ)) {
principalActorAxis = EAxis::X;
} else {
principalActorAxis = EAxis::Z;
}
} else {
if (FMath::Abs(upDotActorY) > FMath::Abs(upDotActorZ)) {
principalActorAxis = EAxis::Y;
} else {
principalActorAxis = EAxis::Z;
}
}
FVector actorSnappedUp;
switch (principalActorAxis) {
case EAxis::X:
actorSnappedUp = hitActorX * FMath::Sign(upDotActorX);
break;
case EAxis::Y:
actorSnappedUp = hitActorY * FMath::Sign(upDotActorY);
break;
case EAxis::Z:
actorSnappedUp = hitActorZ * FMath::Sign(upDotActorZ);
break;
}
const FVector decalAxisX = -(hit.Normal);
const FVector decalAxisZ = actorSnappedUp;
const FVector decalAxisY = FVector::CrossProduct(decalAxisX, decalAxisZ);
```

The resulting values can be used similarly to the following:

```
DecalComponent->SetWorldRotation(FMatrix(decalAxisX, decalAxisY, decalAxisZ, FVector(0, 0, 0)).Rotator());
```