Files
UnrealEngineUWP/Engine/Source/Editor/DataLayerEditor/Private/DataLayer/DataLayerHierarchy.cpp
JeanFrancois Dube f59630be43 Data Layers: fix "Iterating over Data Layers without a World Partition" when trying to refresh the data layers outliner for non-partitioned maps.
#rb richard.malo
#preflight none
#rnx

[CL 20574099 by JeanFrancois Dube in ue5-main branch]
2022-06-09 07:59:49 -04:00

553 lines
19 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DataLayerHierarchy.h"
#include "DataLayerMode.h"
#include "DataLayerActorTreeItem.h"
#include "DataLayersActorDescTreeItem.h"
#include "DataLayerTreeItem.h"
#include "WorldDataLayersTreeItem.h"
#include "DataLayerMode.h"
#include "DataLayer/DataLayerEditorSubsystem.h"
#include "WorldPartition/DataLayer/DataLayerAsset.h"
#include "WorldPartition/DataLayer/DataLayerSubsystem.h"
#include "WorldPartition/DataLayer/DataLayerInstanceWithAsset.h"
#include "WorldPartition/DataLayer/WorldDataLayers.h"
#include "WorldPartition/WorldPartitionSubsystem.h"
#include "WorldPartition/WorldPartitionHelpers.h"
#include "WorldPartition/IWorldPartitionEditorModule.h"
#include "LevelInstance/LevelInstanceSubsystem.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "EngineUtils.h"
#include "Editor.h"
#include "Modules/ModuleManager.h"
TUniquePtr<FDataLayerHierarchy> FDataLayerHierarchy::Create(FDataLayerMode* Mode, const TWeakObjectPtr<UWorld>& World)
{
return TUniquePtr<FDataLayerHierarchy>(new FDataLayerHierarchy(Mode, World));
}
FDataLayerHierarchy::FDataLayerHierarchy(FDataLayerMode* Mode, const TWeakObjectPtr<UWorld>& World)
: ISceneOutlinerHierarchy(Mode)
, RepresentingWorld(World)
, bShowEditorDataLayers(true)
, bShowRuntimeDataLayers(true)
, bShowDataLayerActors(true)
, bShowUnloadedActors(true)
, bShowOnlySelectedActors(false)
, bHighlightSelectedDataLayers(false)
, bShowLevelInstanceContent(false)
{
if (GEngine)
{
GEngine->OnLevelActorAdded().AddRaw(this, &FDataLayerHierarchy::OnLevelActorAdded);
GEngine->OnLevelActorDeleted().AddRaw(this, &FDataLayerHierarchy::OnLevelActorDeleted);
GEngine->OnLevelActorListChanged().AddRaw(this, &FDataLayerHierarchy::OnLevelActorListChanged);
}
IWorldPartitionEditorModule& WorldPartitionEditorModule = FModuleManager::LoadModuleChecked<IWorldPartitionEditorModule>("WorldPartitionEditor");
WorldPartitionEditorModule.OnWorldPartitionCreated().AddRaw(this, &FDataLayerHierarchy::OnWorldPartitionCreated);
if (World.IsValid())
{
if (World->PersistentLevel)
{
World->PersistentLevel->OnLoadedActorAddedToLevelEvent.AddRaw(this, &FDataLayerHierarchy::OnLoadedActorAdded);
World->PersistentLevel->OnLoadedActorRemovedFromLevelEvent.AddRaw(this, &FDataLayerHierarchy::OnLoadedActorRemoved);
}
World->OnWorldPartitionInitialized().AddRaw(this, &FDataLayerHierarchy::OnWorldPartitionInitialized);
World->OnWorldPartitionUninitialized().AddRaw(this, &FDataLayerHierarchy::OnWorldPartitionUninitialized);
if (UWorldPartition* WorldPartition = World->GetWorldPartition())
{
WorldPartition->OnActorDescAddedEvent.AddRaw(this, &FDataLayerHierarchy::OnActorDescAdded);
WorldPartition->OnActorDescRemovedEvent.AddRaw(this, &FDataLayerHierarchy::OnActorDescRemoved);
}
}
UDataLayerEditorSubsystem::Get()->OnDataLayerChanged().AddRaw(this, &FDataLayerHierarchy::OnDataLayerChanged);
UDataLayerEditorSubsystem::Get()->OnActorDataLayersChanged().AddRaw(this, &FDataLayerHierarchy::OnActorDataLayersChanged);
FWorldDelegates::LevelAddedToWorld.AddRaw(this, &FDataLayerHierarchy::OnLevelAdded);
FWorldDelegates::LevelRemovedFromWorld.AddRaw(this, &FDataLayerHierarchy::OnLevelRemoved);
}
FDataLayerHierarchy::~FDataLayerHierarchy()
{
if (GEngine)
{
GEngine->OnLevelActorAdded().RemoveAll(this);
GEngine->OnLevelActorDeleted().RemoveAll(this);
GEngine->OnLevelActorListChanged().RemoveAll(this);
}
IWorldPartitionEditorModule& WorldPartitionEditorModule = FModuleManager::LoadModuleChecked<IWorldPartitionEditorModule>("WorldPartitionEditor");
WorldPartitionEditorModule.OnWorldPartitionCreated().RemoveAll(this);
if (RepresentingWorld.IsValid())
{
if (RepresentingWorld->PersistentLevel)
{
RepresentingWorld->PersistentLevel->OnLoadedActorAddedToLevelEvent.RemoveAll(this);
RepresentingWorld->PersistentLevel->OnLoadedActorRemovedFromLevelEvent.RemoveAll(this);
}
RepresentingWorld->OnWorldPartitionInitialized().RemoveAll(this);
RepresentingWorld->OnWorldPartitionUninitialized().RemoveAll(this);
if (UWorldPartition* WorldPartition = RepresentingWorld->GetWorldPartition())
{
WorldPartition->OnActorDescAddedEvent.RemoveAll(this);
WorldPartition->OnActorDescRemovedEvent.RemoveAll(this);
}
}
UDataLayerEditorSubsystem::Get()->OnDataLayerChanged().RemoveAll(this);
UDataLayerEditorSubsystem::Get()->OnActorDataLayersChanged().RemoveAll(this);
FWorldDelegates::LevelAddedToWorld.RemoveAll(this);
FWorldDelegates::LevelRemovedFromWorld.RemoveAll(this);
}
bool FDataLayerHierarchy::IsDataLayerPartOfSelection(const UDataLayerInstance* DataLayerInstance) const
{
if (!bShowOnlySelectedActors)
{
return true;
}
if (UDataLayerEditorSubsystem::Get()->DoesDataLayerContainSelectedActors(DataLayerInstance))
{
return true;
}
bool bFoundSelected = false;
DataLayerInstance->ForEachChild([this, &bFoundSelected](const UDataLayerInstance* Child)
{
bFoundSelected = IsDataLayerPartOfSelection(Child);
return !bFoundSelected; // Continue iterating if not found
});
return bFoundSelected;
};
FSceneOutlinerTreeItemPtr FDataLayerHierarchy::CreateDataLayerTreeItem(UDataLayerInstance* InDataLayer, bool bInForce) const
{
FSceneOutlinerTreeItemPtr Item = Mode->CreateItemFor<FDataLayerTreeItem>(InDataLayer, bInForce);
if (FDataLayerTreeItem* DataLayerTreeItem = Item ? Item->CastTo<FDataLayerTreeItem>() : nullptr)
{
DataLayerTreeItem->SetIsHighlightedIfSelected(bHighlightSelectedDataLayers);
}
return Item;
}
UWorld* FDataLayerHierarchy::GetOwningWorld() const
{
UWorld* World = RepresentingWorld.Get();
return World ? World->PersistentLevel->GetWorld() : nullptr;
}
void FDataLayerHierarchy::CreateItems(TArray<FSceneOutlinerTreeItemPtr>& OutItems) const
{
if (!GetOwningWorld()->GetWorldDataLayers())
{
return;
}
const UDataLayerSubsystem* DataLayerSubsystem = UWorld::GetSubsystem<UDataLayerSubsystem>(GetOwningWorld());
auto IsDataLayerShown = [this](const UDataLayerInstance* DataLayerInstance)
{
const bool bIsRuntimeDataLayer = DataLayerInstance->IsRuntime();
return ((bIsRuntimeDataLayer && bShowRuntimeDataLayers) || (!bIsRuntimeDataLayer && bShowEditorDataLayers)) && IsDataLayerPartOfSelection(DataLayerInstance);
};
auto CreateDataLayerTreeItems = [this, DataLayerSubsystem, &OutItems, IsDataLayerShown](const ULevel* OuterLevel)
{
DataLayerSubsystem->ForEachDataLayer([this, &OutItems, IsDataLayerShown](UDataLayerInstance* DataLayer)
{
if (IsDataLayerShown(DataLayer))
{
if (FSceneOutlinerTreeItemPtr DataLayerItem = CreateDataLayerTreeItem(DataLayer))
{
OutItems.Add(DataLayerItem);
}
}
return true;
}, OuterLevel);
};
// CurrentLevel represents the current level if different than the PersistentLevel
ULevel* CurrentLevel = (GetOwningWorld()->GetCurrentLevel() && !GetOwningWorld()->GetCurrentLevel()->IsPersistentLevel()) ? GetOwningWorld()->GetCurrentLevel() : nullptr;
CreateDataLayerTreeItems(GetOwningWorld()->PersistentLevel);
CreateDataLayerTreeItems(CurrentLevel);
if (bShowDataLayerActors)
{
for (AActor* Actor : FActorRange(GetOwningWorld()))
{
// Only consider actors of current level (if there is one)
if (!CurrentLevel || (Actor->GetLevel() == CurrentLevel))
{
for (const UDataLayerInstance* DataLayerInstance : Actor->GetDataLayerInstances())
{
if (IsDataLayerShown(DataLayerInstance))
{
if (FSceneOutlinerTreeItemPtr DataLayerActorItem = Mode->CreateItemFor<FDataLayerActorTreeItem>(FDataLayerActorTreeItemData(Actor, const_cast<UDataLayerInstance*>(DataLayerInstance))))
{
OutItems.Add(DataLayerActorItem);
}
}
}
}
}
if (bShowUnloadedActors)
{
ULevelInstanceSubsystem* LevelInstanceSubsystem = UWorld::GetSubsystem<ULevelInstanceSubsystem>(GetOwningWorld());
if (UWorldPartitionSubsystem* WorldPartitionSubsystem = UWorld::GetSubsystem<UWorldPartitionSubsystem>(GetOwningWorld()))
{
WorldPartitionSubsystem->ForEachWorldPartition([this, CurrentLevel, LevelInstanceSubsystem, DataLayerSubsystem, IsDataLayerShown, &OutItems](UWorldPartition* WorldPartition)
{
// Skip WorldPartition if it's not the one of the current level (the editing level instance)
// or if we hide the content of level instances
UWorld* OuterWorld = WorldPartition->GetTypedOuter<UWorld>();
ULevel* OuterLevel = OuterWorld ? OuterWorld->PersistentLevel : nullptr;
if ((CurrentLevel && (CurrentLevel != OuterLevel)) ||
(!bShowLevelInstanceContent && OuterLevel && LevelInstanceSubsystem && LevelInstanceSubsystem->GetOwningLevelInstance(OuterLevel)))
{
return true;
}
// Create an FDataLayerActorDescTreeItem for each unloaded actor of this WorldPartition
FWorldPartitionHelpers::ForEachActorDesc(WorldPartition, [this, DataLayerSubsystem, IsDataLayerShown, &OutItems](const FWorldPartitionActorDesc* ActorDesc)
{
if (ActorDesc != nullptr && !ActorDesc->IsLoaded())
{
for (const FName& DataLayerInstanceName : ActorDesc->GetDataLayerInstanceNames())
{
if (const UDataLayerInstance* const DataLayerInstance = DataLayerSubsystem->GetDataLayerInstance(DataLayerInstanceName))
{
if (IsDataLayerShown(DataLayerInstance))
{
if (const FSceneOutlinerTreeItemPtr ActorDescItem = Mode->CreateItemFor<FDataLayerActorDescTreeItem>(FDataLayerActorDescTreeItemData(ActorDesc->GetGuid(), ActorDesc->GetContainer(), const_cast<UDataLayerInstance*>(DataLayerInstance))))
{
OutItems.Add(ActorDescItem);
}
}
}
}
}
return true;
});
return true;
});
}
}
}
}
FSceneOutlinerTreeItemPtr FDataLayerHierarchy::FindOrCreateParentItem(const ISceneOutlinerTreeItem& Item, const TMap<FSceneOutlinerTreeItemID, FSceneOutlinerTreeItemPtr>& Items, bool bCreate)
{
if (const FDataLayerTreeItem* DataLayerTreeItem = Item.CastTo<FDataLayerTreeItem>())
{
if (UDataLayerInstance* DataLayerInstance = DataLayerTreeItem->GetDataLayer())
{
if (UDataLayerInstance* ParentDataLayer = DataLayerInstance->GetParent())
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ParentDataLayer))
{
return *ParentItem;
}
else if (bCreate)
{
return CreateDataLayerTreeItem(ParentDataLayer, true);
}
}
else
{
// Parent WorldDataLayers
if (AWorldDataLayers* OuterWorldDataLayers = DataLayerInstance->GetOuterAWorldDataLayers())
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(OuterWorldDataLayers))
{
return *ParentItem;
}
else if (bCreate)
{
return Mode->CreateItemFor<FWorldDataLayersTreeItem>(OuterWorldDataLayers, true);
}
}
}
}
}
else if (const FDataLayerActorTreeItem* DataLayerActorTreeItem = Item.CastTo<FDataLayerActorTreeItem>())
{
if (UDataLayerInstance* DataLayerInstance = DataLayerActorTreeItem->GetDataLayer())
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(DataLayerInstance))
{
return *ParentItem;
}
else if (bCreate)
{
return CreateDataLayerTreeItem(DataLayerInstance, true);
}
}
}
else if (const FDataLayerActorDescTreeItem* DataLayerActorDescTreeItem = Item.CastTo<FDataLayerActorDescTreeItem>())
{
if (UDataLayerInstance* DataLayerInstance = DataLayerActorDescTreeItem->GetDataLayer())
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(DataLayerInstance))
{
return *ParentItem;
}
else if (bCreate)
{
return CreateDataLayerTreeItem(DataLayerInstance, true);
}
}
}
return nullptr;
}
void FDataLayerHierarchy::OnWorldPartitionCreated(UWorld* InWorld)
{
if (RepresentingWorld.Get() == InWorld)
{
FullRefreshEvent();
}
}
void FDataLayerHierarchy::OnLevelActorsAdded(const TArray<AActor*>& InActors)
{
if (!bShowDataLayerActors)
{
return;
}
if (UWorld* OwningWorld = GetOwningWorld())
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Added;
for (AActor* Actor : InActors)
{
if (Actor && (Actor->GetWorld() == OwningWorld))
{
TArray<const UDataLayerInstance*> DataLayerInstances = Actor->GetDataLayerInstances();
EventData.Items.Reserve(DataLayerInstances.Num());
for (const UDataLayerInstance* DataLayerInstance : DataLayerInstances)
{
EventData.Items.Add(Mode->CreateItemFor<FDataLayerActorTreeItem>(FDataLayerActorTreeItemData(Actor, const_cast<UDataLayerInstance*>(DataLayerInstance))));
}
}
}
if (!EventData.Items.IsEmpty())
{
HierarchyChangedEvent.Broadcast(EventData);
}
}
}
void FDataLayerHierarchy::OnLevelActorsRemoved(const TArray<AActor*>& InActors)
{
if (UWorld* OwningWorld = GetOwningWorld())
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Removed;
for (AActor* Actor : InActors)
{
if (Actor != nullptr)
{
const TArray<const UDataLayerInstance*> DataLayerInstances = Actor->GetDataLayerInstances();
for (const UDataLayerInstance* DataLayerInstance : DataLayerInstances)
{
EventData.ItemIDs.Add(FDataLayerActorTreeItem::ComputeTreeItemID(Actor, DataLayerInstance));
}
}
}
if (!EventData.ItemIDs.IsEmpty())
{
HierarchyChangedEvent.Broadcast(EventData);
}
}
}
void FDataLayerHierarchy::OnLevelActorAdded(AActor* InActor)
{
OnLevelActorsAdded({ InActor });
}
void FDataLayerHierarchy::OnActorDataLayersChanged(const TWeakObjectPtr<AActor>& InActor)
{
AActor* Actor = InActor.Get();
if (Actor && (GetOwningWorld() == Actor->GetWorld()))
{
FullRefreshEvent();
}
}
void FDataLayerHierarchy::OnDataLayerChanged(const EDataLayerAction Action, const TWeakObjectPtr<const UDataLayerInstance>& ChangedDataLayer, const FName& ChangedProperty)
{
const UDataLayerInstance* DataLayerInstance = ChangedDataLayer.Get();
if (DataLayerInstance || (Action == EDataLayerAction::Delete) || (Action == EDataLayerAction::Reset))
{
FullRefreshEvent();
}
}
void FDataLayerHierarchy::OnLevelActorDeleted(AActor* InActor)
{
OnLevelActorsRemoved({ InActor });
}
void FDataLayerHierarchy::OnLevelActorListChanged()
{
FullRefreshEvent();
}
void FDataLayerHierarchy::OnWorldPartitionInitialized(UWorldPartition* InWorldPartition)
{
FullRefreshEvent();
}
void FDataLayerHierarchy::OnWorldPartitionUninitialized(UWorldPartition* InWorldPartition)
{
FullRefreshEvent();
}
void FDataLayerHierarchy::OnLevelAdded(ULevel* InLevel, UWorld* InWorld)
{
if (!InWorld->IsGameWorld() && InLevel && (GetOwningWorld() == InWorld))
{
OnLevelActorsAdded(InLevel->Actors);
}
}
void FDataLayerHierarchy::OnLevelRemoved(ULevel* InLevel, UWorld* InWorld)
{
if (!InWorld->IsGameWorld() && InLevel && (GetOwningWorld() == InWorld))
{
OnLevelActorsRemoved(InLevel->Actors);
}
}
void FDataLayerHierarchy::OnLoadedActorAdded(AActor& InActor)
{
if (!bShowDataLayerActors)
{
return;
}
// Handle the actor being added to the level
OnLevelActorAdded(&InActor);
// Remove corresponding actor desc items
if ((GetOwningWorld() == InActor.GetWorld()))
{
const TArray<const UDataLayerInstance*> DataLayerInstances = InActor.GetDataLayerInstances();
if (DataLayerInstances.Num() > 0)
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Removed;
EventData.ItemIDs.Reserve(DataLayerInstances.Num());
ULevelInstanceSubsystem* LevelInstanceSubsystem = UWorld::GetSubsystem<ULevelInstanceSubsystem>(GetOwningWorld());
TArray<AActor*> ParentActors = LevelInstanceSubsystem->GetParentLevelInstanceActors(InActor.GetLevel());
for (const UDataLayerInstance* DataLayerInstance : DataLayerInstances)
{
EventData.ItemIDs.Add(FDataLayerActorDescTreeItem::ComputeTreeItemID(InActor.GetActorGuid(), DataLayerInstance, ParentActors));
}
HierarchyChangedEvent.Broadcast(EventData);
}
}
}
void FDataLayerHierarchy::OnLoadedActorRemoved(AActor& InActor)
{
// Handle the actor being removed from the level
OnLevelActorDeleted(&InActor);
// Add any corresponding actor desc items for this actor
UWorldPartition* const WorldPartition = RepresentingWorld->GetWorldPartition();
if ((GetOwningWorld() == InActor.GetWorld()) && WorldPartition != nullptr)
{
const TArray<const UDataLayerInstance*> DataLayerInstances = InActor.GetDataLayerInstances();
if (DataLayerInstances.Num() > 0)
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Added;
EventData.Items.Reserve(DataLayerInstances.Num());
for (const UDataLayerInstance* DataLayerInstance : DataLayerInstances)
{
EventData.Items.Add(Mode->CreateItemFor<FDataLayerActorDescTreeItem>(FDataLayerActorDescTreeItemData(InActor.GetActorGuid(), WorldPartition, const_cast<UDataLayerInstance*>(DataLayerInstance))));
}
HierarchyChangedEvent.Broadcast(EventData);
}
}
}
void FDataLayerHierarchy::OnActorDescAdded(FWorldPartitionActorDesc* InActorDesc)
{
if (!bShowUnloadedActors || !InActorDesc || InActorDesc->IsLoaded(true))
{
return;
}
UWorldPartition* const WorldPartition = RepresentingWorld->GetWorldPartition();
const UDataLayerSubsystem* const DataLayerSubsystem = UWorld::GetSubsystem<UDataLayerSubsystem>(GetOwningWorld());
const TArray<FName>& DataLayerInstanceNames = InActorDesc->GetDataLayerInstanceNames();
if (DataLayerSubsystem && DataLayerInstanceNames.Num() > 0)
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Added;
EventData.Items.Reserve(DataLayerInstanceNames.Num());
for (const FName& DataLayerInstanceName : DataLayerInstanceNames)
{
const UDataLayerInstance* const DataLayerInstance = DataLayerSubsystem->GetDataLayerInstance(DataLayerInstanceName);
EventData.Items.Add(Mode->CreateItemFor<FDataLayerActorDescTreeItem>(FDataLayerActorDescTreeItemData(InActorDesc->GetGuid(), WorldPartition, const_cast<UDataLayerInstance*>(DataLayerInstance))));
}
HierarchyChangedEvent.Broadcast(EventData);
}
}
void FDataLayerHierarchy::OnActorDescRemoved(FWorldPartitionActorDesc* InActorDesc)
{
if (!bShowUnloadedActors || (InActorDesc == nullptr))
{
return;
}
const UDataLayerSubsystem* const DataLayerSubsystem = UWorld::GetSubsystem<UDataLayerSubsystem>(GetOwningWorld());
const TArray<FName>& DataLayerInstanceNames = InActorDesc->GetDataLayerInstanceNames();
if (DataLayerSubsystem && DataLayerInstanceNames.Num() > 0)
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Removed;
EventData.ItemIDs.Reserve(DataLayerInstanceNames.Num());
TArray<AActor*> ParentActors = FDataLayerActorDescTreeItem::GetParentActors(InActorDesc->GetContainer());
for (const FName& DataLayerInstanceName : DataLayerInstanceNames)
{
const UDataLayerInstance* const DataLayer = DataLayerSubsystem->GetDataLayerInstance(DataLayerInstanceName);
EventData.ItemIDs.Add(FDataLayerActorDescTreeItem::ComputeTreeItemID(InActorDesc->GetGuid(), DataLayer, ParentActors));
}
HierarchyChangedEvent.Broadcast(EventData);
}
}
void FDataLayerHierarchy::FullRefreshEvent()
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::FullRefresh;
HierarchyChangedEvent.Broadcast(EventData);
}