// Copyright 2020-2024 CesiumGS, Inc. and Contributors #include "GlobeAwareDefaultPawn.h" #include "Camera/CameraComponent.h" #include "CesiumActors.h" #include "CesiumCustomVersion.h" #include "CesiumFlyToComponent.h" #include "CesiumGeoreference.h" #include "CesiumGlobeAnchorComponent.h" #include "CesiumRuntime.h" #include "CesiumTransforms.h" #include "CesiumUtility/Math.h" #include "CesiumWgs84Ellipsoid.h" #include "Curves/CurveFloat.h" #include "DrawDebugHelpers.h" #include "Engine/World.h" #include "GameFramework/PlayerController.h" #include "UObject/ConstructorHelpers.h" #include "VecMath.h" #include #include #include #include #include #if WITH_EDITOR #include "Editor.h" #endif AGlobeAwareDefaultPawn::AGlobeAwareDefaultPawn() : ADefaultPawn() { // Structure to hold one-time initialization struct FConstructorStatics { ConstructorHelpers::FObjectFinder ProgressCurve; ConstructorHelpers::FObjectFinder HeightPercentageCurve; ConstructorHelpers::FObjectFinder MaximumHeightByDistanceCurve; FConstructorStatics() : ProgressCurve(TEXT( "/CesiumForUnreal/Curves/FlyTo/Curve_CesiumFlyToDefaultProgress_Float.Curve_CesiumFlyToDefaultProgress_Float")), HeightPercentageCurve(TEXT( "/CesiumForUnreal/Curves/FlyTo/Curve_CesiumFlyToDefaultHeightPercentage_Float.Curve_CesiumFlyToDefaultHeightPercentage_Float")), MaximumHeightByDistanceCurve(TEXT( "/CesiumForUnreal/Curves/FlyTo/Curve_CesiumFlyToDefaultMaximumHeightByDistance_Float.Curve_CesiumFlyToDefaultMaximumHeightByDistance_Float")) { } }; static FConstructorStatics ConstructorStatics; this->FlyToProgressCurve_DEPRECATED = ConstructorStatics.ProgressCurve.Object; this->FlyToAltitudeProfileCurve_DEPRECATED = ConstructorStatics.HeightPercentageCurve.Object; this->FlyToMaximumAltitudeCurve_DEPRECATED = ConstructorStatics.MaximumHeightByDistanceCurve.Object; #if WITH_EDITOR this->SetIsSpatiallyLoaded(false); #endif this->GlobeAnchor = CreateDefaultSubobject(TEXT("GlobeAnchor")); } void AGlobeAwareDefaultPawn::MoveRight(float Val) { this->_moveAlongViewAxis(EAxis::Y, Val); } void AGlobeAwareDefaultPawn::MoveForward(float Val) { this->_moveAlongViewAxis(EAxis::X, Val); } void AGlobeAwareDefaultPawn::MoveUp_World(float Val) { if (Val == 0.0f) { return; } ACesiumGeoreference* pGeoreference = this->GetGeoreference(); if (!IsValid(pGeoreference)) { return; } UCesiumEllipsoid* pEllipsoid = pGeoreference->GetEllipsoid(); check(IsValid(pEllipsoid)); FVector upEcef = pEllipsoid->GeodeticSurfaceNormal( this->GlobeAnchor->GetEarthCenteredEarthFixedPosition()); FVector up = pGeoreference->TransformEarthCenteredEarthFixedDirectionToUnreal(upEcef); FTransform transform{}; USceneComponent* pRootComponent = this->GetRootComponent(); if (IsValid(pRootComponent)) { USceneComponent* pParent = pRootComponent->GetAttachParent(); if (IsValid(pParent)) { transform = pParent->GetComponentToWorld(); } } this->_moveAlongVector(transform.TransformVector(up), Val); } FRotator AGlobeAwareDefaultPawn::GetViewRotation() const { if (!Controller) { return this->GetActorRotation(); } ACesiumGeoreference* pGeoreference = this->GetGeoreference(); if (!pGeoreference) { return this->GetActorRotation(); } // The control rotation is expressed in a left-handed East-South-Up (ESU) // coordinate system: // * Yaw: Clockwise from East: 0 is East, 90 degrees is // South, 180 degrees is West, 270 degrees is North. // * Pitch: Angle above level, Positive is looking up, negative is looking // down // * Roll: Rotation around the look direction. Positive is a barrel roll to // the right (clockwise). FRotator localRotation = Controller->GetControlRotation(); FTransform transform{}; USceneComponent* pRootComponent = this->GetRootComponent(); if (IsValid(pRootComponent)) { USceneComponent* pParent = pRootComponent->GetAttachParent(); if (IsValid(pParent)) { transform = pParent->GetComponentToWorld(); } } // Transform the rotation in the ESU frame to the Unreal world frame. FVector globePosition = transform.InverseTransformPosition(this->GetPawnViewLocation()); FMatrix esuAdjustmentMatrix = pGeoreference->ComputeEastSouthUpToUnrealTransformation(globePosition) * transform.ToMatrixNoScale(); return FRotator(esuAdjustmentMatrix.ToQuat() * localRotation.Quaternion()); } FRotator AGlobeAwareDefaultPawn::GetBaseAimRotation() const { return this->GetViewRotation(); } const FTransform& AGlobeAwareDefaultPawn::GetGlobeToUnrealWorldTransform() const { AActor* pParent = this->GetAttachParentActor(); if (IsValid(pParent)) { return pParent->GetActorTransform(); } return FTransform::Identity; } void AGlobeAwareDefaultPawn::FlyToLocationECEF( const FVector& ECEFDestination, double YawAtDestination, double PitchAtDestination, bool CanInterruptByMoving) { UCesiumFlyToComponent* FlyTo = this->FindComponentByClass(); if (!IsValid(FlyTo)) { UE_LOG( LogCesium, Warning, TEXT( "Cannot call deprecated FlyToLocationLongitudeLatitudeHeight because the GlobeAwareDefaultPawn does not have a CesiumFlyToComponent.")) return; } // Make sure functions attached to the deprecated delegates will be called. FlyTo->OnFlightComplete.AddUniqueDynamic( this, &AGlobeAwareDefaultPawn::_onFlightComplete); FlyTo->OnFlightInterrupted.AddUniqueDynamic( this, &AGlobeAwareDefaultPawn::_onFlightInterrupted); FlyTo->FlyToLocationEarthCenteredEarthFixed( ECEFDestination, YawAtDestination, PitchAtDestination, CanInterruptByMoving); } void AGlobeAwareDefaultPawn::FlyToLocationLongitudeLatitudeHeight( const FVector& LongitudeLatitudeHeightDestination, double YawAtDestination, double PitchAtDestination, bool CanInterruptByMoving) { UCesiumFlyToComponent* FlyTo = this->FindComponentByClass(); if (!IsValid(FlyTo)) { UE_LOG( LogCesium, Warning, TEXT( "Cannot call deprecated FlyToLocationLongitudeLatitudeHeight because the GlobeAwareDefaultPawn does not have a CesiumFlyToComponent.")) return; } // Make sure functions attached to the deprecated delegates will be called. FlyTo->OnFlightComplete.AddUniqueDynamic( this, &AGlobeAwareDefaultPawn::_onFlightComplete); FlyTo->OnFlightInterrupted.AddUniqueDynamic( this, &AGlobeAwareDefaultPawn::_onFlightInterrupted); FlyTo->FlyToLocationLongitudeLatitudeHeight( LongitudeLatitudeHeightDestination, YawAtDestination, PitchAtDestination, CanInterruptByMoving); } void AGlobeAwareDefaultPawn::Serialize(FArchive& Ar) { Super::Serialize(Ar); Ar.UsingCustomVersion(FCesiumCustomVersion::GUID); } void AGlobeAwareDefaultPawn::PostLoad() { Super::PostLoad(); // For backward compatibility, copy the value of the deprecated Georeference // property to its new home in the GlobeAnchor. It doesn't appear to be // possible to do this in Serialize: // https://udn.unrealengine.com/s/question/0D54z00007CAbHFCA1/backward-compatibile-serialization-for-uobject-pointers const int32 CesiumVersion = this->GetLinkerCustomVersion(FCesiumCustomVersion::GUID); if (CesiumVersion < FCesiumCustomVersion::GeoreferenceRefactoring) { if (this->Georeference_DEPRECATED != nullptr && this->GlobeAnchor && this->GlobeAnchor->GetGeoreference() == nullptr) { this->GlobeAnchor->SetGeoreference(this->Georeference_DEPRECATED); } } #if WITH_EDITOR if (CesiumVersion < FCesiumCustomVersion::FlyToComponent && !HasAnyFlags(RF_ClassDefaultObject)) { // If this is a Blueprint object, like DynamicPawn, its construction // scripts may not have been run yet at this point. Doing so might cause // a Fly To component to be added. So we force it to happen here so // that we don't end up adding a duplicate CesiumFlyToComponent. this->RerunConstructionScripts(); UCesiumFlyToComponent* FlyTo = this->FindComponentByClass(); if (!FlyTo) { FlyTo = Cast(this->AddComponentByClass( UCesiumFlyToComponent::StaticClass(), false, FTransform::Identity, false)); FlyTo->SetFlags(RF_Transactional); this->AddInstanceComponent(FlyTo); UE_LOG( LogCesium, Warning, TEXT( "Added CesiumFlyToComponent to %s in order to preserve backward compatibility."), *this->GetName()); } FlyTo->RotationToUse = ECesiumFlyToRotation::ControlRotationInEastSouthUp; FlyTo->ProgressCurve = this->FlyToProgressCurve_DEPRECATED; FlyTo->HeightPercentageCurve = this->FlyToAltitudeProfileCurve_DEPRECATED; FlyTo->MaximumHeightByDistanceCurve = this->FlyToMaximumAltitudeCurve_DEPRECATED; FlyTo->Duration = this->FlyToDuration_DEPRECATED; } #endif } ACesiumGeoreference* AGlobeAwareDefaultPawn::GetGeoreference() const { if (!IsValid(this->GlobeAnchor)) { UE_LOG( LogCesium, Error, TEXT( "GlobeAwareDefaultPawn %s does not have a valid GlobeAnchorComponent."), *this->GetName()); return nullptr; } ACesiumGeoreference* pGeoreference = this->GlobeAnchor->ResolveGeoreference(); if (!IsValid(pGeoreference)) { UE_LOG( LogCesium, Error, TEXT( "GlobeAwareDefaultPawn %s does not have a valid CesiumGeoreference."), *this->GetName()); pGeoreference = nullptr; } return pGeoreference; } UCurveFloat* AGlobeAwareDefaultPawn::GetFlyToProgressCurve_DEPRECATED() const { UCesiumFlyToComponent* FlyTo = this->FindComponentByClass(); if (!IsValid(FlyTo)) return nullptr; return FlyTo->ProgressCurve; } void AGlobeAwareDefaultPawn::SetFlyToProgressCurve_DEPRECATED( UCurveFloat* NewValue) { UCesiumFlyToComponent* FlyTo = this->FindComponentByClass(); if (!IsValid(FlyTo)) return; FlyTo->ProgressCurve = NewValue; } UCurveFloat* AGlobeAwareDefaultPawn::GetFlyToAltitudeProfileCurve_DEPRECATED() const { UCesiumFlyToComponent* FlyTo = this->FindComponentByClass(); if (!IsValid(FlyTo)) return nullptr; return FlyTo->HeightPercentageCurve; } void AGlobeAwareDefaultPawn::SetFlyToAltitudeProfileCurve_DEPRECATED( UCurveFloat* NewValue) { UCesiumFlyToComponent* FlyTo = this->FindComponentByClass(); if (!IsValid(FlyTo)) return; FlyTo->HeightPercentageCurve = NewValue; } UCurveFloat* AGlobeAwareDefaultPawn::GetFlyToMaximumAltitudeCurve_DEPRECATED() const { UCesiumFlyToComponent* FlyTo = this->FindComponentByClass(); if (!IsValid(FlyTo)) return nullptr; return FlyTo->MaximumHeightByDistanceCurve; } void AGlobeAwareDefaultPawn::SetFlyToMaximumAltitudeCurve_DEPRECATED( UCurveFloat* NewValue) { UCesiumFlyToComponent* FlyTo = this->FindComponentByClass(); if (!IsValid(FlyTo)) return; FlyTo->MaximumHeightByDistanceCurve = NewValue; } float AGlobeAwareDefaultPawn::GetFlyToDuration_DEPRECATED() const { UCesiumFlyToComponent* FlyTo = this->FindComponentByClass(); if (!IsValid(FlyTo)) return 0.0f; return FlyTo->Duration; } void AGlobeAwareDefaultPawn::SetFlyToDuration_DEPRECATED(float NewValue) { UCesiumFlyToComponent* FlyTo = this->FindComponentByClass(); if (!IsValid(FlyTo)) return; FlyTo->Duration = NewValue; } void AGlobeAwareDefaultPawn::_moveAlongViewAxis(EAxis::Type axis, double Val) { if (Val == 0.0) { return; } FRotator worldRotation = this->GetViewRotation(); this->_moveAlongVector( FRotationMatrix(worldRotation).GetScaledAxis(axis), Val); } void AGlobeAwareDefaultPawn::_moveAlongVector( const FVector& vector, double Val) { if (Val == 0.0) { return; } FRotator worldRotation = this->GetViewRotation(); AddMovementInput(vector, Val); } void AGlobeAwareDefaultPawn::_onFlightComplete() { this->OnFlightComplete_DEPRECATED.Broadcast(); } void AGlobeAwareDefaultPawn::_onFlightInterrupted() { this->OnFlightInterrupt_DEPRECATED.Broadcast(); }