// Copyright Epic Games, Inc. All Rights Reserved. #include "SceneOutlinerModule.h" #include "Modules/ModuleManager.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Framework/Application/SlateApplication.h" #include "SSceneOutliner.h" #include "Algo/Find.h" #include "SceneOutlinerActorInfoColumn.h" #include "SceneOutlinerGutter.h" #include "SceneOutlinerItemLabelColumn.h" #include "SceneOutlinerSourceControlColumn.h" #include "SceneOutlinerPinnedColumn.h" #include "SceneOutlinerTextInfoColumn.h" #include "SceneOutlinerUnsavedColumn.h" #include "SceneOutlinerPublicTypes.h" #include "ActorPickingMode.h" #include "ActorBrowsingMode.h" #include "ActorBrowsingModeCommands.h" #include "ActorTreeItem.h" #include "ActorFolderTreeItem.h" #include "ComponentTreeItem.h" #include "ActorDescTreeItem.h" #include "FolderTreeItem.h" #include "WorldTreeItem.h" #include "WorldPartition/ActorDescContainerInstance.h" #include "WorldPartition/WorldPartitionActorDescInstance.h" #include "WorldPartition/DataLayer/DataLayerInstance.h" #include "WorldPartition/DataLayer/DataLayerManager.h" #include "WorldPartition/DataLayer/ExternalDataLayerAsset.h" #include "WorldPartition/ContentBundle/ContentBundleEngineSubsystem.h" #include "WorldPartition/ContentBundle/ContentBundleDescriptor.h" #include "LevelInstance/LevelInstanceSubsystem.h" #include "LevelInstance/LevelInstanceInterface.h" #include "Editor.h" #include "ISourceControlModule.h" #include "EditorModeManager.h" #include "LevelEditor.h" #define LOCTEXT_NAMESPACE "SceneOutlinerModule" /* FSceneOutlinerModule interface *****************************************************************************/ FSceneOutlinerModule::FSceneOutlinerModule() : ColumnPermissionList(MakeShareable(new FNamePermissionList())) { ColumnPermissionList->OnFilterChanged().AddLambda([this]() { ColumnPermissionListChanged.Broadcast(); }); } void FSceneOutlinerModule::StartupModule() { RegisterDefaultColumnType< FSceneOutlinerItemLabelColumn >(FSceneOutlinerColumnInfo(ESceneOutlinerColumnVisibility::Visible, 10, FCreateSceneOutlinerColumn(), false, TOptional(), FSceneOutlinerBuiltInColumnTypes::Label_Localized())); // Register builtin column types which are not active by default RegisterColumnType(); RegisterColumnType(); RegisterColumnType(); RegisterColumnType(); RegisterColumnType(); // Register Commands FActorBrowsingModeCommands::Register(); } void FSceneOutlinerModule::ShutdownModule() { UnRegisterColumnType(); UnRegisterColumnType(); UnRegisterColumnType(); UnRegisterColumnType(); UnRegisterColumnType(); UnRegisterColumnType(); // Un-Register Commands FActorBrowsingModeCommands::Unregister(); } TSharedRef FSceneOutlinerModule::CreateSceneOutliner(const FSceneOutlinerInitializationOptions& InitOptions) const { TRACE_CPUPROFILER_EVENT_SCOPE(FSceneOutlinerModule::CreateSceneOutliner); return SNew(SSceneOutliner, InitOptions) .IsEnabled(FSlateApplication::Get().GetNormalExecutionAttribute()); } TSharedRef FSceneOutlinerModule::CreateActorPicker(const FSceneOutlinerInitializationOptions& InInitOptions, const FOnActorPicked& OnActorPickedDelegate, TWeakObjectPtr SpecifiedWorld, bool bInHideLevelInstanceHierarchy) const { FSceneOutlinerInitializationOptions InitOptions(InInitOptions); if (!InitOptions.ModeFactory.IsBound()) { auto OnItemPicked = FOnSceneOutlinerItemPicked::CreateLambda([OnActorPickedDelegate](TSharedRef Item) { if (FActorTreeItem* ActorItem = Item->CastTo()) { if (ActorItem->IsValid()) { OnActorPickedDelegate.ExecuteIfBound(ActorItem->Actor.Get()); } } }); FCreateSceneOutlinerMode ModeFactory = FCreateSceneOutlinerMode::CreateLambda([OnItemPicked, SpecifiedWorld, bInHideLevelInstanceHierarchy](SSceneOutliner* Outliner) { FActorModeParams Params; Params.SceneOutliner = Outliner; Params.SpecifiedWorldToDisplay = SpecifiedWorld; Params.bHideComponents = true; Params.bHideLevelInstanceHierarchy = bInHideLevelInstanceHierarchy; Params.bHideUnloadedActors = true; Params.bHideEmptyFolders = true; return new FActorPickingMode(Params, OnItemPicked); }); InitOptions.ModeFactory = ModeFactory; } if (InitOptions.ColumnMap.Num() == 0) { InitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::Label(), FSceneOutlinerColumnInfo(ESceneOutlinerColumnVisibility::Visible, 0, FCreateSceneOutlinerColumn(), false, TOptional(), FSceneOutlinerBuiltInColumnTypes::Label_Localized())); InitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::ActorInfo(), FSceneOutlinerColumnInfo(ESceneOutlinerColumnVisibility::Visible, 10, FCreateSceneOutlinerColumn(), true, TOptional(), FSceneOutlinerBuiltInColumnTypes::ActorInfo_Localized())); InitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::Pinned(), FSceneOutlinerColumnInfo(ESceneOutlinerColumnVisibility::Invisible, 5, FCreateSceneOutlinerColumn(), true, TOptional(), FSceneOutlinerBuiltInColumnTypes::Pinned_Localized())); CreateActorInfoColumns(InitOptions); } return CreateSceneOutliner(InitOptions); } TSharedRef FSceneOutlinerModule::CreateComponentPicker(const FSceneOutlinerInitializationOptions& InInitOptions, const FOnComponentPicked& OnComponentPickedDelegate, TWeakObjectPtr SpecifiedWorld) const { auto OnItemPicked = FOnSceneOutlinerItemPicked::CreateLambda([OnComponentPickedDelegate](TSharedRef Item) { if (FComponentTreeItem* ComponentItem = Item->CastTo()) { if (ComponentItem->IsValid()) { OnComponentPickedDelegate.ExecuteIfBound(ComponentItem->Component.Get()); } } }); FCreateSceneOutlinerMode ModeFactory = FCreateSceneOutlinerMode::CreateLambda([&OnItemPicked, &SpecifiedWorld](SSceneOutliner* Outliner) { FActorModeParams Params; Params.SceneOutliner = Outliner; Params.SpecifiedWorldToDisplay = SpecifiedWorld; Params.bHideComponents = false; Params.bHideActorWithNoComponent = true; Params.bHideLevelInstanceHierarchy = true; Params.bHideUnloadedActors = true; Params.bHideEmptyFolders = true; Params.bSearchComponentsByActorName = true; return new FActorPickingMode(Params, OnItemPicked); }); FSceneOutlinerInitializationOptions InitOptions(InInitOptions); InitOptions.ModeFactory = ModeFactory; if (InitOptions.ColumnMap.Num() == 0) { InitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::Label(), FSceneOutlinerColumnInfo(ESceneOutlinerColumnVisibility::Visible, 0, FCreateSceneOutlinerColumn(), false, TOptional(), FSceneOutlinerBuiltInColumnTypes::Label_Localized())); InitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::ActorInfo(), FSceneOutlinerColumnInfo(ESceneOutlinerColumnVisibility::Visible, 10, FCreateSceneOutlinerColumn(), true, TOptional(), FSceneOutlinerBuiltInColumnTypes::ActorInfo_Localized())); CreateActorInfoColumns(InitOptions); } return CreateSceneOutliner(InitOptions); } TSharedRef< ISceneOutliner > FSceneOutlinerModule::CreateActorBrowser(const FSceneOutlinerInitializationOptions& InInitOptions, TWeakObjectPtr SpecifiedWorld) const { FCreateSceneOutlinerMode ModeFactory = FCreateSceneOutlinerMode::CreateLambda([&SpecifiedWorld](SSceneOutliner* Outliner) { return new FActorBrowsingMode(Outliner, SpecifiedWorld); }); FSceneOutlinerInitializationOptions InitOptions(InInitOptions); InitOptions.ModeFactory = ModeFactory; if (InitOptions.ColumnMap.Num() == 0) { CreateActorBrowserColumns(InitOptions); } return CreateSceneOutliner(InitOptions); } TSharedPtr FSceneOutlinerModule::CreateCustomRegisteredOutliner(FName ID, FSceneOutlinerInitializationOptions InInitOptions) { FSceneOutlinerFactory* FoundInitOptionsFactory = CustomOutlinerFactories.Find(ID); if(!FoundInitOptionsFactory) { return nullptr; } return FoundInitOptionsFactory->Execute(InInitOptions); } void FSceneOutlinerModule::RegisterCustomSceneOutlinerFactory(FName ID, FSceneOutlinerFactory InOutlinerFactory) { CustomOutlinerFactories.Add(ID, InOutlinerFactory); } void FSceneOutlinerModule::UnregisterCustomSceneOutlinerFactory(FName ID) { CustomOutlinerFactories.Remove(ID); } bool FSceneOutlinerModule::IsCustomSceneOutlinerFactoryRegistered(FName ID) { return CustomOutlinerFactories.Contains(ID); } void FSceneOutlinerModule::CreateActorBrowserColumns(FSceneOutlinerInitializationOptions& InInitOptions, UWorld* InWorld) const { if(!InWorld) { // Query the Level Editor to get the correct world based on context TWeakPtr LevelEditor = FModuleManager::GetModuleChecked(TEXT("LevelEditor")).GetLevelEditorInstance(); if (TSharedPtr LevelEditorPin = LevelEditor.Pin()) { InWorld = LevelEditorPin->GetEditorModeManager().GetWorld(); } } InInitOptions.UseDefaultColumns(); InInitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::Gutter(), FSceneOutlinerColumnInfo(ESceneOutlinerColumnVisibility::Visible, 0, FCreateSceneOutlinerColumn(), true, TOptional(), FSceneOutlinerBuiltInColumnTypes::Gutter_Localized())); InInitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::ActorInfo(), FSceneOutlinerColumnInfo(ESceneOutlinerColumnVisibility::Visible, 20, FCreateSceneOutlinerColumn(), true, TOptional(), FSceneOutlinerBuiltInColumnTypes::ActorInfo_Localized())); CreateWorldPartitionColumns(InInitOptions, InWorld); CreateActorInfoColumns(InInitOptions, InWorld); } void FSceneOutlinerModule::CreateWorldPartitionColumns(FSceneOutlinerInitializationOptions& InInitOptions, UWorld* WorldPtr) const { ESceneOutlinerColumnVisibility SourceControlColumnVisibility = ESceneOutlinerColumnVisibility::Invisible; ISourceControlModule& SourceControlModule = ISourceControlModule::Get(); if (SourceControlModule.IsEnabled()) { if (WorldPtr && WorldPtr->PersistentLevel->IsUsingExternalActors()) { // The source control column should be visible by default in source-controlled levels using external actors SourceControlColumnVisibility = ESceneOutlinerColumnVisibility::Visible; } } InInitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::SourceControl(), FSceneOutlinerColumnInfo(SourceControlColumnVisibility, 30, FCreateSceneOutlinerColumn(), true, TOptional(), FSceneOutlinerBuiltInColumnTypes::SourceControl_Localized())); ESceneOutlinerColumnVisibility UnsavedColumnVisibility = ESceneOutlinerColumnVisibility::Invisible; if (WorldPtr && WorldPtr->IsPartitionedWorld()) { // We don't want the pinned column in non wp levels InInitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::Pinned(), FSceneOutlinerColumnInfo(ESceneOutlinerColumnVisibility::Visible, 5, FCreateSceneOutlinerColumn(), true, TOptional(), FSceneOutlinerBuiltInColumnTypes::Pinned_Localized())); // We want the unsaved column to be visible by default in partitioned levels UnsavedColumnVisibility = ESceneOutlinerColumnVisibility::Visible; } InInitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::Unsaved(), FSceneOutlinerColumnInfo(UnsavedColumnVisibility, 1, FCreateSceneOutlinerColumn(), true, TOptional(), FSceneOutlinerBuiltInColumnTypes::Unsaved_Localized())); } void FSceneOutlinerModule::CreateActorInfoColumns(FSceneOutlinerInitializationOptions& InInitOptions, UWorld *WorldPtr) const { FGetTextForItem MobilityInfoText = FGetTextForItem::CreateLambda([](const ISceneOutlinerTreeItem& Item) -> FString { if (const FActorTreeItem* ActorItem = Item.CastTo()) { AActor* Actor = ActorItem->Actor.Get(); if (!Actor) { return FString(); } FString Result; USceneComponent* RootComponent = Actor->GetRootComponent(); if (RootComponent) { if (RootComponent->Mobility == EComponentMobility::Static) { Result = FString(TEXT("Static")); } if (RootComponent->Mobility == EComponentMobility::Stationary) { Result = FString(TEXT("Stationary")); } else if (RootComponent->Mobility == EComponentMobility::Movable) { Result = FString(TEXT("Movable")); } } return Result; } return FString(); }); FGetTextForItem LayerInfoText = FGetTextForItem::CreateLambda([](const ISceneOutlinerTreeItem& Item) -> FString { FString Result; if (const FActorTreeItem* ActorItem = Item.CastTo()) { AActor* Actor = ActorItem->Actor.Get(); if (!Actor) { return FString(); } for (const auto& Layer : Actor->Layers) { if (Result.Len()) { Result += TEXT(", "); } Result += Layer.ToString(); } } return Result; }); FGetTextForItem DataLayerInfoText = FGetTextForItem::CreateLambda([](const ISceneOutlinerTreeItem& Item) -> FString { TStringBuilder<128> Builder; TSet DataLayerShortNames; auto BuildDataLayers = [&Builder, &DataLayerShortNames](const auto& DataLayerInstances, bool bPartOfOtherLevel) { if (const UDataLayerInstance* const* ExternalDataLayerInstance = !bPartOfOtherLevel ? Algo::FindByPredicate(DataLayerInstances, [](const UDataLayerInstance* DataLayerInstance) { return DataLayerInstance->IsA(); }) : nullptr) { Builder += (*ExternalDataLayerInstance)->GetDataLayerShortName(); } for (const UDataLayerInstance* DataLayerInstance : DataLayerInstances) { if (!DataLayerInstance->IsA()) { bool bIsAlreadyInSet = false; DataLayerShortNames.Add(DataLayerInstance->GetDataLayerShortName(), &bIsAlreadyInSet); if (!bIsAlreadyInSet) { if (Builder.Len()) { Builder += TEXT(", "); } // Put a '*' in front of DataLayers that are not part of of the main world if (bPartOfOtherLevel) { Builder += "*"; } Builder += DataLayerInstance->GetDataLayerShortName(); } } } }; auto BuildDataLayersWithContext = [BuildDataLayers](const ISceneOutlinerTreeItem& Item, bool bUseLevelContext) { if (const FActorTreeItem* ActorItem = Item.CastTo()) { if (AActor* Actor = ActorItem->Actor.Get()) { BuildDataLayers(bUseLevelContext ? Actor->GetDataLayerInstancesForLevel() : Actor->GetDataLayerInstances(), bUseLevelContext); } } else if (const FActorDescTreeItem* ActorDescItem = Item.CastTo()) { if (const FWorldPartitionActorDescInstance* ActorDescInstance = *ActorDescItem->ActorDescHandle; ActorDescInstance && !ActorDescInstance->GetDataLayerInstanceNames().IsEmpty()) { if (const UActorDescContainerInstance* ActorDescContainerInstance = ActorDescInstance->GetContainerInstance()) { const UWorld* OwningWorld = ActorDescContainerInstance->GetTopWorldPartition()->GetWorld(); if (const UDataLayerManager* DataLayerManager = UDataLayerManager::GetDataLayerManager(OwningWorld)) { TSet DataLayerInstances; DataLayerInstances.Append(DataLayerManager->GetDataLayerInstances(ActorDescInstance->GetDataLayerInstanceNames().ToArray())); if (ULevelInstanceSubsystem* LevelInstanceSubsystem = UWorld::GetSubsystem(OwningWorld)) { UWorld* OuterWorld = ActorDescContainerInstance->GetTypedOuter(); // Add parent container Data Layer Instances AActor* CurrentActor = OuterWorld ? Cast(LevelInstanceSubsystem->GetOwningLevelInstance(OuterWorld->PersistentLevel)) : nullptr; while (CurrentActor) { DataLayerInstances.Append(bUseLevelContext ? CurrentActor->GetDataLayerInstancesForLevel() : CurrentActor->GetDataLayerInstances()); CurrentActor = Cast(LevelInstanceSubsystem->GetParentLevelInstance(CurrentActor)); }; } BuildDataLayers(DataLayerInstances, bUseLevelContext); } } } } }; // List Actor's DataLayers part of the owning world, then those only part of the actor level BuildDataLayersWithContext(Item, false); BuildDataLayersWithContext(Item, true); return Builder.ToString(); }); FGetTextForItem ContentBundleInfoText = FGetTextForItem::CreateLambda([](const ISceneOutlinerTreeItem& Item)->FString { UContentBundleEngineSubsystem* ContentBundleEngineSubsystem = UContentBundleEngineSubsystem::Get(); if (const FActorTreeItem* ActorItem = Item.CastTo()) { if (AActor* Actor = ActorItem->Actor.Get(); Actor && Actor->GetContentBundleGuid().IsValid()) { if (const UContentBundleDescriptor* Descriptor = ContentBundleEngineSubsystem->GetContentBundleDescriptor(Actor->GetContentBundleGuid())) { return Descriptor->GetDisplayName(); } } } else if (const FActorDescTreeItem* ActorDescItem = Item.CastTo()) { if (const FWorldPartitionActorDescInstance* ActorDescInstance = *ActorDescItem->ActorDescHandle; ActorDescInstance && ActorDescInstance->GetContentBundleGuid().IsValid()) { if (const UContentBundleDescriptor* Descriptor = ContentBundleEngineSubsystem->GetContentBundleDescriptor(ActorDescInstance->GetContentBundleGuid())) { return Descriptor->GetDisplayName(); } } } return TEXT(""); }); FGetTextForItem SubPackageInfoText = FGetTextForItem::CreateLambda([](const ISceneOutlinerTreeItem& Item) -> FString { if (const FActorTreeItem* ActorItem = Item.CastTo()) { if (AActor* Actor = ActorItem->Actor.Get()) { if (const ILevelInstanceInterface* ActorAsLevelInstance = Cast(Actor)) { return ActorAsLevelInstance->GetWorldAssetPackage(); } } } else if (const FActorDescTreeItem* ActorDescItem = Item.CastTo()) { if (const FWorldPartitionActorDescInstance* ActorDescInstance = *ActorDescItem->ActorDescHandle) { if (FName LevelPackage = ActorDescInstance->GetChildContainerPackage(); !LevelPackage.IsNone()) { return ActorDescInstance->GetChildContainerPackage().ToString(); } } } return FString(); }); FGetTextForItem SocketInfoText = FGetTextForItem::CreateLambda([](const ISceneOutlinerTreeItem& Item) -> FString { if (const FActorTreeItem* ActorItem = Item.CastTo()) { AActor* Actor = ActorItem->Actor.Get(); if (Actor) { return Actor->GetAttachParentSocketName().ToString(); } } return FString(); }); FGetTextForItem InternalNameInfoText = FGetTextForItem::CreateLambda([](const ISceneOutlinerTreeItem& Item) -> FString { if (const FActorTreeItem* ActorItem = Item.CastTo()) { if (AActor* Actor = ActorItem->Actor.Get()) { return Actor->GetName(); } } else if (const FComponentTreeItem* ComponentItem = Item.CastTo()) { if (UActorComponent* Component = ComponentItem->Component.Get()) { return Component->GetName(); } } else if (const FActorDescTreeItem* ActorDescItem = Item.CastTo()) { if (const FWorldPartitionActorDescInstance* ActorDescInstance = *ActorDescItem->ActorDescHandle) { return ActorDescInstance->GetActorNameString(); } } else if (const FActorFolderTreeItem* ActorFolderItem = Item.CastTo()) { if (const UActorFolder* ActorFolder = Cast(ActorFolderItem->GetActorFolder())) { return ActorFolder->GetName(); } } return FString(); }); FGetTextForItem LevelInfoText = FGetTextForItem::CreateLambda([](const ISceneOutlinerTreeItem& Item) -> FString { if (const FActorTreeItem* ActorItem = Item.CastTo()) { if (AActor* Actor = ActorItem->Actor.Get()) { return FPackageName::GetShortName(Actor->GetPackage()->GetName()); } } else if (const FActorDescTreeItem* ActorDescItem = Item.CastTo()) { if (const FWorldPartitionActorDescInstance* ActorDescInstance = *ActorDescItem->ActorDescHandle) { return FPackageName::GetShortName(ActorDescInstance->GetActorPackage()); } } else if (const FActorFolderTreeItem* ActorFolderItem = Item.CastTo()) { if (const UActorFolder* ActorFolder = Cast(ActorFolderItem->GetActorFolder())) { return FPackageName::GetShortName(ActorFolder->GetPackage()->GetName()); } } return FString(); }); FGetTextForItem UncachedLightsInfoText = FGetTextForItem::CreateLambda([](const ISceneOutlinerTreeItem& Item) -> FString { if (const FActorTreeItem* ActorItem = Item.CastTo()) { AActor* Actor = ActorItem->Actor.Get(); if (Actor) { return FString::Printf(TEXT("%7d"), Actor->GetNumUncachedStaticLightingInteractions()); } } return FString(); }); auto AddTextInfoColumn = [&InInitOptions](FName ColumnID, TAttribute ColumnName, FGetTextForItem ColumnInfo, const FText& ColumnTooltip = FText::GetEmpty()) { InInitOptions.ColumnMap.Add( ColumnID, FSceneOutlinerColumnInfo( ESceneOutlinerColumnVisibility::Invisible, 20, FCreateSceneOutlinerColumn::CreateStatic( &FTextInfoColumn::CreateTextInfoColumn, ColumnID, ColumnInfo, ColumnTooltip), true, TOptional(), ColumnName)); }; // The "Level" column should be named "Package Short Name" in wp enabled levels FText LevelColumnName; if(WorldPtr && WorldPtr->PersistentLevel && WorldPtr->PersistentLevel->IsUsingExternalActors()) { LevelColumnName = FSceneOutlinerBuiltInColumnTypes::PackageShortName_Localized(); } else { LevelColumnName = FSceneOutlinerBuiltInColumnTypes::Level_Localized(); } AddTextInfoColumn(FSceneOutlinerBuiltInColumnTypes::Mobility(), FSceneOutlinerBuiltInColumnTypes::Mobility_Localized(), MobilityInfoText); AddTextInfoColumn(FSceneOutlinerBuiltInColumnTypes::Level(), LevelColumnName, LevelInfoText); if (UWorldPartition* WorldPartition = WorldPtr ? WorldPtr->GetWorldPartition() : nullptr) { AddTextInfoColumn(FSceneOutlinerBuiltInColumnTypes::DataLayer(), FSceneOutlinerBuiltInColumnTypes::DataLayer_Localized(), DataLayerInfoText); if (WorldPartition->IsContentBundleEnabled()) { AddTextInfoColumn(FSceneOutlinerBuiltInColumnTypes::ContentBundle(), FSceneOutlinerBuiltInColumnTypes::ContentBundle_Localized(), ContentBundleInfoText); } } else { AddTextInfoColumn(FSceneOutlinerBuiltInColumnTypes::Layer(), FSceneOutlinerBuiltInColumnTypes::Layer_Localized(), LayerInfoText); } AddTextInfoColumn(FSceneOutlinerBuiltInColumnTypes::SubPackage(), FSceneOutlinerBuiltInColumnTypes::SubPackage_Localized(), SubPackageInfoText); AddTextInfoColumn(FSceneOutlinerBuiltInColumnTypes::Socket(), FSceneOutlinerBuiltInColumnTypes::Socket_Localized(), SocketInfoText); AddTextInfoColumn(FSceneOutlinerBuiltInColumnTypes::IDName(), FSceneOutlinerBuiltInColumnTypes::IDName_Localized(), InternalNameInfoText); AddTextInfoColumn(FSceneOutlinerBuiltInColumnTypes::UncachedLights(), FSceneOutlinerBuiltInColumnTypes::UncachedLights_Localized(), UncachedLightsInfoText); } IMPLEMENT_MODULE(FSceneOutlinerModule, SceneOutliner); #undef LOCTEXT_NAMESPACE //SceneOutlinerModule