// Copyright 2020-2024 CesiumGS, Inc. and Contributors #pragma once #include "CesiumGeospatial/SimplePlanarEllipsoidCurve.h" #include "CesiumGlobeAnchoredActorComponent.h" #include "CesiumFlyToComponent.generated.h" class UCurveFloat; class UCesiumGlobeAnchorComponent; /** * The delegate for when the Actor finishes flying. */ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FCesiumFlightCompleted); /** * The delegate for when the Actor's flight is interrupted. */ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FCesiumFlightInterrupted); /** * Indicates which rotation to use for orienting the object during flights. */ UENUM(BlueprintType) enum class ECesiumFlyToRotation : uint8 { /** * Uses the relative rotation of the root component of the Actor to which the * CesiumFlyToComponent is attached. */ Actor, /** * Uses the ControlRotation of the Controller of the Pawn to which the * CesiumFlyToComponent is attached. The ControlRotation is interpreted as * being relative to the Unreal coordinate system. * * If the component is attached to an Actor that is not a Pawn, or if the Pawn * does not have a Controller, this option is equivalent to the "Actor" * option. */ ControlRotationInUnreal, /** * Uses the ControlRotation of the Controller of the Pawn to which the * CesiumFlyToComponent is attached. The ControlRotation is interpreted as * being relative to the Pawn's local East-South-Up coordinate system. * * This is the option to use with a GlobeAwareDefaultPawn or a DynamicPawn, * because those classes interpret the ControlRotation as being relative to * East-South-Up. * * If the component is attached to an Actor that is not a Pawn, or if the Pawn * does not have a Controller, this option is equivalent to the "Actor" * option. */ ControlRotationInEastSouthUp }; /** * Smoothly animates the Actor to which it is attached on a flight to a new * location on the globe. */ UCLASS(ClassGroup = "Cesium", Meta = (BlueprintSpawnableComponent)) class CESIUMRUNTIME_API UCesiumFlyToComponent : public UCesiumGlobeAnchoredActorComponent { GENERATED_BODY() public: UCesiumFlyToComponent(); /** * A curve that is used to determine the flight progress percentage for all * the other curves. The input is the fraction (0.0 to 1.0) of the total time * that has passed so far, and the output is the fraction of the total curve * that should be traversed at this time. This curve allows the Actor to * accelerate and deaccelerate as desired throughout the flight. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cesium") UCurveFloat* ProgressCurve; /** * A curve that controls what percentage of the maximum height the Actor * should take at a given time on the flight. This curve must be kept in the 0 * to 1 range on both axes. The MaximumHeightByDistanceCurve controls the * actual maximum height that is achieved during the flight. * * If this curve is not specified, the height will be a smooth interpolation * between the height at the original location and the height at the * destination location, and the MaximumHeightByDistanceCurve will be ignored. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cesium") UCurveFloat* HeightPercentageCurve; /** * A curve that controls the maximum height that will be achieved during the * flight as a function of the straight-line distance of the flight, in * meters. If the start and end point are on opposite sides of the globe, the * straight-line distance goes through the Earth even though the flight itself * will not. * * If HeightPercentageCurve is not specified, this property is ignored. * If HeightPercentageCurve is specified, but this property is not, then the * maximum height is 30,000 meters regardless of distance. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cesium") UCurveFloat* MaximumHeightByDistanceCurve; /** * The length in seconds that the flight should last. */ UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Cesium", meta = (ClampMin = 0.0)) float Duration = 5.0f; /** * Indicates which rotation to use during flights. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cesium") ECesiumFlyToRotation RotationToUse = ECesiumFlyToRotation::Actor; /** * A delegate that will be called when the Actor finishes flying. * */ UPROPERTY(BlueprintAssignable, Category = "Cesium"); FCesiumFlightCompleted OnFlightComplete; /** * A delegate that will be called when the Actor's flight is interrupted. * */ UPROPERTY(BlueprintAssignable, Category = "Cesium"); FCesiumFlightInterrupted OnFlightInterrupted; /** * Begin a smooth flight to the given Earth-Centered, Earth-Fixed * (ECEF) destination, such that the Actor ends at the specified yaw and * pitch. The yaw and pitch are expressed relative to an East-South-Up frame * at the destination. The characteristics of the flight can be configured * with the properties on this component. * * If CanInterruptByMoving is true and the Actor moves independent of this * component, then the flight in progress will be canceled. */ UFUNCTION(BlueprintCallable, Category = "Cesium") void FlyToLocationEarthCenteredEarthFixed( const FVector& EarthCenteredEarthFixedDestination, double YawAtDestination, double PitchAtDestination, bool CanInterruptByMoving); /** * Begin a smooth camera flight to the given WGS84 longitude in degrees (x), * latitude in degrees (y), and height in meters (z) such that the camera * ends at the given yaw and pitch. The yaw and pitch are expressed relative * to an East-South-Up frame at the destination. The characteristics of the * flight can be configured with the properties on this component. * * Note that the height is measured in meters above the WGS84 ellipsoid, and * should not be confused with a height relative to mean sea level, which may * be tens of meters different depending on where you are on the globe. * * If CanInterruptByMoving is true and the Actor moves independent of this * component, then the flight in progress will be canceled. */ UFUNCTION(BlueprintCallable, Category = "Cesium") void FlyToLocationLongitudeLatitudeHeight( const FVector& LongitudeLatitudeHeightDestination, double YawAtDestination, double PitchAtDestination, bool CanInterruptByMoving); /** * Begin a smooth flight to the given destination in Unreal coordinates, such * that the Actor ends at the specified yaw and pitch. The yaw and pitch are * expressed relative to an East-South-Up frame at the destination. The * characteristics of the flight can be configured with the properties on this * component. * * If CanInterruptByMoving is true and the Actor moves independent of this * component, then the flight in progress will be canceled. */ UFUNCTION(BlueprintCallable, Category = "Cesium") void FlyToLocationUnreal( const FVector& UnrealDestination, double YawAtDestination, double PitchAtDestination, bool CanInterruptByMoving); /** * Interrupts the flight that is currently in progress, leaving the Actor * wherever it is currently. */ UFUNCTION(BlueprintCallable, Category = "Cesium") void InterruptFlight(); protected: virtual void TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; private: FQuat GetCurrentRotationEastSouthUp(); void SetCurrentRotationEastSouthUp(const FQuat& EastSouthUpRotation); bool _flightInProgress = false; bool _canInterruptByMoving; float _currentFlyTime; double _maxHeight; FVector _destinationEcef; FQuat _sourceRotation; FQuat _destinationRotation; FVector _previousPositionEcef; TUniquePtr _currentCurve; double _length; };