Files
UnrealEngineUWP/Engine/Source/Runtime/Landscape/Private/LandscapeSubsystem.cpp
graham wihlidal 730418b0dc Landscape Nanite serialization fixes (moved representation update out of Serialize and into PreSave), added slow task dialog during builds, and added a "Build Nanite" (and part of Build All Landscape) editor top level menu item under Build (so artists don't have to always navigate to the Build Data button on the landscape actor to rebuild the Nanite representation).
[FYI] jonathan.bard, brian.karis, ola.olsson
#preflight skip

#ROBOMERGE-AUTHOR: graham.wihlidal
#ROBOMERGE-SOURCE: CL 20585983 via CL 20585993 via CL 20586004
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v955-20579017)

[CL 20588028 by graham wihlidal in ue5-main branch]
2022-06-09 20:26:50 -04:00

281 lines
7.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LandscapeSubsystem.h"
#include "UObject/UObjectGlobals.h"
#include "Engine/EngineBaseTypes.h"
#include "Engine/World.h"
#include "ContentStreaming.h"
#include "Landscape.h"
#include "LandscapeProxy.h"
#include "LandscapeStreamingProxy.h"
#include "LandscapeInfo.h"
#include "ProfilingDebugging/CsvProfiler.h"
#include "WorldPartition/WorldPartitionSubsystem.h"
#include "ActorPartition/ActorPartitionSubsystem.h"
#include "Engine/World.h"
#include "Math/IntRect.h"
#include "LandscapeConfigHelper.h"
#include "Engine/Canvas.h"
#include "EngineUtils.h"
static int32 GUseStreamingManagerForCameras = 1;
static FAutoConsoleVariableRef CVarUseStreamingManagerForCameras(
TEXT("grass.UseStreamingManagerForCameras"),
GUseStreamingManagerForCameras,
TEXT("1: Use Streaming Manager; 0: Use ViewLocationsRenderedLastFrame"));
DECLARE_CYCLE_STAT(TEXT("LandscapeSubsystem Tick"), STAT_LandscapeSubsystemTick, STATGROUP_Landscape);
#define LOCTEXT_NAMESPACE "LandscapeSubsystem"
ULandscapeSubsystem::ULandscapeSubsystem()
{
}
ULandscapeSubsystem::~ULandscapeSubsystem()
{
}
void ULandscapeSubsystem::RegisterActor(ALandscapeProxy* Proxy)
{
Proxies.AddUnique(Proxy);
}
void ULandscapeSubsystem::UnregisterActor(ALandscapeProxy* Proxy)
{
Proxies.Remove(Proxy);
}
void ULandscapeSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
#if WITH_EDITOR
GrassMapsBuilder = new FLandscapeGrassMapsBuilder(GetWorld());
GIBakedTextureBuilder = new FLandscapeGIBakedTextureBuilder(GetWorld());
PhysicalMaterialBuilder = new FLandscapePhysicalMaterialBuilder(GetWorld());
#endif
}
void ULandscapeSubsystem::Deinitialize()
{
#if WITH_EDITOR
if (GrassMapsBuilder)
{
delete GrassMapsBuilder;
}
if (GIBakedTextureBuilder)
{
delete GIBakedTextureBuilder;
}
if (PhysicalMaterialBuilder)
{
delete PhysicalMaterialBuilder;
}
#endif
Proxies.Empty();
Super::Deinitialize();
}
TStatId ULandscapeSubsystem::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(ULandscapeSubsystem, STATGROUP_Tickables);
}
ETickableTickType ULandscapeSubsystem::GetTickableTickType() const
{
return HasAnyFlags(RF_ClassDefaultObject) || !GetWorld() || GetWorld()->IsNetMode(NM_DedicatedServer) ? ETickableTickType::Never : ETickableTickType::Always;
}
void ULandscapeSubsystem::Tick(float DeltaTime)
{
SCOPE_CYCLE_COUNTER(STAT_LandscapeSubsystemTick);
TRACE_CPUPROFILER_EVENT_SCOPE(ULandscapeSubsystem::Tick);
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(Landscape);
LLM_SCOPE(ELLMTag::Landscape);
Super::Tick(DeltaTime);
UWorld* World = GetWorld();
static TArray<FVector> OldCameras;
TArray<FVector>* Cameras = nullptr;
if (GUseStreamingManagerForCameras == 0)
{
if (OldCameras.Num() || World->ViewLocationsRenderedLastFrame.Num())
{
Cameras = &OldCameras;
// there is a bug here, which often leaves us with no cameras in the editor
if (World->ViewLocationsRenderedLastFrame.Num())
{
check(IsInGameThread());
Cameras = &World->ViewLocationsRenderedLastFrame;
OldCameras = *Cameras;
}
}
}
else
{
int32 Num = IStreamingManager::Get().GetNumViews();
if (Num)
{
OldCameras.Reset(Num);
for (int32 Index = 0; Index < Num; Index++)
{
auto& ViewInfo = IStreamingManager::Get().GetViewInformation(Index);
OldCameras.Add(ViewInfo.ViewOrigin);
}
Cameras = &OldCameras;
}
}
int32 InOutNumComponentsCreated = 0;
for (ALandscapeProxy* Proxy : Proxies)
{
#if WITH_EDITOR
if (GIsEditor)
{
if (ALandscape* Landscape = Cast<ALandscape>(Proxy))
{
Landscape->TickLayers(DeltaTime);
}
// editor-only
if (!World->IsPlayInEditor())
{
Proxy->UpdateGIBakedTextures();
Proxy->UpdatePhysicalMaterialTasks();
}
}
#endif
if (Cameras && Proxy->ShouldTickGrass())
{
Proxy->TickGrass(*Cameras, InOutNumComponentsCreated);
}
}
#if WITH_EDITOR
if (GIsEditor && !World->IsPlayInEditor())
{
LandscapePhysicalMaterial::GarbageCollectTasks();
}
#endif
}
#if WITH_EDITOR
void ULandscapeSubsystem::BuildAll()
{
BuildGrassMaps();
BuildGIBakedTextures();
BuildPhysicalMaterial();
BuildNanite();
}
void ULandscapeSubsystem::BuildGrassMaps()
{
GrassMapsBuilder->Build();
}
int32 ULandscapeSubsystem::GetOutdatedGrassMapCount()
{
return GrassMapsBuilder->GetOutdatedGrassMapCount(/*bInForceUpdate*/false);
}
void ULandscapeSubsystem::BuildGIBakedTextures()
{
GIBakedTextureBuilder->Build();
}
int32 ULandscapeSubsystem::GetOutdatedGIBakedTextureComponentsCount()
{
return GIBakedTextureBuilder->GetOutdatedGIBakedTextureComponentsCount(/*bInForceUpdate*/false);
}
void ULandscapeSubsystem::BuildPhysicalMaterial()
{
PhysicalMaterialBuilder->Build();
}
int32 ULandscapeSubsystem::GetOudatedPhysicalMaterialComponentsCount()
{
return PhysicalMaterialBuilder->GetOudatedPhysicalMaterialComponentsCount();
}
void ULandscapeSubsystem::BuildNanite()
{
UWorld* World = GetWorld();
if (!World || World->IsGameWorld())
{
return;
}
for (TActorIterator<ALandscapeProxy> ProxyIt(World); ProxyIt; ++ProxyIt)
{
ProxyIt->UpdateNaniteRepresentation();
ProxyIt->UpdateRenderingMethod();
}
}
bool ULandscapeSubsystem::IsGridBased() const
{
return UWorld::IsPartitionedWorld(GetWorld());
}
void ULandscapeSubsystem::ChangeGridSize(ULandscapeInfo* LandscapeInfo, uint32 GridSizeInComponents)
{
if (!IsGridBased())
{
return;
}
TSet<AActor*> ActorsToDelete;
FLandscapeConfigHelper::ChangeGridSize(LandscapeInfo, GridSizeInComponents, ActorsToDelete);
// This code path is used for converting a non grid based Landscape to a gridbased so it shouldn't delete any actors
check(!ActorsToDelete.Num());
}
ALandscapeProxy* ULandscapeSubsystem::FindOrAddLandscapeProxy(ULandscapeInfo* LandscapeInfo, const FIntPoint& SectionBase)
{
if (!IsGridBased())
{
return LandscapeInfo->GetCurrentLevelLandscapeProxy(true);
}
return FLandscapeConfigHelper::FindOrAddLandscapeStreamingProxy(LandscapeInfo, SectionBase);
}
void ULandscapeSubsystem::DisplayBuildMessages(FCanvas* Canvas, float& XPos, float& YPos)
{
const int32 FontSizeY = 20;
FCanvasTextItem SmallTextItem(FVector2D(0, 0), FText::GetEmpty(), GEngine->GetSmallFont(), FLinearColor::White);
SmallTextItem.EnableShadow(FLinearColor::Black);
if (int32 OutdatedGrassMapCount = GetOutdatedGrassMapCount())
{
SmallTextItem.SetColor(FLinearColor::Red);
SmallTextItem.Text = FText::Format(LOCTEXT("GRASS_MAPS_NEED_TO_BE_REBUILT_FMT", "GRASS MAPS NEEDS TO BE REBUILT ({0} {0}|plural(one=object,other=objects))"), OutdatedGrassMapCount);
Canvas->DrawItem(SmallTextItem, FVector2D(XPos, YPos));
YPos += FontSizeY;
}
if (int32 ComponentsNeedingGITextureBaking = GetOutdatedGIBakedTextureComponentsCount())
{
SmallTextItem.SetColor(FLinearColor::Red);
SmallTextItem.Text = FText::Format(LOCTEXT("LANDSCAPE_TEXTURES_NEED_TO_BE_REBUILT_FMT", "LANDSCAPE BAKED TEXTURES NEEDS TO BE REBUILT ({0} {0}|plural(one=object,other=objects))"), ComponentsNeedingGITextureBaking);
Canvas->DrawItem(SmallTextItem, FVector2D(XPos, YPos));
YPos += FontSizeY;
}
if (int32 ComponentsWithOudatedPhysicalMaterial = GetOudatedPhysicalMaterialComponentsCount())
{
SmallTextItem.SetColor(FLinearColor::Red);
SmallTextItem.Text = FText::Format(LOCTEXT("LANDSCAPE_PHYSICALMATERIAL_NEED_TO_BE_REBUILT_FMT", "LANDSCAPE PHYSICAL MATERIAL NEEDS TO BE REBUILT ({0} {0}|plural(one=object,other=objects))"), ComponentsWithOudatedPhysicalMaterial);
Canvas->DrawItem(SmallTextItem, FVector2D(XPos, YPos));
YPos += FontSizeY;
}
}
#endif
#undef LOCTEXT_NAMESPACE