diff --git a/Source/CesiumRuntime/Private/Cesium3DTileset.cpp b/Source/CesiumRuntime/Private/Cesium3DTileset.cpp index 7d9719203..a6b30a4ec 100644 --- a/Source/CesiumRuntime/Private/Cesium3DTileset.cpp +++ b/Source/CesiumRuntime/Private/Cesium3DTileset.cpp @@ -883,6 +883,12 @@ void ACesium3DTileset::UpdateLoadStatus() { this->LoadProgress = this->_pTileset->computeLoadProgress(); + // If we have tiles to hide next frame, we haven't completely finished loading + // yet. We need to tick once more + if (!this->_tilesToHideNextFrame.empty()) { + this->LoadProgress = glm::min(this->LoadProgress, 99.9999f); + } + if (this->LoadProgress < 100 || this->_lastTilesWaitingForOcclusionResults > 0) { this->_activeLoading = true; @@ -2028,12 +2034,15 @@ void ACesium3DTileset::Tick(float DeltaTime) { CreateViewStateFromViewParameters(camera, unrealWorldToCesiumTileset)); } - const Cesium3DTilesSelection::ViewUpdateResult& result = - this->_captureMovieMode - ? this->_pTileset->updateViewOffline(frustums) - : this->_pTileset->updateView(frustums, DeltaTime); + Cesium3DTilesSelection::ViewUpdateResult result; + if (this->_captureMovieMode) { + TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::updateViewOffline) + result = this->_pTileset->updateViewOffline(frustums); + } else { + TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::updateView) + result = this->_pTileset->updateView(frustums, DeltaTime); + } updateLastViewUpdateResultState(result); - this->UpdateLoadStatus(); removeCollisionForTiles(result.tilesFadingOut); @@ -2066,6 +2075,8 @@ void ACesium3DTileset::Tick(float DeltaTime) { updateTileFade(pTile, false); } } + + this->UpdateLoadStatus(); } void ACesium3DTileset::EndPlay(const EEndPlayReason::Type EndPlayReason) { diff --git a/Source/CesiumRuntime/Private/CesiumRuntime.cpp b/Source/CesiumRuntime/Private/CesiumRuntime.cpp index d0289b183..a46aeada0 100644 --- a/Source/CesiumRuntime/Private/CesiumRuntime.cpp +++ b/Source/CesiumRuntime/Private/CesiumRuntime.cpp @@ -100,20 +100,28 @@ std::string getCacheDatabaseName() { } // namespace +std::shared_ptr& getCacheDatabase() { + static int MaxCacheItems = + GetDefault()->MaxCacheItems; + + static std::shared_ptr pCacheDatabase = + std::make_shared( + spdlog::default_logger(), + getCacheDatabaseName(), + MaxCacheItems); + + return pCacheDatabase; +} + const std::shared_ptr& getAssetAccessor() { static int RequestsPerCachePrune = GetDefault()->RequestsPerCachePrune; - static int MaxCacheItems = - GetDefault()->MaxCacheItems; static std::shared_ptr pAssetAccessor = std::make_shared( std::make_shared( spdlog::default_logger(), std::make_shared(), - std::make_shared( - spdlog::default_logger(), - getCacheDatabaseName(), - MaxCacheItems), + getCacheDatabase(), RequestsPerCachePrune)); return pAssetAccessor; } diff --git a/Source/CesiumRuntime/Private/Tests/CesiumGeoreference.spec.cpp b/Source/CesiumRuntime/Private/Tests/CesiumGeoreference.spec.cpp index 571a69fb0..ac35d51c0 100644 --- a/Source/CesiumRuntime/Private/Tests/CesiumGeoreference.spec.cpp +++ b/Source/CesiumRuntime/Private/Tests/CesiumGeoreference.spec.cpp @@ -12,7 +12,7 @@ using namespace CesiumUtility; BEGIN_DEFINE_SPEC( FCesiumGeoreferenceSpec, - "Cesium.Georeference", + "Cesium.Unit.Georeference", EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter) diff --git a/Source/CesiumRuntime/Private/Tests/CesiumLoadTest.cpp b/Source/CesiumRuntime/Private/Tests/CesiumLoadTest.cpp index 682b7bdd9..af8b064a1 100644 --- a/Source/CesiumRuntime/Private/Tests/CesiumLoadTest.cpp +++ b/Source/CesiumRuntime/Private/Tests/CesiumLoadTest.cpp @@ -7,20 +7,15 @@ #include "Tests/AutomationCommon.h" #include "Tests/AutomationEditorCommon.h" -#include "Editor.h" -#include "Engine/World.h" -#include "EngineUtils.h" -#include "GameFramework/PlayerStart.h" - -#include "Cesium3DTileset.h" -#include "CesiumCameraManager.h" -#include "CesiumGeoreference.h" -#include "CesiumIonRasterOverlay.h" +#include "CesiumAsync/ICacheDatabase.h" +#include "CesiumGltfComponent.h" #include "CesiumRuntime.h" -#include "CesiumSunSky.h" +#include "CesiumSceneGeneration.h" #include "CesiumTestHelpers.h" #include "GlobeAwareDefaultPawn.h" +using namespace Cesium; + IMPLEMENT_SIMPLE_AUTOMATION_TEST( FCesiumLoadTestDenver, "Cesium.Performance.LoadTestDenver", @@ -31,282 +26,237 @@ IMPLEMENT_SIMPLE_AUTOMATION_TEST( "Cesium.Performance.LoadTestGoogleplex", EAutomationTestFlags::EditorContext | EAutomationTestFlags::PerfFilter) +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FCesiumLoadTestMontrealPointCloud, + "Cesium.Performance.LoadTestMontrealPointCloud", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::PerfFilter) + struct LoadTestContext { - UWorld* world; - ACesiumGeoreference* georeference; - ACesiumCameraManager* cameraManager; - AGlobeAwareDefaultPawn* pawn; - std::vector tilesets; - - void setCamera(const FCesiumCamera& camera) { - // Take over first camera, or add if it doesn't exist - const TMap cameras = cameraManager->GetCameras(); - if (cameras.IsEmpty()) { - cameraManager->AddCamera(camera); - } else { - cameraManager->UpdateCamera(0, camera); - } - } + SceneGenerationContext creationContext; + SceneGenerationContext playContext; - void refreshTilesets() { - std::vector::iterator it; - for (it = tilesets.begin(); it != tilesets.end(); ++it) - (*it)->RefreshTileset(); - } + bool testInProgress; + double startMark; + double endMark; - void setSuspendUpdate(bool suspend) { - std::vector::iterator it; - for (it = tilesets.begin(); it != tilesets.end(); ++it) - (*it)->SuspendUpdate = suspend; + void reset() { + creationContext = playContext = SceneGenerationContext(); + testInProgress = false; + startMark = endMark = 0; } }; -bool neverBreak(LoadTestContext& context) { return false; } +LoadTestContext gLoadTestContext; -bool breakWhenTilesetsLoaded(LoadTestContext& context) { - std::vector::const_iterator it; - for (it = context.tilesets.begin(); it != context.tilesets.end(); ++it) { - ACesium3DTileset* tileset = *it; +DEFINE_LATENT_AUTOMATION_COMMAND_FOUR_PARAMETER( + TimeLoadingCommand, + FString, + loggingName, + LoadTestContext&, + context, + std::function, + setupStep, + std::function, + verifyStep); +bool TimeLoadingCommand::Update() { - int progress = (int)tileset->GetLoadProgress(); - if (progress != 100) - return false; - } - return true; -} + if (!context.testInProgress) { -bool tickWorldUntil( - LoadTestContext& context, - size_t timeoutSecs, - std::function breakFunction) { - const double minStepTime = 0.050; // Don't loop faster than 20 fps + // Bind all play in editor pointers + context.playContext.initForPlay(context.creationContext); - const double testStartMark = FPlatformTime::Seconds(); - const double testEndMark = testStartMark + (double)timeoutSecs; - double lastTimeMark = testStartMark; + if (setupStep) + setupStep(context.playContext); - while (1) { - double frameTimeMark = FPlatformTime::Seconds(); + // Start test mark, turn updates back on + context.startMark = FPlatformTime::Seconds(); + UE_LOG(LogCesium, Display, TEXT("-- Load start mark -- %s"), *loggingName); - if (frameTimeMark > testEndMark) - return true; + context.playContext.setSuspendUpdate(false); + + context.testInProgress = true; + + // Return, let world tick + return false; + } - double frameElapsedTime = frameTimeMark - lastTimeMark; + double timeMark = FPlatformTime::Seconds(); + double testElapsedTime = timeMark - context.startMark; - if (frameElapsedTime < minStepTime) { - double sleepTarget = minStepTime - frameElapsedTime; - FPlatformProcess::Sleep(sleepTarget); - continue; + // The command is over if tilesets are loaded, or timed out + // Wait for a maximum of 20 seconds + const size_t testTimeout = 20; + bool tilesetsloaded = context.playContext.areTilesetsDoneLoading(); + bool timedOut = testElapsedTime >= testTimeout; + + if (tilesetsloaded || timedOut) { + context.endMark = timeMark; + UE_LOG(LogCesium, Display, TEXT("-- Load end mark -- %s"), *loggingName); + + if (timedOut) { + UE_LOG( + LogCesium, + Error, + TEXT("TIMED OUT: Loading stopped after %.2f seconds"), + testElapsedTime); + } else { + UE_LOG( + LogCesium, + Display, + TEXT("Tileset load completed in %.2f seconds"), + testElapsedTime); } - // - // Force a tick. Derived from various examples in this code base - // Search for FTSTicker::GetCoreTicker().Tick - // + if (verifyStep) + verifyStep(context.playContext); - // Increment global frame counter once for each app tick. - GFrameCounter++; + // Turn on the editor tileset updates so we can see what we loaded + gLoadTestContext.creationContext.setSuspendUpdate(false); - // Let world tick at same rate as this loop - context.world->Tick(ELevelTick::LEVELTICK_ViewportsOnly, frameElapsedTime); + context.testInProgress = false; - // Application tick - FTaskGraphInterface::Get().ProcessThreadUntilIdle( - ENamedThreads::GameThread); - FTSTicker::GetCoreTicker().Tick(frameElapsedTime); + // Command is done + return true; + } - // Let UI update - // Not absolutely necessary, but convenient when running the tests - // from inside the editor, so the UI doesn't appear frozen - FSlateApplication::Get().PumpMessages(); - FSlateApplication::Get().Tick(); + // Let world tick, we'll come back to this command + return false; +} - if (breakFunction(context)) - return false; +struct TestPass { + FString name; + std::function setupStep; + std::function verifyStep; +}; - lastTimeMark = frameTimeMark; - }; +void clearCacheDb() { + std::shared_ptr pCacheDatabase = + getCacheDatabase(); + pCacheDatabase->clearAll(); } -void setupForGoogleTiles(LoadTestContext& context) { +bool RunLoadTest( + const FString& testName, + std::function locationSetup, + const std::vector& testPasses) { - FVector targetOrigin(-122.083969, 37.424492, 142.859116); - FString targetUrl( - "https://tile.googleapis.com/v1/3dtiles/root.json?key=AIzaSyCnRPXWDIj1LuX6OWIweIqZFHHoXVgdYss"); + gLoadTestContext.reset(); - FCesiumCamera camera; - camera.ViewportSize = FVector2D(1024, 768); - camera.Location = FVector(0, 0, 0); - camera.Rotation = FRotator(-25, 95, 0); - camera.FieldOfViewDegrees = 90; - context.setCamera(camera); + // + // Programmatically set up the world + // + UE_LOG(LogCesium, Display, TEXT("Creating world objects...")); + createCommonWorldObjects(gLoadTestContext.creationContext); - context.georeference->SetOriginLongitudeLatitudeHeight(targetOrigin); + // Configure location specific objects + locationSetup(gLoadTestContext.creationContext); + gLoadTestContext.creationContext.trackForPlay(); - context.pawn->SetActorLocation(FVector(0, 0, 0)); - context.pawn->SetActorRotation(FRotator(-25, 95, 0)); + // Halt tileset updates and reset them + gLoadTestContext.creationContext.setSuspendUpdate(true); + gLoadTestContext.creationContext.refreshTilesets(); + clearCacheDb(); - ACesium3DTileset* tileset = context.world->SpawnActor(); - tileset->SetUrl(targetUrl); - tileset->SetTilesetSource(ETilesetSource::FromUrl); - tileset->SetActorLabel(TEXT("Google Photorealistic 3D Tiles")); + // + // Start async commands + // - context.tilesets.push_back(tileset); -} + // Start play in editor (don't sim in editor) + ADD_LATENT_AUTOMATION_COMMAND(FStartPIECommand(false)); -void setupForDenver(LoadTestContext& context) { - - FVector targetOrigin(-104.988892, 39.743462, 1798.679443); - FString ionToken( - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2NmZhZTk4NS01MDFmLTRjODgtOTlkYy04NjIwODhiZWExOGYiLCJpZCI6MjU5LCJpYXQiOjE2ODg1MTI4ODd9.haoe5hsJyfHk1dQAHVK6N8dW_kfmtdbyuhlGwFdEHbM"); - - FCesiumCamera camera; - camera.ViewportSize = FVector2D(1024, 768); - camera.Location = FVector(0, 0, 0); - camera.Rotation = FRotator(-5.2, -149.4, 0); - camera.FieldOfViewDegrees = 90; - context.setCamera(camera); - - context.georeference->SetOriginLongitudeLatitudeHeight(targetOrigin); - - context.pawn->SetActorLocation(FVector(0, 0, 0)); - context.pawn->SetActorRotation(FRotator(-5.2, -149.4, 0)); - - // Add Cesium World Terrain - ACesium3DTileset* worldTerrainTileset = - context.world->SpawnActor(); - worldTerrainTileset->SetTilesetSource(ETilesetSource::FromCesiumIon); - worldTerrainTileset->SetIonAssetID(1); - worldTerrainTileset->SetIonAccessToken(ionToken); - worldTerrainTileset->SetActorLabel(TEXT("Cesium World Terrain")); - - // Bing Maps Aerial overlay - UCesiumIonRasterOverlay* pOverlay = NewObject( - worldTerrainTileset, - FName("Bing Maps Aerial"), - RF_Transactional); - pOverlay->MaterialLayerKey = TEXT("Overlay0"); - pOverlay->IonAssetID = 2; - pOverlay->SetActive(true); - pOverlay->OnComponentCreated(); - worldTerrainTileset->AddInstanceComponent(pOverlay); - - // Aerometrex Denver - ACesium3DTileset* aerometrexTileset = - context.world->SpawnActor(); - aerometrexTileset->SetTilesetSource(ETilesetSource::FromCesiumIon); - aerometrexTileset->SetIonAssetID(354307); - aerometrexTileset->SetIonAccessToken(ionToken); - aerometrexTileset->SetMaximumScreenSpaceError(2.0); - aerometrexTileset->SetActorLabel(TEXT("Aerometrex Denver")); - - context.tilesets.push_back(worldTerrainTileset); - context.tilesets.push_back(aerometrexTileset); -} + std::vector::const_iterator it; + for (it = testPasses.begin(); it != testPasses.end(); ++it) { + const TestPass& pass = *it; -void createCommonWorldObjects(LoadTestContext& context) { + // Wait a bit + ADD_LATENT_AUTOMATION_COMMAND(FWaitLatentCommand(2.0f)); - context.world = FAutomationEditorCommonUtils::CreateNewMap(); + // Do our timing capture + FString loggingName = testName + ":" + pass.name; - ACesiumSunSky* sunSky = context.world->SpawnActor(); - APlayerStart* playerStart = context.world->SpawnActor(); + ADD_LATENT_AUTOMATION_COMMAND(TimeLoadingCommand( + loggingName, + gLoadTestContext, + pass.setupStep, + pass.verifyStep)); + } - context.cameraManager = - ACesiumCameraManager::GetDefaultCameraManager(context.world); + // Wait a bit + ADD_LATENT_AUTOMATION_COMMAND(FWaitLatentCommand(2.0f)); - FSoftObjectPath objectPath( - TEXT("Class'/CesiumForUnreal/DynamicPawn.DynamicPawn_C'")); - TSoftObjectPtr DynamicPawn = TSoftObjectPtr(objectPath); + // End play in editor + ADD_LATENT_AUTOMATION_COMMAND(FEndPlayMapCommand()); - context.georeference = - ACesiumGeoreference::GetDefaultGeoreference(context.world); - context.pawn = context.world->SpawnActor( - Cast(DynamicPawn.LoadSynchronous())); + return true; +} - context.pawn->AutoPossessPlayer = EAutoReceiveInput::Player0; +void refreshTilesets(SceneGenerationContext& context) { + gLoadTestContext.playContext.refreshTilesets(); } -bool RunLoadTest(const size_t locationIndex) { +bool FCesiumLoadTestDenver::RunTest(const FString& Parameters) { - // - // Programmatically set up the world - // - UE_LOG(LogCesium, Display, TEXT("Creating world objects...")); - LoadTestContext context; - createCommonWorldObjects(context); + std::vector testPasses; + testPasses.push_back(TestPass{"Cold Cache", nullptr, nullptr}); + testPasses.push_back(TestPass{"Warm Cache", refreshTilesets, nullptr}); - // Configure location specific objects - switch (locationIndex) { - case 0: - setupForGoogleTiles(context); - break; - case 1: - setupForDenver(context); - break; - default: - break; - } + return RunLoadTest(GetTestName(), setupForDenver, testPasses); +} - // Halt tileset updates and reset them - context.setSuspendUpdate(true); - context.refreshTilesets(); +bool FCesiumLoadTestGoogleplex::RunTest(const FString& Parameters) { - // Let world settle for 1 second - UE_LOG(LogCesium, Display, TEXT("Letting world settle for 1 second...")); - tickWorldUntil(context, 1, neverBreak); + std::vector testPasses; + testPasses.push_back(TestPass{"Cold Cache", nullptr, nullptr}); + testPasses.push_back(TestPass{"Warm Cache", refreshTilesets, nullptr}); - // Start test mark, turn updates back on - double loadStartMark = FPlatformTime::Seconds(); - UE_LOG(LogCesium, Display, TEXT("-- Load start mark --")); - context.setSuspendUpdate(false); + return RunLoadTest(GetTestName(), setupForGoogleTiles, testPasses); +} - // Spin for a maximum of 20 seconds, or until tilesets finish loading - const size_t testTimeout = 20; - UE_LOG( - LogCesium, - Display, - TEXT("Tick world until tilesets load, or %d seconds elapse..."), - testTimeout); - bool timedOut = tickWorldUntil(context, testTimeout, breakWhenTilesetsLoaded); +bool FCesiumLoadTestMontrealPointCloud::RunTest(const FString& Parameters) { - double loadEndMark = FPlatformTime::Seconds(); - UE_LOG(LogCesium, Display, TEXT("-- Load end mark --")); + auto adjustCamera = [this](SceneGenerationContext& context) { + // Zoom way out + FCesiumCamera zoomedOut; + zoomedOut.ViewportSize = FVector2D(1024, 768); + zoomedOut.Location = FVector(0, 0, 7240000.0); + zoomedOut.Rotation = FRotator(-90.0, 0.0, 0.0); + zoomedOut.FieldOfViewDegrees = 90; + context.setCamera(zoomedOut); - // - // Skip object cleanup. Let all objects be available for viewing after test - // - // Let world settle for 1 second - UE_LOG(LogCesium, Display, TEXT("Letting world settle for 1 second...")); - tickWorldUntil(context, 1, neverBreak); - - // Freeze updates - context.setSuspendUpdate(true); - - double loadElapsedTime = loadEndMark - loadStartMark; - - if (timedOut) { - UE_LOG( - LogCesium, - Error, - TEXT("TIMED OUT: Loading stopped after %.2f seconds"), - loadElapsedTime); - } else { - UE_LOG( - LogCesium, - Display, - TEXT("Tileset load completed in %.2f seconds"), - loadElapsedTime); - } + context.pawn->SetActorLocation(zoomedOut.Location); + }; - return !timedOut; -} + auto verifyVisibleTiles = [this](SceneGenerationContext& context) { + Cesium3DTilesSelection::Tileset* pTileset = + context.tilesets[0]->GetTileset(); + if (TestNotNull("Tileset", pTileset)) { + int visibleTiles = 0; + pTileset->forEachLoadedTile([&](Cesium3DTilesSelection::Tile& tile) { + if (tile.getState() != Cesium3DTilesSelection::TileLoadState::Done) + return; + const Cesium3DTilesSelection::TileContent& content = tile.getContent(); + const Cesium3DTilesSelection::TileRenderContent* pRenderContent = + content.getRenderContent(); + if (!pRenderContent) { + return; + } + + UCesiumGltfComponent* Gltf = static_cast( + pRenderContent->getRenderResources()); + if (Gltf && Gltf->IsVisible()) { + ++visibleTiles; + } + }); + + TestEqual("visibleTiles", visibleTiles, 1); + } + }; -bool FCesiumLoadTestDenver::RunTest(const FString& Parameters) { - return RunLoadTest(1); -} + std::vector testPasses; + testPasses.push_back(TestPass{"Cold Cache", nullptr, nullptr}); + testPasses.push_back(TestPass{"Adjust", adjustCamera, verifyVisibleTiles}); -bool FCesiumLoadTestGoogleplex::RunTest(const FString& Parameters) { - return RunLoadTest(0); + return RunLoadTest(GetTestName(), setupForMontrealPointCloud, testPasses); } #endif diff --git a/Source/CesiumRuntime/Private/Tests/CesiumSceneGeneration.cpp b/Source/CesiumRuntime/Private/Tests/CesiumSceneGeneration.cpp new file mode 100644 index 000000000..e18bf30b8 --- /dev/null +++ b/Source/CesiumRuntime/Private/Tests/CesiumSceneGeneration.cpp @@ -0,0 +1,223 @@ +// Copyright 2020-2023 CesiumGS, Inc. and Contributors + +#include "CesiumSceneGeneration.h" + +#if WITH_EDITOR + +#include "Tests/AutomationEditorCommon.h" + +#include "GameFramework/PlayerStart.h" + +#include "Cesium3DTileset.h" +#include "CesiumCameraManager.h" +#include "CesiumGeoreference.h" +#include "CesiumIonRasterOverlay.h" +#include "CesiumSunSky.h" +#include "GlobeAwareDefaultPawn.h" + +#include "CesiumTestHelpers.h" + +namespace Cesium { + +FString SceneGenerationContext::testIonToken( + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3NjU3OGE4Zi0xOGM4LTQ4NjYtODc4ZS02YWNkMDZmY2Y1M2YiLCJpZCI6MjU5LCJpYXQiOjE2OTA4Nzg3MjB9.uxePYJL59S4pG5aqJHb9goikVSO-Px6xA7kZH8oM1eM"); + +void SceneGenerationContext::setCamera(const FCesiumCamera& camera) { + // Take over first camera, or add if it doesn't exist + const TMap cameras = cameraManager->GetCameras(); + if (cameras.IsEmpty()) { + cameraManager->AddCamera(camera); + } else { + cameraManager->UpdateCamera(0, camera); + } +} + +void SceneGenerationContext::refreshTilesets() { + std::vector::iterator it; + for (it = tilesets.begin(); it != tilesets.end(); ++it) + (*it)->RefreshTileset(); +} + +void SceneGenerationContext::setSuspendUpdate(bool suspend) { + std::vector::iterator it; + for (it = tilesets.begin(); it != tilesets.end(); ++it) + (*it)->SuspendUpdate = suspend; +} + +bool SceneGenerationContext::areTilesetsDoneLoading() { + if (tilesets.empty()) + return false; + + std::vector::const_iterator it; + for (it = tilesets.begin(); it != tilesets.end(); ++it) { + ACesium3DTileset* tileset = *it; + + int progress = (int)tileset->GetLoadProgress(); + if (progress != 100) { + // We aren't done + return false; + } + } + return true; +} + +void SceneGenerationContext::trackForPlay() { + CesiumTestHelpers::trackForPlay(georeference); + CesiumTestHelpers::trackForPlay(cameraManager); + CesiumTestHelpers::trackForPlay(pawn); + + std::vector::iterator it; + for (it = tilesets.begin(); it != tilesets.end(); ++it) { + ACesium3DTileset* tileset = *it; + CesiumTestHelpers::trackForPlay(tileset); + } +} + +void SceneGenerationContext::initForPlay( + SceneGenerationContext& creationContext) { + world = GEditor->PlayWorld; + georeference = CesiumTestHelpers::findInPlay(creationContext.georeference); + cameraManager = CesiumTestHelpers::findInPlay(creationContext.cameraManager); + pawn = CesiumTestHelpers::findInPlay(creationContext.pawn); + + tilesets.clear(); + + std::vector& creationTilesets = creationContext.tilesets; + std::vector::iterator it; + for (it = creationTilesets.begin(); it != creationTilesets.end(); ++it) { + ACesium3DTileset* creationTileset = *it; + ACesium3DTileset* tileset = CesiumTestHelpers::findInPlay(creationTileset); + tilesets.push_back(tileset); + } +} + +void createCommonWorldObjects(SceneGenerationContext& context) { + + context.world = FAutomationEditorCommonUtils::CreateNewMap(); + + ACesiumSunSky* sunSky = context.world->SpawnActor(); + APlayerStart* playerStart = context.world->SpawnActor(); + + context.cameraManager = + ACesiumCameraManager::GetDefaultCameraManager(context.world); + + FSoftObjectPath objectPath( + TEXT("Class'/CesiumForUnreal/DynamicPawn.DynamicPawn_C'")); + TSoftObjectPtr DynamicPawn = TSoftObjectPtr(objectPath); + + context.georeference = + ACesiumGeoreference::GetDefaultGeoreference(context.world); + context.pawn = context.world->SpawnActor( + Cast(DynamicPawn.LoadSynchronous())); + + context.pawn->AutoPossessPlayer = EAutoReceiveInput::Player0; + + AWorldSettings* pWorldSettings = context.world->GetWorldSettings(); + if (pWorldSettings) + pWorldSettings->bEnableWorldBoundsChecks = false; +} + +void setupForGoogleTiles(SceneGenerationContext& context) { + + FVector targetOrigin(-122.083969, 37.424492, 142.859116); + FString targetUrl( + "https://tile.googleapis.com/v1/3dtiles/root.json?key=AIzaSyCnRPXWDIj1LuX6OWIweIqZFHHoXVgdYss"); + + FCesiumCamera camera; + camera.ViewportSize = FVector2D(1024, 768); + camera.Location = FVector(0, 0, 0); + camera.Rotation = FRotator(-25, 95, 0); + camera.FieldOfViewDegrees = 90; + context.setCamera(camera); + + context.georeference->SetOriginLongitudeLatitudeHeight(targetOrigin); + + context.pawn->SetActorLocation(FVector(0, 0, 0)); + context.pawn->SetActorRotation(FRotator(-25, 95, 0)); + + ACesium3DTileset* tileset = context.world->SpawnActor(); + tileset->SetUrl(targetUrl); + tileset->SetTilesetSource(ETilesetSource::FromUrl); + tileset->SetActorLabel(TEXT("Google Photorealistic 3D Tiles")); + + context.tilesets.push_back(tileset); +} + +void setupForDenver(SceneGenerationContext& context) { + + FVector targetOrigin(-104.988892, 39.743462, 1798.679443); + + FCesiumCamera camera; + camera.ViewportSize = FVector2D(1024, 768); + camera.Location = FVector(0, 0, 0); + camera.Rotation = FRotator(-5.2, -149.4, 0); + camera.FieldOfViewDegrees = 90; + context.setCamera(camera); + + context.georeference->SetOriginLongitudeLatitudeHeight(targetOrigin); + + context.pawn->SetActorLocation(FVector(0, 0, 0)); + context.pawn->SetActorRotation(FRotator(-5.2, -149.4, 0)); + + // Add Cesium World Terrain + ACesium3DTileset* worldTerrainTileset = + context.world->SpawnActor(); + worldTerrainTileset->SetTilesetSource(ETilesetSource::FromCesiumIon); + worldTerrainTileset->SetIonAssetID(1); + worldTerrainTileset->SetIonAccessToken(SceneGenerationContext::testIonToken); + worldTerrainTileset->SetActorLabel(TEXT("Cesium World Terrain")); + + // Bing Maps Aerial overlay + UCesiumIonRasterOverlay* pOverlay = NewObject( + worldTerrainTileset, + FName("Bing Maps Aerial"), + RF_Transactional); + pOverlay->MaterialLayerKey = TEXT("Overlay0"); + pOverlay->IonAssetID = 2; + pOverlay->SetActive(true); + pOverlay->OnComponentCreated(); + worldTerrainTileset->AddInstanceComponent(pOverlay); + + // Aerometrex Denver + ACesium3DTileset* aerometrexTileset = + context.world->SpawnActor(); + aerometrexTileset->SetTilesetSource(ETilesetSource::FromCesiumIon); + aerometrexTileset->SetIonAssetID(354307); + aerometrexTileset->SetIonAccessToken(SceneGenerationContext::testIonToken); + aerometrexTileset->SetMaximumScreenSpaceError(2.0); + aerometrexTileset->SetActorLabel(TEXT("Aerometrex Denver")); + + context.tilesets.push_back(worldTerrainTileset); + context.tilesets.push_back(aerometrexTileset); +} + +void setupForMontrealPointCloud(SceneGenerationContext& context) { + FVector targetOrigin(-73.616526, 45.57335, 95.048859); + + FCesiumCamera camera; + camera.ViewportSize = FVector2D(1024, 768); + camera.Location = FVector(0, 0, 0); + camera.Rotation = FRotator(-90.0, 0.0, 0.0); + camera.FieldOfViewDegrees = 90; + context.setCamera(camera); + + context.georeference->SetOriginLongitudeLatitudeHeight(targetOrigin); + + context.pawn->SetActorLocation(FVector(0, 0, 0)); + context.pawn->SetActorRotation(FRotator(-90.0, 0.0, 0.0)); + + // Montreal Point Cloud + ACesium3DTileset* montrealTileset = + context.world->SpawnActor(); + montrealTileset->SetTilesetSource(ETilesetSource::FromCesiumIon); + montrealTileset->SetIonAssetID(28945); + montrealTileset->SetIonAccessToken(SceneGenerationContext::testIonToken); + montrealTileset->SetMaximumScreenSpaceError(16.0); + montrealTileset->SetActorLabel(TEXT("Montreal Point Cloud")); + + context.tilesets.push_back(montrealTileset); +} + +} // namespace Cesium + +#endif // #if WITH_EDITOR diff --git a/Source/CesiumRuntime/Private/Tests/CesiumSceneGeneration.h b/Source/CesiumRuntime/Private/Tests/CesiumSceneGeneration.h new file mode 100644 index 000000000..85ff0cd1b --- /dev/null +++ b/Source/CesiumRuntime/Private/Tests/CesiumSceneGeneration.h @@ -0,0 +1,43 @@ +// Copyright 2020-2023 CesiumGS, Inc. and Contributors + +#pragma once + +#if WITH_EDITOR + +#include "CesiumCameraManager.h" +#include "Editor.h" + +class UWorld; +class ACesiumGeoreference; +class AGlobeAwareDefaultPawn; +class ACesium3DTileset; + +namespace Cesium { + +struct SceneGenerationContext { + UWorld* world; + ACesiumGeoreference* georeference; + ACesiumCameraManager* cameraManager; + AGlobeAwareDefaultPawn* pawn; + std::vector tilesets; + + void setCamera(const FCesiumCamera& camera); + void refreshTilesets(); + void setSuspendUpdate(bool suspend); + bool areTilesetsDoneLoading(); + + void trackForPlay(); + void initForPlay(SceneGenerationContext& creationContext); + + static FString testIonToken; +}; + +void createCommonWorldObjects(SceneGenerationContext& context); + +void setupForDenver(SceneGenerationContext& context); +void setupForGoogleTiles(SceneGenerationContext& context); +void setupForMontrealPointCloud(SceneGenerationContext& context); + +}; // namespace Cesium + +#endif // #if WITH_EDITOR diff --git a/Source/CesiumRuntime/Private/Tests/CesiumTestHelpers.h b/Source/CesiumRuntime/Private/Tests/CesiumTestHelpers.h index 44d66ae75..7abf17a08 100644 --- a/Source/CesiumRuntime/Private/Tests/CesiumTestHelpers.h +++ b/Source/CesiumRuntime/Private/Tests/CesiumTestHelpers.h @@ -172,6 +172,8 @@ template T* findInPlay(T* pEditorObject) { return nullptr; return getComponentWithTag(pPlayOwner, getUniqueTag(pEditorObject)); } + + return nullptr; } /// diff --git a/Source/CesiumRuntime/Public/CesiumRuntime.h b/Source/CesiumRuntime/Public/CesiumRuntime.h index f383d15c7..5c4252b48 100644 --- a/Source/CesiumRuntime/Public/CesiumRuntime.h +++ b/Source/CesiumRuntime/Public/CesiumRuntime.h @@ -12,6 +12,7 @@ class UCesiumRasterOverlay; namespace CesiumAsync { class AsyncSystem; class IAssetAccessor; +class ICacheDatabase; } // namespace CesiumAsync DECLARE_LOG_CATEGORY_EXTERN(LogCesium, Log, All); @@ -48,3 +49,6 @@ CESIUMRUNTIME_API extern FCesiumRasterOverlayIonTroubleshooting CESIUMRUNTIME_API CesiumAsync::AsyncSystem& getAsyncSystem() noexcept; CESIUMRUNTIME_API const std::shared_ptr& getAssetAccessor(); + +CESIUMRUNTIME_API std::shared_ptr& +getCacheDatabase();