// Copyright 2020-2024 CesiumGS, Inc. and Contributors #include "CesiumOriginShiftComponent.h" #include "CesiumGeoreference.h" #include "CesiumGlobeAnchorComponent.h" #include "CesiumSubLevelComponent.h" #include "CesiumSubLevelSwitcherComponent.h" #include "CesiumWgs84Ellipsoid.h" #include "LevelInstance/LevelInstanceActor.h" #if WITH_EDITOR #include "Editor.h" #endif ECesiumOriginShiftMode UCesiumOriginShiftComponent::GetMode() const { return this->Mode; } void UCesiumOriginShiftComponent::SetMode(ECesiumOriginShiftMode NewMode) { this->Mode = NewMode; } double UCesiumOriginShiftComponent::GetDistance() const { return this->Distance; } void UCesiumOriginShiftComponent::SetDistance(double NewDistance) { this->Distance = NewDistance; } UCesiumOriginShiftComponent::UCesiumOriginShiftComponent() { this->PrimaryComponentTick.bCanEverTick = true; this->PrimaryComponentTick.TickGroup = ETickingGroup::TG_PrePhysics; this->bAutoActivate = true; } namespace { /** * @brief Clamping addition. * * Returns the sum of the given values, clamping the result to * the minimum/maximum value that can be represented as a 32 bit * signed integer. * * @param f The floating point value * @param i The integer value * @return The clamped result */ int32 clampedAdd(double f, int32 i) { int64 sum = static_cast(f) + static_cast(i); int64 min = static_cast(TNumericLimits::Min()); int64 max = static_cast(TNumericLimits::Max()); int64 clamped = FMath::Max(min, FMath::Min(max, sum)); return static_cast(clamped); } } // namespace void UCesiumOriginShiftComponent::TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (TickType != ELevelTick::LEVELTICK_All) return; if (!this->IsActive() || this->Mode == ECesiumOriginShiftMode::Disabled) return; UCesiumGlobeAnchorComponent* GlobeAnchor = this->GetGlobeAnchor(); if (!IsValid(GlobeAnchor)) return; ACesiumGeoreference* Georeference = GlobeAnchor->ResolveGeoreference(); if (!IsValid(Georeference)) return; UCesiumEllipsoid* Ellipsoid = Georeference->GetEllipsoid(); check(IsValid(Ellipsoid)); UCesiumSubLevelSwitcherComponent* Switcher = Georeference->GetSubLevelSwitcher(); if (!Switcher) return; const TArray>& Sublevels = Switcher->GetRegisteredSubLevelsWeak(); // If we don't have any known sub-levels, and aren't origin shifting outside // of sub-levels, then bail quickly to save ourselves a little work. if (Sublevels.IsEmpty() && this->Mode == ECesiumOriginShiftMode::SwitchSubLevelsOnly) { return; } FVector ActorEcef = GlobeAnchor->GetEarthCenteredEarthFixedPosition(); ALevelInstance* ClosestActiveLevel = nullptr; double ClosestLevelDistance = std::numeric_limits::max(); for (int32 i = 0; i < Sublevels.Num(); ++i) { ALevelInstance* Current = Sublevels[i].Get(); if (!IsValid(Current)) continue; UCesiumSubLevelComponent* SubLevelComponent = Current->FindComponentByClass(); if (!IsValid(SubLevelComponent)) continue; if (!SubLevelComponent->GetEnabled()) continue; FVector LevelEcef = Ellipsoid->LongitudeLatitudeHeightToEllipsoidCenteredEllipsoidFixed( FVector( SubLevelComponent->GetOriginLongitude(), SubLevelComponent->GetOriginLatitude(), SubLevelComponent->GetOriginHeight())); double LevelDistance = FVector::Distance(LevelEcef, ActorEcef); if (LevelDistance < SubLevelComponent->GetLoadRadius() && LevelDistance < ClosestLevelDistance) { ClosestActiveLevel = Current; ClosestLevelDistance = LevelDistance; } } Switcher->SetTargetSubLevel(ClosestActiveLevel); // Only shift the origin when we're outside of all sub-levels. bool doOriginShift = Switcher->GetTargetSubLevel() == nullptr && Switcher->GetCurrentSubLevel() == nullptr && this->Mode != ECesiumOriginShiftMode::SwitchSubLevelsOnly; if (doOriginShift) { // We're between sub-levels, but we also only want to shift the origin when // the Actor has traveled more than Distance from the old origin. AActor* Actor = this->GetOwner(); doOriginShift = IsValid(Actor) && Actor->GetActorLocation().SquaredLength() > this->Distance * this->Distance; } if (doOriginShift) { if (this->Mode == ECesiumOriginShiftMode::ChangeCesiumGeoreference) { Georeference->SetOriginEarthCenteredEarthFixed(ActorEcef); } else { check(false && "Missing ECesiumOriginShiftMode implementation.") } } }