diff --git a/Source/CesiumRuntime/Private/Cesium3DTileset.cpp b/Source/CesiumRuntime/Private/Cesium3DTileset.cpp index 408236dc0..49335cb72 100644 --- a/Source/CesiumRuntime/Private/Cesium3DTileset.cpp +++ b/Source/CesiumRuntime/Private/Cesium3DTileset.cpp @@ -799,7 +799,7 @@ void ACesium3DTileset::OnConstruction(const FTransform& Transform) { for (UCesiumGltfComponent* pGltf : gltfComponents) { if (pGltf && IsValid(pGltf) && pGltf->IsVisible()) { - pGltf->SetVisibility(false, true); + // pGltf->SetVisibility(false, true); pGltf->SetCollisionEnabled(ECollisionEnabled::NoCollision); } } @@ -1259,6 +1259,7 @@ void ACesium3DTileset::DestroyTileset() { ++this->_tilesetsBeingDestroyed; this->_pTileset->getAsyncDestructionCompleteEvent().thenInMainThread( [this]() { --this->_tilesetsBeingDestroyed; }); + this->_viewGroups.clear(); this->_pTileset.Reset(); switch (this->TilesetSource) { @@ -1654,7 +1655,8 @@ bool ACesium3DTileset::ShouldTickIfViewportsOnly() const { namespace { template void forEachRenderableTile(const auto& tiles, Func&& f) { - for (Cesium3DTilesSelection::Tile* pTile : tiles) { + for (const CesiumUtility::IntrusivePointer& + pTile : tiles) { if (!pTile || pTile->getState() != Cesium3DTilesSelection::TileLoadState::Done) { continue; @@ -1682,13 +1684,17 @@ void forEachRenderableTile(const auto& tiles, Func&& f) { } void removeVisibleTilesFromList( - std::vector& list, - const std::vector& visibleTiles) { + std::vector>& + list, + const std::vector< + CesiumUtility::IntrusivePointer>& + visibleTiles) { if (list.empty()) { return; } - for (Cesium3DTilesSelection::Tile* pTile : visibleTiles) { + for (const CesiumUtility::IntrusivePointer& + pTile : visibleTiles) { auto it = std::find(list.begin(), list.end(), pTile); if (it != list.end()) { list.erase(it); @@ -1705,14 +1711,20 @@ void removeVisibleTilesFromList( * * @param tiles The tiles to hide */ -void hideTiles(const std::vector& tiles) { +void hideTiles( + uint32 viewStateKey, + const std::vector< + CesiumUtility::IntrusivePointer>& tiles) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::HideTiles) forEachRenderableTile( tiles, - [](Cesium3DTilesSelection::Tile* /*pTile*/, UCesiumGltfComponent* pGltf) { + [viewStateKey]( + const CesiumUtility::IntrusivePointer< + Cesium3DTilesSelection::Tile>& /*pTile*/, + UCesiumGltfComponent* pGltf) { if (pGltf->IsVisible()) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::SetVisibilityFalse) - pGltf->SetVisibility(false, true); + // pGltf->SetVisibility(false, true); } else { // TODO: why is this happening? UE_LOG( @@ -1720,6 +1732,8 @@ void hideTiles(const std::vector& tiles) { Verbose, TEXT("Tile to no longer render does not have a visible Gltf")); } + + pGltf->SetViewGroupVisibility(viewStateKey, false); }); } @@ -1728,11 +1742,14 @@ void hideTiles(const std::vector& tiles) { * list. This includes tiles that are fading out. */ void removeCollisionForTiles( - const std::unordered_set& tiles) { + const std::unordered_set< + CesiumUtility::IntrusivePointer>& tiles) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::RemoveCollisionForTiles) forEachRenderableTile( tiles, - [](Cesium3DTilesSelection::Tile* /*pTile*/, UCesiumGltfComponent* pGltf) { + [](const CesiumUtility::IntrusivePointer< + Cesium3DTilesSelection::Tile>& /*pTile*/, + UCesiumGltfComponent* pGltf) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::SetCollisionDisabled) pGltf->SetCollisionEnabled(ECollisionEnabled::NoCollision); }); @@ -1814,10 +1831,11 @@ void ACesium3DTileset::updateLastViewUpdateResultState( ResolveGeoreference(); check(Georeference); - for (Cesium3DTilesSelection::Tile* tile : result.tilesToRenderThisFrame) { + for (const CesiumUtility::IntrusivePointer& + pTile : result.tilesToRenderThisFrame) { CesiumGeometry::OrientedBoundingBox obb = Cesium3DTilesSelection::getOrientedBoundingBoxFromBoundingVolume( - tile->getBoundingVolume(), + pTile->getBoundingVolume(), Georeference->GetEllipsoid()->GetNativeEllipsoid()); FVector unrealCenter = @@ -1828,9 +1846,9 @@ void ACesium3DTileset::updateLastViewUpdateResultState( TEXT("ID %s (%p)"), UTF8_TO_TCHAR( Cesium3DTilesSelection::TileIdUtilities::createTileIdString( - tile->getTileID()) + pTile->getTileID()) .c_str()), - tile); + pTile.get()); DrawDebugString(World, unrealCenter, text, nullptr, FColor::Red, 0, true); } @@ -1914,13 +1932,17 @@ void ACesium3DTileset::updateLastViewUpdateResultState( } void ACesium3DTileset::showTilesToRender( - const std::vector& tiles) { + uint32 viewStateKey, + const std::vector< + CesiumUtility::IntrusivePointer>& tiles) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::ShowTilesToRender) forEachRenderableTile( tiles, - [&RootComponent = this->RootComponent, + [viewStateKey, + &RootComponent = this->RootComponent, &BodyInstance = this->BodyInstance]( - Cesium3DTilesSelection::Tile* pTile, + const CesiumUtility::IntrusivePointer& + pTile, UCesiumGltfComponent* pGltf) { applyActorCollisionSettings(BodyInstance, pGltf); @@ -1945,9 +1967,11 @@ void ACesium3DTileset::showTilesToRender( if (!pGltf->IsVisible()) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::SetVisibilityTrue) - pGltf->SetVisibility(true, true); + // pGltf->SetVisibility(true, true); } + pGltf->SetViewGroupVisibility(viewStateKey, true); + { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::SetCollisionEnabled) pGltf->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); @@ -1959,7 +1983,8 @@ static void updateTileFades(const auto& tiles, bool fadingIn) { forEachRenderableTile( tiles, [fadingIn]( - Cesium3DTilesSelection::Tile* pTile, + const CesiumUtility::IntrusivePointer& + pTile, UCesiumGltfComponent* pGltf) { float percentage = pTile->getContent() .getRenderContent() @@ -2036,12 +2061,12 @@ void ACesium3DTileset::Tick(float DeltaTime) { UCesiumEllipsoid* ellipsoid = this->ResolveGeoreference()->GetEllipsoid(); std::vector frustums; - for (const FCesiumCamera& camera : cameras) { - frustums.push_back(CreateViewStateFromViewParameters( - camera, - unrealWorldToCesiumTileset, - ellipsoid)); - } + // for (const FCesiumCamera& camera : cameras) { + // frustums.push_back(CreateViewStateFromViewParameters( + // camera, + // unrealWorldToCesiumTileset, + // ellipsoid)); + // } const Cesium3DTilesSelection::ViewUpdateResult* pResult; if (this->_captureMovieMode) { @@ -2049,19 +2074,23 @@ void ACesium3DTileset::Tick(float DeltaTime) { pResult = &this->_pTileset->updateViewOffline(frustums); } else { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::updateView) - pResult = &this->_pTileset->updateView(frustums, DeltaTime); + pResult = &this->_pTileset->updateViewGroup( + this->_pTileset->getDefaultViewGroup(), + frustums, + DeltaTime); } updateLastViewUpdateResultState(*pResult); removeCollisionForTiles(pResult->tilesFadingOut); removeVisibleTilesFromList( - _tilesToHideNextFrame, + this->_tilesToHideNextFrame, pResult->tilesToRenderThisFrame); - hideTiles(_tilesToHideNextFrame); + hideTiles(0, this->_tilesToHideNextFrame); _tilesToHideNextFrame.clear(); - for (Cesium3DTilesSelection::Tile* pTile : pResult->tilesFadingOut) { + for (const CesiumUtility::IntrusivePointer& + pTile : pResult->tilesFadingOut) { Cesium3DTilesSelection::TileRenderContent* pRenderContent = pTile->getContent().getRenderContent(); if (!this->UseLodTransitions || @@ -2071,7 +2100,7 @@ void ACesium3DTileset::Tick(float DeltaTime) { } } - showTilesToRender(pResult->tilesToRenderThisFrame); + showTilesToRender(0, pResult->tilesToRenderThisFrame); if (this->UseLodTransitions) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::UpdateTileFades) @@ -2080,6 +2109,147 @@ void ACesium3DTileset::Tick(float DeltaTime) { } this->UpdateLoadStatus(); + + ACesiumCameraManager* pCameraManager = + ACesiumCameraManager::GetDefaultCameraManager(this); + if (pCameraManager) { + this->_viewGroups.resize(pCameraManager->ViewGroups.Num()); + for (int32 i = 0; i < pCameraManager->ViewGroups.Num(); ++i) { + const FCesiumViewGroup& group = pCameraManager->ViewGroups[i]; + +#if WITH_EDITOR + if (group.EditorViewportIndex >= 0) { + TArray viewportClients = + GEditor->GetAllViewportClients(); + FEditorViewportClient* pEditorViewportClient = + viewportClients[group.EditorViewportIndex]; + + if (!pEditorViewportClient->IsVisible() || + !pEditorViewportClient->IsRealtime()) { + continue; + } + + FRotator rotation; + + switch (pEditorViewportClient->GetViewportType()) { + case LVT_OrthoXY: + rotation = FRotator(-90.0f, -90.0f, 0.0f); + break; + case LVT_OrthoNegativeXY: + rotation = FRotator(90.0f, 90.0f, 0.0f); + break; + case LVT_OrthoXZ: + rotation = FRotator(0.0f, -90.0f, 0.0f); + break; + case LVT_OrthoNegativeXZ: + rotation = FRotator(0.0f, 90.0f, 0.0f); + break; + case LVT_OrthoYZ: + rotation = FRotator(0.0f, 0.0f, 0.0f); + break; + case LVT_OrthoNegativeYZ: + rotation = FRotator(0.0f, 180.0f, 0.0f); + break; + case LVT_OrthoFreelook: + case LVT_Perspective: + case LVT_MAX: + case LVT_None: + 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(size.X) / dpiScalingFactor; + size.Y = static_cast(size.Y) / dpiScalingFactor; + } + + std::vector frustums; + + if (pEditorViewportClient->IsPerspective()) { + if (pEditorViewportClient->IsAspectRatioConstrained()) { + frustums.emplace_back(CreateViewStateFromViewParameters( + FCesiumCamera( + FVector2D(size), + location, + rotation, + fov, + pEditorViewportClient->AspectRatio), + unrealWorldToCesiumTileset, + ellipsoid)); + } else { + frustums.emplace_back(CreateViewStateFromViewParameters( + FCesiumCamera(FVector2D(size), location, rotation, fov), + unrealWorldToCesiumTileset, + ellipsoid)); + } + } else { + Cesium3DTilesSelection::ViewState fakePerspective = + CreateViewStateFromViewParameters( + FCesiumCamera(FVector2D(size), location, rotation, fov), + unrealWorldToCesiumTileset, + ellipsoid); + + double orthographicUnitsPerPixel = + pEditorViewportClient->GetOrthoUnitsPerPixel( + pEditorViewportClient->Viewport); + double orthographicWidth = orthographicUnitsPerPixel * (double)size.X; + + double right = orthographicWidth / 100.0; // centimeters to meters + double left = -right; + double top = pEditorViewportClient->IsAspectRatioConstrained() + ? right / pEditorViewportClient->AspectRatio + : right / (double(size.X) / double(size.Y)); + double bottom = -top; + + Cesium3DTilesSelection::ViewState orthographic( + fakePerspective.getPosition(), + fakePerspective.getDirection(), + fakePerspective.getUp(), + fakePerspective.getViewportSize(), + left, + right, + bottom, + top, + ellipsoid->GetNativeEllipsoid()); + frustums.emplace_back(orthographic); + } + + this->_viewGroups[i].setWeight(group.LoadWeight); + + const Cesium3DTilesSelection::ViewUpdateResult& result = + this->_pTileset->updateViewGroup( + this->_viewGroups[i], + frustums, + DeltaTime); + + showTilesToRender(group.ViewStateKey, result.tilesToRenderThisFrame); + hideTiles( + group.ViewStateKey, + std::vector( + result.tilesFadingOut.begin(), + result.tilesFadingOut.end())); + } +#endif + } + + this->_pTileset->loadTiles(); + } } void ACesium3DTileset::EndPlay(const EEndPlayReason::Type EndPlayReason) { diff --git a/Source/CesiumRuntime/Private/CesiumCameraManager.cpp b/Source/CesiumRuntime/Private/CesiumCameraManager.cpp index 707885a5a..505907093 100644 --- a/Source/CesiumRuntime/Private/CesiumCameraManager.cpp +++ b/Source/CesiumRuntime/Private/CesiumCameraManager.cpp @@ -81,11 +81,38 @@ ACesiumCameraManager::ACesiumCameraManager() : AActor() { #if WITH_EDITOR this->SetIsSpatiallyLoaded(false); #endif + PrimaryActorTick.bCanEverTick = true; } bool ACesiumCameraManager::ShouldTickIfViewportsOnly() const { return true; } -void ACesiumCameraManager::Tick(float DeltaTime) { Super::Tick(DeltaTime); } +void ACesiumCameraManager::Tick(float DeltaTime) { + Super::Tick(DeltaTime); + + const TArray& viewportClients = + GEditor->GetAllViewportClients(); + + this->ViewGroups.SetNum(viewportClients.Num()); + + for (int32 i = 0; i < viewportClients.Num(); ++i) { + FEditorViewportClient* pClient = viewportClients[i]; + FCesiumViewGroup& group = this->ViewGroups[i]; + + group.ViewDescription = FString("Editor Viewport #"); + group.ViewDescription.AppendInt(i); + group.EditorViewportIndex = i; + group.ViewActor = nullptr; + + if (pClient->ViewState.GetReference() != nullptr) { + group.ViewStateKey = pClient->ViewState.GetReference()->GetViewKey(); + } else { + group.ViewStateKey = -1; + } + + if (group.LoadWeight <= 0.0) + group.LoadWeight = 1.0; + } +} int32 ACesiumCameraManager::AddCamera(UPARAM(ref) const FCesiumCamera& camera) { int32 cameraId = this->_currentCameraId++; diff --git a/Source/CesiumRuntime/Private/CesiumCreditSystem.cpp b/Source/CesiumRuntime/Private/CesiumCreditSystem.cpp index 9ee39cedf..3f9dcc043 100644 --- a/Source/CesiumRuntime/Private/CesiumCreditSystem.cpp +++ b/Source/CesiumRuntime/Private/CesiumCreditSystem.cpp @@ -333,13 +333,14 @@ void ACesiumCreditSystem::Tick(float DeltaTime) { return; } + const CesiumUtility::CreditsSnapshot& credits = _pCreditSystem->getSnapshot(); + const std::vector& creditsToShowThisFrame = - _pCreditSystem->getCreditsToShowThisFrame(); + credits.currentCredits; // if the credit list has changed, we want to reformat the credits - CreditsUpdated = - creditsToShowThisFrame.size() != _lastCreditsCount || - _pCreditSystem->getCreditsToNoLongerShowThisFrame().size() > 0; + CreditsUpdated = creditsToShowThisFrame.size() != _lastCreditsCount || + credits.removedCredits.size() > 0; if (CreditsUpdated) { FString OnScreenCredits; @@ -385,7 +386,6 @@ void ACesiumCreditSystem::Tick(float DeltaTime) { CreditsWidget->SetCredits(Credits, OnScreenCredits); } - _pCreditSystem->startNextFrame(); } namespace { diff --git a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp index e775d81e8..83329f786 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp @@ -2314,7 +2314,7 @@ loadModelAnyThreadPart( return CesiumGltfTextures::createInWorkerThread(asyncSystem, *options.pModel) .thenInWorkerThread( [transform, ellipsoid, options = std::move(options)]() mutable - -> UCesiumGltfComponent::CreateOffGameThreadResult { + -> UCesiumGltfComponent::CreateOffGameThreadResult { auto pHalf = MakeUnique(); loadModelMetadata(pHalf->loadModelResult, options); @@ -3386,7 +3386,7 @@ UCesiumGltfComponent::CreateOffGameThread( } } - Gltf->SetVisibility(false, true); + // Gltf->SetVisibility(false, true); Gltf->SetCollisionEnabled(ECollisionEnabled::NoCollision); return Gltf; } @@ -3643,6 +3643,48 @@ void UCesiumGltfComponent::UpdateFade(float fadePercentage, bool fadingIn) { } } +void UCesiumGltfComponent::SetViewGroupVisibility( + uint32 viewStateKey, + bool visibility) { + bool* pVisibility = this->_viewGroupVisibility.Find(viewStateKey); + if (!pVisibility || *pVisibility != visibility) { + this->_viewGroupVisibility.FindOrAdd(viewStateKey) = visibility; + + // bool anyAreVisible = false; + + // for (const auto& kvp : this->_viewGroupVisibility) { + // anyAreVisible |= kvp.Value; + // } + + // this->_viewGroupVisibility.FindOrAdd(nullptr) = anyAreVisible; + + for (const TObjectPtr& pComponent : + this->GetAttachChildren()) { + UCesiumGltfPrimitiveComponent* pPrimitive = + Cast(pComponent); + if (!pPrimitive) + continue; + + if (!pPrimitive->SceneProxy) + continue; + + ENQUEUE_RENDER_COMMAND(Cesium_UpdateViewGroupVisibility) + ([viewGroupVisibility = this->_viewGroupVisibility, + pProxy = pPrimitive->SceneProxy]( + FRHICommandListImmediate& RHICmdList) mutable { + UCesiumGltfPrimitiveComponent::updateVisibilityInRenderThread( + pProxy, + std::move(viewGroupVisibility)); + }); + } + } +} + +bool UCesiumGltfComponent::GetViewGroupVisibility(uint32 viewStateKey) { + bool* pResult = this->_viewGroupVisibility.Find(viewStateKey); + return pResult && *pResult; +} + template #if ENGINE_VERSION_5_4_OR_HIGHER static Chaos::FTriangleMeshImplicitObjectPtr diff --git a/Source/CesiumRuntime/Private/CesiumGltfComponent.h b/Source/CesiumRuntime/Private/CesiumGltfComponent.h index e78b2d273..feb92f9d4 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfComponent.h +++ b/Source/CesiumRuntime/Private/CesiumGltfComponent.h @@ -134,7 +134,12 @@ class UCesiumGltfComponent : public USceneComponent { void UpdateFade(float fadePercentage, bool fadingIn); + void SetViewGroupVisibility(uint32 viewStateKey, bool visibility); + bool GetViewGroupVisibility(uint32 viewStateKey); + private: UPROPERTY() UTexture2D* Transparent1x1 = nullptr; + + TMap _viewGroupVisibility{{0, false}}; }; diff --git a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp index 68951e5f9..d84afdbe4 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp @@ -2,11 +2,14 @@ #include "CesiumGltfPrimitiveComponent.h" #include "CalcBounds.h" +#include "CesiumCameraManager.h" +#include "CesiumGltfComponent.h" #include "CesiumLifetime.h" #include "CesiumMaterialUserData.h" #include "Engine/Texture.h" #include "Materials/MaterialInstanceDynamic.h" #include "PhysicsEngine/BodySetup.h" +#include "StaticMeshSceneProxy.h" #include "VecMath.h" #include @@ -90,6 +93,93 @@ FBoxSphereBounds UCesiumGltfPrimitiveComponent::CalcBounds( return Super::CalcBounds(LocalToWorld); } +namespace { + +class FCesiumGltfPrimitiveSceneProxy : public FStaticMeshSceneProxy { +public: + FCesiumGltfPrimitiveSceneProxy( + UStaticMeshComponent* Component, + bool bForceLODsShareStaticLighting, + TMap&& viewGroupVisibility) + : FStaticMeshSceneProxy(Component, bForceLODsShareStaticLighting), + _viewsVisibility(std::move(viewGroupVisibility)) {} + + FPrimitiveViewRelevance + GetViewRelevance(const FSceneView* View) const override { + FPrimitiveViewRelevance relevance = + FStaticMeshSceneProxy::GetViewRelevance(View); + + const bool* pVisibility = + this->_viewsVisibility.Find(View->State->GetViewKey()); + if (pVisibility) { + // We have visibility information for the view group corresponding to this + // ViewActor. It may be the default view group if ViewActor==nullptr. + if (!*pVisibility) { + relevance.bDrawRelevance = false; + relevance.bShadowRelevance = false; + } + } else { + // We don't have visibility information for a view group corresponding to + // this ViewActor, so use the visibility information for the default view + // group. + pVisibility = this->_viewsVisibility.Find(0); + check(pVisibility); + if (pVisibility && !*pVisibility) { + relevance.bDrawRelevance = false; + relevance.bShadowRelevance = false; + } + } + + return relevance; + } + + void updateVisibility(TMap&& newVisibility) { + this->_viewsVisibility = std::move(newVisibility); + } + +private: + // An entry per view assigned to a view group, plus an entry for nullptr + // which is the visibility of this primitive in the default view group. + TMap _viewsVisibility; +}; + +} // namespace + +FPrimitiveSceneProxy* UCesiumGltfPrimitiveComponent::CreateStaticMeshSceneProxy( + Nanite::FMaterialAudit& NaniteMaterials, + bool bCreateNanite) { + check(!bCreateNanite); + + ACesiumCameraManager* pCameraManager = + ACesiumCameraManager::GetDefaultCameraManager(this->GetOwner()); + + UCesiumGltfComponent* pGltf = + Cast(this->GetAttachParent()); + + TMap viewGroupVisibility{ + {0, pGltf ? pGltf->GetViewGroupVisibility(0) : false}}; + if (pCameraManager) { + for (const FCesiumViewGroup& group : pCameraManager->ViewGroups) { + if (group.ViewStateKey > 0) + viewGroupVisibility.Add( + group.ViewStateKey, + pGltf ? pGltf->GetViewGroupVisibility(group.ViewStateKey) : false); + } + } else { + viewGroupVisibility = {{0, false}}; + } + + auto* Proxy = ::new FCesiumGltfPrimitiveSceneProxy( + this, + false, + std::move(viewGroupVisibility)); +#if STATICMESH_ENABLE_DEBUG_RENDERING + SendRenderDebugPhysics(Proxy); +#endif + + return Proxy; +} + FBoxSphereBounds UCesiumGltfInstancedComponent::CalcBounds( const FTransform& LocalToWorld) const { if (auto bounds = calcBounds(*this, LocalToWorld)) { @@ -159,6 +249,15 @@ UCesiumGltfPrimitiveComponent::getPrimitiveData() const { return _cesiumData; } +void UCesiumGltfPrimitiveComponent::updateVisibilityInRenderThread( + FPrimitiveSceneProxy* pProxy, + TMap&& visibility) { + check(pProxy); + check(visibility.Contains(0)); + static_cast(pProxy)->updateVisibility( + std::move(visibility)); +} + CesiumPrimitiveData& UCesiumGltfInstancedComponent::getPrimitiveData() { return _cesiumData; } diff --git a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h index f37b6e0a0..ce551c824 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h +++ b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h @@ -38,12 +38,20 @@ class UCesiumGltfPrimitiveComponent : public UStaticMeshComponent, FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override; + FPrimitiveSceneProxy* CreateStaticMeshSceneProxy( + Nanite::FMaterialAudit& NaniteMaterials, + bool bCreateNanite) override; + void UpdateTransformFromCesium(const glm::dmat4& CesiumToUnrealTransform) override; CesiumPrimitiveData& getPrimitiveData() override; const CesiumPrimitiveData& getPrimitiveData() const override; + static void updateVisibilityInRenderThread( + FPrimitiveSceneProxy* pProxy, + TMap&& visibility); + private: CesiumPrimitiveData _cesiumData; }; diff --git a/Source/CesiumRuntime/Private/CesiumSceneCapture2D.cpp b/Source/CesiumRuntime/Private/CesiumSceneCapture2D.cpp new file mode 100644 index 000000000..3bb25b00f --- /dev/null +++ b/Source/CesiumRuntime/Private/CesiumSceneCapture2D.cpp @@ -0,0 +1,27 @@ +// Copyright 2020-2025 CesiumGS, Inc. and Contributors + +#include "CesiumSceneCapture2D.h" +#include "CesiumSceneCaptureComponent2D.h" + +ACesiumSceneCapture2D::ACesiumSceneCapture2D( + const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) { + CaptureComponent2D = CreateDefaultSubobject( + TEXT("NewSceneCaptureComponent2D")); + CaptureComponent2D->SetupAttachment(RootComponent); +} + +void ACesiumSceneCapture2D::OnInterpToggle(bool bEnable) { + CaptureComponent2D->SetVisibility(bEnable); +} + +void ACesiumSceneCapture2D::CalcCamera( + float DeltaTime, + FMinimalViewInfo& OutMinimalViewInfo) { + if (UCesiumSceneCaptureComponent2D* SceneCaptureComponent = + GetCaptureComponent2D()) { + SceneCaptureComponent->GetCameraView(DeltaTime, OutMinimalViewInfo); + } else { + Super::CalcCamera(DeltaTime, OutMinimalViewInfo); + } +} diff --git a/Source/CesiumRuntime/Private/CesiumSceneCaptureComponent2D.cpp b/Source/CesiumRuntime/Private/CesiumSceneCaptureComponent2D.cpp new file mode 100644 index 000000000..22db8bcff --- /dev/null +++ b/Source/CesiumRuntime/Private/CesiumSceneCaptureComponent2D.cpp @@ -0,0 +1,11 @@ +// Copyright 2020-2025 CesiumGS, Inc. and Contributors + +#include "CesiumSceneCaptureComponent2D.h" + +UCesiumSceneCaptureComponent2D::UCesiumSceneCaptureComponent2D( + const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) {} + +const AActor* UCesiumSceneCaptureComponent2D::GetViewOwner() const { + return this->GetOwner(); +} diff --git a/Source/CesiumRuntime/Public/Cesium3DTileset.h b/Source/CesiumRuntime/Public/Cesium3DTileset.h index 95c19d593..4f08f3f56 100644 --- a/Source/CesiumRuntime/Public/Cesium3DTileset.h +++ b/Source/CesiumRuntime/Public/Cesium3DTileset.h @@ -1289,8 +1289,11 @@ class CESIUMRUNTIME_API ACesium3DTileset : public AActor { * * @param tiles The tiles */ - void - showTilesToRender(const std::vector& tiles); + void showTilesToRender( + uint32 viewStateKey, + const std::vector< + CesiumUtility::IntrusivePointer>& + tiles); /** * Will be called after the tileset is loaded or spawned, to register @@ -1366,10 +1369,14 @@ class CESIUMRUNTIME_API ACesium3DTileset : public AActor { // If we find a way to clear the wrong occlusion information in the // Unreal Engine, then this field may be removed, and the // tilesToHideThisFrame may be hidden immediately. - std::vector _tilesToHideNextFrame; + std::vector> + _tilesToHideNextFrame; int32 _tilesetsBeingDestroyed; + std::vector _viewGroups; + TObjectPtr _pLastMonitorViewActor = nullptr; + friend class UnrealPrepareRendererResources; friend class UCesiumGltfPointsComponent; }; diff --git a/Source/CesiumRuntime/Public/CesiumCameraManager.h b/Source/CesiumRuntime/Public/CesiumCameraManager.h index 4575a7dd2..83ed690b7 100644 --- a/Source/CesiumRuntime/Public/CesiumCameraManager.h +++ b/Source/CesiumRuntime/Public/CesiumCameraManager.h @@ -2,12 +2,51 @@ #pragma once +#include "Camera/CameraComponent.h" #include "CesiumCamera.h" #include "Containers/Map.h" #include "GameFramework/Actor.h" - #include "CesiumCameraManager.generated.h" +USTRUCT(BlueprintType) +struct CESIUMRUNTIME_API FCesiumViewGroup { + GENERATED_USTRUCT_BODY() + + /** + * A human-readable description of this view group. + */ + UPROPERTY(Category = "Cesium", VisibleAnywhere, BlueprintReadOnly) + FString ViewDescription = "Unknown"; + + /** + * The index of the viewing FEditorViewportClient in + * GEditor->GetAllViewportClients(), or -1 if this view is not an editor + * viewport client. + */ + UPROPERTY(Category = "Cesium", VisibleAnywhere, BlueprintReadOnly) + int32 EditorViewportIndex = -1; + + /** + * The viewing Actor, which is expected to have either a + * USceneCaptureComponent2D or a UCameraComponent attached to it. + */ + UPROPERTY(Category = "Cesium", VisibleAnywhere, BlueprintReadOnly) + TSoftObjectPtr ViewActor = nullptr; + + /** + * The unique ID of the FSceneViewStateInterface, as returned by its + * GetViewKey method. + */ + UPROPERTY(Category = "Cesium", VisibleAnywhere, BlueprintReadOnly) + int64 ViewStateKey = -1; + + /** + * The weight of this view group, used to prioritize tile loading. + */ + UPROPERTY(Category = "Cesium", EditAnywhere, BlueprintReadWrite) + double LoadWeight = 1.0; +}; + /** * @brief Manages custom {@link FCesiumCamera}s for all * {@link ACesium3DTileset}s in the world. @@ -72,6 +111,9 @@ class CESIUMRUNTIME_API ACesiumCameraManager : public AActor { virtual void Tick(float DeltaTime) override; + UPROPERTY(Category = "Cesium", EditAnywhere, BlueprintReadWrite) + TArray ViewGroups; + private: int32 _currentCameraId = 0; TMap _cameras; diff --git a/Source/CesiumRuntime/Public/CesiumSceneCapture2D.h b/Source/CesiumRuntime/Public/CesiumSceneCapture2D.h new file mode 100644 index 000000000..17370e41e --- /dev/null +++ b/Source/CesiumRuntime/Public/CesiumSceneCapture2D.h @@ -0,0 +1,35 @@ +// Copyright 2020-2025 CesiumGS, Inc. and Contributors + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/SceneCapture.h" +#include "UObject/ObjectMacros.h" +#include "CesiumSceneCapture2D.generated.h" + +UCLASS(hidecategories = (Collision, Material, Attachment, Actor), MinimalAPI) +class ACesiumSceneCapture2D : public ASceneCapture { + GENERATED_UCLASS_BODY() + +private: + /** Scene capture component. */ + UPROPERTY( + Category = Cesium, + VisibleAnywhere, + BlueprintReadOnly, + meta = (AllowPrivateAccess = "true")) + TObjectPtr CaptureComponent2D; + +public: + UFUNCTION(BlueprintCallable, Category = "Rendering") + void OnInterpToggle(bool bEnable); + + CESIUMRUNTIME_API virtual void CalcCamera( + float DeltaTime, + struct FMinimalViewInfo& OutMinimalViewInfo) override; + + /** Returns CaptureComponent2D subobject **/ + class UCesiumSceneCaptureComponent2D* GetCaptureComponent2D() const { + return CaptureComponent2D; + } +}; diff --git a/Source/CesiumRuntime/Public/CesiumSceneCaptureComponent2D.h b/Source/CesiumRuntime/Public/CesiumSceneCaptureComponent2D.h new file mode 100644 index 000000000..e6d9ec25b --- /dev/null +++ b/Source/CesiumRuntime/Public/CesiumSceneCaptureComponent2D.h @@ -0,0 +1,30 @@ +// Copyright 2020-2025 CesiumGS, Inc. and Contributors + +#pragma once + +#include "Components/SceneCaptureComponent2D.h" +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "CesiumSceneCaptureComponent2D.generated.h" + +/** + * Used to capture a 'snapshot' of the scene from a single plane and feed it to + * a render target. + * + * This is identical to Unreal Engine's built-in + * `USceneCaptureCaptureComponent2D` except that it overrides `GetViewOwner` to + * returning `GetOwner`. This allows the capture component to be used as a view + * of a `Cesium3DTileset`. + */ +UCLASS( + hidecategories = (Collision, Object, Physics, SceneComponent), + ClassGroup = Rendering, + editinlinenew, + meta = (BlueprintSpawnableComponent), + MinimalAPI) +class UCesiumSceneCaptureComponent2D : public USceneCaptureComponent2D { + GENERATED_UCLASS_BODY() + +public: + const AActor* GetViewOwner() const override; +}; diff --git a/extern/cesium-native b/extern/cesium-native index ae62bd8c6..73b7143ed 160000 --- a/extern/cesium-native +++ b/extern/cesium-native @@ -1 +1 @@ -Subproject commit ae62bd8c6a7fbce08a541eecd86a313bfb906e15 +Subproject commit 73b7143eda219ce9b91fdb06229be1e3fb960a4c