// Copyright Epic Games, Inc. All Rights Reserved. #include "LevelEditorSubsystem.h" #include "Editor.h" #include "Editor/UnrealEdEngine.h" #include "EditorScriptingHelpers.h" #include "Elements/Framework/TypedElementSelectionSet.h" #include "Engine/MapBuildDataRegistry.h" #include "FileHelpers.h" #include "IAssetViewport.h" #include "LevelEditor.h" #include "LevelEditorMenuContext.h" #include "Modules/ModuleManager.h" #include "SLevelViewport.h" #include "Subsystems/UnrealEditorSubsystem.h" #include "ToolMenuDelegates.h" #include "ToolMenus.h" #include "UnrealEdGlobals.h" #include "EditorModeManager.h" #include "Styling/SlateIconFinder.h" #include "Subsystems/ActorEditorContextSubsystem.h" #include "LevelInstance/LevelInstanceSubsystem.h" #include "LevelInstance/LevelInstanceInterface.h" #include "ClassIconFinder.h" #include "LightingBuildOptions.h" #include "Widgets/Images/SImage.h" #include "Widgets/Input/SComboButton.h" #include "LevelEditorInternalTools.h" #include "EditorState/EditorStateSubsystem.h" #include "LevelEditorCameraEditorState.h" DEFINE_LOG_CATEGORY_STATIC(LevelEditorSubsystem, Log, All); #define LOCTEXT_NAMESPACE "LevelEditorSubsystem" void ULevelEditorSubsystem::Initialize(FSubsystemCollectionBase& Collection) { Collection.InitializeDependency(); UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateUObject(this, &ULevelEditorSubsystem::ExtendQuickActionMenu)); UActorEditorContextSubsystem::Get()->RegisterClient(this); FWorldDelegates::LevelAddedToWorld.AddUObject(this, &ULevelEditorSubsystem::OnLevelAddedOrRemoved); FWorldDelegates::LevelRemovedFromWorld.AddUObject(this, &ULevelEditorSubsystem::OnLevelAddedOrRemoved); FWorldDelegates::OnCurrentLevelChanged.AddUObject(this, &ULevelEditorSubsystem::OnCurrentLevelChanged); FEditorDelegates::PreSaveWorldWithContext.AddUObject(this, &ULevelEditorSubsystem::HandleOnPreSaveWorldWithContext); FEditorDelegates::PostSaveWorldWithContext.AddUObject(this, &ULevelEditorSubsystem::HandleOnPostSaveWorldWithContext); FEditorDelegates::OnEditorCameraMoved.AddUObject(this, &ULevelEditorSubsystem::HandleOnEditorCameraMoved); FEditorDelegates::MapChange.AddUObject(this, &ULevelEditorSubsystem::HandleOnMapChanged); FEditorDelegates::OnMapOpened.AddUObject(this, &ULevelEditorSubsystem::HandleOnMapOpened); Collection.InitializeDependency(); UEditorStateSubsystem::RegisterEditorStateType(); } void ULevelEditorSubsystem::Deinitialize() { UActorEditorContextSubsystem::Get()->UnregisterClient(this); FWorldDelegates::LevelAddedToWorld.RemoveAll(this); FWorldDelegates::LevelRemovedFromWorld.RemoveAll(this); FWorldDelegates::OnCurrentLevelChanged.RemoveAll(this); UToolMenus::UnRegisterStartupCallback(this); UToolMenus::UnregisterOwner(this); FEditorDelegates::PreSaveWorldWithContext.RemoveAll(this); FEditorDelegates::PostSaveWorldWithContext.RemoveAll(this); FEditorDelegates::OnEditorCameraMoved.RemoveAll(this); FEditorDelegates::MapChange.RemoveAll(this); FEditorDelegates::OnMapOpened.RemoveAll(this); UEditorStateSubsystem::UnregisterEditorStateType(); } void ULevelEditorSubsystem::ExtendQuickActionMenu() { FToolMenuOwnerScoped MenuOwner(this); UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.InViewportPanel"); { FToolMenuSection& Section = Menu->FindOrAddSection("QuickActions"); FToolMenuEntry& Entry = Section.AddDynamicEntry("LevelActors", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) { UQuickActionMenuContext* Context = InSection.FindContext(); if (Context && Context->CurrentSelection && Context->CurrentSelection->GetElementList()->Num() == 1) { FToolUIAction PilotActorAction; PilotActorAction.ExecuteAction = FToolMenuExecuteAction::CreateUObject(this, &ULevelEditorSubsystem::PilotLevelActor); FToolMenuEntry& PilotActorEntry = InSection.AddEntry(FToolMenuEntry::InitToolBarButton( "PilotActor", PilotActorAction, LOCTEXT("PilotSelectedActor", "Pilot Selected Actor"), LOCTEXT("PilotSelectedActorToolTip", "Move the selected actor around using the viewport controls, and bind the viewport to the actor's location and orientation."), FSlateIcon(FAppStyle::Get().GetStyleSetName(), "LevelViewport.PilotSelectedActor") )); // LevelEditorSubsystem.AddKeybindFromCommand(FLightEditingCommands::Get().SwapLightType); } })); } } void ULevelEditorSubsystem::PilotLevelActor(const FToolMenuContext& InContext) { UQuickActionMenuContext* QuickMenuContext = InContext.FindContext(); AActor* SelectedActor = QuickMenuContext->CurrentSelection->GetTopSelectedObject(); PilotLevelActor(SelectedActor); } void ULevelEditorSubsystem::PilotLevelActor(AActor* ActorToPilot, FName ViewportConfigKey) { TSharedPtr LevelViewport = InternalEditorLevelLibrary::GetLevelViewport(ViewportConfigKey); if (LevelViewport.IsValid()) { FLevelEditorViewportClient& LevelViewportClient = LevelViewport->GetLevelViewportClient(); LevelViewportClient.SetActorLock(ActorToPilot); if (LevelViewportClient.IsPerspective() && LevelViewportClient.GetActiveActorLock().IsValid()) { LevelViewportClient.MoveCameraToLockedActor(); } } } void ULevelEditorSubsystem::EjectPilotLevelActor(FName ViewportConfigKey) { TSharedPtr LevelViewport = InternalEditorLevelLibrary::GetLevelViewport(ViewportConfigKey); if (LevelViewport.IsValid()) { FLevelEditorViewportClient& LevelViewportClient = LevelViewport->GetLevelViewportClient(); if (AActor* LockedActor = LevelViewportClient.GetActiveActorLock().Get()) { //// Check to see if the locked actor was previously overriding the camera settings //if (CanGetCameraInformationFromActor(LockedActor)) //{ // // Reset the settings // LevelViewportClient.ViewFOV = LevelViewportClient.FOVAngle; //} LevelViewportClient.SetActorLock(nullptr); // remove roll and pitch from camera when unbinding from actors GEditor->RemovePerspectiveViewRotation(true, true, false); } } } AActor* ULevelEditorSubsystem::GetPilotLevelActor(FName ViewportConfigKey) { TSharedPtr LevelViewport = InternalEditorLevelLibrary::GetLevelViewport(ViewportConfigKey); if (!LevelViewport.IsValid()) { return nullptr; } FLevelEditorViewportClient& LevelViewportClient = LevelViewport->GetLevelViewportClient(); if (AActor* CinematicActorLock = LevelViewportClient.GetCinematicActorLock().GetLockedActor()) { return CinematicActorLock; } return LevelViewportClient.GetActiveActorLock().Get(); } void ULevelEditorSubsystem::SetExactCameraView(bool bExactCameraView, FName ViewportConfigKey) { TSharedPtr LevelViewport = InternalEditorLevelLibrary::GetLevelViewport(ViewportConfigKey); if (!LevelViewport.IsValid()) { return; } FLevelEditorViewportClient& LevelViewportClient = LevelViewport->GetLevelViewportClient(); LevelViewportClient.bLockedCameraView = bExactCameraView; } bool ULevelEditorSubsystem::GetExactCameraView(FName ViewportConfigKey) { TSharedPtr LevelViewport = InternalEditorLevelLibrary::GetLevelViewport(ViewportConfigKey); if (!LevelViewport.IsValid()) { return false; } FLevelEditorViewportClient& LevelViewportClient = LevelViewport->GetLevelViewportClient(); return LevelViewportClient.bLockedCameraView; } void ULevelEditorSubsystem::EditorPlaySimulate() { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveViewport(); if (ActiveLevelViewport.IsValid()) { FRequestPlaySessionParams SessionParams; SessionParams.WorldType = EPlaySessionWorldType::SimulateInEditor; SessionParams.DestinationSlateViewport = ActiveLevelViewport; GUnrealEd->RequestPlaySession(SessionParams); } } void ULevelEditorSubsystem::EditorInvalidateViewports() { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveLevelViewport(); if (ActiveLevelViewport.IsValid()) { FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); LevelViewportClient.Invalidate(); } } void ULevelEditorSubsystem::EditorSetViewportRealtime(bool bInRealtime, FName ViewportConfigKey) { TSharedPtr LevelViewport = InternalEditorLevelLibrary::GetLevelViewport(ViewportConfigKey); if (LevelViewport.IsValid()) { FLevelEditorViewportClient& LevelViewportClient = LevelViewport->GetLevelViewportClient(); FText RealTimeOverrideSystemName = LOCTEXT("LevelEditorSubsystemRealtimeOverride", "Level Editor Subsystem Realtime Override"); if (bInRealtime) { LevelViewportClient.RemoveRealtimeOverride(RealTimeOverrideSystemName); } else { LevelViewportClient.AddRealtimeOverride(false, RealTimeOverrideSystemName); } } } void ULevelEditorSubsystem::EditorSetGameView(bool bGameView, FName ViewportConfigKey) { TSharedPtr LevelViewport = InternalEditorLevelLibrary::GetLevelViewport(ViewportConfigKey); if (LevelViewport.IsValid()) { if (LevelViewport->IsInGameView() != bGameView) { LevelViewport->ToggleGameView(); } } } bool ULevelEditorSubsystem::EditorGetGameView(FName ViewportConfigKey) { TSharedPtr LevelViewport = InternalEditorLevelLibrary::GetLevelViewport(ViewportConfigKey); if (LevelViewport.IsValid()) { return LevelViewport->IsInGameView(); } return false; } void ULevelEditorSubsystem::EditorRequestBeginPlay() { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveViewport(); if (ActiveLevelViewport.IsValid()) { FRequestPlaySessionParams SessionParams; SessionParams.WorldType = EPlaySessionWorldType::PlayInEditor; SessionParams.DestinationSlateViewport = ActiveLevelViewport; GUnrealEd->RequestPlaySession(SessionParams); } } void ULevelEditorSubsystem::EditorRequestEndPlay() { GUnrealEd->RequestEndPlayMap(); } bool ULevelEditorSubsystem::IsInPlayInEditor() const { return GUnrealEd->IsPlayingSessionInEditor(); } TArray ULevelEditorSubsystem::GetViewportConfigKeys() { TArray ViewportConfigKeys; FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr LevelEditor = LevelEditorModule.GetFirstLevelEditor(); if (LevelEditor.IsValid()) { for (TSharedPtr LevelViewport : LevelEditor->GetViewports()) { if (LevelViewport.IsValid()) { ViewportConfigKeys.Add(LevelViewport->GetConfigKey()); } } } return ViewportConfigKeys; } FName ULevelEditorSubsystem::GetActiveViewportConfigKey() { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr LevelEditor = LevelEditorModule.GetFirstLevelEditor(); if (LevelEditor.IsValid()) { TSharedPtr ActiveLevelViewport = LevelEditor->GetActiveViewportInterface(); if (ActiveLevelViewport.IsValid()) { return ActiveLevelViewport->GetConfigKey(); } } return NAME_None; } void ULevelEditorSubsystem::SetAllowsCinematicControl(bool bAllow, FName ViewportConfigKey) { TSharedPtr LevelViewport = InternalEditorLevelLibrary::GetLevelViewport(ViewportConfigKey); if (LevelViewport.IsValid()) { return LevelViewport->SetAllowsCinematicControl(bAllow); } } bool ULevelEditorSubsystem::GetAllowsCinematicControl(FName ViewportConfigKey) { TSharedPtr LevelViewport = InternalEditorLevelLibrary::GetLevelViewport(ViewportConfigKey); if (LevelViewport.IsValid()) { return LevelViewport->GetAllowsCinematicControl(); } return false; } /** * * Editor Scripting | Level * **/ bool ULevelEditorSubsystem::NewLevel(const FString& AssetPath, bool bIsPartitionedWorld) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE()) { return false; } UUnrealEditorSubsystem* UnrealEditorSubsystem = GEditor->GetEditorSubsystem(); if (!UnrealEditorSubsystem) { return false; } FString FailureReason; FString ObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(AssetPath, FailureReason); if (ObjectPath.IsEmpty()) { UE_LOG(LevelEditorSubsystem, Error, TEXT("NewLevel. Failed to create the level. %s"), *FailureReason); return false; } if (!EditorScriptingHelpers::IsAValidPathForCreateNewAsset(ObjectPath, FailureReason)) { UE_LOG(LevelEditorSubsystem, Error, TEXT("NewLevel. Failed to validate the destination. %s"), *FailureReason); return false; } if (FPackageName::DoesPackageExist(ObjectPath)) { UE_LOG(LevelEditorSubsystem, Error, TEXT("NewLevel. Failed to validate the destination '%s'. There's alreay an asset at the destination."), *ObjectPath); return false; } UWorld* World = GEditor->NewMap(bIsPartitionedWorld); if (!World) { UE_LOG(LevelEditorSubsystem, Error, TEXT("NewLevel. Failed to create the new level.")); return false; } FString DestinationLongPackagePath = FPackageName::ObjectPathToPackageName(ObjectPath); if (!UEditorLoadingAndSavingUtils::SaveMap(World, DestinationLongPackagePath)) { UE_LOG(LevelEditorSubsystem, Warning, TEXT("NewLevel. Failed to save the new level.")); return false; } return true; } bool ULevelEditorSubsystem::NewLevelFromTemplate(const FString& AssetPath, const FString& TemplateAssetPath) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE()) { return false; } UUnrealEditorSubsystem* UnrealEditorSubsystem = GEditor->GetEditorSubsystem(); if (!UnrealEditorSubsystem) { return false; } FString FailureReason; FString ObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(AssetPath, FailureReason); if (ObjectPath.IsEmpty()) { UE_LOG(LevelEditorSubsystem, Error, TEXT("NewLevelFromTemplate. Failed to create the level. %s"), *FailureReason); return false; } if (!EditorScriptingHelpers::IsAValidPathForCreateNewAsset(ObjectPath, FailureReason)) { UE_LOG(LevelEditorSubsystem, Error, TEXT("NewLevelFromTemplate. Failed to validate the destination. %s"), *FailureReason); return false; } // DuplicateAsset does it, but failed with a Modal if (FPackageName::DoesPackageExist(ObjectPath)) { UE_LOG(LevelEditorSubsystem, Error, TEXT("NewLevelFromTemplate. Failed to validate the destination '%s'. There's alreay an asset at the destination."), *ObjectPath); return false; } FString TemplateObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(TemplateAssetPath, FailureReason); if (TemplateObjectPath.IsEmpty()) { UE_LOG(LevelEditorSubsystem, Error, TEXT("NewLevelFromTemplate. Failed to create the level. %s"), *FailureReason); return false; } const bool bLoadAsTemplate = true; // Load the template map file - passes LoadAsTemplate==true making the // level load into an untitled package that won't save over the template if (!FEditorFileUtils::LoadMap(*TemplateObjectPath, bLoadAsTemplate)) { UE_LOG(LevelEditorSubsystem, Error, TEXT("NewLevelFromTemplate. Failed to create the new level from template.")); return false; } UWorld* World = GEditor->GetEditorWorldContext().World(); if (!World) { UE_LOG(LevelEditorSubsystem, Error, TEXT("NewLevelFromTemplate. Failed to find the new created world.")); return false; } FString DestinationLongPackagePath = FPackageName::ObjectPathToPackageName(ObjectPath); if (!UEditorLoadingAndSavingUtils::SaveMap(World, DestinationLongPackagePath)) { UE_LOG(LevelEditorSubsystem, Error, TEXT("NewLevelFromTemplate. Failed to save the new level.")); return false; } return true; } bool ULevelEditorSubsystem::LoadLevel(const FString& AssetPath) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE()) { return false; } UUnrealEditorSubsystem* UnrealEditorSubsystem = GEditor->GetEditorSubsystem(); if (!UnrealEditorSubsystem) { return false; } FString FailureReason; FString ObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(AssetPath, FailureReason); if (ObjectPath.IsEmpty()) { UE_LOG(LevelEditorSubsystem, Error, TEXT("LoadLevel. Failed to load level: %s"), *FailureReason); return false; } return UEditorLoadingAndSavingUtils::LoadMap(ObjectPath) != nullptr; } bool ULevelEditorSubsystem::SaveCurrentLevel() { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE()) { return false; } UUnrealEditorSubsystem* UnrealEditorSubsystem = GEditor->GetEditorSubsystem(); if (!UnrealEditorSubsystem) { return false; } UWorld* World = UnrealEditorSubsystem->GetEditorWorld(); if (!World) { UE_LOG(LevelEditorSubsystem, Error, TEXT("SaveCurrentLevel. Can't save the current level because there is no world.")); return false; } ULevel* Level = World->GetCurrentLevel(); if (!Level) { UE_LOG(LevelEditorSubsystem, Error, TEXT("SaveCurrentLevel. Can't save the level because there is no current level.")); return false; } FString Filename = FEditorFileUtils::GetFilename(Level->OwningWorld); if (Filename.Len() == 0) { UE_LOG(LevelEditorSubsystem, Error, TEXT("SaveCurrentLevel. Can't save the level because it doesn't have a filename. Use EditorLoadingAndSavingUtils.")); return false; } TArray MapPackages; MapPackages.Add(Level->GetOutermost()); if (Level->MapBuildData) { MapPackages.AddUnique(Level->MapBuildData->GetOutermost()); } // Checkout without a prompt TArray* PackagesCheckedOut = nullptr; const bool bErrorIfAlreadyCheckedOut = false; FEditorFileUtils::CheckoutPackages(MapPackages, PackagesCheckedOut, bErrorIfAlreadyCheckedOut); return FEditorFileUtils::SaveLevel(Level); } bool ULevelEditorSubsystem::SaveAllDirtyLevels() { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE()) { return false; } UUnrealEditorSubsystem* UnrealEditorSubsystem = GEditor->GetEditorSubsystem(); if (!UnrealEditorSubsystem) { return false; } UWorld* World = UnrealEditorSubsystem->GetEditorWorld(); if (!World) { UE_LOG(LevelEditorSubsystem, Error, TEXT("SaveAllDirtyLevels. Can't save the current level because there is no world.")); return false; } TArray DirtyMapPackages; TArray DirtyLevels; for (ULevel* Level : World->GetLevels()) { if (Level) { UPackage* OutermostPackage = Level->GetOutermost(); if (OutermostPackage->IsDirty()) { FString Filename = FEditorFileUtils::GetFilename(Level->OwningWorld); if (Filename.Len() == 0) { UE_LOG(LevelEditorSubsystem, Warning, TEXT("SaveAllDirtyLevels. Can't save the level '%s' because it doesn't have a filename. Use EditorLoadingAndSavingUtils."), *OutermostPackage->GetName()); continue; } DirtyLevels.Add(Level); DirtyMapPackages.Add(OutermostPackage); if (Level->MapBuildData) { UPackage* BuiltDataPackage = Level->MapBuildData->GetOutermost(); if (BuiltDataPackage->IsDirty() && BuiltDataPackage != OutermostPackage) { DirtyMapPackages.Add(BuiltDataPackage); } } } } } bool bAllSaved = true; if (DirtyMapPackages.Num() > 0) { // Checkout without a prompt TArray* PackagesCheckedOut = nullptr; const bool bErrorIfAlreadyCheckedOut = false; FEditorFileUtils::CheckoutPackages(DirtyMapPackages, PackagesCheckedOut, bErrorIfAlreadyCheckedOut); for (ULevel* Level : DirtyLevels) { bool bSaved = FEditorFileUtils::SaveLevel(Level); if (!bSaved) { UE_LOG(LevelEditorSubsystem, Warning, TEXT("SaveAllDirtyLevels. Can't save the level '%s'."), *World->GetOutermost()->GetName()); bAllSaved = false; } } } else { UE_LOG(LevelEditorSubsystem, Log, TEXT("SaveAllDirtyLevels. There is no dirty level.")); } return bAllSaved; } bool ULevelEditorSubsystem::SetCurrentLevelByName(FName LevelName) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE()) { return false; } if (LevelName == NAME_None) { UE_LOG(LevelEditorSubsystem, Error, TEXT("SetCurrentLevel. LevelName is invalid.")); return false; } UUnrealEditorSubsystem* UnrealEditorSubsystem = GEditor->GetEditorSubsystem(); if (!UnrealEditorSubsystem) { return false; } UWorld* World = UnrealEditorSubsystem->GetEditorWorld(); if (!World) { UE_LOG(LevelEditorSubsystem, Warning, TEXT("SetCurrentLevel. Can't set the current level because there is no world.")); return false; } bool bLevelFound = false; const TArray& AllLevels = World->GetLevels(); if (AllLevels.Num() > 0) { FString LevelNameStr = LevelName.ToString(); for (ULevel* Level : AllLevels) { if (FPackageName::GetShortName(Level->GetOutermost()) == LevelNameStr) { // SetCurrentLevel return true only if the level is changed and it's not the same as the current. //For UEditorLevelLibrary, always return true. World->SetCurrentLevel(Level); bLevelFound = true; break; } } } return bLevelFound; } ULevel* ULevelEditorSubsystem::GetCurrentLevel() { UUnrealEditorSubsystem* UnrealEditorSubsystem = GEditor->GetEditorSubsystem(); if (!UnrealEditorSubsystem) { return nullptr; } UWorld* World = UnrealEditorSubsystem->GetEditorWorld(); if (!World) { UE_LOG(LevelEditorSubsystem, Error, TEXT("GetCurrentLevel. Can't Get the current level because there is no world.")); return nullptr; } return World->GetCurrentLevel(); } UTypedElementSelectionSet* ULevelEditorSubsystem::GetSelectionSet() { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr LevelEditor = LevelEditorModule.GetFirstLevelEditor(); if (LevelEditor.IsValid()) { return LevelEditor->GetMutableElementSelectionSet(); } return nullptr; } bool ULevelEditorSubsystem::BuildLightMaps(ELightingBuildQuality Quality, bool bWithReflectionCaptures) { FLightingBuildOptions LightingOptions; LightingOptions.QualityLevel = Quality; UUnrealEditorSubsystem* UnrealEditorSubsystem = GEditor->GetEditorSubsystem(); if (!UnrealEditorSubsystem) { return false; } UWorld* World = UnrealEditorSubsystem->GetEditorWorld(); if (!World) { UE_LOG(LevelEditorSubsystem, Error, TEXT("BuildLightMaps. Can't build the light maps of the current level because there is no world.")); return false; } bool Success = false; auto BuildFailedDelegate = [&World, &Success]() { UE_LOG(LevelEditorSubsystem, Error, TEXT("BuildLightMaps. Failed building lighting for %s"), *World->GetOutermost()->GetName()); Success = false; }; FDelegateHandle BuildFailedDelegateHandle = FEditorDelegates::OnLightingBuildFailed.AddLambda(BuildFailedDelegate); auto BuildSucceededDelegate = [&World, &Success]() { UE_LOG(LevelEditorSubsystem, Log, TEXT("BuildLightMaps. Successfully built lighting for %s"), *World->GetOutermost()->GetName()); Success = true; }; FDelegateHandle BuildSucceededDelegateHandle = FEditorDelegates::OnLightingBuildSucceeded.AddLambda(BuildSucceededDelegate); UE_LOG(LevelEditorSubsystem, Log, TEXT("BuildLightMaps. Start building lighting for %s"), *World->GetOutermost()->GetName()); GEditor->BuildLighting(LightingOptions); while (GEditor->IsLightingBuildCurrentlyRunning()) { GEditor->UpdateBuildLighting(); } if (bWithReflectionCaptures) { GEditor->BuildReflectionCaptures(); } FEditorDelegates::OnLightingBuildFailed.Remove(BuildFailedDelegateHandle); FEditorDelegates::OnLightingBuildSucceeded.Remove(BuildSucceededDelegateHandle); return Success; } FEditorModeTools* ULevelEditorSubsystem::GetLevelEditorModeManager() { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr LevelEditor = LevelEditorModule.GetFirstLevelEditor(); if (LevelEditor.IsValid() && !IsRunningCommandlet()) { return &LevelEditor->GetEditorModeManager(); } return nullptr; } // Widget used to show current level in viewport class SCurrentLevelWidget : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SCurrentLevelWidget) {} SLATE_ARGUMENT(UWorld*, World) SLATE_END_ARGS() ~SCurrentLevelWidget() { GEditor->GetEditorWorldContext().RemoveRef(World); } void Construct(const FArguments& InArgs) { World = (InArgs._World); GEditor->GetEditorWorldContext().AddRef(World); CommandList = MakeShareable(new FUICommandList); ChildSlot [ SNew(SComboButton) .Cursor(EMouseCursor::Default) .VAlign(VAlign_Center) .ComboButtonStyle(FAppStyle::Get(), "SimpleComboButton") .Visibility(this, &SCurrentLevelWidget::GetCurrentLevelButtonVisibility) .OnGetMenuContent(this, &SCurrentLevelWidget::GenerateLevelMenu) .ButtonContent() [ // Current Level SNew(SVerticalBox) + SVerticalBox::Slot() .Padding(0.0f, 0.0f, 2.0f, 0.0f) .AutoHeight() [ SNew(STextBlock) .Visibility(this, &SCurrentLevelWidget::GetCurrentLevelTextVisibility) .Text(this, &SCurrentLevelWidget::GetCurrentLevelText) ] // Referencing Level Instance (if any) + SVerticalBox::Slot() .AutoHeight() [ SNew(SHorizontalBox) .Visibility_Lambda([this]() { return IsEditingLevelInstanceCurrentLevel() ? EVisibility::Visible : EVisibility::Collapsed; }) + SHorizontalBox::Slot() .AutoWidth() [ SNew(STextBlock) .Text(LOCTEXT("FromBegin", "(")) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(1.f, 1.f, 1.f, 1.f) [ SNew(SBox) .WidthOverride(16.f) .HeightOverride(16.f) [ SNew(SImage) .Image_Lambda([this]() { return FClassIconFinder::FindIconForActor(GetEditingLevelInstance()); }) .ColorAndOpacity(FAppStyle::Get().GetSlateColor("Colors.AccentGreen")) ] ] + SHorizontalBox::Slot() .AutoWidth() [ SNew(STextBlock) .Text_Lambda([this]() { AActor* LevelInstance = GetEditingLevelInstance(); return LevelInstance ? FText::FromString(LevelInstance->GetActorLabel()) : FText::GetEmpty(); }) .ColorAndOpacity(FAppStyle::Get().GetSlateColor("Colors.AccentGreen")) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(0.f, 0.f, 4.f, 0.f) [ SNew(STextBlock) .Text(LOCTEXT("FromEnd", ")")) ] ] ] ]; } private: AActor* GetEditingLevelInstance() const { return InternalEditorLevelLibrary::GetEditingLevelInstance(GetWorld()); } bool IsEditingLevelInstanceCurrentLevel() const { return InternalEditorLevelLibrary::IsEditingLevelInstanceCurrentLevel(GetWorld()); } FText GetCurrentLevelText() const { if (GetWorld() && GetWorld()->GetCurrentLevel()) { // Get the level name const FText ActualLevelName = FText::FromName(FPackageName::GetShortFName(GetWorld()->GetCurrentLevel()->GetOutermost()->GetFName())); if (GetWorld()->GetCurrentLevel() == GetWorld()->PersistentLevel) { FFormatNamedArguments Args; Args.Add(TEXT("ActualLevelName"), ActualLevelName); return FText::Format(LOCTEXT("LevelName", "{0} (Persistent)"), ActualLevelName); } return ActualLevelName; } return FText::GetEmpty(); } bool IsVisible() const { return InternalEditorLevelLibrary::IsActorEditorContextVisible(GetWorld()); } EVisibility GetCurrentLevelTextVisibility() const { return IsVisible() ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed; } EVisibility GetCurrentLevelButtonVisibility() const { return IsVisible() ? EVisibility::Visible : EVisibility::Collapsed; } TSharedRef GenerateLevelMenu() const { // Get all menu extenders for this context menu from the level editor module FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); TSharedRef InCommandList = CommandList.ToSharedRef(); TSharedPtr MenuExtender = LevelEditorModule.AssembleExtenders(InCommandList, LevelEditorModule.GetAllLevelEditorLevelMenuExtenders()); // Create the menu FMenuBuilder LevelMenuBuilder(/*bShouldCloseWindowAfterMenuSelection*/true, InCommandList, MenuExtender); LevelMenuBuilder.BeginSection("LevelListing", LOCTEXT("Levels", "Levels")); LevelMenuBuilder.EndSection(); return LevelMenuBuilder.MakeWidget(); } UWorld* GetWorld() const { return World; } TSharedPtr CommandList; UWorld* World; }; bool ULevelEditorSubsystem::GetActorEditorContextDisplayInfo(UWorld* InWorld, FActorEditorContextClientDisplayInfo& OutDiplayInfo) const { if (InternalEditorLevelLibrary::IsActorEditorContextVisible(InWorld)) { OutDiplayInfo.Title = TEXT("Level"); OutDiplayInfo.Brush = FSlateIconFinder::FindIconBrushForClass(UWorld::StaticClass()); return true; } return false; } void ULevelEditorSubsystem::OnLevelAddedOrRemoved(ULevel* InLevel, UWorld* InWorld) { if (!InWorld->IsGameWorld()) { ActorEditorContextClientChanged.Broadcast(this); } } void ULevelEditorSubsystem::OnCurrentLevelChanged(ULevel* InNewLevel, ULevel* InOldLevel, UWorld* InWorld) { if (!InWorld->IsGameWorld()) { ActorEditorContextClientChanged.Broadcast(this); } } void ULevelEditorSubsystem::HandleOnPreSaveWorldWithContext(class UWorld* World, FObjectPreSaveContext ObjectSaveContext) { OnPreSaveWorld.Broadcast(ObjectSaveContext.GetSaveFlags(), World); } void ULevelEditorSubsystem::HandleOnPostSaveWorldWithContext(class UWorld* World, FObjectPostSaveContext ObjectSaveContext) { OnPostSaveWorld.Broadcast(ObjectSaveContext.GetSaveFlags(), World, ObjectSaveContext.SaveSucceeded()); } void ULevelEditorSubsystem::HandleOnEditorCameraMoved(const FVector& Location, const FRotator& Rotation, ELevelViewportType ViewportType, int32 ViewIndex) { OnEditorCameraMoved.Broadcast(Location, Rotation, ViewportType, ViewIndex); } void ULevelEditorSubsystem::HandleOnMapChanged(uint32 MapChangeFlags) { OnMapChanged.Broadcast(MapChangeFlags); } void ULevelEditorSubsystem::HandleOnMapOpened(const FString& Filename, bool bAsTemplate) { OnMapOpened.Broadcast(Filename, bAsTemplate); } TSharedRef ULevelEditorSubsystem::GetActorEditorContextWidget(UWorld* InWorld) const { return SNew(SCurrentLevelWidget).World(InWorld); } #undef LOCTEXT_NAMESPACE