// Copyright Epic Games, Inc. All Rights Reserved. #include "ActorHierarchy.h" #include "Engine/Engine.h" #include "Engine/World.h" #include "EngineUtils.h" #include "WorldTreeItem.h" #include "LevelTreeItem.h" #include "ActorTreeItem.h" #include "ActorDescTreeItem.h" #include "ComponentTreeItem.h" #include "ActorFolderTreeItem.h" #include "ISceneOutlinerMode.h" #include "ActorEditorUtils.h" #include "LevelUtils.h" #include "GameFramework/WorldSettings.h" #include "EditorActorFolders.h" #include "EditorFolderUtils.h" #include "LevelInstance/LevelInstanceInterface.h" #include "LevelInstance/LevelInstanceSubsystem.h" #include "Modules/ModuleManager.h" #include "WorldPartition/ActorDescContainerInstance.h" #include "WorldPartition/WorldPartitionActorDescInstance.h" #include "WorldPartition/WorldPartition.h" #include "WorldPartition/WorldPartitionHelpers.h" #include "WorldPartition/WorldPartitionActorDesc.h" #include "WorldPartition/IWorldPartitionEditorModule.h" #include "WorldPartition/WorldPartitionSubsystem.h" #include "ActorFolder.h" #include "ActorMode.h" #include "SSceneOutliner.h" TUniquePtr FActorHierarchy::Create(ISceneOutlinerMode* Mode, const TWeakObjectPtr& World) { FActorHierarchy* Hierarchy = new FActorHierarchy(Mode, World); Create_Internal(Hierarchy, World); return TUniquePtr(Hierarchy); } void FActorHierarchy::Create_Internal(FActorHierarchy* Hierarchy, const TWeakObjectPtr& World) { GEngine->OnLevelActorAdded().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorAdded); GEngine->OnLevelActorDeleted().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorDeleted); GEngine->OnLevelActorDetached().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorDetached); GEngine->OnLevelActorAttached().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorAttached); GEngine->OnLevelActorFolderChanged().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorFolderChanged); GEngine->OnLevelActorListChanged().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorListChanged); GEngine->OnActorFolderAdded().AddRaw(Hierarchy, &FActorHierarchy::OnActorFolderAdded); GEngine->OnActorFoldersUpdatedEvent().AddRaw(Hierarchy, &FActorHierarchy::OnActorFoldersUpdatedEvent); IWorldPartitionEditorModule::Get().OnWorldPartitionCreated().AddRaw(Hierarchy, &FActorHierarchy::OnWorldPartitionCreated); if (World.IsValid()) { if (World->PersistentLevel) { World->PersistentLevel->OnLoadedActorAddedToLevelEvent.AddRaw(Hierarchy, &FActorHierarchy::OnLoadedActorAdded); World->PersistentLevel->OnLoadedActorRemovedFromLevelEvent.AddRaw(Hierarchy, &FActorHierarchy::OnLoadedActorRemoved); } World->OnWorldPartitionInitialized().AddRaw(Hierarchy, &FActorHierarchy::OnWorldPartitionInitialized); World->OnWorldPartitionUninitialized().AddRaw(Hierarchy, &FActorHierarchy::OnWorldPartitionUninitialized); if (UWorldPartition* WorldPartition = World->GetWorldPartition()) { WorldPartition->OnActorDescInstanceAddedEvent.AddRaw(Hierarchy, &FActorHierarchy::OnActorDescInstanceAdded); WorldPartition->OnActorDescInstanceRemovedEvent.AddRaw(Hierarchy, &FActorHierarchy::OnActorDescInstanceRemoved); } } FWorldDelegates::LevelAddedToWorld.AddRaw(Hierarchy, &FActorHierarchy::OnLevelAdded); FWorldDelegates::LevelRemovedFromWorld.AddRaw(Hierarchy, &FActorHierarchy::OnLevelRemoved); auto& Folders = FActorFolders::Get(); Folders.OnFolderCreated.AddRaw(Hierarchy, &FActorHierarchy::OnBroadcastFolderCreate); Folders.OnFolderMoved.AddRaw(Hierarchy, &FActorHierarchy::OnBroadcastFolderMove); Folders.OnFolderDeleted.AddRaw(Hierarchy, &FActorHierarchy::OnBroadcastFolderDelete); } FActorHierarchy::FActorHierarchy(ISceneOutlinerMode* Mode, const TWeakObjectPtr& World) : ISceneOutlinerHierarchy(Mode) , RepresentingWorld(World) { } FActorHierarchy::~FActorHierarchy() { if (GEngine) { GEngine->OnLevelActorAdded().RemoveAll(this); GEngine->OnLevelActorDeleted().RemoveAll(this); GEngine->OnLevelActorDetached().RemoveAll(this); GEngine->OnLevelActorAttached().RemoveAll(this); GEngine->OnLevelActorFolderChanged().RemoveAll(this); GEngine->OnLevelActorListChanged().RemoveAll(this); GEngine->OnActorFolderAdded().RemoveAll(this); GEngine->OnActorFoldersUpdatedEvent().RemoveAll(this); } IWorldPartitionEditorModule::Get().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->OnActorDescInstanceAddedEvent.RemoveAll(this); WorldPartition->OnActorDescInstanceRemovedEvent.RemoveAll(this); } } FWorldDelegates::LevelAddedToWorld.RemoveAll(this); FWorldDelegates::LevelRemovedFromWorld.RemoveAll(this); auto& Folders = FActorFolders::Get(); Folders.OnFolderCreated.RemoveAll(this); Folders.OnFolderMoved.RemoveAll(this); Folders.OnFolderDeleted.RemoveAll(this); } FSceneOutlinerTreeItemPtr FActorHierarchy::FindOrCreateParentItem(const ISceneOutlinerTreeItem& Item, const TMap& Items, bool bCreate) { if (Item.IsA()) { return nullptr; } else if (const FActorTreeItem* ActorTreeItem = Item.CastTo()) { if (AActor* Actor = ActorTreeItem->Actor.Get()) { // Parent Actor (Actor attachement / parenting) if (AActor* ParentActor = Actor->GetSceneOutlinerParent()) { if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ParentActor)) { return *ParentItem; } // If Parent can be listed in SceneOutliner return nullptr so it gets created else if (ParentActor->IsListedInSceneOutliner()) { return bCreate ? CreateItemForActor(ParentActor, true) : nullptr; } } // Parent Folder FFolder Folder = Actor->GetFolder(); if (Mode->ShouldShowFolders() && !Folder.IsNone()) { if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(Folder)) { return *ParentItem; } else { return bCreate ? CreateItemForFolder(Folder, ActorTreeItem->Actor->GetWorld(), true) : nullptr; } } // Parent Level Instance if (ILevelInstanceInterface* OwningLevelInstance = Cast(Folder.GetRootObjectPtr())) { const ILevelInstanceInterface* LevelInstance = Cast(Actor); const bool bIsAnEditingLevelInstance = LevelInstance ? LevelInstance->IsEditing() : false; // Parent this to a LevelInstance if the parent LevelInstance is being edited or if this is a sub LevelInstance which is being edited if (bShowingLevelInstances || (OwningLevelInstance->IsEditing() || bIsAnEditingLevelInstance)) { AActor* OwningActor = CastChecked(OwningLevelInstance); if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(OwningActor)) { return *ParentItem; } else { return bCreate ? CreateItemForActor(OwningActor, true) : nullptr; } } } // Parent Level Using Actor Folders ULevel* OwningLevel = Cast(Folder.GetRootObjectPtr()); // For the persistent level, fallback on the world if (FActorMode::IsActorLevelDisplayable(OwningLevel)) { if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(OwningLevel)) { return *ParentItem; } else { return bCreate ? Mode->CreateItemFor(OwningLevel, true) : nullptr; } } } } else if (const FActorFolderTreeItem* FolderItem = Item.CastTo()) { // We should never call FindParents on a folder item if folders are not being shown check(Mode->ShouldShowFolders()); const FFolder ParentPath = FolderItem->GetFolder().GetParent(); // Parent Folder if (!ParentPath.IsNone()) { if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ParentPath)) { return *ParentItem; } else { return bCreate ? CreateItemForFolder(ParentPath, FolderItem->World, true) : nullptr; } } // Parent Level Instance else if (ILevelInstanceInterface* OwningLevelInstance = Cast(ParentPath.GetRootObjectPtr())) { if (bShowingLevelInstances || OwningLevelInstance->IsEditing()) { AActor* OwningActor = CastChecked(OwningLevelInstance); if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(OwningActor)) { return *ParentItem; } else { return bCreate ? CreateItemForActor(OwningActor, true) : nullptr; } } } // Parent Level Using Actor Folders else if (ULevel* OwningLevel = Cast(ParentPath.GetRootObjectPtr())) { if (FActorMode::IsActorLevelDisplayable(OwningLevel)) { if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(OwningLevel)) { return *ParentItem; } else { return bCreate ? Mode->CreateItemFor(OwningLevel, true) : nullptr; } } } } else if (const FComponentTreeItem* ComponentTreeItem = Item.CastTo()) { if (AActor* Owner = ComponentTreeItem->Component->GetOwner()) { if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(Owner)) { return *ParentItem; } else if (Owner->IsListedInSceneOutliner()) { return bCreate ? CreateItemForActor(Owner, true) : nullptr; } } // do not default to world on Component items return nullptr; } else if (const FActorDescTreeItem* ActorDescItem = Item.CastTo()) { if (const FWorldPartitionActorDescInstance* ActorDescInstance = *ActorDescItem->ActorDescHandle) { if (UWorld* RepresentingWorldPtr = RepresentingWorld.Get()) { const FFolder ActorDescFolder = FActorFolders::GetActorDescInstanceFolder(*RepresentingWorldPtr, ActorDescInstance); if (!ActorDescFolder.IsNone()) { if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ActorDescFolder)) { return *ParentItem; } else { return bCreate ? CreateItemForFolder(ActorDescFolder, RepresentingWorldPtr, true) : nullptr; } } } // Parent Actor (Actor attachement / parenting) const FGuid& ParentActorGuid = ActorDescInstance->GetSceneOutlinerParent(); if (ParentActorGuid.IsValid()) { if (UActorDescContainerInstance* ContainerInstance = ActorDescInstance->GetContainerInstance()) { if (const FWorldPartitionActorDescInstance* ParentActorDesc = ContainerInstance->GetActorDescInstance(ParentActorGuid)) { // If parent actor is loaded if (AActor* ParentActor = ParentActorDesc->GetActor()) { // Find loaded parent actor node (from the object ptr) if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ParentActor)) { return *ParentItem; } else { return bCreate ? CreateItemForActor(ParentActor, true) : nullptr; } } // Find unloaded parent actor node (from the guid) if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(FActorDescTreeItem::ComputeTreeItemID(ParentActorGuid, ContainerInstance))) { return *ParentItem; } else { return bCreate ? Mode->CreateItemFor(FActorDescTreeItem(ParentActorGuid, ContainerInstance)) : nullptr; } } } } else if (UActorDescContainerInstance* ContainerInstance = ActorDescInstance->GetContainerInstance()) { const ULevelInstanceSubsystem* LevelInstanceSubsystem = UWorld::GetSubsystem(RepresentingWorld.Get()); UWorld* OuterWorld = ContainerInstance->GetTypedOuter(); // If parent actor is loaded if (AActor* ParentActor = OuterWorld ? Cast(LevelInstanceSubsystem->GetOwningLevelInstance(OuterWorld->PersistentLevel)) : nullptr) { if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ParentActor)) { return *ParentItem; } else { return bCreate ? CreateItemForActor(ParentActor, true) : nullptr; } } // If parent actor is not loaded else if (UActorDescContainerInstance* ParentContainerInstance = Cast(ContainerInstance->GetOuter())) { if (const FWorldPartitionActorDescInstance* ParentActorDescInstance = ParentContainerInstance->GetActorDescInstance(ContainerInstance->GetContainerActorGuid())) { if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(FActorDescTreeItem::ComputeTreeItemID(ParentActorDescInstance->GetGuid(), ParentContainerInstance))) { return *ParentItem; } else { return bCreate ? CreateItemForActorDescInstance(ParentActorDescInstance, true) : nullptr; } } } } } } // If we get here return world item if (UWorld* RepresentingWorldPtr = RepresentingWorld.Get()) { if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(RepresentingWorldPtr)) { return *ParentItem; } else { return bCreate ? Mode->CreateItemFor(RepresentingWorldPtr, true) : nullptr; } } return nullptr; } void FActorHierarchy::CreateComponentItems(const AActor* Actor, TArray& OutItems) const { check(Actor); // Add all this actors components if showing components and the owning actor was created if (bShowingComponents) { for (UActorComponent* Component : Actor->GetComponents()) { if (Component != nullptr && !Component->GetClass()->HasAnyClassFlags(CLASS_Hidden)) { if (FSceneOutlinerTreeItemPtr ComponentItem = Mode->CreateItemFor(FComponentTreeItem(Component, bSearchComponentsByActorName))) { OutItems.Add(ComponentItem); } } } } } bool FActorHierarchy::IsShowingUnloadedActors() const { return bShowingUnloadedActors && RepresentingWorld.IsValid() && !RepresentingWorld->IsPlayInEditor(); } void FActorHierarchy::CreateFolderItems(UWorld* InWorld, TArray& OutItems) const { // In game world, create folder through FindOrCreateParentItem if (InWorld->IsGameWorld()) { return; } if (Mode->ShouldShowFolders() && bShowingEmptyFolders) { // Add any folders which might match the current search terms FActorFolders::Get().ForEachFolder(*InWorld, [this, &InWorld, &OutItems](const FFolder& Folder) { if (FSceneOutlinerTreeItemPtr FolderItem = CreateItemForFolder(Folder, InWorld)) { OutItems.Add(FolderItem); } return true; }); } } FSceneOutlinerTreeItemPtr FActorHierarchy::CreateItemForActor(AActor* InActor, bool bForce) const { return Mode->CreateItemFor(InActor, bForce); } FSceneOutlinerTreeItemPtr FActorHierarchy::CreateItemForActorDescInstance(const FWorldPartitionActorDescInstance* InActorDescInstance, bool bForce) const { return Mode->CreateItemFor(InActorDescInstance, bForce); } FSceneOutlinerTreeItemPtr FActorHierarchy::CreateItemForFolder(const FFolder& InFolder, const TWeakObjectPtr& InWorld, bool bForce) const { return Mode->CreateItemFor(FActorFolderTreeItem(InFolder, InWorld), bForce); } bool FActorHierarchy::CheckLevelInstanceEditing(UWorld* World, AActor* Actor) const { const ULevelInstanceSubsystem* LevelInstanceSubsystem = World->GetSubsystem(); // If we are not showing LevelInstances, LevelInstance sub actor items should not be created unless they belong to a LevelInstance which is being edited if (LevelInstanceSubsystem) { if (const ILevelInstanceInterface* ParentLevelInstance = LevelInstanceSubsystem->GetParentLevelInstance(Actor)) { if (!bShowingLevelInstances && !ParentLevelInstance->IsEditing()) { return false; } } } return true; } void FActorHierarchy::InsertActorItemAndCreateComponents(AActor* InActor, FSceneOutlinerTreeItemPtr ActorItem, TArray& OutItems) const { const int32 InsertLocation = OutItems.Num(); // Create all component items CreateComponentItems(InActor, OutItems); // If we are only showing actors with valid components, don't add the actor if no components were added if (bShowingOnlyActorWithValidComponents) { // If the number of items remained the same after component insertion no components were inserted if (OutItems.Num() == InsertLocation) { return; } } if (ActorItem) { // Add the actor before the components OutItems.Insert(ActorItem, InsertLocation); } } void FActorHierarchy::CreateUnloadedItems(UWorld* World, TArray& OutItems) const { TFunction AddChildContainer = [this, &OutItems, &AddChildContainer](const FWorldPartitionActorDescInstance* ActorDescInstance) { FWorldPartitionActorDesc::FContainerInstance SubContainerInstance; if (ActorDescInstance->GetChildContainerInstance(SubContainerInstance)) { for (FActorDescInstanceList::TIterator<> It(SubContainerInstance.ContainerInstance); It; ++It) { const FWorldPartitionActorDescInstance* SubActorDescInstance = *It; if (SubActorDescInstance && FActorDescTreeItem::ShouldDisplayInOutliner(SubActorDescInstance)) { if (FSceneOutlinerTreeItemPtr SubActorDescItem = Mode->CreateItemFor(SubActorDescInstance)) { OutItems.Add(SubActorDescItem); } AddChildContainer(SubActorDescInstance); } } } }; if (IsShowingUnloadedActors()) { if (UWorldPartitionSubsystem* WorldPartitionSubsystem = UWorld::GetSubsystem(World)) { const ULevelInstanceSubsystem* LevelInstanceSubsystem = World->GetSubsystem(); WorldPartitionSubsystem->ForEachWorldPartition([this, LevelInstanceSubsystem, &OutItems, &AddChildContainer](UWorldPartition* WorldPartition) { UWorld* OuterWorld = WorldPartition->GetTypedOuter(); ULevel* OuterLevel = OuterWorld ? OuterWorld->PersistentLevel : nullptr; // Skip unloaded actors if they are part of a non editing level instance and the outliner hides the content of level instances if (!bShowingLevelInstances) { ILevelInstanceInterface* LevelInstance = LevelInstanceSubsystem ? LevelInstanceSubsystem->GetOwningLevelInstance(OuterLevel) : nullptr; if (LevelInstance && !LevelInstance->IsEditing()) { return true; } } const TMap LoadedActors = FWorldPartitionHelpers::GetLoadedActorsForLevel(OuterLevel); const TMap RegisteredActors = FWorldPartitionHelpers::GetRegisteredActorsForLevel(OuterLevel); FWorldPartitionHelpers::ForEachActorDescInstance(WorldPartition, [this, &LoadedActors, &RegisteredActors, &OutItems, &AddChildContainer, LevelInstanceSubsystem](const FWorldPartitionActorDescInstance* ActorDescInstance) { if (ActorDescInstance && FActorDescTreeItem::ShouldDisplayInOutliner(ActorDescInstance)) { if (AActor* const* LoadedActor = LoadedActors.Find(ActorDescInstance->GetGuid())) { if (!IsValid(*LoadedActor) || RegisteredActors.Contains(ActorDescInstance->GetGuid())) { return true; } } UActorDescContainerInstance* ContainerInstance = ActorDescInstance->GetContainerInstance(); if (const FSceneOutlinerTreeItemPtr ActorDescItem = Mode->CreateItemFor(FActorDescTreeItem(ActorDescInstance->GetGuid(), ContainerInstance))) { OutItems.Add(ActorDescItem); } if (bShowingLevelInstances) { // If parent actor is not loaded, recurse UWorld* OuterWorld = ContainerInstance->GetTypedOuter(); if (AActor* ParentActor = OuterWorld ? Cast(LevelInstanceSubsystem->GetOwningLevelInstance(OuterWorld->PersistentLevel)) : nullptr; !ParentActor) { AddChildContainer(ActorDescInstance); } } } return true; }); return true; }); } } } void FActorHierarchy::CreateWorldChildren(UWorld* World, TArray& OutItems) const { TRACE_CPUPROFILER_EVENT_SCOPE(FActorHierarchy::CreateWorldChildren); check(World); CreateFolderItems(World, OutItems); // Create all actor items for (FActorIterator ActorIt(World); ActorIt; ++ActorIt) { AActor* Actor = *ActorIt; if (!CheckLevelInstanceEditing(World, Actor)) { continue; } FSceneOutlinerTreeItemPtr ActorItem = CreateItemForActor(Actor); InsertActorItemAndCreateComponents(Actor, ActorItem, OutItems); } CreateUnloadedItems(World, OutItems); } void FActorHierarchy::CreateItems(TArray& OutItems) const { TRACE_CPUPROFILER_EVENT_SCOPE(FActorHierarchy::CreateItems); if (RepresentingWorld.IsValid()) { UWorld* RepresentingWorldPtr = RepresentingWorld.Get(); check(RepresentingWorldPtr); if (FSceneOutlinerTreeItemPtr WorldItem = Mode->CreateItemFor(RepresentingWorldPtr)) { OutItems.Add(WorldItem); } // Create world children regardless of if a world item was created CreateWorldChildren(RepresentingWorldPtr, OutItems); } } void FActorHierarchy::CreateChildren(const FSceneOutlinerTreeItemPtr& Item, TArray& OutChildren) const { auto CreateChildrenFolders = [this](UWorld* InWorld, const FFolder& InParentFolder, const FFolder::FRootObject& InFolderRootObject, TArray& OutChildren) { // In game world, create folder through FindOrCreateParentItem if (InWorld->IsGameWorld()) { return; } FActorFolders::Get().ForEachFolderWithRootObject(*InWorld, InFolderRootObject, [this, InWorld, &InParentFolder, &OutChildren](const FFolder& Folder) { if (Folder.IsChildOf(InParentFolder)) { if (FSceneOutlinerTreeItemPtr NewFolderItem = CreateItemForFolder(Folder, InWorld)) { OutChildren.Add(NewFolderItem); } } return true; }); }; UWorld* World = RepresentingWorld.Get(); if (FWorldTreeItem* WorldItem = Item->CastTo()) { check(WorldItem->World == RepresentingWorld); CreateWorldChildren(WorldItem->World.Get(), OutChildren); } else if (const FActorTreeItem* ParentActorItem = Item->CastTo()) { AActor* ParentActor = ParentActorItem->Actor.Get(); check(ParentActor->GetWorld() == RepresentingWorld); CreateComponentItems(ParentActor, OutChildren); TArray ChildActors; if (const ILevelInstanceInterface* LevelInstanceParent = Cast(ParentActor)) { const ULevelInstanceSubsystem* LevelInstanceSubsystem = RepresentingWorld->GetSubsystem(); check(LevelInstanceSubsystem); LevelInstanceSubsystem->ForEachActorInLevelInstance(LevelInstanceParent, [this, LevelInstanceParent, LevelInstanceSubsystem, &ChildActors](AActor* SubActor) { const ILevelInstanceInterface* LevelInstance = Cast(SubActor); const bool bIsAnEditingLevelInstance = LevelInstance ? LevelInstanceSubsystem->IsEditingLevelInstance(LevelInstance) : false; if (bShowingLevelInstances || (LevelInstanceSubsystem->IsEditingLevelInstance(LevelInstanceParent) || bIsAnEditingLevelInstance)) { ChildActors.Add(SubActor); } return true; }); check(World == CastChecked(LevelInstanceParent)->GetWorld()); FFolder ParentFolder = ParentActor->GetFolder(); CreateChildrenFolders(World, ParentFolder, ParentActor, OutChildren); } else { // NOTE: GetAttachedActors can sometimes return the actor itself or its parent depending on how the component ownership is setup // but we don't have to check for that here because the Outliner will simply discard duplicates ParentActor->GetAttachedActors(ChildActors, true, true); } for (auto ChildActor : ChildActors) { if (FSceneOutlinerTreeItemPtr ChildActorItem = CreateItemForActor(ChildActor)) { OutChildren.Add(ChildActorItem); CreateComponentItems(ChildActor, OutChildren); } } } else if (FActorFolderTreeItem* FolderItem = Item->CastTo()) { check(Mode->ShouldShowFolders()); check(World == FolderItem->World.Get()); FFolder ParentFolder = FolderItem->GetFolder(); check(!ParentFolder.IsNone()); CreateChildrenFolders(World, ParentFolder, ParentFolder.GetRootObject(), OutChildren); } } void FActorHierarchy::FullRefreshEvent() { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::FullRefresh; HierarchyChangedEvent.Broadcast(EventData); } void FActorHierarchy::OnWorldPartitionCreated(UWorld* InWorld) { if (RepresentingWorld.Get() == InWorld) { FullRefreshEvent(); } } void FActorHierarchy::OnLevelActorAdded(AActor* InActor) { if (InActor != nullptr && RepresentingWorld.Get() == InActor->GetWorld()) { // CreateItemForActor already checks this by calling FActorMode::IsActorDisplayable, but that is after the actor tree item has // already been created so this is a small optimization to avoid creating it and then calling Actor->GetActorLabel() which can be expensive if (InActor->IsListedInSceneOutliner()) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Added; EventData.Items.Add(CreateItemForActor(InActor)); HierarchyChangedEvent.Broadcast(EventData); } } } void FActorHierarchy::OnLevelActorDeleted(AActor* InActor) { if (InActor != nullptr && RepresentingWorld.Get() == InActor->GetWorld()) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Removed; EventData.ItemIDs.Add(InActor); HierarchyChangedEvent.Broadcast(EventData); } } void FActorHierarchy::OnLevelActorAttached(AActor* InActor, const AActor* InParent) { if (InActor != nullptr && RepresentingWorld.Get() == InActor->GetWorld()) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Moved; EventData.ItemIDs.Add(InActor); HierarchyChangedEvent.Broadcast(EventData); } } void FActorHierarchy::OnLevelActorDetached(AActor* InActor, const AActor* InParent) { if (InActor != nullptr && RepresentingWorld.Get() == InActor->GetWorld()) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Moved; EventData.ItemIDs.Add(InActor); HierarchyChangedEvent.Broadcast(EventData); } } void FActorHierarchy::RemoveActorDesc(AActor& InActor) { // Loaded actors can be in sub-world partitions so we use the outer world if (UWorldPartition* WorldPartition = FWorldPartitionHelpers::GetWorldPartition(&InActor)) { const FGuid& ActorGuid = InActor.GetActorGuid(); if (FWorldPartitionActorDescInstance* ActorDescInstance = WorldPartition->GetActorDescInstance(ActorGuid)) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Removed; EventData.ItemIDs.Add(FActorDescTreeItem::ComputeTreeItemID(ActorGuid, ActorDescInstance->GetContainerInstance())); HierarchyChangedEvent.Broadcast(EventData); } } } void FActorHierarchy::AddActorDesc(AActor& InActor) { if (IsShowingUnloadedActors()) { if (UWorldPartition* WorldPartition = FWorldPartitionHelpers::GetWorldPartition(&InActor)) { const FGuid& ActorGuid = InActor.GetActorGuid(); if (FWorldPartitionActorDescInstance* ActorDescInstance = WorldPartition->GetActorDescInstance(ActorGuid); FActorDescTreeItem::ShouldDisplayInOutliner(ActorDescInstance)) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Added; EventData.Items.Add(Mode->CreateItemFor(FActorDescTreeItem(ActorDescInstance->GetGuid(), ActorDescInstance->GetContainerInstance()))); HierarchyChangedEvent.Broadcast(EventData); } } } } void FActorHierarchy::OnLoadedActorAdded(AActor& InActor) { OnLevelActorAdded(&InActor); RemoveActorDesc(InActor); } void FActorHierarchy::OnLoadedActorRemoved(AActor& InActor) { OnLevelActorDeleted(&InActor); AddActorDesc(InActor); } void FActorHierarchy::OnActorDescInstanceAdded(FWorldPartitionActorDescInstance* InActorDescInstance) { if (IsShowingUnloadedActors() && InActorDescInstance && !InActorDescInstance->IsLoaded(true) && FActorDescTreeItem::ShouldDisplayInOutliner(InActorDescInstance)) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Added; EventData.Items.Add(Mode->CreateItemFor(FActorDescTreeItem(InActorDescInstance->GetGuid(), InActorDescInstance->GetContainerInstance()))); HierarchyChangedEvent.Broadcast(EventData); } } void FActorHierarchy::OnActorDescInstanceRemoved(FWorldPartitionActorDescInstance* InActorDescInstance) { if (IsShowingUnloadedActors() && InActorDescInstance) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Removed; EventData.ItemIDs.Add(FActorDescTreeItem::ComputeTreeItemID(InActorDescInstance->GetGuid(), InActorDescInstance->GetContainerInstance())); HierarchyChangedEvent.Broadcast(EventData); } } void FActorHierarchy::OnWorldPartitionInitialized(UWorldPartition* InWorldPartition) { FullRefreshEvent(); } void FActorHierarchy::OnWorldPartitionUninitialized(UWorldPartition* InWorldPartition) { FullRefreshEvent(); } void FActorHierarchy::OnComponentsUpdated() { FullRefreshEvent(); } void FActorHierarchy::OnLevelActorListChanged() { FullRefreshEvent(); } void FActorHierarchy::OnActorFoldersUpdatedEvent(ULevel* InLevel) { FullRefreshEvent(); } void FActorHierarchy::OnActorFolderAdded(UActorFolder* InActorFolder) { check(InActorFolder); ULevel* Level = InActorFolder->GetOuterULevel(); if (Level && Mode->ShouldShowFolders() && (RepresentingWorld.Get() == Level->GetWorld())) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Added; EventData.Items.Add(CreateItemForFolder(InActorFolder->GetFolder(), RepresentingWorld)); HierarchyChangedEvent.Broadcast(EventData); } } void FActorHierarchy::OnLevelAdded(ULevel* InLevel, UWorld* InWorld) { if (InLevel != nullptr && RepresentingWorld.Get() == InWorld) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Added; EventData.Items.Reserve(InLevel->Actors.Num()); for (AActor* Actor : InLevel->Actors) { if (Actor != nullptr) { EventData.Items.Add(CreateItemForActor(Actor)); } } HierarchyChangedEvent.Broadcast(EventData); } } void FActorHierarchy::RemoveLevelActorFolders(ULevel* InLevel, UWorld* InWorld) { // If either this level or the owning world are using actor folders, remove level's actor folders if (InLevel->IsUsingActorFolders() || InWorld->PersistentLevel->IsUsingActorFolders()) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Removed; EventData.ItemIDs.Add(InLevel); HierarchyChangedEvent.Broadcast(EventData); } } void FActorHierarchy::OnLevelRemoved(ULevel* InLevel, UWorld* InWorld) { if (InLevel != nullptr && RepresentingWorld.Get() == InWorld) { { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Removed; EventData.ItemIDs.Reserve(InLevel->Actors.Num()); for (AActor* Actor : InLevel->Actors) { if (Actor != nullptr) { EventData.ItemIDs.Add(Actor); } } HierarchyChangedEvent.Broadcast(EventData); } RemoveLevelActorFolders(InLevel, InWorld); } } /** Called when a folder is to be created */ void FActorHierarchy::OnBroadcastFolderCreate(UWorld& InWorld, const FFolder& InNewFolder) { if (Mode->ShouldShowFolders() && RepresentingWorld.Get() == &InWorld) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Added; EventData.Items.Add(CreateItemForFolder(InNewFolder, &InWorld)); EventData.ItemActions = SceneOutliner::ENewItemAction::Select | SceneOutliner::ENewItemAction::Rename; HierarchyChangedEvent.Broadcast(EventData); } } /** Called when a folder is to be moved */ void FActorHierarchy::OnBroadcastFolderMove(UWorld& InWorld, const FFolder& InOldFolder, const FFolder& InNewFolder) { if (Mode->ShouldShowFolders() && RepresentingWorld.Get() == &InWorld) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::FolderMoved; EventData.ItemIDs.Add(InOldFolder); EventData.NewPaths.Add(InNewFolder); HierarchyChangedEvent.Broadcast(EventData); } } /** Called when a folder is to be deleted */ void FActorHierarchy::OnBroadcastFolderDelete(UWorld& InWorld, const FFolder& InFolder) { if (Mode->ShouldShowFolders() && RepresentingWorld.Get() == &InWorld) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Removed; EventData.ItemIDs.Add(InFolder); HierarchyChangedEvent.Broadcast(EventData); } } void FActorHierarchy::OnLevelActorFolderChanged(const AActor* InActor, FName OldPath) { if (Mode->ShouldShowFolders() && RepresentingWorld.Get() == InActor->GetWorld()) { FSceneOutlinerHierarchyChangedData EventData; EventData.Type = FSceneOutlinerHierarchyChangedData::Moved; EventData.ItemIDs.Add(FSceneOutlinerTreeItemID(InActor)); HierarchyChangedEvent.Broadcast(EventData); } }