bmh/FlightSimulation/Plugins/CesiumForUnreal_5.4/Source/CesiumRuntime/Private/Cesium3DTileset.cpp
2025-02-07 22:52:32 +08:00

2270 lines
73 KiB
C++

// Copyright 2020-2024 CesiumGS, Inc. and Contributors
#include "Cesium3DTileset.h"
#include "Async/Async.h"
#include "Camera/CameraTypes.h"
#include "Camera/PlayerCameraManager.h"
#include "Cesium3DTilesSelection/EllipsoidTilesetLoader.h"
#include "Cesium3DTilesSelection/Tile.h"
#include "Cesium3DTilesSelection/TilesetLoadFailureDetails.h"
#include "Cesium3DTilesSelection/TilesetOptions.h"
#include "Cesium3DTilesSelection/TilesetSharedAssetSystem.h"
#include "Cesium3DTilesetLoadFailureDetails.h"
#include "Cesium3DTilesetRoot.h"
#include "CesiumActors.h"
#include "CesiumAsync/SharedAssetDepot.h"
#include "CesiumBoundingVolumeComponent.h"
#include "CesiumCamera.h"
#include "CesiumCameraManager.h"
#include "CesiumCommon.h"
#include "CesiumCustomVersion.h"
#include "CesiumGeospatial/GlobeTransforms.h"
#include "CesiumGltf/ImageAsset.h"
#include "CesiumGltf/Ktx2TranscodeTargets.h"
#include "CesiumGltfComponent.h"
#include "CesiumGltfPointsSceneProxyUpdater.h"
#include "CesiumGltfPrimitiveComponent.h"
#include "CesiumIonClient/Connection.h"
#include "CesiumRasterOverlay.h"
#include "CesiumRuntime.h"
#include "CesiumRuntimeSettings.h"
#include "CesiumTileExcluder.h"
#include "CesiumViewExtension.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/Engine.h"
#include "Engine/LocalPlayer.h"
#include "Engine/SceneCapture2D.h"
#include "Engine/Texture.h"
#include "Engine/Texture2D.h"
#include "Engine/TextureRenderTarget2D.h"
#include "Engine/World.h"
#include "EngineUtils.h"
#include "GameFramework/PlayerController.h"
#include "Kismet/GameplayStatics.h"
#include "LevelSequenceActor.h"
#include "LevelSequencePlayer.h"
#include "Math/UnrealMathUtility.h"
#include "PixelFormat.h"
#include "StereoRendering.h"
#include "UnrealPrepareRendererResources.h"
#include "VecMath.h"
#include <glm/gtc/matrix_inverse.hpp>
#include <memory>
#include <spdlog/spdlog.h>
#ifdef CESIUM_DEBUG_TILE_STATES
#include "HAL/PlatformFileManager.h"
#include <Cesium3DTilesSelection/DebugTileStateDatabase.h>
#endif
FCesium3DTilesetLoadFailure OnCesium3DTilesetLoadFailure{};
#if WITH_EDITOR
#include "Editor.h"
#include "EditorViewportClient.h"
#include "FileHelpers.h"
#include "LevelEditorViewport.h"
#endif
// Avoid complaining about the deprecated metadata struct
PRAGMA_DISABLE_DEPRECATION_WARNINGS
// Sets default values
ACesium3DTileset::ACesium3DTileset()
: AActor(),
Georeference(nullptr),
ResolvedGeoreference(nullptr),
CreditSystem(nullptr),
_pTileset(nullptr),
#ifdef CESIUM_DEBUG_TILE_STATES
_pStateDebug(nullptr),
#endif
_lastTilesRendered(0),
_lastWorkerThreadTileLoadQueueLength(0),
_lastMainThreadTileLoadQueueLength(0),
_lastTilesVisited(0),
_lastTilesCulled(0),
_lastTilesOccluded(0),
_lastTilesWaitingForOcclusionResults(0),
_lastMaxDepthVisited(0),
_captureMovieMode{false},
_beforeMoviePreloadAncestors{PreloadAncestors},
_beforeMoviePreloadSiblings{PreloadSiblings},
_beforeMovieLoadingDescendantLimit{LoadingDescendantLimit},
_beforeMovieUseLodTransitions{true},
_tilesetsBeingDestroyed(0) {
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.TickGroup = ETickingGroup::TG_PostUpdateWork;
#if WITH_EDITOR
this->SetIsSpatiallyLoaded(false);
#endif
this->SetActorEnableCollision(true);
this->RootComponent =
CreateDefaultSubobject<UCesium3DTilesetRoot>(TEXT("Tileset"));
this->Root = this->RootComponent;
PlatformName = UGameplayStatics::GetPlatformName();
}
ACesium3DTileset::~ACesium3DTileset() { this->DestroyTileset(); }
PRAGMA_ENABLE_DEPRECATION_WARNINGS
TSoftObjectPtr<ACesiumGeoreference> ACesium3DTileset::GetGeoreference() const {
return this->Georeference;
}
void ACesium3DTileset::SetMobility(EComponentMobility::Type NewMobility) {
if (NewMobility != this->RootComponent->Mobility) {
this->RootComponent->SetMobility(NewMobility);
DestroyTileset();
}
}
void ACesium3DTileset::SampleHeightMostDetailed(
const TArray<FVector>& LongitudeLatitudeHeightArray,
FCesiumSampleHeightMostDetailedCallback OnHeightsSampled) {
// It's possible to call this function before a Tick happens, so make sure
// that the necessary variables are resolved.
this->ResolveGeoreference();
this->ResolveCameraManager();
this->ResolveCreditSystem();
if (this->_pTileset == nullptr) {
this->LoadTileset();
}
std::vector<CesiumGeospatial::Cartographic> positions;
positions.reserve(LongitudeLatitudeHeightArray.Num());
for (const FVector& position : LongitudeLatitudeHeightArray) {
positions.emplace_back(CesiumGeospatial::Cartographic::fromDegrees(
position.X,
position.Y,
position.Z));
}
auto sampleHeights = [this, &positions]() mutable {
if (this->_pTileset) {
return this->_pTileset->sampleHeightMostDetailed(positions)
.catchImmediately([positions = std::move(positions)](
std::exception&& exception) mutable {
std::vector<bool> sampleSuccess(positions.size(), false);
return Cesium3DTilesSelection::SampleHeightResult{
std::move(positions),
std::move(sampleSuccess),
{exception.what()}};
});
} else {
std::vector<bool> sampleSuccess(positions.size(), false);
return getAsyncSystem().createResolvedFuture(
Cesium3DTilesSelection::SampleHeightResult{
std::move(positions),
std::move(sampleSuccess),
{"Could not sample heights from tileset because it has not "
"been created."}});
}
};
sampleHeights().thenImmediately(
[this, OnHeightsSampled = std::move(OnHeightsSampled)](
Cesium3DTilesSelection::SampleHeightResult&& result) {
if (!IsValid(this))
return;
check(result.positions.size() == result.sampleSuccess.size());
// This should do nothing, but will prevent undefined behavior if
// the array sizes are unexpectedly different.
result.sampleSuccess.resize(result.positions.size(), false);
TArray<FCesiumSampleHeightResult> sampleHeightResults;
sampleHeightResults.Reserve(result.positions.size());
for (size_t i = 0; i < result.positions.size(); ++i) {
const CesiumGeospatial::Cartographic& position = result.positions[i];
FCesiumSampleHeightResult unrealResult;
unrealResult.LongitudeLatitudeHeight = FVector(
CesiumUtility::Math::radiansToDegrees(position.longitude),
CesiumUtility::Math::radiansToDegrees(position.latitude),
position.height);
unrealResult.SampleSuccess = result.sampleSuccess[i];
sampleHeightResults.Emplace(std::move(unrealResult));
}
TArray<FString> warnings;
warnings.Reserve(result.warnings.size());
for (const std::string& warning : result.warnings) {
warnings.Emplace(UTF8_TO_TCHAR(warning.c_str()));
}
OnHeightsSampled.ExecuteIfBound(this, sampleHeightResults, warnings);
});
}
void ACesium3DTileset::SetGeoreference(
TSoftObjectPtr<ACesiumGeoreference> NewGeoreference) {
this->Georeference = NewGeoreference;
this->InvalidateResolvedGeoreference();
this->ResolveGeoreference();
}
ACesiumGeoreference* ACesium3DTileset::ResolveGeoreference() {
if (IsValid(this->ResolvedGeoreference)) {
return this->ResolvedGeoreference;
}
if (IsValid(this->Georeference.Get())) {
this->ResolvedGeoreference = this->Georeference.Get();
} else {
this->ResolvedGeoreference =
ACesiumGeoreference::GetDefaultGeoreferenceForActor(this);
}
UCesium3DTilesetRoot* pRoot = Cast<UCesium3DTilesetRoot>(this->RootComponent);
if (pRoot) {
this->ResolvedGeoreference->OnGeoreferenceUpdated.AddUniqueDynamic(
pRoot,
&UCesium3DTilesetRoot::HandleGeoreferenceUpdated);
this->ResolvedGeoreference->OnEllipsoidChanged.AddUniqueDynamic(
this,
&ACesium3DTileset::HandleOnGeoreferenceEllipsoidChanged);
// Update existing tile positions, if any.
pRoot->HandleGeoreferenceUpdated();
}
return this->ResolvedGeoreference;
}
void ACesium3DTileset::InvalidateResolvedGeoreference() {
if (IsValid(this->ResolvedGeoreference)) {
this->ResolvedGeoreference->OnGeoreferenceUpdated.RemoveAll(
this->RootComponent);
}
this->ResolvedGeoreference = nullptr;
}
TSoftObjectPtr<ACesiumCreditSystem> ACesium3DTileset::GetCreditSystem() const {
return this->CreditSystem;
}
void ACesium3DTileset::SetCreditSystem(
TSoftObjectPtr<ACesiumCreditSystem> NewCreditSystem) {
this->CreditSystem = NewCreditSystem;
this->InvalidateResolvedCreditSystem();
this->ResolveCreditSystem();
}
ACesiumCreditSystem* ACesium3DTileset::ResolveCreditSystem() {
if (IsValid(this->ResolvedCreditSystem)) {
return this->ResolvedCreditSystem;
}
if (IsValid(this->CreditSystem.Get())) {
this->ResolvedCreditSystem = this->CreditSystem.Get();
} else {
this->ResolvedCreditSystem =
ACesiumCreditSystem::GetDefaultCreditSystem(this);
}
// Refresh the tileset so it uses the new credit system.
this->RefreshTileset();
return this->ResolvedCreditSystem;
}
void ACesium3DTileset::InvalidateResolvedCreditSystem() {
this->ResolvedCreditSystem = nullptr;
this->RefreshTileset();
}
TSoftObjectPtr<ACesiumCameraManager>
ACesium3DTileset::GetCameraManager() const {
return this->CameraManager;
}
void ACesium3DTileset::SetCameraManager(
TSoftObjectPtr<ACesiumCameraManager> NewCameraManager) {
this->CameraManager = NewCameraManager;
this->InvalidateResolvedCameraManager();
this->ResolveCameraManager();
}
ACesiumCameraManager* ACesium3DTileset::ResolveCameraManager() {
if (IsValid(this->ResolvedCameraManager)) {
return this->ResolvedCameraManager;
}
if (IsValid(this->CameraManager.Get())) {
this->ResolvedCameraManager = this->CameraManager.Get();
} else {
this->ResolvedCameraManager =
ACesiumCameraManager::GetDefaultCameraManager(this);
}
return this->ResolvedCameraManager;
}
void ACesium3DTileset::InvalidateResolvedCameraManager() {
this->ResolvedCameraManager = nullptr;
this->RefreshTileset();
}
void ACesium3DTileset::RefreshTileset() { this->DestroyTileset(); }
void ACesium3DTileset::TroubleshootToken() {
OnCesium3DTilesetIonTroubleshooting.Broadcast(this);
}
void ACesium3DTileset::AddFocusViewportDelegate() {
#if WITH_EDITOR
FEditorDelegates::OnFocusViewportOnActors.AddLambda(
[this](const TArray<AActor*>& actors) {
if (actors.Num() == 1 && actors[0] == this) {
this->OnFocusEditorViewportOnThis();
}
});
#endif // WITH_EDITOR
}
void ACesium3DTileset::PostInitProperties() {
UE_LOG(
LogCesium,
Verbose,
TEXT("Called PostInitProperties on actor %s"),
*this->GetName());
Super::PostInitProperties();
AddFocusViewportDelegate();
UCesiumRuntimeSettings* pSettings =
GetMutableDefault<UCesiumRuntimeSettings>();
if (pSettings) {
CanEnableOcclusionCulling =
pSettings->EnableExperimentalOcclusionCullingFeature;
#if WITH_EDITOR
pSettings->OnSettingChanged().AddUObject(
this,
&ACesium3DTileset::RuntimeSettingsChanged);
#endif
}
}
void ACesium3DTileset::SetUseLodTransitions(bool InUseLodTransitions) {
if (InUseLodTransitions != this->UseLodTransitions) {
this->UseLodTransitions = InUseLodTransitions;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetTilesetSource(ETilesetSource InSource) {
if (InSource != this->TilesetSource) {
this->DestroyTileset();
this->TilesetSource = InSource;
}
}
namespace {
bool MapsAreEqual(
const TMap<FString, FString>& Lhs,
const TMap<FString, FString>& Rhs) {
if (Lhs.Num() != Rhs.Num()) {
return false;
}
for (const auto& [Key, Value] : Lhs) {
const FString* RhsVal = Rhs.Find(Key);
if (!RhsVal || *RhsVal != Value) {
return false;
}
}
return true;
}
} // namespace
void ACesium3DTileset::SetRequestHeaders(
const TMap<FString, FString>& InRequestHeaders) {
if (!MapsAreEqual(InRequestHeaders, this->RequestHeaders)) {
this->DestroyTileset();
this->RequestHeaders = InRequestHeaders;
}
}
void ACesium3DTileset::SetUrl(const FString& InUrl) {
if (InUrl != this->Url) {
if (this->TilesetSource == ETilesetSource::FromUrl) {
this->DestroyTileset();
}
this->Url = InUrl;
}
}
void ACesium3DTileset::SetIonAssetID(int64 InAssetID) {
if (InAssetID >= 0 && InAssetID != this->IonAssetID) {
if (this->TilesetSource == ETilesetSource::FromCesiumIon) {
this->DestroyTileset();
}
this->IonAssetID = InAssetID;
}
}
void ACesium3DTileset::SetIonAccessToken(const FString& InAccessToken) {
if (this->IonAccessToken != InAccessToken) {
if (this->TilesetSource == ETilesetSource::FromCesiumIon) {
this->DestroyTileset();
}
this->IonAccessToken = InAccessToken;
}
}
void ACesium3DTileset::SetCesiumIonServer(UCesiumIonServer* Server) {
if (this->CesiumIonServer != Server) {
if (this->TilesetSource == ETilesetSource::FromCesiumIon) {
this->DestroyTileset();
}
this->CesiumIonServer = Server;
}
}
void ACesium3DTileset::SetMaximumScreenSpaceError(
double InMaximumScreenSpaceError) {
if (MaximumScreenSpaceError != InMaximumScreenSpaceError) {
MaximumScreenSpaceError = InMaximumScreenSpaceError;
FCesiumGltfPointsSceneProxyUpdater::UpdateSettingsInProxies(this);
}
}
bool ACesium3DTileset::GetEnableOcclusionCulling() const {
return GetDefault<UCesiumRuntimeSettings>()
->EnableExperimentalOcclusionCullingFeature &&
EnableOcclusionCulling;
}
void ACesium3DTileset::SetEnableOcclusionCulling(bool bEnableOcclusionCulling) {
if (this->EnableOcclusionCulling != bEnableOcclusionCulling) {
this->EnableOcclusionCulling = bEnableOcclusionCulling;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetOcclusionPoolSize(int32 newOcclusionPoolSize) {
if (this->OcclusionPoolSize != newOcclusionPoolSize) {
this->OcclusionPoolSize = newOcclusionPoolSize;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetDelayRefinementForOcclusion(
bool bDelayRefinementForOcclusion) {
if (this->DelayRefinementForOcclusion != bDelayRefinementForOcclusion) {
this->DelayRefinementForOcclusion = bDelayRefinementForOcclusion;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetCreatePhysicsMeshes(bool bCreatePhysicsMeshes) {
if (this->CreatePhysicsMeshes != bCreatePhysicsMeshes) {
this->CreatePhysicsMeshes = bCreatePhysicsMeshes;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetCreateNavCollision(bool bCreateNavCollision) {
if (this->CreateNavCollision != bCreateNavCollision) {
this->CreateNavCollision = bCreateNavCollision;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetAlwaysIncludeTangents(bool bAlwaysIncludeTangents) {
if (this->AlwaysIncludeTangents != bAlwaysIncludeTangents) {
this->AlwaysIncludeTangents = bAlwaysIncludeTangents;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetGenerateSmoothNormals(bool bGenerateSmoothNormals) {
if (this->GenerateSmoothNormals != bGenerateSmoothNormals) {
this->GenerateSmoothNormals = bGenerateSmoothNormals;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetEnableWaterMask(bool bEnableMask) {
if (this->EnableWaterMask != bEnableMask) {
this->EnableWaterMask = bEnableMask;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetIgnoreKhrMaterialsUnlit(
bool bIgnoreKhrMaterialsUnlit) {
if (this->IgnoreKhrMaterialsUnlit != bIgnoreKhrMaterialsUnlit) {
this->IgnoreKhrMaterialsUnlit = bIgnoreKhrMaterialsUnlit;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetMaterial(UMaterialInterface* InMaterial) {
if (this->Material != InMaterial) {
this->Material = InMaterial;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetTranslucentMaterial(UMaterialInterface* InMaterial) {
if (this->TranslucentMaterial != InMaterial) {
this->TranslucentMaterial = InMaterial;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetWaterMaterial(UMaterialInterface* InMaterial) {
if (this->WaterMaterial != InMaterial) {
this->WaterMaterial = InMaterial;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetCustomDepthParameters(
FCustomDepthParameters InCustomDepthParameters) {
if (this->CustomDepthParameters != InCustomDepthParameters) {
this->CustomDepthParameters = InCustomDepthParameters;
this->DestroyTileset();
}
}
void ACesium3DTileset::SetPointCloudShading(
FCesiumPointCloudShading InPointCloudShading) {
if (PointCloudShading != InPointCloudShading) {
PointCloudShading = InPointCloudShading;
FCesiumGltfPointsSceneProxyUpdater::UpdateSettingsInProxies(this);
}
}
void ACesium3DTileset::PlayMovieSequencer() {
this->_beforeMoviePreloadAncestors = this->PreloadAncestors;
this->_beforeMoviePreloadSiblings = this->PreloadSiblings;
this->_beforeMovieLoadingDescendantLimit = this->LoadingDescendantLimit;
this->_beforeMovieUseLodTransitions = this->UseLodTransitions;
this->_captureMovieMode = true;
this->PreloadAncestors = false;
this->PreloadSiblings = false;
this->LoadingDescendantLimit = 10000;
this->UseLodTransitions = false;
}
void ACesium3DTileset::StopMovieSequencer() {
this->_captureMovieMode = false;
this->PreloadAncestors = this->_beforeMoviePreloadAncestors;
this->PreloadSiblings = this->_beforeMoviePreloadSiblings;
this->LoadingDescendantLimit = this->_beforeMovieLoadingDescendantLimit;
this->UseLodTransitions = this->_beforeMovieUseLodTransitions;
}
void ACesium3DTileset::PauseMovieSequencer() { this->StopMovieSequencer(); }
#if WITH_EDITOR
void ACesium3DTileset::OnFocusEditorViewportOnThis() {
UE_LOG(
LogCesium,
Verbose,
TEXT("Called OnFocusEditorViewportOnThis on actor %s"),
*this->GetName());
struct CalculateECEFCameraPosition {
const CesiumGeospatial::Ellipsoid& ellipsoid;
glm::dvec3 operator()(const CesiumGeometry::BoundingSphere& sphere) {
const glm::dvec3& center = sphere.getCenter();
glm::dmat4 ENU =
CesiumGeospatial::GlobeTransforms::eastNorthUpToFixedFrame(
center,
ellipsoid);
glm::dvec3 offset =
sphere.getRadius() *
glm::normalize(
glm::dvec3(ENU[0]) + glm::dvec3(ENU[1]) + glm::dvec3(ENU[2]));
glm::dvec3 position = center + offset;
return position;
}
glm::dvec3
operator()(const CesiumGeometry::OrientedBoundingBox& orientedBoundingBox) {
const glm::dvec3& center = orientedBoundingBox.getCenter();
glm::dmat4 ENU =
CesiumGeospatial::GlobeTransforms::eastNorthUpToFixedFrame(
center,
ellipsoid);
const glm::dmat3& halfAxes = orientedBoundingBox.getHalfAxes();
glm::dvec3 offset =
glm::length(halfAxes[0] + halfAxes[1] + halfAxes[2]) *
glm::normalize(
glm::dvec3(ENU[0]) + glm::dvec3(ENU[1]) + glm::dvec3(ENU[2]));
glm::dvec3 position = center + offset;
return position;
}
glm::dvec3
operator()(const CesiumGeospatial::BoundingRegion& boundingRegion) {
return (*this)(boundingRegion.getBoundingBox());
}
glm::dvec3
operator()(const CesiumGeospatial::BoundingRegionWithLooseFittingHeights&
boundingRegionWithLooseFittingHeights) {
return (*this)(boundingRegionWithLooseFittingHeights.getBoundingRegion()
.getBoundingBox());
}
glm::dvec3 operator()(const CesiumGeospatial::S2CellBoundingVolume& s2) {
return (*this)(s2.computeBoundingRegion());
}
};
const Cesium3DTilesSelection::Tile* pRootTile =
this->_pTileset->getRootTile();
if (!pRootTile) {
return;
}
const Cesium3DTilesSelection::BoundingVolume& boundingVolume =
pRootTile->getBoundingVolume();
ACesiumGeoreference* pGeoreference = this->ResolveGeoreference();
const CesiumGeospatial::Ellipsoid& ellipsoid =
pGeoreference->GetEllipsoid()->GetNativeEllipsoid();
// calculate unreal camera position
glm::dvec3 ecefCameraPosition =
std::visit(CalculateECEFCameraPosition{ellipsoid}, boundingVolume);
FVector unrealCameraPosition =
pGeoreference->TransformEarthCenteredEarthFixedPositionToUnreal(
VecMath::createVector(ecefCameraPosition));
// calculate unreal camera orientation
glm::dvec3 ecefCenter =
Cesium3DTilesSelection::getBoundingVolumeCenter(boundingVolume);
FVector unrealCenter =
pGeoreference->TransformEarthCenteredEarthFixedPositionToUnreal(
VecMath::createVector(ecefCenter));
FVector unrealCameraFront =
(unrealCenter - unrealCameraPosition).GetSafeNormal();
FVector unrealCameraRight =
FVector::CrossProduct(FVector::ZAxisVector, unrealCameraFront)
.GetSafeNormal();
FVector unrealCameraUp =
FVector::CrossProduct(unrealCameraFront, unrealCameraRight)
.GetSafeNormal();
FRotator cameraRotator = FMatrix(
unrealCameraFront,
unrealCameraRight,
unrealCameraUp,
FVector::ZeroVector)
.Rotator();
// Update all viewports.
for (FLevelEditorViewportClient* LinkedViewportClient :
GEditor->GetLevelViewportClients()) {
// Dont move camera attach to an actor
if (!LinkedViewportClient->IsAnyActorLocked()) {
FViewportCameraTransform& ViewTransform =
LinkedViewportClient->GetViewTransform();
LinkedViewportClient->SetViewRotation(cameraRotator);
LinkedViewportClient->SetViewLocation(unrealCameraPosition);
LinkedViewportClient->Invalidate();
}
}
}
#endif
const glm::dmat4&
ACesium3DTileset::GetCesiumTilesetToUnrealRelativeWorldTransform() const {
return Cast<UCesium3DTilesetRoot>(this->RootComponent)
->GetCesiumTilesetToUnrealRelativeWorldTransform();
}
void ACesium3DTileset::UpdateTransformFromCesium() {
const glm::dmat4& CesiumToUnreal =
this->GetCesiumTilesetToUnrealRelativeWorldTransform();
TArray<UCesiumGltfComponent*> gltfComponents;
this->GetComponents<UCesiumGltfComponent>(gltfComponents);
for (UCesiumGltfComponent* pGltf : gltfComponents) {
pGltf->UpdateTransformFromCesium(CesiumToUnreal);
}
if (this->BoundingVolumePoolComponent) {
this->BoundingVolumePoolComponent->UpdateTransformFromCesium(
CesiumToUnreal);
}
}
void ACesium3DTileset::HandleOnGeoreferenceEllipsoidChanged(
UCesiumEllipsoid* OldEllipsoid,
UCesiumEllipsoid* NewEllpisoid) {
UE_LOG(LogCesium, Warning, TEXT("Ellipsoid changed"));
this->RefreshTileset();
}
// Called when the game starts or when spawned
void ACesium3DTileset::BeginPlay() {
Super::BeginPlay();
this->ResolveGeoreference();
this->ResolveCameraManager();
this->ResolveCreditSystem();
this->LoadTileset();
// Search for level sequence.
for (auto sequenceActorIt = TActorIterator<ALevelSequenceActor>(GetWorld());
sequenceActorIt;
++sequenceActorIt) {
ALevelSequenceActor* sequenceActor = *sequenceActorIt;
if (!IsValid(sequenceActor->GetSequencePlayer())) {
continue;
}
FScriptDelegate playMovieSequencerDelegate;
playMovieSequencerDelegate.BindUFunction(this, FName("PlayMovieSequencer"));
sequenceActor->GetSequencePlayer()->OnPlay.Add(playMovieSequencerDelegate);
FScriptDelegate stopMovieSequencerDelegate;
stopMovieSequencerDelegate.BindUFunction(this, FName("StopMovieSequencer"));
sequenceActor->GetSequencePlayer()->OnStop.Add(stopMovieSequencerDelegate);
FScriptDelegate pauseMovieSequencerDelegate;
pauseMovieSequencerDelegate.BindUFunction(
this,
FName("PauseMovieSequencer"));
sequenceActor->GetSequencePlayer()->OnPause.Add(
pauseMovieSequencerDelegate);
}
}
void ACesium3DTileset::OnConstruction(const FTransform& Transform) {
this->ResolveGeoreference();
this->ResolveCameraManager();
this->ResolveCreditSystem();
this->LoadTileset();
// Hide all existing tiles. The still-visible ones will be shown next time we
// tick. But if update is suspended, leave the components in their current
// state.
if (!this->SuspendUpdate) {
TArray<UCesiumGltfComponent*> gltfComponents;
this->GetComponents<UCesiumGltfComponent>(gltfComponents);
for (UCesiumGltfComponent* pGltf : gltfComponents) {
if (pGltf && IsValid(pGltf) && pGltf->IsVisible()) {
pGltf->SetVisibility(false, true);
pGltf->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
}
}
}
void ACesium3DTileset::NotifyHit(
UPrimitiveComponent* MyComp,
AActor* Other,
UPrimitiveComponent* OtherComp,
bool bSelfMoved,
FVector HitLocation,
FVector HitNormal,
FVector NormalImpulse,
const FHitResult& Hit) {
// std::cout << "Hit face index: " << Hit.FaceIndex << std::endl;
// FHitResult detailedHit;
// FCollisionQueryParams params;
// params.bReturnFaceIndex = true;
// params.bTraceComplex = true;
// MyComp->LineTraceComponent(detailedHit, Hit.TraceStart, Hit.TraceEnd,
// params);
// std::cout << "Hit face index 2: " << detailedHit.FaceIndex << std::endl;
}
void ACesium3DTileset::UpdateLoadStatus() {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::UpdateLoadStatus)
float nativeLoadProgress = this->_pTileset->computeLoadProgress();
// If native tileset still loading, just copy its progress
if (nativeLoadProgress < 100) {
this->LoadProgress = nativeLoadProgress;
return;
}
// Native tileset is 100% loaded, but there might be a few frames where
// nothing needs to be loaded as we are waiting for occlusion results to come
// back, which means we are not done with loading all the tiles in the tileset
// yet. Interpret this as 99% (almost) done
if (this->_lastTilesWaitingForOcclusionResults > 0) {
this->LoadProgress = 99;
return;
}
// If we have tiles to hide next frame, we haven't completely finished loading
// yet. We need to tick once more. We're really close to done.
if (!this->_tilesToHideNextFrame.empty()) {
this->LoadProgress = glm::min(this->LoadProgress, 99.9999f);
return;
}
// We can now report 100 percent loaded
float lastLoadProgress = this->LoadProgress;
this->LoadProgress = 100;
// Only broadcast the update when we first hit 100%, not everytime
if (lastLoadProgress != LoadProgress) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::BroadcastOnTilesetLoaded)
// Tileset just finished loading, we broadcast the update
UE_LOG(LogCesium, Verbose, TEXT("Broadcasting OnTileLoaded"));
OnTilesetLoaded.Broadcast();
}
}
namespace {
const TSharedRef<CesiumViewExtension, ESPMode::ThreadSafe>&
getCesiumViewExtension() {
static TSharedRef<CesiumViewExtension, ESPMode::ThreadSafe>
cesiumViewExtension =
GEngine->ViewExtensions->NewExtension<CesiumViewExtension>();
return cesiumViewExtension;
}
} // namespace
void ACesium3DTileset::LoadTileset() {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::LoadTileset)
if (this->_pTileset) {
// Tileset already loaded, do nothing.
return;
}
UWorld* pWorld = this->GetWorld();
if (!pWorld) {
return;
}
AWorldSettings* pWorldSettings = pWorld->GetWorldSettings();
if (pWorldSettings && pWorldSettings->bEnableWorldBoundsChecks) {
UE_LOG(
LogCesium,
Warning,
TEXT(
"\"Enable World Bounds Checks\" in the world settings is currently enabled. Please consider disabling it to avoid potential issues."),
*this->Url);
}
// Make sure we have a valid Cesium ion server if we need one.
if (this->TilesetSource == ETilesetSource::FromCesiumIon &&
!IsValid(this->CesiumIonServer)) {
this->Modify();
this->CesiumIonServer = UCesiumIonServer::GetServerForNewObjects();
}
const TSharedRef<CesiumViewExtension, ESPMode::ThreadSafe>&
cesiumViewExtension = getCesiumViewExtension();
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor =
getAssetAccessor();
const CesiumAsync::AsyncSystem& asyncSystem = getAsyncSystem();
// Both the feature flag and the CesiumViewExtension are global, not owned by
// the Tileset. We're just applying one to the other here out of convenience.
cesiumViewExtension->SetEnabled(
GetDefault<UCesiumRuntimeSettings>()
->EnableExperimentalOcclusionCullingFeature);
TArray<UCesiumRasterOverlay*> rasterOverlays;
this->GetComponents<UCesiumRasterOverlay>(rasterOverlays);
TArray<UCesiumTileExcluder*> tileExcluders;
this->GetComponents<UCesiumTileExcluder>(tileExcluders);
const UCesiumFeaturesMetadataComponent* pFeaturesMetadataComponent =
this->FindComponentByClass<UCesiumFeaturesMetadataComponent>();
// Check if this component exists for backwards compatibility.
PRAGMA_DISABLE_DEPRECATION_WARNINGS
const UDEPRECATED_CesiumEncodedMetadataComponent* pEncodedMetadataComponent =
this->FindComponentByClass<UDEPRECATED_CesiumEncodedMetadataComponent>();
this->_featuresMetadataDescription = std::nullopt;
this->_metadataDescription_DEPRECATED = std::nullopt;
if (pFeaturesMetadataComponent) {
FCesiumFeaturesMetadataDescription& description =
this->_featuresMetadataDescription.emplace();
description.Features = {pFeaturesMetadataComponent->FeatureIdSets};
description.PrimitiveMetadata = {
pFeaturesMetadataComponent->PropertyTextureNames};
description.ModelMetadata = {
pFeaturesMetadataComponent->PropertyTables,
pFeaturesMetadataComponent->PropertyTextures};
} else if (pEncodedMetadataComponent) {
UE_LOG(
LogCesium,
Warning,
TEXT(
"CesiumEncodedMetadataComponent is deprecated. Use CesiumFeaturesMetadataComponent instead."));
this->_metadataDescription_DEPRECATED = {
pEncodedMetadataComponent->FeatureTables,
pEncodedMetadataComponent->FeatureTextures};
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
this->_cesiumViewExtension = cesiumViewExtension;
if (GetDefault<UCesiumRuntimeSettings>()
->EnableExperimentalOcclusionCullingFeature &&
this->EnableOcclusionCulling && !this->BoundingVolumePoolComponent) {
const glm::dmat4& cesiumToUnreal =
GetCesiumTilesetToUnrealRelativeWorldTransform();
this->BoundingVolumePoolComponent =
NewObject<UCesiumBoundingVolumePoolComponent>(this);
this->BoundingVolumePoolComponent->SetFlags(
RF_Transient | RF_DuplicateTransient | RF_TextExportTransient);
this->BoundingVolumePoolComponent->RegisterComponent();
this->BoundingVolumePoolComponent->UpdateTransformFromCesium(
cesiumToUnreal);
}
if (this->BoundingVolumePoolComponent) {
this->BoundingVolumePoolComponent->initPool(this->OcclusionPoolSize);
}
CesiumGeospatial::Ellipsoid pNativeEllipsoid =
this->ResolveGeoreference()->GetEllipsoid()->GetNativeEllipsoid();
ACesiumCreditSystem* pCreditSystem = this->ResolvedCreditSystem;
Cesium3DTilesSelection::TilesetExternals externals{
pAssetAccessor,
std::make_shared<UnrealPrepareRendererResources>(this),
asyncSystem,
pCreditSystem ? pCreditSystem->GetExternalCreditSystem() : nullptr,
spdlog::default_logger(),
(GetDefault<UCesiumRuntimeSettings>()
->EnableExperimentalOcclusionCullingFeature &&
this->EnableOcclusionCulling && this->BoundingVolumePoolComponent)
? this->BoundingVolumePoolComponent->getPool()
: nullptr};
this->_startTime = std::chrono::high_resolution_clock::now();
this->LoadProgress = 0;
Cesium3DTilesSelection::TilesetOptions options;
options.ellipsoid = pNativeEllipsoid;
options.enableOcclusionCulling =
GetDefault<UCesiumRuntimeSettings>()
->EnableExperimentalOcclusionCullingFeature &&
this->EnableOcclusionCulling;
options.delayRefinementForOcclusion = this->DelayRefinementForOcclusion;
options.showCreditsOnScreen = ShowCreditsOnScreen;
options.loadErrorCallback =
[this](const Cesium3DTilesSelection::TilesetLoadFailureDetails& details) {
static_assert(
uint8_t(ECesium3DTilesetLoadType::CesiumIon) ==
uint8_t(Cesium3DTilesSelection::TilesetLoadType::CesiumIon));
static_assert(
uint8_t(ECesium3DTilesetLoadType::TilesetJson) ==
uint8_t(Cesium3DTilesSelection::TilesetLoadType::TilesetJson));
static_assert(
uint8_t(ECesium3DTilesetLoadType::Unknown) ==
uint8_t(Cesium3DTilesSelection::TilesetLoadType::Unknown));
uint8_t typeValue = uint8_t(details.type);
assert(
uint8_t(details.type) <=
uint8_t(Cesium3DTilesSelection::TilesetLoadType::TilesetJson));
assert(this->_pTileset == details.pTileset);
FCesium3DTilesetLoadFailureDetails ueDetails{};
ueDetails.Tileset = this;
ueDetails.Type = ECesium3DTilesetLoadType(typeValue);
ueDetails.HttpStatusCode = details.statusCode;
ueDetails.Message = UTF8_TO_TCHAR(details.message.c_str());
// Broadcast the event from the game thread.
// Even if we're already in the game thread, let the stack unwind.
// Otherwise actions that destroy the Tileset will cause a deadlock.
AsyncTask(
ENamedThreads::GameThread,
[ueDetails = std::move(ueDetails)]() {
OnCesium3DTilesetLoadFailure.Broadcast(ueDetails);
});
};
// Generous per-frame time limits for loading / unloading on main thread.
options.mainThreadLoadingTimeLimit = 5.0;
options.tileCacheUnloadTimeLimit = 5.0;
options.contentOptions.generateMissingNormalsSmooth =
this->GenerateSmoothNormals;
// TODO: figure out why water material crashes mac
#if PLATFORM_MAC
#else
options.contentOptions.enableWaterMask = this->EnableWaterMask;
#endif
CesiumGltf::SupportedGpuCompressedPixelFormats supportedFormats;
supportedFormats.ETC1_RGB = GPixelFormats[EPixelFormat::PF_ETC1].Supported;
supportedFormats.ETC2_RGBA =
GPixelFormats[EPixelFormat::PF_ETC2_RGBA].Supported;
supportedFormats.BC1_RGB = GPixelFormats[EPixelFormat::PF_DXT1].Supported;
supportedFormats.BC3_RGBA = GPixelFormats[EPixelFormat::PF_DXT5].Supported;
supportedFormats.BC4_R = GPixelFormats[EPixelFormat::PF_BC4].Supported;
supportedFormats.BC5_RG = GPixelFormats[EPixelFormat::PF_BC5].Supported;
supportedFormats.BC7_RGBA = GPixelFormats[EPixelFormat::PF_BC7].Supported;
supportedFormats.ASTC_4x4_RGBA =
GPixelFormats[EPixelFormat::PF_ASTC_4x4].Supported;
supportedFormats.PVRTC2_4_RGBA =
GPixelFormats[EPixelFormat::PF_PVRTC2].Supported;
supportedFormats.ETC2_EAC_R11 =
GPixelFormats[EPixelFormat::PF_ETC2_R11_EAC].Supported;
supportedFormats.ETC2_EAC_RG11 =
GPixelFormats[EPixelFormat::PF_ETC2_RG11_EAC].Supported;
options.contentOptions.ktx2TranscodeTargets =
CesiumGltf::Ktx2TranscodeTargets(supportedFormats, false);
options.contentOptions.applyTextureTransform = false;
options.requestHeaders.reserve(this->RequestHeaders.Num());
for (const auto& [Key, Value] : this->RequestHeaders) {
options.requestHeaders.emplace_back(CesiumAsync::IAssetAccessor::THeader{
TCHAR_TO_UTF8(*Key),
TCHAR_TO_UTF8(*Value)});
}
switch (this->TilesetSource) {
case ETilesetSource::FromEllipsoid:
UE_LOG(LogCesium, Log, TEXT("Loading tileset from ellipsoid"));
this->_pTileset = TUniquePtr<Cesium3DTilesSelection::Tileset>(
Cesium3DTilesSelection::EllipsoidTilesetLoader::createTileset(
externals,
options)
.release());
break;
case ETilesetSource::FromUrl:
UE_LOG(LogCesium, Log, TEXT("Loading tileset from URL %s"), *this->Url);
this->_pTileset = MakeUnique<Cesium3DTilesSelection::Tileset>(
externals,
TCHAR_TO_UTF8(*this->Url),
options);
break;
case ETilesetSource::FromCesiumIon:
UE_LOG(
LogCesium,
Log,
TEXT("Loading tileset for asset ID %d"),
this->IonAssetID);
FString token = this->IonAccessToken.IsEmpty()
? this->CesiumIonServer->DefaultIonAccessToken
: this->IonAccessToken;
#if WITH_EDITOR
this->CesiumIonServer->ResolveApiUrl();
#endif
std::string ionAssetEndpointUrl =
TCHAR_TO_UTF8(*this->CesiumIonServer->ApiUrl);
if (!ionAssetEndpointUrl.empty()) {
// Make sure the URL ends with a slash
if (!ionAssetEndpointUrl.empty() && *ionAssetEndpointUrl.rbegin() != '/')
ionAssetEndpointUrl += '/';
this->_pTileset = MakeUnique<Cesium3DTilesSelection::Tileset>(
externals,
static_cast<uint32_t>(this->IonAssetID),
TCHAR_TO_UTF8(*token),
options,
ionAssetEndpointUrl);
}
break;
}
#ifdef CESIUM_DEBUG_TILE_STATES
FString dbDirectory = FPaths::Combine(
FPaths::ProjectSavedDir(),
TEXT("CesiumDebugTileStateDatabase"));
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
if (!PlatformFile.DirectoryExists(*dbDirectory)) {
PlatformFile.CreateDirectory(*dbDirectory);
}
FString dbFile =
FPaths::Combine(dbDirectory, this->GetName() + TEXT(".sqlite"));
this->_pStateDebug =
MakeUnique<Cesium3DTilesSelection::DebugTileStateDatabase>(
TCHAR_TO_UTF8(*dbFile));
#endif
for (UCesiumRasterOverlay* pOverlay : rasterOverlays) {
if (pOverlay->IsActive()) {
pOverlay->AddToTileset();
}
}
for (UCesiumTileExcluder* pTileExcluder : tileExcluders) {
if (pTileExcluder->IsActive()) {
pTileExcluder->AddToTileset();
}
}
switch (this->TilesetSource) {
case ETilesetSource::FromEllipsoid:
UE_LOG(LogCesium, Log, TEXT("Loading tileset from ellipsoid done"));
break;
case ETilesetSource::FromUrl:
UE_LOG(
LogCesium,
Log,
TEXT("Loading tileset from URL %s done"),
*this->Url);
break;
case ETilesetSource::FromCesiumIon:
UE_LOG(
LogCesium,
Log,
TEXT("Loading tileset for asset ID %d done"),
this->IonAssetID);
break;
}
switch (ApplyDpiScaling) {
case (EApplyDpiScaling::UseProjectDefault):
_scaleUsingDPI =
GetDefault<UCesiumRuntimeSettings>()->ScaleLevelOfDetailByDPI;
break;
case (EApplyDpiScaling::Yes):
_scaleUsingDPI = true;
break;
case (EApplyDpiScaling::No):
_scaleUsingDPI = false;
break;
default:
_scaleUsingDPI = true;
}
}
void ACesium3DTileset::DestroyTileset() {
if (this->_cesiumViewExtension) {
this->_cesiumViewExtension = nullptr;
}
switch (this->TilesetSource) {
case ETilesetSource::FromEllipsoid:
UE_LOG(LogCesium, Verbose, TEXT("Destroying tileset from ellipsoid"));
break;
case ETilesetSource::FromUrl:
UE_LOG(
LogCesium,
Verbose,
TEXT("Destroying tileset from URL %s"),
*this->Url);
break;
case ETilesetSource::FromCesiumIon:
UE_LOG(
LogCesium,
Verbose,
TEXT("Destroying tileset for asset ID %d"),
this->IonAssetID);
break;
}
// The way CesiumRasterOverlay::add is currently implemented, destroying the
// tileset without removing overlays will make it impossible to add it again
// once a new tileset is created (e.g. when switching between terrain
// assets)
TArray<UCesiumRasterOverlay*> rasterOverlays;
this->GetComponents<UCesiumRasterOverlay>(rasterOverlays);
for (UCesiumRasterOverlay* pOverlay : rasterOverlays) {
if (pOverlay->IsActive()) {
pOverlay->RemoveFromTileset();
}
}
TArray<UCesiumTileExcluder*> tileExcluders;
this->GetComponents<UCesiumTileExcluder>(tileExcluders);
for (UCesiumTileExcluder* pTileExcluder : tileExcluders) {
if (pTileExcluder->IsActive()) {
pTileExcluder->RemoveFromTileset();
}
}
if (!this->_pTileset) {
return;
}
// Don't allow this Cesium3DTileset to be fully destroyed until
// any cesium-native Tilesets it created have wrapped up any async
// operations in progress and have been fully destroyed.
// See IsReadyForFinishDestroy.
++this->_tilesetsBeingDestroyed;
this->_pTileset->getAsyncDestructionCompleteEvent().thenInMainThread(
[this]() { --this->_tilesetsBeingDestroyed; });
this->_pTileset.Reset();
switch (this->TilesetSource) {
case ETilesetSource::FromEllipsoid:
UE_LOG(LogCesium, Verbose, TEXT("Destroying tileset from ellipsoid done"));
break;
case ETilesetSource::FromUrl:
UE_LOG(
LogCesium,
Verbose,
TEXT("Destroying tileset from URL %s done"),
*this->Url);
break;
case ETilesetSource::FromCesiumIon:
UE_LOG(
LogCesium,
Verbose,
TEXT("Destroying tileset for asset ID %d done"),
this->IonAssetID);
break;
}
}
std::vector<FCesiumCamera> ACesium3DTileset::GetCameras() const {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::CollectCameras)
std::vector<FCesiumCamera> cameras = this->GetPlayerCameras();
std::vector<FCesiumCamera> sceneCaptures = this->GetSceneCaptures();
cameras.insert(
cameras.end(),
std::make_move_iterator(sceneCaptures.begin()),
std::make_move_iterator(sceneCaptures.end()));
#if WITH_EDITOR
std::vector<FCesiumCamera> editorCameras = this->GetEditorCameras();
cameras.insert(
cameras.end(),
std::make_move_iterator(editorCameras.begin()),
std::make_move_iterator(editorCameras.end()));
#endif
ACesiumCameraManager* pCameraManager = this->ResolvedCameraManager;
if (pCameraManager) {
const TMap<int32, FCesiumCamera>& extraCameras =
pCameraManager->GetCameras();
cameras.reserve(cameras.size() + extraCameras.Num());
for (auto cameraIt : extraCameras) {
cameras.push_back(cameraIt.Value);
}
}
return cameras;
}
std::vector<FCesiumCamera> ACesium3DTileset::GetPlayerCameras() const {
UWorld* pWorld = this->GetWorld();
if (!pWorld) {
return {};
}
double worldToMeters = 100.0;
AWorldSettings* pWorldSettings = pWorld->GetWorldSettings();
if (pWorldSettings) {
worldToMeters = pWorldSettings->WorldToMeters;
}
TSharedPtr<IStereoRendering, ESPMode::ThreadSafe> pStereoRendering = nullptr;
if (GEngine) {
pStereoRendering = GEngine->StereoRenderingDevice;
}
bool useStereoRendering = false;
if (pStereoRendering && pStereoRendering->IsStereoEnabled()) {
useStereoRendering = true;
}
std::vector<FCesiumCamera> cameras;
cameras.reserve(pWorld->GetNumPlayerControllers());
for (auto playerControllerIt = pWorld->GetPlayerControllerIterator();
playerControllerIt;
playerControllerIt++) {
const TWeakObjectPtr<APlayerController> pPlayerController =
*playerControllerIt;
if (pPlayerController == nullptr) {
continue;
}
const APlayerCameraManager* pPlayerCameraManager =
pPlayerController->PlayerCameraManager;
if (!pPlayerCameraManager) {
continue;
}
double fov = pPlayerCameraManager->GetFOVAngle();
FVector location;
FRotator rotation;
pPlayerController->GetPlayerViewPoint(location, rotation);
int32 sizeX, sizeY;
pPlayerController->GetViewportSize(sizeX, sizeY);
if (sizeX < 1 || sizeY < 1) {
continue;
}
float dpiScalingFactor = 1.0f;
if (this->_scaleUsingDPI) {
ULocalPlayer* LocPlayer = Cast<ULocalPlayer>(pPlayerController->Player);
if (LocPlayer && LocPlayer->ViewportClient) {
dpiScalingFactor = LocPlayer->ViewportClient->GetDPIScale();
}
}
if (useStereoRendering) {
const auto leftEye = EStereoscopicEye::eSSE_LEFT_EYE;
const auto rightEye = EStereoscopicEye::eSSE_RIGHT_EYE;
uint32 stereoLeftSizeX = static_cast<uint32>(sizeX);
uint32 stereoLeftSizeY = static_cast<uint32>(sizeY);
uint32 stereoRightSizeX = static_cast<uint32>(sizeX);
uint32 stereoRightSizeY = static_cast<uint32>(sizeY);
if (useStereoRendering) {
int32 _x;
int32 _y;
pStereoRendering
->AdjustViewRect(leftEye, _x, _y, stereoLeftSizeX, stereoLeftSizeY);
pStereoRendering->AdjustViewRect(
rightEye,
_x,
_y,
stereoRightSizeX,
stereoRightSizeY);
}
FVector2D stereoLeftSize(stereoLeftSizeX, stereoLeftSizeY);
FVector2D stereoRightSize(stereoRightSizeX, stereoRightSizeY);
if (stereoLeftSize.X >= 1.0 && stereoLeftSize.Y >= 1.0) {
FVector leftEyeLocation = location;
FRotator leftEyeRotation = rotation;
pStereoRendering->CalculateStereoViewOffset(
leftEye,
leftEyeRotation,
worldToMeters,
leftEyeLocation);
FMatrix projection =
pStereoRendering->GetStereoProjectionMatrix(leftEye);
// TODO: consider assymetric frustums using 4 fovs
double one_over_tan_half_hfov = projection.M[0][0];
double hfov =
glm::degrees(2.0 * glm::atan(1.0 / one_over_tan_half_hfov));
cameras.emplace_back(
stereoLeftSize,
leftEyeLocation,
leftEyeRotation,
hfov);
}
if (stereoRightSize.X >= 1.0 && stereoRightSize.Y >= 1.0) {
FVector rightEyeLocation = location;
FRotator rightEyeRotation = rotation;
pStereoRendering->CalculateStereoViewOffset(
rightEye,
rightEyeRotation,
worldToMeters,
rightEyeLocation);
FMatrix projection =
pStereoRendering->GetStereoProjectionMatrix(rightEye);
double one_over_tan_half_hfov = projection.M[0][0];
double hfov =
glm::degrees(2.0f * glm::atan(1.0f / one_over_tan_half_hfov));
cameras.emplace_back(
stereoRightSize,
rightEyeLocation,
rightEyeRotation,
hfov);
}
} else {
cameras.emplace_back(
FVector2D(sizeX / dpiScalingFactor, sizeY / dpiScalingFactor),
location,
rotation,
fov);
}
}
return cameras;
}
std::vector<FCesiumCamera> ACesium3DTileset::GetSceneCaptures() const {
// TODO: really USceneCaptureComponent2D can be attached to any actor, is it
// worth searching every actor? Might it be better to provide an interface
// where users can volunteer cameras to be used with the tile selection as
// needed?
TArray<AActor*> sceneCaptures;
static TSubclassOf<ASceneCapture2D> SceneCapture2D =
ASceneCapture2D::StaticClass();
UGameplayStatics::GetAllActorsOfClass(this, SceneCapture2D, sceneCaptures);
std::vector<FCesiumCamera> cameras;
cameras.reserve(sceneCaptures.Num());
for (AActor* pActor : sceneCaptures) {
ASceneCapture2D* pSceneCapture = static_cast<ASceneCapture2D*>(pActor);
if (!pSceneCapture) {
continue;
}
USceneCaptureComponent2D* pSceneCaptureComponent =
pSceneCapture->GetCaptureComponent2D();
if (!pSceneCaptureComponent) {
continue;
}
if (pSceneCaptureComponent->ProjectionType !=
ECameraProjectionMode::Type::Perspective) {
continue;
}
UTextureRenderTarget2D* pRenderTarget =
pSceneCaptureComponent->TextureTarget;
if (!pRenderTarget) {
continue;
}
FVector2D renderTargetSize(pRenderTarget->SizeX, pRenderTarget->SizeY);
if (renderTargetSize.X < 1.0 || renderTargetSize.Y < 1.0) {
continue;
}
FVector captureLocation = pSceneCaptureComponent->GetComponentLocation();
FRotator captureRotation = pSceneCaptureComponent->GetComponentRotation();
double captureFov = pSceneCaptureComponent->FOVAngle;
cameras.emplace_back(
renderTargetSize,
captureLocation,
captureRotation,
captureFov);
}
return cameras;
}
/*static*/ Cesium3DTilesSelection::ViewState
ACesium3DTileset::CreateViewStateFromViewParameters(
const FCesiumCamera& camera,
const glm::dmat4& unrealWorldToTileset,
UCesiumEllipsoid* ellipsoid) {
double horizontalFieldOfView =
FMath::DegreesToRadians(camera.FieldOfViewDegrees);
double actualAspectRatio;
glm::dvec2 size(camera.ViewportSize.X, camera.ViewportSize.Y);
if (camera.OverrideAspectRatio != 0.0f) {
// Use aspect ratio and recompute effective viewport size after black bars
// are added.
actualAspectRatio = camera.OverrideAspectRatio;
double computedX = actualAspectRatio * camera.ViewportSize.Y;
double computedY = camera.ViewportSize.Y / actualAspectRatio;
double barWidth = camera.ViewportSize.X - computedX;
double barHeight = camera.ViewportSize.Y - computedY;
if (barWidth > 0.0 && barWidth > barHeight) {
// Black bars on the sides
size.x = computedX;
} else if (barHeight > 0.0 && barHeight > barWidth) {
// Black bars on the top and bottom
size.y = computedY;
}
} else {
actualAspectRatio = camera.ViewportSize.X / camera.ViewportSize.Y;
}
double verticalFieldOfView =
atan(tan(horizontalFieldOfView * 0.5) / actualAspectRatio) * 2.0;
FVector direction = camera.Rotation.RotateVector(FVector(1.0f, 0.0f, 0.0f));
FVector up = camera.Rotation.RotateVector(FVector(0.0f, 0.0f, 1.0f));
glm::dvec3 tilesetCameraLocation = glm::dvec3(
unrealWorldToTileset *
glm::dvec4(camera.Location.X, camera.Location.Y, camera.Location.Z, 1.0));
glm::dvec3 tilesetCameraFront = glm::normalize(glm::dvec3(
unrealWorldToTileset *
glm::dvec4(direction.X, direction.Y, direction.Z, 0.0)));
glm::dvec3 tilesetCameraUp = glm::normalize(
glm::dvec3(unrealWorldToTileset * glm::dvec4(up.X, up.Y, up.Z, 0.0)));
return Cesium3DTilesSelection::ViewState::create(
tilesetCameraLocation,
tilesetCameraFront,
tilesetCameraUp,
size,
horizontalFieldOfView,
verticalFieldOfView,
ellipsoid->GetNativeEllipsoid());
}
#if WITH_EDITOR
std::vector<FCesiumCamera> ACesium3DTileset::GetEditorCameras() const {
if (!GEditor) {
return {};
}
UWorld* pWorld = this->GetWorld();
if (!IsValid(pWorld)) {
return {};
}
// Do not include editor cameras when running in a game world (which includes
// Play-in-Editor)
if (pWorld->IsGameWorld()) {
return {};
}
const TArray<FEditorViewportClient*>& viewportClients =
GEditor->GetAllViewportClients();
std::vector<FCesiumCamera> cameras;
cameras.reserve(viewportClients.Num());
for (FEditorViewportClient* pEditorViewportClient : viewportClients) {
if (!pEditorViewportClient) {
continue;
}
if (!pEditorViewportClient->IsVisible() ||
!pEditorViewportClient->IsRealtime() ||
!pEditorViewportClient->IsPerspective()) {
continue;
}
FRotator rotation;
if (pEditorViewportClient->bUsingOrbitCamera) {
rotation = (pEditorViewportClient->GetLookAtLocation() -
pEditorViewportClient->GetViewLocation())
.Rotation();
} else {
rotation = pEditorViewportClient->GetViewRotation();
}
const FVector& location = pEditorViewportClient->GetViewLocation();
double fov = pEditorViewportClient->ViewFOV;
FIntPoint offset;
FIntPoint size;
pEditorViewportClient->GetViewportDimensions(offset, size);
if (size.X < 1 || size.Y < 1) {
continue;
}
if (this->_scaleUsingDPI) {
float dpiScalingFactor = pEditorViewportClient->GetDPIScale();
size.X = static_cast<float>(size.X) / dpiScalingFactor;
size.Y = static_cast<float>(size.Y) / dpiScalingFactor;
}
if (pEditorViewportClient->IsAspectRatioConstrained()) {
cameras.emplace_back(
size,
location,
rotation,
fov,
pEditorViewportClient->AspectRatio);
} else {
cameras.emplace_back(size, location, rotation, fov);
}
}
return cameras;
}
#endif
bool ACesium3DTileset::ShouldTickIfViewportsOnly() const {
return this->UpdateInEditor;
}
namespace {
template <typename Func>
void forEachRenderableTile(const auto& tiles, Func&& f) {
for (Cesium3DTilesSelection::Tile* pTile : tiles) {
if (!pTile ||
pTile->getState() != Cesium3DTilesSelection::TileLoadState::Done) {
continue;
}
const Cesium3DTilesSelection::TileContent& content = pTile->getContent();
const Cesium3DTilesSelection::TileRenderContent* pRenderContent =
content.getRenderContent();
if (!pRenderContent) {
continue;
}
UCesiumGltfComponent* Gltf = static_cast<UCesiumGltfComponent*>(
pRenderContent->getRenderResources());
if (!Gltf) {
// When a tile does not have render resources (i.e. a glTF), then
// the resources either have not yet been loaded or prepared,
// or the tile is from an external tileset and does not directly
// own renderable content. In both cases, the tile is ignored here.
continue;
}
f(pTile, Gltf);
}
}
void removeVisibleTilesFromList(
std::vector<Cesium3DTilesSelection::Tile*>& list,
const std::vector<Cesium3DTilesSelection::Tile*>& visibleTiles) {
if (list.empty()) {
return;
}
for (Cesium3DTilesSelection::Tile* pTile : visibleTiles) {
auto it = std::find(list.begin(), list.end(), pTile);
if (it != list.end()) {
list.erase(it);
}
}
}
/**
* @brief Hides the visual representations of the given tiles.
*
* The visual representations (i.e. the `getRendererResources` of the
* tiles) are assumed to be `UCesiumGltfComponent` instances that
* are made invisible by this call.
*
* @param tiles The tiles to hide
*/
void hideTiles(const std::vector<Cesium3DTilesSelection::Tile*>& tiles) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::HideTiles)
forEachRenderableTile(
tiles,
[](Cesium3DTilesSelection::Tile* /*pTile*/, UCesiumGltfComponent* pGltf) {
if (pGltf->IsVisible()) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::SetVisibilityFalse)
pGltf->SetVisibility(false, true);
} else {
// TODO: why is this happening?
UE_LOG(
LogCesium,
Verbose,
TEXT("Tile to no longer render does not have a visible Gltf"));
}
});
}
/**
* @brief Removes collision for tiles that have been removed from the render
* list. This includes tiles that are fading out.
*/
void removeCollisionForTiles(
const std::unordered_set<Cesium3DTilesSelection::Tile*>& tiles) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::RemoveCollisionForTiles)
forEachRenderableTile(
tiles,
[](Cesium3DTilesSelection::Tile* /*pTile*/, UCesiumGltfComponent* pGltf) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::SetCollisionDisabled)
pGltf->SetCollisionEnabled(ECollisionEnabled::NoCollision);
});
}
/**
* @brief Applies the actor collision settings for a newly created glTF
* component
*
* TODO Add details here what that means
* @param BodyInstance ...
* @param Gltf ...
*/
void applyActorCollisionSettings(
const FBodyInstance& BodyInstance,
UCesiumGltfComponent* Gltf) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::ApplyActorCollisionSettings)
const TArray<USceneComponent*>& ChildrenComponents =
Gltf->GetAttachChildren();
for (USceneComponent* ChildComponent : ChildrenComponents) {
UCesiumGltfPrimitiveComponent* PrimitiveComponent =
Cast<UCesiumGltfPrimitiveComponent>(ChildComponent);
if (PrimitiveComponent != nullptr) {
if (PrimitiveComponent->GetCollisionObjectType() !=
BodyInstance.GetObjectType()) {
PrimitiveComponent->SetCollisionObjectType(
BodyInstance.GetObjectType());
}
const UEnum* ChannelEnum = StaticEnum<ECollisionChannel>();
if (ChannelEnum) {
FCollisionResponseContainer responseContainer =
BodyInstance.GetResponseToChannels();
PrimitiveComponent->SetCollisionResponseToChannels(responseContainer);
}
}
}
}
} // namespace
void ACesium3DTileset::updateTilesetOptionsFromProperties() {
Cesium3DTilesSelection::TilesetOptions& options =
this->_pTileset->getOptions();
options.maximumScreenSpaceError =
static_cast<double>(this->MaximumScreenSpaceError);
options.maximumCachedBytes = this->MaximumCachedBytes;
options.preloadAncestors = this->PreloadAncestors;
options.preloadSiblings = this->PreloadSiblings;
options.forbidHoles = this->ForbidHoles;
options.maximumSimultaneousTileLoads = this->MaximumSimultaneousTileLoads;
options.loadingDescendantLimit = this->LoadingDescendantLimit;
options.enableFrustumCulling = this->EnableFrustumCulling;
options.enableOcclusionCulling =
GetDefault<UCesiumRuntimeSettings>()
->EnableExperimentalOcclusionCullingFeature &&
this->EnableOcclusionCulling;
options.showCreditsOnScreen = this->ShowCreditsOnScreen;
options.delayRefinementForOcclusion = this->DelayRefinementForOcclusion;
options.enableFogCulling = this->EnableFogCulling;
options.enforceCulledScreenSpaceError = this->EnforceCulledScreenSpaceError;
options.culledScreenSpaceError =
static_cast<double>(this->CulledScreenSpaceError);
options.enableLodTransitionPeriod = this->UseLodTransitions;
options.lodTransitionLength = this->LodTransitionLength;
// options.kickDescendantsWhileFadingIn = false;
}
void ACesium3DTileset::updateLastViewUpdateResultState(
const Cesium3DTilesSelection::ViewUpdateResult& result) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::updateLastViewUpdateResultState)
if (this->DrawTileInfo) {
const UWorld* World = GetWorld();
check(World);
const TSoftObjectPtr<ACesiumGeoreference> Georeference =
ResolveGeoreference();
check(Georeference);
for (Cesium3DTilesSelection::Tile* tile : result.tilesToRenderThisFrame) {
CesiumGeometry::OrientedBoundingBox obb =
Cesium3DTilesSelection::getOrientedBoundingBoxFromBoundingVolume(
tile->getBoundingVolume(),
Georeference->GetEllipsoid()->GetNativeEllipsoid());
FVector unrealCenter =
Georeference->TransformEarthCenteredEarthFixedPositionToUnreal(
VecMath::createVector(obb.getCenter()));
FString text = FString::Printf(
TEXT("ID %s (%p)"),
UTF8_TO_TCHAR(
Cesium3DTilesSelection::TileIdUtilities::createTileIdString(
tile->getTileID())
.c_str()),
tile);
DrawDebugString(World, unrealCenter, text, nullptr, FColor::Red, 0, true);
}
}
#ifdef CESIUM_DEBUG_TILE_STATES
if (this->_pStateDebug && GetWorld()->IsPlayInEditor()) {
this->_pStateDebug->recordAllTileStates(
result.frameNumber,
*this->_pTileset);
}
#endif
if (!this->LogSelectionStats && !this->LogSharedAssetStats) {
return;
}
if (result.tilesToRenderThisFrame.size() != this->_lastTilesRendered ||
result.workerThreadTileLoadQueueLength !=
this->_lastWorkerThreadTileLoadQueueLength ||
result.mainThreadTileLoadQueueLength !=
this->_lastMainThreadTileLoadQueueLength ||
result.tilesVisited != this->_lastTilesVisited ||
result.culledTilesVisited != this->_lastCulledTilesVisited ||
result.tilesCulled != this->_lastTilesCulled ||
result.tilesOccluded != this->_lastTilesOccluded ||
result.tilesWaitingForOcclusionResults !=
this->_lastTilesWaitingForOcclusionResults ||
result.maxDepthVisited != this->_lastMaxDepthVisited) {
this->_lastTilesRendered = result.tilesToRenderThisFrame.size();
this->_lastWorkerThreadTileLoadQueueLength =
result.workerThreadTileLoadQueueLength;
this->_lastMainThreadTileLoadQueueLength =
result.mainThreadTileLoadQueueLength;
this->_lastTilesVisited = result.tilesVisited;
this->_lastCulledTilesVisited = result.culledTilesVisited;
this->_lastTilesCulled = result.tilesCulled;
this->_lastTilesOccluded = result.tilesOccluded;
this->_lastTilesWaitingForOcclusionResults =
result.tilesWaitingForOcclusionResults;
this->_lastMaxDepthVisited = result.maxDepthVisited;
if (this->LogSelectionStats) {
UE_LOG(
LogCesium,
Display,
TEXT(
"%s: %d ms, Unreal Frame #%d, Tileset Frame: #%d, Visited %d, Culled Visited %d, Rendered %d, Culled %d, Occluded %d, Waiting For Occlusion Results %d, Max Depth Visited: %d, Loading-Worker %d, Loading-Main %d, Loaded tiles %g%%"),
*this->GetName(),
(std::chrono::high_resolution_clock::now() - this->_startTime)
.count() /
1000000,
GFrameCounter,
result.frameNumber,
result.tilesVisited,
result.culledTilesVisited,
result.tilesToRenderThisFrame.size(),
result.tilesCulled,
result.tilesOccluded,
result.tilesWaitingForOcclusionResults,
result.maxDepthVisited,
result.workerThreadTileLoadQueueLength,
result.mainThreadTileLoadQueueLength,
this->LoadProgress);
}
if (this->LogSharedAssetStats && this->_pTileset) {
const Cesium3DTilesSelection::TilesetSharedAssetSystem::ImageDepot&
imageDepot = *this->_pTileset->getSharedAssetSystem().pImage;
UE_LOG(
LogCesium,
Display,
TEXT(
"Images shared asset depot: %d distinct assets, %d inactive assets pending deletion (%d bytes)"),
imageDepot.getAssetCount(),
imageDepot.getInactiveAssetCount(),
imageDepot.getInactiveAssetTotalSizeBytes());
}
}
}
void ACesium3DTileset::showTilesToRender(
const std::vector<Cesium3DTilesSelection::Tile*>& tiles) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::ShowTilesToRender)
forEachRenderableTile(
tiles,
[&RootComponent = this->RootComponent,
&BodyInstance = this->BodyInstance](
Cesium3DTilesSelection::Tile* pTile,
UCesiumGltfComponent* pGltf) {
applyActorCollisionSettings(BodyInstance, pGltf);
if (pGltf->GetAttachParent() == nullptr) {
// The AttachToComponent method is ridiculously complex,
// so print a warning if attaching fails for some reason
bool attached = pGltf->AttachToComponent(
RootComponent,
FAttachmentTransformRules::KeepRelativeTransform);
if (!attached) {
FString tileIdString(
Cesium3DTilesSelection::TileIdUtilities::createTileIdString(
pTile->getTileID())
.c_str());
UE_LOG(
LogCesium,
Warning,
TEXT("Tile %s could not be attached to root"),
*tileIdString);
}
}
if (!pGltf->IsVisible()) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::SetVisibilityTrue)
pGltf->SetVisibility(true, true);
}
{
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::SetCollisionEnabled)
pGltf->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
}
});
}
static void updateTileFades(const auto& tiles, bool fadingIn) {
forEachRenderableTile(
tiles,
[fadingIn](
Cesium3DTilesSelection::Tile* pTile,
UCesiumGltfComponent* pGltf) {
float percentage = pTile->getContent()
.getRenderContent()
->getLodTransitionFadePercentage();
pGltf->UpdateFade(percentage, fadingIn);
});
}
// Called every frame
void ACesium3DTileset::Tick(float DeltaTime) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::TilesetTick)
Super::Tick(DeltaTime);
this->ResolveGeoreference();
this->ResolveCameraManager();
this->ResolveCreditSystem();
UCesium3DTilesetRoot* pRoot = Cast<UCesium3DTilesetRoot>(this->RootComponent);
if (!pRoot) {
return;
}
if (this->SuspendUpdate) {
return;
}
if (!this->_pTileset) {
LoadTileset();
// In the unlikely event that we _still_ don't have a tileset, stop here so
// we don't crash below. This shouldn't happen.
if (!this->_pTileset) {
assert(false);
return;
}
}
if (this->BoundingVolumePoolComponent && this->_cesiumViewExtension) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::UpdateOcclusion)
const TArray<USceneComponent*>& children =
this->BoundingVolumePoolComponent->GetAttachChildren();
for (USceneComponent* pChild : children) {
UCesiumBoundingVolumeComponent* pBoundingVolume =
Cast<UCesiumBoundingVolumeComponent>(pChild);
if (!pBoundingVolume) {
continue;
}
pBoundingVolume->UpdateOcclusion(*this->_cesiumViewExtension.Get());
}
}
updateTilesetOptionsFromProperties();
std::vector<FCesiumCamera> cameras = this->GetCameras();
glm::dmat4 ueTilesetToUeWorld =
VecMath::createMatrix4D(this->GetActorTransform().ToMatrixWithScale());
const glm::dmat4& cesiumTilesetToUeTileset =
this->GetCesiumTilesetToUnrealRelativeWorldTransform();
glm::dmat4 unrealWorldToCesiumTileset =
glm::affineInverse(ueTilesetToUeWorld * cesiumTilesetToUeTileset);
if (glm::isnan(unrealWorldToCesiumTileset[3].x) ||
glm::isnan(unrealWorldToCesiumTileset[3].y) ||
glm::isnan(unrealWorldToCesiumTileset[3].z)) {
// Probably caused by a zero scale.
return;
}
UCesiumEllipsoid* ellipsoid = this->ResolveGeoreference()->GetEllipsoid();
std::vector<Cesium3DTilesSelection::ViewState> frustums;
for (const FCesiumCamera& camera : cameras) {
frustums.push_back(CreateViewStateFromViewParameters(
camera,
unrealWorldToCesiumTileset,
ellipsoid));
}
const Cesium3DTilesSelection::ViewUpdateResult* pResult;
if (this->_captureMovieMode) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::updateViewOffline)
pResult = &this->_pTileset->updateViewOffline(frustums);
} else {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::updateView)
pResult = &this->_pTileset->updateView(frustums, DeltaTime);
}
updateLastViewUpdateResultState(*pResult);
removeCollisionForTiles(pResult->tilesFadingOut);
removeVisibleTilesFromList(
_tilesToHideNextFrame,
pResult->tilesToRenderThisFrame);
hideTiles(_tilesToHideNextFrame);
_tilesToHideNextFrame.clear();
for (Cesium3DTilesSelection::Tile* pTile : pResult->tilesFadingOut) {
Cesium3DTilesSelection::TileRenderContent* pRenderContent =
pTile->getContent().getRenderContent();
if (!this->UseLodTransitions ||
(pRenderContent &&
pRenderContent->getLodTransitionFadePercentage() >= 1.0f)) {
_tilesToHideNextFrame.push_back(pTile);
}
}
showTilesToRender(pResult->tilesToRenderThisFrame);
if (this->UseLodTransitions) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::UpdateTileFades)
updateTileFades(pResult->tilesToRenderThisFrame, true);
updateTileFades(pResult->tilesFadingOut, false);
}
this->UpdateLoadStatus();
}
void ACesium3DTileset::EndPlay(const EEndPlayReason::Type EndPlayReason) {
this->DestroyTileset();
AActor::EndPlay(EndPlayReason);
}
void ACesium3DTileset::PostLoad() {
BodyInstance.FixupData(this); // We need to call this one after Loading the
// actor to have correct BodyInstance values.
Super::PostLoad();
if (CesiumActors::shouldValidateFlags(this))
CesiumActors::validateActorFlags(this);
#if WITH_EDITOR
const int32 CesiumVersion =
this->GetLinkerCustomVersion(FCesiumCustomVersion::GUID);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (CesiumVersion < FCesiumCustomVersion::CesiumIonServer) {
this->CesiumIonServer = UCesiumIonServer::GetBackwardCompatibleServer(
this->IonAssetEndpointUrl_DEPRECATED);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
#endif
}
void ACesium3DTileset::Serialize(FArchive& Ar) {
Super::Serialize(Ar);
Ar.UsingCustomVersion(FCesiumCustomVersion::GUID);
const int32 CesiumVersion = Ar.CustomVer(FCesiumCustomVersion::GUID);
if (CesiumVersion < FCesiumCustomVersion::TilesetExplicitSource) {
// In previous versions, the tileset source was inferred from the presence
// of a non-empty URL property, rather than being explicitly specified.
if (this->Url.Len() > 0) {
this->TilesetSource = ETilesetSource::FromUrl;
} else {
this->TilesetSource = ETilesetSource::FromCesiumIon;
}
}
if (CesiumVersion < FCesiumCustomVersion::TilesetMobilityRemoved) {
this->RootComponent->SetMobility(this->Mobility_DEPRECATED);
}
}
#if WITH_EDITOR
void ACesium3DTileset::PostEditChangeProperty(
FPropertyChangedEvent& PropertyChangedEvent) {
Super::PostEditChangeProperty(PropertyChangedEvent);
if (!PropertyChangedEvent.Property) {
return;
}
FName PropName = PropertyChangedEvent.Property->GetFName();
FString PropNameAsString = PropertyChangedEvent.Property->GetName();
if (PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, TilesetSource) ||
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, Url) ||
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, IonAssetID) ||
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, IonAccessToken) ||
PropName ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, CreatePhysicsMeshes) ||
PropName ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, CreateNavCollision) ||
PropName ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, AlwaysIncludeTangents) ||
PropName ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, GenerateSmoothNormals) ||
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, EnableWaterMask) ||
PropName ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, IgnoreKhrMaterialsUnlit) ||
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, Material) ||
PropName ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, TranslucentMaterial) ||
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, WaterMaterial) ||
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, ApplyDpiScaling) ||
PropName ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, EnableOcclusionCulling) ||
PropName ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, UseLodTransitions) ||
PropName ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, ShowCreditsOnScreen) ||
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, Root) ||
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, CesiumIonServer) ||
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, RequestHeaders) ||
// For properties nested in structs, GET_MEMBER_NAME_CHECKED will prefix
// with the struct name, so just do a manual string comparison.
PropNameAsString == TEXT("RenderCustomDepth") ||
PropNameAsString == TEXT("CustomDepthStencilValue") ||
PropNameAsString == TEXT("CustomDepthStencilWriteMask")) {
this->DestroyTileset();
} else if (
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, Georeference)) {
this->InvalidateResolvedGeoreference();
} else if (
PropName == GET_MEMBER_NAME_CHECKED(ACesium3DTileset, CreditSystem)) {
this->InvalidateResolvedCreditSystem();
} else if (
PropName ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, MaximumScreenSpaceError)) {
TArray<UCesiumRasterOverlay*> rasterOverlays;
this->GetComponents<UCesiumRasterOverlay>(rasterOverlays);
for (UCesiumRasterOverlay* pOverlay : rasterOverlays) {
pOverlay->Refresh();
}
TArray<UCesiumTileExcluder*> tileExcluders;
this->GetComponents<UCesiumTileExcluder>(tileExcluders);
for (UCesiumTileExcluder* pTileExcluder : tileExcluders) {
pTileExcluder->Refresh();
}
// Maximum Screen Space Error can affect how attenuated points are rendered,
// so propagate the new value to the render proxies for this tileset.
FCesiumGltfPointsSceneProxyUpdater::UpdateSettingsInProxies(this);
}
}
void ACesium3DTileset::PostEditChangeChainProperty(
FPropertyChangedChainEvent& PropertyChangedChainEvent) {
Super::PostEditChangeChainProperty(PropertyChangedChainEvent);
if (!PropertyChangedChainEvent.Property ||
PropertyChangedChainEvent.PropertyChain.IsEmpty()) {
return;
}
FName PropName =
PropertyChangedChainEvent.PropertyChain.GetHead()->GetValue()->GetFName();
if (PropName ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, PointCloudShading)) {
FCesiumGltfPointsSceneProxyUpdater::UpdateSettingsInProxies(this);
}
}
void ACesium3DTileset::PostEditUndo() {
Super::PostEditUndo();
// It doesn't appear to be possible to get detailed information about what
// changed in the undo/redo operation, so we have to assume the worst and
// recreate the tileset.
this->DestroyTileset();
}
void ACesium3DTileset::PostEditImport() {
Super::PostEditImport();
// Recreate the tileset on Paste.
this->DestroyTileset();
}
bool ACesium3DTileset::CanEditChange(const FProperty* InProperty) const {
if (InProperty->GetFName() ==
GET_MEMBER_NAME_CHECKED(ACesium3DTileset, EnableWaterMask)) {
// Disable this option on Mac
return PlatformName != TEXT("Mac");
}
return true;
}
#endif
void ACesium3DTileset::BeginDestroy() {
this->InvalidateResolvedGeoreference();
this->DestroyTileset();
AActor::BeginDestroy();
}
bool ACesium3DTileset::IsReadyForFinishDestroy() {
bool ready = AActor::IsReadyForFinishDestroy();
ready &= this->_tilesetsBeingDestroyed == 0;
if (!ready) {
getAssetAccessor()->tick();
getAsyncSystem().dispatchMainThreadTasks();
}
return ready;
}
void ACesium3DTileset::Destroyed() {
this->DestroyTileset();
AActor::Destroyed();
}
#if WITH_EDITOR
void ACesium3DTileset::RuntimeSettingsChanged(
UObject* pObject,
struct FPropertyChangedEvent& changed) {
bool occlusionCullingAvailable =
GetDefault<UCesiumRuntimeSettings>()
->EnableExperimentalOcclusionCullingFeature;
if (occlusionCullingAvailable != this->CanEnableOcclusionCulling) {
this->CanEnableOcclusionCulling = occlusionCullingAvailable;
this->RefreshTileset();
}
}
#endif