Files
UnrealEngineUWP/Engine/Source/Runtime/Landscape/Private/LandscapeSubsystem.cpp
jonathan bard 670ce37131 Added UTickableWorldSubsystem as a base class for world sub systems that need to be ticked along with their world.
By default:
* It implements FTickableGameObject::GetTickableGameObjectWorld to return the subsystem's world
* It prevents the subsystem from ticking as soon as the subsystem is deinitialized (has required custom solutions in the past to avoid crashes when UWaterSubsystem and UAutoDestroySubsystem get ticked after their world is pending destroy)
* It prevents the subsystem's CDO from ever ticking

#rb jean-francois.dube, chris.gagnon, ben.zeigler
#jira none

[CL 15512967 by jonathan bard in ue5-main branch]
2021-02-24 06:56:06 -04:00

264 lines
7.1 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"
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();
}
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();
}
bool ULandscapeSubsystem::IsGridBased() const
{
return UWorld::HasSubsystem<UWorldPartitionSubsystem>(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