// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "LevelEditorActions.h" #include "SceneView.h" #include "Factories/Factory.h" #include "Animation/AnimSequence.h" #include "Components/LightComponent.h" #include "HAL/PlatformFilemanager.h" #include "Misc/MessageDialog.h" #include "HAL/FileManager.h" #include "Misc/App.h" #include "Modules/ModuleManager.h" #include "Layout/WidgetPath.h" #include "Framework/Application/MenuStack.h" #include "Framework/Application/SlateApplication.h" #include "TexAlignTools.h" #include "Components/SkeletalMeshComponent.h" #include "Materials/Material.h" #include "Editor/EditorPerProjectUserSettings.h" #include "ISourceControlModule.h" #include "SourceControlHelpers.h" #include "Editor/UnrealEdEngine.h" #include "Settings/EditorExperimentalSettings.h" #include "Factories/BlueprintFactory.h" #include "Editor/GroupActor.h" #include "Materials/MaterialInstance.h" #include "Engine/Light.h" #include "Engine/BlueprintGeneratedClass.h" #include "Kismet2/ComponentEditorUtils.h" #include "Engine/Selection.h" #include "Misc/ConfigCacheIni.h" #include "UObject/UObjectIterator.h" #include "EngineUtils.h" #include "EditorModes.h" #include "UnrealEdMisc.h" #include "FileHelpers.h" #include "EditorModeInterpolation.h" #include "UnrealEdGlobals.h" #include "Toolkits/AssetEditorManager.h" #include "LevelEditor.h" #include "Matinee/MatineeActor.h" #include "Engine/LevelScriptBlueprint.h" #include "LightingBuildOptions.h" #include "EditorSupportDelegates.h" #include "SLevelEditor.h" #include "EditorBuildUtils.h" #include "ScopedTransaction.h" #include "Kismet2/KismetEditorUtilities.h" #include "IContentBrowserSingleton.h" #include "ContentBrowserModule.h" #include "Interfaces/IMainFrameModule.h" #include "DlgDeltaTransform.h" #include "Editor/NewLevelDialog/Public/NewLevelDialogModule.h" #include "MRUFavoritesList.h" #include "Editor/SceneOutliner/Private/SSocketChooser.h" #include "SnappingUtils.h" #include "LevelEditorViewport.h" #include "Layers/ILayers.h" #include "IPlacementMode.h" #include "IPlacementModeModule.h" #include "AssetSelection.h" #include "IDocumentation.h" #include "SourceCodeNavigation.h" #include "EngineAnalytics.h" #include "Interfaces/IAnalyticsProvider.h" #include "EditorClassUtils.h" #include "EditorActorFolders.h" #include "ActorPickerMode.h" #include "Misc/EngineBuildSettings.h" #include "Misc/HotReloadInterface.h" #include "SourceControlWindows.h" #include "Framework/Notifications/NotificationManager.h" #include "Widgets/Notifications/SNotificationList.h" #include "CreateBlueprintFromActorDialog.h" #include "Settings/EditorProjectSettings.h" #include "Engine/LODActor.h" #include "IHierarchicalLODUtilities.h" #include "HierarchicalLODUtilitiesModule.h" #include "Application/IPortalApplicationWindow.h" #include "IPortalServiceLocator.h" #include "MaterialShaderQualitySettings.h" #include "IVREditorModule.h" #include "ComponentRecreateRenderStateContext.h" #include "ILauncherPlatform.h" #include "LauncherPlatformModule.h" #include "Engine/LevelStreaming.h" #include "Engine/LevelStreamingDynamic.h" #include "EditorLevelUtils.h" #include "ActorGroupingUtils.h" #include "LevelUtils.h" #include "ISceneOutliner.h" #include "ISettingsModule.h" #if WITH_LIVE_CODING #include "ILiveCodingModule.h" #endif DEFINE_LOG_CATEGORY_STATIC(LevelEditorActions, Log, All); #define LOCTEXT_NAMESPACE "LevelEditorActions" const FName HotReloadModule("HotReload"); namespace LevelEditorActionsHelpers { /** * If the passed in class is generated by a Blueprint, it will open that Blueprint, otherwise it will help the user create a Blueprint based on that class * * @param InWindowTitle The window title if the Blueprint needs to be created * @param InBlueprintClass The class to create a Blueprint based on or to open if it is a Blueprint * @param InLevelEditor When opening the Blueprint, this level editor is the parent window * @param InNewBPName If we have to create a new BP, this is the suggested name */ UBlueprint* OpenOrCreateBlueprintFromClass(FText InWindowTitle, UClass* InBlueprintClass, TWeakPtr< SLevelEditor > InLevelEditor, FString InNewBPName = TEXT("")) { UBlueprint* Blueprint = NULL; // If the current set class is not a Blueprint, we need to allow the user to create one to edit if(!InBlueprintClass->ClassGeneratedBy) { Blueprint = FKismetEditorUtilities::CreateBlueprintFromClass(InWindowTitle, InBlueprintClass, InNewBPName); } else { Blueprint = Cast(InBlueprintClass->ClassGeneratedBy); } if(Blueprint) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; FAssetEditorManager::Get().OpenEditorForAsset( Blueprint, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, InLevelEditor.Pin() ); } return Blueprint; } /** Check to see whether this world is a persistent world with a valid file on disk */ bool IsPersistentWorld(UWorld* InWorld) { UPackage* Pkg = InWorld ? InWorld->GetOutermost() : nullptr; if (Pkg && FPackageName::IsValidLongPackageName(Pkg->GetName())) { FString FileName; return FPackageName::DoesPackageExist(Pkg->GetName(), nullptr, &FileName); } return false; } } bool FLevelEditorActionCallbacks::DefaultCanExecuteAction() { return FSlateApplication::Get().IsNormalExecution(); } void FLevelEditorActionCallbacks::BrowseDocumentation() { IDocumentation::Get()->OpenHome(FDocumentationSourceInfo(TEXT("help_menu"))); } void FLevelEditorActionCallbacks::BrowseAPIReference() { IDocumentation::Get()->OpenAPIHome(FDocumentationSourceInfo(TEXT("help_menu"))); } void FLevelEditorActionCallbacks::BrowseCVars() { GEditor->Exec(GetWorld(), TEXT("help")); } void FLevelEditorActionCallbacks::BrowseViewportControls() { FString URL; if (FUnrealEdMisc::Get().GetURL(TEXT("ViewportControlsURL"), URL)) { FPlatformProcess::LaunchURL(*URL, NULL, NULL); } } void FLevelEditorActionCallbacks::NewLevel() { if (GUnrealEd->WarnIfLightingBuildIsCurrentlyRunning()) { return; } IMainFrameModule& MainFrameModule = FModuleManager::GetModuleChecked("MainFrame"); FString TemplateMapPackageName; FNewLevelDialogModule& NewLevelDialogModule = FModuleManager::LoadModuleChecked("NewLevelDialog"); if (NewLevelDialogModule.CreateAndShowNewLevelDialog(MainFrameModule.GetParentWindow(), TemplateMapPackageName)) { // The new map screen will return a blank TemplateName if the user has selected to begin a new blank map if (TemplateMapPackageName.IsEmpty()) { GEditor->CreateNewMapForEditing(); } else { // New map screen returned a non-empty TemplateName, so the user has selected to begin from a template map bool TemplateFound = false; // Search all template map folders for a match with TemplateName const bool bIncludeReadOnlyRoots = true; if ( FPackageName::IsValidLongPackageName(TemplateMapPackageName, bIncludeReadOnlyRoots) ) { const FString MapPackageFilename = FPackageName::LongPackageNameToFilename(TemplateMapPackageName, FPackageName::GetMapPackageExtension()); if ( FPaths::FileExists(MapPackageFilename) ) { // File found because the size check came back non-zero TemplateFound = true; // If there are any unsaved changes to the current level, see if the user wants to save those first. bool bPromptUserToSave = true; bool bSaveMapPackages = true; bool bSaveContentPackages = false; if ( FEditorFileUtils::SaveDirtyPackages(bPromptUserToSave, bSaveMapPackages, bSaveContentPackages) ) { // Load the template map file - passes LoadAsTemplate==true making the // level load into an untitled package that won't save over the template FEditorFileUtils::LoadMap(*MapPackageFilename, /*bLoadAsTemplate=*/true); } } } if (!TemplateFound) { UE_LOG( LevelEditorActions, Warning, TEXT("Couldn't find template map package %s"), *TemplateMapPackageName); GEditor->CreateNewMapForEditing(); } } } } bool FLevelEditorActionCallbacks::NewLevel_CanExecute() { return FSlateApplication::Get().IsNormalExecution() && !GLevelEditorModeTools().IsTracking(); } void FLevelEditorActionCallbacks::OpenLevel() { FEditorFileUtils::LoadMap(); } bool FLevelEditorActionCallbacks::OpenLevel_CanExecute() { return FSlateApplication::Get().IsNormalExecution() && !GLevelEditorModeTools().IsTracking(); } void FLevelEditorActionCallbacks::DeltaTransform() { if (GUnrealEd->WarnIfLightingBuildIsCurrentlyRunning()) { return; } FDlgDeltaTransform DeltaDialog; const FDlgDeltaTransform::EResult MoveDialogResult = DeltaDialog.ShowModal(); } void FLevelEditorActionCallbacks::OpenRecentFile( int32 RecentFileIndex ) { IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked( "MainFrame" ); FMainMRUFavoritesList* RecentsAndFavorites = MainFrameModule.GetMRUFavoritesList(); // Save the name of the file we are attempting to load as VerifyFile/AskSaveChanges might rearrange the MRU list on us const FString NewPackageName = RecentsAndFavorites->GetMRUItem( RecentFileIndex ); if( RecentsAndFavorites->VerifyMRUFile( RecentFileIndex ) ) { // Prompt the user to save any outstanding changes. if( FEditorFileUtils::SaveDirtyPackages(true, true, false) ) { FString NewFilename; if (FPackageName::TryConvertLongPackageNameToFilename(NewPackageName, NewFilename, FPackageName::GetMapPackageExtension())) { // Load the requested level. FEditorFileUtils::LoadMap(NewFilename); } } else { // something went wrong or the user pressed cancel. Return to the editor so the user doesn't lose their changes } } } void FLevelEditorActionCallbacks::OpenFavoriteFile( int32 FavoriteFileIndex ) { IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked( "MainFrame" ); FMainMRUFavoritesList* MRUFavoritesList = MainFrameModule.GetMRUFavoritesList(); const FString PackageName = MRUFavoritesList->GetFavoritesItem( FavoriteFileIndex ); if( MRUFavoritesList->VerifyFavoritesFile( FavoriteFileIndex ) ) { // Prompt the user to save any outstanding changes if( FEditorFileUtils::SaveDirtyPackages(true, true, false) ) { FString FileName; if (FPackageName::TryConvertLongPackageNameToFilename(PackageName, FileName, FPackageName::GetMapPackageExtension())) { // Load the requested level. FEditorFileUtils::LoadMap(FileName); } // Move the item to the head of the list MRUFavoritesList->MoveFavoritesItemToHead(PackageName); } else { // something went wrong or the user pressed cancel. Return to the editor so the user doesn't lose their changes } } } void FLevelEditorActionCallbacks::ToggleFavorite() { IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked( "MainFrame" ); FMainMRUFavoritesList* MRUFavoritesList = MainFrameModule.GetMRUFavoritesList(); check( MRUFavoritesList ); if (LevelEditorActionsHelpers::IsPersistentWorld(GetWorld())) { const FString PackageName = GetWorld()->GetOutermost()->GetName(); // If the map was already favorited, remove it from the favorites if ( MRUFavoritesList->ContainsFavoritesItem(PackageName) ) { MRUFavoritesList->RemoveFavoritesItem(PackageName); } // If the map was not already favorited, add it to the favorites else { MRUFavoritesList->AddFavoritesItem(PackageName); } } } void FLevelEditorActionCallbacks::RemoveFavorite( int32 FavoriteFileIndex ) { IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked( "MainFrame" ); FMainMRUFavoritesList* MRUFavoritesList = MainFrameModule.GetMRUFavoritesList(); const FString PackageName = MRUFavoritesList->GetFavoritesItem( FavoriteFileIndex ); if( MRUFavoritesList->VerifyFavoritesFile( FavoriteFileIndex ) ) { if ( MRUFavoritesList->ContainsFavoritesItem(PackageName) ) { MRUFavoritesList->RemoveFavoritesItem(PackageName); } } } bool FLevelEditorActionCallbacks::ToggleFavorite_CanExecute() { if (LevelEditorActionsHelpers::IsPersistentWorld(GetWorld())) { const FMainMRUFavoritesList& MRUFavorites = *FModuleManager::LoadModuleChecked("MainFrame").GetMRUFavoritesList(); const int32 NumFavorites = MRUFavorites.GetNumFavorites(); // Disable the favorites button if the map isn't associated to a file yet (new map, never before saved, etc.) const FString PackageName = GetWorld()->GetOutermost()->GetName(); return (NumFavorites < FLevelEditorCommands::Get().OpenFavoriteFileCommands.Num() || MRUFavorites.ContainsFavoritesItem(PackageName)); } return false; } bool FLevelEditorActionCallbacks::ToggleFavorite_IsChecked() { bool bIsChecked = false; if (LevelEditorActionsHelpers::IsPersistentWorld(GetWorld())) { const FString PackageName = GetWorld()->GetOutermost()->GetName(); IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked( "MainFrame" ); bIsChecked = MainFrameModule.GetMRUFavoritesList()->ContainsFavoritesItem(PackageName); } return bIsChecked; } bool FLevelEditorActionCallbacks::CanSaveWorld() { return FSlateApplication::Get().IsNormalExecution() && (!GUnrealEd || !GUnrealEd->GetPackageAutoSaver().IsAutoSaving()); } void FLevelEditorActionCallbacks::Save() { FEditorFileUtils::SaveCurrentLevel(); } void FLevelEditorActionCallbacks::SaveCurrentAs() { UWorld* World = GetWorld(); ULevel* CurrentLevel = World->GetCurrentLevel(); UClass* CurrentStreamingLevelClass = ULevelStreamingDynamic::StaticClass(); for (ULevelStreaming* StreamingLevel : World->GetStreamingLevels()) { if (StreamingLevel->GetLoadedLevel() == CurrentLevel) { CurrentStreamingLevelClass = StreamingLevel->GetClass(); break; } } const bool bSavedPersistentLevelAs = CurrentLevel == World->PersistentLevel; FString SavedFilename; bool bSaved = FEditorFileUtils::SaveLevelAs(CurrentLevel, &SavedFilename); if(bSaved) { if (bSavedPersistentLevelAs) { FEditorFileUtils::LoadMap(SavedFilename); } else { // Remove the level we just saved over EditorLevelUtils::RemoveLevelFromWorld(CurrentLevel); // Add the new level we just saved as to the plevel FString PackageName; if (FPackageName::TryConvertFilenameToLongPackageName(SavedFilename, PackageName)) { ULevelStreaming* StreamingLevel = UEditorLevelUtils::AddLevelToWorld(World, *PackageName, CurrentStreamingLevelClass); // Make the level we just added current because the expectation is that the new level replaces the existing current level EditorLevelUtils::MakeLevelCurrent(StreamingLevel->GetLoadedLevel()); } FEditorDelegates::RefreshLevelBrowser.Broadcast(); } } } void FLevelEditorActionCallbacks::SaveAllLevels() { const bool bPromptUserToSave = false; const bool bSaveMapPackages = true; const bool bSaveContentPackages = false; const bool bFastSave = false; FEditorFileUtils::SaveDirtyPackages( bPromptUserToSave, bSaveMapPackages, bSaveContentPackages, bFastSave ); } void FLevelEditorActionCallbacks::ImportScene_Clicked() { FEditorFileUtils::Import(); } void FLevelEditorActionCallbacks::ExportAll_Clicked() { const bool bExportSelectedActorsOnly = false; FEditorFileUtils::Export( bExportSelectedActorsOnly ); } void FLevelEditorActionCallbacks::ExportSelected_Clicked() { const bool bExportSelectedActorsOnly = true; FEditorFileUtils::Export( bExportSelectedActorsOnly ); } bool FLevelEditorActionCallbacks::ExportSelected_CanExecute() { // Only enable the option if at least one thing is selected and its not a worldsettings return GEditor->GetSelectedActors()->Num() > 0 && !GEditor->IsWorldSettingsSelected(); } void FLevelEditorActionCallbacks::AttachToActor(AActor* ParentActorPtr) { USceneComponent* ComponentWithSockets = NULL; //@TODO: Should create a menu for each component that contains sockets, or have some form of disambiguation within the menu (like a fully qualified path) // Instead, we currently only display the sockets on the root component if (ParentActorPtr != NULL) { if (USceneComponent* RootComponent = ParentActorPtr->GetRootComponent()) { if (RootComponent->HasAnySockets()) { ComponentWithSockets = RootComponent; } } } // Show socket chooser if we have sockets to select if (ComponentWithSockets != NULL) { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked( "LevelEditor"); TSharedPtr< ILevelEditor > LevelEditor = LevelEditorModule.GetFirstLevelEditor(); // Create as context menu FSlateApplication::Get().PushMenu( LevelEditor.ToSharedRef(), FWidgetPath(), SNew(SSocketChooserPopup) .SceneComponent( ComponentWithSockets ) .OnSocketChosen_Static( &FLevelEditorActionCallbacks::AttachToSocketSelection, ParentActorPtr ), FSlateApplication::Get().GetCursorPos(), FPopupTransitionEffect( FPopupTransitionEffect::ContextMenu ) ); } else { AttachToSocketSelection( NAME_None, ParentActorPtr ); } } void FLevelEditorActionCallbacks::AttachToSocketSelection(const FName SocketName, AActor* ParentActorPtr) { FSlateApplication::Get().DismissAllMenus(); if(ParentActorPtr != NULL) { // Attach each child FScopedTransaction Transaction(LOCTEXT("AttachActors", "Attach actors")); bool bAttached = false; for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It ) { AActor* Actor = Cast( *It ); if (GEditor->CanParentActors(ParentActorPtr, Actor)) { bAttached = true; GEditor->ParentActors(ParentActorPtr, Actor, SocketName); } } if (!bAttached) { Transaction.Cancel(); } } } void FLevelEditorActionCallbacks::SetMaterialQualityLevel( EMaterialQualityLevel::Type NewQualityLevel ) { auto* Settings = GetMutableDefault(); Settings->MaterialQualityLevel = NewQualityLevel; Settings->PostEditChange(); //Ensure the material quality cvar is also set. static IConsoleVariable* MaterialQualityLevelVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.MaterialQualityLevel")); MaterialQualityLevelVar->Set(NewQualityLevel, ECVF_SetByScalability); GUnrealEd->OnSceneMaterialsModified(); GUnrealEd->RedrawAllViewports(); } bool FLevelEditorActionCallbacks::IsMaterialQualityLevelChecked( EMaterialQualityLevel::Type TestQualityLevel ) { static const auto MaterialQualityLevelVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MaterialQualityLevel")); EMaterialQualityLevel::Type MaterialQualityLevel = (EMaterialQualityLevel::Type)FMath::Clamp(MaterialQualityLevelVar->GetValueOnGameThread(), 0, (int32)EMaterialQualityLevel::Num-1); return TestQualityLevel == MaterialQualityLevel; } void FLevelEditorActionCallbacks::ToggleFeatureLevelPreview() { GEditor->ToggleFeatureLevelPreview(); } bool FLevelEditorActionCallbacks::IsFeatureLevelPreviewEnabled() { if (GUnrealEd->IsLightingBuildCurrentlyRunning()) { return false; } if (GEditor->PreviewFeatureLevel == ERHIFeatureLevel::SM5) { return true; } return GEditor->IsFeatureLevelPreviewEnabled(); } bool FLevelEditorActionCallbacks::IsFeatureLevelPreviewActive() { if (GEditor->PreviewFeatureLevel == ERHIFeatureLevel::SM5) { return false; } return GEditor->IsFeatureLevelPreviewEnabled() && GEditor->IsFeatureLevelPreviewActive(); } bool FLevelEditorActionCallbacks::IsPreviewModeButtonVisible() { return GEditor->PreviewFeatureLevel != ERHIFeatureLevel::SM5; } void FLevelEditorActionCallbacks::SetPreviewPlatform(FName MaterialQualityPlatform, ERHIFeatureLevel::Type PreviewFeatureLevel) { GEditor->SetPreviewPlatform(MaterialQualityPlatform, PreviewFeatureLevel); } bool FLevelEditorActionCallbacks::IsPreviewPlatformChecked(FName InMaterialQualityPlatform, ERHIFeatureLevel::Type InPreviewFeatureLevel) { const FName& PreviewPlatform = UMaterialShaderQualitySettings::Get()->GetPreviewPlatform(); return PreviewPlatform == InMaterialQualityPlatform && InPreviewFeatureLevel == GEditor->PreviewFeatureLevel; } void FLevelEditorActionCallbacks::SetFeatureLevelPreview(ERHIFeatureLevel::Type InPreviewFeatureLevel) { // When called through SMenuEntryBlock::OnClicked(), the popup menus are not dismissed when // clicking on a checkbox, but they are dismissed when clicking on a button. We need the popup // menus to go away, or SetFeaturePlatform() is unable to display a progress dialog. Force // the dismissal here. FSlateApplication::Get().DismissAllMenus(); GEditor->SetPreviewPlatform(NAME_None, InPreviewFeatureLevel); } bool FLevelEditorActionCallbacks::IsFeatureLevelPreviewChecked(ERHIFeatureLevel::Type InPreviewFeatureLevel) { return GEditor->PreviewFeatureLevel == InPreviewFeatureLevel; } bool FLevelEditorActionCallbacks::IsFeatureLevelPreviewAvailable(ERHIFeatureLevel::Type InPreviewFeatureLevel) { return GShaderPlatformForFeatureLevel[InPreviewFeatureLevel] != SP_NumPlatforms; } void FLevelEditorActionCallbacks::ConfigureLightingBuildOptions( const FLightingBuildOptions& Options ) { GConfig->SetBool( TEXT("LightingBuildOptions"), TEXT("OnlyBuildSelected"), Options.bOnlyBuildSelected, GEditorPerProjectIni ); GConfig->SetBool( TEXT("LightingBuildOptions"), TEXT("OnlyBuildCurrentLevel"), Options.bOnlyBuildCurrentLevel, GEditorPerProjectIni ); GConfig->SetBool( TEXT("LightingBuildOptions"), TEXT("OnlyBuildSelectedLevels"),Options.bOnlyBuildSelectedLevels, GEditorPerProjectIni ); GConfig->SetBool( TEXT("LightingBuildOptions"), TEXT("OnlyBuildVisibility"), Options.bOnlyBuildVisibility, GEditorPerProjectIni ); } bool FLevelEditorActionCallbacks::CanBuildLighting() { // Building lighting modifies the BuildData package, which the PIE session will also be referencing without getting notified return !(GEditor->PlayWorld || GUnrealEd->bIsSimulatingInEditor); } bool FLevelEditorActionCallbacks::CanBuildReflectionCaptures() { // Building reflection captures modifies the BuildData package, which the PIE session will also be referencing without getting notified return !(GEditor->PlayWorld || GUnrealEd->bIsSimulatingInEditor) // Build reflection captures requires SM5. Don't allow building when previewing other feature levels. && GetWorld()->FeatureLevel >= ERHIFeatureLevel::SM5; } void FLevelEditorActionCallbacks::Build_Execute() { // Reset build options ConfigureLightingBuildOptions( FLightingBuildOptions() ); // Build everything! FEditorBuildUtils::EditorBuild( GetWorld(), FBuildOptions::BuildAll ); } bool FLevelEditorActionCallbacks::Build_CanExecute() { return CanBuildLighting() && CanBuildReflectionCaptures(); } void FLevelEditorActionCallbacks::BuildAndSubmitToSourceControl_Execute() { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked( TEXT("LevelEditor") ); LevelEditorModule.SummonBuildAndSubmit(); } void FLevelEditorActionCallbacks::BuildLightingOnly_Execute() { // Reset build options ConfigureLightingBuildOptions( FLightingBuildOptions() ); // Build lighting! const bool bAllowLightingDialog = false; FEditorBuildUtils::EditorBuild( GetWorld(), FBuildOptions::BuildLighting, bAllowLightingDialog ); } bool FLevelEditorActionCallbacks::BuildLighting_CanExecute() { static const auto AllowStaticLightingVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting")); const bool bAllowStaticLighting = (!AllowStaticLightingVar || AllowStaticLightingVar->GetValueOnGameThread() != 0); return bAllowStaticLighting && CanBuildLighting() && CanBuildReflectionCaptures(); } void FLevelEditorActionCallbacks::BuildReflectionCapturesOnly_Execute() { GEditor->BuildReflectionCaptures(); } bool FLevelEditorActionCallbacks::BuildReflectionCapturesOnly_CanExecute() { return CanBuildReflectionCaptures(); } void FLevelEditorActionCallbacks::BuildLightingOnly_VisibilityOnly_Execute() { // Configure build options FLightingBuildOptions LightingBuildOptions; LightingBuildOptions.bOnlyBuildVisibility = true; ConfigureLightingBuildOptions( LightingBuildOptions ); // Build lighting! const bool bAllowLightingDialog = false; FEditorBuildUtils::EditorBuild( GetWorld(), FBuildOptions::BuildLighting, bAllowLightingDialog ); // Reset build options ConfigureLightingBuildOptions( FLightingBuildOptions() ); } bool FLevelEditorActionCallbacks::LightingBuildOptions_UseErrorColoring_IsChecked() { bool bUseErrorColoring = false; GConfig->GetBool(TEXT("LightingBuildOptions"), TEXT("UseErrorColoring"), bUseErrorColoring, GEditorPerProjectIni); return bUseErrorColoring; } void FLevelEditorActionCallbacks::LightingBuildOptions_UseErrorColoring_Toggled() { bool bUseErrorColoring = false; GConfig->GetBool( TEXT("LightingBuildOptions"), TEXT("UseErrorColoring"), bUseErrorColoring, GEditorPerProjectIni ); GConfig->SetBool( TEXT("LightingBuildOptions"), TEXT("UseErrorColoring"), !bUseErrorColoring, GEditorPerProjectIni ); } bool FLevelEditorActionCallbacks::LightingBuildOptions_ShowLightingStats_IsChecked() { bool bShowLightingBuildInfo = false; GConfig->GetBool(TEXT("LightingBuildOptions"), TEXT("ShowLightingBuildInfo"), bShowLightingBuildInfo, GEditorPerProjectIni); return bShowLightingBuildInfo; } void FLevelEditorActionCallbacks::LightingBuildOptions_ShowLightingStats_Toggled() { bool bShowLightingBuildInfo = false; GConfig->GetBool( TEXT("LightingBuildOptions"), TEXT("ShowLightingBuildInfo"), bShowLightingBuildInfo, GEditorPerProjectIni ); GConfig->SetBool( TEXT("LightingBuildOptions"), TEXT("ShowLightingBuildInfo"), !bShowLightingBuildInfo, GEditorPerProjectIni ); } void FLevelEditorActionCallbacks::BuildGeometryOnly_Execute() { // Build geometry! FEditorBuildUtils::EditorBuild( GetWorld(), FBuildOptions::BuildVisibleGeometry ); } void FLevelEditorActionCallbacks::BuildGeometryOnly_OnlyCurrentLevel_Execute() { // Build geometry (current level)! FEditorBuildUtils::EditorBuild( GetWorld(), FBuildOptions::BuildGeometry ); } void FLevelEditorActionCallbacks::BuildPathsOnly_Execute() { // Build paths! FEditorBuildUtils::EditorBuild( GetWorld(), FBuildOptions::BuildAIPaths ); } void FLevelEditorActionCallbacks::BuildLODsOnly_Execute() { // Build HLOD FEditorBuildUtils::EditorBuild(GetWorld(), FBuildOptions::BuildHierarchicalLOD); } void FLevelEditorActionCallbacks::BuildTextureStreamingOnly_Execute() { FEditorBuildUtils::EditorBuildTextureStreaming(GetWorld()); GEngine->DeferredCommands.AddUnique(TEXT("MAP CHECK NOTIFYRESULTS")); } bool FLevelEditorActionCallbacks::IsLightingQualityChecked( ELightingBuildQuality TestQuality ) { int32 CurrentQualityLevel; GConfig->GetInt(TEXT("LightingBuildOptions"), TEXT("QualityLevel"), CurrentQualityLevel, GEditorPerProjectIni); return TestQuality == CurrentQualityLevel; } void FLevelEditorActionCallbacks::SetLightingQuality( ELightingBuildQuality NewQuality ) { GConfig->SetInt(TEXT("LightingBuildOptions"), TEXT("QualityLevel"), (int32)NewQuality, GEditorPerProjectIni); } float FLevelEditorActionCallbacks::GetLightingDensityIdeal() { return ( GEngine->IdealLightMapDensity ); } void FLevelEditorActionCallbacks::SetLightingDensityIdeal( float Value ) { GEngine->IdealLightMapDensity = Value; // We need to make sure that Maximum is always slightly larger than ideal... if (GEngine->IdealLightMapDensity >= GEngine->MaxLightMapDensity - 0.01f) { SetLightingDensityMaximum( GEngine->IdealLightMapDensity + 0.01f ); } FEditorSupportDelegates::RedrawAllViewports.Broadcast(); } float FLevelEditorActionCallbacks::GetLightingDensityMaximum() { return ( GEngine->MaxLightMapDensity ); } void FLevelEditorActionCallbacks::SetLightingDensityMaximum( float Value ) { GEngine->MaxLightMapDensity = Value; // We need to make sure that Maximum is always slightly larger than ideal... if (GEngine->MaxLightMapDensity <= GEngine->IdealLightMapDensity + 0.01f) { GEngine->MaxLightMapDensity = GEngine->IdealLightMapDensity + 0.01f; } FEditorSupportDelegates::RedrawAllViewports.Broadcast(); } float FLevelEditorActionCallbacks::GetLightingDensityColorScale() { return ( GEngine->RenderLightMapDensityColorScale ); } void FLevelEditorActionCallbacks::SetLightingDensityColorScale( float Value ) { GEngine->RenderLightMapDensityColorScale = Value; FEditorSupportDelegates::RedrawAllViewports.Broadcast(); } float FLevelEditorActionCallbacks::GetLightingDensityGrayscaleScale() { return ( GEngine->RenderLightMapDensityGrayscaleScale ); } void FLevelEditorActionCallbacks::SetLightingDensityGrayscaleScale( float Value ) { GEngine->RenderLightMapDensityGrayscaleScale = Value; FEditorSupportDelegates::RedrawAllViewports.Broadcast(); } void FLevelEditorActionCallbacks::SetLightingDensityRenderGrayscale() { GEngine->bRenderLightMapDensityGrayscale = !GEngine->bRenderLightMapDensityGrayscale; GEngine->SaveConfig(); FEditorSupportDelegates::RedrawAllViewports.Broadcast(); } bool FLevelEditorActionCallbacks::IsLightingDensityRenderGrayscaleChecked() { return GEngine->bRenderLightMapDensityGrayscale; } void FLevelEditorActionCallbacks::SetLightingResolutionStaticMeshes( ECheckBoxState NewCheckedState ) { FLightmapResRatioAdjustSettings& Settings = FLightmapResRatioAdjustSettings::Get(); Settings.bStaticMeshes = ( NewCheckedState == ECheckBoxState::Checked ); } ECheckBoxState FLevelEditorActionCallbacks::IsLightingResolutionStaticMeshesChecked() { return ( FLightmapResRatioAdjustSettings::Get().bStaticMeshes ? ECheckBoxState::Checked : ECheckBoxState::Unchecked ); } void FLevelEditorActionCallbacks::SetLightingResolutionBSPSurfaces( ECheckBoxState NewCheckedState ) { FLightmapResRatioAdjustSettings& Settings = FLightmapResRatioAdjustSettings::Get(); Settings.bBSPSurfaces = ( NewCheckedState == ECheckBoxState::Checked ); } ECheckBoxState FLevelEditorActionCallbacks::IsLightingResolutionBSPSurfacesChecked() { return ( FLightmapResRatioAdjustSettings::Get().bBSPSurfaces ? ECheckBoxState::Checked : ECheckBoxState::Unchecked ); } void FLevelEditorActionCallbacks::SetLightingResolutionLevel( FLightmapResRatioAdjustSettings::AdjustLevels NewLevel ) { FLightmapResRatioAdjustSettings& Settings = FLightmapResRatioAdjustSettings::Get(); Settings.LevelOptions = NewLevel; } bool FLevelEditorActionCallbacks::IsLightingResolutionLevelChecked( FLightmapResRatioAdjustSettings::AdjustLevels TestLevel ) { return ( FLightmapResRatioAdjustSettings::Get().LevelOptions == TestLevel ); } void FLevelEditorActionCallbacks::SetLightingResolutionSelectedObjectsOnly() { FLightmapResRatioAdjustSettings& Settings = FLightmapResRatioAdjustSettings::Get(); Settings.bSelectedObjectsOnly = !Settings.bSelectedObjectsOnly; } bool FLevelEditorActionCallbacks::IsLightingResolutionSelectedObjectsOnlyChecked() { return FLightmapResRatioAdjustSettings::Get().bSelectedObjectsOnly; } float FLevelEditorActionCallbacks::GetLightingResolutionMinSMs() { return static_cast( FLightmapResRatioAdjustSettings::Get().Min_StaticMeshes ); } void FLevelEditorActionCallbacks::SetLightingResolutionMinSMs( float Value ) { FLightmapResRatioAdjustSettings& Settings = FLightmapResRatioAdjustSettings::Get(); Settings.Min_StaticMeshes = static_cast( Value ); } float FLevelEditorActionCallbacks::GetLightingResolutionMaxSMs() { return static_cast( FLightmapResRatioAdjustSettings::Get().Max_StaticMeshes ); } void FLevelEditorActionCallbacks::SetLightingResolutionMaxSMs( float Value ) { FLightmapResRatioAdjustSettings& Settings = FLightmapResRatioAdjustSettings::Get(); Settings.Max_StaticMeshes = static_cast( Value ); } float FLevelEditorActionCallbacks::GetLightingResolutionMinBSPs() { return static_cast( FLightmapResRatioAdjustSettings::Get().Min_BSPSurfaces ); } void FLevelEditorActionCallbacks::SetLightingResolutionMinBSPs( float Value ) { FLightmapResRatioAdjustSettings& Settings = FLightmapResRatioAdjustSettings::Get(); Settings.Min_BSPSurfaces = static_cast( Value ); } float FLevelEditorActionCallbacks::GetLightingResolutionMaxBSPs() { return static_cast( FLightmapResRatioAdjustSettings::Get().Max_BSPSurfaces ); } void FLevelEditorActionCallbacks::SetLightingResolutionMaxBSPs( float Value ) { FLightmapResRatioAdjustSettings& Settings = FLightmapResRatioAdjustSettings::Get(); Settings.Max_BSPSurfaces = static_cast( Value ); } int32 FLevelEditorActionCallbacks::GetLightingResolutionRatio() { return FMath::RoundToInt(FLightmapResRatioAdjustSettings::Get().Ratio * 100.0f); } void FLevelEditorActionCallbacks::SetLightingResolutionRatio( int32 Value ) { FLightmapResRatioAdjustSettings& Settings = FLightmapResRatioAdjustSettings::Get(); const float NewValue = Value / 100.0f; if ( Settings.Ratio != NewValue ) { Settings.Ratio = NewValue; Settings.ApplyRatioAdjustment(); } } void FLevelEditorActionCallbacks::SetLightingResolutionRatioCommit( int32 Value, ETextCommit::Type CommitInfo) { if ((CommitInfo == ETextCommit::OnEnter) || (CommitInfo == ETextCommit::OnUserMovedFocus)) { SetLightingResolutionRatio( Value ); } } void FLevelEditorActionCallbacks::ShowLightingStaticMeshInfo() { if (GUnrealEd) { GUnrealEd->ShowLightingStaticMeshInfoWindow(); } } void FLevelEditorActionCallbacks::ShowSceneStats() { if (GUnrealEd) { GUnrealEd->OpenSceneStatsWindow(); } } void FLevelEditorActionCallbacks::ShowTextureStats() { if (GUnrealEd) { GUnrealEd->OpenTextureStatsWindow(); } } void FLevelEditorActionCallbacks::MapCheck_Execute() { GEditor->Exec( GetWorld(), TEXT("MAP CHECK") ); } bool FLevelEditorActionCallbacks::CanShowSourceCodeActions() { if (GEditor) { // Don't allow hot reloading if we're running networked PIE instances // The reason, is it's fairly complicated to handle the re-wiring that needs to happen when we re-instance objects like player controllers, possessed pawns, etc... const TIndirectArray& WorldContextList = GEditor->GetWorldContexts(); for (const FWorldContext& WorldContext : WorldContextList) { if (WorldContext.World() && WorldContext.World()->WorldType == EWorldType::PIE && WorldContext.World()->NetDriver) { return false; } } } IHotReloadInterface& HotReloadSupport = FModuleManager::LoadModuleChecked(HotReloadModule); // If there is at least one loaded game module, source code actions should be available. return HotReloadSupport.IsAnyGameModuleLoaded(); } void FLevelEditorActionCallbacks::RecompileGameCode_Clicked() { #if WITH_LIVE_CODING ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); if (LiveCoding != nullptr && LiveCoding->IsEnabledByDefault()) { LiveCoding->EnableForSession(true); if (LiveCoding->IsEnabledForSession()) { LiveCoding->Compile(); } else { FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("NoLiveCodingCompileAfterHotReload", "Live Coding cannot be enabled after hot-reload has been used. Please restart the editor.")); } return; } #endif // Don't allow a recompile while already compiling! IHotReloadInterface& HotReloadSupport = FModuleManager::LoadModuleChecked(HotReloadModule); if( !HotReloadSupport.IsCurrentlyCompiling() ) { // We want compiling to happen asynchronously HotReloadSupport.DoHotReloadFromEditor(EHotReloadFlags::None); } } bool FLevelEditorActionCallbacks::Recompile_CanExecute() { #if WITH_LIVE_CODING ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); if (LiveCoding != nullptr && LiveCoding->IsEnabledByDefault()) { return !LiveCoding->IsCompiling(); } #endif // We can't recompile while in PIE if (GEditor->bIsPlayWorldQueued || GEditor->PlayWorld) { return false; } // We're not able to recompile if a compile is already in progress! IHotReloadInterface& HotReloadSupport = FModuleManager::LoadModuleChecked(HotReloadModule); return !HotReloadSupport.IsCurrentlyCompiling() && !(FApp::GetEngineIsPromotedBuild() && FEngineBuildSettings::IsPerforceBuild()); } #if WITH_LIVE_CODING void FLevelEditorActionCallbacks::LiveCoding_ToggleEnabled() { ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); if (LiveCoding != nullptr) { LiveCoding->EnableByDefault(!LiveCoding->IsEnabledByDefault()); if (LiveCoding->IsEnabledByDefault() && !LiveCoding->IsEnabledForSession()) { FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("NoEnableLiveCodingAfterHotReload", "Live Coding cannot be enabled after hot-reload has been used. Please restart the editor.")); } } } bool FLevelEditorActionCallbacks::LiveCoding_IsEnabled( ) { ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); return LiveCoding != nullptr && LiveCoding->IsEnabledByDefault(); } void FLevelEditorActionCallbacks::LiveCoding_StartSession_Clicked() { ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); if (LiveCoding != nullptr) { LiveCoding->EnableForSession(true); if (!LiveCoding->IsEnabledForSession()) { FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("NoStartedLiveCodingAfterHotReload", "Live Coding cannot be started after hot-reload has been used. Please restart the editor.")); } } } bool FLevelEditorActionCallbacks::LiveCoding_CanStartSession() { ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); return LiveCoding != nullptr && LiveCoding->IsEnabledByDefault() && !LiveCoding->HasStarted(); } void FLevelEditorActionCallbacks::LiveCoding_ShowConsole_Clicked() { ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); if (LiveCoding!= nullptr) { LiveCoding->ShowConsole(); } } bool FLevelEditorActionCallbacks::LiveCoding_CanShowConsole() { ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); return LiveCoding!= nullptr && LiveCoding->IsEnabledForSession(); } void FLevelEditorActionCallbacks::LiveCoding_Settings_Clicked() { FModuleManager::LoadModuleChecked("Settings").ShowViewer("Editor", "General", "Live Coding"); } #endif void FLevelEditorActionCallbacks::ConnectToSourceControl_Clicked() { // Show login window regardless of current status - its useful as a shortcut to change settings. ISourceControlModule& SourceControlModule = ISourceControlModule::Get(); SourceControlModule.ShowLoginDialog(FSourceControlLoginClosed(), ELoginWindowMode::Modeless, EOnLoginWindowStartup::PreserveProvider); } bool FLevelEditorActionCallbacks::CheckOutModifiedFiles_CanExecute() { ISourceControlModule& SourceControlModule = ISourceControlModule::Get(); if (ISourceControlModule::Get().IsEnabled() && ISourceControlModule::Get().GetProvider().IsAvailable()) { TArray PackagesToSave; FEditorFileUtils::GetDirtyWorldPackages(PackagesToSave); FEditorFileUtils::GetDirtyContentPackages(PackagesToSave); return PackagesToSave.Num() > 0; } return false; } void FLevelEditorActionCallbacks::CheckOutModifiedFiles_Clicked() { TArray PackagesToSave; FEditorFileUtils::GetDirtyWorldPackages(PackagesToSave); FEditorFileUtils::GetDirtyContentPackages(PackagesToSave); const bool bCheckDirty = true; const bool bPromptUserToSave = false; FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, bCheckDirty, bPromptUserToSave); } bool FLevelEditorActionCallbacks::SubmitToSourceControl_CanExecute() { ISourceControlModule& SourceControlModule = ISourceControlModule::Get(); return ISourceControlModule::Get().IsEnabled() && ISourceControlModule::Get().GetProvider().IsAvailable() && FSourceControlWindows::CanChoosePackagesToCheckIn(); } void FLevelEditorActionCallbacks::SubmitToSourceControl_Clicked() { FSourceControlWindows::ChoosePackagesToCheckIn(); } void FLevelEditorActionCallbacks::GoToCodeForActor_Clicked() { const auto& SelectedActorInfo = AssetSelectionUtils::GetSelectedActorInfo(); FSourceCodeNavigation::NavigateToClass(SelectedActorInfo.SelectionClass); } void FLevelEditorActionCallbacks::GoToDocsForActor_Clicked() { const auto& SelectedActorInfo = AssetSelectionUtils::GetSelectedActorInfo(); if( SelectedActorInfo.SelectionClass != nullptr ) { FString DocumentationLink = FEditorClassUtils::GetDocumentationLink(SelectedActorInfo.SelectionClass); if (!DocumentationLink.IsEmpty()) { IDocumentation::Get()->Open( DocumentationLink, FDocumentationSourceInfo(TEXT("rightclick_viewdoc")) ); } } } void FLevelEditorActionCallbacks::FindInContentBrowser_Clicked() { GEditor->SyncToContentBrowser(); } void FLevelEditorActionCallbacks::EditAsset_Clicked( const EToolkitMode::Type ToolkitMode, TWeakPtr< SLevelEditor > LevelEditor, bool bConfirmMultiple ) { if( GEditor->GetSelectedActorCount() > 0 ) { TArray< UObject* > ReferencedAssets; const bool bIgnoreOtherAssetsIfBPReferenced = true; GEditor->GetReferencedAssetsForEditorSelection( ReferencedAssets, bIgnoreOtherAssetsIfBPReferenced ); bool bShouldOpenEditors = (ReferencedAssets.Num() == 1); if (ReferencedAssets.Num() > 1) { if (bConfirmMultiple) { int32 Response = FMessageDialog::Open( EAppMsgType::YesNo, LOCTEXT("OpenAllAssetEditors", "There is more than one referenced asset in the selection. Do you want to open them all for editing?") ); bShouldOpenEditors = (Response == EAppReturnType::Yes); } else { bShouldOpenEditors = true; } } if (bShouldOpenEditors) { // Clear focus so the level viewport can receive its focus lost call (and clear pending keyup events which wouldn't arrive) FSlateApplication::Get().ClearKeyboardFocus(EFocusCause::WindowActivate); auto LevelEditorSharedPtr = LevelEditor.Pin(); if (LevelEditorSharedPtr.IsValid()) { for (auto Asset : ReferencedAssets) { FAssetEditorManager::Get().OpenEditorForAsset(Asset, ToolkitMode, LevelEditorSharedPtr); } } } } } void FLevelEditorActionCallbacks::LockActorMovement_Clicked() { GEditor->ToggleSelectedActorMovementLock(); } void FLevelEditorActionCallbacks::DetachActor_Clicked() { GEditor->DetachSelectedActors(); } void FLevelEditorActionCallbacks::AttachSelectedActors() { GUnrealEd->AttachSelectedActors(); } void FLevelEditorActionCallbacks::AttachActorIteractive() { if(GUnrealEd->GetSelectedActorCount()) { FActorPickerModeModule& ActorPickerMode = FModuleManager::Get().GetModuleChecked("ActorPickerMode"); ActorPickerMode.BeginActorPickingMode( FOnGetAllowedClasses(), FOnShouldFilterActor::CreateStatic(&FLevelEditorActionCallbacks::IsAttachableActor), FOnActorSelected::CreateStatic(&FLevelEditorActionCallbacks::AttachToActor) ); } } bool FLevelEditorActionCallbacks::IsAttachableActor( const AActor* const ParentActor ) { for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It ) { AActor* Actor = static_cast( *It ); if (!GEditor->CanParentActors(ParentActor, Actor)) { return false; } USceneComponent* ChildRoot = Actor->GetRootComponent(); USceneComponent* ParentRoot = ParentActor->GetRootComponent(); if (ChildRoot != nullptr && ParentRoot != nullptr && ChildRoot->IsAttachedTo(ParentRoot)) { return false; } } return true; } void FLevelEditorActionCallbacks::CreateNewOutlinerFolder_Clicked() { const FName NewFolderName = FActorFolders::Get().GetDefaultFolderNameForSelection(*GetWorld()); FActorFolders::Get().CreateFolderContainingSelection(*GetWorld(), NewFolderName); } void FLevelEditorActionCallbacks::GoHere_Clicked( const FVector* Point ) { if( GCurrentLevelEditingViewportClient ) { FVector ZoomToPoint = FVector::ZeroVector; if( !Point ) { FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues( GCurrentLevelEditingViewportClient->Viewport, GCurrentLevelEditingViewportClient->GetWorld()->Scene, GCurrentLevelEditingViewportClient->EngineShowFlags) .SetRealtimeUpdate(true)); FSceneView* SceneView = GCurrentLevelEditingViewportClient->CalcSceneView(&ViewFamily); if(SceneView) { FIntPoint MousePosition; FVector WorldOrigin; FVector WorldDirection; GCurrentLevelEditingViewportClient->Viewport->GetMousePos(MousePosition); SceneView->DeprojectFVector2D(MousePosition, WorldOrigin, WorldDirection); FHitResult HitResult; FCollisionQueryParams LineParams(SCENE_QUERY_STAT(FocusOnPoint), true); if(GCurrentLevelEditingViewportClient->GetWorld()->LineTraceSingleByObjectType(HitResult, WorldOrigin, WorldOrigin + WorldDirection * HALF_WORLD_MAX, FCollisionObjectQueryParams(ECC_WorldStatic), LineParams)) { ZoomToPoint = HitResult.ImpactPoint; } } } else { ZoomToPoint = *Point; } const float PushOutSize = 500; FBox BoundingBox(ZoomToPoint-PushOutSize, ZoomToPoint+PushOutSize); GCurrentLevelEditingViewportClient->FocusViewportOnBox(BoundingBox); } } bool FLevelEditorActionCallbacks::LockActorMovement_IsChecked() { return GEditor->HasLockedActors(); } void FLevelEditorActionCallbacks::AddActor_Clicked( UActorFactory* ActorFactory, FAssetData AssetData, bool bUsePlacement ) { UObject* Object = AssetData.GetAsset(); if(bUsePlacement && IPlacementModeModule::IsAvailable() && Object != NULL) { FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); LevelEditorModule.FocusViewport(); // Make sure we're in actor placement mode GLevelEditorModeTools().ActivateMode(FBuiltinEditorModes::EM_Placement); TArray AssetsToPlace; AssetsToPlace.Add(Object); auto* PlacementMode = GLevelEditorModeTools().GetActiveModeTyped(FBuiltinEditorModes::EM_Placement); PlacementMode->StartPlacing(AssetsToPlace, ActorFactory); } else { FLevelEditorActionCallbacks::AddActor(ActorFactory, AssetData, NULL); } } AActor* FLevelEditorActionCallbacks::AddActor( UActorFactory* ActorFactory, const FAssetData& AssetData, const FTransform* ActorTransform ) { AActor* NewActor = GEditor->UseActorFactory( ActorFactory, AssetData, ActorTransform ); if ( NewActor != NULL && IPlacementModeModule::IsAvailable() ) { IPlacementModeModule::Get().AddToRecentlyPlaced( AssetData.GetAsset(), ActorFactory ); } return NewActor; } void FLevelEditorActionCallbacks::AddActorFromClass_Clicked( UClass* ActorClass ) { FLevelEditorActionCallbacks::AddActorFromClass(ActorClass); } AActor* FLevelEditorActionCallbacks::AddActorFromClass( UClass* ActorClass ) { AActor* NewActor = NULL; if ( ActorClass ) { // Look for an actor factory capable of creating actors of that type. UActorFactory* ActorFactory = GEditor->FindActorFactoryForActorClass( ActorClass ); if( ActorFactory ) { NewActor = GEditor->UseActorFactoryOnCurrentSelection( ActorFactory, nullptr ); if ( NewActor != NULL && IPlacementModeModule::IsAvailable() ) { IPlacementModeModule::Get().AddToRecentlyPlaced( ActorClass, ActorFactory ); } } else { // No actor factory was found; use SpawnActor instead. GUnrealEd->Exec( GetWorld(), *FString::Printf( TEXT("ACTOR ADD CLASS=%s"), *ActorClass->GetName() ) ); } } return NewActor; } void FLevelEditorActionCallbacks::ReplaceActors_Clicked( UActorFactory* ActorFactory, FAssetData AssetData ) { FLevelEditorActionCallbacks::ReplaceActors(ActorFactory, AssetData); } AActor* FLevelEditorActionCallbacks::ReplaceActors( UActorFactory* ActorFactory, const FAssetData& AssetData ) { AActor* NewActor = NULL; // Have a first stab at filling in the factory properties. FText ErrorMessage; if( ActorFactory->CanCreateActorFrom( AssetData, ErrorMessage ) ) { // Replace all selected actors with actors created from the specified factory GEditor->ReplaceSelectedActors( ActorFactory, AssetData ); if ( IPlacementModeModule::IsAvailable() ) { IPlacementModeModule::Get().AddToRecentlyPlaced( AssetData.GetAsset(), ActorFactory ); } } else { FNotificationInfo ErrorNotification( ErrorMessage ); ErrorNotification.Image = FEditorStyle::GetBrush(TEXT("MessageLog.Error")); ErrorNotification.bFireAndForget = true; ErrorNotification.ExpireDuration = 3.0f; // Need this message to last a little longer than normal since the user may want to "Show Log" ErrorNotification.bUseThrobber = true; FSlateNotificationManager::Get().AddNotification(ErrorNotification); } return NewActor; } void FLevelEditorActionCallbacks::ReplaceActorsFromClass_Clicked( UClass* ActorClass ) { if ( ActorClass ) { // Look for an actor factory capable of creating actors of that type. UActorFactory* ActorFactory = GEditor->FindActorFactoryForActorClass( ActorClass ); if( ActorFactory ) { // Replace all selected actors with actors created from the specified factory UObject* TargetAsset = GEditor->GetSelectedObjects()->GetTop(); FText ErrorMessage; FText UnusedErrorMessage; const FAssetData NoAssetData; const FAssetData TargetAssetData(TargetAsset); if( ActorFactory->CanCreateActorFrom( TargetAssetData, ErrorMessage ) ) { // Replace all selected actors with actors created from the specified factory GEditor->ReplaceSelectedActors( ActorFactory, TargetAssetData ); } else if ( ActorFactory->CanCreateActorFrom( NoAssetData, UnusedErrorMessage ) ) { // Replace all selected actors with actors created from the specified factory GEditor->ReplaceSelectedActors( ActorFactory, NoAssetData ); } else { FNotificationInfo ErrorNotification( ErrorMessage ); ErrorNotification.Image = FEditorStyle::GetBrush(TEXT("MessageLog.Error")); ErrorNotification.bFireAndForget = true; ErrorNotification.ExpireDuration = 3.0f; // Need this message to last a little longer than normal since the user may want to "Show Log" ErrorNotification.bUseThrobber = true; FSlateNotificationManager::Get().AddNotification(ErrorNotification); } } else { // No actor factory was found; use SpawnActor instead. GUnrealEd->Exec( GetWorld(), *FString::Printf( TEXT("ACTOR REPLACE CLASS=%s"), *ActorClass->GetName() ) ); } } } bool FLevelEditorActionCallbacks::Duplicate_CanExecute() { TArray ActiveModes; GLevelEditorModeTools().GetActiveModes( ActiveModes ); for( int32 ModeIndex = 0; ModeIndex < ActiveModes.Num(); ++ModeIndex ) { const EEditAction::Type CanProcess = ActiveModes[ModeIndex]->GetActionEditDuplicate(); if (CanProcess == EEditAction::Process) { return true; } else if (CanProcess == EEditAction::Halt) { return false; } } // If we can copy, we can duplicate bool bCanCopy = false; if (GEditor->GetSelectedComponentCount() > 0) { TArray SelectedComponents; for (FSelectionIterator It(GEditor->GetSelectedComponentIterator()); It; ++It) { SelectedComponents.Add(CastChecked(*It)); } bCanCopy = FComponentEditorUtils::CanCopyComponents(SelectedComponents); } else { UWorld* World = GetWorld(); if (World) { bCanCopy = GUnrealEd->CanCopySelectedActorsToClipboard(World); } } if (!bCanCopy) { TWeakPtr LevelEditor = FModuleManager::GetModuleChecked(TEXT("LevelEditor")).GetLevelEditorInstance(); if (LevelEditor.IsValid()) { TSharedPtr SceneOutlinerPtr = LevelEditor.Pin()->GetSceneOutliner(); if (SceneOutlinerPtr.IsValid()) { bCanCopy = SceneOutlinerPtr->Copy_CanExecute(); } } } return bCanCopy; } bool FLevelEditorActionCallbacks::Delete_CanExecute() { TArray ActiveModes; GLevelEditorModeTools().GetActiveModes( ActiveModes ); for( int32 ModeIndex = 0; ModeIndex < ActiveModes.Num(); ++ModeIndex ) { const EEditAction::Type CanProcess = ActiveModes[ModeIndex]->GetActionEditDelete(); if (CanProcess == EEditAction::Process) { return true; } else if (CanProcess == EEditAction::Halt) { return false; } } bool bCanDelete = false; if (GEditor->GetSelectedComponentCount() > 0) { TArray SelectedComponents; for (FSelectionIterator It(GEditor->GetSelectedComponentIterator()); It; ++It) { SelectedComponents.Add(CastChecked(*It)); } bCanDelete = FComponentEditorUtils::CanDeleteComponents(SelectedComponents); } else { UWorld* World = GetWorld(); if (World) { bCanDelete = GUnrealEd->CanDeleteSelectedActors(World, true, false); } } if (!bCanDelete) { TWeakPtr LevelEditor = FModuleManager::GetModuleChecked(TEXT("LevelEditor")).GetLevelEditorInstance(); if (LevelEditor.IsValid()) { TSharedPtr SceneOutlinerPtr = LevelEditor.Pin()->GetSceneOutliner(); if (SceneOutlinerPtr.IsValid()) { bCanDelete = SceneOutlinerPtr->Delete_CanExecute(); } } } return bCanDelete; } void FLevelEditorActionCallbacks::Rename_Execute() { UActorComponent* Component = Cast(*GEditor->GetSelectedComponentIterator()); if (Component) { GEditor->BroadcastLevelComponentRequestRename(Component); } else if (AActor* Actor = Cast(*GEditor->GetSelectedActorIterator())) { GEditor->BroadcastLevelActorRequestRename(Actor); } else { TWeakPtr LevelEditor = FModuleManager::GetModuleChecked(TEXT("LevelEditor")).GetLevelEditorInstance(); if (LevelEditor.IsValid()) { TSharedPtr SceneOutlinerPtr = LevelEditor.Pin()->GetSceneOutliner(); if (SceneOutlinerPtr.IsValid()) { SceneOutlinerPtr->Rename_Execute(); } } } } bool FLevelEditorActionCallbacks::Rename_CanExecute() { bool bCanRename = false; if (GEditor->GetSelectedComponentCount() == 1) { if (UActorComponent* ComponentToRename = GEditor->GetSelectedComponents()->GetTop()) { // We can't edit non-instance components or the default scene root bCanRename = ComponentToRename->CreationMethod == EComponentCreationMethod::Instance && ComponentToRename->GetFName() != USceneComponent::GetDefaultSceneRootVariableName(); } } else { bCanRename = GEditor->GetSelectedActorCount() == 1; } if (!bCanRename) { TWeakPtr LevelEditor = FModuleManager::GetModuleChecked(TEXT("LevelEditor")).GetLevelEditorInstance(); if (LevelEditor.IsValid()) { TSharedPtr SceneOutlinerPtr = LevelEditor.Pin()->GetSceneOutliner(); if (SceneOutlinerPtr.IsValid()) { bCanRename = SceneOutlinerPtr->Rename_CanExecute(); } } } return bCanRename; } bool FLevelEditorActionCallbacks::Cut_CanExecute() { TArray ActiveModes; GLevelEditorModeTools().GetActiveModes( ActiveModes ); for( int32 ModeIndex = 0; ModeIndex < ActiveModes.Num(); ++ModeIndex ) { const EEditAction::Type CanProcess = ActiveModes[ModeIndex]->GetActionEditCut(); if (CanProcess == EEditAction::Process) { return true; } else if (CanProcess == EEditAction::Halt) { return false; } } bool bCanCut = false; if (GEditor->GetSelectedComponentCount() > 0) { // Make sure the components can be copied and deleted TArray SelectedComponents; for (FSelectionIterator It(GEditor->GetSelectedComponentIterator()); It; ++It) { SelectedComponents.Add(CastChecked(*It)); } bCanCut = FComponentEditorUtils::CanCopyComponents(SelectedComponents) && FComponentEditorUtils::CanDeleteComponents(SelectedComponents); } else { // For actors, if we can copy, we can cut UWorld* World = GetWorld(); if (World) { bCanCut = GUnrealEd->CanCopySelectedActorsToClipboard(World); } } if (!bCanCut) { TWeakPtr LevelEditor = FModuleManager::GetModuleChecked(TEXT("LevelEditor")).GetLevelEditorInstance(); if (LevelEditor.IsValid()) { TSharedPtr SceneOutlinerPtr = LevelEditor.Pin()->GetSceneOutliner(); if (SceneOutlinerPtr.IsValid()) { bCanCut = SceneOutlinerPtr->Cut_CanExecute(); } } } return bCanCut; } bool FLevelEditorActionCallbacks::Copy_CanExecute() { TArray ActiveModes; GLevelEditorModeTools().GetActiveModes( ActiveModes ); for( int32 ModeIndex = 0; ModeIndex < ActiveModes.Num(); ++ModeIndex ) { const EEditAction::Type CanProcess = ActiveModes[ModeIndex]->GetActionEditCopy(); if (CanProcess == EEditAction::Process) { return true; } else if (CanProcess == EEditAction::Halt) { return false; } } bool bCanCopy = false; if (GEditor->GetSelectedComponentCount() > 0) { TArray SelectedComponents; for (FSelectionIterator It(GEditor->GetSelectedComponentIterator()); It; ++It) { SelectedComponents.Add(CastChecked(*It)); } bCanCopy = FComponentEditorUtils::CanCopyComponents(SelectedComponents); } else { UWorld* World = GetWorld(); if (World) { bCanCopy = GUnrealEd->CanCopySelectedActorsToClipboard(World); } } if (!bCanCopy) { TWeakPtr LevelEditor = FModuleManager::GetModuleChecked(TEXT("LevelEditor")).GetLevelEditorInstance(); if (LevelEditor.IsValid()) { TSharedPtr SceneOutlinerPtr = LevelEditor.Pin()->GetSceneOutliner(); if (SceneOutlinerPtr.IsValid()) { bCanCopy = SceneOutlinerPtr->Copy_CanExecute(); } } } return bCanCopy; } bool FLevelEditorActionCallbacks::Paste_CanExecute() { TArray ActiveModes; GLevelEditorModeTools().GetActiveModes( ActiveModes ); for( int32 ModeIndex = 0; ModeIndex < ActiveModes.Num(); ++ModeIndex ) { const EEditAction::Type CanProcess = ActiveModes[ModeIndex]->GetActionEditPaste(); if (CanProcess == EEditAction::Process) { return true; } else if (CanProcess == EEditAction::Halt) { return false; } } bool bCanPaste = false; if (GEditor->GetSelectedComponentCount() > 0) { if(ensureMsgf(GEditor->GetSelectedActorCount() == 1, TEXT("Expected SelectedActorCount to be 1 but was %d"), GEditor->GetSelectedActorCount())) { auto SelectedActor = CastChecked(*GEditor->GetSelectedActorIterator()); bCanPaste = FComponentEditorUtils::CanPasteComponents(SelectedActor->GetRootComponent()); } } else { UWorld* World = GetWorld(); if (World) { bCanPaste = GUnrealEd->CanPasteSelectedActorsFromClipboard(World); } } if (!bCanPaste) { TWeakPtr LevelEditor = FModuleManager::GetModuleChecked(TEXT("LevelEditor")).GetLevelEditorInstance(); if (LevelEditor.IsValid()) { TSharedPtr SceneOutlinerPtr = LevelEditor.Pin()->GetSceneOutliner(); if (SceneOutlinerPtr.IsValid()) { bCanPaste = SceneOutlinerPtr->Paste_CanExecute(); } } } return bCanPaste; } bool FLevelEditorActionCallbacks::PasteHere_CanExecute() { return Paste_CanExecute(); // For now, just do the same check as Paste } void FLevelEditorActionCallbacks::ExecuteExecCommand( FString Command ) { UWorld* OldWorld = nullptr; // The play world needs to be selected if it exists if (GIsEditor && GEditor->PlayWorld && !GIsPlayInEditorWorld) { OldWorld = SetPlayInEditorWorld(GEditor->PlayWorld); } GUnrealEd->Exec(GetWorld(), *Command); // Restore the old world if there was one if (OldWorld) { RestoreEditorWorld(OldWorld); } } void FLevelEditorActionCallbacks::OnSelectAllActorsOfClass( bool bArchetype ) { GEditor->SelectAllActorsWithClass( bArchetype ); } void FLevelEditorActionCallbacks::OnSelectComponentOwnerActor() { auto ComponentOwner = Cast(*GEditor->GetSelectedActorIterator()); check(ComponentOwner); GEditor->SelectNone(true, true, false); GEditor->SelectActor(ComponentOwner, true, true, true); } bool FLevelEditorActionCallbacks::CanSelectComponentOwnerActor() { return GEditor->GetSelectedComponentCount() > 0; } void FLevelEditorActionCallbacks::OnSelectAllActorsControlledByMatinee() { GEditor->SelectAllActorsControlledByMatinee(); } void FLevelEditorActionCallbacks::OnSelectOwningHLODCluster() { if (GEditor->GetSelectedActorCount() > 0) { AActor* Actor = Cast(GEditor->GetSelectedActors()->GetSelectedObject(0)); FHierarchicalLODUtilitiesModule& Module = FModuleManager::LoadModuleChecked("HierarchicalLODUtilities"); IHierarchicalLODUtilities* Utilities = Module.GetUtilities(); ALODActor* ParentActor = Utilities->GetParentLODActor(Actor); if (Actor && ParentActor) { GEditor->SelectNone(false, true); GEditor->SelectActor(ParentActor, true, false); GEditor->NoteSelectionChange(); } } } void FLevelEditorActionCallbacks::OnSelectMatineeActor( AMatineeActor * ActorToSelect ) { GEditor->SelectNone( false, true ); GEditor->SelectActor(ActorToSelect, true, false, true); GEditor->NoteSelectionChange(); } void FLevelEditorActionCallbacks::OnSelectMatineeGroup( AActor* Actor ) { if( GLevelEditorModeTools().IsModeActive( FBuiltinEditorModes::EM_InterpEdit ) ) { FEdModeInterpEdit* InterpEditMode = (FEdModeInterpEdit*)GLevelEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_InterpEdit ); if ( InterpEditMode && InterpEditMode->MatineeActor ) { InterpEditMode->UpdateSelectedActor(); } } } void FLevelEditorActionCallbacks::OnApplyMaterialToSurface() { FEditorDelegates::LoadSelectedAssetsIfNeeded.Broadcast(); GUnrealEd->Exec( GetWorld(), TEXT("POLY SETMATERIAL") ); } void FLevelEditorActionCallbacks::OnSelectAllLights() { GEditor->GetSelectedActors()->BeginBatchSelectOperation(); // Select all light actors. for( ALight* Light : TActorRange(GetWorld()) ) { GUnrealEd->SelectActor( Light, true, false, false ); } GEditor->GetSelectedActors()->EndBatchSelectOperation(); } void FLevelEditorActionCallbacks::OnSelectStationaryLightsExceedingOverlap() { GEditor->SelectNone( true, true ); for( FActorIterator It(GetWorld()); It; ++It ) { AActor* Actor = *It; TInlineComponentArray Components; Actor->GetComponents(Components); for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { ULightComponent* LightComponent = Components[ComponentIndex]; if (LightComponent->GetOwner() // Use the component's lighting properties to determine if this is a stationary light, instead of checking the actor type // Because blueprint lights may be operating as stationary lights && LightComponent->HasStaticShadowing() && !LightComponent->HasStaticLighting() && LightComponent->bAffectsWorld && LightComponent->CastShadows && LightComponent->CastStaticShadows && LightComponent->PreviewShadowMapChannel == INDEX_NONE) { GUnrealEd->SelectActor( LightComponent->GetOwner(), true, true, false ); } } } } void FLevelEditorActionCallbacks::OnSurfaceAlignment( ETexAlign AlignmentMode ) { GTexAlignTools.GetAligner( AlignmentMode )->Align( GetWorld(), AlignmentMode ); } void FLevelEditorActionCallbacks::RegroupActor_Clicked() { UActorGroupingUtils::Get()->GroupSelected(); } void FLevelEditorActionCallbacks::UngroupActor_Clicked() { UActorGroupingUtils::Get()->UngroupSelected(); } void FLevelEditorActionCallbacks::LockGroup_Clicked() { UActorGroupingUtils::Get()->LockSelectedGroups(); } void FLevelEditorActionCallbacks::UnlockGroup_Clicked() { UActorGroupingUtils::Get()->UnlockSelectedGroups(); } void FLevelEditorActionCallbacks::AddActorsToGroup_Clicked() { UActorGroupingUtils::Get()->AddSelectedToGroup(); } void FLevelEditorActionCallbacks::RemoveActorsFromGroup_Clicked() { UActorGroupingUtils::Get()->RemoveSelectedFromGroup(); } void FLevelEditorActionCallbacks::LocationGridSnap_Clicked() { GUnrealEd->Exec( GetWorld(), *FString::Printf( TEXT("MODE GRID=%d"), !GetDefault()->GridEnabled ? 1 : 0 ) ); } bool FLevelEditorActionCallbacks::LocationGridSnap_IsChecked() { return GetDefault()->GridEnabled; } void FLevelEditorActionCallbacks::RotationGridSnap_Clicked() { GUnrealEd->Exec( GetWorld(), *FString::Printf( TEXT("MODE ROTGRID=%d"), !GetDefault()->RotGridEnabled ? 1 : 0 ) ); } bool FLevelEditorActionCallbacks::RotationGridSnap_IsChecked() { return GetDefault()->RotGridEnabled; } void FLevelEditorActionCallbacks::ScaleGridSnap_Clicked() { GUnrealEd->Exec( GetWorld(), *FString::Printf( TEXT("MODE SCALEGRID=%d"), !GetDefault()->SnapScaleEnabled ? 1 : 0 ) ); } bool FLevelEditorActionCallbacks::ScaleGridSnap_IsChecked() { return GetDefault()->SnapScaleEnabled; } bool FLevelEditorActionCallbacks::SaveAnimationFromSkeletalMeshComponent(AActor * EditorActor, AActor * SimActor, TArray & OutEditorComponents) { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked( TEXT("LevelEditor") ); // currently blueprint actors don't work because their property can't get copied over. if (Cast(EditorActor->GetClass()) != nullptr) { return false; } // find all skel components TInlineComponentArray SimSkelComponents; SimActor->GetComponents(SimSkelComponents); if(SimSkelComponents.Num() > 0) { // see if simulating, bool bSimulating = false; for (auto & Comp : SimSkelComponents) { bSimulating |= (Comp->SkeletalMesh && Comp->SkeletalMesh->Skeleton && Comp->IsSimulatingPhysics()); } // if any of them are legitimately simulating if (bSimulating) { // ask users if you'd like to make an animation FFormatNamedArguments Args; Args.Add(TEXT("ActorName"), FText::FromString(GetNameSafe(EditorActor))); FText AskQuestion = FText::Format(LOCTEXT("KeepSimulationChanges_AskSaveAnimation", "Would you like to save animations from simulation for {ActorName} actor"), Args); if(EAppReturnType::Yes == FMessageDialog::Open(EAppMsgType::YesNo, AskQuestion)) { for (auto & Comp : SimSkelComponents) { if (Comp->SkeletalMesh && Comp->SkeletalMesh->Skeleton && Comp->IsSimulatingPhysics()) { // now record to animation class UAnimSequence* Sequence = LevelEditorModule.OnCaptureSingleFrameAnimSequence().IsBound() ? LevelEditorModule.OnCaptureSingleFrameAnimSequence().Execute(Comp) : nullptr; if(Sequence) { Comp->SetAnimationMode(EAnimationMode::AnimationSingleNode); Comp->AnimationData.AnimToPlay = Sequence; Comp->SetAnimation(Sequence); Comp->SetSimulatePhysics(false); // add the matching component to EditorCompoennts class USkeletalMeshComponent * MatchingComponent = Cast(EditorUtilities::FindMatchingComponentInstance(Comp, EditorActor)); if (MatchingComponent) { OutEditorComponents.Add(MatchingComponent); } else { UE_LOG(LevelEditorActions, Warning, TEXT("Matching component could not be found %s(%s)"), *GetNameSafe(Comp), *GetNameSafe(EditorActor)); } } } } return true; } } } return false; } void FLevelEditorActionCallbacks::OpenMergeActor_Clicked() { FGlobalTabmanager::Get()->InvokeTab(FName("MergeActors")); } void FLevelEditorActionCallbacks::OnKeepSimulationChanges() { // @todo simulate: There are lots of types of changes that can't be "kept", like attachment or newly-spawned actors. This // feature currently only supports propagating changes to regularly-editable properties on an instance of a PIE actor // that still exists in the editor world. // Make sure we have some actors selected, and PIE is running if( GEditor->GetSelectedActorCount() > 0 && GEditor->PlayWorld != NULL ) { int32 UpdatedActorCount = 0; int32 TotalCopiedPropertyCount = 0; FString FirstUpdatedActorLabel; { const FScopedTransaction Transaction( NSLOCTEXT( "LevelEditorCommands", "KeepSimulationChanges", "Keep Simulation Changes" ) ); TArray ComponentsToReinitialize; for( auto ActorIt( GEditor->GetSelectedActorIterator() ); ActorIt; ++ActorIt ) { auto* SimWorldActor = CastChecked( *ActorIt ); // Find our counterpart actor AActor* EditorWorldActor = EditorUtilities::GetEditorWorldCounterpartActor( SimWorldActor ); if( EditorWorldActor != NULL ) { SaveAnimationFromSkeletalMeshComponent(EditorWorldActor, SimWorldActor, ComponentsToReinitialize); // We only want to copy CPF_Edit properties back, or properties that are set through editor manipulation // NOTE: This needs to match what we're doing in the BuildSelectedActorInfo() function const auto CopyOptions = ( EditorUtilities::ECopyOptions::Type )( EditorUtilities::ECopyOptions::CallPostEditChangeProperty | EditorUtilities::ECopyOptions::CallPostEditMove | EditorUtilities::ECopyOptions::OnlyCopyEditOrInterpProperties | EditorUtilities::ECopyOptions::FilterBlueprintReadOnly); const int32 CopiedPropertyCount = EditorUtilities::CopyActorProperties( SimWorldActor, EditorWorldActor, CopyOptions ); if( CopiedPropertyCount > 0 ) { ++UpdatedActorCount; TotalCopiedPropertyCount += CopiedPropertyCount; if( FirstUpdatedActorLabel.IsEmpty() ) { FirstUpdatedActorLabel = EditorWorldActor->GetActorLabel(); } } } // need to reinitialize animation for (auto MeshComp : ComponentsToReinitialize) { if(MeshComp->SkeletalMesh) { MeshComp->InitAnim(true); } } } } // Let the user know what happened { FNotificationInfo NotificationInfo( FText::GetEmpty() ); NotificationInfo.bFireAndForget = true; NotificationInfo.FadeInDuration = 0.25f; NotificationInfo.FadeOutDuration = 1.0f; NotificationInfo.ExpireDuration = 1.0f; NotificationInfo.bUseLargeFont = false; NotificationInfo.bUseSuccessFailIcons = true; NotificationInfo.bAllowThrottleWhenFrameRateIsLow = false; // Don't throttle as it causes distracting hitches in Simulate mode SNotificationItem::ECompletionState CompletionState; if( UpdatedActorCount > 0 ) { if( UpdatedActorCount > 1 ) { FFormatNamedArguments Args; Args.Add( TEXT("UpdatedActorCount"), UpdatedActorCount ); Args.Add( TEXT("TotalCopiedPropertyCount"), TotalCopiedPropertyCount ); NotificationInfo.Text = FText::Format( NSLOCTEXT( "LevelEditorCommands", "KeepSimulationChanges_MultipleActorsUpdatedNotification", "Saved state for {UpdatedActorCount} actors ({TotalCopiedPropertyCount} properties)" ), Args ); } else { FFormatNamedArguments Args; Args.Add( TEXT("FirstUpdatedActorLabel"), FText::FromString( FirstUpdatedActorLabel ) ); Args.Add( TEXT("TotalCopiedPropertyCount"), TotalCopiedPropertyCount ); NotificationInfo.Text = FText::Format( NSLOCTEXT( "LevelEditorCommands", "KeepSimulationChanges_ActorUpdatedNotification", "Saved state for {FirstUpdatedActorLabel} ({TotalCopiedPropertyCount} properties)" ), Args ); } CompletionState = SNotificationItem::CS_Success; } else { NotificationInfo.Text = NSLOCTEXT( "LevelEditorCommands", "KeepSimulationChanges_NoActorsUpdated", "No properties were copied" ); CompletionState = SNotificationItem::CS_Fail; } const auto Notification = FSlateNotificationManager::Get().AddNotification( NotificationInfo ); Notification->SetCompletionState( CompletionState ); } } } bool FLevelEditorActionCallbacks::CanExecuteKeepSimulationChanges() { return AssetSelectionUtils::GetSelectedActorInfo().NumSimulationChanges > 0; } void FLevelEditorActionCallbacks::OnMakeSelectedActorLevelCurrent() { GUnrealEd->MakeSelectedActorsLevelCurrent(); } void FLevelEditorActionCallbacks::OnMoveSelectedToCurrentLevel() { UEditorLevelUtils::MoveSelectedActorsToLevel(GetWorld()->GetCurrentLevel()); } void FLevelEditorActionCallbacks::OnFindActorLevelInContentBrowser() { GEditor->SyncActorLevelsToContentBrowser(); } bool FLevelEditorActionCallbacks::CanExecuteFindActorLevelInContentBrowser() { return GEditor->CanSyncActorLevelsToContentBrowser(); } void FLevelEditorActionCallbacks::OnFindLevelsInLevelBrowser() { const bool bDeselectOthers = true; GEditor->SelectLevelInLevelBrowser( bDeselectOthers ); } void FLevelEditorActionCallbacks::OnSelectLevelInLevelBrowser() { const bool bDeselectOthers = false; GEditor->SelectLevelInLevelBrowser( bDeselectOthers ); } void FLevelEditorActionCallbacks::OnDeselectLevelInLevelBrowser() { GEditor->DeselectLevelInLevelBrowser(); } void FLevelEditorActionCallbacks::OnFindActorInLevelScript() { GUnrealEd->FindSelectedActorsInLevelScript(); } void FLevelEditorActionCallbacks::OnShowWorldProperties( TWeakPtr< SLevelEditor > LevelEditor ) { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked( TEXT("LevelEditor") ); LevelEditorModule.GetLevelEditorTabManager()->InvokeTab(FName("WorldSettingsTab")); } void FLevelEditorActionCallbacks::OpenContentBrowser() { FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); ContentBrowserModule.Get().FocusPrimaryContentBrowser(true); } void FLevelEditorActionCallbacks::OpenMarketplace() { FUnrealEdMisc::Get().OpenMarketplace(); } void FLevelEditorActionCallbacks::ToggleVR() { IVREditorModule& VREditorModule = IVREditorModule::Get(); VREditorModule.EnableVREditor( !VREditorModule.IsVREditorEnabled() ); } bool FLevelEditorActionCallbacks::ToggleVR_CanExecute() { IVREditorModule& VREditorModule = IVREditorModule::Get(); return VREditorModule.IsVREditorAvailable(); } bool FLevelEditorActionCallbacks::ToggleVR_IsButtonActive() { IVREditorModule& VREditorModule = IVREditorModule::Get(); return VREditorModule.IsVREditorButtonActive(); } bool FLevelEditorActionCallbacks::ToggleVR_IsChecked() { IVREditorModule& VREditorModule = IVREditorModule::Get(); return VREditorModule.IsVREditorEnabled(); } bool FLevelEditorActionCallbacks::CanSelectGameModeBlueprint() { bool bCheckOutNeeded = false; FString ConfigFilePath = FPaths::ConvertRelativePathToFull(FString::Printf(TEXT("%sDefaultEngine.ini"), *FPaths::SourceConfigDir())); if(ISourceControlModule::Get().IsEnabled()) { // note: calling QueueStatusUpdate often does not spam status updates as an internal timer prevents this //ISourceControlModule::Get().QueueStatusUpdate(ConfigFilePath); ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(ConfigFilePath, EStateCacheUsage::Use); bCheckOutNeeded = SourceControlState.IsValid() && SourceControlState->CanCheckout(); } else { bCheckOutNeeded = (FPaths::FileExists(ConfigFilePath) && IFileManager::Get().IsReadOnly(*ConfigFilePath)); } return !bCheckOutNeeded; } void FLevelEditorActionCallbacks::OpenLevelBlueprint( TWeakPtr< SLevelEditor > LevelEditor ) { if( LevelEditor.Pin()->GetWorld()->GetCurrentLevel() ) { ULevelScriptBlueprint* LevelScriptBlueprint = LevelEditor.Pin()->GetWorld()->PersistentLevel->GetLevelScriptBlueprint(); if (LevelScriptBlueprint) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; FAssetEditorManager::Get().OpenEditorForAsset( LevelScriptBlueprint, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, LevelEditor.Pin() ); } else { FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "UnableToCreateLevelScript", "Unable to find or create a level blueprint for this level.") ); } } } void FLevelEditorActionCallbacks::CreateBlankBlueprintClass() { // Use the BlueprintFactory to allow the user to pick a parent class for the new Blueprint class UBlueprintFactory* NewFactory = Cast(NewObject(GetTransientPackage(), UBlueprintFactory::StaticClass())); FEditorDelegates::OnConfigureNewAssetProperties.Broadcast(NewFactory); if ( NewFactory->ConfigureProperties() ) { UClass* SelectedClass = NewFactory->ParentClass; // Now help the user pick a path and name for the new Blueprint UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprintFromClass(NSLOCTEXT("LevelEditorCommands", "CreateBlankBlueprintClass_Title", "Create Blank Blueprint Class"), SelectedClass); if( Blueprint ) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; FAssetEditorManager::Get().OpenEditorForAsset( Blueprint, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone); } } } bool FLevelEditorActionCallbacks::CanHarvestSelectedActorsIntoBlueprintClass() { return GEditor->GetSelectedActorCount() > 0; } void FLevelEditorActionCallbacks::HarvestSelectedActorsIntoBlueprintClass() { const bool bHarvest = true; FCreateBlueprintFromActorDialog::OpenDialog(bHarvest); } bool FLevelEditorActionCallbacks::CanSubclassSelectedActorIntoBlueprintClass() { bool bCanSubclass = GEditor->GetSelectedActorCount() == 1; if (bCanSubclass) { AActor* Actor = Cast(*GEditor->GetSelectedActorIterator()); bCanSubclass = FKismetEditorUtilities::CanCreateBlueprintOfClass(Actor->GetClass()); } return bCanSubclass; } void FLevelEditorActionCallbacks::SubclassSelectedActorIntoBlueprintClass() { const bool bHarvest = false; FCreateBlueprintFromActorDialog::OpenDialog(bHarvest); } void FLevelEditorActionCallbacks::CheckOutProjectSettingsConfig( ) { FString ConfigFilePath = FPaths::ConvertRelativePathToFull(FString::Printf(TEXT("%sDefaultEngine.ini"), *FPaths::SourceConfigDir())); if(ISourceControlModule::Get().IsEnabled()) { USourceControlHelpers::CheckOutOrAddFile(ConfigFilePath); } else { FPlatformFileManager::Get().GetPlatformFile().SetReadOnly(*ConfigFilePath, false); } } void FLevelEditorActionCallbacks::OnShowOnlySelectedActors() { const FScopedTransaction Transaction( NSLOCTEXT( "LevelEditorCommands", "ShowOnlySelectedActors", "Show Only Selected Actors" ) ); // First hide unselected as this will also hide group actor members GUnrealEd->edactHideUnselected( GetWorld() ); // Then unhide selected to ensure that everything that's selected will be unhidden GUnrealEd->edactUnhideSelected(GetWorld()); } void FLevelEditorActionCallbacks::OnToggleTransformWidgetVisibility() { GLevelEditorModeTools().SetShowWidget( !GLevelEditorModeTools().GetShowWidget() ); GUnrealEd->RedrawAllViewports(); } bool FLevelEditorActionCallbacks::OnGetTransformWidgetVisibility() { return GLevelEditorModeTools().GetShowWidget(); } void FLevelEditorActionCallbacks::OnAllowTranslucentSelection() { auto* Settings = GetMutableDefault(); // Toggle 'allow select translucent' Settings->bAllowSelectTranslucent = !Settings->bAllowSelectTranslucent; Settings->PostEditChange(); // Need to refresh hit proxies as we changed what should be rendered into them GUnrealEd->RedrawAllViewports(); } bool FLevelEditorActionCallbacks::OnIsAllowTranslucentSelectionEnabled() { return GetDefault()->bAllowSelectTranslucent == true; } void FLevelEditorActionCallbacks::OnAllowGroupSelection() { AGroupActor::ToggleGroupMode(); } bool FLevelEditorActionCallbacks::OnIsAllowGroupSelectionEnabled() { return UActorGroupingUtils::IsGroupingActive(); } void FLevelEditorActionCallbacks::OnToggleStrictBoxSelect() { ULevelEditorViewportSettings* ViewportSettings = GetMutableDefault(); ViewportSettings->bStrictBoxSelection = !ViewportSettings->bStrictBoxSelection; ViewportSettings->PostEditChange(); } bool FLevelEditorActionCallbacks::OnIsStrictBoxSelectEnabled() { return GetDefault()->bStrictBoxSelection; } void FLevelEditorActionCallbacks::OnToggleTransparentBoxSelect() { ULevelEditorViewportSettings* ViewportSettings = GetMutableDefault(); ViewportSettings->bTransparentBoxSelection = !ViewportSettings->bTransparentBoxSelection; ViewportSettings->PostEditChange(); } bool FLevelEditorActionCallbacks::OnIsTransparentBoxSelectEnabled() { return GetDefault()->bTransparentBoxSelection; } void FLevelEditorActionCallbacks::OnDrawBrushMarkerPolys() { GEditor->Exec( GetWorld(), *FString::Printf( TEXT("MODE SHOWBRUSHMARKERPOLYS=%d"), !GEditor->bShowBrushMarkerPolys ? 1 : 0 ) ); GEditor->SaveConfig(); } bool FLevelEditorActionCallbacks::OnIsDrawBrushMarkerPolysEnabled() { return GEditor->bShowBrushMarkerPolys; } void FLevelEditorActionCallbacks::OnToggleOnlyLoadVisibleInPIE() { ULevelEditorPlaySettings* PlaySettings = GetMutableDefault(); PlaySettings->bOnlyLoadVisibleLevelsInPIE = !PlaySettings->bOnlyLoadVisibleLevelsInPIE; PlaySettings->PostEditChange(); PlaySettings->SaveConfig(); } bool FLevelEditorActionCallbacks::OnIsOnlyLoadVisibleInPIEEnabled() { return GetDefault()->bOnlyLoadVisibleLevelsInPIE; } void FLevelEditorActionCallbacks::OnToggleSocketSnapping() { GEditor->bEnableSocketSnapping = !GEditor->bEnableSocketSnapping; GEditor->RedrawLevelEditingViewports(); } bool FLevelEditorActionCallbacks::OnIsSocketSnappingEnabled() { return GEditor->bEnableSocketSnapping; } void FLevelEditorActionCallbacks::OnToggleParticleSystemLOD() { GEngine->bEnableEditorPSysRealtimeLOD = !GEngine->bEnableEditorPSysRealtimeLOD; GEditor->RedrawLevelEditingViewports(); } bool FLevelEditorActionCallbacks::OnIsParticleSystemLODEnabled() { return GEditor->bEnableEditorPSysRealtimeLOD; } void FLevelEditorActionCallbacks::OnToggleFreezeParticleSimulation() { IConsoleManager& ConsoleManager = IConsoleManager::Get(); IConsoleVariable* CVar = ConsoleManager.FindConsoleVariable(TEXT("FX.FreezeParticleSimulation")); if (CVar) { CVar->Set(CVar->GetInt() == 0 ? 1 : 0, ECVF_SetByConsole); } } bool FLevelEditorActionCallbacks::OnIsParticleSimulationFrozen() { IConsoleManager& ConsoleManager = IConsoleManager::Get(); static const auto* CVar = ConsoleManager.FindConsoleVariable(TEXT("FX.FreezeParticleSimulation")); if (CVar) { return CVar->GetInt() != 0; } return false; } void FLevelEditorActionCallbacks::OnToggleParticleSystemHelpers() { GEditor->bDrawParticleHelpers = !GEditor->bDrawParticleHelpers; } bool FLevelEditorActionCallbacks::OnIsParticleSystemHelpersEnabled() { return GEditor->bDrawParticleHelpers; } void FLevelEditorActionCallbacks::OnToggleLODViewLocking() { GEditor->bEnableLODLocking = !GEditor->bEnableLODLocking; GEditor->RedrawLevelEditingViewports(); } bool FLevelEditorActionCallbacks::OnIsLODViewLockingEnabled() { return GEditor->bEnableLODLocking; } void FLevelEditorActionCallbacks::OnToggleLevelStreamingVolumePrevis() { ULevelEditorViewportSettings* ViewportSettings = GetMutableDefault(); ViewportSettings->bLevelStreamingVolumePrevis = !ViewportSettings->bLevelStreamingVolumePrevis; ViewportSettings->PostEditChange(); } bool FLevelEditorActionCallbacks::OnIsLevelStreamingVolumePrevisEnabled() { return GetDefault()->bLevelStreamingVolumePrevis; } FText FLevelEditorActionCallbacks::GetAudioVolumeToolTip() { if ( !GEditor->IsRealTimeAudioMuted() ) { const float Volume = GEditor->GetRealTimeAudioVolume() * 100.0f; return FText::AsNumber( FMath::RoundToInt(Volume) ); } return NSLOCTEXT("UnrealEd", "Muted", "Muted" ); } float FLevelEditorActionCallbacks::GetAudioVolume() { return GEditor->GetRealTimeAudioVolume(); } void FLevelEditorActionCallbacks::OnAudioVolumeChanged(float Volume) { GEditor->SetRealTimeAudioVolume(Volume); } bool FLevelEditorActionCallbacks::GetAudioMuted() { return GEditor->IsRealTimeAudioMuted(); } void FLevelEditorActionCallbacks::OnAudioMutedChanged(bool bMuted) { GEditor->MuteRealTimeAudio(bMuted); } void FLevelEditorActionCallbacks::SnapObjectToView_Clicked() { const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "SnapObjectToView", "Snap Object to View")); for (FSelectionIterator It(GEditor->GetSelectedActorIterator()); It; ++It) { AActor* Actor = Cast(*It); Actor->Modify(); FVector Location = GCurrentLevelEditingViewportClient->GetViewLocation(); FRotator Rotation = GCurrentLevelEditingViewportClient->GetViewRotation(); Actor->SetActorLocation(Location); Actor->SetActorRotation(Rotation); } } void FLevelEditorActionCallbacks::OnEnableActorSnap() { FSnappingUtils::EnableActorSnap( !FSnappingUtils::IsSnapToActorEnabled() ); // If the setting is enabled and there's no distance, revert to default if ( FSnappingUtils::IsSnapToActorEnabled() && FSnappingUtils::GetActorSnapDistance() == 0.0f ) { FSnappingUtils::SetActorSnapDistance( 1.0f ); } } bool FLevelEditorActionCallbacks::OnIsActorSnapEnabled() { return FSnappingUtils::IsSnapToActorEnabled(); } void FLevelEditorActionCallbacks::OnEnableVertexSnap() { ULevelEditorViewportSettings* ViewportSettings = GetMutableDefault(); ViewportSettings->bSnapVertices = !ViewportSettings->bSnapVertices; } bool FLevelEditorActionCallbacks::OnIsVertexSnapEnabled() { return GetDefault()->bSnapVertices; } FText FLevelEditorActionCallbacks::GetActorSnapTooltip() { // If the setting is enabled, return the distance, otherwise say disabled if ( FSnappingUtils::IsSnapToActorEnabled() ) { static const FNumberFormattingOptions FormatOptions = FNumberFormattingOptions() .SetMinimumFractionalDigits(2) .SetMaximumFractionalDigits(2); return FText::AsNumber( FSnappingUtils::GetActorSnapDistance(), &FormatOptions ); } return NSLOCTEXT("UnrealEd", "Disabled", "Disabled" ); } float FLevelEditorActionCallbacks::GetActorSnapSetting() { // If the setting is enabled, return the distance, otherwise say 0 if (FSnappingUtils::IsSnapToActorEnabled() ) { return FSnappingUtils::GetActorSnapDistance(true) ; } return 0.0f; } void FLevelEditorActionCallbacks::SetActorSnapSetting(float Distance) { FSnappingUtils::SetActorSnapDistance( Distance ); // If the distance is 0, disable the setting until it's > 0 FSnappingUtils::EnableActorSnap( ( Distance > 0.0f ? true : false ) ); } void FLevelEditorActionCallbacks::OnToggleHideViewportUI() { GLevelEditorModeTools().SetHideViewportUI( !GLevelEditorModeTools().IsViewportUIHidden() ); } bool FLevelEditorActionCallbacks::IsViewportUIHidden() { return GLevelEditorModeTools().IsViewportUIHidden(); } bool FLevelEditorActionCallbacks::IsEditorModeActive( FEditorModeID EditorMode ) { return GLevelEditorModeTools().IsModeActive( EditorMode ); } void FLevelEditorActionCallbacks::OnAddVolume( UClass* VolumeClass ) { GUnrealEd->Exec( GetWorld(), *FString::Printf( TEXT("BRUSH ADDVOLUME CLASS=%s"), *VolumeClass->GetName() ) ); // A new volume actor was added, update the volumes visibility. // This volume should be hidden if the user doesn't have this type of volume visible. GUnrealEd->UpdateVolumeActorVisibility( VolumeClass ); GEditor->RedrawAllViewports(); } void FLevelEditorActionCallbacks::OnAddMatinee() { // Warn the user prior to creating our actor if ( GEditor->ShouldOpenMatinee( NULL ) ) { // Spawn a matinee actor at the origin, and either move infront of the camera or focus camera on it (depending on the viewport) and open for edit UActorFactory* ActorFactory = GEditor->FindActorFactoryForActorClass( AMatineeActor::StaticClass() ); check( ActorFactory ); AMatineeActor* MatineeActor = CastChecked( FLevelEditorActionCallbacks::AddActor( ActorFactory, FAssetData(), &FTransform::Identity ) ); if( GCurrentLevelEditingViewportClient->IsPerspective() ) { GEditor->MoveActorInFrontOfCamera( *MatineeActor, GCurrentLevelEditingViewportClient->GetViewLocation(), GCurrentLevelEditingViewportClient->GetViewRotation().Vector() ); } else { GEditor->MoveViewportCamerasToActor( *MatineeActor, false ); } GEditor->OpenMatinee( MatineeActor, false ); } } void FLevelEditorActionCallbacks::SelectActorsInLayers() { // Iterate over selected actors and make a list of all layers the selected actors belong to. TArray< FName > SelectedLayers; for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It ) { AActor* Actor = Cast( *It ); // Add them to the list of selected layers. for( int32 LayerIndex = 0 ; LayerIndex < Actor->Layers.Num() ; ++LayerIndex ) { SelectedLayers.AddUnique( Actor->Layers[ LayerIndex ] ); } } bool bSelect = true; bool bNotify = true; GEditor->Layers->SelectActorsInLayers( SelectedLayers, bSelect, bNotify ); } void FLevelEditorActionCallbacks::SetWidgetMode( FWidget::EWidgetMode WidgetMode ) { if( !GLevelEditorModeTools().IsTracking() ) { GLevelEditorModeTools().SetWidgetMode( WidgetMode ); GEditor->RedrawAllViewports(); } } bool FLevelEditorActionCallbacks::IsWidgetModeActive( FWidget::EWidgetMode WidgetMode ) { return GLevelEditorModeTools().GetWidgetMode() == WidgetMode; } bool FLevelEditorActionCallbacks::CanSetWidgetMode( FWidget::EWidgetMode WidgetMode ) { return GLevelEditorModeTools().UsesTransformWidget(WidgetMode) == true; } bool FLevelEditorActionCallbacks::IsTranslateRotateModeVisible() { return GetDefault()->bAllowTranslateRotateZWidget; } void FLevelEditorActionCallbacks::SetCoordinateSystem( ECoordSystem CoordinateSystem ) { GLevelEditorModeTools().SetCoordSystem( CoordinateSystem ); } bool FLevelEditorActionCallbacks::IsCoordinateSystemActive( ECoordSystem CoordinateSystem ) { return GLevelEditorModeTools().GetCoordSystem() == CoordinateSystem; } void FLevelEditorActionCallbacks::MoveActorToGrid_Clicked( bool InAlign, bool bInPerActor ) { const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "MoveActorToGrid", "Snap Origin to Grid") ); MoveActorTo_Clicked( InAlign, NULL, bInPerActor ); } void FLevelEditorActionCallbacks::MoveActorToActor_Clicked( bool InAlign ) { AActor* Actor = GEditor->GetSelectedActors()->GetBottom(); if( Actor ) { const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "MoveActorToActor", "Snap Origin to Actor") ); MoveActorTo_Clicked( InAlign, Actor ); } } void FLevelEditorActionCallbacks::MoveActorTo_Clicked( const bool InAlign, const AActor* InDestination/* = NULL*/, bool bInPerActor/* = false*/ ) { // Fires ULevel::LevelDirtiedEvent when falling out of scope. FScopedLevelDirtied LevelDirtyCallback; // Update the pivot location. FVector Delta = FVector::ZeroVector; FVector NewLocation = FVector::ZeroVector; FRotator NewRotation = FRotator::ZeroRotator; if(!bInPerActor) { if ( InDestination ) { NewLocation = InDestination->GetActorLocation(); NewRotation = InDestination->GetActorRotation(); GEditor->SetPivot( NewLocation, false, true ); } else { const FVector OldPivot = GEditor->GetPivotLocation(); const FVector NewPivot = OldPivot.GridSnap(GEditor->GetGridSize()); Delta = NewPivot - OldPivot; GEditor->SetPivot( NewPivot, false, true ); } } for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It ) { AActor* Actor = Cast( *It ); checkSlow( Actor ); if ( Actor == InDestination ) // Early out { continue; } Actor->Modify(); if(!InDestination) { if ( bInPerActor ) { const FVector OldPivot = Actor->GetActorLocation(); const FVector NewPivot = OldPivot.GridSnap(GEditor->GetGridSize()); Delta = NewPivot - OldPivot; GEditor->SetPivot( NewPivot, false, true ); } NewLocation = Actor->GetActorLocation() + Delta; } Actor->TeleportTo( NewLocation, ( !InAlign ? Actor->GetActorRotation() : NewRotation ), false, true ); Actor->InvalidateLightingCache(); Actor->UpdateComponentTransforms(); Actor->MarkPackageDirty(); LevelDirtyCallback.Request(); } GEditor->RedrawLevelEditingViewports(); GEditor->RebuildAlteredBSP(); // Update the Bsp of any levels containing a modified brush } void FLevelEditorActionCallbacks::SnapTo2DLayer_Clicked() { // Fires ULevel::LevelDirtiedEvent when falling out of scope. FScopedLevelDirtied LevelDirtyCallback; const ULevelEditorViewportSettings* ViewportSettings = GetDefault(); const ULevelEditor2DSettings* Settings2D = GetDefault(); if (Settings2D->SnapLayers.IsValidIndex(ViewportSettings->ActiveSnapLayerIndex)) { const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "SnapSelection2D", "Snap Selection to 2D Layer")); float SnapDepth = Settings2D->SnapLayers[ViewportSettings->ActiveSnapLayerIndex].Depth; USelection* SelectedActors = GEditor->GetSelectedActors(); for (FSelectionIterator Iter(*SelectedActors); Iter; ++Iter) { AActor* Actor = CastChecked(*Iter); // Only snap actors that are not attached to something else if (Actor->GetAttachParentActor() == nullptr) { FTransform Transform = Actor->GetTransform(); FVector CurrentLocation = Transform.GetLocation(); switch (Settings2D->SnapAxis) { case ELevelEditor2DAxis::X: CurrentLocation.X = SnapDepth; break; case ELevelEditor2DAxis::Y: CurrentLocation.Y = SnapDepth; break; case ELevelEditor2DAxis::Z: CurrentLocation.Z = SnapDepth; break; } Transform.SetLocation(CurrentLocation); Actor->Modify(); Actor->SetActorTransform(Transform); Actor->InvalidateLightingCache(); Actor->UpdateComponentTransforms(); Actor->MarkPackageDirty(); LevelDirtyCallback.Request(); } } GEditor->RedrawLevelEditingViewports(true); GEditor->RebuildAlteredBSP(); } } bool FLevelEditorActionCallbacks::CanSnapTo2DLayer() { const ULevelEditor2DSettings* Settings = GetDefault(); return Settings->SnapLayers.IsValidIndex(GetDefault()->ActiveSnapLayerIndex) && (GEditor->GetSelectedActorCount() > 0); } void FLevelEditorActionCallbacks::MoveSelectionToDifferent2DLayer_Clicked(bool bGoingUp, bool bForceToTopOrBottom) { // Change the active layer first const ULevelEditor2DSettings* Settings2D = GetDefault(); ULevelEditorViewportSettings* SettingsVP = GetMutableDefault(); const int32 NumLayers = Settings2D->SnapLayers.Num(); if (NumLayers > 0) { if (bGoingUp && (SettingsVP->ActiveSnapLayerIndex > 0)) { SettingsVP->ActiveSnapLayerIndex = bForceToTopOrBottom ? 0 : (SettingsVP->ActiveSnapLayerIndex - 1); SettingsVP->PostEditChange(); } else if (!bGoingUp && ((SettingsVP->ActiveSnapLayerIndex + 1) < NumLayers)) { SettingsVP->ActiveSnapLayerIndex = bForceToTopOrBottom ? (NumLayers - 1) : (SettingsVP->ActiveSnapLayerIndex + 1); SettingsVP->PostEditChange(); } } // Snap the selection to the new active layer SnapTo2DLayer_Clicked(); } bool FLevelEditorActionCallbacks::CanMoveSelectionToDifferent2DLayer(bool bGoingUp) { const ULevelEditor2DSettings* Settings2D = GetDefault(); const ULevelEditorViewportSettings* SettingsVP = GetMutableDefault(); const int32 SelectedIndex = SettingsVP->ActiveSnapLayerIndex; const int32 NumLayers = Settings2D->SnapLayers.Num(); const bool bHasLayerInDirection = bGoingUp ? (SelectedIndex > 0) : (SelectedIndex + 1 < NumLayers); const bool bHasActorSelected = GEditor->GetSelectedActorCount() > 0; // Allow it even if there is no layer in the corresponding direction, to let it double as a snap operation shortcut even when at the end stops return bHasLayerInDirection || bHasActorSelected; } void FLevelEditorActionCallbacks::Select2DLayerDeltaAway_Clicked(int32 Delta) { const ULevelEditor2DSettings* Settings2D = GetDefault(); ULevelEditorViewportSettings* SettingsVP = GetMutableDefault(); const int32 SelectedIndex = SettingsVP->ActiveSnapLayerIndex; const int32 NumLayers = Settings2D->SnapLayers.Num(); if (NumLayers > 0) { const int32 NewIndex = ((NumLayers + SelectedIndex + Delta) % NumLayers); SettingsVP->ActiveSnapLayerIndex = NewIndex; SettingsVP->PostEditChange(); } } void FLevelEditorActionCallbacks::SnapToFloor_Clicked( bool InAlign, bool InUseLineTrace, bool InUseBounds, bool InUsePivot ) { const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "SnapActorsToFloor", "Snap Actors To Floor") ); SnapTo_Clicked( InAlign, InUseLineTrace, InUseBounds, InUsePivot ); } void FLevelEditorActionCallbacks::SnapActorToActor_Clicked( bool InAlign, bool InUseLineTrace, bool InUseBounds, bool InUsePivot ) { AActor* Actor = GEditor->GetSelectedActors()->GetBottom(); if( Actor ) { const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "SnapActorsToActor", "Snap Actors To Actor") ); SnapTo_Clicked( InAlign, InUseLineTrace, InUseBounds, InUsePivot, Actor ); } } void FLevelEditorActionCallbacks::SnapTo_Clicked( const bool InAlign, const bool InUseLineTrace, const bool InUseBounds, const bool InUsePivot, AActor* InDestination ) { // Fires ULevel::LevelDirtiedEvent when falling out of scope. FScopedLevelDirtied LevelDirtyCallback; bool bSnappedComponents = false; if( GEditor->GetSelectedComponentCount() > 0 ) { for(FSelectedEditableComponentIterator It(GEditor->GetSelectedEditableComponentIterator()); It; ++It) { USceneComponent* SceneComponent = Cast(*It); if(SceneComponent) { SceneComponent->Modify(); AActor* ActorOwner = SceneComponent->GetOwner(); bSnappedComponents = true; if(ActorOwner) { ActorOwner->Modify(); GEditor->SnapObjectTo(FActorOrComponent(SceneComponent), InAlign, InUseLineTrace, InUseBounds, InUsePivot, FActorOrComponent(InDestination)); ActorOwner->InvalidateLightingCache(); ActorOwner->UpdateComponentTransforms(); LevelDirtyCallback.Request(); } } } USceneComponent* LastComp = GEditor->GetSelectedComponents()->GetBottom(); GEditor->SetPivot(LastComp->GetComponentLocation(), false, true); } if( !bSnappedComponents ) { for(FSelectionIterator It(GEditor->GetSelectedActorIterator()); It; ++It) { AActor* Actor = Cast(*It); if(Actor) { Actor->Modify(); GEditor->SnapObjectTo(FActorOrComponent(Actor), InAlign, InUseLineTrace, InUseBounds, InUsePivot, FActorOrComponent(InDestination)); Actor->InvalidateLightingCache(); Actor->UpdateComponentTransforms(); LevelDirtyCallback.Request(); } } AActor* Actor = GEditor->GetSelectedActors()->GetBottom(); if(Actor) { GEditor->SetPivot(Actor->GetActorLocation(), false, true); if(UActorGroupingUtils::IsGroupingActive()) { // set group pivot for the root-most group AGroupActor* ActorGroupRoot = AGroupActor::GetRootForActor(Actor, true, true); if(ActorGroupRoot) { ActorGroupRoot->CenterGroupLocation(); } } } } GEditor->RedrawLevelEditingViewports(); } void FLevelEditorActionCallbacks::AlignBrushVerticesToGrid_Execute() { UWorld* World = GUnrealEd->GetWorld(); GEditor->Exec(World, TEXT("ACTOR ALIGN VERTS")); } bool FLevelEditorActionCallbacks::ActorSelected_CanExecute() { // Had to have something selected return ( ( GEditor->GetSelectedActorCount() > 0 ) ? true : false ); } bool FLevelEditorActionCallbacks::ActorsSelected_CanExecute() { // Has to have more than one selected return ( ( GEditor->GetSelectedActorCount() > 1 ) ? true : false ); } void FLevelEditorActionCallbacks::GeometryCollection_SelectAllGeometry() { GEditor->Exec(GetWorld(), TEXT("GeometryCollection.SelectAllGeometry")); } void FLevelEditorActionCallbacks::GeometryCollection_SelectNone() { GEditor->Exec(GetWorld(), TEXT("GeometryCollection.SelectNone")); } void FLevelEditorActionCallbacks::GeometryCollection_SelectInverseGeometry() { GEditor->Exec(GetWorld(), TEXT("GeometryCollection.SelectInverseGeometry")); } bool FLevelEditorActionCallbacks::GeometryCollection_IsChecked() { return true; } class UWorld* FLevelEditorActionCallbacks::GetWorld() { return GEditor->GetEditorWorldContext().World(); } /** UI_COMMAND takes long for the compile to optimize */ PRAGMA_DISABLE_OPTIMIZATION void FLevelEditorCommands::RegisterCommands() { UI_COMMAND( BrowseDocumentation, "Documentation...", "Opens the main documentation page, and allows you to search across all UE4 support sites.", EUserInterfaceActionType::Button, FInputChord( EKeys::F1 ) ); UI_COMMAND( BrowseAPIReference, "API Reference...", "Opens the API reference documentation", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( BrowseCVars, "Console Variables", "Creates an HTML file to browse the console variables and commands (console command 'help')", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( BrowseViewportControls, "Viewport Controls...", "Opens the viewport controls cheat sheet", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( NewLevel, "New Level...", "Create a new level, or choose a level template to start from.", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Control, EKeys::N ) ); UI_COMMAND( OpenLevel, "Open Level...", "Loads an existing level", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Control, EKeys::O ) ); UI_COMMAND( Save, "Save Current", "Saves the current level to disk", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::S) ); UI_COMMAND( SaveAs, "Save Current As...", "Save the current level as...", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Control|EModifierKey::Alt, EKeys::S ) ); UI_COMMAND( SaveAllLevels, "Save All Levels", "Saves all unsaved levels to disk", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ToggleFavorite, "Toggle Favorite", "Sets whether the currently loaded level will appear in the list of favorite levels", EUserInterfaceActionType::Button, FInputChord() ); for( int32 CurRecentIndex = 0; CurRecentIndex < FLevelEditorCommands::MaxRecentFiles; ++CurRecentIndex ) { // NOTE: The actual label and tool-tip will be overridden at runtime when the command is bound to a menu item, however // we still need to set one here so that the key bindings UI can function properly TSharedRef< FUICommandInfo > OpenRecentFile = FUICommandInfoDecl( this->AsShared(), FName( *FString::Printf( TEXT( "OpenRecentFile%i" ), CurRecentIndex ) ), FText::Format( NSLOCTEXT( "LevelEditorCommands", "OpenRecentFile", "Open Recent File {0}" ), FText::AsNumber( CurRecentIndex ) ), NSLOCTEXT( "LevelEditorCommands", "OpenRecentFileToolTip", "Opens a recently opened file" ) ) .UserInterfaceType( EUserInterfaceActionType::Button ) .DefaultChord( FInputChord() ); OpenRecentFileCommands.Add( OpenRecentFile ); } for (int32 CurFavoriteIndex = 0; CurFavoriteIndex < FLevelEditorCommands::MaxFavoriteFiles; ++CurFavoriteIndex) { // NOTE: The actual label and tool-tip will be overridden at runtime when the command is bound to a menu item, however // we still need to set one here so that the key bindings UI can function properly TSharedRef< FUICommandInfo > OpenFavoriteFile = FUICommandInfoDecl( this->AsShared(), FName(*FString::Printf(TEXT("OpenFavoriteFile%i"), CurFavoriteIndex)), FText::Format(NSLOCTEXT("LevelEditorCommands", "OpenFavoriteFile", "Open Favorite File {0}"), FText::AsNumber(CurFavoriteIndex)), NSLOCTEXT("LevelEditorCommands", "OpenFavoriteFileToolTip", "Opens a favorite file")) .UserInterfaceType(EUserInterfaceActionType::Button) .DefaultChord(FInputChord()); OpenFavoriteFileCommands.Add(OpenFavoriteFile); } UI_COMMAND( ImportScene, "Import Into Level...", "Imports a scene from a FBX or T3D format into the current level", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND( ExportAll, "Export All...", "Exports the entire level to a file on disk (multiple formats are supported.)", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ExportSelected, "Export Selected...", "Exports currently-selected objects to a file on disk (multiple formats are supported.)", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( Build, "Build All Levels", "Builds all levels (precomputes lighting data and visibility data, generates navigation networks and updates brush models.)\nThis action is not available while Play in Editor is active, or when previewing less than Shader Model 5", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( BuildAndSubmitToSourceControl, "Build and Submit...", "Displays a window that allows you to build all levels and submit them to source control", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( BuildLightingOnly, "Build Lighting", "Only precomputes lighting (all levels.)\nThis action is not available while Play in Editor is active, or when previewing less than Shader Model 5", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control|EModifierKey::Shift, EKeys::Semicolon) ); UI_COMMAND( BuildReflectionCapturesOnly, "Build Reflection Captures", "Updates Reflection Captures and stores their data in the BuildData package.\nThis action is not available while Play in Editor is active, or when previewing less than Shader Model 5", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( BuildLightingOnly_VisibilityOnly, "Precompute Static Visibility", "Only precomputes static visibility data (all levels.)", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( LightingBuildOptions_UseErrorColoring, "Use Error Coloring", "When enabled, errors during lighting precomputation will be baked as colors into light map data", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( LightingBuildOptions_ShowLightingStats, "Show Lighting Stats", "When enabled, a window containing metrics about lighting performance and memory will be displayed after a successful build.", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( BuildGeometryOnly, "Build Geometry", "Only builds geometry (all levels.)", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( BuildGeometryOnly_OnlyCurrentLevel, "Build Geometry (Current Level)", "Builds geometry, only for the current level", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( BuildPathsOnly, "Build Paths", "Only builds paths (all levels.)", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( BuildLODsOnly, "Build LODs", "Only builds LODs (all levels.)", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( BuildTextureStreamingOnly, "Build Texture Streaming", "Build texture streaming data", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( LightingQuality_Production, "Production", "Sets precomputed lighting quality to highest possible quality (slowest computation time.)", EUserInterfaceActionType::RadioButton, FInputChord() ); UI_COMMAND( LightingQuality_High, "High", "Sets precomputed lighting quality to high quality", EUserInterfaceActionType::RadioButton, FInputChord() ); UI_COMMAND( LightingQuality_Medium, "Medium", "Sets precomputed lighting quality to medium quality", EUserInterfaceActionType::RadioButton, FInputChord() ); UI_COMMAND( LightingQuality_Preview, "Preview", "Sets precomputed lighting quality to preview quality (fastest computation time.)", EUserInterfaceActionType::RadioButton, FInputChord() ); UI_COMMAND( LightingDensity_RenderGrayscale, "Render Grayscale", "Renders the lightmap density.", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( LightingResolution_CurrentLevel, "Current Level", "Adjust only primitives in the current level.", EUserInterfaceActionType::RadioButton, FInputChord() ); UI_COMMAND( LightingResolution_SelectedLevels, "Selected Levels", "Adjust only primitives in the selected levels.", EUserInterfaceActionType::RadioButton, FInputChord() ); UI_COMMAND( LightingResolution_AllLoadedLevels, "All Loaded Levels", "Adjust primitives in all loaded levels.", EUserInterfaceActionType::RadioButton, FInputChord() ); UI_COMMAND( LightingResolution_SelectedObjectsOnly, "Selected Objects Only", "Adjust only selected objects in the levels.", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( LightingStaticMeshInfo, "Lighting StaticMesh Info...", "Shows the lighting information for the StaticMeshes.", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SceneStats, "Open Scene Stats", "Opens the Scene Stats viewer", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( TextureStats, "Open Texture Stats", "Opens the Texture Stats viewer", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MapCheck, "Open Map Check", "Checks map for errors", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( RecompileGameCode, "Recompile Game Code", "Recompiles and reloads C++ code for game systems on the fly", EUserInterfaceActionType::Button, FInputChord( EKeys::P, EModifierKey::Alt | EModifierKey::Control | EModifierKey::Shift ) ); #if WITH_LIVE_CODING UI_COMMAND( LiveCoding_Enable, "Enable Live Coding (Experimental)", "Hot-patches C++ function changes into the current process. Currently does not allow class layout changes.", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( LiveCoding_StartSession, "Start Session", "Starts a live coding session.", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( LiveCoding_ShowConsole, "Show Console", "Displays the live coding console window.", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( LiveCoding_Settings, "Settings...", "Open the live coding settings", EUserInterfaceActionType::Button, FInputChord() ); #endif UI_COMMAND( EditAsset, "Edit Asset", "Edits the asset associated with the selected actor", EUserInterfaceActionType::Button, FInputChord( EKeys::E, EModifierKey::Control ) ); UI_COMMAND( EditAssetNoConfirmMultiple, "Edit Asset", "Edits the asset associated with the selected actor", EUserInterfaceActionType::Button, FInputChord( EKeys::E, EModifierKey::Control | EModifierKey::Shift ) ); UI_COMMAND( GoHere, "Go Here", "Moves the camera to the current mouse position", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SnapCameraToObject, "Snap View to Object", "Snaps the view to the selected object", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SnapObjectToCamera, "Snap Object to View", "Snaps the selected object to the view", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( GoToCodeForActor, "Go to C++ Code for Actor", "Opens a code editing IDE and navigates to the source file associated with the seleced actor", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( GoToDocsForActor, "Go to Documentation for Actor", "Opens documentation for the Actor in the default web browser", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( PasteHere, "Paste Here", "Pastes the actor at the click location", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SnapOriginToGrid, "Snap Origin to Grid", "Snaps the actor to the nearest grid location at its origin", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Control, EKeys::End ) ); UI_COMMAND( SnapOriginToGridPerActor, "Snap Origin to Grid Per Actor", "Snaps each selected actor separately to the nearest grid location at its origin", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( AlignOriginToGrid, "Align Origin to Grid", "Aligns the actor to the nearest grid location at its origin", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SnapTo2DLayer, "Snap to 2D Layer", "Snaps the actor to the current 2D snap layer", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Control, EKeys::SpaceBar ) ); UI_COMMAND( MoveSelectionUpIn2DLayers, "Bring selection forward a snap layer", "Bring selection forward a snap layer", EUserInterfaceActionType::Button, FInputChord(EKeys::PageUp, EModifierKey::Control) ); UI_COMMAND( MoveSelectionDownIn2DLayers, "Send selection backward a snap layer", "Send selection backward a snap layer", EUserInterfaceActionType::Button, FInputChord(EKeys::PageDown, EModifierKey::Control) ); UI_COMMAND( MoveSelectionToTop2DLayer, "Bring selection to the front snap layer", "Bring selection to the front snap layer", EUserInterfaceActionType::Button, FInputChord(EKeys::PageUp, EModifierKey::Shift | EModifierKey::Control) ); UI_COMMAND( MoveSelectionToBottom2DLayer, "Send selection to the back snap layer", "Send selection to the back snap layer", EUserInterfaceActionType::Button, FInputChord(EKeys::PageDown, EModifierKey::Shift | EModifierKey::Control) ); UI_COMMAND( Select2DLayerAbove, "Select next 2D layer", "Changes the active layer to the next 2D layer", EUserInterfaceActionType::Button, FInputChord(EKeys::PageUp, EModifierKey::Alt) ); UI_COMMAND( Select2DLayerBelow, "Select previous 2D layer", "Changes the active layer to the previous 2D layer", EUserInterfaceActionType::Button, FInputChord(EKeys::PageDown, EModifierKey::Alt) ); UI_COMMAND( SnapToFloor, "Snap to Floor", "Snaps the actor or component to the floor below it", EUserInterfaceActionType::Button, FInputChord( EKeys::End ) ); UI_COMMAND( AlignToFloor, "Align to Floor", "Aligns the actor or component with the floor", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SnapPivotToFloor, "Snap Pivot to Floor", "Snaps the actor to the floor at its pivot point", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Alt, EKeys::End ) ); UI_COMMAND( AlignPivotToFloor, "Align Pivot to Floor", "Aligns the actor with the floor at its pivot point", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SnapBottomCenterBoundsToFloor, "Snap Bottom Center Bounds to Floor", "Snaps the actor to the floor at its bottom center bounds", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Shift, EKeys::End ) ); UI_COMMAND( AlignBottomCenterBoundsToFloor, "Align Bottom Center Bounds to Floor", "Aligns the actor with the floor at its bottom center bounds", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SnapOriginToActor, "Snap Origin to Actor", "SNaps the actor to another actor at its origin", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( AlignOriginToActor, "Align Origin to Actor", "Aligns the actor to another actor at its origin", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SnapToActor, "Snap to Actor", "Snaps the actor to another actor", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( AlignToActor, "Align to Actor", "Aligns the actor with another actor", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SnapPivotToActor, "Snap Pivot to Actor", "Snaps the actor to another actor at its pivot point", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( AlignPivotToActor, "Align Pivot to Actor", "Aligns the actor with another actor at its pivot point", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SnapBottomCenterBoundsToActor, "Snap Bottom Center Bounds to Actor", "Snaps the actor to another actor at its bottom center bounds", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( AlignBottomCenterBoundsToActor, "Align Bottom Center Bounds to Actor", "Aligns the actor with another actor at its bottom center bounds", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( DeltaTransformToActors, "Delta Transform", "Apply Delta Transform to selected actors", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MirrorActorX, "Mirror X", "Mirrors the actor along the X axis", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MirrorActorY, "Mirror Y", "Mirrors the actor along the Y axis", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MirrorActorZ, "Mirror Z", "Mirrors the actor along the Z axis", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( LockActorMovement, "Lock Actor Movement", "Locks the actor so it cannot be moved", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( DetachFromParent, "Detach", "Detach the actor from its parent", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( AttachSelectedActors, "Attach Selected Actors", "Attach the selected actors to the last selected actor", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Alt, EKeys::B) ); UI_COMMAND( AttachActorIteractive, "Attach Actor Interactive", "Start an interactive actor picker to let you choose a parent for the currently selected actor", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Alt, EKeys::A) ); UI_COMMAND( CreateNewOutlinerFolder, "Create Folder", "Place the selected actors in a new folder", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( HoldToEnableVertexSnapping, "Hold to Enable Vertex Snapping", "When the key binding is pressed and held vertex snapping will be enabled", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::V) ); //@ todo Slate better tooltips for pivot options UI_COMMAND( SavePivotToPrePivot, "Set as Pivot Offset", "Sets the current pivot location as the pivot offset for this actor", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ResetPrePivot, "Reset Pivot Offset", "Resets the pivot offset for this actor", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ResetPivot, "Reset Pivot", "Resets the pivot", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MovePivotHere, "Set Pivot Offset Here", "Sets the pivot offset to the clicked location", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MovePivotHereSnapped, "Set Pivot Offset Here (Snapped)", "Sets the pivot offset to the nearest grid point to the clicked location", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MovePivotToCenter, "Center on Selection", "Centers the pivot to the middle of the selection", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ConvertToAdditive, "Additive", "Converts the selected brushes to additive brushes", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ConvertToSubtractive, "Subtractive", "Converts the selected brushes to subtractive brushes", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( OrderFirst, "To First", "Changes the drawing order of the selected brushes so they are the first to draw", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( OrderLast, "To Last", "Changes the drawing order of the selected brushes so they are the last to draw", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MakeSolid, "Solid", "Makes the selected brushes solid", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MakeSemiSolid, "Semi-Solid", "Makes the selected brushes semi-solid", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MakeNonSolid, "Non-Solid", "Makes the selected brushes non-solid", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MergePolys, "Merge", "Merges multiple polygons on a brush face into as few as possible", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SeparatePolys, "Separate", "Reverses the effect of a previous merge", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( AlignBrushVerticesToGrid, "Align Brush Verticies To Grid", "Align brush verticies to the grid", EUserInterfaceActionType::Button, FInputChord() ); // RegroupActors uses GroupActors for it's label and tooltip when simply grouping a selection of actors using overrides. This is to provide display of the chord which is the same for both. UI_COMMAND( GroupActors, "Group", "Groups the selected actors", EUserInterfaceActionType::Button, FInputChord( /*EKeys::G, EModifierKey::Control*/ ) ); UI_COMMAND( RegroupActors, "Regroup", "Regroups the selected actors into a new group, removing any current groups in the selection", EUserInterfaceActionType::Button, FInputChord( EKeys::G, EModifierKey::Control ) ); UI_COMMAND( UngroupActors, "Ungroup", "Ungroups the selected actors", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Shift, EKeys::G ) ); UI_COMMAND( AddActorsToGroup, "Add to Group", "Adds the selected actors to the selected group", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( RemoveActorsFromGroup, "Remove from Group", "Removes the selected actors from the selected groups", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( LockGroup, "Lock", "Locks the selected groups", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( UnlockGroup, "Unlock", "Unlocks the selected groups", EUserInterfaceActionType::Button, FInputChord() ); #if PLATFORM_MAC UI_COMMAND( ShowAll, "Show All Actors", "Shows all actors", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Command, EKeys::H ) ); #else UI_COMMAND( ShowAll, "Show All Actors", "Shows all actors", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Control, EKeys::H ) ); #endif UI_COMMAND( ShowSelectedOnly, "Show Only Selected", "Shows only the selected actors", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ShowSelected, "Show Selected", "Shows the selected actors", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Shift, EKeys::H ) ); UI_COMMAND( HideSelected, "Hide Selected", "Hides the selected actors", EUserInterfaceActionType::Button, FInputChord( EKeys::H ) ); UI_COMMAND( ShowAllStartup, "Show All At Startup", "Shows all actors at startup", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ShowSelectedStartup, "Show Selected At Startup", "Shows selected actors at startup", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( HideSelectedStartup, "Hide Selected At Startup", "Hide selected actors at startup", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( CycleNavigationDataDrawn, "Cycle Navigation Data Drawn", "Cycles through navigation data (navmeshes for example) to draw one at a time", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Alt, EKeys::N ) ); UI_COMMAND( SelectNone, "Unselect All", "Unselects all actors", EUserInterfaceActionType::Button, FInputChord( EKeys::Escape ) ) ; UI_COMMAND( InvertSelection, "Invert Selection", "Inverts the current selection", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectImmediateChildren, "Select Immediate Children", "Selects immediate children of the current selection", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Alt|EModifierKey::Control, EKeys::D) ); UI_COMMAND( SelectAllDescendants, "Select All Descendants", "Selects all descendants of the current selection", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Shift|EModifierKey::Control, EKeys::D) ); UI_COMMAND( SelectAllActorsOfSameClass, "Select All Actors of Same Class", "Selects all the actors that have the same class", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift|EModifierKey::Control, EKeys::A) ); UI_COMMAND( SelectAllActorsOfSameClassWithArchetype, "Select All Actors with Same Archetype", "Selects all the actors of the same class that have the same archetype", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectComponentOwnerActor, "Select Component Owner", "Select the actor that owns the currently selected component(s)", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectRelevantLights, "Select Relevant Lights", "Select all lights relevant to the current selection", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectStaticMeshesOfSameClass, "Select All Using Selected Static Meshes (Selected Actor Types)", "Selects all actors with the same static mesh and actor class as the selection", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectOwningHierarchicalLODCluster, "Select Owning Hierarchical LOD cluster Using Selected Static Mesh (Selected Actor Types)", "Select Owning Hierarchical LOD cluster for the selected actor", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND( SelectStaticMeshesAllClasses, "Select All Using Selected Static Meshes (All Actor Types)", "Selects all actors with the same static mesh as the selection", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Shift, EKeys::E ) ); UI_COMMAND( SelectSkeletalMeshesOfSameClass, "Select All Using Selected Skeletal Meshes (Selected Actor Types)", "Selects all actors with the same skeletal mesh and actor class as the selection", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectSkeletalMeshesAllClasses, "Select All Using Selected Skeletal Meshes (All Actor Types)", "Selects all actors with the same skeletal mesh as the selection", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectAllWithSameMaterial, "Select All With Same Material", "Selects all actors with the same material as the selection", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectMatchingEmitter, "Select All Matching Emitters", "Selects all emitters with the same particle system as the selection", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectAllLights, "Select All Lights", "Selects all lights", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectStationaryLightsExceedingOverlap, "Select Stationary Lights exceeding overlap", "Selects all stationary lights exceeding the overlap limit", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectAllAddditiveBrushes, "Select All Additive Brushes", "Selects all additive brushes", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectAllSubtractiveBrushes, "Select All Subtractive Brushes", "Selects all subtractive brushes", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectAllActorsControlledByMatinee, "Select Actors Used by This Matinee", "Selects all actors controlled by this Matinee", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SelectAllSurfaces, "Select All Surfaces", "Selects all bsp surfaces", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::S) ); UI_COMMAND( SurfSelectAllMatchingBrush, "Select Matching Brush", "Selects the surfaces belonging to the same brush as the selected surfaces", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::B) ); UI_COMMAND( SurfSelectAllMatchingTexture, "Select Matching Material", "Selects all surfaces with the same material as the selected surfaces", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::T) ); UI_COMMAND( SurfSelectAllAdjacents, "Select All Adjacent Surfaces", "Selects all surfaces adjacent to the currently selected surfaces", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::J) ); UI_COMMAND( SurfSelectAllAdjacentCoplanars, "Select All Coplanar Surfaces", "Selects all surfaces adjacent and coplanar with the selected surfaces", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::C) ); UI_COMMAND( SurfSelectAllAdjacentWalls, "Select All Adjacent Wall Surfaces", "Selects all adjacent upright surfaces", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::W) ); UI_COMMAND( SurfSelectAllAdjacentFloors, "Select All Adjacent Floor Surfaces", "Selects all adjacent floor sufaces(ones with normals pointing up)", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::U) ); UI_COMMAND( SurfSelectAllAdjacentSlants, "Select All Adjacent Slant Surfaces", "Selects all adjacent slant surfaces (surfaces that are not walls, floors, or ceilings according to their normals) to the currently selected surfaces.", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::Y) ); UI_COMMAND( SurfSelectReverse, "Invert Surface Selection", "Inverts the current surface selection", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::Q) ); UI_COMMAND( SurfSelectMemorize, "Memorize Surface Selection", "Stores the current surface selection in memory", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::M) ); UI_COMMAND( SurfSelectRecall, "Recall Surface Selection", "Replace the current selection with the selection saved in memory", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::R) ); UI_COMMAND( SurfSelectOr, "Surface Selection OR", "Replace the current selection with only the surfaces which are both currently selected and contained within the saved selection in memory", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::O) ); UI_COMMAND( SurfSelectAnd, "Surface Selection AND", "Add the selection of surfaces saved in memory to the current selection", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::A) ); UI_COMMAND( SurfSelectXor, "Surace Selection XOR", " Replace the current selection with only the surfaces that are not in both the current selection and the selection saved in memory", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Shift, EKeys::X) ); UI_COMMAND( SurfUnalign, "Align Surface Default", "Default surface alignment", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SurfAlignPlanarAuto, "Align Surface Planar", "Planar surface alignment", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SurfAlignPlanarWall, "Align Surface Planar Wall", "Planar wall surface alignment", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SurfAlignPlanarFloor, "Align Surface Planar Floor", "Planar floor surface alignment", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SurfAlignBox, "Align Surface Box", "Box surface alignment", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SurfAlignFit, "Align Surface Fit", "Best fit surface alignment", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ApplyMaterialToSurface, "Apply Material to Surface Selection", "Applies the selected material to the selected surfaces", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( CreateBoundingBoxVolume, "Create Bounding Box Blocking Volume From Mesh", "Create a bounding box blocking volume from the static mesh", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( CreateHeavyConvexVolume, "Heavy Convex Blocking Volume From Mesh", "Creates a heavy convex blocking volume from the static mesh", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( CreateNormalConvexVolume, "Normal Convex Blocking Volume From Mesh", "Creates a normal convex blocking volume from the static mesh", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( CreateLightConvexVolume, "Light Convex Blocking Volume From Mesh", "Creates a light convex blocking volume from the static mesh", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( CreateRoughConvexVolume, "Rought Convex Blocking Volume From Mesh", "Creates a rough convex blocking volume from the static mesh", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( KeepSimulationChanges, "Keep Simulation Changes", "Saves the changes made to this actor in Simulate mode to the actor's default state.", EUserInterfaceActionType::Button, FInputChord( EKeys::K ) ); UI_COMMAND( MakeActorLevelCurrent, "Make Selected Actor's Level Current", "Makes the selected actor's level the current level", EUserInterfaceActionType::Button, FInputChord( EKeys::M ) ); #if PLATFORM_MAC UI_COMMAND( MoveSelectedToCurrentLevel, "Move Selection to Current Level", "Moves the selected actors to the current level", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Command, EKeys::M ) ); #else UI_COMMAND( MoveSelectedToCurrentLevel, "Move Selection to Current Level", "Moves the selected actors to the current level", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Control, EKeys::M ) ); #endif UI_COMMAND( FindActorLevelInContentBrowser, "Find Actor Level in Content Browser", "Finds the selected actors' level in the content browser", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( FindLevelsInLevelBrowser, "Find Levels in Level Browser", "Finds the selected actors' levels in the level browser", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( AddLevelsToSelection, "Add Levels to Selection", "Adds the selected actors' levels to the current level browser selection", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( RemoveLevelsFromSelection, "Remove Levels from Selection", "Removes the selected actors' levels from the current level browser selection", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( FindActorInLevelScript, "Find in Level Blueprint", "Finds any references to the selected actor in its level's blueprint", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( WorldProperties, "World Settings", "Displays the world settings", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( OpenContentBrowser, "Open Content Browser", "Opens the Content Browser", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control|EModifierKey::Shift, EKeys::F) ); UI_COMMAND( OpenMarketplace, "Open Marketplace", "Opens the Marketplace", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( AddMatinee, "Add Matinee [Legacy]", "Creates a new matinee actor to edit", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( EditMatinee, "Edit Matinee", "Selects a Matinee to edit", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ToggleVR, "Toggle VR", "Toggles VR (Virtual Reality) mode", EUserInterfaceActionType::ToggleButton, FInputChord( EModifierKey::Alt, EKeys::V ) ); UI_COMMAND( OpenLevelBlueprint, "Open Level Blueprint", "Edit the Level Blueprint for the current level", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( CheckOutProjectSettingsConfig, "Check Out", "Checks out the project settings config file so the game mode can be set.", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( CreateBlankBlueprintClass, "New Empty Blueprint Class...", "Create a new Blueprint Class", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ConvertSelectionToBlueprintViaHarvest, "Convert Selected Components to Blueprint Class...", "Replace all of the selected actors with a new Blueprint Class based on Actor that contains the components", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ConvertSelectionToBlueprintViaSubclass, "Convert Selected Actor to Blueprint Class...", "Replace the selected actor with a new Blueprint subclass based on the class of the selected Actor", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ShowTransformWidget, "Show Transform Widget", "Toggles the visibility of the transform widgets", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( AllowTranslucentSelection, "Allow Translucent Selection", "Allows translucent objects to be selected", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::T) ); UI_COMMAND( AllowGroupSelection, "Allow Group Selection", "Allows actor groups to be selected", EUserInterfaceActionType::ToggleButton, FInputChord(EModifierKey::Control|EModifierKey::Shift, EKeys::G) ); UI_COMMAND( StrictBoxSelect, "Strict Box Selection", "When enabled an object must be entirely encompassed by the selection box when marquee box selecting", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( TransparentBoxSelect, "Box Select Occluded Objects", "When enabled, marquee box select operations will also select objects that are occluded by other objects.", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( DrawBrushMarkerPolys, "Draw Brush Polys", "Draws semi-transparent polygons around a brush when selected", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( OnlyLoadVisibleInPIE, "Only Load Visible Levels in Game Preview", "If enabled, when game preview starts, only visible levels will be loaded", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( ToggleSocketSnapping, "Enable Socket Snapping", "Enables or disables snapping to sockets", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( ToggleParticleSystemLOD, "Enable Particle System LOD Switching", "If enabled particle systems will use distance LOD switching in perspective viewports", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( ToggleFreezeParticleSimulation, "Freeze Particle Simulation", "If enabled particle systems will freeze their simulation state", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( ToggleParticleSystemHelpers, "Toggle Particle System Helpers", "Toggles showing particle system helper widgets in viewports", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( ToggleLODViewLocking, "Enable LOD View Locking", "If enabled viewports of the same type will use the same LOD", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( LevelStreamingVolumePrevis, "Enable Automatic Level Streaming", "If enabled, the viewport will stream in levels automatically when the camera is moved", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( EnableActorSnap, "Enable Actor Snapping", "If enabled, actors will snap to the location of other actors when they are within distance", EUserInterfaceActionType::ToggleButton, FInputChord(EModifierKey::Control|EModifierKey::Shift, EKeys::K) ); UI_COMMAND( EnableVertexSnap, "Enable Vertex Snapping","If enabled, actors will snap to the location of the nearest vertex on another actor in the direction of movement", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( ToggleHideViewportUI, "Hide Viewport UI", "Toggles hidden viewport UI mode. Hides all overlaid viewport UI widgets", EUserInterfaceActionType::ToggleButton, FInputChord() ); //if (FParse::Param( FCommandLine::Get(), TEXT( "editortoolbox" ) )) //{ // UI_COMMAND( BspMode, "Enable Bsp Mode", "Enables BSP mode", EUserInterfaceActionType::ToggleButton, FInputChord( EModifierKey::Shift, EKeys::One ) ); // UI_COMMAND( MeshPaintMode, "Enable Mesh Paint Mode", "Enables mesh paint mode", EUserInterfaceActionType::ToggleButton, FInputChord( EModifierKey::Shift, EKeys::Two ) ); // UI_COMMAND( LandscapeMode, "Enable Landscape Mode", "Enables landscape editing", EUserInterfaceActionType::ToggleButton, FInputChord( EModifierKey::Shift, EKeys::Three ) ); // UI_COMMAND( FoliageMode, "Enable Foliage Mode", "Enables foliage editing", EUserInterfaceActionType::ToggleButton, FInputChord( EModifierKey::Shift, EKeys::Four ) ); //} UI_COMMAND( ShowSelectedDetails, "Show Actor Details", "Opens a details panel for the selected actors", EUserInterfaceActionType::Button, FInputChord( EKeys::F4 ) ); UI_COMMAND( RecompileShaders, "Recompile Changed Shaders", "Recompiles shaders which are out of date", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Shift|EModifierKey::Control, EKeys::Period ) ); UI_COMMAND( ProfileGPU, "Profile GPU", "Profiles the GPU for the next frame and opens a window with profiled data", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Shift|EModifierKey::Control, EKeys::Comma ) ); UI_COMMAND( ResetAllParticleSystems, "Reset All Particle Systems", "Resets all particle system emitters (removes all active particles and restarts them)", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Shift, EKeys::Slash ) ); UI_COMMAND( ResetSelectedParticleSystem, "Resets Selected Particle Systems" , "Resets selected particle system emitters (removes all active particles and restarts them)", EUserInterfaceActionType::Button, FInputChord( EKeys::Slash ) ); UI_COMMAND( SelectActorsInLayers, "Select all actors in selected actor's layers", "Selects all actors belonging to the layers of the currently selected actors", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Control, EKeys::L ) ); UI_COMMAND( FocusAllViewportsToSelection, "Focus Selected Actors in All Viewports", "Moves the camera in front of the selected actors in all open viewports", EUserInterfaceActionType::Button, FInputChord( EKeys::F, EModifierKey::Shift ) ); UI_COMMAND(MaterialQualityLevel_Low, "Low", "Sets material quality in the scene to low.", EUserInterfaceActionType::RadioButton, FInputChord()); UI_COMMAND(MaterialQualityLevel_Medium, "Medium", "Sets material quality in the scene to medium.", EUserInterfaceActionType::RadioButton, FInputChord()); UI_COMMAND(MaterialQualityLevel_High, "High", "Sets material quality in the scene to high.", EUserInterfaceActionType::RadioButton, FInputChord()); UI_COMMAND(ToggleFeatureLevelPreview, "Preview Mode Toggle", "Toggles the Preview Mode on or off for the currently selected Preview target", EUserInterfaceActionType::ToggleButton, FInputChord()); UI_COMMAND(PreviewPlatformOverride_AndroidGLES2, "Android ES2", "Mobile preview using Android's quality settings.", EUserInterfaceActionType::Check, FInputChord()); UI_COMMAND(PreviewPlatformOverride_DefaultES2, "HTML5", "HTML5 preview.", EUserInterfaceActionType::Check, FInputChord()); UI_COMMAND(PreviewPlatformOverride_DefaultES31, "Default High-End Mobile", "Use default mobile settings (no quality overrides).", EUserInterfaceActionType::Check, FInputChord()); UI_COMMAND(PreviewPlatformOverride_AndroidGLES31, "Android ES 3.1", "Mobile preview using Android ES3.1 quality settings.", EUserInterfaceActionType::Check, FInputChord()); UI_COMMAND(PreviewPlatformOverride_AndroidVulkanES31, "Android Vulkan", "Mobile preview using Android Vulkan quality settings.", EUserInterfaceActionType::Check, FInputChord()); UI_COMMAND(PreviewPlatformOverride_IOSMetalES31, "iOS", "Mobile preview using iOS material quality settings.", EUserInterfaceActionType::Check, FInputChord()); UI_COMMAND( ConnectToSourceControl, "Connect to Source Control...", "Opens a dialog to connect to source control.", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND( ChangeSourceControlSettings, "Change Source Control Settings...", "Opens a dialog to change source control settings.", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND( CheckOutModifiedFiles, "Check Out Modified Files...", "Opens a dialog to check out any assets which have been modified.", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND( SubmitToSourceControl, "Submit to Source Control...", "Opens a dialog with check in options for content and levels.", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(GeometryCollectionSelectAllGeometry, "Select All Geometry In Hierarchy", "Select all geometry in hierarchy", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(GeometryCollectionSelectNone, "Deselect All Geometry In Hierarchy", "Deselect all geometry in hierarchy", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(GeometryCollectionSelectInverseGeometry, "Select Inverse Geometry In Hierarchy", "Select inverse geometry in hierarchy", EUserInterfaceActionType::Button, FInputChord()); static const FText FeatureLevelLabels[ERHIFeatureLevel::Num] = { NSLOCTEXT("LevelEditorCommands", "FeatureLevelPreviewType_ES2", "Mobile / HTML5"), NSLOCTEXT("LevelEditorCommands", "FeatureLevelPreviewType_ES31", "High-End Mobile"), NSLOCTEXT("LevelEditorCommands", "FeatureLevelPreviewType_SM4", "Shader Model 4"), NSLOCTEXT("LevelEditorCommands", "FeatureLevelPreviewType_SM5", "Shader Model 5"), }; static const FText FeatureLevelToolTips[ERHIFeatureLevel::Num] = { NSLOCTEXT("LevelEditorCommands", "FeatureLevelPreviewTooltip_ES2", "OpenGLES 2"), NSLOCTEXT("LevelEditorCommands", "FeatureLevelPreviewTooltip_ES3", "OpenGLES 3.1, Metal, Vulkan"), NSLOCTEXT("LevelEditorCommands", "FeatureLevelPreviewTooltip_SM4", "DirectX 10, OpenGL 3.3+"), NSLOCTEXT("LevelEditorCommands", "FeatureLevelPreviewTooltip_SM5", "DirectX 11, OpenGL 4.3+, PS4, XB1"), }; for (int32 i = 0; i < ERHIFeatureLevel::Num; ++i) { FName Name; GetFeatureLevelName((ERHIFeatureLevel::Type)i, Name); FeatureLevelPreview[i] = FUICommandInfoDecl( this->AsShared(), Name, FeatureLevelLabels[i], FeatureLevelToolTips[i]) .UserInterfaceType(EUserInterfaceActionType::Check) .DefaultChord(FInputChord()); } UI_COMMAND(OpenMergeActor, "Merge Actors", "Opens the Merge Actor panel", EUserInterfaceActionType::Button, FInputChord()); } PRAGMA_ENABLE_OPTIMIZATION #undef LOCTEXT_NAMESPACE