156 lines
4.7 KiB
C++
156 lines
4.7 KiB
C++
// 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<int64>(f) + static_cast<int64>(i);
|
|
int64 min = static_cast<int64>(TNumericLimits<int32>::Min());
|
|
int64 max = static_cast<int64>(TNumericLimits<int32>::Max());
|
|
int64 clamped = FMath::Max(min, FMath::Min(max, sum));
|
|
return static_cast<int32>(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<TWeakObjectPtr<ALevelInstance>>& 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<double>::max();
|
|
|
|
for (int32 i = 0; i < Sublevels.Num(); ++i) {
|
|
ALevelInstance* Current = Sublevels[i].Get();
|
|
if (!IsValid(Current))
|
|
continue;
|
|
|
|
UCesiumSubLevelComponent* SubLevelComponent =
|
|
Current->FindComponentByClass<UCesiumSubLevelComponent>();
|
|
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.")
|
|
}
|
|
}
|
|
}
|