// Copyright Epic Games, Inc. All Rights Reserved. #include "MeshPaintMode.h" #include "EditorModeManager.h" #include "MeshPaintModeCommands.h" #include "EditorViewportClient.h" #include "GeometryCollection/GeometryCollectionComponent.h" // IWYU pragma: keep #include "MeshPaintModeToolkit.h" #include "IMeshPaintComponentAdapter.h" #include "MeshPaintModeSettings.h" #include "InteractiveToolManager.h" #include "Tools/EdModeInteractiveToolsContext.h" #include "Engine/SkeletalMesh.h" #include "ComponentReregisterContext.h" #include "Dialogs/Dialogs.h" #include "Components/StaticMeshComponent.h" #include "Components/SkeletalMeshComponent.h" #include "ScopedTransaction.h" #include "PackageTools.h" #include "MeshPaintAdapterFactory.h" #include "MeshPaintHelpers.h" #include "MeshPaintModeHelpers.h" #include "MeshSelect.h" #include "MeshTexturePaintingTool.h" #include "MeshVertexPaintingTool.h" #include "Modules/ModuleManager.h" #include "LevelEditor.h" #include "TexturePaintToolset.h" #include "ToolContextInterfaces.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(MeshPaintMode) #define LOCTEXT_NAMESPACE "MeshPaintMode" FName UMeshPaintMode::MeshPaintMode_VertexColor = FName(TEXT("VertexColor")); FName UMeshPaintMode::MeshPaintMode_VertexWeights = FName(TEXT("VertexWeights")); FName UMeshPaintMode::MeshPaintMode_TextureColor = FName(TEXT("TextureColor")); FName UMeshPaintMode::MeshPaintMode_TextureAsset = FName(TEXT("Texture")); FString UMeshPaintMode::VertexSelectToolName = TEXT("VertexAdapterClickTool"); FString UMeshPaintMode::TextureColorSelectToolName = TEXT("TextureColorAdapterClickTool"); FString UMeshPaintMode::TextureAssetSelectToolName = TEXT("TextureAssetAdapterClickTool"); FString UMeshPaintMode::VertexColorPaintToolName = TEXT("VertexColorBrushTool"); FString UMeshPaintMode::VertexWeightPaintToolName = TEXT("VertexWeightBrushTool"); FString UMeshPaintMode::TextureColorPaintToolName = TEXT("TextureColorBrushTool"); FString UMeshPaintMode::TextureAssetPaintToolName = TEXT("TextureBrushTool"); UMeshPaintMode* UMeshPaintMode::GetMeshPaintMode() { return Cast(GLevelEditorModeTools().GetActiveScriptableMode("MeshPaintMode")); } FName UMeshPaintMode::GetValidPaletteName(const FName InName) { if (InName == UMeshPaintMode::MeshPaintMode_VertexColor || InName == UMeshPaintMode::MeshPaintMode_VertexWeights || InName == UMeshPaintMode::MeshPaintMode_TextureColor || InName == UMeshPaintMode::MeshPaintMode_TextureAsset) { return InName; } return UMeshPaintMode::MeshPaintMode_VertexColor; } template static T* GetTypedToolProperties() { UMeshPaintMode* MeshPaintMode = UMeshPaintMode::GetMeshPaintMode(); UInteractiveToolManager* ToolManager = MeshPaintMode != nullptr ? MeshPaintMode->GetToolManager() : nullptr; UInteractiveTool* Tool = ToolManager != nullptr ? ToolManager->GetActiveTool(EToolSide::Mouse) : nullptr; if (Tool != nullptr) { const TArray PropertyArray = Tool->GetToolProperties(); for (UObject* Property : PropertyArray) { if (T* FoundProperty = Cast(Property)) { return FoundProperty; } } } return nullptr; } UMeshPaintingToolProperties* UMeshPaintMode::GetToolProperties() { return GetTypedToolProperties(); } UMeshVertexPaintingToolProperties* UMeshPaintMode::GetVertexToolProperties() { return GetTypedToolProperties(); } UMeshVertexColorPaintingToolProperties* UMeshPaintMode::GetVertexColorToolProperties() { return GetTypedToolProperties(); } UMeshVertexWeightPaintingToolProperties* UMeshPaintMode::GetVertexWeightToolProperties() { return GetTypedToolProperties(); } UMeshTexturePaintingToolProperties* UMeshPaintMode::GetTextureToolProperties() { return GetTypedToolProperties(); } UMeshTextureColorPaintingToolProperties* UMeshPaintMode::GetTextureColorToolProperties() { return GetTypedToolProperties(); } UMeshTextureAssetPaintingToolProperties* UMeshPaintMode::GetTextureAssetToolProperties() { return GetTypedToolProperties(); } UMeshPaintMode::UMeshPaintMode() : Super() { SettingsClass = UMeshPaintModeSettings::StaticClass(); Info = FEditorModeInfo( FName(TEXT("MeshPaintMode")), LOCTEXT("ModeName", "Mesh Paint"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.MeshPaintMode", "LevelEditor.MeshPaintMode.Small"), true, 600 ); } void UMeshPaintMode::Enter() { Super::Enter(); GEditor->OnEditorClose().AddUObject(this, &UMeshPaintMode::OnResetViewMode); FCoreUObjectDelegates::OnObjectsReplaced.AddUObject(this, &UMeshPaintMode::OnObjectsReplaced); ModeSettings = Cast(SettingsObject); FMeshPaintEditorModeCommands ToolManagerCommands = FMeshPaintEditorModeCommands::Get(); UVertexAdapterClickToolBuilder* VertexClickToolBuilder = NewObject(this); RegisterTool(ToolManagerCommands.SelectVertex, VertexSelectToolName, VertexClickToolBuilder); UTextureColorAdapterClickToolBuilder* TextureColorClickToolBuilder = NewObject(this); RegisterTool(ToolManagerCommands.SelectTextureColor, TextureColorSelectToolName, TextureColorClickToolBuilder); UTextureAssetAdapterClickToolBuilder* TextureAssetClickToolBuilder = NewObject(this); RegisterTool(ToolManagerCommands.SelectTextureAsset, TextureAssetSelectToolName, TextureAssetClickToolBuilder); UMeshVertexColorPaintingToolBuilder* MeshColorPaintingToolBuilder = NewObject(this); RegisterTool(ToolManagerCommands.PaintVertexColor, VertexColorPaintToolName, MeshColorPaintingToolBuilder); UMeshVertexWeightPaintingToolBuilder* WeightPaintingToolBuilder = NewObject(this); RegisterTool(ToolManagerCommands.PaintVertexWeight, VertexWeightPaintToolName, WeightPaintingToolBuilder); UMeshTextureColorPaintingToolBuilder* MeshTextureColorPaintingToolBuilder = NewObject(this); RegisterTool(ToolManagerCommands.PaintTextureColor, TextureColorPaintToolName, MeshTextureColorPaintingToolBuilder); UMeshTextureAssetPaintingToolBuilder* TextureAssetPaintingToolBuilder = NewObject(this); RegisterTool(ToolManagerCommands.PaintTextureAsset, TextureAssetPaintToolName, TextureAssetPaintingToolBuilder); UpdateSelectedMeshes(); // Toolkit PaletteChangedHandle = Toolkit->OnPaletteChanged().AddUObject(this, &UMeshPaintMode::UpdateOnPaletteChange); // disable tool change tracking to activate default tool GetToolManager()->ConfigureChangeTrackingMode(EToolChangeTrackingMode::NoChangeTracking); Toolkit->SetCurrentPalette(GetValidPaletteName(ModeSettings->DefaultPalette)); // switch back to full undo / redo tracking mode here if that is behavior we want //GetToolManager()->ConfigureChangeTrackingMode(EToolChangeTrackingMode::FullUndoRedo); FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked(FName(TEXT("LevelEditor"))); LevelEditor.OnRedrawLevelEditingViewports().AddUObject(this, &UMeshPaintMode::UpdateOnMaterialChange); // some global cvars can affect whether painting is valid (nanite on/off etc) CVarDelegateHandle = IConsoleManager::Get().RegisterConsoleVariableSink_Handle(FConsoleCommandDelegate::CreateLambda([this]{ bRecacheValidForPaint = true; })); } void UMeshPaintMode::Exit() { ModeSettings->DefaultPalette = Toolkit->GetCurrentPalette(); Toolkit->OnPaletteChanged().Remove(PaletteChangedHandle); FCoreUObjectDelegates::OnObjectsReplaced.RemoveAll(this); GEditor->OnEditorClose().RemoveAll(this); OnResetViewMode(); const FMeshPaintEditorModeCommands& Commands = FMeshPaintEditorModeCommands::Get(); const TSharedRef& CommandList = Toolkit->GetToolkitCommands(); for (auto It : Commands.Commands) { for (const TSharedPtr Action : It.Value) { CommandList->UnmapAction(Action); } } Super::Exit(); GEngine->GetEngineSubsystem()->ResetState(); FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked(FName(TEXT("LevelEditor"))); LevelEditor.OnRedrawLevelEditingViewports().RemoveAll(this); IConsoleManager::Get().UnregisterConsoleVariableSink_Handle(CVarDelegateHandle); CVarDelegateHandle = {}; } void UMeshPaintMode::CreateToolkit() { Toolkit = MakeShareable(new FMeshPaintModeToolkit); } void UMeshPaintMode::Tick(FEditorViewportClient* ViewportClient, float DeltaTime) { if (bRecacheDataSizes) { UpdateCachedDataSizes(); } if (bRecacheValidForPaint) { GEngine->GetEngineSubsystem()->UpdatePaintSupportState(); bRecacheValidForPaint = false; } // Close the active paint tool if selection (or other state) changes mean that it's not longer valid to paint. // For example if the selected component or it's materials no longer supports texture painting. EndPaintToolIfNoLongerValid(); // Make sure that correct tab is visible for the current tool. // Note that currently Color and Weight mode share the same Select tool. UInteractiveTool const* ActiveTool = GetToolManager()->GetActiveTool(EToolSide::Mouse); const FString ActiveToolName = GetToolManager()->GetActiveToolName(EToolSide::Mouse); FName ActiveTab = Toolkit->GetCurrentPalette(); FName TargetTab = ActiveTab; if (ActiveToolName == VertexColorPaintToolName) { TargetTab = MeshPaintMode_VertexColor; } else if (ActiveToolName == VertexWeightPaintToolName) { TargetTab = MeshPaintMode_VertexWeights; } else if (ActiveToolName == TextureColorPaintToolName || ActiveToolName == TextureColorSelectToolName) { TargetTab = MeshPaintMode_TextureColor; } else if (ActiveToolName == TextureAssetPaintToolName || ActiveToolName == TextureAssetSelectToolName) { TargetTab = MeshPaintMode_TextureAsset; } EMeshPaintActiveMode CurrentActiveMode = EMeshPaintActiveMode::VertexColor; if (TargetTab == MeshPaintMode_VertexColor) { CurrentActiveMode = EMeshPaintActiveMode::VertexColor; } else if (TargetTab == MeshPaintMode_VertexWeights) { CurrentActiveMode = EMeshPaintActiveMode::VertexWeights; } else if (TargetTab == MeshPaintMode_TextureColor) { CurrentActiveMode = EMeshPaintActiveMode::TextureColor; } else if (TargetTab == MeshPaintMode_TextureAsset) { CurrentActiveMode = EMeshPaintActiveMode::Texture; } if (TargetTab != ActiveTab || ActiveTool == nullptr) { Toolkit->SetCurrentPalette(TargetTab); } if (ViewportClient->IsPerspective()) { // Make sure perspective viewports are still set to real-time GEditor->GetEditorSubsystem()->SetRealtimeViewport(ViewportClient, true); // Set viewport show flags GEditor->GetEditorSubsystem()->SetViewportColorMode(CurrentActiveMode, ModeSettings->ColorViewMode, ViewportClient, ActiveTool); } } bool UMeshPaintMode::HandleClick(FEditorViewportClient* InViewportClient, HHitProxy* HitProxy, const FViewportClick& Click) { return true; } TMap>> UMeshPaintMode::GetModeCommands() const { return FMeshPaintEditorModeCommands::GetCommands(); } void UMeshPaintMode::BindCommands() { const FMeshPaintEditorModeCommands& Commands = FMeshPaintEditorModeCommands::Get(); const TSharedRef& CommandList = Toolkit->GetToolkitCommands(); CommandList->MapAction(Commands.SwapColor, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::SwapColors), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanSwapColors))); CommandList->MapAction(Commands.FillVertex, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::FillVertexColors), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanFillVertexColors))); CommandList->MapAction(Commands.FillTexture, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::FillTexture), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanFillTexture))); CommandList->MapAction(Commands.PropagateMesh, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::PropagateVertexColorsToMesh), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanPropagateVertexColorsToMesh))); CommandList->MapAction(Commands.PropagateLODs, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::PropagateVertexColorsToLODs), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanPropagateVertexColorsToLODs))); CommandList->MapAction(Commands.SaveVertex, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::SaveVertexColorsToAssets), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanSaveVertexColorsToAssets))); CommandList->MapAction(Commands.SaveTexture, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::SaveTexturePackages), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanSaveTexturePackages))); CommandList->MapAction(Commands.Add, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::AddMeshPaintTextures), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanAddMeshPaintTextures))); CommandList->MapAction(Commands.RemoveVertex, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::RemoveInstanceVertexColors), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanRemoveInstanceVertexColors))); CommandList->MapAction(Commands.RemoveTexture, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::RemoveMeshPaintTexture), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanRemoveMeshPaintTextures))); CommandList->MapAction(Commands.Copy, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::Copy), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanCopy))); CommandList->MapAction(Commands.Paste, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::Paste), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanPaste))); CommandList->MapAction(Commands.Import, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::ImportVertexColorsFromFile), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanImportVertexColorsFromFile))); CommandList->MapAction(Commands.GetTextureColors, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::ImportVertexColorsFromMeshPaintTexture), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanImportVertexColorsFromMeshPaintTexture))); CommandList->MapAction(Commands.GetVertexColors, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::ImportMeshPaintTextureFromVertexColors), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanImportMeshPaintTextureFromVertexColors))); CommandList->MapAction(Commands.FixVertex, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::FixVertexColors), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanFixVertexColors))); CommandList->MapAction(Commands.FixTexture, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::FixTextureColors), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanFixTextureColors))); CommandList->MapAction(Commands.PreviousLOD, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::CycleMeshLODs, -1), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanCycleMeshLODs))); CommandList->MapAction(Commands.NextLOD, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::CycleMeshLODs, 1), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanCycleMeshLODs))); CommandList->MapAction(Commands.PreviousTexture, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::CycleTextures, -1), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanCycleTextures))); CommandList->MapAction(Commands.NextTexture, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::CycleTextures, 1), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanCycleTextures))); CommandList->MapAction(Commands.IncreaseBrushRadius, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::ChangeBrushRadius, 1), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanChangeBrush), EUIActionRepeatMode::RepeatEnabled)); CommandList->MapAction(Commands.DecreaseBrushRadius, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::ChangeBrushRadius, -1), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanChangeBrush), EUIActionRepeatMode::RepeatEnabled)); CommandList->MapAction(Commands.IncreaseBrushStrength, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::ChangeBrushStrength, 1), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanChangeBrush), EUIActionRepeatMode::RepeatEnabled)); CommandList->MapAction(Commands.DecreaseBrushStrength, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::ChangeBrushStrength, -1), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanChangeBrush), EUIActionRepeatMode::RepeatEnabled)); CommandList->MapAction(Commands.IncreaseBrushFalloff, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::ChangeBrushFalloff, 1), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanChangeBrush), EUIActionRepeatMode::RepeatEnabled)); CommandList->MapAction(Commands.DecreaseBrushFalloff, FUIAction( FExecuteAction::CreateUObject(this, &UMeshPaintMode::ChangeBrushFalloff, -1), FCanExecuteAction::CreateUObject(this, &UMeshPaintMode::CanChangeBrush), EUIActionRepeatMode::RepeatEnabled)); } void UMeshPaintMode::OnToolStarted(UInteractiveToolManager* Manager, UInteractiveTool* Tool) { FMeshPaintingToolActionCommands::UpdateToolCommandBinding(Tool, Toolkit->GetToolkitCommands(), false); if (UMeshVertexPaintingTool* VertexPaintingTool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left))) { VertexPaintingTool->OnPaintingFinished().BindUObject(this, &UMeshPaintMode::OnVertexPaintFinished); } if (UMeshTextureColorPaintingTool* TextureColorPaintingTool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left))) { TextureColorPaintingTool->OnPaintingFinished().BindUObject(this, &UMeshPaintMode::OnTextureColorVertexPaintFinished); } } void UMeshPaintMode::OnVertexPaintFinished() { if (UMeshVertexPaintingToolProperties* VertexPaintingToolProperties = UMeshPaintMode::GetVertexToolProperties()) { if (!VertexPaintingToolProperties->bPaintOnSpecificLOD) { PropagateVertexColorsToLODs(); } else { if (UMeshPaintingSubsystem* MeshPaintingSubsystem = GEngine->GetEngineSubsystem()) { bRecacheDataSizes = true; MeshPaintingSubsystem->Refresh(); } } } } void UMeshPaintMode::OnTextureColorVertexPaintFinished(UMeshComponent* MeshComponent) { if (UMeshTextureColorPaintingToolProperties* TexturePaintingToolProperties = UMeshPaintMode::GetTextureColorToolProperties()) { if (TexturePaintingToolProperties->bPropagateToVertexColor) { GEditor->GetEditorSubsystem()->ImportVertexColorsFromMeshPaintTexture(MeshComponent); } } } void UMeshPaintMode::OnToolEnded(UInteractiveToolManager* Manager, UInteractiveTool* Tool) { FMeshPaintingToolActionCommands::UpdateToolCommandBinding(Tool, Toolkit->GetToolkitCommands(), true); // First update your bindings, then call the base behavior Super::OnToolEnded(Manager, Tool); } void UMeshPaintMode::ActorSelectionChangeNotify() { UpdateSelectedMeshes(); } void UMeshPaintMode::ElementSelectionChangeNotify() { UpdateSelectedMeshes(); } void UMeshPaintMode::ActorPropChangeNotify() { // Setting change on selected components can change whether they are valid for painting. bRecacheValidForPaint = true; } void UMeshPaintMode::UpdateSelectedMeshes() { if (UMeshPaintingSubsystem* MeshPaintingSubsystem = GEngine->GetEngineSubsystem()) { MeshPaintingSubsystem->ResetState(); TArray CurrentMeshComponents = GetSelectedComponents(); MeshPaintingSubsystem->AddSelectedMeshComponents(CurrentMeshComponents); MeshPaintingSubsystem->bNeedsRecache = true; } bRecacheDataSizes = true; bRecacheValidForPaint = true; } void UMeshPaintMode::EndPaintToolIfNoLongerValid() { bool bInvalidTool = false; UInteractiveToolManager* ToolManager = GetToolManager(); UInteractiveTool const* Tool = (ToolManager == nullptr) ? nullptr : ToolManager->GetActiveTool(EToolSide::Mouse); if (Tool != nullptr) { UMeshPaintingSubsystem* MeshPaintingSubsystem = GEngine->GetEngineSubsystem(); if (Tool->IsA()) { bInvalidTool = !MeshPaintingSubsystem->GetSelectionSupportsVertexPaint(); } else if (Tool->IsA()) { bInvalidTool = !MeshPaintingSubsystem->GetSelectionSupportsTextureColorPaint(); } else if (Tool->IsA()) { bInvalidTool = !MeshPaintingSubsystem->GetSelectionSupportsTextureAssetPaint(); } } if (bInvalidTool) { GetInteractiveToolsContext()->EndTool(EToolShutdownType::Accept); ActivateDefaultTool(); } } void UMeshPaintMode::UpdateOnMaterialChange(bool bInvalidateHitProxies) { // Need to recheck whether the current material supports texture paint. bRecacheValidForPaint = true; } void UMeshPaintMode::OnObjectsReplaced(const TMap& InOldToNewInstanceMap) { if (UMeshPaintingSubsystem* MeshPaintingSubsystem = GEngine->GetEngineSubsystem()) { MeshPaintingSubsystem->ClearSelectedMeshComponents(); MeshPaintingSubsystem->Refresh(); UpdateSelectedMeshes(); } } void UMeshPaintMode::FillVertexColors() { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionFillInstColors", "Filling Per-Instance Vertex Colors")); const TArray MeshComponents = GetSelectedComponents(); UMeshPaintingSubsystem* MeshPaintingSubsystem = GEngine->GetEngineSubsystem(); static const bool bConvertSRGB = false; FColor FillColor = FColor::White; FColor MaskColor = FColor::White; if (GetToolManager()->GetActiveTool(EToolSide::Mouse)->IsA()) { FillColor = MeshPaintingSubsystem->GenerateColorForTextureWeight((int32)GetVertexWeightToolProperties()->TextureWeightType, (int32)GetVertexWeightToolProperties()->PaintTextureWeightIndex).ToFColor(bConvertSRGB); } else if (UMeshVertexColorPaintingToolProperties* ColorProperties = GetVertexColorToolProperties()) { FillColor = ColorProperties->PaintColor.ToFColor(bConvertSRGB); MaskColor.R = ColorProperties->bWriteRed ? 255 : 0; MaskColor.G = ColorProperties->bWriteGreen ? 255 : 0; MaskColor.B = ColorProperties->bWriteBlue ? 255 : 0; MaskColor.A = ColorProperties->bWriteAlpha ? 255 : 0; } TUniquePtr< FComponentReregisterContext > ComponentReregisterContext; /** Fill each mesh component with the given vertex color */ for (UMeshComponent* Component : MeshComponents) { checkf(Component != nullptr, TEXT("Invalid Mesh Component")); Component->Modify(); ComponentReregisterContext = MakeUnique(Component); TSharedPtr MeshAdapter = MeshPaintingSubsystem->GetAdapterForComponent(Component); if (MeshAdapter) { MeshAdapter->PreEdit(); } UMeshVertexPaintingToolProperties* VertexProperties = UMeshPaintMode::GetVertexToolProperties(); const bool bPaintOnSpecificLOD = VertexProperties ? VertexProperties->bPaintOnSpecificLOD : false; if (Component->IsA()) { MeshPaintingSubsystem->FillStaticMeshVertexColors(Cast(Component), bPaintOnSpecificLOD ? VertexProperties->LODIndex : -1, FillColor, MaskColor); } else if (Component->IsA()) { GEngine->GetEngineSubsystem()->FillSkeletalMeshVertexColors(Cast(Component), bPaintOnSpecificLOD ? VertexProperties->LODIndex : -1, FillColor, MaskColor); } else if (MeshAdapter) // We don't have a custom fill function for this type of component; try to go through the adapter. { TArray MeshIndices = MeshAdapter->GetMeshIndices(); UMeshPaintingSubsystem* PaintingSubsystem = GEngine->GetEngineSubsystem(); for (uint32 VID : MeshIndices) { FColor Color; MeshAdapter->GetVertexColor((int32)VID, Color); PaintingSubsystem->ApplyFillWithMask(Color, MaskColor, FillColor); MeshAdapter->SetVertexColor((int32)VID, Color); } } if (MeshAdapter) { MeshAdapter->PostEdit(); } } } void UMeshPaintMode::PropagateVertexColorsToMesh() { const TArray StaticMeshComponents = GetSelectedComponents(); FSuppressableWarningDialog::FSetupInfo SetupInfo(LOCTEXT("PushInstanceVertexColorsPrompt_Message", "This operation copies vertex colors from LOD 0 of the selected instance to all LODs of the source asset, overwriting any existing vertex colors.\n\nThis change will also propagate to all other instances of the same asset that do not have custom vertex colors."), LOCTEXT("PushInstanceVertexColorsPrompt_Title", "Warning: Overwriting Vertex Colors on Source Asset"), "Warning_PushInstanceVertexColorsPrompt"); SetupInfo.ConfirmText = LOCTEXT("PushInstanceVertexColorsPrompt_ConfirmText", "Overwrite"); SetupInfo.CancelText = LOCTEXT("PushInstanceVertexColorsPrompt_CancelText", "Cancel"); SetupInfo.CheckBoxText = LOCTEXT("PushInstanceVertexColorsPrompt_CheckBoxText", "Always overwrite source asset without prompting"); FSuppressableWarningDialog VertexColorCopyWarning(SetupInfo); // Prompt the user to see if they really want to push the vert colors to the source mesh and to explain // the ramifications of doing so. This uses a suppressible dialog so that the user has the choice to always ignore the warning. if (VertexColorCopyWarning.ShowModal() != FSuppressableWarningDialog::Cancel) { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionPropogateColors", "Propagating Vertex Colors To Source Meshes")); GEditor->GetEditorSubsystem()->PropagateVertexColors(StaticMeshComponents); } } bool UMeshPaintMode::CanPropagateVertexColorsToMesh() const { // Check whether or not our selected Static Mesh Components contain instance based vertex colors (only these can be propagated to the base mesh) int32 NumInstanceVertexColorBytes = 0; TArray StaticMeshes; TArray StaticMeshComponents = GetSelectedComponents(); return GEditor->GetEditorSubsystem()->CanPropagateVertexColors(StaticMeshComponents, StaticMeshes, NumInstanceVertexColorBytes); } void UMeshPaintMode::ImportVertexColorsFromFile() { const TArray MeshComponents = GetSelectedComponents(); if (MeshComponents.Num() == 1) { /** Import vertex color to single selected mesh component */ FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionImportColors", "Importing Vertex Colors From Texture")); GEditor->GetEditorSubsystem()->ImportVertexColorsFromTexture(MeshComponents[0]); } bRecacheDataSizes = true; } void UMeshPaintMode::SaveVertexColorsToAssets() { const TArray StaticMeshComponents = GetSelectedComponents(); const TArray SkeletalMeshComponents = GetSelectedComponents(); /** Try and save outstanding dirty packages for currently selected mesh components */ TArray ObjectsToSave; for (UStaticMeshComponent* StaticMeshComponent : StaticMeshComponents) { if (StaticMeshComponent && StaticMeshComponent->GetStaticMesh()) { ObjectsToSave.Add(StaticMeshComponent->GetStaticMesh()); } } for (USkeletalMeshComponent* SkeletalMeshComponent : SkeletalMeshComponents) { if (SkeletalMeshComponent && SkeletalMeshComponent->GetSkeletalMeshAsset()) { ObjectsToSave.Add(SkeletalMeshComponent->GetSkeletalMeshAsset()); } } if (ObjectsToSave.Num() > 0) { UPackageTools::SavePackagesForObjects(ObjectsToSave); } } bool UMeshPaintMode::CanSaveVertexColorsToAssets() const { // Check whether or not any of our selected mesh components contain mesh objects which require saving TArray Components = GetSelectedComponents(); bool bValid = false; for (UMeshComponent* Component : Components) { UObject* Object = nullptr; if (UStaticMeshComponent* StaticMeshComponent = Cast(Component)) { Object = StaticMeshComponent->GetStaticMesh(); } else if (USkeletalMeshComponent* SkeletalMeshComponent = Cast(Component)) { Object = SkeletalMeshComponent->GetSkeletalMeshAsset(); } if (Object != nullptr && Object->GetOutermost()->IsDirty()) { bValid = true; break; } } return bValid; } bool UMeshPaintMode::CanRemoveInstanceVertexColors() const { const TArray StaticMeshComponents = GetSelectedComponents(); int32 PaintingMeshLODIndex = 0; if (UMeshVertexPaintingToolProperties* VertexProperties = UMeshPaintMode::GetVertexToolProperties()) { PaintingMeshLODIndex = VertexProperties->bPaintOnSpecificLOD ? VertexProperties->LODIndex : 0; } int32 NumValidMeshes = 0; // Retrieve per instance vertex color information (only valid if the component contains actual instance vertex colors) for (UStaticMeshComponent* Component : StaticMeshComponents) { if (Component != nullptr && Component->GetStaticMesh() != nullptr && Component->GetStaticMesh()->GetNumLODs() > (int32)PaintingMeshLODIndex) { uint32 BufferSize = GEngine->GetEngineSubsystem()->GetVertexColorBufferSize(Component, PaintingMeshLODIndex, true); if (BufferSize > 0) { ++NumValidMeshes; } } } return (NumValidMeshes != 0); } bool UMeshPaintMode::CanPasteInstanceVertexColors() const { const TArray StaticMeshComponents = GetSelectedComponents(); const TArray CopiedColorsByComponent = GEngine->GetEngineSubsystem()->GetCopiedColorsByComponent(); return GEditor->GetEditorSubsystem()->CanPasteInstanceVertexColors(StaticMeshComponents, CopiedColorsByComponent); } bool UMeshPaintMode::CanCopyInstanceVertexColors() const { const TArray StaticMeshComponents = GetSelectedComponents(); int32 PaintingMeshLODIndex = 0; if (UMeshVertexPaintingToolProperties* VertexProperties = UMeshPaintMode::GetVertexToolProperties()) { PaintingMeshLODIndex = VertexProperties->bPaintOnSpecificLOD ? VertexProperties->LODIndex : 0; } return GEditor->GetEditorSubsystem()->CanCopyInstanceVertexColors(StaticMeshComponents, PaintingMeshLODIndex); } bool UMeshPaintMode::CanPropagateVertexColorsToLODs() const { bool bPaintOnSpecificLOD = false; if (UMeshVertexPaintingToolProperties* VertexProperties = UMeshPaintMode::GetVertexToolProperties()) { bPaintOnSpecificLOD = VertexProperties ? VertexProperties->bPaintOnSpecificLOD : false; } // Can propagate when the mesh contains per-lod vertex colors or when we are not painting to a specific lod const bool bSelectionContainsPerLODColors = GEngine->GetEngineSubsystem()->SelectionContainsPerLODColors(); return bSelectionContainsPerLODColors || !bPaintOnSpecificLOD; } void UMeshPaintMode::CopyInstanceVertexColors() { const TArray StaticMeshComponents = GetSelectedComponents(); TArray CopiedColorsByComponent; GEditor->GetEditorSubsystem()->CopyVertexColors(StaticMeshComponents, CopiedColorsByComponent); GEngine->GetEngineSubsystem()->SetCopiedColorsByComponent(CopiedColorsByComponent); } void UMeshPaintMode::PasteInstanceVertexColors() { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionPasteInstColors", "Pasting Per-Instance Vertex Colors")); const TArray StaticMeshComponents = GetSelectedComponents(); TArray CopiedColorsByComponent = GEngine->GetEngineSubsystem()->GetCopiedColorsByComponent(); GEditor->GetEditorSubsystem()->PasteVertexColors(StaticMeshComponents, CopiedColorsByComponent); bRecacheDataSizes = true; } void UMeshPaintMode::FixVertexColors() { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionFixInstColors", "Fixing Per-Instance Vertex Colors")); const TArray StaticMeshComponents = GetSelectedComponents(); for (UStaticMeshComponent* Component : StaticMeshComponents) { Component->FixupOverrideColorsIfNecessary(); } bRecacheDataSizes = true; } bool UMeshPaintMode::CanFixVertexColors() const { const TArray StaticMeshComponents = GetSelectedComponents(); bool bAnyMeshNeedsFixing = false; /** Check if there are any static mesh components which require fixing */ for (UStaticMeshComponent* Component : StaticMeshComponents) { bAnyMeshNeedsFixing |= Component->RequiresOverrideVertexColorsFixup(); } return bAnyMeshNeedsFixing; } void UMeshPaintMode::RemoveInstanceVertexColors() { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionRemoveInstColors", "Removing Per-Instance Vertex Colors")); const TArray StaticMeshComponents = GetSelectedComponents(); for (UStaticMeshComponent* Component : StaticMeshComponents) { GEngine->GetEngineSubsystem()->RemoveComponentInstanceVertexColors(Component); } bRecacheDataSizes = true; } void UMeshPaintMode::PropagateVertexColorsToLODs() { //Only show the lost data warning if there is actually some data to lose bool bAbortChange = false; UMeshPaintingSubsystem* MeshPaintingSubsystem = GEngine->GetEngineSubsystem(); TArray PaintableComponents = MeshPaintingSubsystem->GetPaintableMeshComponents(); const bool bSelectionContainsPerLODColors = MeshPaintingSubsystem->SelectionContainsPerLODColors(); if (bSelectionContainsPerLODColors) { //Warn the user they will lose custom painting data FSuppressableWarningDialog::FSetupInfo SetupInfo(LOCTEXT("LooseLowersLODsVertexColorsPrompt_Message", "This operation copies vertex colors from LOD 0 to all other LODs in this instance, overwriting any existing vertex colors.\n\nAt least one LOD has custom vertex colors that will be lost."), LOCTEXT("LooseLowersLODsVertexColorsPrompt_Title", "Warning: Overwriting Vertex Colors on LODs"), "Warning_LooseLowersLODsVertexColorsPrompt"); SetupInfo.ConfirmText = LOCTEXT("LooseLowersLODsVertexColorsPrompt_ConfirmText", "Overwrite"); SetupInfo.CancelText = LOCTEXT("LooseLowersLODsVertexColorsPrompt_CancelText", "Cancel"); SetupInfo.CheckBoxText = LOCTEXT("LooseLowersLODsVertexColorsPrompt_CheckBoxText", "Always overwrite LODs without prompting"); FSuppressableWarningDialog LooseLowersLODsVertexColorsWarning(SetupInfo); // Prompt the user to see if they really want to propagate the base lod vert colors to the lowers LODs. if (LooseLowersLODsVertexColorsWarning.ShowModal() == FSuppressableWarningDialog::Cancel) { bAbortChange = true; } else { // Reset the state flag as we'll be removing all per-lod colors MeshPaintingSubsystem->ClearSelectionLODColors(); GEditor->GetEditorSubsystem()->RemovePerLODColors(PaintableComponents); } } //The user cancel the change, avoid changing the value if (bAbortChange) { return; } for (UMeshComponent* SelectedComponent : PaintableComponents) { if (SelectedComponent) { TSharedPtr MeshAdapter = MeshPaintingSubsystem->GetAdapterForComponent(SelectedComponent); GEngine->GetEngineSubsystem()->ApplyVertexColorsToAllLODs(*MeshAdapter, SelectedComponent); FComponentReregisterContext ReregisterContext(SelectedComponent); } } bRecacheDataSizes = true; MeshPaintingSubsystem->Refresh(); } template TArray UMeshPaintMode::GetSelectedComponents() const { FToolBuilderState SelectionState; GetToolManager()->GetContextQueriesAPI()->GetCurrentSelectionState(SelectionState); TArray Components; for (int32 SelectionIndex = 0; SelectionIndex < SelectionState.SelectedComponents.Num(); ++SelectionIndex) { ComponentClass* SelectedComponent = Cast(SelectionState.SelectedComponents[SelectionIndex]); if (SelectedComponent) { Components.AddUnique(SelectedComponent); } } if (Components.Num() == 0) { for (int32 SelectionIndex = 0; SelectionIndex < SelectionState.SelectedActors.Num(); ++SelectionIndex) { AActor* SelectedActor = Cast(SelectionState.SelectedActors[SelectionIndex]); if (SelectedActor) { TInlineComponentArray ActorComponents; SelectedActor->GetComponents(ActorComponents); for (ComponentClass* Component : ActorComponents) { Components.AddUnique(Component); } } } } return Components; } template TArray UMeshPaintMode::GetSelectedComponents() const; template TArray UMeshPaintMode::GetSelectedComponents() const; template TArray UMeshPaintMode::GetSelectedComponents() const; template TArray UMeshPaintMode::GetSelectedComponents() const; void UMeshPaintMode::UpdateCachedDataSizes() { CachedVertexDataSize = 0; CachedMeshPaintTextureResourceSize = 0; const bool bInstance = true; if (UMeshPaintingSubsystem* MeshPaintingSubsystem = GEngine->GetEngineSubsystem()) { const TArray MeshComponents = GetSelectedComponents(); for (UMeshComponent* MeshComponent : MeshComponents) { int32 NumLODs = MeshPaintingSubsystem->GetNumberOfLODs(MeshComponent); for (int32 LODIndex = 0; LODIndex < NumLODs; ++LODIndex) { CachedVertexDataSize += MeshPaintingSubsystem->GetVertexColorBufferSize(MeshComponent, LODIndex, bInstance); } CachedMeshPaintTextureResourceSize += MeshPaintingSubsystem->GetMeshPaintTextureResourceSize(MeshComponent); } } bRecacheDataSizes = false; } void UMeshPaintMode::CycleMeshLODs(int32 Direction) { if (UMeshVertexPaintingTool* VertexPaintingTool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left))) { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_CycleLOD", "Changed Current LOD")); VertexPaintingTool->CycleMeshLODs(Direction); } } void UMeshPaintMode::CycleTextures(int32 Direction) { if (UMeshTextureAssetPaintingTool* TexturePaintingTool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left))) { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_CycleTexture", "Changed Current Texture")); TexturePaintingTool->CycleTextures(Direction); } } bool UMeshPaintMode::CanCycleTextures() const { UMeshTextureAssetPaintingTool* TexturePaintingTool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left)); return TexturePaintingTool != nullptr; } void UMeshPaintMode::ChangeBrushRadius(int32 Direction) { if (UBaseBrushTool* Tool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left))) { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_ChangeBrushRadius", "Changed Brush Radius")); if (Direction > 0) { Tool->IncreaseBrushSizeAction(); } else { Tool->DecreaseBrushSizeAction(); } } } void UMeshPaintMode::ChangeBrushStrength(int32 Direction) { if (UBaseBrushTool* Tool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left))) { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_ChangeBrushStrength", "Changed Brush Strength")); if (Direction > 0) { Tool->IncreaseBrushStrengthAction(); } else { Tool->DecreaseBrushStrengthAction(); } } } void UMeshPaintMode::ChangeBrushFalloff(int32 Direction) { if (UBaseBrushTool* Tool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left))) { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_ChangeBrushFalloff", "Changed Brush Falloff")); if (Direction > 0) { Tool->IncreaseBrushFalloffAction(); } else { Tool->DecreaseBrushFalloffAction(); } } } bool UMeshPaintMode::CanChangeBrush() const { UBaseBrushTool* Tool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left)); return Tool != nullptr; } void UMeshPaintMode::ActivateDefaultTool() { FName PaletteName = Toolkit->GetCurrentPalette(); if (PaletteName == UMeshPaintMode::MeshPaintMode_VertexColor || PaletteName == UMeshPaintMode::MeshPaintMode_VertexWeights) { GetInteractiveToolsContext()->StartTool(VertexSelectToolName); } if (PaletteName == UMeshPaintMode::MeshPaintMode_TextureColor) { GetInteractiveToolsContext()->StartTool(TextureColorSelectToolName); } if (PaletteName == UMeshPaintMode::MeshPaintMode_TextureAsset) { GetInteractiveToolsContext()->StartTool(TextureAssetSelectToolName); } } void UMeshPaintMode::UpdateOnPaletteChange(FName NewPaletteName) { UpdateSelectedMeshes(); FString SwitchToToolPaint; FString SwitchToToolSelect; if (NewPaletteName == UMeshPaintMode::MeshPaintMode_VertexColor) { SwitchToToolPaint = VertexColorPaintToolName; SwitchToToolSelect = VertexSelectToolName; } else if (NewPaletteName == UMeshPaintMode::MeshPaintMode_VertexWeights) { SwitchToToolPaint = VertexWeightPaintToolName; SwitchToToolSelect = VertexSelectToolName; } else if (NewPaletteName == UMeshPaintMode::MeshPaintMode_TextureColor) { SwitchToToolPaint = TextureColorPaintToolName; SwitchToToolSelect = TextureColorSelectToolName; } else if (NewPaletteName == UMeshPaintMode::MeshPaintMode_TextureAsset) { SwitchToToolPaint = TextureAssetPaintToolName; SwitchToToolSelect = TextureAssetSelectToolName; } if (!SwitchToToolPaint.IsEmpty()) { // Figure out which tool we would like to be in based on currently-active tool const FString ActiveTool = GetToolManager()->GetActiveToolName(EToolSide::Mouse); const bool bInAnyPaintTool = (ActiveTool == VertexColorPaintToolName || ActiveTool == VertexWeightPaintToolName || ActiveTool == TextureColorPaintToolName || ActiveTool == TextureAssetPaintToolName); const bool bUsePaintTool = bInAnyPaintTool && GetInteractiveToolsContext()->CanStartTool(SwitchToToolPaint); const FString SwitchToTool = bUsePaintTool ? SwitchToToolPaint : SwitchToToolSelect; // Change to new tool if it is different if (SwitchToTool != ActiveTool) { GetInteractiveToolsContext()->StartTool(SwitchToTool); } } } void UMeshPaintMode::FillTexture() { if (UMeshTexturePaintingTool* TexturePaintingTool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left))) { TexturePaintingTool->FloodCurrentPaintTexture(); } } void UMeshPaintMode::OnResetViewMode() { // Reset viewport color mode and realtime override for all active viewports for (FEditorViewportClient* ViewportClient : GEditor->GetAllViewportClients()) { if (!ViewportClient || ViewportClient->GetModeTools() != GetModeManager()) { continue; } GEditor->GetEditorSubsystem()->SetViewportColorMode(EMeshPaintActiveMode::VertexColor, EMeshPaintDataColorViewMode::Normal, ViewportClient, nullptr); GEditor->GetEditorSubsystem()->SetRealtimeViewport(ViewportClient, false); } } bool UMeshPaintMode::IsInSelectTool() const { FString ActiveTool = GetToolManager()->GetActiveToolName(EToolSide::Mouse); return ActiveTool == VertexSelectToolName || ActiveTool == TextureColorSelectToolName || ActiveTool == TextureAssetSelectToolName; } bool UMeshPaintMode::IsInPaintTool() const { FString ActiveTool = GetToolManager()->GetActiveToolName(EToolSide::Mouse); return ActiveTool == VertexColorPaintToolName || ActiveTool == VertexWeightPaintToolName || ActiveTool == TextureColorPaintToolName || ActiveTool == TextureAssetPaintToolName; } void UMeshPaintMode::SwapColors() { GEditor->GetEditorSubsystem()->SwapColors(); } bool UMeshPaintMode::CanSwapColors() const { return IsInPaintTool(); } bool UMeshPaintMode::CanFillVertexColors() const { return IsInPaintTool(); } bool UMeshPaintMode::CanFillTexture() const { return IsInPaintTool(); } void UMeshPaintMode::SaveTexturePackages() { TArray TexturesToSave; if (UMeshTexturePaintingTool* TexturePaintingTool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left))) { TexturePaintingTool->GetModifiedTexturesToSave(TexturesToSave); } if (TexturesToSave.Num()) { UPackageTools::SavePackagesForObjects(TexturesToSave); } } bool UMeshPaintMode::CanSaveTexturePackages() const { TArray TexturesToSave; if (UMeshTexturePaintingTool* TexturePaintingTool = Cast(GetToolManager()->GetActiveTool(EToolSide::Left))) { TexturePaintingTool->GetModifiedTexturesToSave(TexturesToSave); } return TexturesToSave.Num() > 0; } void UMeshPaintMode::AddMeshPaintTextures() { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionAddMeshPaintTexture", "Creating Mesh Paint Texture")); const TArray StaticMeshComponents = GetSelectedComponents(); for (UStaticMeshComponent* Component : StaticMeshComponents) { GEngine->GetEngineSubsystem()->CreateComponentMeshPaintTexture(Component); } bRecacheDataSizes = true; bRecacheValidForPaint = true; } bool UMeshPaintMode::CanAddMeshPaintTextures() const { if (IsInSelectTool()) { const TArray StaticMeshComponents = GetSelectedComponents(); for (UStaticMeshComponent* Component : StaticMeshComponents) { if (Component->GetMeshPaintTexture() == nullptr && Component->CanMeshPaintTextureColors()) { return true; } } } return false; } void UMeshPaintMode::RemoveMeshPaintTexture() { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionRemoveMeshPaintTexture", "Removing Mesh Paint Texture")); const TArray StaticMeshComponents = GetSelectedComponents(); for (UStaticMeshComponent* Component : StaticMeshComponents) { GEngine->GetEngineSubsystem()->RemoveComponentMeshPaintTexture(Component); } bRecacheDataSizes = true; bRecacheValidForPaint = true; } bool UMeshPaintMode::CanRemoveMeshPaintTextures() const { const TArray StaticMeshComponents = GetSelectedComponents(); for (UStaticMeshComponent* Component : StaticMeshComponents) { if (Component->GetMeshPaintTexture() != nullptr) { return true; } } return false; } void UMeshPaintMode::CopyMeshPaintTexture() { const TArray StaticMeshComponents = GetSelectedComponents(); if (StaticMeshComponents.Num() > 0) { GEngine->GetEngineSubsystem()->SetCopiedTexture(StaticMeshComponents[0]->GetMeshPaintTexture()); } } bool UMeshPaintMode::CanCopyMeshPaintTexture() const { const TArray StaticMeshComponents = GetSelectedComponents(); if (StaticMeshComponents.Num() == 1 && StaticMeshComponents[0]->GetMeshPaintTexture() != nullptr && StaticMeshComponents[0]->CanMeshPaintTextureColors()) { return true; } return false; } void UMeshPaintMode::Copy() { FName PaletteName = Toolkit->GetCurrentPalette(); if (PaletteName == UMeshPaintMode::MeshPaintMode_VertexColor || PaletteName == UMeshPaintMode::MeshPaintMode_VertexWeights) { CopyInstanceVertexColors(); } else if (PaletteName == UMeshPaintMode::MeshPaintMode_TextureColor) { CopyMeshPaintTexture(); } } bool UMeshPaintMode::CanCopy() const { FName PaletteName = Toolkit->GetCurrentPalette(); if (PaletteName == UMeshPaintMode::MeshPaintMode_VertexColor || PaletteName == UMeshPaintMode::MeshPaintMode_VertexWeights) { return CanCopyInstanceVertexColors(); } else if (PaletteName == UMeshPaintMode::MeshPaintMode_TextureColor) { return CanCopyMeshPaintTexture(); } return false; } void UMeshPaintMode::PasteMeshPaintTexture() { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionPasteMeshPaintTexture", "Pasting Texture Colors")); FImage const& Image = GEngine->GetEngineSubsystem()->GetCopiedTexture(); const TArray StaticMeshComponents = GetSelectedComponents(); for (UStaticMeshComponent* Component : StaticMeshComponents) { GEngine->GetEngineSubsystem()->CreateComponentMeshPaintTexture(Component, Image); } bRecacheDataSizes = true; bRecacheValidForPaint = true; GEngine->GetEngineSubsystem()->Refresh(); } bool UMeshPaintMode::CanPasteMeshPaintTexture() const { FImage const& Image = GEngine->GetEngineSubsystem()->GetCopiedTexture(); if (Image.GetNumPixels() == 0) { return false; } const TArray StaticMeshComponents = GetSelectedComponents(); for (UStaticMeshComponent* Component : StaticMeshComponents) { if (Component->CanMeshPaintTextureColors()) { return true; } } return false; } void UMeshPaintMode::Paste() { FName PaletteName = Toolkit->GetCurrentPalette(); if (PaletteName == UMeshPaintMode::MeshPaintMode_VertexColor || PaletteName == UMeshPaintMode::MeshPaintMode_VertexWeights) { PasteInstanceVertexColors(); } else if (PaletteName == UMeshPaintMode::MeshPaintMode_TextureColor) { PasteMeshPaintTexture(); } } bool UMeshPaintMode::CanPaste() const { FName PaletteName = Toolkit->GetCurrentPalette(); if (PaletteName == UMeshPaintMode::MeshPaintMode_VertexColor || PaletteName == UMeshPaintMode::MeshPaintMode_VertexWeights) { return CanPasteInstanceVertexColors(); } else if (PaletteName == UMeshPaintMode::MeshPaintMode_TextureColor) { return CanPasteMeshPaintTexture(); } return false; } bool UMeshPaintMode::CanImportVertexColorsFromFile() const { FName PaletteName = Toolkit->GetCurrentPalette(); return PaletteName == UMeshPaintMode::MeshPaintMode_VertexColor || PaletteName == UMeshPaintMode::MeshPaintMode_VertexWeights; } void UMeshPaintMode::ImportVertexColorsFromMeshPaintTexture() { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionImportVertexColorFromTextureColor", "Importing Vertex Colors From Mesh Paint Textures")); const TArray MeshComponents = GetSelectedComponents(); for (UMeshComponent* Component : MeshComponents) { GEditor->GetEditorSubsystem()->ImportVertexColorsFromMeshPaintTexture(Component); } bRecacheDataSizes = true; } bool UMeshPaintMode::CanImportVertexColorsFromMeshPaintTexture() const { const TArray MeshComponents = GetSelectedComponents(); for (UMeshComponent* Component : MeshComponents) { if (Component->GetMeshPaintTexture() != nullptr) { return true; } } return false; } void UMeshPaintMode::ImportMeshPaintTextureFromVertexColors() { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionImportTextureColorFromVertexColor", "Importing Mesh Paint Textures From Vertex Colors")); const TArray MeshComponents = GetSelectedComponents(); for (UMeshComponent* Component : MeshComponents) { GEditor->GetEditorSubsystem()->ImportMeshPaintTextureFromVertexColors(Component); } bRecacheDataSizes = true; bRecacheValidForPaint = true; GEngine->GetEngineSubsystem()->Refresh(); } bool UMeshPaintMode::CanImportMeshPaintTextureFromVertexColors() const { const TArray StaticMeshComponents = GetSelectedComponents(); for (UStaticMeshComponent* Component : StaticMeshComponents) { if (Component->CanMeshPaintTextureColors()) { return true; } } return false; } void UMeshPaintMode::FixTextureColors() { FScopedTransaction Transaction(LOCTEXT("LevelMeshPainter_TransactionFixTextureColors", "Fixing Per-Instance Texture Colors")); GEditor->GetEditorSubsystem()->FixTextureColors(GetSelectedComponents()); bRecacheDataSizes = true; GEngine->GetEngineSubsystem()->Refresh(); } bool UMeshPaintMode::CanFixTextureColors() const { return GEditor->GetEditorSubsystem()->CanFixTextureColors(GetSelectedComponents()); } bool UMeshPaintMode::CanCycleMeshLODs() const { FName PaletteName = Toolkit->GetCurrentPalette(); return PaletteName == UMeshPaintMode::MeshPaintMode_VertexColor || PaletteName == UMeshPaintMode::MeshPaintMode_VertexWeights; } #undef LOCTEXT_NAMESPACE