1881 lines
70 KiB
C++
1881 lines
70 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MaterialInstanceEditor.h"
|
|
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "EngineGlobals.h"
|
|
#include "Engine/Texture.h"
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "Widgets/Views/SListView.h"
|
|
#include "UObject/ObjectSaveContext.h"
|
|
#include "UObject/Package.h"
|
|
#include "Editor.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Styling/CoreStyle.h"
|
|
#include "MaterialEditor/DEditorTextureParameterValue.h"
|
|
#include "MaterialEditor/DEditorRuntimeVirtualTextureParameterValue.h"
|
|
#include "MaterialEditor/DEditorSparseVolumeTextureParameterValue.h"
|
|
#include "Materials/Material.h"
|
|
#include "MaterialEditor/MaterialEditorInstanceConstant.h"
|
|
#include "ThumbnailRendering/SceneThumbnailInfoWithPrimitive.h"
|
|
#include "Materials/MaterialFunction.h"
|
|
#include "Materials/MaterialInstance.h"
|
|
#include "Materials/MaterialInstanceConstant.h"
|
|
#include "Materials/MaterialFunctionInstance.h"
|
|
#include "Materials/MaterialExpressionFunctionOutput.h"
|
|
#include "MaterialEditorModule.h"
|
|
#include "ToolMenus.h"
|
|
#include "Toolkits/AssetEditorToolkitMenuContext.h"
|
|
#include "MaterialEditorContext.h"
|
|
|
|
#include "Materials/MaterialExpressionTextureBase.h"
|
|
#include "Materials/MaterialExpressionTextureSampleParameter.h"
|
|
#include "Materials/MaterialExpressionRuntimeVirtualTextureSampleParameter.h"
|
|
#include "Materials/MaterialExpressionSparseVolumeTextureSample.h"
|
|
|
|
#include "MaterialEditor.h"
|
|
#include "MaterialEditorActions.h"
|
|
#include "MaterialEditorUtilities.h"
|
|
|
|
#include "PropertyEditorModule.h"
|
|
#include "MaterialEditorInstanceDetailCustomization.h"
|
|
#include "SMaterialLayersFunctionsTree.h"
|
|
|
|
#include "EditorViewportCommands.h"
|
|
#include "Widgets/Docking/SDockTab.h"
|
|
#include "CanvasTypes.h"
|
|
#include "UnrealEdGlobals.h"
|
|
#include "Editor/UnrealEdEngine.h"
|
|
#include "AdvancedPreviewSceneModule.h"
|
|
#include "ContentBrowserModule.h"
|
|
#include "Misc/MessageDialog.h"
|
|
#include "Framework/Commands/UICommandInfo.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "MaterialStats.h"
|
|
#include "MaterialEditingLibrary.h"
|
|
#include "Widgets/Layout/SScrollBox.h"
|
|
#include "DebugViewModeHelpers.h"
|
|
#include "IContentBrowserSingleton.h"
|
|
#include "MaterialEditorTabs.h"
|
|
#include "MaterialEditorViewportToolbarSections.h"
|
|
#include "Materials/MaterialFunctionMaterialLayer.h"
|
|
#include "Materials/MaterialFunctionMaterialLayerBlend.h"
|
|
#include "VT/RuntimeVirtualTexture.h"
|
|
#include "SparseVolumeTexture/SparseVolumeTexture.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Subsystems/AssetEditorSubsystem.h"
|
|
#include "SMaterialEditorSubstrateWidget.h"
|
|
#include "ViewportToolbar/UnrealEdViewportToolbar.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "MaterialInstanceEditor"
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogMaterialInstanceEditor, Log, All);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// SMaterialTreeWidgetItem
|
|
class SMaterialTreeWidgetItem : public SMultiColumnTableRow< TWeakObjectPtr<UMaterialInterface> >
|
|
{
|
|
public:
|
|
SLATE_BEGIN_ARGS(SMaterialTreeWidgetItem)
|
|
: _ParentIndex( -1 )
|
|
, _WidgetInfoToVisualize()
|
|
{}
|
|
SLATE_ARGUMENT( int32, ParentIndex )
|
|
SLATE_ARGUMENT( TWeakObjectPtr<UMaterialInterface>, WidgetInfoToVisualize )
|
|
SLATE_END_ARGS()
|
|
|
|
/**
|
|
* Construct child widgets that comprise this widget.
|
|
*
|
|
* @param InArgs Declaration from which to construct this widget
|
|
*/
|
|
void Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView )
|
|
{
|
|
this->WidgetInfo = InArgs._WidgetInfoToVisualize;
|
|
this->ParentIndex = InArgs._ParentIndex;
|
|
|
|
SMultiColumnTableRow< TWeakObjectPtr<UMaterialInterface> >::Construct( FSuperRowType::FArguments(), InOwnerTableView );
|
|
}
|
|
|
|
/** @return Widget based on the column name */
|
|
virtual TSharedRef<SWidget> GenerateWidgetForColumn( const FName& ColumnName ) override
|
|
{
|
|
FText Entry;
|
|
FSlateFontInfo FontInfo = FCoreStyle::GetDefaultFontStyle("Regular", 9);
|
|
if ( ColumnName == "Parent" )
|
|
{
|
|
if ( ParentIndex == 0 )
|
|
{
|
|
Entry = NSLOCTEXT("UnrealEd", "Material", "Material");
|
|
}
|
|
else if ( ParentIndex != -1 )
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add( TEXT("Index"), ParentIndex );
|
|
Entry = FText::Format( FText::FromString("Parent {Index}"), Args );
|
|
}
|
|
else
|
|
{
|
|
Entry = NSLOCTEXT("UnrealEd", "Current", "Current");
|
|
FontInfo = FCoreStyle::GetDefaultFontStyle("Bold", 9);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Entry = FText::FromString( WidgetInfo.Get()->GetName() );
|
|
if ( ParentIndex == -1 )
|
|
{
|
|
FontInfo = FCoreStyle::GetDefaultFontStyle("Bold", 9);
|
|
}
|
|
}
|
|
|
|
return
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(2)
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( Entry )
|
|
.Font( FontInfo )
|
|
];
|
|
}
|
|
|
|
protected:
|
|
/** The info about the widget that we are visualizing */
|
|
TAttribute< TWeakObjectPtr<UMaterialInterface> > WidgetInfo;
|
|
|
|
/** The index this material has in our parents array */
|
|
int32 ParentIndex;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// SFunctionTreeWidgetItem
|
|
class SFunctionTreeWidgetItem : public SMultiColumnTableRow< TWeakObjectPtr<UMaterialFunctionInterface> >
|
|
{
|
|
public:
|
|
SLATE_BEGIN_ARGS(SFunctionTreeWidgetItem)
|
|
: _ParentIndex( -1 )
|
|
, _WidgetInfoToVisualize()
|
|
{}
|
|
SLATE_ARGUMENT( int32, ParentIndex )
|
|
SLATE_ARGUMENT( TWeakObjectPtr<UMaterialFunctionInterface>, WidgetInfoToVisualize )
|
|
SLATE_END_ARGS()
|
|
|
|
/**
|
|
* Construct child widgets that comprise this widget.
|
|
*
|
|
* @param InArgs Declaration from which to construct this widget
|
|
*/
|
|
void Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView )
|
|
{
|
|
this->WidgetInfo = InArgs._WidgetInfoToVisualize;
|
|
this->ParentIndex = InArgs._ParentIndex;
|
|
|
|
SMultiColumnTableRow< TWeakObjectPtr<UMaterialFunctionInterface> >::Construct( FSuperRowType::FArguments(), InOwnerTableView );
|
|
}
|
|
|
|
/** @return Widget based on the column name */
|
|
virtual TSharedRef<SWidget> GenerateWidgetForColumn( const FName& ColumnName ) override
|
|
{
|
|
FText Entry;
|
|
FSlateFontInfo FontInfo = FSlateFontInfo(FCoreStyle::GetDefaultFont(), 9, "Regular");
|
|
if ( ColumnName == "Parent" )
|
|
{
|
|
if ( ParentIndex == 0 )
|
|
{
|
|
Entry = NSLOCTEXT("UnrealEd", "Function", "Function");
|
|
}
|
|
else if ( ParentIndex != -1 )
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add( TEXT("Index"), ParentIndex );
|
|
Entry = FText::Format( FText::FromString("Parent {Index}"), Args );
|
|
}
|
|
else
|
|
{
|
|
Entry = NSLOCTEXT("UnrealEd", "Current", "Current");
|
|
FontInfo = FSlateFontInfo(FCoreStyle::GetDefaultFont(), 9, "Bold");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Entry = FText::FromString( WidgetInfo.Get()->GetName() );
|
|
if ( ParentIndex == -1 )
|
|
{
|
|
FontInfo = FSlateFontInfo(FCoreStyle::GetDefaultFont(), 9, "Bold");
|
|
}
|
|
}
|
|
|
|
return
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(2)
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( Entry )
|
|
.Font( FontInfo )
|
|
];
|
|
}
|
|
|
|
protected:
|
|
/** The info about the widget that we are visualizing */
|
|
TAttribute< TWeakObjectPtr<UMaterialFunctionInterface> > WidgetInfo;
|
|
|
|
/** The index this material has in our parents array */
|
|
int32 ParentIndex;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FMaterialInstanceEditor
|
|
|
|
static constexpr int32 SpacingBetweenLines = 13;
|
|
|
|
void FMaterialInstanceEditor::RegisterTabSpawners(const TSharedRef<class FTabManager>& InTabManager)
|
|
{
|
|
WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_MaterialInstanceEditor", "Material Instance Editor"));
|
|
auto WorkspaceMenuCategoryRef = WorkspaceMenuCategory.ToSharedRef();
|
|
|
|
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
|
|
|
|
InTabManager->RegisterTabSpawner( FMaterialInstanceEditorTabs::PreviewTabId, FOnSpawnTab::CreateSP( this, &FMaterialInstanceEditor::SpawnTab_Preview ) )
|
|
.SetDisplayName( LOCTEXT( "ViewportTab", "Viewport" ) )
|
|
.SetGroup( WorkspaceMenuCategoryRef )
|
|
.SetIcon( FSlateIcon( FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Viewports" ) );
|
|
|
|
InTabManager->RegisterTabSpawner( FMaterialInstanceEditorTabs::PropertiesTabId, FOnSpawnTab::CreateSP( this, &FMaterialInstanceEditor::SpawnTab_Properties ) )
|
|
.SetDisplayName( LOCTEXT( "PropertiesTab", "Details" ) )
|
|
.SetGroup( WorkspaceMenuCategoryRef )
|
|
.SetIcon( FSlateIcon( FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details" ) );
|
|
|
|
if (!bIsFunctionPreviewMaterial)
|
|
{
|
|
FText DisplayName = Substrate::IsMaterialLayeringSupportEnabled() ? LOCTEXT("MaterialLayersTab", "Material Layers") : LOCTEXT("LayerPropertiesTab", "Layer Parameters");
|
|
InTabManager->RegisterTabSpawner(FMaterialInstanceEditorTabs::LayerPropertiesTabId, FOnSpawnTab::CreateSP(this, &FMaterialInstanceEditor::SpawnTab_LayerProperties))
|
|
.SetDisplayName(DisplayName)
|
|
.SetGroup(WorkspaceMenuCategoryRef)
|
|
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Layers"));
|
|
}
|
|
|
|
InTabManager->RegisterTabSpawner(FMaterialInstanceEditorTabs::PreviewSettingsTabId, FOnSpawnTab::CreateSP(this, &FMaterialInstanceEditor::SpawnTab_PreviewSettings))
|
|
.SetDisplayName(LOCTEXT("PreviewSceneSettingsTab", "Preview Scene Settings"))
|
|
.SetGroup(WorkspaceMenuCategoryRef)
|
|
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details"));
|
|
|
|
if(Substrate::IsMaterialLayeringSupportEnabled())
|
|
{
|
|
InTabManager->RegisterTabSpawner(FMaterialInstanceEditorTabs::AssetBrowserTabId, FOnSpawnTab::CreateSP(this, &FMaterialInstanceEditor::SpawnTab_AssetBrowser))
|
|
.SetDisplayName(LOCTEXT("AssetBrowserTab", "Asset Browser"))
|
|
.SetGroup(WorkspaceMenuCategoryRef)
|
|
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details"));
|
|
|
|
InTabManager->RegisterTabSpawner(FMaterialInstanceEditorTabs::SubstrateTabId, FOnSpawnTab::CreateSP(this, &FMaterialInstanceEditor::SpawnTab_Substrate))
|
|
.SetDisplayName(LOCTEXT("SubstrateTab", "Substrate"))
|
|
.SetGroup(WorkspaceMenuCategoryRef)
|
|
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.Palette")); // SUBSTRATE_TODO a Substrate icon
|
|
}
|
|
|
|
MaterialStatsManager->RegisterTabs();
|
|
|
|
OnRegisterTabSpawners().Broadcast(InTabManager);
|
|
}
|
|
|
|
void FMaterialInstanceEditor::UnregisterTabSpawners(const TSharedRef<class FTabManager>& InTabManager)
|
|
{
|
|
FAssetEditorToolkit::UnregisterTabSpawners(InTabManager);
|
|
|
|
InTabManager->UnregisterTabSpawner( FMaterialInstanceEditorTabs::PreviewTabId );
|
|
InTabManager->UnregisterTabSpawner(FMaterialInstanceEditorTabs:: PropertiesTabId );
|
|
if (!bIsFunctionPreviewMaterial)
|
|
{
|
|
InTabManager->UnregisterTabSpawner(FMaterialInstanceEditorTabs::LayerPropertiesTabId);
|
|
}
|
|
InTabManager->UnregisterTabSpawner( FMaterialInstanceEditorTabs::PreviewSettingsTabId );
|
|
if(Substrate::IsMaterialLayeringSupportEnabled())
|
|
{
|
|
InTabManager->UnregisterTabSpawner( FMaterialInstanceEditorTabs::AssetBrowserTabId );
|
|
InTabManager->UnregisterTabSpawner(FMaterialInstanceEditorTabs::SubstrateTabId);
|
|
}
|
|
|
|
MaterialStatsManager->UnregisterTabs();
|
|
|
|
OnUnregisterTabSpawners().Broadcast(InTabManager);
|
|
}
|
|
|
|
void FMaterialInstanceEditor::InitEditorForMaterial(UMaterialInstance* InMaterial)
|
|
{
|
|
check(InMaterial);
|
|
MaterialFunctionOriginal = nullptr;
|
|
MaterialFunctionInstance = nullptr;
|
|
FunctionMaterialProxy = nullptr;
|
|
FunctionInstanceProxy = nullptr;
|
|
}
|
|
|
|
void FMaterialInstanceEditor::InitEditorForMaterialFunction(UMaterialFunctionInstance* InMaterialFunction)
|
|
{
|
|
check(InMaterialFunction);
|
|
MaterialFunctionOriginal = InMaterialFunction;
|
|
|
|
// Working version of the function instance
|
|
MaterialFunctionInstance = (UMaterialFunctionInstance*)StaticDuplicateObject(InMaterialFunction, GetTransientPackage(), NAME_None, ~RF_Standalone, UMaterialFunctionInstance::StaticClass());
|
|
MaterialFunctionInstance->Parent = InMaterialFunction;
|
|
|
|
// Preview material for function expressions
|
|
FunctionMaterialProxy = NewObject<UMaterial>();
|
|
{
|
|
FArchiveUObject DummyArchive;
|
|
FunctionMaterialProxy->Serialize(DummyArchive);
|
|
}
|
|
|
|
FunctionMaterialProxy->SetShadingModel(MSM_Unlit);
|
|
FunctionMaterialProxy->SetFlags(RF_Transactional);
|
|
FunctionMaterialProxy->bIsFunctionPreviewMaterial = true;
|
|
|
|
UMaterialFunctionInterface* BaseFunctionInterface = MaterialFunctionInstance;
|
|
while (UMaterialFunctionInstance* Instance = Cast<UMaterialFunctionInstance>(BaseFunctionInterface))
|
|
{
|
|
BaseFunctionInterface = Instance->GetBaseFunction();
|
|
}
|
|
if (UMaterialFunction* BaseFunction = Cast<UMaterialFunction>(BaseFunctionInterface))
|
|
{
|
|
FunctionMaterialProxy->AssignExpressionCollection(BaseFunction->GetExpressionCollection());
|
|
}
|
|
|
|
// Set expressions to be used with preview material
|
|
bool bSetPreviewExpression = false;
|
|
UMaterialExpressionFunctionOutput* FirstOutput = NULL;
|
|
for (int32 ExpressionIndex = FunctionMaterialProxy->GetExpressions().Num() - 1; ExpressionIndex >= 0; --ExpressionIndex)
|
|
{
|
|
UMaterialExpression* Expression = FunctionMaterialProxy->GetExpressions()[ExpressionIndex];
|
|
check(Expression);
|
|
|
|
Expression->Function = NULL;
|
|
Expression->Material = FunctionMaterialProxy;
|
|
|
|
if (UMaterialExpressionFunctionOutput* FunctionOutput = Cast<UMaterialExpressionFunctionOutput>(Expression))
|
|
{
|
|
FirstOutput = FunctionOutput;
|
|
if (FunctionOutput->bLastPreviewed)
|
|
{
|
|
bSetPreviewExpression = true;
|
|
FunctionOutput->ConnectToPreviewMaterial(FunctionMaterialProxy, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bSetPreviewExpression && FirstOutput)
|
|
{
|
|
FirstOutput->ConnectToPreviewMaterial(FunctionMaterialProxy, 0);
|
|
}
|
|
|
|
{
|
|
FMaterialUpdateContext UpdateContext(FMaterialUpdateContext::EOptions::SyncWithRenderingThread);
|
|
UpdateContext.AddMaterial(FunctionMaterialProxy);
|
|
FunctionMaterialProxy->PreEditChange(NULL);
|
|
FunctionMaterialProxy->PostEditChange();
|
|
}
|
|
|
|
UMaterialFunctionInstance* ParentFunctionInstance = Cast<UMaterialFunctionInstance>(InMaterialFunction->Parent);
|
|
UMaterialInterface* FunctionInstanceParent = FunctionMaterialProxy;
|
|
if (ParentFunctionInstance)
|
|
{
|
|
// If our FunctionInstance inherits from *another* FunctionInstance, the parent may have overriden certain parameters
|
|
// These overriden values should be the default value as far as our FunctionInstance is concerned
|
|
// To model this, we create a MI that will hold these overriden values. So our editor material hierarchy will look like this
|
|
// * UMaterial - includes the UMaterialExpressions, copied from our base UMaterialFunction
|
|
// * UMaterialInstance - we're creating this here, holds all parameter values overriden by our parent UMaterialFunctionInstance(s)
|
|
// * UMaterialInstance - will be created below, this is the object/proxy we'll be editing and potentially setting parameters on
|
|
UMaterialInstanceConstant* FunctionMaterialInstanceProxy = NewObject<UMaterialInstanceConstant>(GetTransientPackage(), NAME_None, RF_Transactional);
|
|
FunctionMaterialInstanceProxy->SetParentEditorOnly(FunctionMaterialProxy);
|
|
{
|
|
FMaterialInstanceParameterUpdateContext UpdateContext(FunctionMaterialInstanceProxy);
|
|
TArray<FMaterialParameterInfo> ParameterInfos;
|
|
TArray<FGuid> ParameterIds;
|
|
for (int32 ParameterTypeIndex = 0; ParameterTypeIndex < NumMaterialParameterTypes; ++ParameterTypeIndex)
|
|
{
|
|
const EMaterialParameterType ParameterType = (EMaterialParameterType)ParameterTypeIndex;
|
|
FunctionMaterialProxy->GetAllParameterInfoOfType(ParameterType, ParameterInfos, ParameterIds);
|
|
for (const FMaterialParameterInfo& ParameterInfo : ParameterInfos)
|
|
{
|
|
FMaterialParameterMetadata ParameterMeta;
|
|
if (ParentFunctionInstance->GetParameterOverrideValue(ParameterType, ParameterInfo.Name, ParameterMeta))
|
|
{
|
|
UpdateContext.SetParameterValueEditorOnly(ParameterInfo, ParameterMeta, EMaterialSetParameterValueFlags::SetCurveAtlas);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
FunctionInstanceParent = FunctionMaterialInstanceProxy;
|
|
}
|
|
|
|
// Preview instance for function expressions
|
|
FunctionInstanceProxy = NewObject<UMaterialInstanceConstant>(GetTransientPackage(), NAME_None, RF_Transactional);
|
|
FunctionInstanceProxy->SetParentEditorOnly(FunctionInstanceParent);
|
|
|
|
MaterialFunctionInstance->OverrideMaterialInstanceParameterValues(FunctionInstanceProxy);
|
|
FunctionInstanceProxy->PreEditChange(NULL);
|
|
FunctionInstanceProxy->PostEditChange();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::InitMaterialInstanceEditor( const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, UObject* ObjectToEdit )
|
|
{
|
|
GEditor->RegisterForUndo( this );
|
|
|
|
check( ObjectToEdit );
|
|
bIsFunctionPreviewMaterial = !!(MaterialFunctionInstance);
|
|
UMaterialInstanceConstant* InstanceConstant = bIsFunctionPreviewMaterial ? FunctionInstanceProxy : Cast<UMaterialInstanceConstant>(ObjectToEdit);
|
|
|
|
bShowAllMaterialParameters = false;
|
|
|
|
// Construct a temp holder for our instance parameters.
|
|
MaterialEditorInstance = NewObject<UMaterialEditorInstanceConstant>(GetTransientPackage(), NAME_None, RF_Transactional);
|
|
|
|
bool bTempUseOldStyleMICEditorGroups = true;
|
|
GConfig->GetBool(TEXT("/Script/UnrealEd.EditorEngine"), TEXT("UseOldStyleMICEditorGroups"), bTempUseOldStyleMICEditorGroups, GEngineIni);
|
|
MaterialEditorInstance->bUseOldStyleMICEditorGroups = bTempUseOldStyleMICEditorGroups;
|
|
MaterialEditorInstance->SetSourceInstance(InstanceConstant);
|
|
MaterialEditorInstance->SetSourceFunction(MaterialFunctionOriginal);
|
|
|
|
MaterialStatsManager = FMaterialStatsUtils::CreateMaterialStats(this, false, GetDefault<UMaterialEditorSettings>()->bAllowIgnoringCompilationErrors);
|
|
MaterialStatsManager->SetMaterialsDisplayNames({MaterialEditorInstance->SourceInstance->GetName()});
|
|
|
|
// Register our commands. This will only register them if not previously registered
|
|
FMaterialEditorCommands::Register();
|
|
|
|
CreateInternalWidgets();
|
|
|
|
BindCommands();
|
|
RegisterToolBar();
|
|
|
|
UpdatePreviewViewportsVisibility();
|
|
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>("MaterialEditor");
|
|
|
|
TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_MaterialInstanceEditor_Layout_v8")
|
|
->AddArea
|
|
(
|
|
FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical)
|
|
->Split
|
|
(
|
|
FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)->SetSizeCoefficient(0.9f)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()->SetSizeCoefficient(0.70f)->SetHideTabWell(true)
|
|
->AddTab(FMaterialInstanceEditorTabs::PreviewTabId, ETabState::OpenedTab)
|
|
->AddTab(FMaterialInstanceEditorTabs::PreviewSettingsTabId, ETabState::ClosedTab)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()->SetSizeCoefficient(0.30f)
|
|
->AddTab(FMaterialInstanceEditorTabs::PropertiesTabId, ETabState::OpenedTab)
|
|
)
|
|
)
|
|
);
|
|
|
|
if (!bIsFunctionPreviewMaterial)
|
|
{
|
|
|
|
if(Substrate::IsMaterialLayeringSupportEnabled())
|
|
{
|
|
StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_MaterialInstanceEditor_Layout_v9")
|
|
->AddArea
|
|
(
|
|
FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical)
|
|
->Split
|
|
(
|
|
FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)->SetSizeCoefficient(0.9f)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()->SetSizeCoefficient(0.70f)->SetHideTabWell(true)
|
|
->AddTab(FMaterialInstanceEditorTabs::PreviewTabId, ETabState::OpenedTab)
|
|
->AddTab(FMaterialInstanceEditorTabs::PreviewSettingsTabId, ETabState::ClosedTab)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewSplitter()->SetOrientation(Orient_Vertical)->SetSizeCoefficient(0.7f)
|
|
->Split
|
|
(
|
|
FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)->SetSizeCoefficient(0.3f)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()/*->SetSizeCoefficient(0.30f)*/
|
|
->AddTab(FMaterialInstanceEditorTabs::LayerPropertiesTabId, ETabState::OpenedTab)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()/*->SetSizeCoefficient(0.30f)*/
|
|
->AddTab(FMaterialInstanceEditorTabs::AssetBrowserTabId, ETabState::OpenedTab)
|
|
)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()->SetSizeCoefficient(0.30f)
|
|
->AddTab(FMaterialInstanceEditorTabs::PropertiesTabId, ETabState::OpenedTab)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_MaterialInstanceEditor_Layout_v8")
|
|
->AddArea
|
|
(
|
|
FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical)
|
|
->Split
|
|
(
|
|
FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)->SetSizeCoefficient(0.9f)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()->SetSizeCoefficient(0.70f)->SetHideTabWell(true)
|
|
->AddTab(FMaterialInstanceEditorTabs::PreviewTabId, ETabState::OpenedTab)
|
|
->AddTab(FMaterialInstanceEditorTabs::PreviewSettingsTabId, ETabState::ClosedTab)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()->SetSizeCoefficient(0.30f)
|
|
->AddTab(FMaterialInstanceEditorTabs::PropertiesTabId, ETabState::OpenedTab)
|
|
->AddTab(FMaterialInstanceEditorTabs::LayerPropertiesTabId, ETabState::OpenedTab)
|
|
->SetForegroundTab(FMaterialInstanceEditorTabs::PropertiesTabId)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
const bool bCreateDefaultStandaloneMenu = true;
|
|
const bool bCreateDefaultToolbar = true;
|
|
TArray<UObject*> ObjectsToEdit;
|
|
ObjectsToEdit.Add(ObjectToEdit);
|
|
ObjectsToEdit.Add(MaterialEditorInstance);
|
|
FAssetEditorToolkit::InitAssetEditor( Mode, InitToolkitHost, MaterialInstanceEditorAppIdentifier, StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, ObjectsToEdit );
|
|
|
|
AddMenuExtender(MaterialEditorModule->GetMenuExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects()));
|
|
|
|
ExtendToolbar();
|
|
RegenerateMenusAndToolbars();
|
|
|
|
// @todo toolkit world centric editing
|
|
/*if( IsWorldCentricAssetEditor() )
|
|
{
|
|
SpawnToolkitTab(GetToolbarTabId(), FString(), EToolkitTabSpot::ToolBar);
|
|
SpawnToolkitTab(PreviewTabId, FString(), EToolkitTabSpot::Viewport);
|
|
SpawnToolkitTab(PropertiesTabId, FString(), EToolkitTabSpot::Details);
|
|
}*/
|
|
|
|
// Load editor settings.
|
|
LoadSettings();
|
|
|
|
// Set the preview mesh for the material. This call must occur after the toolbar is initialized.
|
|
|
|
if ( !SetPreviewAssetByName( *InstanceConstant->PreviewMesh.ToString() ) )
|
|
{
|
|
// If the preview mesh could not be found for this instance, attempt to use the preview mesh for the parent material if one exists,
|
|
// or use a default instead if the parent's preview mesh cannot be used
|
|
|
|
if ( InstanceConstant->Parent == nullptr || !SetPreviewAssetByName( *InstanceConstant->Parent->PreviewMesh.ToString() ) )
|
|
{
|
|
USceneThumbnailInfoWithPrimitive* ThumbnailInfoWithPrim = Cast<USceneThumbnailInfoWithPrimitive>( InstanceConstant->ThumbnailInfo );
|
|
|
|
if ( ThumbnailInfoWithPrim != nullptr )
|
|
{
|
|
SetPreviewAssetByName( *ThumbnailInfoWithPrim->PreviewMesh.ToString() );
|
|
}
|
|
}
|
|
}
|
|
|
|
Refresh();
|
|
|
|
// Notify other editors if this material editor has a post process named output, which may affect their preview
|
|
NotifyUserSceneTextureLoadOrUnload();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::ReInitMaterialFunctionProxies()
|
|
{
|
|
if (bIsFunctionPreviewMaterial)
|
|
{
|
|
// Temporarily store unsaved parameters
|
|
TArray<FScalarParameterValue> ScalarParameterValues = FunctionInstanceProxy->ScalarParameterValues;
|
|
TArray<FVectorParameterValue> VectorParameterValues = FunctionInstanceProxy->VectorParameterValues;
|
|
TArray<FDoubleVectorParameterValue> DoubleVectorParameterValues = FunctionInstanceProxy->DoubleVectorParameterValues;
|
|
TArray<FTextureParameterValue> TextureParameterValues = FunctionInstanceProxy->TextureParameterValues;
|
|
TArray<FTextureCollectionParameterValue> TextureCollectionParameterValues = FunctionInstanceProxy->TextureCollectionParameterValues;
|
|
TArray<FRuntimeVirtualTextureParameterValue> RuntimeVirtualTextureParameterValues = FunctionInstanceProxy->RuntimeVirtualTextureParameterValues;
|
|
TArray<FSparseVolumeTextureParameterValue> SparseVolumeTextureParameterValues = FunctionInstanceProxy->SparseVolumeTextureParameterValues;
|
|
TArray<FFontParameterValue> FontParameterValues = FunctionInstanceProxy->FontParameterValues;
|
|
|
|
const FStaticParameterSet& OldStaticParameters = FunctionInstanceProxy->GetStaticParameters();
|
|
TArray<FStaticSwitchParameter> StaticSwitchParameters = OldStaticParameters.StaticSwitchParameters;
|
|
TArray<FStaticComponentMaskParameter> StaticComponentMaskParameters = OldStaticParameters.EditorOnly.StaticComponentMaskParameters;
|
|
|
|
// Regenerate proxies
|
|
InitEditorForMaterialFunction(MaterialFunctionOriginal);
|
|
MaterialEditorInstance->SetSourceInstance(FunctionInstanceProxy);
|
|
MaterialEditorInstance->SetSourceFunction(MaterialFunctionOriginal);
|
|
|
|
// Restore dynamic parameters, filtering those that no-longer exist
|
|
TArray<FMaterialParameterInfo> OutParameterInfo;
|
|
TArray<FGuid> Guids;
|
|
|
|
FunctionInstanceProxy->GetAllScalarParameterInfo(OutParameterInfo, Guids);
|
|
FunctionInstanceProxy->ScalarParameterValues.Empty();
|
|
for (FScalarParameterValue& ScalarParameter : ScalarParameterValues)
|
|
{
|
|
int32 Index = Guids.Find(ScalarParameter.ExpressionGUID);
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
FunctionInstanceProxy->ScalarParameterValues.Add(ScalarParameter);
|
|
FunctionInstanceProxy->ScalarParameterValues.Last().ParameterInfo = OutParameterInfo[Index];
|
|
}
|
|
}
|
|
|
|
FunctionInstanceProxy->GetAllVectorParameterInfo(OutParameterInfo, Guids);
|
|
FunctionInstanceProxy->VectorParameterValues.Empty();
|
|
for (FVectorParameterValue& VectorParameter : VectorParameterValues)
|
|
{
|
|
int32 Index = Guids.Find(VectorParameter.ExpressionGUID);
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
FunctionInstanceProxy->VectorParameterValues.Add(VectorParameter);
|
|
FunctionInstanceProxy->VectorParameterValues.Last().ParameterInfo = OutParameterInfo[Index];
|
|
}
|
|
}
|
|
|
|
FunctionInstanceProxy->GetAllDoubleVectorParameterInfo(OutParameterInfo, Guids);
|
|
FunctionInstanceProxy->DoubleVectorParameterValues.Empty();
|
|
for (FDoubleVectorParameterValue& DoubleVectorParameter : DoubleVectorParameterValues)
|
|
{
|
|
int32 Index = Guids.Find(DoubleVectorParameter.ExpressionGUID);
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
FunctionInstanceProxy->DoubleVectorParameterValues.Add(DoubleVectorParameter);
|
|
FunctionInstanceProxy->DoubleVectorParameterValues.Last().ParameterInfo = OutParameterInfo[Index];
|
|
}
|
|
}
|
|
|
|
FunctionInstanceProxy->GetAllTextureParameterInfo(OutParameterInfo, Guids);
|
|
FunctionInstanceProxy->TextureParameterValues.Empty();
|
|
for (FTextureParameterValue& TextureParameter : TextureParameterValues)
|
|
{
|
|
int32 Index = Guids.Find(TextureParameter.ExpressionGUID);
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
FunctionInstanceProxy->TextureParameterValues.Add(TextureParameter);
|
|
FunctionInstanceProxy->TextureParameterValues.Last().ParameterInfo = OutParameterInfo[Index];
|
|
}
|
|
}
|
|
|
|
FunctionInstanceProxy->GetAllTextureCollectionParameterInfo(OutParameterInfo, Guids);
|
|
FunctionInstanceProxy->TextureCollectionParameterValues.Empty();
|
|
for (FTextureCollectionParameterValue& TextureCollectionParameter : TextureCollectionParameterValues)
|
|
{
|
|
int32 Index = Guids.Find(TextureCollectionParameter.ExpressionGUID);
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
FunctionInstanceProxy->TextureCollectionParameterValues.Add(TextureCollectionParameter);
|
|
FunctionInstanceProxy->TextureCollectionParameterValues.Last().ParameterInfo = OutParameterInfo[Index];
|
|
}
|
|
}
|
|
|
|
FunctionInstanceProxy->GetAllRuntimeVirtualTextureParameterInfo(OutParameterInfo, Guids);
|
|
FunctionInstanceProxy->RuntimeVirtualTextureParameterValues.Empty();
|
|
for (FRuntimeVirtualTextureParameterValue& RuntimeVirtualTextureParameter : RuntimeVirtualTextureParameterValues)
|
|
{
|
|
int32 Index = Guids.Find(RuntimeVirtualTextureParameter.ExpressionGUID);
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
FunctionInstanceProxy->RuntimeVirtualTextureParameterValues.Add(RuntimeVirtualTextureParameter);
|
|
FunctionInstanceProxy->RuntimeVirtualTextureParameterValues.Last().ParameterInfo = OutParameterInfo[Index];
|
|
}
|
|
}
|
|
|
|
FunctionInstanceProxy->GetAllSparseVolumeTextureParameterInfo(OutParameterInfo, Guids);
|
|
FunctionInstanceProxy->SparseVolumeTextureParameterValues.Empty();
|
|
for (FSparseVolumeTextureParameterValue& SparseVolumeTextureParameter : SparseVolumeTextureParameterValues)
|
|
{
|
|
int32 Index = Guids.Find(SparseVolumeTextureParameter.ExpressionGUID);
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
FunctionInstanceProxy->SparseVolumeTextureParameterValues.Add(SparseVolumeTextureParameter);
|
|
FunctionInstanceProxy->SparseVolumeTextureParameterValues.Last().ParameterInfo = OutParameterInfo[Index];
|
|
}
|
|
}
|
|
|
|
FunctionInstanceProxy->GetAllFontParameterInfo(OutParameterInfo, Guids);
|
|
FunctionInstanceProxy->FontParameterValues.Empty();
|
|
for (FFontParameterValue& FontParameter : FontParameterValues)
|
|
{
|
|
int32 Index = Guids.Find(FontParameter.ExpressionGUID);
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
FunctionInstanceProxy->FontParameterValues.Add(FontParameter);
|
|
FunctionInstanceProxy->FontParameterValues.Last().ParameterInfo = OutParameterInfo[Index];
|
|
}
|
|
}
|
|
|
|
// Restore static parameters, filtering those that no-longer exist
|
|
FStaticParameterSet StaticParametersOverride = FunctionInstanceProxy->GetStaticParameters();
|
|
|
|
FunctionInstanceProxy->GetAllStaticSwitchParameterInfo(OutParameterInfo, Guids);
|
|
StaticParametersOverride.StaticSwitchParameters.Empty();
|
|
for (FStaticSwitchParameter& StaticSwitchParameter : StaticSwitchParameters)
|
|
{
|
|
int32 Index = Guids.Find(StaticSwitchParameter.ExpressionGUID);
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
StaticParametersOverride.StaticSwitchParameters.Add(StaticSwitchParameter);
|
|
StaticParametersOverride.StaticSwitchParameters.Last().ParameterInfo = OutParameterInfo[Index];
|
|
}
|
|
}
|
|
|
|
FunctionInstanceProxy->GetAllStaticComponentMaskParameterInfo(OutParameterInfo, Guids);
|
|
StaticParametersOverride.EditorOnly.StaticComponentMaskParameters.Empty();
|
|
for (FStaticComponentMaskParameter& StaticComponentMaskParameter : StaticComponentMaskParameters)
|
|
{
|
|
int32 Index = Guids.Find(StaticComponentMaskParameter.ExpressionGUID);
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
StaticParametersOverride.EditorOnly.StaticComponentMaskParameters.Add(StaticComponentMaskParameter);
|
|
StaticParametersOverride.EditorOnly.StaticComponentMaskParameters.Last().ParameterInfo = OutParameterInfo[Index];
|
|
}
|
|
}
|
|
|
|
FunctionInstanceProxy->UpdateStaticPermutation(StaticParametersOverride);
|
|
|
|
// Refresh and apply to preview
|
|
FunctionInstanceProxy->PreEditChange(NULL);
|
|
FunctionInstanceProxy->PostEditChange();
|
|
SetPreviewMaterial(FunctionInstanceProxy);
|
|
}
|
|
}
|
|
|
|
FMaterialInstanceEditor::FMaterialInstanceEditor()
|
|
: MaterialEditorInstance(nullptr)
|
|
, bIsFunctionPreviewMaterial(false)
|
|
, MenuExtensibilityManager(new FExtensibilityManager)
|
|
, ToolBarExtensibilityManager(new FExtensibilityManager)
|
|
, MaterialFunctionOriginal(nullptr)
|
|
, MaterialFunctionInstance(nullptr)
|
|
, FunctionMaterialProxy(nullptr)
|
|
, FunctionInstanceProxy(nullptr)
|
|
{
|
|
UPackage::PreSavePackageWithContextEvent.AddRaw(this, &FMaterialInstanceEditor::PreSavePackage);
|
|
}
|
|
|
|
FMaterialInstanceEditor::~FMaterialInstanceEditor()
|
|
{
|
|
bDestructing = true;
|
|
|
|
// Notify other editors if this material editor has a post process named output, which may affect their preview
|
|
NotifyUserSceneTextureLoadOrUnload();
|
|
|
|
// Broadcast that this editor is going down to all listeners
|
|
OnMaterialEditorClosed().Broadcast();
|
|
|
|
GEditor->UnregisterForUndo( this );
|
|
|
|
UPackage::PreSavePackageWithContextEvent.RemoveAll(this);
|
|
|
|
// The streaming data will be null if there were any edits
|
|
if (MaterialEditorInstance && MaterialEditorInstance->SourceInstance && !MaterialEditorInstance->SourceInstance->HasTextureStreamingData())
|
|
{
|
|
MaterialEditorInstance->SourceInstance->SetMarkTextureAsEditorStreamingPool(false);
|
|
|
|
UPackage* Package = MaterialEditorInstance->SourceInstance->GetOutermost();
|
|
if (Package && Package->IsDirty() && Package != GetTransientPackage())
|
|
{
|
|
FMaterialEditorUtilities::BuildTextureStreamingData(MaterialEditorInstance->SourceInstance);
|
|
}
|
|
}
|
|
|
|
if (MaterialEditorInstance)
|
|
{
|
|
MaterialEditorInstance->SourceInstance = nullptr;
|
|
MaterialEditorInstance->SourceFunction = nullptr;
|
|
MaterialEditorInstance->MarkAsGarbage();
|
|
MaterialEditorInstance = nullptr;
|
|
}
|
|
|
|
MaterialParentList.Empty();
|
|
FunctionParentList.Empty();
|
|
|
|
SaveSettings();
|
|
|
|
MaterialInstanceDetails.Reset();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::AddReferencedObjects(FReferenceCollector& Collector)
|
|
{
|
|
// Serialize our custom object instance
|
|
Collector.AddReferencedObject(MaterialEditorInstance);
|
|
}
|
|
|
|
void FMaterialInstanceEditor::BindCommands()
|
|
{
|
|
const FMaterialEditorCommands& Commands = FMaterialEditorCommands::Get();
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.Apply,
|
|
FExecuteAction::CreateSP( this, &FMaterialInstanceEditor::OnApply ),
|
|
FCanExecuteAction::CreateSP( this, &FMaterialInstanceEditor::OnApplyEnabled ),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &FMaterialInstanceEditor::OnApplyVisible));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ShowAllMaterialParameters,
|
|
FExecuteAction::CreateSP( this, &FMaterialInstanceEditor::ToggleShowAllMaterialParameters ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP( this, &FMaterialInstanceEditor::IsShowAllMaterialParametersChecked ) );
|
|
|
|
ToolkitCommands->MapAction(
|
|
FEditorViewportCommands::Get().ToggleRealTime,
|
|
FExecuteAction::CreateSP( PreviewVC.ToSharedRef(), &SMaterialEditor3DPreviewViewport::OnToggleRealtime ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP( PreviewVC.ToSharedRef(), &SMaterialEditor3DPreviewViewport::IsRealtime ) );
|
|
}
|
|
|
|
void FMaterialInstanceEditor::OnApply()
|
|
{
|
|
if (bIsFunctionPreviewMaterial && MaterialEditorInstance)
|
|
{
|
|
UE_LOG(LogMaterialInstanceEditor, Log, TEXT("Applying instance %s"), *GetEditingObjects()[0]->GetName());
|
|
MaterialEditorInstance->bIsFunctionInstanceDirty = true;
|
|
MaterialEditorInstance->ApplySourceFunctionChanges();
|
|
}
|
|
}
|
|
|
|
bool FMaterialInstanceEditor::OnApplyEnabled() const
|
|
{
|
|
return MaterialEditorInstance && MaterialEditorInstance->bIsFunctionInstanceDirty == true;
|
|
}
|
|
|
|
bool FMaterialInstanceEditor::OnApplyVisible() const
|
|
{
|
|
return MaterialEditorInstance && MaterialEditorInstance->bIsFunctionPreviewMaterial == true;
|
|
}
|
|
|
|
bool FMaterialInstanceEditor::OnRequestClose(EAssetEditorCloseReason InCloseReason)
|
|
{
|
|
if (MaterialEditorInstance->bIsFunctionInstanceDirty && InCloseReason != EAssetEditorCloseReason::AssetForceDeleted)
|
|
{
|
|
// Find out the user wants to do with this dirty function instance
|
|
EAppReturnType::Type YesNoCancelReply = FMessageDialog::Open(EAppMsgType::YesNoCancel,
|
|
FText::Format(
|
|
NSLOCTEXT("UnrealEd", "Prompt_MaterialInstanceEditorClose", "Would you like to apply changes to this instance to the original asset?\n{0}\n(No will lose all changes!)"),
|
|
FText::FromString(MaterialFunctionOriginal->GetPathName())));
|
|
|
|
switch (YesNoCancelReply)
|
|
{
|
|
case EAppReturnType::Yes:
|
|
// Update instance and exit
|
|
MaterialEditorInstance->ApplySourceFunctionChanges();
|
|
break;
|
|
|
|
case EAppReturnType::No:
|
|
// Exit
|
|
break;
|
|
|
|
case EAppReturnType::Cancel:
|
|
// Don't exit
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FMaterialInstanceEditor::ToggleShowAllMaterialParameters()
|
|
{
|
|
bShowAllMaterialParameters = !bShowAllMaterialParameters;
|
|
UpdatePropertyWindow();
|
|
}
|
|
|
|
bool FMaterialInstanceEditor::IsShowAllMaterialParametersChecked() const
|
|
{
|
|
return bShowAllMaterialParameters;
|
|
}
|
|
|
|
void FMaterialInstanceEditor::CreateInternalWidgets()
|
|
{
|
|
PreviewVC = SNew(SMaterialEditor3DPreviewViewport)
|
|
.MaterialEditor(SharedThis(this));
|
|
|
|
PreviewUIViewport = SNew(SMaterialEditorUIPreviewViewport, GetMaterialInterface());
|
|
|
|
FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>( "PropertyEditor" );
|
|
FDetailsViewArgs DetailsViewArgs;
|
|
DetailsViewArgs.NameAreaSettings = FDetailsViewArgs::HideNameArea;
|
|
DetailsViewArgs.bHideSelectionTip = true;
|
|
DetailsViewArgs.NotifyHook = this;
|
|
DetailsViewArgs.bShowModifiedPropertiesOption = false;
|
|
DetailsViewArgs.bShowCustomFilterOption = true;
|
|
MaterialInstanceDetails = PropertyEditorModule.CreateDetailView( DetailsViewArgs );
|
|
// the sizes of the parameter lists are only based on the parent material and not changed out from under the details panel
|
|
// When a parameter is added open MI editors are refreshed
|
|
// the tree should also refresh if one of the layer or blend assets is swapped
|
|
|
|
auto ValidationLambda = ([](const FRootPropertyNodeList& PropertyNodeList) { return true; });
|
|
MaterialInstanceDetails->SetCustomValidatePropertyNodesFunction(FOnValidateDetailsViewPropertyNodes::CreateLambda(MoveTemp(ValidationLambda)));
|
|
if (!bIsFunctionPreviewMaterial)
|
|
{
|
|
MaterialLayersFunctionsInstance = SNew(SMaterialLayersFunctionsInstanceWrapper)
|
|
.InMaterialEditorInstance(MaterialEditorInstance)
|
|
.InShowHiddenDelegate(FGetShowHiddenParameters::CreateSP(this, &FMaterialInstanceEditor::GetShowHiddenParameters));
|
|
MaterialLayersFunctionsInstance->OnLayerPropertyChanged.BindSP(this, &FMaterialInstanceEditor::Refresh);
|
|
}
|
|
|
|
FOnGetDetailCustomizationInstance LayoutMICDetails = FOnGetDetailCustomizationInstance::CreateStatic(
|
|
&FMaterialInstanceParameterDetails::MakeInstance, MaterialEditorInstance.Get(), MaterialLayersFunctionsInstance.Get(), FGetShowHiddenParameters::CreateSP(this, &FMaterialInstanceEditor::GetShowHiddenParameters) );
|
|
MaterialInstanceDetails->RegisterInstancedCustomPropertyLayout( UMaterialEditorInstanceConstant::StaticClass(), LayoutMICDetails );
|
|
MaterialInstanceDetails->SetCustomFilterLabel(LOCTEXT("ShowOverriddenOnly", "Show Only Overridden Parameters"));
|
|
MaterialInstanceDetails->SetCustomFilterDelegate(FSimpleDelegate::CreateSP(this, &FMaterialInstanceEditor::FilterOverriddenProperties));
|
|
MaterialEditorInstance->DetailsView = MaterialInstanceDetails;
|
|
|
|
SubstrateWidget = SNew(SMaterialEditorSubstrateWidget, SharedThis(this));
|
|
}
|
|
|
|
void FMaterialInstanceEditor::FilterOverriddenProperties()
|
|
{
|
|
MaterialEditorInstance->bShowOnlyOverrides = !MaterialEditorInstance->bShowOnlyOverrides;
|
|
MaterialInstanceDetails->ForceRefresh();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::UpdatePreviewViewportsVisibility()
|
|
{
|
|
UMaterial* PreviewMaterial = GetMaterialInterface()->GetBaseMaterial();
|
|
if( PreviewMaterial->IsUIMaterial() )
|
|
{
|
|
PreviewVC->SetVisibility(EVisibility::Collapsed);
|
|
PreviewUIViewport->SetVisibility(EVisibility::Visible);
|
|
}
|
|
else
|
|
{
|
|
PreviewVC->SetVisibility(EVisibility::Visible);
|
|
PreviewUIViewport->SetVisibility(EVisibility::Collapsed);
|
|
}
|
|
}
|
|
|
|
void FMaterialInstanceEditor::RegisterToolBar()
|
|
{
|
|
FName MenuName = FAssetEditorToolkit::GetToolMenuToolbarName();
|
|
if (!UToolMenus::Get()->IsMenuRegistered(MenuName))
|
|
{
|
|
UToolMenu* ToolBar = UToolMenus::Get()->RegisterMenu(MenuName, "AssetEditor.DefaultToolBar", EMultiBoxType::ToolBar);
|
|
|
|
FToolMenuInsert InsertAfterAssetSection("Asset", EToolMenuInsertType::After);
|
|
{
|
|
FToolMenuSection& MaterialInstanceSection = ToolBar->AddSection("MaterialInstanceTools", TAttribute<FText>(), InsertAfterAssetSection);
|
|
|
|
MaterialInstanceSection.AddEntry(FToolMenuEntry::InitToolBarButton(FMaterialEditorCommands::Get().Apply));
|
|
MaterialInstanceSection.AddEntry(FToolMenuEntry::InitToolBarButton(FMaterialEditorCommands::Get().ShowAllMaterialParameters));
|
|
MaterialInstanceSection.AddEntry(FToolMenuEntry::InitComboButton(
|
|
"Hierarchy",
|
|
FToolUIActionChoice(),
|
|
FNewToolMenuDelegate::CreateLambda([](UToolMenu* InSubMenu)
|
|
{
|
|
UMaterialEditorMenuContext* SubMenuContext = InSubMenu->FindContext<UMaterialEditorMenuContext>();
|
|
if (SubMenuContext && SubMenuContext->MaterialEditor.IsValid())
|
|
{
|
|
SubMenuContext->MaterialEditor.Pin()->GenerateInheritanceMenu(InSubMenu);
|
|
}
|
|
}),
|
|
LOCTEXT("Hierarchy", "Hierarchy"),
|
|
FText::GetEmpty(),
|
|
FSlateIcon(FAppStyle::Get().GetStyleSetName(), "MaterialEditor.Hierarchy"),
|
|
false
|
|
));
|
|
}
|
|
|
|
FToolMenuSection& UISection = ToolBar->AddSection("Stats", TAttribute<FText>(), InsertAfterAssetSection);
|
|
UISection.AddEntry(FToolMenuEntry::InitToolBarButton(FMaterialEditorCommands::Get().TogglePlatformStats));
|
|
}
|
|
}
|
|
|
|
void FMaterialInstanceEditor::InitToolMenuContext(FToolMenuContext& MenuContext)
|
|
{
|
|
FAssetEditorToolkit::InitToolMenuContext(MenuContext);
|
|
|
|
UMaterialEditorMenuContext* Context = NewObject<UMaterialEditorMenuContext>();
|
|
Context->MaterialEditor = SharedThis(this);
|
|
MenuContext.AddObject(Context);
|
|
}
|
|
|
|
void FMaterialInstanceEditor::ExtendToolbar()
|
|
{
|
|
AddToolbarExtender(GetToolBarExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects()));
|
|
|
|
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>( "MaterialEditor" );
|
|
AddToolbarExtender(MaterialEditorModule->GetToolBarExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects()));
|
|
}
|
|
|
|
void FMaterialInstanceEditor::GenerateInheritanceMenu(UToolMenu* Menu)
|
|
{
|
|
RebuildInheritanceList();
|
|
Menu->bShouldCloseWindowAfterMenuSelection = true;
|
|
Menu->SetMaxHeight(500);
|
|
|
|
{
|
|
FToolMenuSection& Section = Menu->AddSection("ParentChain", LOCTEXT("ParentChain", "Parent Chain"));
|
|
if (bIsFunctionPreviewMaterial)
|
|
{
|
|
if (FunctionParentList.Num() == 0)
|
|
{
|
|
const FText NoParentText = LOCTEXT("NoParentFound", "No Parent Found");
|
|
TSharedRef<SWidget> NoParentWidget = SNew(STextBlock)
|
|
.Text(NoParentText);
|
|
Section.AddEntry(FToolMenuEntry::InitWidget("NoParentEntry", NoParentWidget, FText::GetEmpty()));
|
|
}
|
|
for (FAssetData FunctionParent : FunctionParentList)
|
|
{
|
|
FMaterialEditor::AddInheritanceMenuEntry(Section, FunctionParent, bIsFunctionPreviewMaterial);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (MaterialParentList.Num() == 0)
|
|
{
|
|
const FText NoParentText = LOCTEXT("NoParentFound", "No Parent Found");
|
|
TSharedRef<SWidget> NoParentWidget = SNew(STextBlock)
|
|
.Text(NoParentText);
|
|
Section.AddEntry(FToolMenuEntry::InitWidget("NoParentEntry", NoParentWidget, FText::GetEmpty()));
|
|
}
|
|
for (FAssetData MaterialParent : MaterialParentList)
|
|
{
|
|
FMaterialEditor::AddInheritanceMenuEntry(Section, MaterialParent, bIsFunctionPreviewMaterial);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bIsFunctionPreviewMaterial)
|
|
{
|
|
FToolMenuSection& Section = Menu->AddSection("MaterialInstances", LOCTEXT("MaterialInstances", "Material Instances"));
|
|
for (const FAssetData& MaterialChild : MaterialChildList)
|
|
{
|
|
FMaterialEditor::AddInheritanceMenuEntry(Section, MaterialChild, bIsFunctionPreviewMaterial);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FMaterialInstanceEditor::RefreshPreviewViewport()
|
|
{
|
|
if (PreviewVC.IsValid())
|
|
{
|
|
PreviewVC->RefreshViewport();
|
|
}
|
|
}
|
|
|
|
TSharedRef<SDockTab> FMaterialInstanceEditor::SpawnTab_Preview( const FSpawnTabArgs& Args )
|
|
{
|
|
check( Args.GetTabId().TabType == FMaterialInstanceEditorTabs::PreviewTabId );
|
|
|
|
TSharedRef<SDockTab> SpawnedTab = SNew(SDockTab)
|
|
.Label(LOCTEXT("ViewportTabTitle", "Viewport"))
|
|
[
|
|
SNew( SOverlay )
|
|
+ SOverlay::Slot()
|
|
[
|
|
PreviewVC.ToSharedRef()
|
|
]
|
|
+ SOverlay::Slot()
|
|
[
|
|
PreviewUIViewport.ToSharedRef()
|
|
]
|
|
];
|
|
|
|
PreviewVC->OnAddedToTab( SpawnedTab );
|
|
|
|
AddToSpawnedToolPanels( Args.GetTabId().TabType, SpawnedTab );
|
|
return SpawnedTab;
|
|
}
|
|
|
|
|
|
TSharedRef<SDockTab> FMaterialInstanceEditor::SpawnTab_Properties( const FSpawnTabArgs& Args )
|
|
{
|
|
check( Args.GetTabId().TabType == FMaterialInstanceEditorTabs::PropertiesTabId );
|
|
|
|
TSharedRef<SDockTab> SpawnedTab = SNew(SDockTab)
|
|
.Label(LOCTEXT("MaterialPropertiesTitle", "Details"))
|
|
[
|
|
SNew(SBorder)
|
|
.Padding(4)
|
|
[
|
|
MaterialInstanceDetails.ToSharedRef()
|
|
]
|
|
];
|
|
|
|
UpdatePropertyWindow();
|
|
|
|
AddToSpawnedToolPanels( Args.GetTabId().TabType, SpawnedTab );
|
|
return SpawnedTab;
|
|
}
|
|
|
|
TSharedRef<SDockTab> FMaterialInstanceEditor::SpawnTab_LayerProperties(const FSpawnTabArgs& Args)
|
|
{
|
|
check(Args.GetTabId().TabType == FMaterialInstanceEditorTabs::LayerPropertiesTabId);
|
|
|
|
if(Substrate::IsMaterialLayeringSupportEnabled())
|
|
{
|
|
TSharedRef<SDockTab> SpawnedTab = SNew(SDockTab)
|
|
.Label(LOCTEXT("MaterialLayerPropertiesTitle", "Layer Parameters"))
|
|
[
|
|
MaterialLayersFunctionsInstance.ToSharedRef()
|
|
];
|
|
|
|
AddToSpawnedToolPanels(Args.GetTabId().TabType, SpawnedTab);
|
|
return SpawnedTab;
|
|
}
|
|
else
|
|
{
|
|
TSharedRef<SDockTab> SpawnedTab = SNew(SDockTab)
|
|
.Label(LOCTEXT("MaterialLayerPropertiesTitle", "Layer Parameters"))
|
|
[
|
|
SNew(SBorder)
|
|
.Padding(4)
|
|
[
|
|
MaterialLayersFunctionsInstance.ToSharedRef()
|
|
]
|
|
];
|
|
|
|
AddToSpawnedToolPanels(Args.GetTabId().TabType, SpawnedTab);
|
|
return SpawnedTab;
|
|
}
|
|
}
|
|
|
|
TSharedRef<SDockTab> FMaterialInstanceEditor::SpawnTab_PreviewSettings(const FSpawnTabArgs& Args)
|
|
{
|
|
check(Args.GetTabId() == FMaterialInstanceEditorTabs::PreviewSettingsTabId);
|
|
|
|
TSharedRef<SWidget> InWidget = SNullWidget::NullWidget;
|
|
if (PreviewVC.IsValid())
|
|
{
|
|
FAdvancedPreviewSceneModule& AdvancedPreviewSceneModule = FModuleManager::LoadModuleChecked<FAdvancedPreviewSceneModule>("AdvancedPreviewScene");
|
|
InWidget = AdvancedPreviewSceneModule.CreateAdvancedPreviewSceneSettingsWidget(PreviewVC->GetPreviewScene());
|
|
}
|
|
|
|
TSharedRef<SDockTab> SpawnedTab = SNew(SDockTab)
|
|
.Label(LOCTEXT("PreviewSceneSettingsTab", "Preview Scene Settings"))
|
|
[
|
|
SNew(SBox)
|
|
[
|
|
InWidget
|
|
]
|
|
];
|
|
|
|
return SpawnedTab;
|
|
}
|
|
|
|
TSharedRef<SDockTab> FMaterialInstanceEditor::SpawnTab_AssetBrowser(const FSpawnTabArgs& Args)
|
|
{
|
|
check(Args.GetTabId() == FMaterialInstanceEditorTabs::AssetBrowserTabId);
|
|
|
|
// TSharedRef<SWidget> InWidget = SNullWidget::NullWidget;
|
|
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
|
|
|
|
// Configure filter for asset picker
|
|
FAssetPickerConfig Config;
|
|
Config.SelectionMode = ESelectionMode::Single;
|
|
Config.Filter.ClassPaths.Add(UMaterialFunctionMaterialLayer::StaticClass()->GetClassPathName());
|
|
Config.Filter.ClassPaths.Add(UMaterialFunctionMaterialLayerInstance::StaticClass()->GetClassPathName());
|
|
Config.Filter.ClassPaths.Add(UMaterialFunctionMaterialLayerBlend::StaticClass()->GetClassPathName());
|
|
Config.Filter.ClassPaths.Add(UMaterialFunctionMaterialLayerBlendInstance::StaticClass()->GetClassPathName());
|
|
Config.bAddFilterUI = true;
|
|
Config.ThumbnailScale = 0.4f;
|
|
Config.InitialThumbnailSize = EThumbnailSize::Small;
|
|
Config.InitialAssetViewType = EAssetViewType::Tile;
|
|
Config.OnAssetDoubleClicked = FOnAssetDoubleClicked::CreateSP(this, &FMaterialInstanceEditor::OnAssetDoubleClicked);
|
|
Config.OnGetAssetContextMenu = FOnGetAssetContextMenu::CreateSP(this, &FMaterialInstanceEditor::OnGetAssetContextMenu);
|
|
Config.bForceShowEngineContent = true;
|
|
TSharedRef<SDockTab> SpawnedTab = SNew(SDockTab)
|
|
.Label(LOCTEXT("AssetBrowserTab", "Asset Browser"))
|
|
[
|
|
SNew(SBox)
|
|
[
|
|
ContentBrowserModule.Get().CreateAssetPicker(Config)
|
|
]
|
|
];
|
|
|
|
return SpawnedTab;
|
|
}
|
|
TSharedRef<SDockTab> FMaterialInstanceEditor::SpawnTab_Substrate(const FSpawnTabArgs& Args)
|
|
{
|
|
check(Args.GetTabId() == FMaterialInstanceEditorTabs::SubstrateTabId);
|
|
|
|
TSharedRef<SDockTab> SubstrateTab = SNew(SDockTab)
|
|
.Label(LOCTEXT("MaterialSubstrateTabTitle", "Substrate"))
|
|
[
|
|
SNew(SBox)
|
|
[
|
|
SubstrateWidget.ToSharedRef()
|
|
]
|
|
];
|
|
|
|
return SubstrateTab;
|
|
}
|
|
TSharedPtr<SWidget> FMaterialInstanceEditor::OnGetAssetContextMenu(const TArray<FAssetData>& SelectedAssets) const
|
|
{
|
|
if (SelectedAssets.Num() <= 0)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
UObject* SelectedAsset = SelectedAssets[0].GetAsset();
|
|
if (SelectedAsset == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
FMenuBuilder MenuBuilder(true, MakeShared<FUICommandList>());
|
|
|
|
MenuBuilder.BeginSection(TEXT("Asset"), LOCTEXT("AssetSectionLabel", "Asset"));
|
|
{
|
|
MenuBuilder.AddMenuEntry(
|
|
LOCTEXT("Browse", "Browse to Asset"),
|
|
LOCTEXT("BrowseTooltip", "Browses to the associated asset and selects it in the most recently used Content Browser (summoning one if necessary)"),
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "SystemWideCommands.FindInContentBrowser.Small"),
|
|
FUIAction(
|
|
FExecuteAction::CreateLambda([SelectedAsset] ()
|
|
{
|
|
if (SelectedAsset)
|
|
{
|
|
const TArray<FAssetData>& Assets = { SelectedAsset };
|
|
const FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
|
|
ContentBrowserModule.Get().SyncBrowserToAssets(Assets);
|
|
}
|
|
}),
|
|
FCanExecuteAction::CreateLambda([] () { return true; })
|
|
)
|
|
);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::OnAssetDoubleClicked(const FAssetData& AssetData)
|
|
{
|
|
if (UAssetEditorSubsystem* EditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>())
|
|
{
|
|
EditorSubsystem->OpenEditorForAsset(AssetData.ToSoftObjectPath());
|
|
}
|
|
}
|
|
void FMaterialInstanceEditor::AddToSpawnedToolPanels(const FName& TabIdentifier, const TSharedRef<SDockTab>& SpawnedTab)
|
|
{
|
|
TWeakPtr<SDockTab>* TabSpot = SpawnedToolPanels.Find(TabIdentifier);
|
|
if (!TabSpot)
|
|
{
|
|
SpawnedToolPanels.Add(TabIdentifier, SpawnedTab);
|
|
}
|
|
else
|
|
{
|
|
check(!TabSpot->IsValid());
|
|
*TabSpot = SpawnedTab;
|
|
}
|
|
}
|
|
|
|
FName FMaterialInstanceEditor::GetToolkitFName() const
|
|
{
|
|
return FName("MaterialInstanceEditor");
|
|
}
|
|
|
|
FText FMaterialInstanceEditor::GetBaseToolkitName() const
|
|
{
|
|
return LOCTEXT("AppLabel", "Material Instance Editor");
|
|
}
|
|
|
|
FText FMaterialInstanceEditor::GetToolkitName() const
|
|
{
|
|
const UObject* EditingObject = GetEditingObjects()[0];
|
|
check(EditingObject);
|
|
|
|
return GetLabelForObject(EditingObject);
|
|
}
|
|
|
|
FText FMaterialInstanceEditor::GetToolkitToolTipText() const
|
|
{
|
|
const UObject* EditingObject = GetEditingObjects()[0];
|
|
|
|
// Overridden to accommodate editing of multiple objects (original and preview materials)
|
|
return FAssetEditorToolkit::GetToolTipTextForObject(EditingObject);
|
|
}
|
|
|
|
FString FMaterialInstanceEditor::GetWorldCentricTabPrefix() const
|
|
{
|
|
return LOCTEXT("WorldCentricTabPrefix", "Material Instance ").ToString();
|
|
}
|
|
|
|
FLinearColor FMaterialInstanceEditor::GetWorldCentricTabColorScale() const
|
|
{
|
|
return FLinearColor( 0.3f, 0.2f, 0.5f, 0.5f );
|
|
}
|
|
|
|
UMaterialInterface* FMaterialInstanceEditor::GetMaterialInterface() const
|
|
{
|
|
return MaterialEditorInstance->SourceInstance;
|
|
}
|
|
|
|
void FMaterialInstanceEditor::NotifyPreChange(FProperty* PropertyThatChanged)
|
|
{
|
|
}
|
|
|
|
void FMaterialInstanceEditor::NotifyPostChange( const FPropertyChangedEvent& PropertyChangedEvent, FProperty* PropertyThatChanged)
|
|
{
|
|
// If they changed the parent, regenerate the parent list.
|
|
if(PropertyThatChanged->GetName()==TEXT("Parent"))
|
|
{
|
|
bool bSetEmptyParent = false;
|
|
|
|
// Check to make sure they didnt set the parent to themselves.
|
|
if(MaterialEditorInstance->Parent==MaterialEditorInstance->SourceInstance)
|
|
{
|
|
bSetEmptyParent = true;
|
|
}
|
|
|
|
if (bSetEmptyParent)
|
|
{
|
|
FMaterialUpdateContext UpdateContext;
|
|
MaterialEditorInstance->Parent = NULL;
|
|
|
|
if(MaterialEditorInstance->SourceInstance)
|
|
{
|
|
MaterialEditorInstance->SourceInstance->SetParentEditorOnly(NULL);
|
|
MaterialEditorInstance->SourceInstance->PostEditChange();
|
|
}
|
|
UpdateContext.AddMaterialInstance(MaterialEditorInstance->SourceInstance);
|
|
}
|
|
|
|
RebuildInheritanceList();
|
|
|
|
UpdatePropertyWindow();
|
|
|
|
// If the parent of this instance changed we need to update the cached state on the stats manager to have the updated parent.
|
|
MaterialStatsManager->SetMaterial(MaterialEditorInstance->SourceInstance);
|
|
MaterialStatsManager->Update();
|
|
}
|
|
else if(PropertyThatChanged->GetName() == TEXT("PreviewMesh"))
|
|
{
|
|
RefreshPreviewAsset();
|
|
}
|
|
|
|
// Rebuild the property window to account for the possibility that
|
|
// the item changed was a static switch or function call parameter
|
|
UObject* PropertyClass = PropertyThatChanged->GetOwner<UObject>();
|
|
if(PropertyClass && (PropertyClass->GetName() == TEXT("DEditorStaticSwitchParameterValue") || PropertyClass->GetName() == TEXT("EditorParameterGroup"))//DEditorMaterialLayerParameters"))
|
|
&& MaterialEditorInstance->Parent && MaterialEditorInstance->SourceInstance )
|
|
{
|
|
// TODO: We need to hit this on MaterialLayerParam updates but only get notifications for their array elements changing, hence the overly generic test above
|
|
MaterialEditorInstance->VisibleExpressions.Empty();
|
|
FMaterialEditorUtilities::GetVisibleMaterialParameters(MaterialEditorInstance->Parent->GetMaterial(), MaterialEditorInstance->SourceInstance, MaterialEditorInstance->VisibleExpressions);
|
|
|
|
UpdatePropertyWindow();
|
|
}
|
|
|
|
RefreshOnScreenMessages();
|
|
|
|
// something was changed in the material so we need to reflect this in the stats
|
|
MaterialStatsManager->SignalMaterialChanged();
|
|
|
|
// Update the preview window when the user changes a property.
|
|
PreviewVC->RefreshViewport();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::RefreshPreviewAsset()
|
|
{
|
|
UObject* PreviewAsset = MaterialEditorInstance->SourceInstance->PreviewMesh.TryLoad();
|
|
if (!PreviewAsset)
|
|
{
|
|
// Attempt to use the parent material's preview mesh if the instance's preview mesh is invalid, and use a default
|
|
// sphere instead if the parent's mesh is also invalid
|
|
UMaterialInterface* ParentMaterial = MaterialEditorInstance->SourceInstance->Parent;
|
|
|
|
UObject* ParentPreview = ParentMaterial != nullptr ? ParentMaterial->PreviewMesh.TryLoad() : nullptr;
|
|
PreviewAsset = ParentPreview != nullptr ? ParentPreview : ToRawPtr(GUnrealEd->GetThumbnailManager()->EditorSphere);
|
|
|
|
USceneThumbnailInfoWithPrimitive* ThumbnailInfo = Cast<USceneThumbnailInfoWithPrimitive>(MaterialEditorInstance->SourceInstance->ThumbnailInfo);
|
|
if (ThumbnailInfo)
|
|
{
|
|
ThumbnailInfo->PreviewMesh.Reset();
|
|
}
|
|
|
|
}
|
|
PreviewVC->SetPreviewAsset(PreviewAsset);
|
|
}
|
|
|
|
void FMaterialInstanceEditor::PreSavePackage(UPackage* Package, FObjectPreSaveContext ObjectSaveContext)
|
|
{
|
|
// The streaming data will be null if there were any edits
|
|
if (MaterialEditorInstance &&
|
|
MaterialEditorInstance->SourceInstance &&
|
|
MaterialEditorInstance->SourceInstance->GetOutermost() == Package &&
|
|
!MaterialEditorInstance->SourceInstance->HasTextureStreamingData())
|
|
{
|
|
FMaterialEditorUtilities::BuildTextureStreamingData(MaterialEditorInstance->SourceInstance);
|
|
}
|
|
}
|
|
|
|
void FMaterialInstanceEditor::RebuildInheritanceList()
|
|
{
|
|
if (bIsFunctionPreviewMaterial)
|
|
{
|
|
FunctionParentList.Empty();
|
|
|
|
// Append function instance parent chain
|
|
UMaterialFunctionInstance* Current = MaterialFunctionOriginal;
|
|
UMaterialFunctionInterface* Parent = Current->Parent;
|
|
while (Parent)
|
|
{
|
|
FunctionParentList.Insert(Parent, 0);
|
|
|
|
Current = Cast<UMaterialFunctionInstance>(Parent);
|
|
Parent = Current ? ToRawPtr(Current->Parent) : nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MaterialChildList.Empty();
|
|
MaterialParentList.Empty();
|
|
|
|
// Travel up the parent chain for this material instance until we reach the root material.
|
|
UMaterialInstance* InstanceConstant = MaterialEditorInstance->SourceInstance;
|
|
|
|
if(InstanceConstant)
|
|
{
|
|
UMaterialEditingLibrary::GetChildInstances(InstanceConstant, MaterialChildList);
|
|
|
|
// Add all parents
|
|
UMaterialInterface* Parent = InstanceConstant->Parent;
|
|
while(Parent && Parent != InstanceConstant)
|
|
{
|
|
MaterialParentList.Insert(Parent,0);
|
|
|
|
// If the parent is a material then break.
|
|
InstanceConstant = Cast<UMaterialInstance>(Parent);
|
|
|
|
if(InstanceConstant)
|
|
{
|
|
Parent = InstanceConstant->Parent;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FMaterialInstanceEditor::RebuildMaterialInstanceEditor()
|
|
{
|
|
if( MaterialEditorInstance )
|
|
{
|
|
ReInitMaterialFunctionProxies();
|
|
MaterialEditorInstance->CopyBasePropertiesFromParent();
|
|
MaterialEditorInstance->RegenerateArrays();
|
|
RebuildInheritanceList(); // Required b/c recompiled parent materials result in invalid weak object pointers
|
|
UpdatePropertyWindow();
|
|
}
|
|
}
|
|
|
|
void FMaterialInstanceEditor::DrawMessages( FViewport* Viewport, FCanvas* Canvas )
|
|
{
|
|
Canvas->PushAbsoluteTransform(FMatrix::Identity);
|
|
if ( MaterialEditorInstance->Parent && MaterialEditorInstance->SourceInstance && GAreScreenMessagesEnabled )
|
|
{
|
|
const FMaterialResource* MaterialResource = MaterialEditorInstance->SourceInstance->GetMaterialResource(GMaxRHIFeatureLevel);
|
|
UMaterial* BaseMaterial = MaterialEditorInstance->SourceInstance->GetMaterial();
|
|
int32 DrawPositionY = 50;
|
|
if ( BaseMaterial && MaterialResource )
|
|
{
|
|
constexpr int32 DrawPositionX = 5;
|
|
|
|
ERHIFeatureLevel::Type FeatureLevel = MaterialResource->GetFeatureLevel();
|
|
FString FeatureLevelName;
|
|
GetFeatureLevelName(FeatureLevel,FeatureLevelName);
|
|
|
|
const TArray<FString>& CompileErrors = MaterialResource->GetCompileErrors();
|
|
for(int32 ErrorIndex = 0; ErrorIndex < CompileErrors.Num(); ErrorIndex++)
|
|
{
|
|
Canvas->DrawShadowedString(DrawPositionX, DrawPositionY, *FString::Printf(TEXT("[%s] %s"), *FeatureLevelName, *CompileErrors[ErrorIndex]), GEngine->GetTinyFont(), FLinearColor(1, 0, 0));
|
|
DrawPositionY += SpacingBetweenLines;
|
|
}
|
|
}
|
|
|
|
DrawOnScreenMessages( Canvas, DrawPositionY );
|
|
}
|
|
Canvas->PopTransform();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::RefreshOnScreenMessages()
|
|
{
|
|
OnScreenMessages.Reset();
|
|
|
|
if (MaterialEditorInstance->SourceInstance)
|
|
{
|
|
UMaterial* BaseMaterial = MaterialEditorInstance->SourceInstance->GetMaterial();
|
|
if (BaseMaterial)
|
|
{
|
|
UEnum* SamplerTypeEnum = StaticEnum<EMaterialSamplerType>();
|
|
check(SamplerTypeEnum);
|
|
UEnum* MaterialTypeEnum = StaticEnum<ERuntimeVirtualTextureMaterialType>();
|
|
check(MaterialTypeEnum);
|
|
|
|
const int32 GroupCount = MaterialEditorInstance->ParameterGroups.Num();
|
|
for (int32 GroupIndex = 0; GroupIndex < GroupCount; ++GroupIndex)
|
|
{
|
|
const FEditorParameterGroup& Group = MaterialEditorInstance->ParameterGroups[GroupIndex];
|
|
const int32 ParameterCount = Group.Parameters.Num();
|
|
for (int32 ParameterIndex = 0; ParameterIndex < ParameterCount; ++ParameterIndex)
|
|
{
|
|
UDEditorTextureParameterValue* TextureParameterValue = Cast<UDEditorTextureParameterValue>(Group.Parameters[ParameterIndex]);
|
|
if (TextureParameterValue && TextureParameterValue->ExpressionId.IsValid())
|
|
{
|
|
UTexture* Texture = NULL;
|
|
MaterialEditorInstance->SourceInstance->GetTextureParameterValue(TextureParameterValue->ParameterInfo, Texture);
|
|
if (Texture)
|
|
{
|
|
EMaterialSamplerType SamplerType = UMaterialExpressionTextureBase::GetSamplerTypeForTexture(Texture);
|
|
UMaterialExpressionTextureSampleParameter* Expression = BaseMaterial->FindExpressionByGUID<UMaterialExpressionTextureSampleParameter>(TextureParameterValue->ExpressionId);
|
|
|
|
FString ErrorMessage;
|
|
if (Expression && !Expression->TextureIsValid(Texture, ErrorMessage))
|
|
{
|
|
OnScreenMessages.Emplace(FLinearColor(1, 0, 0),
|
|
FString::Printf(TEXT("Error: %s has invalid texture %s: %s."),
|
|
*TextureParameterValue->ParameterInfo.Name.ToString(),
|
|
*Texture->GetPathName(),
|
|
*ErrorMessage));
|
|
}
|
|
else
|
|
{
|
|
if (Expression && Expression->SamplerType != SamplerType)
|
|
{
|
|
FString SamplerTypeDisplayName = SamplerTypeEnum->GetDisplayNameTextByValue(Expression->SamplerType).ToString();
|
|
OnScreenMessages.Emplace(FLinearColor(1, 1, 0),
|
|
FString::Printf(TEXT("Warning: %s samples %s as %s."),
|
|
*TextureParameterValue->ParameterInfo.Name.ToString(),
|
|
*Texture->GetPathName(),
|
|
*SamplerTypeDisplayName));
|
|
}
|
|
if (Expression && ((Expression->SamplerType == (EMaterialSamplerType)TC_Normalmap || Expression->SamplerType == (EMaterialSamplerType)TC_Masks) && Texture->SRGB))
|
|
{
|
|
FString SamplerTypeDisplayName = SamplerTypeEnum->GetDisplayNameTextByValue(Expression->SamplerType).ToString();
|
|
OnScreenMessages.Emplace(FLinearColor(1, 1, 0),
|
|
FString::Printf(TEXT("Warning: %s samples texture as '%s'. SRGB should be disabled for '%s'."),
|
|
*TextureParameterValue->ParameterInfo.Name.ToString(),
|
|
*SamplerTypeDisplayName,
|
|
*Texture->GetPathName()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UDEditorRuntimeVirtualTextureParameterValue* RuntimeVirtualTextureParameterValue = Cast<UDEditorRuntimeVirtualTextureParameterValue>(Group.Parameters[ParameterIndex]);
|
|
if (RuntimeVirtualTextureParameterValue && RuntimeVirtualTextureParameterValue->ExpressionId.IsValid())
|
|
{
|
|
URuntimeVirtualTexture* RuntimeVirtualTexture = NULL;
|
|
MaterialEditorInstance->SourceInstance->GetRuntimeVirtualTextureParameterValue(RuntimeVirtualTextureParameterValue->ParameterInfo, RuntimeVirtualTexture);
|
|
if (RuntimeVirtualTexture)
|
|
{
|
|
UMaterialExpressionRuntimeVirtualTextureSampleParameter* Expression = BaseMaterial->FindExpressionByGUID<UMaterialExpressionRuntimeVirtualTextureSampleParameter>(RuntimeVirtualTextureParameterValue->ExpressionId);
|
|
if (!Expression)
|
|
{
|
|
const FText ExpressionNameText = FText::Format(LOCTEXT("MissingRVTExpression", "Warning: Runtime Virtual Texture Expression {0} not found."), FText::FromName(RuntimeVirtualTextureParameterValue->ParameterInfo.Name));
|
|
OnScreenMessages.Emplace(FLinearColor(1, 1, 0), ExpressionNameText.ToString());
|
|
}
|
|
if (Expression && Expression->MaterialType != RuntimeVirtualTexture->GetMaterialType())
|
|
{
|
|
FString BaseMaterialTypeDisplayName = MaterialTypeEnum->GetDisplayNameTextByValue((int64)(Expression->MaterialType)).ToString();
|
|
FString OverrideMaterialTypeDisplayName = MaterialTypeEnum->GetDisplayNameTextByValue((int64)(RuntimeVirtualTexture->GetMaterialType())).ToString();
|
|
|
|
OnScreenMessages.Emplace(FLinearColor(1, 1, 0),
|
|
FText::Format(LOCTEXT("MismatchedRVTType", "Warning: '{0}' interprets the virtual texture as '{1}' not '{2}', {3}"),
|
|
FText::FromName(RuntimeVirtualTextureParameterValue->ParameterInfo.Name),
|
|
FText::FromString(BaseMaterialTypeDisplayName),
|
|
FText::FromString(OverrideMaterialTypeDisplayName),
|
|
FText::FromString(RuntimeVirtualTexture->GetPathName())).ToString());
|
|
}
|
|
if (Expression && Expression->bSinglePhysicalSpace != RuntimeVirtualTexture->GetSinglePhysicalSpace())
|
|
{
|
|
OnScreenMessages.Emplace(FLinearColor(1, 1, 0),
|
|
FText::Format(LOCTEXT("VirtualTexturePagePackingWarning", "Warning: '{0}' interprets the virtual texture page table packing as {1} not {2}, {3}"),
|
|
FText::FromName(RuntimeVirtualTextureParameterValue->ParameterInfo.Name),
|
|
FText::FromString(RuntimeVirtualTexture->GetSinglePhysicalSpace() ? TEXT("true") : TEXT("false")),
|
|
FText::FromString(Expression->bSinglePhysicalSpace ? TEXT("true") : TEXT("false")),
|
|
FText::FromString(RuntimeVirtualTexture->GetPathName())).ToString());
|
|
}
|
|
if (Expression && Expression->bAdaptive != RuntimeVirtualTexture->GetAdaptivePageTable())
|
|
{
|
|
OnScreenMessages.Emplace(FLinearColor(1, 1, 0),
|
|
FText::Format(LOCTEXT("VirtualTextureAdaptiveWarning", "Warning: '{0}' interprets the adaptive page table setting as {1} not {2}, {3}"),
|
|
FText::FromName(RuntimeVirtualTextureParameterValue->ParameterInfo.Name),
|
|
FText::FromString(RuntimeVirtualTexture->GetAdaptivePageTable() ? TEXT("true") : TEXT("false")),
|
|
FText::FromString(Expression->bAdaptive ? TEXT("true") : TEXT("false")),
|
|
FText::FromString(RuntimeVirtualTexture->GetPathName())).ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
UDEditorSparseVolumeTextureParameterValue * SparseVolumeTextureParameterValue = Cast<UDEditorSparseVolumeTextureParameterValue>(Group.Parameters[ParameterIndex]);
|
|
if (SparseVolumeTextureParameterValue && SparseVolumeTextureParameterValue->ExpressionId.IsValid())
|
|
{
|
|
USparseVolumeTexture* SparseVolumeTexture = NULL;
|
|
MaterialEditorInstance->SourceInstance->GetSparseVolumeTextureParameterValue(SparseVolumeTextureParameterValue->ParameterInfo, SparseVolumeTexture);
|
|
if (SparseVolumeTexture)
|
|
{
|
|
UMaterialExpressionSparseVolumeTextureSampleParameter* Expression = BaseMaterial->FindExpressionByGUID<UMaterialExpressionSparseVolumeTextureSampleParameter>(SparseVolumeTextureParameterValue->ExpressionId);
|
|
if (!Expression)
|
|
{
|
|
const FText ExpressionNameText = FText::Format(LOCTEXT("MissingSVTExpression", "Warning: Sparse Volume Texture Expression {0} not found."), FText::FromName(SparseVolumeTextureParameterValue->ParameterInfo.Name));
|
|
OnScreenMessages.Emplace(FLinearColor(1, 1, 0), ExpressionNameText.ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws sampler/texture mismatch warning strings.
|
|
* @param Canvas - The canvas on which to draw.
|
|
* @param DrawPositionY - The Y position at which to draw. Upon return contains the Y value following the last line of text drawn.
|
|
*/
|
|
void FMaterialInstanceEditor::DrawOnScreenMessages(FCanvas* Canvas, int32& DrawPositionY)
|
|
{
|
|
UFont* FontToUse = GEngine->GetTinyFont();
|
|
for (const FOnScreenMessage& Message : OnScreenMessages)
|
|
{
|
|
Canvas->DrawShadowedString(
|
|
5,
|
|
DrawPositionY,
|
|
*Message.Message,
|
|
FontToUse,
|
|
Message.Color);
|
|
DrawPositionY += SpacingBetweenLines;
|
|
}
|
|
}
|
|
|
|
bool FMaterialInstanceEditor::SetPreviewAsset(UObject* InAsset)
|
|
{
|
|
if (PreviewVC.IsValid())
|
|
{
|
|
return PreviewVC->SetPreviewAsset(InAsset);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FMaterialInstanceEditor::SetPreviewAssetByName(const TCHAR* InAssetName)
|
|
{
|
|
if (PreviewVC.IsValid())
|
|
{
|
|
return PreviewVC->SetPreviewAssetByName(InAssetName);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FMaterialInstanceEditor::SetPreviewMaterial(UMaterialInterface* InMaterialInterface)
|
|
{
|
|
if (PreviewVC.IsValid())
|
|
{
|
|
PreviewVC->SetPreviewMaterial(InMaterialInterface);
|
|
}
|
|
}
|
|
|
|
void FMaterialInstanceEditor::GetShowHiddenParameters(bool& bShowHiddenParameters)
|
|
{
|
|
bShowHiddenParameters = bShowAllMaterialParameters;
|
|
}
|
|
|
|
void FMaterialInstanceEditor::Tick(float DeltaTime)
|
|
{
|
|
MaterialStatsManager->SetMaterial(MaterialEditorInstance->SourceInstance);
|
|
MaterialStatsManager->Update();
|
|
}
|
|
|
|
TStatId FMaterialInstanceEditor::GetStatId() const
|
|
{
|
|
RETURN_QUICK_DECLARE_CYCLE_STAT(FMaterialInstanceEditor, STATGROUP_Tickables);
|
|
}
|
|
|
|
void FMaterialInstanceEditor::SaveAsset_Execute()
|
|
{
|
|
if (bIsFunctionPreviewMaterial && MaterialEditorInstance)
|
|
{
|
|
UE_LOG(LogMaterialInstanceEditor, Log, TEXT("Saving and applying instance %s"), *GetEditingObjects()[0]->GetName());
|
|
MaterialEditorInstance->ApplySourceFunctionChanges();
|
|
}
|
|
|
|
IMaterialEditor::SaveAsset_Execute();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::SaveAssetAs_Execute()
|
|
{
|
|
if (bIsFunctionPreviewMaterial && MaterialEditorInstance)
|
|
{
|
|
UE_LOG(LogMaterialInstanceEditor, Log, TEXT("Saving and applying instance %s"), *GetEditingObjects()[0]->GetName());
|
|
MaterialEditorInstance->ApplySourceFunctionChanges();
|
|
}
|
|
|
|
IMaterialEditor::SaveAssetAs_Execute();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::SaveSettings()
|
|
{
|
|
GConfig->SetBool(TEXT("MaterialInstanceEditor"), TEXT("bDrawGrid"), PreviewVC->IsRealtime(), GEditorPerProjectIni);
|
|
GConfig->SetInt(TEXT("MaterialInstanceEditor"), TEXT("PrimType"), PreviewVC->PreviewPrimType, GEditorPerProjectIni);
|
|
}
|
|
|
|
void FMaterialInstanceEditor::LoadSettings()
|
|
{
|
|
bool bRealtime=false;
|
|
int32 PrimType=static_cast<EThumbnailPrimType>( TPT_Sphere );
|
|
GConfig->GetBool(TEXT("MaterialInstanceEditor"), TEXT("bDrawGrid"), bRealtime, GEditorPerProjectIni);
|
|
GConfig->GetInt(TEXT("MaterialInstanceEditor"), TEXT("PrimType"), PrimType, GEditorPerProjectIni);
|
|
|
|
if(PreviewVC.IsValid())
|
|
{
|
|
if ( bRealtime )
|
|
{
|
|
PreviewVC->OnToggleRealtime();
|
|
}
|
|
|
|
PreviewVC->OnSetPreviewPrimitive( static_cast<EThumbnailPrimType>(PrimType), true);
|
|
}
|
|
}
|
|
|
|
void FMaterialInstanceEditor::OpenSelectedParentEditor(UMaterialInterface* InMaterialInterface)
|
|
{
|
|
ensure(InMaterialInterface);
|
|
|
|
// See if its a material or material instance constant. Don't do anything if the user chose the current material instance.
|
|
if(InMaterialInterface && MaterialEditorInstance->SourceInstance!=InMaterialInterface)
|
|
{
|
|
if(InMaterialInterface->IsA(UMaterial::StaticClass()))
|
|
{
|
|
// Show material editor
|
|
UMaterial* Material = Cast<UMaterial>(InMaterialInterface);
|
|
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(Material);
|
|
}
|
|
else if(InMaterialInterface->IsA(UMaterialInstance::StaticClass()))
|
|
{
|
|
// Show material instance editor
|
|
UMaterialInstance* MaterialInstance = Cast<UMaterialInstance>(InMaterialInterface);
|
|
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(MaterialInstance);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FMaterialInstanceEditor::OpenSelectedParentEditor(UMaterialFunctionInterface* InMaterialFunction)
|
|
{
|
|
ensure(InMaterialFunction);
|
|
|
|
// See if its a material or material instance constant. Don't do anything if the user chose the current material instance.
|
|
if(InMaterialFunction && MaterialFunctionOriginal != InMaterialFunction)
|
|
{
|
|
if(InMaterialFunction->IsA(UMaterialFunctionInstance::StaticClass()))
|
|
{
|
|
// Show function instance editor
|
|
UMaterialFunctionInstance* FunctionInstance = Cast<UMaterialFunctionInstance>(InMaterialFunction);
|
|
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(FunctionInstance);
|
|
}
|
|
else
|
|
{
|
|
// Show function editor
|
|
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(InMaterialFunction);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FMaterialInstanceEditor::UpdatePropertyWindow()
|
|
{
|
|
TArray<UObject*> SelectedObjects;
|
|
SelectedObjects.Add( MaterialEditorInstance );
|
|
MaterialInstanceDetails->SetObjects( SelectedObjects, true );
|
|
if (MaterialLayersFunctionsInstance.IsValid())
|
|
{
|
|
MaterialLayersFunctionsInstance->SetEditorInstance(MaterialEditorInstance);
|
|
}
|
|
}
|
|
|
|
UObject* FMaterialInstanceEditor::GetSyncObject()
|
|
{
|
|
if (MaterialEditorInstance)
|
|
{
|
|
return MaterialEditorInstance->SourceInstance;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool FMaterialInstanceEditor::ApproveSetPreviewAsset(UObject* InAsset)
|
|
{
|
|
// Default impl is to always accept.
|
|
return true;
|
|
}
|
|
|
|
void FMaterialInstanceEditor::Refresh()
|
|
{
|
|
int32 TempIndex;
|
|
const bool bParentChanged = !MaterialParentList.Find( ToRawPtr(MaterialEditorInstance->Parent), TempIndex );
|
|
|
|
PreviewVC->RefreshViewport();
|
|
|
|
if( bParentChanged )
|
|
{
|
|
RebuildInheritanceList();
|
|
}
|
|
|
|
UpdatePropertyWindow();
|
|
|
|
RefreshOnScreenMessages();
|
|
|
|
if (Substrate::IsMaterialLayeringSupportEnabled() && SubstrateWidget)
|
|
{
|
|
SubstrateWidget->UpdateFromMaterial();
|
|
}
|
|
}
|
|
|
|
void FMaterialInstanceEditor::PostUndo( bool bSuccess )
|
|
{
|
|
MaterialEditorInstance->CopyToSourceInstance();
|
|
RefreshPreviewAsset();
|
|
Refresh();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::PostRedo( bool bSuccess )
|
|
{
|
|
MaterialEditorInstance->CopyToSourceInstance();
|
|
RefreshPreviewAsset();
|
|
Refresh();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::NotifyExternalMaterialChange()
|
|
{
|
|
MaterialStatsManager->SignalMaterialChanged();
|
|
Refresh();
|
|
}
|
|
|
|
void FMaterialInstanceEditor::NotifyUserSceneTextureLoadOrUnload()
|
|
{
|
|
if (PreviewVC.IsValid() && PreviewVC->PreviewMaterial)
|
|
{
|
|
UMaterialInstance* MaterialInstance = Cast<UMaterialInstance>(PreviewVC->PreviewMaterial);
|
|
if (MaterialInstance)
|
|
{
|
|
UMaterial* BaseMaterial = MaterialInstance->GetMaterial();
|
|
if (BaseMaterial && BaseMaterial->IsPostProcessMaterial())
|
|
{
|
|
FName Output = MaterialInstance->GetUserSceneTextureOutput(BaseMaterial);
|
|
|
|
// Ignore special SceneColor output name -- this just writes to SceneColor, not a transient UserSceneTexture
|
|
if (!Output.IsNone() && Output != FName("SceneColor"))
|
|
{
|
|
FMaterialEditorUtilities::RefreshPostProcessPreviewMaterials(MaterialInstance);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|