// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "TextureEditorToolkit.h" #include "Widgets/Text/STextBlock.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Engine/Texture.h" #include "Engine/Texture2D.h" #include "Editor.h" #include "Widgets/Layout/SBorder.h" #include "Misc/FeedbackContext.h" #include "Modules/ModuleManager.h" #include "SlateOptMacros.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/SCheckBox.h" #include "EditorStyleSet.h" #include "EditorReimportHandler.h" #include "DeviceProfiles/DeviceProfileManager.h" #include "Engine/LightMapTexture2D.h" #include "Engine/ShadowMapTexture2D.h" #include "Engine/Texture2DDynamic.h" #include "Engine/TextureCube.h" #include "Engine/VolumeTexture.h" #include "Engine/TextureRenderTarget.h" #include "Engine/TextureRenderTarget2D.h" #include "Engine/TextureRenderTargetCube.h" #include "Interfaces/ITextureEditorModule.h" #include "TextureEditor.h" #include "Slate/SceneViewport.h" #include "PropertyEditorModule.h" #include "TextureEditorConstants.h" #include "Models/TextureEditorCommands.h" #include "Widgets/STextureEditorViewport.h" #include "ISettingsModule.h" #include "Widgets/Docking/SDockTab.h" #include "Widgets/Input/SNumericEntryBox.h" #include "DeviceProfiles/DeviceProfile.h" #include "Curves/CurveLinearColorAtlas.h" #define LOCTEXT_NAMESPACE "FTextureEditorToolkit" DEFINE_LOG_CATEGORY_STATIC(LogTextureEditor, Log, All); #define MIPLEVEL_MIN 0 #define MIPLEVEL_MAX 15 #define EXPOSURE_MIN -10 #define EXPOSURE_MAX 10 const FName FTextureEditorToolkit::ViewportTabId(TEXT("TextureEditor_Viewport")); const FName FTextureEditorToolkit::PropertiesTabId(TEXT("TextureEditor_Properties")); UNREALED_API void GetBestFitForNumberOfTiles(int32 InSize, int32& OutRatioX, int32& OutRatioY); /* FTextureEditorToolkit structors *****************************************************************************/ FTextureEditorToolkit::FTextureEditorToolkit() : Texture(nullptr) , VolumeOpacity(1.f) , VolumeOrientation(90, 0, -90) { } FTextureEditorToolkit::~FTextureEditorToolkit( ) { // Release the VT page table allocation used to display this texture UTexture2D* Texture2D = Cast(Texture); if (Texture2D && Texture2D->IsCurrentlyVirtualTextured()) { FVirtualTexture2DResource* Resource = (FVirtualTexture2DResource*)Texture2D->Resource; Resource->ReleaseAllocatedVT(); } FReimportManager::Instance()->OnPreReimport().RemoveAll(this); FReimportManager::Instance()->OnPostReimport().RemoveAll(this); GEditor->GetEditorSubsystem()->OnAssetPostImport.RemoveAll(this); GEditor->UnregisterForUndo(this); } /* FAssetEditorToolkit interface *****************************************************************************/ FString FTextureEditorToolkit::GetDocumentationLink( ) const { return FString(TEXT("Engine/Content/Types/Textures/Properties/Interface")); } void FTextureEditorToolkit::RegisterTabSpawners( const TSharedRef& InTabManager ) { WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_TextureEditor", "Texture Editor")); auto WorkspaceMenuCategoryRef = WorkspaceMenuCategory.ToSharedRef(); FAssetEditorToolkit::RegisterTabSpawners(InTabManager); InTabManager->RegisterTabSpawner(ViewportTabId, FOnSpawnTab::CreateSP(this, &FTextureEditorToolkit::HandleTabSpawnerSpawnViewport)) .SetDisplayName(LOCTEXT("ViewportTab", "Viewport")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports")); InTabManager->RegisterTabSpawner(PropertiesTabId, FOnSpawnTab::CreateSP(this, &FTextureEditorToolkit::HandleTabSpawnerSpawnProperties)) .SetDisplayName(LOCTEXT("PropertiesTab", "Details") ) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details")); } void FTextureEditorToolkit::UnregisterTabSpawners( const TSharedRef& InTabManager ) { FAssetEditorToolkit::UnregisterTabSpawners(InTabManager); InTabManager->UnregisterTabSpawner(ViewportTabId); InTabManager->UnregisterTabSpawner(PropertiesTabId); } void FTextureEditorToolkit::InitTextureEditor( const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, UObject* ObjectToEdit ) { FReimportManager::Instance()->OnPreReimport().AddRaw(this, &FTextureEditorToolkit::HandleReimportManagerPreReimport); FReimportManager::Instance()->OnPostReimport().AddRaw(this, &FTextureEditorToolkit::HandleReimportManagerPostReimport); GEditor->GetEditorSubsystem()->OnAssetPostImport.AddRaw(this, &FTextureEditorToolkit::HandleAssetPostImport); Texture = CastChecked(ObjectToEdit); // Support undo/redo Texture->SetFlags(RF_Transactional); GEditor->RegisterForUndo(this); // initialize view options bIsRedChannel = true; bIsGreenChannel = true; bIsBlueChannel = true; bIsAlphaChannel = false; switch (Texture->CompressionSettings) { default: bIsAlphaChannel = !Texture->CompressionNoAlpha; break; case TC_Normalmap: case TC_Grayscale: case TC_Displacementmap: case TC_VectorDisplacementmap: case TC_DistanceFieldFont: bIsAlphaChannel = false; break; } bIsDesaturation = false; SpecifiedMipLevel = 0; bUseSpecifiedMipLevel = false; SpecifiedLayer = 0; SavedCompressionSetting = false; Zoom = 1.0f; // Register our commands. This will only register them if not previously registered FTextureEditorCommands::Register(); BindCommands(); CreateInternalWidgets(); const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_TextureEditor_Layout_v3") ->AddArea ( FTabManager::NewPrimaryArea() ->SetOrientation(Orient_Horizontal) ->Split ( FTabManager::NewSplitter() ->SetOrientation(Orient_Vertical) ->SetSizeCoefficient(0.66f) ->Split ( FTabManager::NewStack() ->AddTab(GetToolbarTabId(), ETabState::OpenedTab) ->SetHideTabWell(true) ->SetSizeCoefficient(0.1f) ) ->Split ( FTabManager::NewStack() ->AddTab(ViewportTabId, ETabState::OpenedTab) ->SetHideTabWell(true) ->SetSizeCoefficient(0.9f) ) ) ->Split ( FTabManager::NewStack() ->AddTab(PropertiesTabId, ETabState::OpenedTab) ->SetSizeCoefficient(0.33f) ) ); const bool bCreateDefaultStandaloneMenu = true; const bool bCreateDefaultToolbar = true; FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, TextureEditorAppIdentifier, StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, ObjectToEdit); ITextureEditorModule* TextureEditorModule = &FModuleManager::LoadModuleChecked("TextureEditor"); AddMenuExtender(TextureEditorModule->GetMenuExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects())); ExtendToolBar(); RegenerateMenusAndToolbars(); // @todo toolkit world centric editing /*if(IsWorldCentricAssetEditor()) { SpawnToolkitTab(GetToolbarTabId(), FString(), EToolkitTabSpot::ToolBar); SpawnToolkitTab(ViewportTabId, FString(), EToolkitTabSpot::Viewport); SpawnToolkitTab(PropertiesTabId, FString(), EToolkitTabSpot::Details); }*/ } /* ITextureEditorToolkit interface *****************************************************************************/ void FTextureEditorToolkit::CalculateTextureDimensions( uint32& Width, uint32& Height ) const { uint32 ImportedWidth = Texture->Source.GetSizeX(); uint32 ImportedHeight = Texture->Source.GetSizeY(); // if Original Width and Height are 0, use the saved current width and height if ((ImportedWidth == 0) && (ImportedHeight == 0)) { ImportedWidth = Texture->GetSurfaceWidth(); ImportedHeight = Texture->GetSurfaceHeight(); } Width = ImportedWidth; Height = ImportedHeight; // catch if the Width and Height are still zero for some reason if ((Width == 0) || (Height == 0)) { Width = 0; Height= 0; return; } // See if we need to uniformly scale it to fit in viewport // Cap the size to effective dimensions uint32 ViewportW = TextureViewport->GetViewport()->GetSizeXY().X; uint32 ViewportH = TextureViewport->GetViewport()->GetSizeXY().Y; uint32 MaxWidth; uint32 MaxHeight; const bool bFitToViewport = GetFitToViewport(); if (bFitToViewport) { const UVolumeTexture* VolumeTexture = Cast(Texture); // Subtract off the viewport space devoted to padding (2 * PreviewPadding) // so that the texture is padded on all sides MaxWidth = ViewportW; MaxHeight = ViewportH; if (IsCubeTexture()) { // Cubes are displayed 2:1. 2x width if the source exists and is not an unwrapped image. const bool bMultipleSourceImages = Texture->Source.GetNumSlices() > 1; const bool bNoSourceImage = Texture->Source.GetNumSlices() == 0; Width *= (bNoSourceImage || bMultipleSourceImages) ? 2 : 1; } else if (VolumeTexture) { UTextureEditorSettings& Settings = *GetMutableDefault(); if (Settings.VolumeViewMode == ETextureEditorVolumeViewMode::TextureEditorVolumeViewMode_VolumeTrace) { Width = Height; } else { Width = (uint32)((float)Height * (float)PreviewEffectiveTextureWidth / (float)PreviewEffectiveTextureHeight); } } // First, scale up based on the size of the viewport if (MaxWidth > MaxHeight) { Height = Height * MaxWidth / Width; Width = MaxWidth; } else { Width = Width * MaxHeight / Height; Height = MaxHeight; } // then, scale again if our width and height is impacted by the scaling if (Width > MaxWidth) { Height = Height * MaxWidth / Width; Width = MaxWidth; } if (Height > MaxHeight) { Width = Width * MaxHeight / Height; Height = MaxHeight; } } else { Width = PreviewEffectiveTextureWidth * Zoom; Height = PreviewEffectiveTextureHeight * Zoom; } } ESimpleElementBlendMode FTextureEditorToolkit::GetColourChannelBlendMode( ) const { if (Texture && (Texture->CompressionSettings == TC_Grayscale || Texture->CompressionSettings == TC_Alpha)) { return SE_BLEND_Opaque; } // Add the red, green, blue, alpha and desaturation flags to the enum to identify the chosen filters uint32 Result = (uint32)SE_BLEND_RGBA_MASK_START; Result += bIsRedChannel ? (1 << 0) : 0; Result += bIsGreenChannel ? (1 << 1) : 0; Result += bIsBlueChannel ? (1 << 2) : 0; Result += bIsAlphaChannel ? (1 << 3) : 0; // If we only have one color channel active, enable color desaturation by default const int32 NumColorChannelsActive = (bIsRedChannel ? 1 : 0) + (bIsGreenChannel ? 1 : 0) + (bIsBlueChannel ? 1 : 0); const bool bIsDesaturationLocal = bIsDesaturation ? true : (NumColorChannelsActive==1); Result += bIsDesaturationLocal ? (1 << 4) : 0; return (ESimpleElementBlendMode)Result; } bool FTextureEditorToolkit::GetFitToViewport( ) const { const UTextureEditorSettings& Settings = *GetDefault(); return Settings.FitToViewport; } int32 FTextureEditorToolkit::GetMipLevel( ) const { return GetUseSpecifiedMip() ? SpecifiedMipLevel : -1; } int32 FTextureEditorToolkit::GetLayer() const { return SpecifiedLayer; } UTexture* FTextureEditorToolkit::GetTexture( ) const { return Texture; } bool FTextureEditorToolkit::HasValidTextureResource( ) const { return Texture != nullptr && Texture->Resource != nullptr; } bool FTextureEditorToolkit::GetUseSpecifiedMip( ) const { if (GetMaxMipLevel().Get(MIPLEVEL_MAX) > 0) { if (HandleMipLevelCheckBoxIsEnabled()) { return bUseSpecifiedMipLevel; } // by default this is on return true; } // disable the widgets if we have no mip maps return false; } double FTextureEditorToolkit::GetZoom( ) const { return Zoom; } void FTextureEditorToolkit::PopulateQuickInfo( ) { UTexture2D* Texture2D = Cast(Texture); UTextureRenderTarget2D* Texture2DRT = Cast(Texture); UTextureRenderTargetCube* TextureCubeRT = Cast(Texture); UTextureCube* TextureCube = Cast(Texture); UTexture2DDynamic* Texture2DDynamic = Cast(Texture); UVolumeTexture* VolumeTexture = Cast(Texture); const uint32 SurfaceWidth = (uint32)Texture->GetSurfaceWidth(); const uint32 SurfaceHeight = (uint32)Texture->GetSurfaceHeight(); const uint32 SurfaceDepth = VolumeTexture ? (uint32)VolumeTexture->GetSizeZ() : 1; const uint32 ImportedWidth = FMath::Max(SurfaceWidth, Texture->Source.GetSizeX()); const uint32 ImportedHeight = FMath::Max(SurfaceHeight, Texture->Source.GetSizeY()); const uint32 ImportedDepth = FMath::Max(SurfaceDepth, VolumeTexture ? Texture->Source.GetNumSlices() : 1); const int32 ActualMipBias = Texture2D ? (Texture2D->GetNumMips() - Texture2D->GetNumResidentMips()) : Texture->GetCachedLODBias(); const uint32 ActualWidth = FMath::Max(SurfaceWidth >> ActualMipBias, 1); const uint32 ActualHeight = FMath::Max(SurfaceHeight >> ActualMipBias, 1); const uint32 ActualDepth = FMath::Max(SurfaceDepth >> ActualMipBias, 1); // Editor dimensions (takes user specified mip setting into account) const int32 MipLevel = FMath::Max(GetMipLevel(), 0); PreviewEffectiveTextureWidth = FMath::Max(ActualWidth >> MipLevel, 1); PreviewEffectiveTextureHeight = FMath::Max(ActualHeight >> MipLevel, 1);; uint32 PreviewEffectiveTextureDepth = FMath::Max(ActualDepth >> MipLevel, 1); // In game max bias and dimensions const int32 MaxResMipBias = Texture2D ? (Texture2D->GetNumMips() - Texture2D->GetNumMipsAllowed(true)) : Texture->GetCachedLODBias(); const uint32 MaxInGameWidth = FMath::Max(SurfaceWidth >> MaxResMipBias, 1); const uint32 MaxInGameHeight = FMath::Max(SurfaceHeight >> MaxResMipBias, 1); const uint32 MaxInGameDepth = FMath::Max(SurfaceDepth >> MaxResMipBias, 1); // Texture asset size const uint32 Size = (Texture->GetResourceSizeBytes(EResourceSizeMode::Exclusive) + 512) / 1024; FNumberFormattingOptions SizeOptions; SizeOptions.UseGrouping = false; SizeOptions.MaximumFractionalDigits = 0; // Cubes are previewed as unwrapped 2D textures. // These have 2x the width of a cube face. PreviewEffectiveTextureWidth *= IsCubeTexture() ? 2 : 1; FNumberFormattingOptions Options; Options.UseGrouping = false; if (VolumeTexture) { ImportedText->SetText(FText::Format( NSLOCTEXT("TextureEditor", "QuickInfo_Imported_3x", "Imported: {0}x{1}x{2}"), FText::AsNumber(ImportedWidth, &Options), FText::AsNumber(ImportedHeight, &Options), FText::AsNumber(ImportedDepth, &Options))); CurrentText->SetText(FText::Format( NSLOCTEXT("TextureEditor", "QuickInfo_Displayed_3x", "Displayed: {0}x{1}x{2}"), FText::AsNumber(PreviewEffectiveTextureWidth, &Options ), FText::AsNumber(PreviewEffectiveTextureHeight, &Options), FText::AsNumber(PreviewEffectiveTextureDepth, &Options))); MaxInGameText->SetText(FText::Format( NSLOCTEXT("TextureEditor", "QuickInfo_MaxInGame_3x", "Max In-Game: {0}x{1}x{2}"), FText::AsNumber(MaxInGameWidth, &Options), FText::AsNumber(MaxInGameHeight, &Options), FText::AsNumber(MaxInGameDepth, &Options))); UTextureEditorSettings& Settings = *GetMutableDefault(); if (Settings.VolumeViewMode == ETextureEditorVolumeViewMode::TextureEditorVolumeViewMode_VolumeTrace) { PreviewEffectiveTextureWidth = PreviewEffectiveTextureHeight = FMath::Max(PreviewEffectiveTextureWidth, PreviewEffectiveTextureHeight); } else { int32 NumTilesX = 0; int32 NumTilesY = 0; GetBestFitForNumberOfTiles(PreviewEffectiveTextureDepth, NumTilesX, NumTilesY); PreviewEffectiveTextureWidth *= (uint32)NumTilesX; PreviewEffectiveTextureHeight *= (uint32)NumTilesY; } } else { FText CubemapAdd; if(TextureCube) { CubemapAdd = NSLOCTEXT("TextureEditor", "QuickInfo_PerCubeSide", "x6 (CubeMap)"); } ImportedText->SetText(FText::Format( NSLOCTEXT("TextureEditor", "QuickInfo_Imported_2x", "Imported: {0}x{1}"), FText::AsNumber(ImportedWidth, &Options), FText::AsNumber(ImportedHeight, &Options))); CurrentText->SetText(FText::Format( NSLOCTEXT("TextureEditor", "QuickInfo_Displayed_2x", "Displayed: {0}x{1}{2}"), FText::AsNumber(PreviewEffectiveTextureWidth, &Options ), FText::AsNumber(PreviewEffectiveTextureHeight, &Options), CubemapAdd)); MaxInGameText->SetText(FText::Format( NSLOCTEXT("TextureEditor", "QuickInfo_MaxInGame_2x", "Max In-Game: {0}x{1}{2}"), FText::AsNumber(MaxInGameWidth, &Options), FText::AsNumber(MaxInGameHeight, &Options), CubemapAdd)); } SizeText->SetText(FText::Format(NSLOCTEXT("TextureEditor", "QuickInfo_ResourceSize", "Resource Size: {0} Kb"), FText::AsNumber(Size, &SizeOptions))); FText Method = Texture->VirtualTextureStreaming ? NSLOCTEXT("TextureEditor", "QuickInfo_MethodVirtualStreamed", "Virtual Streamed") : (!Texture->bIsStreamable ? NSLOCTEXT("TextureEditor", "QuickInfo_MethodNotStreamed", "Not Streamed") : NSLOCTEXT("TextureEditor", "QuickInfo_MethodStreamed", "Streamed") ); MethodText->SetText(FText::Format(NSLOCTEXT("TextureEditor", "QuickInfo_Method", "Method: {0}"), Method)); LODBiasText->SetText(FText::Format(NSLOCTEXT("TextureEditor", "QuickInfo_LODBias", "Combined LOD Bias: {0}"), FText::AsNumber(Texture->GetCachedLODBias()))); int32 TextureFormatIndex = PF_MAX; if (Texture2D) { TextureFormatIndex = Texture2D->GetPixelFormat(SpecifiedLayer); } else if (TextureCube) { TextureFormatIndex = TextureCube->GetPixelFormat(); } else if (Texture2DRT) { TextureFormatIndex = Texture2DRT->GetFormat(); } else if (Texture2DDynamic) { TextureFormatIndex = Texture2DDynamic->Format; } else if (VolumeTexture) { TextureFormatIndex = VolumeTexture->GetPixelFormat(); } if (TextureFormatIndex != PF_MAX) { FormatText->SetText(FText::Format(NSLOCTEXT("TextureEditor", "QuickInfo_Format", "Format: {0}"), FText::FromString(GPixelFormats[TextureFormatIndex].Name))); } int32 NumMips = 1; if (Texture2D) { NumMips = Texture2D->GetNumMips(); } else if (TextureCube) { NumMips = TextureCube->GetNumMips(); } else if (Texture2DRT) { NumMips = Texture2DRT->GetNumMips(); } else if (Texture2DDynamic) { NumMips = Texture2DDynamic->NumMips; } else if (VolumeTexture) { NumMips = VolumeTexture->GetNumMips(); } NumMipsText->SetText(FText::Format(NSLOCTEXT("TextureEditor", "QuickInfo_NumMips", "Number of Mips: {0}"), FText::AsNumber(NumMips))); if (Texture2D) { HasAlphaChannelText->SetText(FText::Format(NSLOCTEXT("TextureEditor", "QuickInfo_HasAlphaChannel", "Has Alpha Channel: {0}"), Texture2D->HasAlphaChannel() ? NSLOCTEXT("TextureEditor", "True", "True") : NSLOCTEXT("TextureEditor", "False", "False"))); } HasAlphaChannelText->SetVisibility(Texture2D ? EVisibility::Visible : EVisibility::Collapsed); } void FTextureEditorToolkit::SetFitToViewport( const bool bFitToViewport ) { UTextureEditorSettings& Settings = *GetMutableDefault(); Settings.FitToViewport = bFitToViewport; Settings.PostEditChange(); } void FTextureEditorToolkit::SetZoom( double ZoomValue ) { Zoom = FMath::Clamp(ZoomValue, MinZoom, MaxZoom); SetFitToViewport(false); } void FTextureEditorToolkit::ZoomIn( ) { SetZoom(Zoom + ZoomStep); } void FTextureEditorToolkit::ZoomOut( ) { SetZoom(Zoom - ZoomStep); } float FTextureEditorToolkit::GetVolumeOpacity() const { return VolumeOpacity; } void FTextureEditorToolkit::SetVolumeOpacity(float ZoomValue) { VolumeOpacity = FMath::Clamp(ZoomValue, 0.f, 1.f); } const FRotator& FTextureEditorToolkit::GetVolumeOrientation() const { return VolumeOrientation; } void FTextureEditorToolkit::SetVolumeOrientation(const FRotator& InOrientation) { VolumeOrientation = InOrientation; } /* IToolkit interface *****************************************************************************/ FText FTextureEditorToolkit::GetBaseToolkitName( ) const { return LOCTEXT("AppLabel", "Texture Editor"); } FName FTextureEditorToolkit::GetToolkitFName( ) const { return FName("TextureEditor"); } FLinearColor FTextureEditorToolkit::GetWorldCentricTabColorScale( ) const { return FLinearColor(0.3f, 0.2f, 0.5f, 0.5f); } FString FTextureEditorToolkit::GetWorldCentricTabPrefix( ) const { return LOCTEXT("WorldCentricTabPrefix", "Texture ").ToString(); } /* FGCObject interface *****************************************************************************/ void FTextureEditorToolkit::AddReferencedObjects( FReferenceCollector& Collector ) { Collector.AddReferencedObject(Texture); TextureViewport->AddReferencedObjects(Collector); } /* FEditorUndoClient interface *****************************************************************************/ void FTextureEditorToolkit::PostUndo( bool bSuccess ) { } void FTextureEditorToolkit::PostRedo( bool bSuccess ) { PostUndo(bSuccess); } /* FTextureEditorToolkit implementation *****************************************************************************/ void FTextureEditorToolkit::BindCommands( ) { const FTextureEditorCommands& Commands = FTextureEditorCommands::Get(); ToolkitCommands->MapAction( Commands.RedChannel, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleRedChannelActionExecute), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleRedChannelActionIsChecked)); ToolkitCommands->MapAction( Commands.GreenChannel, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleGreenChannelActionExecute), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleGreenChannelActionIsChecked)); ToolkitCommands->MapAction( Commands.BlueChannel, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleBlueChannelActionExecute), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleBlueChannelActionIsChecked)); ToolkitCommands->MapAction( Commands.AlphaChannel, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleAlphaChannelActionExecute), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleAlphaChannelActionIsChecked)); ToolkitCommands->MapAction( Commands.Desaturation, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleDesaturationChannelActionExecute), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleDesaturationChannelActionIsChecked)); ToolkitCommands->MapAction( Commands.FitToViewport, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleFitToViewportActionExecute), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleFitToViewportActionIsChecked)); ToolkitCommands->MapAction( Commands.CheckeredBackground, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleCheckeredBackgroundActionExecute, TextureEditorBackground_Checkered), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleCheckeredBackgroundActionIsChecked, TextureEditorBackground_Checkered)); ToolkitCommands->MapAction( Commands.CheckeredBackgroundFill, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleCheckeredBackgroundActionExecute, TextureEditorBackground_CheckeredFill), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleCheckeredBackgroundActionIsChecked, TextureEditorBackground_CheckeredFill)); ToolkitCommands->MapAction( Commands.SolidBackground, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleCheckeredBackgroundActionExecute, TextureEditorBackground_SolidColor), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleCheckeredBackgroundActionIsChecked, TextureEditorBackground_SolidColor)); // Begin - Volume Texture Specifics ToolkitCommands->MapAction( Commands.DepthSlices, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleVolumeViewModeActionExecute, TextureEditorVolumeViewMode_DepthSlices), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleVolumeViewModeActionIsChecked, TextureEditorVolumeViewMode_DepthSlices)); ToolkitCommands->MapAction( Commands.TraceIntoVolume, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleVolumeViewModeActionExecute, TextureEditorVolumeViewMode_VolumeTrace), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleVolumeViewModeActionIsChecked, TextureEditorVolumeViewMode_VolumeTrace)); // End - Volume Texture Specifics ToolkitCommands->MapAction( Commands.TextureBorder, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleTextureBorderActionExecute), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FTextureEditorToolkit::HandleTextureBorderActionIsChecked)); ToolkitCommands->MapAction( Commands.CompressNow, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleCompressNowActionExecute), FCanExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleCompressNowActionCanExecute)); ToolkitCommands->MapAction( Commands.Reimport, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleReimportActionExecute), FCanExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleReimportActionCanExecute)); ToolkitCommands->MapAction( Commands.Settings, FExecuteAction::CreateSP(this, &FTextureEditorToolkit::HandleSettingsActionExecute)); } TSharedRef FTextureEditorToolkit::BuildTexturePropertiesWidget( ) { FDetailsViewArgs Args; Args.bHideSelectionTip = true; FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); TexturePropertiesWidget = PropertyModule.CreateDetailView(Args); TexturePropertiesWidget->SetObject(Texture); return TexturePropertiesWidget.ToSharedRef(); } void FTextureEditorToolkit::CreateInternalWidgets( ) { TextureViewport = SNew(STextureEditorViewport, SharedThis(this)); TextureProperties = SNew(SVerticalBox) + SVerticalBox::Slot() .AutoHeight() .Padding(2.0f) [ SNew(SBorder) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .FillWidth(0.5f) [ SNew(SVerticalBox) + SVerticalBox::Slot() .AutoHeight() .VAlign(VAlign_Center) .Padding(4.0f) [ SAssignNew(ImportedText, STextBlock) ] + SVerticalBox::Slot() .AutoHeight() .VAlign(VAlign_Center) .Padding(4.0f) [ SAssignNew(CurrentText, STextBlock) ] + SVerticalBox::Slot() .AutoHeight() .VAlign(VAlign_Center) .Padding(4.0f) [ SAssignNew(MaxInGameText, STextBlock) ] + SVerticalBox::Slot() .AutoHeight() .VAlign(VAlign_Center) .Padding(4.0f) [ SAssignNew(SizeText, STextBlock) ] + SVerticalBox::Slot() .AutoHeight() .VAlign(VAlign_Center) .Padding(4.0f) [ SAssignNew(HasAlphaChannelText, STextBlock) ] ] + SHorizontalBox::Slot() .FillWidth(0.5f) [ SNew(SVerticalBox) + SVerticalBox::Slot() .AutoHeight() .VAlign(VAlign_Center) .Padding(4.0f) [ SAssignNew(MethodText, STextBlock) ] + SVerticalBox::Slot() .AutoHeight() .VAlign(VAlign_Center) .Padding(4.0f) [ SAssignNew(FormatText, STextBlock) ] + SVerticalBox::Slot() .AutoHeight() .VAlign(VAlign_Center) .Padding(4.0f) [ SAssignNew(LODBiasText, STextBlock) ] + SVerticalBox::Slot() .AutoHeight() .VAlign(VAlign_Center) .Padding(4.0f) [ SAssignNew(NumMipsText, STextBlock) ] ] ] ] + SVerticalBox::Slot() .FillHeight(1.0f) .Padding(2.0f) [ SNew(SBorder) .Padding(4.0f) [ BuildTexturePropertiesWidget() ] ]; } BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void FTextureEditorToolkit::ExtendToolBar( ) { TSharedRef LODControl = SNew(SBox) .WidthOverride(240.0f) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .FillWidth(1.0f) .MaxWidth(240.0f) .Padding(0.0f, 0.0f, 0.0f, 0.0f) .VAlign(VAlign_Center) [ // Mip and exposure controls SNew(SHorizontalBox) + SHorizontalBox::Slot() .Padding(4.0f, 0.0f, 4.0f, 0.0f) .AutoWidth() [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(SCheckBox) .IsChecked(this, &FTextureEditorToolkit::HandleMipLevelCheckBoxIsChecked) .IsEnabled(this, &FTextureEditorToolkit::HandleMipLevelCheckBoxIsEnabled) .OnCheckStateChanged(this, &FTextureEditorToolkit::HandleMipLevelCheckBoxCheckedStateChanged) ] ] + SHorizontalBox::Slot() .Padding(4.0f, 0.0f, 4.0f, 0.0f) .FillWidth(1) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .Padding(0.0f, 0.0, 4.0, 0.0) .AutoWidth() .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(NSLOCTEXT("TextureEditor", "MipLevel", "Mip Level: ")) ] + SHorizontalBox::Slot() .VAlign(VAlign_Center) .FillWidth(1.0f) [ SNew(SNumericEntryBox) .AllowSpin(true) .MinSliderValue(MIPLEVEL_MIN) .MaxSliderValue(this, &FTextureEditorToolkit::GetMaxMipLevel) .Value(this, &FTextureEditorToolkit::HandleMipLevelEntryBoxValue) .OnValueChanged(this, &FTextureEditorToolkit::HandleMipLevelEntryBoxChanged) .IsEnabled(this, &FTextureEditorToolkit::GetUseSpecifiedMip) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(2.0f) [ SNew(SButton) .Text(NSLOCTEXT("TextureEditor", "MipMinus", "-")) .OnClicked(this, &FTextureEditorToolkit::HandleMipMapMinusButtonClicked) .IsEnabled(this, &FTextureEditorToolkit::GetUseSpecifiedMip) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(2.0f) [ SNew(SButton) .Text(NSLOCTEXT("TextureEditor", "MipPlus", "+")) .OnClicked(this, &FTextureEditorToolkit::HandleMipMapPlusButtonClicked) .IsEnabled(this, &FTextureEditorToolkit::GetUseSpecifiedMip) ] ] ] ]; TSharedRef LayerControl = SNew(SBox) .WidthOverride(200.0f) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .FillWidth(1.0f) .MaxWidth(200.0f) .Padding(0.0f, 0.0f, 0.0f, 0.0f) .VAlign(VAlign_Center) [ // Layer controls SNew(SHorizontalBox) + SHorizontalBox::Slot() .Padding(0.0f, 0.0, 4.0, 0.0) .AutoWidth() .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(NSLOCTEXT("TextureEditor", "Layer", "Layer: ")) ] + SHorizontalBox::Slot() .VAlign(VAlign_Center) .FillWidth(1.0f) [ SNew(SNumericEntryBox) .AllowSpin(true) .MinSliderValue(MIPLEVEL_MIN) .MaxSliderValue(this, &FTextureEditorToolkit::GetMaxLayer) .Value(this, &FTextureEditorToolkit::HandleLayerEntryBoxValue) .OnValueChanged(this, &FTextureEditorToolkit::HandleLayerEntryBoxChanged) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(2.0f) [ SNew(SButton) .Text(NSLOCTEXT("TextureEditor", "LayerMinus", "-")) .OnClicked(this, &FTextureEditorToolkit::HandleLayerMinusButtonClicked) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(2.0f) [ SNew(SButton) .Text(NSLOCTEXT("TextureEditor", "LayerPlus", "+")) .OnClicked(this, &FTextureEditorToolkit::HandleLayerPlusButtonClicked) ] ] ]; TSharedPtr ToolbarExtender = MakeShareable(new FExtender); ToolbarExtender->AddToolBarExtension( "Asset", EExtensionHook::After, GetToolkitCommands(), FToolBarExtensionDelegate::CreateSP(this, &FTextureEditorToolkit::FillToolbar, GetToolkitCommands(), LODControl, LayerControl) ); AddToolbarExtender(ToolbarExtender); ITextureEditorModule* TextureEditorModule = &FModuleManager::LoadModuleChecked("TextureEditor"); AddToolbarExtender(TextureEditorModule->GetToolBarExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects())); } void FTextureEditorToolkit::FillToolbar(FToolBarBuilder& ToolbarBuilder, const TSharedRef< FUICommandList > InToolkitCommands, TSharedRef LODControl, TSharedRef LayerControl) { UCurveLinearColorAtlas* Atlas = Cast(GetTexture()); if (!Atlas) { ToolbarBuilder.BeginSection("TextureMisc"); { ToolbarBuilder.AddToolBarButton(FTextureEditorCommands::Get().CompressNow); ToolbarBuilder.AddToolBarButton(FTextureEditorCommands::Get().Reimport); } ToolbarBuilder.EndSection(); ToolbarBuilder.BeginSection("TextureMipAndExposure"); { ToolbarBuilder.AddWidget(LODControl); } ToolbarBuilder.EndSection(); if (HasLayers()) { ToolbarBuilder.BeginSection("Layers"); { ToolbarBuilder.AddWidget(LayerControl); } ToolbarBuilder.EndSection(); } } } END_SLATE_FUNCTION_BUILD_OPTIMIZATION TOptional FTextureEditorToolkit::GetMaxMipLevel( ) const { const UTexture2D* Texture2D = Cast(Texture); const UTextureCube* TextureCube = Cast(Texture); const UTextureRenderTargetCube* RTTextureCube = Cast(Texture); const UTextureRenderTarget2D* RTTexture2D = Cast(Texture); const UVolumeTexture* VolumeTexture = Cast(Texture); if (Texture2D) { return Texture2D->GetNumMips() - 1; } if (TextureCube) { return TextureCube->GetNumMips() - 1; } if (RTTextureCube) { return RTTextureCube->GetNumMips() - 1; } if (RTTexture2D) { return RTTexture2D->GetNumMips() - 1; } if (VolumeTexture) { return VolumeTexture->GetNumMips() - 1; } return MIPLEVEL_MAX; } TOptional FTextureEditorToolkit::GetMaxLayer() const { return FMath::Max(Texture->Source.GetNumLayers() - 1, 1); } bool FTextureEditorToolkit::IsCubeTexture( ) const { return (Texture->IsA(UTextureCube::StaticClass()) || Texture->IsA(UTextureRenderTargetCube::StaticClass())); } /* FTextureEditorToolkit callbacks *****************************************************************************/ bool FTextureEditorToolkit::HandleAlphaChannelActionCanExecute( ) const { const UTexture2D* Texture2D = Cast(Texture); if (Texture2D == NULL) { return false; } return Texture2D->HasAlphaChannel(); } void FTextureEditorToolkit::HandleAlphaChannelActionExecute( ) { bIsAlphaChannel = !bIsAlphaChannel; } bool FTextureEditorToolkit::HandleAlphaChannelActionIsChecked( ) const { return bIsAlphaChannel; } void FTextureEditorToolkit::HandleBlueChannelActionExecute( ) { bIsBlueChannel = !bIsBlueChannel; } bool FTextureEditorToolkit::HandleBlueChannelActionIsChecked( ) const { return bIsBlueChannel; } void FTextureEditorToolkit::HandleCheckeredBackgroundActionExecute( ETextureEditorBackgrounds Background ) { UTextureEditorSettings& Settings = *GetMutableDefault(); Settings.Background = Background; Settings.PostEditChange(); } bool FTextureEditorToolkit::HandleCheckeredBackgroundActionIsChecked( ETextureEditorBackgrounds Background ) { const UTextureEditorSettings& Settings = *GetDefault(); return (Background == Settings.Background); } // Callback for toggling the volume display action. void FTextureEditorToolkit::HandleVolumeViewModeActionExecute(ETextureEditorVolumeViewMode InViewMode) { UTextureEditorSettings& Settings = *GetMutableDefault(); Settings.VolumeViewMode = InViewMode; Settings.PostEditChange(); } // Callback for getting the checked state of the volume display action. bool FTextureEditorToolkit::HandleVolumeViewModeActionIsChecked(ETextureEditorVolumeViewMode InViewMode) { const UTextureEditorSettings& Settings = *GetDefault(); return (InViewMode == Settings.VolumeViewMode); } void FTextureEditorToolkit::HandleCompressNowActionExecute( ) { GWarn->BeginSlowTask(NSLOCTEXT("TextureEditor", "CompressNow", "Compressing 1 Textures that have Defer Compression set"), true); if (Texture->DeferCompression) { // turn off deferred compression and compress the texture Texture->DeferCompression = false; Texture->Source.Compress(); Texture->PostEditChange(); PopulateQuickInfo(); } GWarn->EndSlowTask(); } bool FTextureEditorToolkit::HandleCompressNowActionCanExecute( ) const { return (Texture->DeferCompression != 0); } void FTextureEditorToolkit::HandleFitToViewportActionExecute( ) { ToggleFitToViewport(); } bool FTextureEditorToolkit::HandleFitToViewportActionIsChecked( ) const { return GetFitToViewport(); } void FTextureEditorToolkit::HandleGreenChannelActionExecute( ) { bIsGreenChannel = !bIsGreenChannel; } bool FTextureEditorToolkit::HandleGreenChannelActionIsChecked( ) const { return bIsGreenChannel; } void FTextureEditorToolkit::HandleMipLevelCheckBoxCheckedStateChanged( ECheckBoxState InNewState ) { bUseSpecifiedMipLevel = InNewState == ECheckBoxState::Checked; } ECheckBoxState FTextureEditorToolkit::HandleMipLevelCheckBoxIsChecked( ) const { return GetUseSpecifiedMip() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; } bool FTextureEditorToolkit::HandleMipLevelCheckBoxIsEnabled( ) const { UTextureCube* TextureCube = Cast(Texture); if (GetMaxMipLevel().Get(MIPLEVEL_MAX) <= 0 || TextureCube) { return false; } return true; } void FTextureEditorToolkit::HandleMipLevelEntryBoxChanged( int32 NewMipLevel ) { SpecifiedMipLevel = FMath::Clamp(NewMipLevel, MIPLEVEL_MIN, GetMaxMipLevel().Get(MIPLEVEL_MAX)); } TOptional FTextureEditorToolkit::HandleMipLevelEntryBoxValue( ) const { return SpecifiedMipLevel; } FReply FTextureEditorToolkit::HandleMipMapMinusButtonClicked( ) { if (SpecifiedMipLevel > MIPLEVEL_MIN) { --SpecifiedMipLevel; } return FReply::Handled(); } FReply FTextureEditorToolkit::HandleMipMapPlusButtonClicked( ) { if (SpecifiedMipLevel < GetMaxMipLevel().Get(MIPLEVEL_MAX)) { ++SpecifiedMipLevel; } return FReply::Handled(); } void FTextureEditorToolkit::HandleLayerEntryBoxChanged(int32 NewLayer) { SpecifiedLayer = FMath::Clamp(NewLayer, 0, Texture->Source.GetNumLayers() - 1); PopulateQuickInfo(); } FReply FTextureEditorToolkit::HandleLayerMinusButtonClicked() { if (SpecifiedLayer > 0) { --SpecifiedLayer; PopulateQuickInfo(); } return FReply::Handled(); } FReply FTextureEditorToolkit::HandleLayerPlusButtonClicked() { if ((SpecifiedLayer + 1) < Texture->Source.GetNumLayers()) { ++SpecifiedLayer; PopulateQuickInfo(); } return FReply::Handled(); } TOptional FTextureEditorToolkit::HandleLayerEntryBoxValue() const { return SpecifiedLayer; } bool FTextureEditorToolkit::HasLayers() const { return Texture->Source.GetNumLayers() > 1; } void FTextureEditorToolkit::HandleRedChannelActionExecute( ) { bIsRedChannel = !bIsRedChannel; } bool FTextureEditorToolkit::HandleRedChannelActionIsChecked( ) const { return bIsRedChannel; } bool FTextureEditorToolkit::HandleReimportActionCanExecute( ) const { if (Texture->IsA() || Texture->IsA() || Texture->IsA() || Texture->IsA() || Texture->IsA()) { return false; } return true; } void FTextureEditorToolkit::HandleReimportActionExecute( ) { FReimportManager::Instance()->Reimport(Texture, /*bAskForNewFileIfMissing=*/true); } void FTextureEditorToolkit::HandleReimportManagerPostReimport( UObject* InObject, bool bSuccess ) { // Ignore if this is regarding a different object if (InObject != Texture) { return; } if (!bSuccess) { // Failed, restore the compression flag Texture->DeferCompression = SavedCompressionSetting; } // Re-enable viewport rendering now that the texture should be in a known state again TextureViewport->EnableRendering(); } void FTextureEditorToolkit::HandleReimportManagerPreReimport( UObject* InObject ) { // Ignore if this is regarding a different object if (InObject != Texture) { return; } // Prevent the texture from being compressed immediately, so the user can see the results SavedCompressionSetting = Texture->DeferCompression; Texture->DeferCompression = true; // Disable viewport rendering until the texture has finished re-importing TextureViewport->DisableRendering(); } void FTextureEditorToolkit::HandleAssetPostImport(UFactory* InFactory, UObject* InObject) { if (Cast(InObject) != nullptr && InObject == Texture) { // Refresh this object within the details panel TexturePropertiesWidget->SetObject(InObject); } } void FTextureEditorToolkit::HandleDesaturationChannelActionExecute( ) { bIsDesaturation = !bIsDesaturation; } bool FTextureEditorToolkit::HandleDesaturationChannelActionIsChecked( ) const { return bIsDesaturation; } void FTextureEditorToolkit::HandleSettingsActionExecute( ) { FModuleManager::LoadModuleChecked("Settings").ShowViewer("Editor", "ContentEditors", "TextureEditor"); } TSharedRef FTextureEditorToolkit::HandleTabSpawnerSpawnProperties( const FSpawnTabArgs& Args ) { check(Args.GetTabId() == PropertiesTabId); TSharedRef SpawnedTab = SNew(SDockTab) .Icon(FEditorStyle::GetBrush("TextureEditor.Tabs.Properties")) .Label(LOCTEXT("TexturePropertiesTitle", "Details")) [ TextureProperties.ToSharedRef() ]; PopulateQuickInfo(); return SpawnedTab; } TSharedRef FTextureEditorToolkit::HandleTabSpawnerSpawnViewport( const FSpawnTabArgs& Args ) { check(Args.GetTabId() == ViewportTabId); return SNew(SDockTab) .Label(LOCTEXT("TextureViewportTitle", "Viewport")) [ TextureViewport.ToSharedRef() ]; } void FTextureEditorToolkit::HandleTextureBorderActionExecute( ) { UTextureEditorSettings& Settings = *GetMutableDefault(); Settings.TextureBorderEnabled = !Settings.TextureBorderEnabled; Settings.PostEditChange(); } bool FTextureEditorToolkit::HandleTextureBorderActionIsChecked( ) const { const UTextureEditorSettings& Settings = *GetDefault(); return Settings.TextureBorderEnabled; } #undef LOCTEXT_NAMESPACE