You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Also restores follow-up CLs 19973746, 19973782 FStaticParameterSet::MaterialLayers can't be deprecated, since a property with the same name is included via FStaticParameterSetRuntimeData So instead, FMaterialLayersFunctionsRuntimeData is updated with SerializeFromMismatchedTag, to allow serializing a full FMaterialLayersFunction. When this happens, the EditorOnly portion is stored in a separate heap allocation, and then transferred to FStaticParameterSet::EditorOnly::MaterialLayers #preflight 626c3405e31dbb512cef1e98 [Backout] - CL19973745 #fyi bob.tellez Original CL Desc ----------------------------------------------------------------- [Backout] - CL19964485 #fyi Ben.Ingram Original CL Desc ----------------------------------------------------------------- Add 'Optional' EditorOnly data for UMaterialInterface and UMaterialFunctionInterface These are separate UObject hierarchies that store editor-only UPROPERTIES, but can be included with cooked content, which allows full editor support. In principle, all editor-only properties could be moved over. So far, this has been limited to UMaterialExpressions, and data related to material parameters. FStaticParameterSet, FMaterialLayersParameters, and FMaterialCachedExpressionData have all been split into separate editor-only/non-editor-only classes, which allows the editor-only portion to be stored on the optional editor-only UObject. #preflight 626ab21dad56c0cbbea32dc4 #rb jason.nadro, francis.hurteau #jira FORT-463329 [CL 20043286 by Jason Nadro in ue5-main branch]
978 lines
33 KiB
C++
978 lines
33 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MaterialEditor/PreviewMaterial.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "MaterialEditor/DEditorParameterValue.h"
|
|
#include "MaterialEditor/DEditorFontParameterValue.h"
|
|
#include "MaterialEditor/DEditorMaterialLayersParameterValue.h"
|
|
#include "MaterialEditor/DEditorRuntimeVirtualTextureParameterValue.h"
|
|
#include "MaterialEditor/DEditorScalarParameterValue.h"
|
|
#include "MaterialEditor/DEditorStaticComponentMaskParameterValue.h"
|
|
#include "MaterialEditor/DEditorStaticSwitchParameterValue.h"
|
|
#include "MaterialEditor/DEditorTextureParameterValue.h"
|
|
#include "MaterialEditor/DEditorVectorParameterValue.h"
|
|
#include "MaterialEditor/DEditorDoubleVectorParameterValue.h"
|
|
#include "AI/NavigationSystemBase.h"
|
|
#include "MaterialEditor/MaterialEditorInstanceConstant.h"
|
|
#include "MaterialEditor/MaterialEditorPreviewParameters.h"
|
|
#include "MaterialEditor/MaterialEditorMeshComponent.h"
|
|
#include "MaterialEditorModule.h"
|
|
#include "Materials/MaterialInstance.h"
|
|
#include "Materials/MaterialInstanceConstant.h"
|
|
#include "Materials/MaterialFunctionInstance.h"
|
|
#include "Materials/MaterialExpressionScalarParameter.h"
|
|
#include "Materials/MaterialExpressionVectorParameter.h"
|
|
#include "Materials/MaterialExpressionTextureSampleParameter.h"
|
|
#include "Materials/MaterialExpressionRuntimeVirtualTextureSampleParameter.h"
|
|
#include "Materials/MaterialExpressionFontSampleParameter.h"
|
|
#include "Materials/MaterialExpressionMaterialAttributeLayers.h"
|
|
#include "Materials/MaterialExpressionStaticBoolParameter.h"
|
|
#include "Materials/MaterialExpressionStaticComponentMaskParameter.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "PropertyEditorDelegates.h"
|
|
#include "IDetailsView.h"
|
|
#include "MaterialEditingLibrary.h"
|
|
#include "MaterialPropertyHelpers.h"
|
|
#include "MaterialStatsCommon.h"
|
|
|
|
/**
|
|
* Class for rendering the material on the preview mesh in the Material Editor
|
|
*/
|
|
class FPreviewMaterial : public FMaterialResource
|
|
{
|
|
public:
|
|
virtual ~FPreviewMaterial()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Should the shader for this material with the given platform, shader type and vertex
|
|
* factory type combination be compiled
|
|
*
|
|
* @param Platform The platform currently being compiled for
|
|
* @param ShaderType Which shader is being compiled
|
|
* @param VertexFactory Which vertex factory is being compiled (can be NULL)
|
|
*
|
|
* @return true if the shader should be compiled
|
|
*/
|
|
virtual bool ShouldCache(EShaderPlatform Platform, const FShaderType* ShaderType, const FVertexFactoryType* VertexFactoryType) const override
|
|
{
|
|
// only generate the needed shaders (which should be very restrictive for fast recompiling during editing)
|
|
// @todo: Add a FindShaderType by fname or something
|
|
|
|
if( Material->IsUIMaterial() )
|
|
{
|
|
if (FCString::Stristr(ShaderType->GetName(), TEXT("TSlateMaterialShaderPS")) ||
|
|
FCString::Stristr(ShaderType->GetName(), TEXT("TSlateMaterialShaderVS")))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
if (Material->IsPostProcessMaterial())
|
|
{
|
|
if (FCString::Stristr(ShaderType->GetName(), TEXT("PostProcess")))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
{
|
|
bool bEditorStatsMaterial = Material->bIsMaterialEditorStatsMaterial;
|
|
|
|
// Always allow HitProxy shaders.
|
|
if (FCString::Stristr(ShaderType->GetName(), TEXT("HitProxy")))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// we only need local vertex factory for the preview static mesh
|
|
if (VertexFactoryType != FindVertexFactoryType(FName(TEXT("FLocalVertexFactory"), FNAME_Find)))
|
|
{
|
|
//cache for gpu skinned vertex factory if the material allows it
|
|
//this way we can have a preview skeletal mesh
|
|
if (bEditorStatsMaterial ||
|
|
!IsUsedWithSkeletalMesh())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
extern ENGINE_API bool IsGPUSkinCacheAvailable(EShaderPlatform Platform);
|
|
bool bSkinCache = IsGPUSkinCacheAvailable(Platform) && (VertexFactoryType == FindVertexFactoryType(FName(TEXT("FGPUSkinPassthroughVertexFactory"), FNAME_Find)));
|
|
|
|
if (
|
|
VertexFactoryType != FindVertexFactoryType(FName(TEXT("TGPUSkinVertexFactoryDefault"), FNAME_Find)) &&
|
|
VertexFactoryType != FindVertexFactoryType(FName(TEXT("TGPUSkinVertexFactoryUnlimited"), FNAME_Find)) &&
|
|
!bSkinCache
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Only allow shaders that are used in the stats.
|
|
if (bEditorStatsMaterial)
|
|
{
|
|
TMap<FName, TArray<FMaterialStatsUtils::FRepresentativeShaderInfo>> ShaderTypeNamesAndDescriptions;
|
|
FMaterialStatsUtils::GetRepresentativeShaderTypesAndDescriptions(ShaderTypeNamesAndDescriptions, this);
|
|
|
|
for (auto DescriptionPair : ShaderTypeNamesAndDescriptions)
|
|
{
|
|
auto &DescriptionArray = DescriptionPair.Value;
|
|
if (DescriptionArray.FindByPredicate([ShaderType = ShaderType](auto& Info) { return Info.ShaderName == ShaderType->GetFName(); }))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// look for any of the needed type
|
|
bool bShaderTypeMatches = false;
|
|
|
|
// For FMaterialResource::GetRepresentativeInstructionCounts
|
|
if (FCString::Stristr(ShaderType->GetName(), TEXT("MaterialCHSFNoLightMapPolicy")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("MobileDirectionalLight")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("MobileMovableDirectionalLight")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("BasePassPSTDistanceFieldShadowsAndLightMapPolicyHQ")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("Simple")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("BasePassPSFNoLightMapPolicy")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("CachedPointIndirectLightingPolicy")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("PrecomputedVolumetricLightmapLightingPolicy")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("BasePassPSFSelfShadowedTranslucencyPolicy")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
// Pick tessellation shader based on material settings
|
|
else if(FCString::Stristr(ShaderType->GetName(), TEXT("BasePassVSFNoLightMapPolicy")) ||
|
|
FCString::Stristr(ShaderType->GetName(), TEXT("BasePassHSFNoLightMapPolicy")) ||
|
|
FCString::Stristr(ShaderType->GetName(), TEXT("BasePassDSFNoLightMapPolicy")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("DepthOnly")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("ShadowDepth")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("Distortion")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("MeshDecal")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("TBasePassForForwardShading")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FDebugViewModeVS")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FVelocity")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FAnisotropy")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("RayTracingDynamicGeometryConverter")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FLumenCard")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
|
|
return bShaderTypeMatches;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Should shaders compiled for this material be saved to disk?
|
|
*/
|
|
virtual bool IsPersistent() const override { return false; }
|
|
virtual FString GetAssetName() const override { return FString::Printf(TEXT("Preview:%s"), *FMaterialResource::GetAssetName()); }
|
|
};
|
|
|
|
/** Implementation of Preview Material functions*/
|
|
UPreviewMaterial::UPreviewMaterial(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
}
|
|
|
|
FMaterialResource* UPreviewMaterial::AllocateResource()
|
|
{
|
|
return new FPreviewMaterial();
|
|
}
|
|
|
|
void UMaterialEditorPreviewParameters::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
if (PreviewMaterial && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive)
|
|
{
|
|
FProperty* PropertyThatChanged = PropertyChangedEvent.Property;
|
|
if (OriginalFunction == nullptr)
|
|
{
|
|
CopyToSourceInstance();
|
|
PreviewMaterial->PostEditChangeProperty(PropertyChangedEvent);
|
|
}
|
|
else
|
|
{
|
|
ApplySourceFunctionChanges();
|
|
if (OriginalFunction->PreviewMaterial)
|
|
{
|
|
OriginalFunction->PreviewMaterial->PostEditChangeProperty(PropertyChangedEvent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UMaterialEditorPreviewParameters::AssignParameterToGroup(UDEditorParameterValue* ParameterValue, const FName& InParameterGroupName)
|
|
{
|
|
check(ParameterValue);
|
|
|
|
FName ParameterGroupName = InParameterGroupName;
|
|
if (ParameterGroupName == TEXT("") || ParameterGroupName == TEXT("None"))
|
|
{
|
|
ParameterGroupName = TEXT("None");
|
|
}
|
|
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>("MaterialEditor");
|
|
|
|
// Material layers
|
|
UDEditorMaterialLayersParameterValue* MaterialLayerParam = Cast<UDEditorMaterialLayersParameterValue>(ParameterValue);
|
|
if (ParameterValue->ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter)
|
|
{
|
|
if (MaterialLayerParam)
|
|
{
|
|
ParameterGroupName = FMaterialPropertyHelpers::LayerParamName;
|
|
}
|
|
else
|
|
{
|
|
FString AppendedGroupName = GlobalGroupPrefix.ToString();
|
|
if (ParameterGroupName != TEXT("None"))
|
|
{
|
|
ParameterGroupName.AppendString(AppendedGroupName);
|
|
ParameterGroupName = FName(*AppendedGroupName);
|
|
}
|
|
else
|
|
{
|
|
ParameterGroupName = TEXT("Global");
|
|
}
|
|
}
|
|
}
|
|
|
|
FEditorParameterGroup& CurrentGroup = FMaterialPropertyHelpers::GetParameterGroup(PreviewMaterial, ParameterGroupName, ParameterGroups);
|
|
CurrentGroup.GroupAssociation = ParameterValue->ParameterInfo.Association;
|
|
ParameterValue->SetFlags(RF_Transactional);
|
|
CurrentGroup.Parameters.Add(ParameterValue);
|
|
}
|
|
|
|
void UMaterialEditorPreviewParameters::RegenerateArrays()
|
|
{
|
|
ParameterGroups.Empty();
|
|
if (PreviewMaterial)
|
|
{
|
|
// Only operate on base materials
|
|
UMaterial* ParentMaterial = PreviewMaterial;
|
|
|
|
// This can run before UMaterial::PostEditChangeProperty has a chance to run, so explicitly call UpdateCachedExpressionData here
|
|
PreviewMaterial->UpdateCachedExpressionData();
|
|
|
|
// Loop through all types of parameters for this material and add them to the parameter arrays.
|
|
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> ParameterValues;
|
|
for (int32 TypeIndex = 0; TypeIndex < NumMaterialParameterTypes; ++TypeIndex)
|
|
{
|
|
const EMaterialParameterType ParameterType = (EMaterialParameterType)TypeIndex;
|
|
|
|
ParentMaterial->GetAllParametersOfType(ParameterType, ParameterValues);
|
|
for (const auto& It : ParameterValues)
|
|
{
|
|
UDEditorParameterValue* Parameter = UDEditorParameterValue::Create(this, ParameterType, It.Key, It.Value);
|
|
Parameter->bOverride = true;
|
|
AssignParameterToGroup(Parameter, It.Value.Group);
|
|
}
|
|
}
|
|
|
|
// Static Material Layers
|
|
{
|
|
FMaterialLayersFunctions MaterialLayers;
|
|
if (ParentMaterial->GetMaterialLayers(MaterialLayers))
|
|
{
|
|
UDEditorMaterialLayersParameterValue* ParameterValue = NewObject<UDEditorMaterialLayersParameterValue>(this);
|
|
ParameterValue->bOverride = true;
|
|
ParameterValue->ParameterValue = MoveTemp(MaterialLayers);
|
|
AssignParameterToGroup(ParameterValue, FName());
|
|
}
|
|
}
|
|
}
|
|
// sort contents of groups
|
|
for (int32 ParameterIdx = 0; ParameterIdx < ParameterGroups.Num(); ParameterIdx++)
|
|
{
|
|
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
|
|
struct FCompareUDEditorParameterValueByParameterName
|
|
{
|
|
FORCEINLINE bool operator()(const UDEditorParameterValue& A, const UDEditorParameterValue& B) const
|
|
{
|
|
FString AName = A.ParameterInfo.Name.ToString();
|
|
FString BName = B.ParameterInfo.Name.ToString();
|
|
return A.SortPriority != B.SortPriority ? A.SortPriority < B.SortPriority : AName < BName;
|
|
}
|
|
};
|
|
ParamGroup.Parameters.Sort(FCompareUDEditorParameterValueByParameterName());
|
|
}
|
|
|
|
// sort groups itself pushing defaults to end
|
|
struct FCompareFEditorParameterGroupByName
|
|
{
|
|
FORCEINLINE bool operator()(const FEditorParameterGroup& A, const FEditorParameterGroup& B) const
|
|
{
|
|
FString AName = A.GroupName.ToString();
|
|
FString BName = B.GroupName.ToString();
|
|
if (AName == TEXT("none"))
|
|
{
|
|
return false;
|
|
}
|
|
if (BName == TEXT("none"))
|
|
{
|
|
return false;
|
|
}
|
|
return A.GroupSortPriority != B.GroupSortPriority ? A.GroupSortPriority < B.GroupSortPriority : AName < BName;
|
|
}
|
|
};
|
|
ParameterGroups.Sort(FCompareFEditorParameterGroupByName());
|
|
TArray<struct FEditorParameterGroup> ParameterDefaultGroups;
|
|
for (int32 ParameterIdx = 0; ParameterIdx < ParameterGroups.Num(); ParameterIdx++)
|
|
{
|
|
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
|
|
|
|
if (ParamGroup.GroupName == TEXT("None"))
|
|
{
|
|
ParameterDefaultGroups.Add(ParamGroup);
|
|
ParameterGroups.RemoveAt(ParameterIdx);
|
|
break;
|
|
}
|
|
}
|
|
if (ParameterDefaultGroups.Num() > 0)
|
|
{
|
|
ParameterGroups.Append(ParameterDefaultGroups);
|
|
}
|
|
|
|
}
|
|
|
|
void UMaterialEditorPreviewParameters::CopyToSourceInstance()
|
|
{
|
|
if (PreviewMaterial->IsTemplate(RF_ClassDefaultObject) == false && OriginalMaterial != nullptr)
|
|
{
|
|
OriginalMaterial->MarkPackageDirty();
|
|
// Scalar Parameters
|
|
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
|
|
{
|
|
FEditorParameterGroup & Group = ParameterGroups[GroupIdx];
|
|
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
|
|
{
|
|
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
|
|
if (Parameter)
|
|
{
|
|
FMaterialParameterMetadata EditorValue;
|
|
if (Parameter->GetValue(EditorValue))
|
|
{
|
|
PreviewMaterial->SetParameterValueEditorOnly(Parameter->ParameterInfo.Name, EditorValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FName UMaterialEditorPreviewParameters::GlobalGroupPrefix = FName("Global ");
|
|
|
|
void UMaterialEditorPreviewParameters::ApplySourceFunctionChanges()
|
|
{
|
|
if (OriginalFunction != nullptr)
|
|
{
|
|
CopyToSourceInstance();
|
|
|
|
OriginalFunction->MarkPackageDirty();
|
|
// Scalar Parameters
|
|
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
|
|
{
|
|
FEditorParameterGroup & Group = ParameterGroups[GroupIdx];
|
|
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
|
|
{
|
|
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
|
|
if (Parameter)
|
|
{
|
|
FMaterialParameterMetadata EditorValue;
|
|
if (Parameter->GetValue(EditorValue))
|
|
{
|
|
OriginalFunction->SetParameterValueEditorOnly(Parameter->ParameterInfo.Name, EditorValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
UMaterialEditingLibrary::UpdateMaterialFunction(OriginalFunction, PreviewMaterial);
|
|
}
|
|
}
|
|
|
|
|
|
#if WITH_EDITOR
|
|
void UMaterialEditorPreviewParameters::PostEditUndo()
|
|
{
|
|
Super::PostEditUndo();
|
|
}
|
|
#endif
|
|
|
|
|
|
FName UMaterialEditorInstanceConstant::GlobalGroupPrefix = FName("Global ");
|
|
|
|
UMaterialEditorInstanceConstant::UMaterialEditorInstanceConstant(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
bIsFunctionPreviewMaterial = false;
|
|
bShowOnlyOverrides = false;
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
if (SourceInstance)
|
|
{
|
|
FProperty* PropertyThatChanged = PropertyChangedEvent.Property;
|
|
bool bLayersParameterChanged = false;
|
|
|
|
FNavigationLockContext NavUpdateLock(ENavigationLockReason::MaterialUpdate);
|
|
|
|
if(PropertyThatChanged && PropertyThatChanged->GetName()==TEXT("Parent") )
|
|
{
|
|
if(bIsFunctionPreviewMaterial)
|
|
{
|
|
bIsFunctionInstanceDirty = true;
|
|
ApplySourceFunctionChanges();
|
|
}
|
|
else
|
|
{
|
|
FMaterialUpdateContext Context;
|
|
|
|
UpdateSourceInstanceParent();
|
|
|
|
Context.AddMaterialInstance(SourceInstance);
|
|
|
|
// Fully update static parameters before recreating render state for all components
|
|
SetSourceInstance(SourceInstance);
|
|
}
|
|
|
|
}
|
|
else if (!bIsFunctionPreviewMaterial)
|
|
{
|
|
// If a material layers parameter changed we need to update it on the source instance
|
|
// immediately so parameters contained within the new functions can be collected
|
|
for (FEditorParameterGroup& Group : ParameterGroups)
|
|
{
|
|
for (UDEditorParameterValue* Parameter : Group.Parameters)
|
|
{
|
|
if (UDEditorMaterialLayersParameterValue* LayersParam = Cast<UDEditorMaterialLayersParameterValue>(Parameter))
|
|
{
|
|
if (SourceInstance->SetMaterialLayers(LayersParam->ParameterValue))
|
|
{
|
|
bLayersParameterChanged = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bLayersParameterChanged)
|
|
{
|
|
RegenerateArrays();
|
|
}
|
|
}
|
|
|
|
CopyToSourceInstance(bLayersParameterChanged);
|
|
|
|
// Tell our source instance to update itself so the preview updates.
|
|
SourceInstance->PostEditChangeProperty(PropertyChangedEvent);
|
|
|
|
// Invalidate the streaming data so that it gets rebuilt.
|
|
SourceInstance->TextureStreamingData.Empty();
|
|
}
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::AssignParameterToGroup(UDEditorParameterValue* ParameterValue, const FName& InParameterGroupName)
|
|
{
|
|
check(ParameterValue);
|
|
|
|
FName ParameterGroupName = InParameterGroupName;
|
|
if (ParameterGroupName == TEXT("") || ParameterGroupName == TEXT("None"))
|
|
{
|
|
if (bUseOldStyleMICEditorGroups == true)
|
|
{
|
|
ParameterGroupName = ParameterValue->GetDefaultGroupName();
|
|
}
|
|
else
|
|
{
|
|
ParameterGroupName = TEXT("None");
|
|
}
|
|
|
|
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>("MaterialEditor");
|
|
|
|
// Material layers
|
|
if (ParameterValue->ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter)
|
|
{
|
|
FString AppendedGroupName = GlobalGroupPrefix.ToString();
|
|
if (ParameterGroupName != TEXT("None"))
|
|
{
|
|
ParameterGroupName.AppendString(AppendedGroupName);
|
|
ParameterGroupName = FName(*AppendedGroupName);
|
|
}
|
|
else
|
|
{
|
|
ParameterGroupName = TEXT("Global");
|
|
}
|
|
}
|
|
}
|
|
|
|
FEditorParameterGroup& CurrentGroup = FMaterialPropertyHelpers::GetParameterGroup(Parent->GetMaterial(), ParameterGroupName, ParameterGroups);
|
|
CurrentGroup.GroupAssociation = ParameterValue->ParameterInfo.Association;
|
|
ParameterValue->SetFlags(RF_Transactional);
|
|
CurrentGroup.Parameters.Add(ParameterValue);
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::RegenerateArrays()
|
|
{
|
|
VisibleExpressions.Empty();
|
|
ParameterGroups.Empty();
|
|
|
|
if (Parent)
|
|
{
|
|
// Only operate on base materials
|
|
UMaterial* ParentMaterial = Parent->GetMaterial();
|
|
SourceInstance->UpdateParameterNames(); // Update any parameter names that may have changed.
|
|
SourceInstance->UpdateCachedData();
|
|
|
|
// Need to get layer info first as other params are collected from layers
|
|
{
|
|
FMaterialLayersFunctions MaterialLayers;
|
|
if (SourceInstance->GetMaterialLayers(MaterialLayers))
|
|
{
|
|
UDEditorMaterialLayersParameterValue& ParameterValue = *(NewObject<UDEditorMaterialLayersParameterValue>(this));
|
|
ParameterValue.bOverride = true;
|
|
ParameterValue.ParameterValue = MoveTemp(MaterialLayers);
|
|
AssignParameterToGroup(&ParameterValue, FName());
|
|
}
|
|
}
|
|
|
|
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> ParameterValues;
|
|
for (int32 TypeIndex = 0; TypeIndex < NumMaterialParameterTypes; ++TypeIndex)
|
|
{
|
|
const EMaterialParameterType ParameterType = (EMaterialParameterType)TypeIndex;
|
|
SourceInstance->GetAllParametersOfType(ParameterType, ParameterValues);
|
|
for (const auto& It : ParameterValues)
|
|
{
|
|
UDEditorParameterValue* Parameter = UDEditorParameterValue::Create(this, ParameterType, It.Key, It.Value);
|
|
AssignParameterToGroup(Parameter, It.Value.Group);
|
|
}
|
|
}
|
|
|
|
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>( "MaterialEditor" );
|
|
MaterialEditorModule->GetVisibleMaterialParameters(ParentMaterial, SourceInstance, VisibleExpressions);
|
|
}
|
|
|
|
// sort contents of groups
|
|
for(int32 ParameterIdx = 0; ParameterIdx < ParameterGroups.Num(); ParameterIdx++)
|
|
{
|
|
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
|
|
struct FCompareUDEditorParameterValueByParameterName
|
|
{
|
|
FORCEINLINE bool operator()(const UDEditorParameterValue& A, const UDEditorParameterValue& B) const
|
|
{
|
|
FString AName = A.ParameterInfo.Name.ToString();
|
|
FString BName = B.ParameterInfo.Name.ToString();
|
|
return A.SortPriority != B.SortPriority ? A.SortPriority < B.SortPriority : AName < BName;
|
|
}
|
|
};
|
|
ParamGroup.Parameters.Sort( FCompareUDEditorParameterValueByParameterName() );
|
|
}
|
|
|
|
// sort groups itself pushing defaults to end
|
|
struct FCompareFEditorParameterGroupByName
|
|
{
|
|
FORCEINLINE bool operator()(const FEditorParameterGroup& A, const FEditorParameterGroup& B) const
|
|
{
|
|
FString AName = A.GroupName.ToString();
|
|
FString BName = B.GroupName.ToString();
|
|
if (AName == TEXT("none"))
|
|
{
|
|
return false;
|
|
}
|
|
if (BName == TEXT("none"))
|
|
{
|
|
return false;
|
|
}
|
|
return A.GroupSortPriority != B.GroupSortPriority ? A.GroupSortPriority < B.GroupSortPriority : AName < BName;
|
|
}
|
|
};
|
|
ParameterGroups.Sort( FCompareFEditorParameterGroupByName() );
|
|
|
|
TArray<struct FEditorParameterGroup> ParameterDefaultGroups;
|
|
for(int32 ParameterIdx=0; ParameterIdx<ParameterGroups.Num(); ParameterIdx++)
|
|
{
|
|
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
|
|
if (bUseOldStyleMICEditorGroups == false)
|
|
{
|
|
if (ParamGroup.GroupName == TEXT("None"))
|
|
{
|
|
ParameterDefaultGroups.Add(ParamGroup);
|
|
ParameterGroups.RemoveAt(ParameterIdx);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ParamGroup.GroupName == TEXT("Vector Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Scalar Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Texture Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Static Switch Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Static Component Mask Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Font Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Material Layers Parameter Values"))
|
|
{
|
|
ParameterDefaultGroups.Add(ParamGroup);
|
|
ParameterGroups.RemoveAt(ParameterIdx);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ParameterDefaultGroups.Num() >0)
|
|
{
|
|
ParameterGroups.Append(ParameterDefaultGroups);
|
|
}
|
|
|
|
if (DetailsView.IsValid())
|
|
{
|
|
// Tell our source instance to update itself so the preview updates.
|
|
DetailsView.Pin()->ForceRefresh();
|
|
}
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void UMaterialEditorInstanceConstant::CleanParameterStack(int32 Index, EMaterialParameterAssociation MaterialType)
|
|
{
|
|
check(GIsEditor);
|
|
TArray<FEditorParameterGroup> CleanedGroups;
|
|
for (FEditorParameterGroup Group : ParameterGroups)
|
|
{
|
|
FEditorParameterGroup DuplicatedGroup = FEditorParameterGroup();
|
|
DuplicatedGroup.GroupAssociation = Group.GroupAssociation;
|
|
DuplicatedGroup.GroupName = Group.GroupName;
|
|
DuplicatedGroup.GroupSortPriority = Group.GroupSortPriority;
|
|
for (UDEditorParameterValue* Parameter : Group.Parameters)
|
|
{
|
|
if (Parameter->ParameterInfo.Association != MaterialType
|
|
|| Parameter->ParameterInfo.Index != Index)
|
|
{
|
|
DuplicatedGroup.Parameters.Add(Parameter);
|
|
}
|
|
}
|
|
CleanedGroups.Add(DuplicatedGroup);
|
|
}
|
|
|
|
ParameterGroups = CleanedGroups;
|
|
CopyToSourceInstance(true);
|
|
}
|
|
void UMaterialEditorInstanceConstant::ResetOverrides(int32 Index, EMaterialParameterAssociation MaterialType)
|
|
{
|
|
check(GIsEditor);
|
|
|
|
for (const FEditorParameterGroup& Group : ParameterGroups)
|
|
{
|
|
for (UDEditorParameterValue* Parameter : Group.Parameters)
|
|
{
|
|
if (Parameter->ParameterInfo.Association == MaterialType
|
|
&& Parameter->ParameterInfo.Index == Index)
|
|
{
|
|
const EMaterialParameterType ParameterType = Parameter->GetParameterType();
|
|
if (ParameterType != EMaterialParameterType::None)
|
|
{
|
|
FMaterialParameterMetadata SourceValue;
|
|
bool bOverride = false;
|
|
if (SourceInstance->GetParameterValue(ParameterType, Parameter->ParameterInfo, SourceValue, EMaterialGetParameterValueFlags::CheckInstanceOverrides))
|
|
{
|
|
bOverride = SourceValue.bOverride;
|
|
}
|
|
Parameter->bOverride = bOverride;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CopyToSourceInstance(true);
|
|
|
|
}
|
|
#endif
|
|
|
|
void UMaterialEditorInstanceConstant::CopyToSourceInstance(const bool bForceStaticPermutationUpdate)
|
|
{
|
|
if (SourceInstance && !SourceInstance->IsTemplate(RF_ClassDefaultObject))
|
|
{
|
|
if (bIsFunctionPreviewMaterial)
|
|
{
|
|
bIsFunctionInstanceDirty = true;
|
|
}
|
|
else
|
|
{
|
|
SourceInstance->MarkPackageDirty();
|
|
}
|
|
|
|
{
|
|
FMaterialInstanceParameterUpdateContext UpdateContext(SourceInstance, EMaterialInstanceClearParameterFlag::All);
|
|
UpdateContext.SetBasePropertyOverrides(BasePropertyOverrides);
|
|
UpdateContext.SetForceStaticPermutationUpdate(bForceStaticPermutationUpdate);
|
|
|
|
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
|
|
{
|
|
FEditorParameterGroup& Group = ParameterGroups[GroupIdx];
|
|
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
|
|
{
|
|
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
|
|
if (Parameter && Parameter->bOverride)
|
|
{
|
|
FMaterialParameterMetadata EditorValue;
|
|
if (Parameter->GetValue(EditorValue))
|
|
{
|
|
UpdateContext.SetParameterValueEditorOnly(Parameter->ParameterInfo, EditorValue, EMaterialSetParameterValueFlags::SetCurveAtlas);
|
|
}
|
|
else if (UDEditorMaterialLayersParameterValue* LayersParameter = Cast<UDEditorMaterialLayersParameterValue>(Parameter))
|
|
{
|
|
UpdateContext.SetMaterialLayers(LayersParameter->ParameterValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy phys material back to source instance
|
|
SourceInstance->PhysMaterial = PhysMaterial;
|
|
|
|
// Copy the Lightmass settings...
|
|
SourceInstance->SetOverrideCastShadowAsMasked(LightmassSettings.CastShadowAsMasked.bOverride);
|
|
SourceInstance->SetCastShadowAsMasked(LightmassSettings.CastShadowAsMasked.ParameterValue);
|
|
SourceInstance->SetOverrideEmissiveBoost(LightmassSettings.EmissiveBoost.bOverride);
|
|
SourceInstance->SetEmissiveBoost(LightmassSettings.EmissiveBoost.ParameterValue);
|
|
SourceInstance->SetOverrideDiffuseBoost(LightmassSettings.DiffuseBoost.bOverride);
|
|
SourceInstance->SetDiffuseBoost(LightmassSettings.DiffuseBoost.ParameterValue);
|
|
SourceInstance->SetOverrideExportResolutionScale(LightmassSettings.ExportResolutionScale.bOverride);
|
|
SourceInstance->SetExportResolutionScale(LightmassSettings.ExportResolutionScale.ParameterValue);
|
|
|
|
// Copy Refraction bias setting
|
|
FMaterialParameterInfo RefractionInfo(TEXT("RefractionDepthBias"));
|
|
SourceInstance->SetScalarParameterValueEditorOnly(RefractionInfo, RefractionDepthBias);
|
|
|
|
SourceInstance->bOverrideSubsurfaceProfile = bOverrideSubsurfaceProfile;
|
|
SourceInstance->SubsurfaceProfile = SubsurfaceProfile;
|
|
|
|
// Update object references and parameter names.
|
|
SourceInstance->UpdateParameterNames();
|
|
VisibleExpressions.Empty();
|
|
|
|
// force refresh of visibility of properties
|
|
if (Parent)
|
|
{
|
|
UMaterial* ParentMaterial = Parent->GetMaterial();
|
|
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>("MaterialEditor");
|
|
MaterialEditorModule->GetVisibleMaterialParameters(ParentMaterial, SourceInstance, VisibleExpressions);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::ApplySourceFunctionChanges()
|
|
{
|
|
if (bIsFunctionPreviewMaterial && bIsFunctionInstanceDirty)
|
|
{
|
|
CopyToSourceInstance();
|
|
|
|
// Copy updated function parameter values
|
|
SourceFunction->ScalarParameterValues = SourceInstance->ScalarParameterValues;
|
|
SourceFunction->VectorParameterValues = SourceInstance->VectorParameterValues;
|
|
SourceFunction->TextureParameterValues = SourceInstance->TextureParameterValues;
|
|
SourceFunction->RuntimeVirtualTextureParameterValues = SourceInstance->RuntimeVirtualTextureParameterValues;
|
|
SourceFunction->FontParameterValues = SourceInstance->FontParameterValues;
|
|
|
|
const FStaticParameterSetEditorOnlyData& StaticParameters = SourceInstance->GetEditorOnlyStaticParameters();
|
|
SourceFunction->StaticSwitchParameterValues = StaticParameters.StaticSwitchParameters;
|
|
SourceFunction->StaticComponentMaskParameterValues = StaticParameters.StaticComponentMaskParameters;
|
|
|
|
SourceFunction->MarkPackageDirty();
|
|
bIsFunctionInstanceDirty = false;
|
|
|
|
UMaterialEditingLibrary::UpdateMaterialFunction(SourceFunction, nullptr);
|
|
}
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::SetSourceInstance(UMaterialInstanceConstant* MaterialInterface)
|
|
{
|
|
check(MaterialInterface);
|
|
SourceInstance = MaterialInterface;
|
|
Parent = SourceInstance->Parent;
|
|
PhysMaterial = SourceInstance->PhysMaterial;
|
|
|
|
CopyBasePropertiesFromParent();
|
|
|
|
RegenerateArrays();
|
|
|
|
//propagate changes to the base material so the instance will be updated if it has a static permutation resource
|
|
FMaterialInstanceParameterUpdateContext UpdateContext(SourceInstance, EMaterialInstanceClearParameterFlag::Static);
|
|
|
|
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
|
|
{
|
|
FEditorParameterGroup& Group = ParameterGroups[GroupIdx];
|
|
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
|
|
{
|
|
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
|
|
if (Parameter && Parameter->bOverride)
|
|
{
|
|
FMaterialParameterMetadata EditorValue;
|
|
if (Parameter->GetValue(EditorValue))
|
|
{
|
|
// Only want to update static parameters here
|
|
if (IsStaticMaterialParameter(EditorValue.Value.Type))
|
|
{
|
|
UpdateContext.SetParameterValueEditorOnly(Parameter->ParameterInfo, EditorValue);
|
|
}
|
|
}
|
|
else if (UDEditorMaterialLayersParameterValue* LayersParameter = Cast<UDEditorMaterialLayersParameterValue>(Parameter))
|
|
{
|
|
UpdateContext.SetMaterialLayers(LayersParameter->ParameterValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::SetSourceFunction(UMaterialFunctionInstance* MaterialFunction)
|
|
{
|
|
SourceFunction = MaterialFunction;
|
|
bIsFunctionPreviewMaterial = !!(SourceFunction);
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::UpdateSourceInstanceParent()
|
|
{
|
|
// If the parent was changed to the source instance, set it to NULL
|
|
if( Parent == SourceInstance )
|
|
{
|
|
Parent = NULL;
|
|
}
|
|
|
|
SourceInstance->SetParentEditorOnly( Parent );
|
|
SourceInstance->PostEditChange();
|
|
}
|
|
|
|
|
|
void UMaterialEditorInstanceConstant::CopyBasePropertiesFromParent()
|
|
{
|
|
BasePropertyOverrides = SourceInstance->BasePropertyOverrides;
|
|
// Copy the overrides (if not yet overridden), so they match their true values in the UI
|
|
if (!BasePropertyOverrides.bOverride_OpacityMaskClipValue)
|
|
{
|
|
BasePropertyOverrides.OpacityMaskClipValue = SourceInstance->GetOpacityMaskClipValue();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_BlendMode)
|
|
{
|
|
BasePropertyOverrides.BlendMode = SourceInstance->GetBlendMode();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_ShadingModel)
|
|
{
|
|
if (SourceInstance->IsShadingModelFromMaterialExpression())
|
|
{
|
|
BasePropertyOverrides.ShadingModel = MSM_FromMaterialExpression;
|
|
}
|
|
else
|
|
{
|
|
BasePropertyOverrides.ShadingModel = SourceInstance->GetShadingModels().GetFirstShadingModel();
|
|
}
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_TwoSided)
|
|
{
|
|
BasePropertyOverrides.TwoSided = SourceInstance->IsTwoSided();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_OutputTranslucentVelocity)
|
|
{
|
|
BasePropertyOverrides.bOutputTranslucentVelocity = SourceInstance->IsTranslucencyWritingVelocity();
|
|
}
|
|
if (!BasePropertyOverrides.DitheredLODTransition)
|
|
{
|
|
BasePropertyOverrides.DitheredLODTransition = SourceInstance->IsDitheredLODTransition();
|
|
}
|
|
|
|
// Copy the Lightmass settings...
|
|
// The lightmass functions (GetCastShadowAsMasked, etc.) check if the value is overridden and returns the current value if so, otherwise returns the parent value
|
|
// So we don't need to wrap these in the same "if not overriding" as above
|
|
LightmassSettings.CastShadowAsMasked.ParameterValue = SourceInstance->GetCastShadowAsMasked();
|
|
LightmassSettings.EmissiveBoost.ParameterValue = SourceInstance->GetEmissiveBoost();
|
|
LightmassSettings.DiffuseBoost.ParameterValue = SourceInstance->GetDiffuseBoost();
|
|
LightmassSettings.ExportResolutionScale.ParameterValue = SourceInstance->GetExportResolutionScale();
|
|
|
|
//Copy refraction settings
|
|
SourceInstance->GetRefractionSettings(RefractionDepthBias);
|
|
|
|
bOverrideSubsurfaceProfile = SourceInstance->bOverrideSubsurfaceProfile;
|
|
// Copy the subsurface profile. GetSubsurfaceProfile_Internal() will return either the overridden profile or one from a parent
|
|
SubsurfaceProfile = SourceInstance->GetSubsurfaceProfile_Internal();
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void UMaterialEditorInstanceConstant::PostEditUndo()
|
|
{
|
|
Super::PostEditUndo();
|
|
|
|
if (bIsFunctionPreviewMaterial && SourceFunction)
|
|
{
|
|
bIsFunctionInstanceDirty = true;
|
|
ApplySourceFunctionChanges();
|
|
}
|
|
else if (SourceInstance)
|
|
{
|
|
FMaterialUpdateContext Context;
|
|
|
|
UpdateSourceInstanceParent();
|
|
|
|
Context.AddMaterialInstance(SourceInstance);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
UMaterialEditorMeshComponent::UMaterialEditorMeshComponent(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
}
|