// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "LevelEditor.h" #include "Matinee/MatineeActor.h" #include "Engine/LevelScriptBlueprint.h" #include "LightingBuildOptions.h" #include "EditorSupportDelegates.h" #include "SLevelEditor.h" #include "LevelEditorActions.h" #include "SLevelViewport.h" #include "EditorBuildUtils.h" #include "Toolkits/AssetEditorManager.h" #include "ScopedTransaction.h" #include "Editor/Kismet/Public/BlueprintEditorModule.h" #include "Editor/PropertyEditor/Public/PropertyEditorModule.h" #include "Editor/ContentBrowser/Public/ContentBrowserModule.h" #include "Editor/MainFrame/Public/MainFrame.h" #include "Editor/UnrealEd/Private/GeomFitUtils.h" #include "Editor/UnrealEd/Public/BSPOps.h" #include "Editor/LevelEditor/Public/DlgDeltaTransform.h" #include "Runtime/Engine/Classes/PhysicsEngine/BodySetup.h" #include "Editor/NewLevelDialog/Public/NewLevelDialogModule.h" #include "DelegateFilter.h" #include "BlueprintUtilities.h" #include "MRUFavoritesList.h" #include "Editor/SceneOutliner/Private/SSocketChooser.h" #include "SnappingUtils.h" #include "Layers/ILayers.h" #include "IPlacementModeModule.h" #include "AssetSelection.h" #include "IDocumentation.h" #include "SourceCodeNavigation.h" #include "Dialogs/DlgPickAssetPath.h" #include "AssetToolsModule.h" #include "BlueprintEditorUtils.h" #include "KismetEditorUtilities.h" #include "DesktopPlatformModule.h" #include "EngineAnalytics.h" #include "AnalyticsEventAttribute.h" #include "IAnalyticsProvider.h" #include "ReferenceViewer.h" #include "ISizeMapModule.h" #include "Developer/MeshUtilities/Public/MeshUtilities.h" #include "EditorClassUtils.h" #include "ComponentEditorUtils.h" #include "EditorActorFolders.h" #include "ActorPickerMode.h" #include "EngineBuildSettings.h" #include "HotReloadInterface.h" #include "ISourceControlModule.h" #include "SourceControlWindows.h" #include "NotificationManager.h" #include "SNotificationList.h" #include "Engine/Selection.h" #include "EngineUtils.h" #include "Engine/StaticMeshActor.h" #include "Engine/Polys.h" #include "Components/LightComponent.h" #include "Engine/StaticMesh.h" #include "Engine/Light.h" #include "Animation/SkeletalMeshActor.h" #include "Editor/Persona/Public/AnimationRecorder.h" #include "Editor/KismetWidgets/Public/CreateBlueprintFromActorDialog.h" 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; } } bool FLevelEditorActionCallbacks::DefaultCanExecuteAction() { return FSlateApplication::Get().IsNormalExecution(); } void FLevelEditorActionCallbacks::BrowseDocumentation() { IDocumentation::Get()->OpenHome(FDocumentationSourceInfo(TEXT("help_menu"))); } void FLevelEditorActionCallbacks::BrowseAPIReference() { IDocumentation::Get()->OpenAPIHome(); } 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 NewFilename = RecentsAndFavorites->GetMRUItem( RecentFileIndex ); if( RecentsAndFavorites->VerifyMRUFile( RecentFileIndex ) ) { // Prompt the user to save any outstanding changes. if( FEditorFileUtils::SaveDirtyPackages(true, true, false) ) { // 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 FileName = MRUFavoritesList->GetFavoritesItem( FavoriteFileIndex ); if( MRUFavoritesList->VerifyFavoritesFile( FavoriteFileIndex ) ) { // Prompt the user to save any outstanding changes if( FEditorFileUtils::SaveDirtyPackages(true, true, false) ) { // Load the requested level. FEditorFileUtils::LoadMap( FileName ); // Move the item to the head of the list MRUFavoritesList->MoveFavoritesItemToHead( FileName ); } 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 ); FString MapFileName; const bool bMapFileExists = FPackageName::DoesPackageExist(GetWorld()->GetOutermost()->GetName(), NULL, &MapFileName); // If the user clicked the toggle favorites button, the map file should exist, but double check to be safe. if ( bMapFileExists ) { // If the map was already favorited, remove it from the favorites if ( MRUFavoritesList->ContainsFavoritesItem( MapFileName ) ) { MRUFavoritesList->RemoveFavoritesItem( MapFileName ); } // If the map was not already favorited, add it to the favorites else { MRUFavoritesList->AddFavoritesItem( MapFileName ); } } } void FLevelEditorActionCallbacks::RemoveFavorite( int32 FavoriteFileIndex ) { IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked( "MainFrame" ); FMainMRUFavoritesList* MRUFavoritesList = MainFrameModule.GetMRUFavoritesList(); const FString FileName = MRUFavoritesList->GetFavoritesItem( FavoriteFileIndex ); if( MRUFavoritesList->VerifyFavoritesFile( FavoriteFileIndex ) ) { if ( MRUFavoritesList->ContainsFavoritesItem( FileName ) ) { MRUFavoritesList->RemoveFavoritesItem( FileName ); } } } bool FLevelEditorActionCallbacks::ToggleFavorite_CanExecute() { if( GetWorld() && GetWorld()->GetOutermost() ) { FString FileName; const bool bMapFileExists = FPackageName::DoesPackageExist(GetWorld()->GetOutermost()->GetName(), NULL, &FileName); // Disable the favorites button if the map isn't associated to a file yet (new map, never before saved, etc.) return bMapFileExists; } return false; } bool FLevelEditorActionCallbacks::ToggleFavorite_IsChecked() { bool bIsChecked = false; FString FileName; const bool bMapFileExists = FPackageName::DoesPackageExist(GetWorld()->GetOutermost()->GetName(), NULL, &FileName); // If the map exists, determine its state based on whether the map is already favorited or not if ( bMapFileExists ) { IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked( "MainFrame" ); const FString CleanedName = FPaths::ConvertRelativePathToFull(FileName); const bool bCleanAlreadyFavorited = MainFrameModule.GetMRUFavoritesList()->ContainsFavoritesItem( CleanedName ); const bool bAlreadyFavorited = bCleanAlreadyFavorited || MainFrameModule.GetMRUFavoritesList()->ContainsFavoritesItem( FileName ); bIsChecked = bAlreadyFavorited; } return bIsChecked; } bool FLevelEditorActionCallbacks::CanSaveWorld() { return FSlateApplication::Get().IsNormalExecution() && (!GUnrealEd || !GUnrealEd->GetPackageAutoSaver().IsAutoSaving()); } void FLevelEditorActionCallbacks::Save() { FEditorFileUtils::SaveCurrentLevel(); } void FLevelEditorActionCallbacks::SaveAs() { FEditorFileUtils::SaveAs( GetWorld()->PersistentLevel ); } 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::Import_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 = Cast(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(), 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->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, 1); return TestQualityLevel == MaterialQualityLevel; } void FLevelEditorActionCallbacks::SetFeatureLevelPreview(ERHIFeatureLevel::Type InPreviewFeatureLevel) { // Record this feature level as we want to use it for all subsequent level creation and loading GEditor->DefaultWorldFeatureLevel = InPreviewFeatureLevel; GetWorld()->ChangeFeatureLevel(InPreviewFeatureLevel); // Update any currently running PIE sessions. for (TObjectIterator It; It; ++It) { UWorld* ItWorld = *It; if (ItWorld->WorldType == EWorldType::PIE) { ItWorld->ChangeFeatureLevel(InPreviewFeatureLevel); } } GUnrealEd->RedrawAllViewports(); } bool FLevelEditorActionCallbacks::IsFeatureLevelPreviewChecked(ERHIFeatureLevel::Type InPreviewFeatureLevel) { return InPreviewFeatureLevel == GetWorld()->FeatureLevel; } 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 ); } void FLevelEditorActionCallbacks::Build_Execute() { // Reset build options ConfigureLightingBuildOptions( FLightingBuildOptions() ); // Build everything! FEditorBuildUtils::EditorBuild( GetWorld(), EBuildOptions::BuildAll ); } 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(), EBuildOptions::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; } void FLevelEditorActionCallbacks::BuildReflectionCapturesOnly_Execute() { GEditor->UpdateReflectionCaptures(); } void FLevelEditorActionCallbacks::BuildLightingOnly_VisibilityOnly_Execute() { // Configure build options FLightingBuildOptions LightingBuildOptions; LightingBuildOptions.bOnlyBuildVisibility = true; ConfigureLightingBuildOptions( LightingBuildOptions ); // Build lighting! const bool bAllowLightingDialog = false; FEditorBuildUtils::EditorBuild( GetWorld(), EBuildOptions::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(), EBuildOptions::BuildVisibleGeometry ); } void FLevelEditorActionCallbacks::BuildGeometryOnly_OnlyCurrentLevel_Execute() { // Build geometry (current level)! FEditorBuildUtils::EditorBuild( GetWorld(), EBuildOptions::BuildGeometry ); } void FLevelEditorActionCallbacks::BuildPathsOnly_Execute() { // Build paths! FEditorBuildUtils::EditorBuild( GetWorld(), EBuildOptions::BuildAIPaths ); } void FLevelEditorActionCallbacks::BuildLODsOnly_Execute() { // Build paths! FEditorBuildUtils::EditorBuild(GetWorld(), EBuildOptions::BuildHierarchicalLOD); if (BuildLighting_CanExecute()) { BuildLightingOnly_Execute(); } } 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() { 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() { // Don't allow a recompile while already compiling! IHotReloadInterface& HotReloadSupport = FModuleManager::LoadModuleChecked(HotReloadModule); if( !HotReloadSupport.IsCurrentlyCompiling() ) { // Don't wait -- we want compiling to happen asynchronously const bool bWaitForCompletion = false; HotReloadSupport.DoHotReloadFromEditor(bWaitForCompletion); } } bool FLevelEditorActionCallbacks::Recompile_CanExecute() { // We're not able to recompile if a compile is already in progress! IHotReloadInterface& HotReloadSupport = FModuleManager::LoadModuleChecked(HotReloadModule); return !HotReloadSupport.IsCurrentlyCompiling() && !(GEngineVersion.IsPromotedBuild() && FEngineBuildSettings::IsPerforceBuild()); } 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(); if( SelectedActorInfo.SelectionClass != nullptr ) { FString ClassHeaderPath; if( FSourceCodeNavigation::FindClassHeaderPath( SelectedActorInfo.SelectionClass, ClassHeaderPath ) && IFileManager::Get().FileSize( *ClassHeaderPath ) != INDEX_NONE ) { FString AbsoluteHeaderPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*ClassHeaderPath); FSourceCodeNavigation::OpenSourceFile( AbsoluteHeaderPath ); } } } 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::ViewReferences_Execute() { if( GEditor->GetSelectedActorCount() > 0 ) { TArray< UObject* > ReferencedAssets; GEditor->GetReferencedAssetsForEditorSelection( ReferencedAssets ); if (ReferencedAssets.Num() > 0) { TArray< FName > ViewableObjects; for( auto ObjectIter = ReferencedAssets.CreateConstIterator(); ObjectIter; ++ObjectIter ) { // Don't allow user to perform certain actions on objects that aren't actually assets (e.g. Level Script blueprint objects) const auto EditingObject = *ObjectIter; if( EditingObject != NULL && EditingObject->IsAsset() ) { ViewableObjects.Add( EditingObject->GetOuter()->GetFName()); } } IReferenceViewerModule::Get().InvokeReferenceViewerTab(ViewableObjects); } } } bool FLevelEditorActionCallbacks::CanViewReferences() { TArray< UObject* > ReferencedAssets; GEditor->GetReferencedAssetsForEditorSelection(ReferencedAssets); return ReferencedAssets.Num() > 0; } void FLevelEditorActionCallbacks::ViewSizeMap_Execute() { if( GEditor->GetSelectedActorCount() > 0 ) { TArray< UObject* > ReferencedAssets; GEditor->GetReferencedAssetsForEditorSelection( ReferencedAssets ); if (ReferencedAssets.Num() > 0) { TArray< FName > ViewableObjects; for( auto ObjectIter = ReferencedAssets.CreateConstIterator(); ObjectIter; ++ObjectIter ) { // Don't allow user to perform certain actions on objects that aren't actually assets (e.g. Level Script blueprint objects) const auto EditingObject = *ObjectIter; if( EditingObject != NULL && EditingObject->IsAsset() ) { ViewableObjects.Add( EditingObject->GetOuter()->GetFName()); } } ISizeMapModule::Get().InvokeSizeMapTab(ViewableObjects); } } } bool FLevelEditorActionCallbacks::CanViewSizeMap() { TArray< UObject* > ReferencedAssets; GEditor->GetReferencedAssetsForEditorSelection(ReferencedAssets); return ReferencedAssets.Num() > 0; } 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) { 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; 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; static FName FocusOnPoint = FName(TEXT("FocusOnPoint")); FCollisionQueryParams LineParams(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 { bCanCopy = GUnrealEd->CanCopySelectedActorsToClipboard(GetWorld()); } 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 { bCanDelete = GUnrealEd->CanDeleteSelectedActors(GetWorld(), true, false); } return bCanDelete; } void FLevelEditorActionCallbacks::Rename_Execute() { UActorComponent* Component = Cast(*GEditor->GetSelectedComponentIterator()); if (Component) { GEditor->BroadcastLevelComponentRequestRename(Component); } else { AActor* Actor = Cast(*GEditor->GetSelectedActorIterator()); if (Actor) { GEditor->BroadcastLevelActorRequestRename(Actor); } } } 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; } 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 bCanCut = GUnrealEd->CanCopySelectedActorsToClipboard(GetWorld()); } 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 { bCanCopy = GUnrealEd->CanCopySelectedActorsToClipboard(GetWorld()); } 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) { check(GEditor->GetSelectedActorCount() == 1); auto SelectedActor = CastChecked(*GEditor->GetSelectedActorIterator()); bCanPaste = FComponentEditorUtils::CanPasteComponents(SelectedActor->GetRootComponent()); } else { bCanPaste = GUnrealEd->CanPasteSelectedActorsFromClipboard(GetWorld()); } return bCanPaste; } bool FLevelEditorActionCallbacks::PasteHere_CanExecute() { return Paste_CanExecute(); // For now, just do the same check as Paste } void FLevelEditorActionCallbacks::ExecuteExecCommand( FString Command ) { GUnrealEd->Exec( GetWorld(), *Command ); } 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::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( TActorIterator It(GetWorld()); It; ++It ) { GUnrealEd->SelectActor( *It, 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() { GUnrealEd->edactRegroupFromSelected(); } void FLevelEditorActionCallbacks::UngroupActor_Clicked() { GUnrealEd->edactUngroupFromSelected(); } void FLevelEditorActionCallbacks::LockGroup_Clicked() { GUnrealEd->edactLockSelectedGroups(); } void FLevelEditorActionCallbacks::UnlockGroup_Clicked() { GUnrealEd->edactUnlockSelectedGroups(); } void FLevelEditorActionCallbacks::AddActorsToGroup_Clicked() { GUnrealEd->edactAddToGroup(); } void FLevelEditorActionCallbacks::RemoveActorsFromGroup_Clicked() { GUnrealEd->edactRemoveFromGroup(); } void FLevelEditorActionCallbacks::MergeActors_Clicked() { GUnrealEd->edactMergeActors(); } bool FLevelEditorActionCallbacks::CanExecuteMergeActors() { IMeshUtilities* MeshUtilities = FModuleManager::Get().LoadModulePtr("MeshUtilities"); if (MeshUtilities && MeshUtilities->GetMeshMergingInterface() != nullptr) { FSelectedActorInfo Info = AssetSelectionUtils::GetSelectedActorInfo(); return (Info.bHaveStaticMeshComponent || Info.bHaveLandscape); } return false; } void FLevelEditorActionCallbacks::MergeActorsByMaterials_Clicked() { GUnrealEd->edactMergeActorsByMaterials(); } bool FLevelEditorActionCallbacks::CanExecuteMergeActorsByMaterials() { FSelectedActorInfo Info = AssetSelectionUtils::GetSelectedActorInfo(); return Info.bHaveStaticMeshComponent; } 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) { // 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 FAnimationRecorder Recorder; if(Recorder.TriggerRecordAnimation(Comp)) { class UAnimSequence * Sequence = Recorder.GetAnimationObject(); if(Sequence) { Recorder.StopRecord(false); 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::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() { GEditor->MoveSelectedActorsToLevel( GetWorld()->GetCurrentLevel() ); } 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() { IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); if (DesktopPlatform != nullptr) { TArray EventAttributes; if (DesktopPlatform->OpenLauncher(false, TEXT("-OpenMarket"))) { EventAttributes.Add(FAnalyticsEventAttribute(TEXT("OpenSucceeded"), TEXT("TRUE"))); } else { EventAttributes.Add(FAnalyticsEventAttribute(TEXT("OpenSucceeded"), TEXT("FALSE"))); if (EAppReturnType::Yes == FMessageDialog::Open(EAppMsgType::YesNo, LOCTEXT("InstallMarketplacePrompt", "The Marketplace requires the Epic Games Launcher, which does not seem to be installed on your computer. Would you like to install it now?"))) { if (!DesktopPlatform->OpenLauncher(true, TEXT("-OpenMarket"))) { EventAttributes.Add(FAnalyticsEventAttribute(TEXT("InstallSucceeded"), TEXT("FALSE"))); FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("Sorry, there was a problem installing the Launcher.\nPlease try to install it manually!"))); } else { EventAttributes.Add(FAnalyticsEventAttribute(TEXT("InstallSucceeded"), TEXT("TRUE"))); } } } EventAttributes.Add(FAnalyticsEventAttribute(TEXT("Source"), TEXT("EditorToolbar"))); if( FEngineAnalytics::IsAvailable() ) { FEngineAnalytics::GetProvider().RecordEvent(TEXT("Editor.Usage.OpenMarketplace"), EventAttributes); } } } 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() { return GEditor->GetSelectedActorCount() == 1; } 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()) { SourceControlHelpers::CheckOutFile(ConfigFilePath); } else { FPlatformFileManager::Get().GetPlatformFile().SetReadOnly(*ConfigFilePath, false); } } void FLevelEditorActionCallbacks::OnShowOnlySelectedActors() { const FScopedTransaction Transaction( NSLOCTEXT( "LevelEditorCommands", "ShowOnlySelectedActors", "Show Only Selected Actors" ) ); GUnrealEd->edactUnhideSelected( GetWorld() ); GUnrealEd->edactHideUnselected( 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 GUnrealEd->bGroupingActive; } 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.FindTConsoleVariableDataInt(TEXT("FX.FreezeParticleSimulation")); if (CVar) { return CVar->GetValueOnGameThread() != 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() { for (FSelectionIterator It(GEditor->GetSelectedActorIterator()); It; ++It) { AActor* Actor = Cast(*It); 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().GetShowWidget() == 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->IsA(AActor::StaticClass()) ); 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::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(GEditor->bGroupingActive) { // set group pivot for the root-most group AGroupActor* ActorGroupRoot = AGroupActor::GetRootForActor(Actor, true, true); if(ActorGroupRoot) { ActorGroupRoot->CenterGroupLocation(); } } } } GEditor->RedrawLevelEditingViewports(); } void FLevelEditorActionCallbacks::OnSaveBrushAsCollision() { // First, find the currently selected actor with a static mesh. // Fail if more than one actor with staticmesh is selected. UStaticMesh* StaticMesh = NULL; FMatrix MeshToWorld; // Pointer to the world UWorld* World = NULL; for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It ) { AActor* Actor = Cast( *It ); checkSlow( Actor->IsA(AActor::StaticClass()) ); UStaticMeshComponent* FoundStaticMeshComponent = NULL; if( Actor->IsA(AStaticMeshActor::StaticClass()) ) { FoundStaticMeshComponent = CastChecked(Actor)->GetStaticMeshComponent(); } UStaticMesh* FoundMesh = FoundStaticMeshComponent ? FoundStaticMeshComponent->StaticMesh : NULL; if( FoundMesh ) { // If we find multiple actors with static meshes, warn and do nothing. if( StaticMesh ) { FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "Error_SelectOneActor", "Please select just one Actor with a StaticMesh.") ); return; } StaticMesh = FoundMesh; MeshToWorld = FoundStaticMeshComponent->ComponentToWorld.ToMatrixWithScale(); // Store the pointer to the world World = Actor->GetWorld(); } } // If no static-mesh-toting actor found, warn and do nothing. if(!StaticMesh) { FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "Error_NoActorWithStaticMesh", "No Actor found with a StaticMesh.") ); return; } // If we already have a collision model for this staticmesh, ask if we want to replace it. if( StaticMesh->BodySetup ) { const bool bDoReplace = EAppReturnType::Yes == FMessageDialog::Open( EAppMsgType::YesNo, NSLOCTEXT("UnrealEd", "Prompt_24", "Static Mesh already has a collision model. \nDo you want to replace it with Builder Brush?") ); if( !bDoReplace ) { return; } } check(World) ABrush* BuildBrush = World->GetDefaultBrush(); if(BuildBrush != nullptr) { // Now get the builder brush. UModel* BuilderModel = BuildBrush->Brush; // Need the transform between builder brush space and static mesh actor space. const FMatrix BrushL2W = BuildBrush->ActorToWorld().ToMatrixWithScale(); const FMatrix MeshW2L = MeshToWorld.InverseFast(); const FMatrix SMToBB = BrushL2W * MeshW2L; const FMatrix SMToBB_AT = SMToBB.TransposeAdjoint(); // Copy the current builder brush into a temp model. // We keep no reference to this, so it will be GC'd at some point. UModel* TempModel = NewObject(); TempModel->Initialize(nullptr, 1); TempModel->Polys->Element.AssignButKeepOwner(BuilderModel->Polys->Element); // Now transform each poly into local space for the selected static mesh. for (int32 i = 0; i < TempModel->Polys->Element.Num(); i++) { FPoly* Poly = &TempModel->Polys->Element[i]; for (int32 j = 0; j < Poly->Vertices.Num(); j++) { Poly->Vertices[j] = SMToBB.TransformPosition(Poly->Vertices[j]); } Poly->Normal = SMToBB_AT.TransformVector(Poly->Normal); Poly->Normal.Normalize(); // SmToBB might have scaling in it. } // Build bounding box. TempModel->BuildBound(); // Build BSP for the brush. FBSPOps::bspBuild(TempModel, FBSPOps::BSP_Good, 15, 70, 1, 0); FBSPOps::bspRefresh(TempModel, 1); FBSPOps::bspBuildBounds(TempModel); // Now - use this as the Rigid Body collision for this static mesh as well. // Make sure rendering is done - so we are not changing data being used by collision drawing. FlushRenderingCommands(); // If we already have a BodySetup - clear it. if (StaticMesh->BodySetup) { StaticMesh->BodySetup->RemoveSimpleCollision(); } // If we don't already have physics props, construct them here. else { StaticMesh->CreateBodySetup(); } // Convert collision model into a collection of convex hulls. // NB: This removes any convex hulls that were already part of the collision data. StaticMesh->BodySetup->CreateFromModel(TempModel, true); } // refresh collision change back to staticmesh components RefreshCollisionChange(StaticMesh); // Finally mark the parent package as 'dirty', so user will be prompted if they want to save it etc. StaticMesh->MarkPackageDirty(); } 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 ); } 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", EUserInterfaceActionType::Button, FInputChord( EKeys::F1 ) ); UI_COMMAND( BrowseAPIReference, "API Reference...", "Opens the API reference documentation", 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", "Saves the current level to disk", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( SaveAs, "Save As...", "Save the current level as...", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Shift|EModifierKey::Control, 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 ); } UI_COMMAND( Import, "Import...", "Imports objects and actors from a 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.)", 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.)", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control|EModifierKey::Shift, EKeys::Semicolon) ); UI_COMMAND( BuildReflectionCapturesOnly, "Update Reflection Captures", "Only updates Reflection Captures (all levels.)", 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( 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 ) ); 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( 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, "Save Pivot to Pre-Pivot", "Saves the pivot to the pre-pivot", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ResetPivot, "Reset Pivot", "Resets the pivot", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( ResetPrePivot, "Reset Pre-Pivot", "Resets the pre-pivot", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MovePivotHere, "Move Here", "Moves the pivot to the clicked location", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MovePivotHereSnapped, "Move Here (Snapped)", "Moves the pivot 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() ); // 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() ); UI_COMMAND( MergeActors, "Create Mesh Proxy...", "Harvest geometry from selected actors and merge them into single mesh", EUserInterfaceActionType::Button, FInputChord() ); UI_COMMAND( MergeActorsByMaterials, "Merge Actors...", "Harvest geometry from selected actors and merge grouping them by materials", 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( 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( 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 blocing 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( SaveBrushAsCollision, "Save Collision from Builder Brush", "Creates a collision primitive on the selected static meshes based on the shape of the builder brush", 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 actors 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( FindLevelsInLevelBrowser, "Find Levels in Level Browser", "Finds the selected actors level 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", "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( 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_High, "High", "Sets material quality in the scene to high.", EUserInterfaceActionType::RadioButton, 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()); static const FText FeatureLevelLabels[ERHIFeatureLevel::Num] = { NSLOCTEXT("LevelEditorCommands", "FeatureLevelPreviewType_ES2", "Mobile / HTML5"), NSLOCTEXT("LevelEditorCommands", "FeatureLevelPreviewType_ES31", "High-End Mobile / Metal"), 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"), 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::RadioButton) .DefaultChord(FInputChord()); } } PRAGMA_ENABLE_OPTIMIZATION #undef LOCTEXT_NAMESPACE