Files
UnrealEngine/Engine/Plugins/Interchange/Runtime/Source/Pipelines/Private/InterchangeGenericMaterialPipeline.cpp
2025-05-18 13:04:45 +08:00

4600 lines
189 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "InterchangeGenericMaterialPipeline.h"
#include "AssetRegistry/AssetData.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "InterchangeGenericTexturePipeline.h"
#include "InterchangeManager.h"
#include "InterchangeMaterialDefinitions.h"
#include "InterchangeMaterialFactoryNode.h"
#include "InterchangeMaterialInstanceNode.h"
#include "InterchangeMaterialReferenceNode.h"
#include "InterchangePipelineLog.h"
#include "InterchangeShaderGraphNode.h"
#include "InterchangeSourceData.h"
#include "InterchangeSparseVolumeTexturePipeline.h"
#include "InterchangeSpecularProfileNode.h"
#include "InterchangeSpecularProfileFactoryNode.h"
#include "InterchangeTexture2DArrayNode.h"
#include "InterchangeTexture2DNode.h"
#include "InterchangeTextureBlurNode.h"
#include "InterchangeTextureCubeNode.h"
#include "InterchangeTextureFactoryNode.h"
#include "InterchangeTextureNode.h"
#include "Materials/Material.h"
#include "Materials/MaterialExpressionAdd.h"
#include "Materials/MaterialExpressionComponentMask.h"
#include "Materials/MaterialExpressionConstant.h"
#include "Materials/MaterialExpressionConstant3Vector.h"
#include "Materials/MaterialExpressionCosine.h"
#include "Materials/MaterialExpressionFresnel.h"
#include "Materials/MaterialExpressionLinearInterpolate.h"
#include "Materials/MaterialExpressionMaterialFunctionCall.h"
#include "Materials/MaterialExpressionMultiply.h"
#include "Materials/MaterialExpressionNoise.h"
#include "Materials/MaterialExpressionOneMinus.h"
#include "Materials/MaterialExpressionRotateAboutAxis.h"
#include "Materials/MaterialExpressionRotator.h"
#include "Materials/MaterialExpressionScalarParameter.h"
#include "Materials/MaterialExpressionSine.h"
#include "Materials/MaterialExpressionStaticBoolParameter.h"
#include "Materials/MaterialExpressionSubstrate.h"
#include "Materials/MaterialExpressionSwitch.h"
#include "Materials/MaterialExpressionTangent.h"
#include "Materials/MaterialExpressionTextureCoordinate.h"
#include "Materials/MaterialExpressionTextureObject.h"
#include "Materials/MaterialExpressionTextureSample.h"
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
#include "Materials/MaterialExpressionTextureSampleParameter2DArray.h"
#include "Materials/MaterialExpressionTextureSampleParameterCube.h"
#include "Materials/MaterialExpressionTime.h"
#include "Materials/MaterialExpressionTransform.h"
#include "Materials/MaterialExpressionTransformPosition.h"
#include "Materials/MaterialExpressionVectorNoise.h"
#include "Materials/MaterialExpressionVectorParameter.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "MaterialX/MaterialExpressions/MaterialExpressionSwizzle.h"
#include "MaterialX/MaterialExpressions/MaterialExpressionTextureSampleParameterBlur.h"
#include "Misc/CoreMisc.h"
#include "Misc/PackageName.h"
#include "Misc/Paths.h"
#include "Nodes/InterchangeBaseNode.h"
#include "Nodes/InterchangeBaseNodeContainer.h"
#include "Nodes/InterchangeSourceNode.h"
#include "Nodes/InterchangeUserDefinedAttribute.h"
#include "Templates/Function.h"
#include "Templates/SubclassOf.h"
#include "UObject/Object.h"
#include "UObject/ObjectMacros.h"
#if UE_BUILD_DEBUG
#include "HAL/PlatformFileManager.h"
#endif
// Material Hash Utils
#include "Material/InterchangeMaterialFactory.h"
#include <string_view>
#include UE_INLINE_GENERATED_CPP_BY_NAME(InterchangeGenericMaterialPipeline)
#define LOCTEXT_NAMESPACE "InterchangeGenericMaterialPipeline"
FString LexToString(UInterchangeGenericMaterialPipeline::EMaterialInputType Value)
{
switch(Value)
{
case UInterchangeGenericMaterialPipeline::EMaterialInputType::Unknown:
return TEXT("Unknown");
case UInterchangeGenericMaterialPipeline::EMaterialInputType::Color:
return TEXT("Color");
case UInterchangeGenericMaterialPipeline::EMaterialInputType::Vector:
return TEXT("Vector");
case UInterchangeGenericMaterialPipeline::EMaterialInputType::Scalar:
return TEXT("Scalar");
default:
ensure(false);
return FString();
}
}
namespace UE::Interchange::InterchangeGenericMaterialPipeline::Private
{
bool AreRequiredPackagesLoaded()
{
auto ArePackagesLoaded = [](const TArray<FString>& PackagePaths) -> bool
{
bool bAllLoaded = true;
for (const FString& PackagePath : PackagePaths)
{
const FString ObjectPath(FPackageName::ExportTextPathToObjectPath(PackagePath));
if (FPackageName::DoesPackageExist(ObjectPath))
{
if (FSoftObjectPath(ObjectPath).TryLoad())
{
continue;
}
else
{
UE_LOG(LogInterchangePipeline, Warning, TEXT("Couldn't load %s"), *PackagePath);
}
}
else
{
UE_LOG(LogInterchangePipeline, Warning, TEXT("Couldn't find %s"), *PackagePath);
}
bAllLoaded = false;
}
return bAllLoaded;
};
TArray<FString> RequiredPackages = {
TEXT("MaterialFunction'/Engine/Functions/Engine_MaterialFunctions01/Shading/ConvertFromDiffSpec.ConvertFromDiffSpec'"),
TEXT("MaterialFunction'/Engine/Functions/Engine_MaterialFunctions01/Texturing/FlattenNormal.FlattenNormal'"),
TEXT("MaterialFunction'/Engine/Functions/Engine_MaterialFunctions02/Utility/MakeFloat3.MakeFloat3'"),
TEXT("MaterialFunction'/Engine/Functions/Engine_MaterialFunctions02/Texturing/CustomRotator.CustomRotator'"),
TEXT("MaterialFunction'/InterchangeAssets/Functions/MF_PhongToMetalRoughness.MF_PhongToMetalRoughness'"),
};
static const bool bRequiredPackagesLoaded = ArePackagesLoaded(RequiredPackages);
return bRequiredPackagesLoaded;
}
void UpdateBlendModeBasedOnOpacityAttributes(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
// Opacity Clip Value
bool bIsMasked = false;
{
float OpacityClipValue;
if (ShaderGraphNode->GetCustomOpacityMaskClipValue(OpacityClipValue))
{
MaterialFactoryNode->SetCustomOpacityMaskClipValue(OpacityClipValue);
bIsMasked = true;
}
}
// Don't change the blend mode if it was already set
TEnumAsByte<EBlendMode> BlendMode = bIsMasked ? EBlendMode::BLEND_Masked : EBlendMode::BLEND_Translucent;
if (!MaterialFactoryNode->GetCustomBlendMode(BlendMode))
{
MaterialFactoryNode->SetCustomBlendMode(BlendMode);
}
// If bland mode is masked or translucent, set lighting mode accordingly without changing it if it was already set
if(BlendMode == EBlendMode::BLEND_Masked || BlendMode == EBlendMode::BLEND_Translucent)
{
TEnumAsByte<ETranslucencyLightingMode> LightingMode = ETranslucencyLightingMode::TLM_Surface;
if (!MaterialFactoryNode->GetCustomTranslucencyLightingMode(LightingMode))
{
MaterialFactoryNode->SetCustomTranslucencyLightingMode(LightingMode);
}
}
}
void UpdateFunctionCallExpression(UInterchangeMaterialExpressionFactoryNode& FunctionCallExpression, const FString& MaterialFunctionPath)
{
const FName MaterialFunctionMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionMaterialFunctionCall, MaterialFunction);
UClass* CustomExpressionClass = UMaterialExpressionMaterialFunctionCall::StaticClass();
FunctionCallExpression.SetCustomExpressionClassName(CustomExpressionClass->GetName());
FunctionCallExpression.AddStringAttribute(MaterialFunctionMemberName.ToString(), MaterialFunctionPath);
FunctionCallExpression.AddApplyAndFillDelegates<FString>(MaterialFunctionMemberName.ToString(), CustomExpressionClass, MaterialFunctionMemberName);
}
UInterchangeMaterialExpressionFactoryNode* CreateExpressionWithMaterialFunction(
UInterchangeBaseNodeContainer* BaseNodeContainer,
UInterchangeMaterialFactoryNode* MaterialFactoryNode,
const FString& Label,
const FString& MaterialFunctionPath)
{
const FString UniqueId = MaterialFactoryNode->GetUniqueID() + Label;
UInterchangeMaterialExpressionFactoryNode* Expression = NewObject<UInterchangeMaterialExpressionFactoryNode>(BaseNodeContainer, NAME_None);
if (!Expression)
{
return nullptr;
}
BaseNodeContainer->SetupNode(Expression, UniqueId, Label, EInterchangeNodeContainerType::FactoryData, MaterialFactoryNode->GetUniqueID());
UpdateFunctionCallExpression(*Expression, MaterialFunctionPath);
return Expression;
}
UMaterialInterface* FindExistingMaterial(const FString& BasePath, const FString& MaterialFullName, const bool bRecursivePaths)
{
UMaterialInterface* Material = nullptr;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
//Make sure we scan the root paths only one time per editor session.
//There can be thousands of root path and it is a slow task even if everything is already scan.
static bool bRootPathScanned = false;
// Finish/update any scans
TArray<FString> ScanPaths;
if (!bRootPathScanned && (BasePath.IsEmpty() || BasePath == TEXT("/")))
{
FPackageName::QueryRootContentPaths(ScanPaths);
bRootPathScanned = true;
}
else if (!BasePath.StartsWith(TEXT("/Temp"), ESearchCase::IgnoreCase)) //We must exclude Temp path to avoid asset registry scan path warnings
{
ScanPaths.Add(BasePath);
}
if (!ScanPaths.IsEmpty())
{
constexpr bool bForceRescan = false;
AssetRegistry.ScanPathsSynchronous(ScanPaths, bForceRescan);
}
FARFilter Filter;
Filter.bRecursiveClasses = true;
Filter.bRecursivePaths = bRecursivePaths;
Filter.ClassPaths.Add(UMaterialInterface::StaticClass()->GetClassPathName());
Filter.PackagePaths.Add(FName(*BasePath));
TArray<FAssetData> AssetData;
AssetRegistry.GetAssets(Filter, AssetData);
TArray<UMaterialInterface*> FoundMaterials;
for (const FAssetData& Data : AssetData)
{
if (Data.AssetName == FName(*MaterialFullName))
{
Material = Cast<UMaterialInterface>(Data.GetAsset());
if (Material != nullptr)
{
FoundMaterials.Add(Material);
}
}
}
return FoundMaterials.Num() > 0 ? FoundMaterials[0] : Material;
}
UMaterialInterface* FindExistingMaterialFromSearchLocation(const FString& MaterialFullName, const FString& ContentPath, EInterchangeMaterialSearchLocation SearchLocation)
{
if (SearchLocation == EInterchangeMaterialSearchLocation::DoNotSearch)
{
return nullptr;
}
//Search in memory
constexpr bool bExactClass = false;
UMaterialInterface* FoundMaterial = nullptr;
//We search only in memory for search in local folder.
if (SearchLocation == EInterchangeMaterialSearchLocation::Local)
{
FoundMaterial = FindObject<UMaterialInterface>(nullptr, *MaterialFullName, bExactClass);
if(FoundMaterial)
{
//Make sure the path of the material in memory is local
FString PackagePath = FoundMaterial->GetPackage()->GetPathName();
if (!PackagePath.Equals(ContentPath))
{
FoundMaterial = nullptr;
}
}
}
if (FoundMaterial == nullptr)
{
FString SearchPath = ContentPath;
// Search in asset's local folder
FoundMaterial = FindExistingMaterial(SearchPath, MaterialFullName, false);
// Search recursively in asset's folder
if (FoundMaterial == nullptr &&
(SearchLocation != EInterchangeMaterialSearchLocation::Local))
{
FoundMaterial = FindExistingMaterial(SearchPath, MaterialFullName, true);
}
if (FoundMaterial == nullptr &&
(SearchLocation == EInterchangeMaterialSearchLocation::UnderParent ||
SearchLocation == EInterchangeMaterialSearchLocation::UnderRoot ||
SearchLocation == EInterchangeMaterialSearchLocation::AllAssets))
{
// Search recursively in parent's folder
SearchPath = FPaths::GetPath(SearchPath);
if (!SearchPath.IsEmpty())
{
FoundMaterial = FindExistingMaterial(SearchPath, MaterialFullName, true);
}
}
if (FoundMaterial == nullptr &&
(SearchLocation == EInterchangeMaterialSearchLocation::UnderRoot ||
SearchLocation == EInterchangeMaterialSearchLocation::AllAssets))
{
// Search recursively in root folder of asset
FString OutPackageRoot, OutPackagePath, OutPackageName;
FPackageName::SplitLongPackageName(SearchPath, OutPackageRoot, OutPackagePath, OutPackageName);
if (!SearchPath.IsEmpty())
{
FoundMaterial = FindExistingMaterial(OutPackageRoot, MaterialFullName, true);
}
}
if (FoundMaterial == nullptr &&
SearchLocation == EInterchangeMaterialSearchLocation::AllAssets)
{
// Search everywhere
FoundMaterial = FindExistingMaterial(TEXT("/"), MaterialFullName, true);
}
}
return FoundMaterial;
}
void AddSpecularProfileToFactoryNode(const UInterchangeBaseNode* ShaderNode, UInterchangeFactoryBaseNode* FactoryNode, UInterchangeBaseNodeContainer* BaseNodeContainer)
{
using namespace UE::Interchange::Materials;
if (FString SpecularProfileUid; ShaderNode->GetStringAttribute(SubstrateMaterial::SpecularProfile.ToString(), SpecularProfileUid))
{
if (BaseNodeContainer->GetNode(SpecularProfileUid))
{
FactoryNode->AddStringAttribute(SubstrateMaterial::SpecularProfile.ToString(), SpecularProfileUid);
FactoryNode->AddFactoryDependencyUid(UInterchangeFactoryBaseNode::BuildFactoryNodeUid(SpecularProfileUid));
}
}
}
}
namespace UE::Interchange::Materials::HashUtils
{
#if UE_BUILD_DEBUG
class FMaterialHashDebugData
{
public:
FMaterialHashDebugData(const FString& InLogDirectoryPath)
:LogDirectoryPath(InLogDirectoryPath) {}
void Reset();
void SaveLogsToFile(const FString& FileName);
template<typename...TArgs>
void LogMessage(const FString& Format, TArgs ...Args)
{
TArray<FStringFormatArg> FormattedArgs;
AddFormattedArg(FormattedArgs, Args...);
FString Message = FString::Format(*Format, FormattedArgs);
UE_LOG(LogInterchangePipeline, Log, TEXT("%s"), *Message);
LogMessageContainer.Add(Message);
}
void LogCurrentNodeAddress()
{
FString NodeAddress = FString::Printf(TEXT("Current Node Address: %s\n"), *GetCurrentNodeAddress());
UE_LOG(LogInterchangePipeline, Log, TEXT("%s"), *NodeAddress);
LogMessageContainer.Add(NodeAddress);
}
FString GetCurrentNodeAddress();
void AddNodeAddress(const FString& NodeAddress, bool bCreatePopCheckPoint = true);
void PopNodeAddressesToLastPopIndex();
private:
template<typename TArg>
void AddFormattedArg(TArray<FStringFormatArg>& FormattedArgs, TArg Arg)
{
FormattedArgs.Add(Arg);
}
template<typename TArg, typename ...TArgs>
void AddFormattedArg(TArray<FStringFormatArg>& FormattedArgs, TArg Arg, TArgs ...Args)
{
FormattedArgs.Add(Arg);
AddFormattedArg(FormattedArgs, Args...);
}
private:
FString LogDirectoryPath;
TArray<FString> LogMessageContainer;
TArray<FString> NodeAddressStack;
TArray<int32> NodeAddressPopCheckPoints;
};
FString FMaterialHashDebugData::GetCurrentNodeAddress()
{
TStringBuilder<512> StringBuilder;
for (int32 i = 0; i < NodeAddressStack.Num(); ++i)
{
StringBuilder.Append(NodeAddressStack[i]);
if (i < NodeAddressStack.Num() - 1)
{
StringBuilder.Append(TEXT("/"));
}
}
return StringBuilder.ToString();
}
void FMaterialHashDebugData::SaveLogsToFile(const FString& FileName)
{
const FString LogFileExtension = TEXT(".txt");
if (LogMessageContainer.Num())
{
static FString FileDirectory = FPaths::ProjectSavedDir() + LogDirectoryPath;
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
if (PlatformFile.CreateDirectoryTree(*FileDirectory))
{
FString AbsolutePath = FileDirectory + FileName + LogFileExtension;
FFileHelper::SaveStringArrayToFile(LogMessageContainer, *AbsolutePath);
}
}
}
void FMaterialHashDebugData::Reset()
{
NodeAddressStack.Empty();
LogMessageContainer.Empty();
}
void FMaterialHashDebugData::AddNodeAddress(const FString& NodeAddress, bool bCreatePopCheckPoint /*= true*/)
{
if (bCreatePopCheckPoint)
{
NodeAddressPopCheckPoints.Add(NodeAddressStack.Num());
}
NodeAddressStack.Add(NodeAddress);
}
void FMaterialHashDebugData::PopNodeAddressesToLastPopIndex()
{
int32 TargetStackSize = 0;
if (NodeAddressPopCheckPoints.Num())
{
TargetStackSize = NodeAddressPopCheckPoints.Last();
}
if (!TargetStackSize)
{
NodeAddressStack.Empty();
NodeAddressPopCheckPoints.Empty();
}
else
{
while (NodeAddressStack.Num() && NodeAddressStack.Num() > TargetStackSize)
{
NodeAddressStack.Pop();
}
NodeAddressPopCheckPoints.Pop();
}
}
#endif
class FDuplicateMaterialHelper
{
public:
#if UE_BUILD_DEBUG
FDuplicateMaterialHelper(UInterchangeGenericMaterialPipeline& InGenericMaterialPipeline, FMaterialHashDebugData* InHashDebugData)
:GenericMaterialPipeline(InGenericMaterialPipeline),
HashDebugData(InHashDebugData)
{}
#else
FDuplicateMaterialHelper(UInterchangeGenericMaterialPipeline& InGenericMaterialPipeline)
:GenericMaterialPipeline(InGenericMaterialPipeline)
{}
#endif
void CopyLeafInputsToFactoryNode(UInterchangeBaseMaterialFactoryNode* FactoryNode);
void ComputMaterialHash(const UInterchangeShaderGraphNode* ShaderGraphNode);
void SetupOverridableScalarParameter(const UInterchangeShaderNode* ShaderNode, const FString& ParameterKey, const FString& OverridableParameterNameKey);
void SetupOverridableVectorParameter(const UInterchangeShaderNode* ShaderNode, const FString& ParameterKey, const FString& OverridableParameterNameKey);
void SetupOverridableStaticBoolParameter(const UInterchangeShaderNode* ShaderNode, const FString& ParameterKey, const FString& OverridableParameterNameKey);
void SetupOverridableTextureParameter(const UInterchangeShaderNode* ShaderNode, const FString& InputKey, const FString& OverridableParameterNameKey);
/**
* Creates a Base Material Factory Node based on if the material is a duplicate material or if it is found for the first time.
* If the option to create a material instance for the parent is enabled, then additional material instance factory for parent would also be created.
*/
UInterchangeBaseMaterialFactoryNode* CreateFactoryForDuplicateMaterials(const UInterchangeShaderGraphNode* ShaderGraphNode, bool bImportUnusedMaterial, bool bCreateInstanceForParent);
void ResetHashData();
bool IsDuplicate()const { return bIsDuplicate; }
const UInterchangeBaseNode* const GetAttributeStorageNode() const { return AttributeStorageNode; }
template<class TInterchangeResultType>
void PostMessage(FText&& MessageText)
{
if (GenericMaterialPipeline.Results)
{
TInterchangeResultType* Result = GenericMaterialPipeline.Results->Add<TInterchangeResultType>();
Result->Text = MoveTemp(MessageText);
}
}
private:
UInterchangeBaseMaterialFactoryNode* CreateMaterialFactory(const UInterchangeShaderGraphNode* ShaderGraphNode);
UInterchangeMaterialInstanceFactoryNode* CreateMaterialInstanceFactoryFromReference(const UInterchangeShaderGraphNode* ShaderGraphNode);
UInterchangeMaterialInstanceFactoryNode* CreateMaterialInstanceFactoryForParent(const UInterchangeShaderGraphNode* ShaderGraphNode);
TEnumAsByte<EBlendMode> GetShaderGraphNodeBlendMode(const UInterchangeShaderGraphNode* ShaderGraphNode) const;
uint8 GetShaderGraphNodeShadingModel(const UInterchangeShaderGraphNode* ShaderGraphNode) const;
int32 ComputeShaderGraphNodeHash(const UInterchangeShaderGraphNode* ShaderGraphNode);
int32 ComputeShaderNodeHash(const UInterchangeShaderNode* ShaderNode);
int32 ComputeShaderInputHash(const UInterchangeShaderNode* ShaderNode, const FString& InputName);
int32 HashCombineCustom(int32 Hash, int32 CombineWith);
private:
UInterchangeGenericMaterialPipeline& GenericMaterialPipeline;
TMap<int32, UInterchangeBaseMaterialFactoryNode*> ParentMaterialFactoryMap;
UInterchangeBaseNode* AttributeStorageNode = nullptr;
#if UE_BUILD_DEBUG
FMaterialHashDebugData* HashDebugData = nullptr;
#endif
TArray<UE::Interchange::FAttributeKey> LeafInputAttributeKeys;
TSet<const UInterchangeShaderNode*> LeafInputShaderNodes;
int32 AccumulatedHash = 0;
int32 MaterialHash = 0;
bool bIsDuplicate = false;
};
FString GetDefaultValueStringForShaderType(FString ShaderType)
{
if (*ShaderType == Standard::Nodes::ScalarParameter::Name)
{
return Standard::Nodes::ScalarParameter::Attributes::DefaultValue.ToString();
}
else if (*ShaderType == Standard::Nodes::VectorParameter::Name)
{
return Standard::Nodes::VectorParameter::Attributes::DefaultValue.ToString();
}
else if (*ShaderType == Standard::Nodes::StaticBoolParameter::Name)
{
return Standard::Nodes::StaticBoolParameter::Attributes::DefaultValue.ToString();
}
return FString();
}
}
#if UE_BUILD_DEBUG
#define ADD_LOG_MESSAGE(...) if(HashDebugData){\
HashDebugData->LogMessage(__VA_ARGS__);\
}
#define ADD_NODE_ADDRESS_MESSAGE() if(HashDebugData){\
HashDebugData->LogCurrentNodeAddress();\
}
#define PUSH_NODE_ADDRESS(Node) if(HashDebugData){\
HashDebugData->AddNodeAddress(Node);\
}
#define PUSH_NODE_ADDRESS_WITHOUT_CHECKPOINT(Node) if(HashDebugData){\
HashDebugData->AddNodeAddress(Node, false);\
}
#define POP_NODE_ADDRESSES() if(HashDebugData){\
HashDebugData->PopNodeAddressesToLastPopIndex();\
}
#else
#define ADD_LOG_MESSAGE(...)
#define ADD_NODE_ADDRESS_MESSAGE()
#define PUSH_NODE_ADDRESS(Node)
#define PUSH_NODE_ADDRESS_WITHOUT_CHECKPOINT(Node)
#define POP_NODE_ADDRESSES()
#endif
UInterchangeGenericMaterialPipeline::UInterchangeGenericMaterialPipeline()
{
TexturePipeline = CreateDefaultSubobject<UInterchangeGenericTexturePipeline>("TexturePipeline");
SparseVolumeTexturePipeline = CreateDefaultSubobject<UInterchangeSparseVolumeTexturePipeline>("SparseVolumeTexturePipeline");
}
FString UInterchangeGenericMaterialPipeline::GetPipelineCategory(UClass* AssetClass)
{
return TEXT("Materials");
}
void UInterchangeGenericMaterialPipeline::PreDialogCleanup(const FName PipelineStackName)
{
if (TexturePipeline)
{
TexturePipeline->PreDialogCleanup(PipelineStackName);
}
if (SparseVolumeTexturePipeline)
{
SparseVolumeTexturePipeline->PreDialogCleanup(PipelineStackName);
}
//Save only pipeline if we are a stand alone pipeline (not a sub object of another pipeline)
if (IsStandAlonePipeline())
{
SaveSettings(PipelineStackName);
}
}
bool UInterchangeGenericMaterialPipeline::IsSettingsAreValid(TOptional<FText>& OutInvalidReason) const
{
if (TexturePipeline && !TexturePipeline->IsSettingsAreValid(OutInvalidReason))
{
return false;
}
if (SparseVolumeTexturePipeline && !SparseVolumeTexturePipeline->IsSettingsAreValid(OutInvalidReason))
{
return false;
}
return Super::IsSettingsAreValid(OutInvalidReason);
}
void UInterchangeGenericMaterialPipeline::AdjustSettingsForContext(const FInterchangePipelineContextParams& ContextParams)
{
Super::AdjustSettingsForContext(ContextParams);
if (TexturePipeline)
{
TexturePipeline->AdjustSettingsForContext(ContextParams);
}
if (SparseVolumeTexturePipeline)
{
SparseVolumeTexturePipeline->AdjustSettingsForContext(ContextParams);
}
#if WITH_EDITOR
TArray<FString> HideCategories;
bool bIsObjectAMaterial = !ContextParams.ReimportAsset ? false : ContextParams.ReimportAsset->IsA(UMaterialInterface::StaticClass());
if (ContextParams.ContextType == EInterchangePipelineContext::AssetCustomLODImport
|| ContextParams.ContextType == EInterchangePipelineContext::AssetCustomLODReimport
|| ContextParams.ContextType == EInterchangePipelineContext::AssetAlternateSkinningImport
|| ContextParams.ContextType == EInterchangePipelineContext::AssetAlternateSkinningReimport
|| ContextParams.ContextType == EInterchangePipelineContext::AssetCustomMorphTargetImport
|| ContextParams.ContextType == EInterchangePipelineContext::AssetCustomMorphTargetReImport)
{
bImportMaterials = false;
HideCategories.Add(UInterchangeGenericMaterialPipeline::GetPipelineCategory(nullptr));
SearchLocation = EInterchangeMaterialSearchLocation::DoNotSearch;
}
if (UInterchangePipelineBase* OuterMostPipeline = GetMostPipelineOuter())
{
for (const FString& HideCategoryName : HideCategories)
{
HidePropertiesOfCategory(OuterMostPipeline, this, HideCategoryName);
}
if (!bIsObjectAMaterial && ContextParams.ContextType == EInterchangePipelineContext::AssetReimport)
{
//When we re-import we hide all setting but search location, so we can find existing materials.
HideProperty(OuterMostPipeline, this, GET_MEMBER_NAME_CHECKED(UInterchangeGenericMaterialPipeline, bImportMaterials));
HideProperty(OuterMostPipeline, this, GET_MEMBER_NAME_CHECKED(UInterchangeGenericMaterialPipeline, MaterialImport));
HideProperty(OuterMostPipeline, this, GET_MEMBER_NAME_CHECKED(UInterchangeGenericMaterialPipeline, bIdentifyDuplicateMaterials));
HideProperty(OuterMostPipeline, this, GET_MEMBER_NAME_CHECKED(UInterchangeGenericMaterialPipeline, bCreateMaterialInstanceForParent));
HideProperty(OuterMostPipeline, this, GET_MEMBER_NAME_CHECKED(UInterchangeGenericMaterialPipeline, ParentMaterial));
HideProperty(OuterMostPipeline, this, GET_MEMBER_NAME_CHECKED(UInterchangeGenericMaterialPipeline, AssetName));
}
}
#endif //WITH_EDITOR
using namespace UE::Interchange;
if (!InterchangeGenericMaterialPipeline::Private::AreRequiredPackagesLoaded())
{
UE_LOG(LogInterchangePipeline, Warning, TEXT("UInterchangeGenericMaterialPipeline: Some required packages are missing. Material import might be wrong"));
}
}
#if WITH_EDITOR
void UInterchangeGenericMaterialPipeline::FilterPropertiesFromTranslatedData(UInterchangeBaseNodeContainer* InBaseNodeContainer)
{
Super::FilterPropertiesFromTranslatedData(InBaseNodeContainer);
//Filter all material pipeline properties if there is no translated material.
TArray<FString> TmpMaterialNodes;
InBaseNodeContainer->GetNodes(UInterchangeShaderGraphNode::StaticClass(), TmpMaterialNodes);
uint32 MaterialCount = TmpMaterialNodes.Num();
InBaseNodeContainer->GetNodes(UInterchangeMaterialInstanceNode::StaticClass(), TmpMaterialNodes);
MaterialCount += TmpMaterialNodes.Num();
if(MaterialCount == 0)
{
TArray<FString> HideCategories;
//Filter out all material properties
HideCategories.Add(TEXT("Materials"));
if (UInterchangePipelineBase* OuterMostPipeline = GetMostPipelineOuter())
{
for (const FString& HideCategoryName : HideCategories)
{
HidePropertiesOfCategory(OuterMostPipeline, this, HideCategoryName);
}
}
}
if(TexturePipeline)
{
TexturePipeline->FilterPropertiesFromTranslatedData(InBaseNodeContainer);
}
if(SparseVolumeTexturePipeline)
{
SparseVolumeTexturePipeline->FilterPropertiesFromTranslatedData(InBaseNodeContainer);
}
}
bool UInterchangeGenericMaterialPipeline::IsPropertyChangeNeedRefresh(const FPropertyChangedEvent& PropertyChangedEvent) const
{
if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UInterchangeGenericMaterialPipeline, bImportMaterials))
{
return true;
}
if (TexturePipeline && TexturePipeline->IsPropertyChangeNeedRefresh(PropertyChangedEvent))
{
return true;
}
if (SparseVolumeTexturePipeline && SparseVolumeTexturePipeline->IsPropertyChangeNeedRefresh(PropertyChangedEvent))
{
return true;
}
return Super::IsPropertyChangeNeedRefresh(PropertyChangedEvent);
}
void UInterchangeGenericMaterialPipeline::GetSupportAssetClasses(TArray<UClass*>& PipelineSupportAssetClasses) const
{
if (TexturePipeline)
{
TexturePipeline->GetSupportAssetClasses(PipelineSupportAssetClasses);
}
if (SparseVolumeTexturePipeline)
{
SparseVolumeTexturePipeline->GetSupportAssetClasses(PipelineSupportAssetClasses);
}
PipelineSupportAssetClasses.Add(UMaterial::StaticClass());
PipelineSupportAssetClasses.Add(UMaterialInstance::StaticClass());
}
#endif //WITH_EDITOR
void UInterchangeGenericMaterialPipeline::ExecutePipeline(UInterchangeBaseNodeContainer* InBaseNodeContainer, const TArray<UInterchangeSourceData*>& InSourceDatas, const FString& ContentBasePath)
{
#if UE_BUILD_DEBUG
UE::Interchange::Materials::HashUtils::FMaterialHashDebugData HashDebugData(TEXT("InterchangeDebug/MaterialHashLogs/"));
UE::Interchange::Materials::HashUtils::FDuplicateMaterialHelper HashHelper(*this, &HashDebugData);
#else
UE::Interchange::Materials::HashUtils::FDuplicateMaterialHelper HashHelper(*this);
#endif
if (!InBaseNodeContainer)
{
UE_LOG(LogInterchangePipeline, Warning, TEXT("UInterchangeGenericMaterialPipeline: Cannot execute pre-import pipeline because InBaseNodeContrainer is null"));
return;
}
//Set the result container to allow error message
//The parent Results container should be set at this point
ensure(Results);
{
if (TexturePipeline)
{
TexturePipeline->SetResultsContainer(Results);
}
if (SparseVolumeTexturePipeline)
{
SparseVolumeTexturePipeline->SetResultsContainer(Results);
}
}
BaseNodeContainer = InBaseNodeContainer;
SourceDatas.Empty(InSourceDatas.Num());
for (const UInterchangeSourceData* SourceData : InSourceDatas)
{
SourceDatas.Add(SourceData);
}
if (TexturePipeline)
{
TexturePipeline->ScriptedExecutePipeline(InBaseNodeContainer, InSourceDatas, ContentBasePath);
}
if (SparseVolumeTexturePipeline)
{
SparseVolumeTexturePipeline->ScriptedExecutePipeline(InBaseNodeContainer, InSourceDatas, ContentBasePath);
}
TSet<UInterchangeShaderGraphNode*> ShaderGraphNodes;
TSet<UInterchangeMaterialInstanceNode*> MaterialInstanceNodes;
TSet<UInterchangeMaterialReferenceNode*> MaterialReferenceNodes;
TSet<UInterchangeBaseMaterialFactoryNode*> BaseMaterialFactoryNodes;
// Find all nodes we need for this pipeline
BaseNodeContainer->IterateNodes(
[&ShaderGraphNodes,
&MaterialInstanceNodes,
&MaterialReferenceNodes,
&BaseMaterialFactoryNodes](const FString& NodeUid, UInterchangeBaseNode* Node)
{
switch (Node->GetNodeContainerType())
{
case EInterchangeNodeContainerType::TranslatedAsset:
{
if (UInterchangeShaderGraphNode* ShaderGraphNode = Cast<UInterchangeShaderGraphNode>(Node))
{
ShaderGraphNodes.Add(ShaderGraphNode);
}
else if (UInterchangeMaterialInstanceNode* InstanceNode = Cast<UInterchangeMaterialInstanceNode>(Node))
{
MaterialInstanceNodes.Add(InstanceNode);
}
else if (UInterchangeMaterialReferenceNode* ReferenceNode = Cast<UInterchangeMaterialReferenceNode>(Node))
{
MaterialReferenceNodes.Add(ReferenceNode);
}
}
case EInterchangeNodeContainerType::FactoryData:
{
if (UInterchangeBaseMaterialFactoryNode* BaseMaterialFactoryNode = Cast<UInterchangeBaseMaterialFactoryNode>(Node))
{
BaseMaterialFactoryNodes.Add(BaseMaterialFactoryNode);
}
}
break;
}
}
);
// Check to see whether materials should be created even if unused
// By default we let the setting of the pipeline to decide if we create the materials, every node with mesh attribute can enable/disable them, depending on the pipeline stack chosen.
bool bImportUnusedMaterial = bImportMaterials;
if (const UInterchangeSourceNode* SourceNode = UInterchangeSourceNode::GetUniqueInstance(BaseNodeContainer))
{
SourceNode->GetCustomImportUnusedMaterial(bImportUnusedMaterial);
bImportUnusedMaterial |= bImportMaterials ;
}
// Can't import materials at runtime, fall back to instances
UInterchangeEditorUtilitiesBase* EditorUtilities = UInterchangeManager::GetInterchangeManager().GetEditorUtilities();
if ((EditorUtilities && EditorUtilities->IsRuntimeOrPIE()) && MaterialImport == EInterchangeMaterialImportOption::ImportAsMaterials)
{
MaterialImport = EInterchangeMaterialImportOption::ImportAsMaterialInstances;
}
if (MaterialImport == EInterchangeMaterialImportOption::ImportAsMaterials)
{
for (const UInterchangeShaderGraphNode* ShaderGraphNode : ShaderGraphNodes)
{
UInterchangeBaseMaterialFactoryNode* MaterialBaseFactoryNode = nullptr;
bool bIsAShaderFunction;
if (ShaderGraphNode->GetCustomIsAShaderFunction(bIsAShaderFunction) && bIsAShaderFunction)
{
MaterialBaseFactoryNode = CreateMaterialFunctionFactoryNode(ShaderGraphNode);
}
else if (!bIdentifyDuplicateMaterials)
{
MaterialBaseFactoryNode = CreateMaterialFactoryNode(ShaderGraphNode);
}
else
{
HashHelper.ResetHashData();
HashHelper.ComputMaterialHash(ShaderGraphNode);
AttributeStorageNode = HashHelper.GetAttributeStorageNode();
/* Creates Material Instance Factory if duplicate material is found. */
MaterialBaseFactoryNode = HashHelper.CreateFactoryForDuplicateMaterials(ShaderGraphNode, bImportUnusedMaterial, bCreateMaterialInstanceForParent);
#if UE_BUILD_DEBUG
HashDebugData.SaveLogsToFile(ShaderGraphNode->GetUniqueID());
#endif
/* Clearing the AttributeStorageNode as it might affect how the MaterialFunctionsFactories are created. */
AttributeStorageNode = nullptr;
}
if (MaterialBaseFactoryNode)
{
BaseMaterialFactoryNodes.Add(MaterialBaseFactoryNode);
}
}
}
else if (MaterialImport == EInterchangeMaterialImportOption::ImportAsMaterialInstances)
{
for (const UInterchangeShaderGraphNode* ShaderGraphNode : ShaderGraphNodes)
{
if (UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode = CreateMaterialInstanceFactoryNode(ShaderGraphNode))
{
BaseMaterialFactoryNodes.Add(MaterialInstanceFactoryNode);
MaterialInstanceFactoryNode->SetEnabled(bImportUnusedMaterial);
}
}
}
for (UInterchangeMaterialInstanceNode* MaterialNode : MaterialInstanceNodes)
{
FString ParentPath;
if (!MaterialNode->GetCustomParent(ParentPath) || ParentPath.IsEmpty())
{
continue;
}
UInterchangeMaterialInstanceFactoryNode* MaterialFactoryNode = nullptr;
FString DisplayLabel = MaterialNode->GetDisplayLabel();
const FString NodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(MaterialNode->GetUniqueID());
if (BaseNodeContainer->IsNodeUidValid(NodeUid))
{
//The node already exist, just return it
MaterialFactoryNode = Cast<UInterchangeMaterialInstanceFactoryNode>(BaseNodeContainer->GetFactoryNode(NodeUid));
if (!MaterialFactoryNode)
{
continue;
}
}
else
{
MaterialFactoryNode = NewObject<UInterchangeMaterialInstanceFactoryNode>(BaseNodeContainer);
if (!ensure(MaterialFactoryNode))
{
continue;
}
//Creating a Material
BaseNodeContainer->SetupNode(MaterialFactoryNode, NodeUid, DisplayLabel, EInterchangeNodeContainerType::FactoryData);
MaterialFactoryNode->AddTargetNodeUid(MaterialNode->GetUniqueID());
MaterialNode->AddTargetNodeUid(MaterialFactoryNode->GetUniqueID());
}
BaseMaterialFactoryNodes.Add(MaterialFactoryNode);
// Set MaterialFactoryNode's display label to MaterialNode's uniqueID
// to reconcile mesh's slot names and material assets
MaterialFactoryNode->SetDisplayLabel(MaterialNode->GetAssetName());
MaterialFactoryNode->SetCustomParent(ParentPath);
const UClass* MaterialClass = (EditorUtilities && EditorUtilities->IsRuntimeOrPIE()) ? UMaterialInstanceDynamic::StaticClass() : UMaterialInstanceConstant::StaticClass();
MaterialFactoryNode->SetCustomInstanceClassName(MaterialClass->GetPathName());
UE::Interchange::InterchangeGenericMaterialPipeline::Private::AddSpecularProfileToFactoryNode(MaterialNode, MaterialFactoryNode, BaseNodeContainer);
TArray<FString> Inputs;
UInterchangeShaderPortsAPI::GatherInputs(MaterialNode, Inputs);
for (const FString& InputName : Inputs)
{
const bool bIsAParameter = UInterchangeShaderPortsAPI::HasParameter(MaterialNode, FName(InputName));
FString InputValueKey = CreateInputKey(InputName,bIsAParameter);
switch (UInterchangeShaderPortsAPI::GetInputType(MaterialNode, InputName, bIsAParameter))
{
case UE::Interchange::EAttributeTypes::Bool:
{
bool AttributeValue = false;
MaterialNode->GetBooleanAttribute(InputValueKey, AttributeValue);
MaterialFactoryNode->AddBooleanAttribute(InputValueKey, AttributeValue);
}
break;
case UE::Interchange::EAttributeTypes::Int32:
{
int32 AttributeValue = 0;
MaterialNode->GetInt32Attribute(InputValueKey, AttributeValue);
MaterialFactoryNode->AddInt32Attribute(InputValueKey, AttributeValue);
}
break;
case UE::Interchange::EAttributeTypes::Float:
{
float AttributeValue = 0.f;
MaterialNode->GetFloatAttribute(InputValueKey, AttributeValue);
MaterialFactoryNode->AddFloatAttribute(InputValueKey, AttributeValue);
}
break;
case UE::Interchange::EAttributeTypes::LinearColor:
{
FLinearColor AttributeValue = FLinearColor::White;
MaterialNode->GetLinearColorAttribute(InputValueKey, AttributeValue);
MaterialFactoryNode->AddLinearColorAttribute(InputValueKey, AttributeValue);
}
break;
case UE::Interchange::EAttributeTypes::String:
{
FString TextureUid;
MaterialNode->GetStringAttribute(InputValueKey, TextureUid);
FString FactoryTextureUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(TextureUid);
MaterialFactoryNode->AddStringAttribute(InputValueKey, FactoryTextureUid);
MaterialFactoryNode->AddFactoryDependencyUid(FactoryTextureUid);
}
break;
}
}
}
for (UInterchangeMaterialReferenceNode* MaterialReferenceNode : MaterialReferenceNodes)
{
FString MaterialContentPath;
if (MaterialReferenceNode->GetCustomContentPath(MaterialContentPath))
{
UInterchangeMaterialReferenceFactoryNode* FactoryNode = NewObject<UInterchangeMaterialReferenceFactoryNode>(BaseNodeContainer);
const FString DisplayLabel = MaterialReferenceNode->GetDisplayLabel();
const FString NodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(MaterialReferenceNode->GetUniqueID());
BaseNodeContainer->SetupNode(FactoryNode, NodeUid, DisplayLabel, EInterchangeNodeContainerType::FactoryData);
FactoryNode->SetCustomReferenceObject(FSoftObjectPath{MaterialContentPath});
BaseMaterialFactoryNodes.Add(FactoryNode);
FactoryNode->AddTargetNodeUid(MaterialReferenceNode->GetUniqueID());
MaterialReferenceNode->AddTargetNodeUid(FactoryNode->GetUniqueID());
}
}
//If we have a valid override name
FString OverrideAssetName = IsStandAlonePipeline() ? DestinationName : FString();
if (OverrideAssetName.IsEmpty() && IsStandAlonePipeline())
{
OverrideAssetName = AssetName;
}
// Enable or disable factory nodes according to import/reimport context
const bool bCombinedImportMaterials = bImportMaterials || bImportUnusedMaterial;
for (UInterchangeBaseMaterialFactoryNode* BaseMaterialFactoryNode : BaseMaterialFactoryNodes)
{
using namespace UE::Interchange::InterchangeGenericMaterialPipeline::Private;
bool bEnableFactoryNode = bCombinedImportMaterials;
if (bEnableFactoryNode)
{
FString MaterialName = BaseMaterialFactoryNode->GetDisplayLabel();
if (UMaterialInterface* ExistingMaterial = FindExistingMaterialFromSearchLocation(MaterialName, ContentBasePath, SearchLocation))
{
BaseMaterialFactoryNode->SetCustomReferenceObject(ExistingMaterial);
// We're reimporting the existing material we found: Enable the factory node if we can.
// TODO: Can this potentially lead to issues, since the existing material to reimport is discovered by name and search location?
if (CacheContextParam.ReimportAsset && CacheContextParam.ReimportAsset == ExistingMaterial)
{
const bool bIsMaterial = BaseMaterialFactoryNode->IsA<UInterchangeMaterialFactoryNode>()
&& ExistingMaterial->IsA<UMaterial>();
const bool bIsMaterialinstance = BaseMaterialFactoryNode->IsA<UInterchangeMaterialInstanceFactoryNode>()
&& ExistingMaterial->IsA<UMaterialInstance>();
// Reimport can only be done on material instances
bEnableFactoryNode = bEnableFactoryNode && !bIsMaterial && bIsMaterialinstance;
}
// Found an existing material on the search location during an import --> Just use it as-is
else
{
// Disabling the factory node here is important so that we don't overwrite the existing asset.
// The existing material will still actually be used for the import's material assignments and etc.
bEnableFactoryNode = false;
}
}
}
BaseMaterialFactoryNode->SetCustomIsMaterialImportEnabled(bEnableFactoryNode);
BaseMaterialFactoryNode->SetEnabled(bEnableFactoryNode);
}
if (IsStandAlonePipeline() && !OverrideAssetName.IsEmpty() && BaseMaterialFactoryNodes.Num() == 1)
{
if (UInterchangeBaseMaterialFactoryNode* Node = *BaseMaterialFactoryNodes.CreateIterator())
{
Node->SetAssetName(OverrideAssetName);
Node->SetDisplayLabel(OverrideAssetName);
}
}
TArray<UInterchangeSpecularProfileNode*> SpecularProfileNodes;
BaseNodeContainer->IterateNodesOfType<UInterchangeSpecularProfileNode>([&](const FString& NodeUid, UInterchangeSpecularProfileNode* Node)
{
SpecularProfileNodes.Add(Node);
});
for (const UInterchangeSpecularProfileNode* SpecularProfileNode : SpecularProfileNodes)
{
CreateSpecularProfileFactoryNode(SpecularProfileNode);
}
}
void UInterchangeGenericMaterialPipeline::ExecutePostFactoryPipeline(const UInterchangeBaseNodeContainer* InBaseNodeContainer, const FString& NodeKey, UObject* CreatedAsset, bool bIsAReimport)
{
if (TexturePipeline)
{
TexturePipeline->ScriptedExecutePostFactoryPipeline(InBaseNodeContainer, NodeKey, CreatedAsset, bIsAReimport);
}
if (SparseVolumeTexturePipeline)
{
SparseVolumeTexturePipeline->ScriptedExecutePostFactoryPipeline(InBaseNodeContainer, NodeKey, CreatedAsset, bIsAReimport);
}
}
void UInterchangeGenericMaterialPipeline::ExecutePostImportPipeline(const UInterchangeBaseNodeContainer* InBaseNodeContainer, const FString& NodeKey, UObject* CreatedAsset, bool bIsAReimport)
{
if (TexturePipeline)
{
TexturePipeline->ScriptedExecutePostImportPipeline(InBaseNodeContainer, NodeKey, CreatedAsset, bIsAReimport);
}
if (SparseVolumeTexturePipeline)
{
SparseVolumeTexturePipeline->ScriptedExecutePostImportPipeline(InBaseNodeContainer, NodeKey, CreatedAsset, bIsAReimport);
}
}
void UInterchangeGenericMaterialPipeline::SetReimportSourceIndex(UClass* ReimportObjectClass, const int32 SourceFileIndex)
{
if (TexturePipeline)
{
TexturePipeline->ScriptedSetReimportSourceIndex(ReimportObjectClass, SourceFileIndex);
}
if (SparseVolumeTexturePipeline)
{
SparseVolumeTexturePipeline->ScriptedSetReimportSourceIndex(ReimportObjectClass, SourceFileIndex);
}
}
UInterchangeBaseMaterialFactoryNode* UInterchangeGenericMaterialPipeline::CreateBaseMaterialFactoryNode(const UInterchangeBaseNode* MaterialNode, TSubclassOf<UInterchangeBaseMaterialFactoryNode> NodeType, bool bAddMaterialInstanceSuffix /*= false*/)
{
const FString MaterialInstanceSuffix = TEXT("_MI");
FString DisplayLabel = MaterialNode->GetDisplayLabel();
FString NodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(MaterialNode->GetUniqueID());
if (bAddMaterialInstanceSuffix)
{
NodeUid += MaterialInstanceSuffix;
}
UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode = nullptr;
if (BaseNodeContainer->IsNodeUidValid(NodeUid))
{
//The node already exist, just return it
MaterialFactoryNode = Cast<UInterchangeBaseMaterialFactoryNode>(BaseNodeContainer->GetFactoryNode(NodeUid));
if (!ensure(MaterialFactoryNode))
{
//Log an error
}
}
else
{
MaterialFactoryNode = NewObject<UInterchangeBaseMaterialFactoryNode>(BaseNodeContainer, NodeType.Get(), NAME_None);
if (!ensure(MaterialFactoryNode))
{
return nullptr;
}
BaseNodeContainer->SetupNode(MaterialFactoryNode, NodeUid, DisplayLabel, EInterchangeNodeContainerType::FactoryData);
MaterialFactoryNode->AddTargetNodeUid(MaterialNode->GetUniqueID());
MaterialNode->AddTargetNodeUid(MaterialFactoryNode->GetUniqueID());
}
return MaterialFactoryNode;
}
bool UInterchangeGenericMaterialPipeline::HasClearCoat(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
using namespace UE::Interchange::Materials::ClearCoat;
const bool bHasClearCoatInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::ClearCoat);
return bHasClearCoatInput;
}
bool UInterchangeGenericMaterialPipeline::HasSheen(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
using namespace UE::Interchange::Materials::Sheen;
const bool bHasSheenColorInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::SheenColor);
return bHasSheenColorInput;
}
bool UInterchangeGenericMaterialPipeline::HasSubsurface(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
using namespace UE::Interchange::Materials::Subsurface;
const bool bHasSubsurfaceColorInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::SubsurfaceColor);
return bHasSubsurfaceColorInput;
}
bool UInterchangeGenericMaterialPipeline::HasThinTranslucency(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
using namespace UE::Interchange::Materials::ThinTranslucent;
const bool bHasTransmissionColorInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::TransmissionColor);
const bool bHasSurfaceCoverageInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::SurfaceCoverage);
return bHasTransmissionColorInput || bHasSurfaceCoverageInput;
}
bool UInterchangeGenericMaterialPipeline::IsMetalRoughModel(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
using namespace UE::Interchange::Materials::PBRMR;
const bool bHasBaseColorInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::BaseColor);
return bHasBaseColorInput;
}
bool UInterchangeGenericMaterialPipeline::IsSpecGlossModel(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
using namespace UE::Interchange::Materials::PBRSG;
const bool bHasSpecularInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::SpecularColor);
const bool bHasGlossinessInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::Glossiness);
return bHasSpecularInput && bHasGlossinessInput;
}
bool UInterchangeGenericMaterialPipeline::IsPhongModel(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
using namespace UE::Interchange::Materials::Phong;
const bool bHasDiffuseInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::DiffuseColor);
const bool bHasSpecularInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::SpecularColor);
return bHasDiffuseInput && bHasSpecularInput;
}
bool UInterchangeGenericMaterialPipeline::IsLambertModel(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
using namespace UE::Interchange::Materials::Lambert;
const bool bHasDiffuseInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::DiffuseColor);
return bHasDiffuseInput;
}
bool UInterchangeGenericMaterialPipeline::IsSurfaceUnlitModel(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
using namespace UE::Interchange::Materials;
FString ShaderType;
ShaderGraphNode->GetCustomShaderType(ShaderType);
if(ShaderType == SurfaceUnlit::Name.ToString())
{
return true;
}
return false;
}
bool UInterchangeGenericMaterialPipeline::HandleSpecGlossModel(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials;
using namespace UE::Interchange::InterchangeGenericMaterialPipeline::Private;
if (IsSpecGlossModel(ShaderGraphNode))
{
// ConvertFromDiffSpec function call
const FString MaterialFunctionPath = TEXT("MaterialFunction'/Engine/Functions/Engine_MaterialFunctions01/Shading/ConvertFromDiffSpec.ConvertFromDiffSpec'");
UInterchangeMaterialExpressionFactoryNode* FunctionCallExpression = CreateExpressionWithMaterialFunction(BaseNodeContainer, MaterialFactoryNode, TEXT("DiffSpecFunc"), MaterialFunctionPath);
const FString FunctionCallExpressionUid = FunctionCallExpression->GetUniqueID();
MaterialFactoryNode->ConnectOutputToBaseColor(FunctionCallExpressionUid, PBRMR::Parameters::BaseColor.ToString());
MaterialFactoryNode->ConnectOutputToMetallic(FunctionCallExpressionUid, PBRMR::Parameters::Metallic.ToString());
MaterialFactoryNode->ConnectOutputToSpecular(FunctionCallExpressionUid, PBRMR::Parameters::Specular.ToString());
// DiffuseColor
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> DiffuseExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, PBRSG::Parameters::DiffuseColor.ToString(), FunctionCallExpressionUid);
if (DiffuseExpressionFactoryNode.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(FunctionCallExpression, PBRSG::Parameters::DiffuseColor.ToString(),
DiffuseExpressionFactoryNode.Get<0>()->GetUniqueID(), DiffuseExpressionFactoryNode.Get<1>());
}
}
// Specular Color
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> SpecularExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, PBRSG::Parameters::SpecularColor.ToString(), FunctionCallExpressionUid);
if (SpecularExpressionFactoryNode.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(FunctionCallExpression, PBRSG::Parameters::SpecularColor.ToString(),
SpecularExpressionFactoryNode.Get<0>()->GetUniqueID(), SpecularExpressionFactoryNode.Get<1>());
}
}
// Glossiness
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> GlossinessExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, PBRSG::Parameters::Glossiness.ToString(), MaterialFactoryNode->GetUniqueID());
if (UInterchangeMaterialExpressionFactoryNode* GlossinessFactoryNode = GlossinessExpressionFactoryNode.Get<0>())
{
UInterchangeMaterialExpressionFactoryNode* OneMinusNode =
CreateExpressionNode(TEXT("InverseGlossiness"), MaterialFactoryNode->GetUniqueID(), UMaterialExpressionOneMinus::StaticClass());
const FString OneMinusNodeInput = GET_MEMBER_NAME_CHECKED(UMaterialExpressionOneMinus, Input).ToString();
const FString OutputName = GlossinessExpressionFactoryNode.Get<1>();
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(OneMinusNode, OneMinusNodeInput, GlossinessFactoryNode->GetUniqueID(), OutputName);
MaterialFactoryNode->ConnectOutputToRoughness(OneMinusNode->GetUniqueID(), PBRMR::Parameters::Roughness.ToString());
}
}
return true;
}
return false;
}
bool UInterchangeGenericMaterialPipeline::HandlePhongModel(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials::Phong;
using namespace UE::Interchange::InterchangeGenericMaterialPipeline::Private;
if (IsPhongModel(ShaderGraphNode))
{
// ConvertFromDiffSpec function call
const FString MaterialFunctionPath = TEXT("MaterialFunction'/InterchangeAssets/Functions/MF_PhongToMetalRoughness.MF_PhongToMetalRoughness'");
UInterchangeMaterialExpressionFactoryNode* FunctionCallExpression = CreateExpressionWithMaterialFunction(BaseNodeContainer, MaterialFactoryNode, TEXT("DiffSpecFunc"), MaterialFunctionPath);
const FString FunctionCallExpressionUid = FunctionCallExpression->GetUniqueID();
MaterialFactoryNode->ConnectOutputToBaseColor(FunctionCallExpressionUid, UE::Interchange::Materials::PBRMR::Parameters::BaseColor.ToString());
MaterialFactoryNode->ConnectOutputToMetallic(FunctionCallExpressionUid, UE::Interchange::Materials::PBRMR::Parameters::Metallic.ToString());
MaterialFactoryNode->ConnectOutputToSpecular(FunctionCallExpressionUid, UE::Interchange::Materials::PBRMR::Parameters::Specular.ToString());
MaterialFactoryNode->ConnectOutputToRoughness(FunctionCallExpressionUid, UE::Interchange::Materials::PBRMR::Parameters::Roughness.ToString());
{
const FString UniqueID = FunctionCallExpression->GetUniqueID();
TFunction<void(const FString&)> ConnectInput;
ConnectInput = [&](const FString& InputName) -> void
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
this->CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, InputName, UniqueID);
if (ExpressionFactoryNode.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(FunctionCallExpression, InputName,
ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
};
ConnectInput(Parameters::AmbientColor.ToString());
ConnectInput(Parameters::DiffuseColor.ToString());
ConnectInput(Parameters::Shininess.ToString());
ConnectInput(Parameters::SpecularColor.ToString());
}
return true;
}
return false;
}
bool UInterchangeGenericMaterialPipeline::HandleLambertModel(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials::Lambert;
if (IsLambertModel(ShaderGraphNode))
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> DiffuseExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::DiffuseColor.ToString(), MaterialFactoryNode->GetUniqueID());
if (DiffuseExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToBaseColor(DiffuseExpressionFactoryNode.Get<0>()->GetUniqueID(), DiffuseExpressionFactoryNode.Get<1>());
}
return true;
}
return false;
}
bool UInterchangeGenericMaterialPipeline::HandleMetalRoughnessModel(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials::PBRMR;
bool bShadingModelHandled = false;
// BaseColor
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::BaseColor);
if (bHasInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::BaseColor.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToBaseColor(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
// Metallic
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::Metallic);
if (bHasInput)
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Scalar);
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::Metallic.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToMetallic(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
// Specular
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::Specular);
if (bHasInput)
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Scalar);
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::Specular.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToSpecular(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
// Roughness
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::Roughness);
if (bHasInput)
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Scalar);
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::Roughness.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToRoughness(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
return bShadingModelHandled;
}
bool UInterchangeGenericMaterialPipeline::HandleClearCoat(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials::ClearCoat;
bool bShadingModelHandled = false;
// Clear Coat
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::ClearCoat);
if (bHasInput)
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Scalar);
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::ClearCoat.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToClearCoat(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
// Clear Coat Roughness
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::ClearCoatRoughness);
if (bHasInput)
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Scalar);
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::ClearCoatRoughness.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToClearCoatRoughness(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
// Clear Coat Normal
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::ClearCoatNormal);
if (bHasInput)
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Vector);
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::ClearCoatNormal.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToClearCoatNormal(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
if (bShadingModelHandled)
{
MaterialFactoryNode->SetCustomShadingModel(EMaterialShadingModel::MSM_ClearCoat);
}
return bShadingModelHandled;
}
bool UInterchangeGenericMaterialPipeline::HandleSubsurface(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials::Subsurface;
bool bShadingModelHandled = false;
// Subsurface Color
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::SubsurfaceColor);
if(bHasInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::SubsurfaceColor.ToString(), MaterialFactoryNode->GetUniqueID());
if(ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToSubsurface(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
if(bShadingModelHandled)
{
MaterialFactoryNode->SetCustomShadingModel(EMaterialShadingModel::MSM_Subsurface);
MaterialFactoryNode->SetCustomBlendMode(EBlendMode::BLEND_Opaque); // Opacity in Subsurface doesn't mean Translucency, according to UE doc
}
return bShadingModelHandled;
}
bool UInterchangeGenericMaterialPipeline::HandleSheen(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials::Sheen;
bool bShadingModelHandled = false;
// Sheen Color
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::SheenColor);
if (bHasInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::SheenColor.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToFuzzColor(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
// Sheen Roughness
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::SheenRoughness);
if (bHasInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::SheenRoughness.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToCloth(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
if (bShadingModelHandled)
{
MaterialFactoryNode->SetCustomShadingModel(EMaterialShadingModel::MSM_Cloth);
}
return bShadingModelHandled;
}
bool UInterchangeGenericMaterialPipeline::HandleThinTranslucent(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials::ThinTranslucent;
bool bShadingModelHandled = false;
// Transmission Color
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::TransmissionColor);
if (bHasInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::TransmissionColor.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToTransmissionColor(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
// Surface Coverage
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::SurfaceCoverage);
if(bHasInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::SurfaceCoverage.ToString(), MaterialFactoryNode->GetUniqueID());
if(ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToSurfaceCoverage(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
bShadingModelHandled = true;
}
}
if (bShadingModelHandled)
{
MaterialFactoryNode->SetCustomBlendMode(EBlendMode::BLEND_Translucent);
MaterialFactoryNode->SetCustomShadingModel(EMaterialShadingModel::MSM_ThinTranslucent);
MaterialFactoryNode->SetCustomTranslucencyLightingMode(ETranslucencyLightingMode::TLM_SurfacePerPixelLighting);
}
return bShadingModelHandled;
}
void UInterchangeGenericMaterialPipeline::HandleCommonParameters(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials::Common;
if (bool bScreenSpaceReflections; ShaderGraphNode->GetCustomScreenSpaceReflections(bScreenSpaceReflections))
{
MaterialFactoryNode->SetCustomScreenSpaceReflections(bScreenSpaceReflections);
}
bool bTwoSidedTransmission = false;
ShaderGraphNode->GetCustomTwoSidedTransmission(bTwoSidedTransmission);
// Two sidedness (ignored for thin translucency as it looks wrong)
if (bTwoSidedTransmission || !HasThinTranslucency(ShaderGraphNode))
{
bool bTwoSided = false;
ShaderGraphNode->GetCustomTwoSided(bTwoSided);
MaterialFactoryNode->SetCustomTwoSided(bTwoSided);
}
// Anisotropy
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::Anisotropy);
if (bHasInput)
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Scalar);
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::Anisotropy.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToAnisotropy(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
}
}
// Emissive
{
const bool bHasEmissiveInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::EmissiveColor);
if (bHasEmissiveInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> EmissiveExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::EmissiveColor.ToString(), MaterialFactoryNode->GetUniqueID());
if (EmissiveExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToEmissiveColor(EmissiveExpressionFactoryNode.Get<0>()->GetUniqueID(), EmissiveExpressionFactoryNode.Get<1>());
}
}
}
// Normal
{
const bool bHasNormalInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::Normal);
if (bHasNormalInput)
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Vector);
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::Normal.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToNormal(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
}
}
// Tangent
{
const bool bHasNormalInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::Tangent);
if(bHasNormalInput)
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Vector);
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::Tangent.ToString(), MaterialFactoryNode->GetUniqueID());
if(ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToTangent(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
}
}
// Opacity / OpacityMask
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Scalar);
bool bUpdateBlendMode = false;
const bool bHasOpacityMaskInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::OpacityMask);
if (bHasOpacityMaskInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> OpacityMaskExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::OpacityMask.ToString(), MaterialFactoryNode->GetUniqueID());
if (OpacityMaskExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToOpacity(OpacityMaskExpressionFactoryNode.Get<0>()->GetUniqueID(), OpacityMaskExpressionFactoryNode.Get<1>());
}
bUpdateBlendMode = true;
}
else
{
const bool bHasOpacityInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::Opacity);
if (bHasOpacityInput)
{
bool bHasSomeTransparency = true;
float OpacityValue;
if (ShaderGraphNode->GetFloatAttribute(UInterchangeShaderPortsAPI::MakeInputValueKey(Parameters::Opacity.ToString()), OpacityValue))
{
bHasSomeTransparency = !FMath::IsNearlyEqual(OpacityValue, 1.f);
}
if (bHasSomeTransparency)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> OpacityExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::Opacity.ToString(), MaterialFactoryNode->GetUniqueID());
if (OpacityExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToOpacity(OpacityExpressionFactoryNode.Get<0>()->GetUniqueID(), OpacityExpressionFactoryNode.Get<1>());
}
bUpdateBlendMode = true;
}
}
}
if (bUpdateBlendMode)
{
UE::Interchange::InterchangeGenericMaterialPipeline::Private::UpdateBlendModeBasedOnOpacityAttributes(ShaderGraphNode, MaterialFactoryNode);
}
}
// Ambient Occlusion
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Scalar);
const bool bHasOcclusionInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::Occlusion);
if (bHasOcclusionInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::Occlusion.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToOcclusion(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
}
}
// Refraction
// probably unlikely that someone will use both at same time but to keep backwards compability IndexOfRefraction will override this one
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Scalar);
const bool bHasIorInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::Refraction);
if(bHasIorInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::Refraction.ToString(), MaterialFactoryNode->GetUniqueID());
if(ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->SetCustomRefractionMethod(ERefractionMode::RM_IndexOfRefraction);
MaterialFactoryNode->ConnectOutputToRefraction(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
}
}
// Index of Refraction (IOR)
// We'll lerp between Air IOR (1) and the IOR from the shader graph based on a fresnel, as per UE doc on refraction.
{
TGuardValue<EMaterialInputType> InputTypeBeingProcessedGuard(MaterialCreationContext.InputTypeBeingProcessed, EMaterialInputType::Scalar);
const bool bHasIorInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::IndexOfRefraction);
if (bHasIorInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::IndexOfRefraction.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->SetCustomRefractionMethod(ERefractionMode::RM_IndexOfRefraction);
UInterchangeMaterialExpressionFactoryNode* IORLerp = CreateExpressionNode(TEXT("IORLerp"), ShaderGraphNode->GetUniqueID(), UMaterialExpressionLinearInterpolate::StaticClass());
const float AirIOR = 1.f;
const FName ConstAMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionLinearInterpolate, ConstA);
IORLerp->AddFloatAttribute(ConstAMemberName.ToString(), AirIOR);
IORLerp->AddApplyAndFillDelegates<float>(ConstAMemberName.ToString(), UMaterialExpressionLinearInterpolate::StaticClass(), ConstAMemberName);
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(IORLerp, GET_MEMBER_NAME_CHECKED(UMaterialExpressionLinearInterpolate, B).ToString(),
ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
UInterchangeMaterialExpressionFactoryNode* IORFresnel = CreateExpressionNode(TEXT("IORFresnel"), ShaderGraphNode->GetUniqueID(), UMaterialExpressionFresnel::StaticClass());
UInterchangeShaderPortsAPI::ConnectDefaultOuputToInput(IORLerp, GET_MEMBER_NAME_CHECKED(UMaterialExpressionLinearInterpolate, Alpha).ToString(), IORFresnel->GetUniqueID());
MaterialFactoryNode->ConnectToRefraction(IORLerp->GetUniqueID());
}
}
}
// Displacement
{
const bool bHasDisplacementInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::Displacement);
if(bHasDisplacementInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> DisplacementExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Parameters::Displacement.ToString(), MaterialFactoryNode->GetUniqueID());
if(DisplacementExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToDisplacement(DisplacementExpressionFactoryNode.Get<0>()->GetUniqueID(), DisplacementExpressionFactoryNode.Get<1>());
}
if (float DisplacementCenter; !bOverrideDisplacement && ShaderGraphNode->GetCustomDisplacementCenterMode(DisplacementCenter))
{
MaterialFactoryNode->SetCustomDisplacementCenter(DisplacementCenter);
}
}
}
}
void UInterchangeGenericMaterialPipeline::HandleFlattenNormalNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode,
UInterchangeMaterialExpressionFactoryNode* FlattenNormalFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes::FlattenNormal;
using namespace UE::Interchange::InterchangeGenericMaterialPipeline::Private;
if (!FlattenNormalFactoryNode)
{
return;
}
const FString MaterialFunctionPath = TEXT("/Engine/Functions/Engine_MaterialFunctions01/Texturing/FlattenNormal.FlattenNormal");
UpdateFunctionCallExpression(*FlattenNormalFactoryNode, MaterialFunctionPath);
// Normal
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> NormalExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Inputs::Normal.ToString(), FlattenNormalFactoryNode->GetUniqueID());
if (NormalExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(FlattenNormalFactoryNode, TEXT("Normal"),
NormalExpression.Get<0>()->GetUniqueID(), NormalExpression.Get<1>());
}
}
// Flatness
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> FlatnessExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Inputs::Flatness.ToString(), FlattenNormalFactoryNode->GetUniqueID());
if (FlatnessExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(FlattenNormalFactoryNode, TEXT("Flatness"),
FlatnessExpression.Get<0>()->GetUniqueID(), FlatnessExpression.Get<1>());
}
}
}
void UInterchangeGenericMaterialPipeline::HandleNormalFromHeightMapNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* NormalFromHeightMapFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes::NormalFromHeightMap;
using namespace UE::Interchange::InterchangeGenericMaterialPipeline::Private;
if (!NormalFromHeightMapFactoryNode)
{
return;
}
const FString MaterialFunctionPath = TEXT("/Engine/Functions/Engine_MaterialFunctions03/Procedurals/NormalFromHeightmap.NormalFromHeightmap");
UpdateFunctionCallExpression(*NormalFromHeightMapFactoryNode, MaterialFunctionPath);
// Heightmap
{
const FString HeightMapInput = Inputs::HeightMap.ToString();
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> HeightMapExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, HeightMapInput, NormalFromHeightMapFactoryNode->GetUniqueID());
if(HeightMapExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(NormalFromHeightMapFactoryNode, HeightMapInput, HeightMapExpression.Get<0>()->GetUniqueID(), HeightMapExpression.Get<1>());
}
}
// Intensity
{
const FString Intensity = Inputs::Intensity.ToString();
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> IntensityExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Intensity, NormalFromHeightMapFactoryNode->GetUniqueID());
if(IntensityExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(NormalFromHeightMapFactoryNode, Intensity, IntensityExpression.Get<0>()->GetUniqueID(), IntensityExpression.Get<1>());
}
}
// Offset
{
const FString Offset = Inputs::Offset.ToString();
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> OffsetExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Offset, NormalFromHeightMapFactoryNode->GetUniqueID());
if(OffsetExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(NormalFromHeightMapFactoryNode, Offset, OffsetExpression.Get<0>()->GetUniqueID(), OffsetExpression.Get<1>());
}
}
// Coordinates
{
const FString Coordinates = Inputs::Coordinates.ToString();
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> CoordinatesExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Coordinates, NormalFromHeightMapFactoryNode->GetUniqueID());
if(CoordinatesExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(NormalFromHeightMapFactoryNode, Coordinates, CoordinatesExpression.Get<0>()->GetUniqueID(), CoordinatesExpression.Get<1>());
}
}
// Channel
{
const FString Channel = Inputs::Channel.ToString();
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ChannelExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Channel, NormalFromHeightMapFactoryNode->GetUniqueID());
if(ChannelExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(NormalFromHeightMapFactoryNode, Channel, ChannelExpression.Get<0>()->GetUniqueID(), ChannelExpression.Get<1>());
}
}
}
void UInterchangeGenericMaterialPipeline::HandleMakeFloat3Node(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode,
UInterchangeMaterialExpressionFactoryNode* MakeFloat3FactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes::MakeFloat3;
using namespace UE::Interchange::InterchangeGenericMaterialPipeline::Private;
if (!MakeFloat3FactoryNode)
{
return;
}
const FString MaterialFunctionPath = TEXT("/Engine/Functions/Engine_MaterialFunctions02/Utility/MakeFloat3.MakeFloat3");
UpdateFunctionCallExpression(*MakeFloat3FactoryNode, MaterialFunctionPath);
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> RedChannelExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Inputs::X.ToString(), MakeFloat3FactoryNode->GetUniqueID());
if (RedChannelExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(MakeFloat3FactoryNode, TEXT("X"),
RedChannelExpression.Get<0>()->GetUniqueID(), RedChannelExpression.Get<1>());
}
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> GreenChannelExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Inputs::Y.ToString(), MakeFloat3FactoryNode->GetUniqueID());
if (GreenChannelExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(MakeFloat3FactoryNode, TEXT("Y"),
GreenChannelExpression.Get<0>()->GetUniqueID(), GreenChannelExpression.Get<1>());
}
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> BlueChannelExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Inputs::Z.ToString(), MakeFloat3FactoryNode->GetUniqueID());
if (BlueChannelExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(MakeFloat3FactoryNode, TEXT("Z"),
BlueChannelExpression.Get<0>()->GetUniqueID(), BlueChannelExpression.Get<1>());
}
}
void UInterchangeGenericMaterialPipeline::HandleTextureNode(
const UInterchangeTextureNode* TextureNode,
UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode,
UInterchangeMaterialExpressionFactoryNode* TextureBaseFactoryNode,
const FString & ExpressionClassName,
bool bIsAParameter)
{
using namespace UE::Interchange::Materials::Standard::Nodes::TextureSample;
FString TextureFactoryUid;
TArray<FString> TextureTargetNodes;
TextureNode->GetTargetNodeUids(TextureTargetNodes);
if(TextureTargetNodes.Num() > 0)
{
TextureFactoryUid = TextureTargetNodes[0];
}
TextureBaseFactoryNode->SetCustomExpressionClassName(ExpressionClassName);
TextureBaseFactoryNode->AddStringAttribute(CreateInputKey(Inputs::Texture.ToString(), bIsAParameter), TextureFactoryUid);
if(UInterchangeTextureFactoryNode* TextureFactoryNode = Cast<UInterchangeTextureFactoryNode>(BaseNodeContainer->GetFactoryNode(TextureFactoryUid)))
{
EMaterialInputType TextureUsage = EMaterialInputType::Unknown;
TextureFactoryNode->GetAttribute(TEXT("TextureUsage"), TextureUsage);
const bool bIsOutputLinear = MaterialExpressionCreationContextStack.Top().OutputName.Equals(Outputs::A.ToString());
const EMaterialInputType DesiredTextureUsage = MaterialCreationContext.InputTypeBeingProcessed == EMaterialInputType::Scalar && bIsOutputLinear ?
EMaterialInputType::Unknown : // Alpha channels are always in linear space so ignore them when determining texture usage
MaterialCreationContext.InputTypeBeingProcessed;
if(TextureUsage == EMaterialInputType::Unknown)
{
if(DesiredTextureUsage == EMaterialInputType::Vector)
{
TextureFactoryNode->SetCustomCompressionSettings(TextureCompressionSettings::TC_Normalmap);
TextureFactoryNode->SetCustomLODGroup(TextureGroup::TEXTUREGROUP_WorldNormalMap);
}
else if(DesiredTextureUsage == EMaterialInputType::Scalar)
{
bool bSRGB;
if(!TextureNode->GetCustomSRGB(bSRGB))
{
// Only set CustomSRGB if it wasn't set by the InterchangeGenericTexturePipeline before
TextureFactoryNode->SetCustomSRGB(false);
}
}
TextureFactoryNode->SetAttribute(TEXT("TextureUsage"), DesiredTextureUsage);
}
else if(TextureUsage != DesiredTextureUsage && DesiredTextureUsage != EMaterialInputType::Unknown)
{
UInterchangeResultWarning_Generic* TextureUsageWarning = AddMessage<UInterchangeResultWarning_Generic>();
TextureUsageWarning->DestinationAssetName = TextureFactoryNode->GetAssetName();
TextureUsageWarning->AssetType = TextureFactoryNode->GetObjectClass();
TextureUsageWarning->Text = FText::Format(LOCTEXT("TextureUsageMismatch", "{0} is being used as both {1} and {2} which aren't compatible."),
FText::FromString(TextureFactoryNode->GetAssetName()), FText::FromString(LexToString(TextureUsage)), FText::FromString(LexToString(DesiredTextureUsage)));
// Flipping the green channel only makes sense for vector data as it's used to compensate for different handedness.
// Clear it if we're not gonna be used only as a vector map. This normally happens when a normal map is also used as a color map.
bool bFlipGreenChannel;
if(TextureFactoryNode->GetCustombFlipGreenChannel(bFlipGreenChannel))
{
TextureFactoryNode->SetCustombFlipGreenChannel(false);
}
}
}
}
void UInterchangeGenericMaterialPipeline::HandleTextureObjectNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* TextureObjectFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes::TextureObject;
bool bIsAParameter;
FString TextureUid = GetTextureUidAttributeFromShaderNode(ShaderNode, Inputs::Texture, bIsAParameter);
FString ExpressionClassName;
FString TextureFactoryUid;
if(const UInterchangeTextureNode* TextureNode = Cast<const UInterchangeTextureNode>(BaseNodeContainer->GetNode(TextureUid)))
{
HandleTextureNode(TextureNode, MaterialFactoryNode, TextureObjectFactoryNode, UMaterialExpressionTextureObject::StaticClass()->GetName(), bIsAParameter);
}
else
{
TextureObjectFactoryNode->SetCustomExpressionClassName(UMaterialExpressionTextureObject::StaticClass()->GetName());
TextureObjectFactoryNode->AddStringAttribute(CreateInputKey(Inputs::Texture.ToString(), bIsAParameter), TextureFactoryUid);
}
}
void UInterchangeGenericMaterialPipeline::HandleTextureSampleNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* TextureSampleFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes::TextureSample;
bool bIsAParameter;
FString TextureUid = GetTextureUidAttributeFromShaderNode(ShaderNode, Inputs::Texture, bIsAParameter);
FString ExpressionClassName;
FString TextureFactoryUid;
if (const UInterchangeTextureNode* TextureNode = Cast<const UInterchangeTextureNode>(BaseNodeContainer->GetNode(TextureUid)))
{
if (TextureNode->IsA<UInterchangeTextureCubeNode>())
{
ExpressionClassName = UMaterialExpressionTextureSampleParameterCube::StaticClass()->GetName();
}
else if (TextureNode->IsA<UInterchangeTexture2DArrayNode>())
{
ExpressionClassName = UMaterialExpressionTextureSampleParameter2DArray::StaticClass()->GetName();
}
else if(TextureNode->IsA<UInterchangeTextureBlurNode>())
{
ExpressionClassName = UMaterialExpressionMaterialXTextureSampleParameterBlur::StaticClass()->GetName();
}
else if (TextureNode->IsA<UInterchangeTexture2DNode>())
{
ExpressionClassName = UMaterialExpressionTextureSampleParameter2D::StaticClass()->GetName();
}
else
{
ExpressionClassName = UMaterialExpressionTextureSampleParameter2D::StaticClass()->GetName();
}
HandleTextureNode(TextureNode, MaterialFactoryNode, TextureSampleFactoryNode, ExpressionClassName, bIsAParameter);
}
else
{
TextureSampleFactoryNode->SetCustomExpressionClassName(UMaterialExpressionTextureSampleParameter2D::StaticClass()->GetName());
TextureSampleFactoryNode->AddStringAttribute(CreateInputKey(Inputs::Texture.ToString(), bIsAParameter), TextureFactoryUid);
}
// Coordinates
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> CoordinatesExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Inputs::Coordinates.ToString(), TextureSampleFactoryNode->GetUniqueID());
if (CoordinatesExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(TextureSampleFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionTextureSample, Coordinates).ToString(),
CoordinatesExpression.Get<0>()->GetUniqueID(), CoordinatesExpression.Get<1>());
}
}
if(ExpressionClassName == UMaterialExpressionMaterialXTextureSampleParameterBlur::StaticClass()->GetName())
{
HandleTextureSampleBlurNode(ShaderNode, MaterialFactoryNode, TextureSampleFactoryNode);
}
}
void UInterchangeGenericMaterialPipeline::HandleTextureSampleBlurNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* TextureSampleFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
// KernelSize
if(int32 KernelSize; ShaderNode->GetInt32Attribute(TextureSampleBlur::Attributes::KernelSize.ToString(), KernelSize))
{
const FName KernelSizeMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionMaterialXTextureSampleParameterBlur, KernelSize);
TextureSampleFactoryNode->AddInt32Attribute(KernelSizeMemberName.ToString(), KernelSize);
TextureSampleFactoryNode->AddApplyAndFillDelegates<int32>(KernelSizeMemberName.ToString(), UMaterialExpressionMaterialXTextureSampleParameterBlur::StaticClass(), KernelSizeMemberName);
}
// FilterSize
if(float FilterSize; ShaderNode->GetFloatAttribute(TextureSampleBlur::Attributes::FilterSize.ToString(), FilterSize))
{
const FName FilterSizeMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionMaterialXTextureSampleParameterBlur, FilterSize);
TextureSampleFactoryNode->AddFloatAttribute(FilterSizeMemberName.ToString(), FilterSize);
TextureSampleFactoryNode->AddApplyAndFillDelegates<float>(FilterSizeMemberName.ToString(), UMaterialExpressionMaterialXTextureSampleParameterBlur::StaticClass(), FilterSizeMemberName);
}
// FilterOffset
if(float FilterOffset; ShaderNode->GetFloatAttribute(TextureSampleBlur::Attributes::FilterOffset.ToString(), FilterOffset))
{
const FName FilterOffsetMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionMaterialXTextureSampleParameterBlur, FilterOffset);
TextureSampleFactoryNode->AddFloatAttribute(FilterOffsetMemberName.ToString(), FilterOffset);
TextureSampleFactoryNode->AddApplyAndFillDelegates<float>(FilterOffsetMemberName.ToString(), UMaterialExpressionMaterialXTextureSampleParameterBlur::StaticClass(), FilterOffsetMemberName);
}
// Filter
if(int32 Filter; ShaderNode->GetInt32Attribute(TextureSampleBlur::Attributes::Filter.ToString(), Filter))
{
const FName FilterMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionMaterialXTextureSampleParameterBlur, Filter);
TextureSampleFactoryNode->AddInt32Attribute(FilterMemberName.ToString(), Filter);
TextureSampleFactoryNode->AddApplyAndFillDelegates<int32>(FilterMemberName.ToString(), UMaterialExpressionMaterialXTextureSampleParameterBlur::StaticClass(), FilterMemberName);
}
}
void UInterchangeGenericMaterialPipeline::HandleTextureCoordinateNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode,
UInterchangeMaterialExpressionFactoryNode*& TexCoordFactoryNode)
{
using namespace UE::Interchange::Materials::Standard;
using namespace UE::Interchange::InterchangeGenericMaterialPipeline::Private;
TexCoordFactoryNode->SetCustomExpressionClassName(UMaterialExpressionTextureCoordinate::StaticClass()->GetName());
// Index
{
int32 CoordIndex;
if (ShaderNode->GetInt32Attribute(UInterchangeShaderPortsAPI::MakeInputValueKey(Nodes::TextureCoordinate::Inputs::Index.ToString()), CoordIndex))
{
const FName CoordinateIndexMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionTextureCoordinate, CoordinateIndex);
TexCoordFactoryNode->AddInt32Attribute(CoordinateIndexMemberName.ToString(), CoordIndex);
TexCoordFactoryNode->AddApplyAndFillDelegates<int32>(CoordinateIndexMemberName.ToString(), UMaterialExpressionTextureCoordinate::StaticClass(), CoordinateIndexMemberName);
}
}
// U tiling
{
if (float UTilingValue; ShaderNode->GetFloatAttribute(UInterchangeShaderPortsAPI::MakeInputValueKey(Nodes::TextureCoordinate::Inputs::UTiling.ToString()), UTilingValue))
{
const FName UTilingMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionTextureCoordinate, UTiling);
TexCoordFactoryNode->AddFloatAttribute(UTilingMemberName.ToString(), UTilingValue);
TexCoordFactoryNode->AddApplyAndFillDelegates<float>(UTilingMemberName.ToString(), UMaterialExpressionTextureCoordinate::StaticClass(), UTilingMemberName);
}
}
// V tiling
{
if(float VTilingValue; ShaderNode->GetFloatAttribute(UInterchangeShaderPortsAPI::MakeInputValueKey(Nodes::TextureCoordinate::Inputs::UTiling.ToString()), VTilingValue))
{
const FName VTilingMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionTextureCoordinate, VTiling);
TexCoordFactoryNode->AddFloatAttribute(VTilingMemberName.ToString(), VTilingValue);
TexCoordFactoryNode->AddApplyAndFillDelegates<float>(VTilingMemberName.ToString(), UMaterialExpressionTextureCoordinate::StaticClass(), VTilingMemberName);
}
}
// Scale
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ScaleExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Nodes::TextureCoordinate::Inputs::Scale.ToString(), TexCoordFactoryNode->GetUniqueID());
if (ScaleExpression.Get<0>())
{
UInterchangeMaterialExpressionFactoryNode* MultiplyExpression =
CreateExpressionNode(ScaleExpression.Get<0>()->GetDisplayLabel() + TEXT("_Multiply"), TexCoordFactoryNode->GetUniqueID(), UMaterialExpressionMultiply::StaticClass());
UInterchangeShaderPortsAPI::ConnectDefaultOuputToInput(MultiplyExpression, GET_MEMBER_NAME_CHECKED(UMaterialExpressionMultiply, A).ToString(),
TexCoordFactoryNode->GetUniqueID());
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(MultiplyExpression, GET_MEMBER_NAME_CHECKED(UMaterialExpressionMultiply, B).ToString(),
ScaleExpression.Get<0>()->GetUniqueID(), ScaleExpression.Get<1>());
TexCoordFactoryNode = MultiplyExpression;
}
}
// Rotate
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> RotateExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Nodes::TextureCoordinate::Inputs::Rotate.ToString(), TexCoordFactoryNode->GetUniqueID());
if (RotateExpression.Get<0>())
{
UInterchangeMaterialExpressionFactoryNode* CallRotatorExpression =
CreateExpressionNode(RotateExpression.Get<0>()->GetDisplayLabel() + TEXT("_Rotator"), TexCoordFactoryNode->GetUniqueID(), UMaterialExpressionMaterialFunctionCall::StaticClass());
const FString MaterialFunctionPath = TEXT("/Engine/Functions/Engine_MaterialFunctions02/Texturing/CustomRotator.CustomRotator");
UpdateFunctionCallExpression(*CallRotatorExpression, MaterialFunctionPath);
UInterchangeShaderPortsAPI::ConnectDefaultOuputToInput(CallRotatorExpression, TEXT("UVs"), TexCoordFactoryNode->GetUniqueID());
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(CallRotatorExpression, TEXT("Rotation Angle (0-1)"), RotateExpression.Get<0>()->GetUniqueID(), RotateExpression.Get<1>());
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> RotationCenterExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Nodes::TextureCoordinate::Inputs::RotationCenter.ToString(), TexCoordFactoryNode->GetUniqueID());
if (RotationCenterExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(CallRotatorExpression, TEXT("Rotation Center"), RotationCenterExpression.Get<0>()->GetUniqueID(), RotationCenterExpression.Get<1>());
}
TexCoordFactoryNode = CallRotatorExpression;
}
}
// Offset
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> OffsetExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Nodes::TextureCoordinate::Inputs::Offset.ToString(), TexCoordFactoryNode->GetUniqueID());
if (OffsetExpression.Get<0>())
{
UInterchangeMaterialExpressionFactoryNode* AddExpression =
CreateExpressionNode(OffsetExpression.Get<0>()->GetDisplayLabel() + TEXT("_Add"), TexCoordFactoryNode->GetUniqueID(), UMaterialExpressionAdd::StaticClass());
UInterchangeShaderPortsAPI::ConnectDefaultOuputToInput(AddExpression, GET_MEMBER_NAME_CHECKED(UMaterialExpressionAdd, A).ToString(),
TexCoordFactoryNode->GetUniqueID());
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(AddExpression, GET_MEMBER_NAME_CHECKED(UMaterialExpressionAdd, B).ToString(),
OffsetExpression.Get<0>()->GetUniqueID(), OffsetExpression.Get<1>());
TexCoordFactoryNode = AddExpression;
}
}
}
void UInterchangeGenericMaterialPipeline::HandleLerpNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* LerpFactoryNode)
{
using namespace UE::Interchange::Materials::Standard;
LerpFactoryNode->SetCustomExpressionClassName(UMaterialExpressionLinearInterpolate::StaticClass()->GetName());
// A
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ColorAExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Nodes::Lerp::Inputs::A.ToString(), LerpFactoryNode->GetUniqueID());
if (ColorAExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(LerpFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionLinearInterpolate, A).ToString(),
ColorAExpression.Get<0>()->GetUniqueID(), ColorAExpression.Get<1>());
}
}
// B
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ColorBExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Nodes::Lerp::Inputs::B.ToString(), LerpFactoryNode->GetUniqueID());
if (ColorBExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(LerpFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionLinearInterpolate, B).ToString(),
ColorBExpression.Get<0>()->GetUniqueID(), ColorBExpression.Get<1>());
}
}
// Factor
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> FactorExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Nodes::Lerp::Inputs::Factor.ToString(), LerpFactoryNode->GetUniqueID());
if (FactorExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(LerpFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionLinearInterpolate, Alpha).ToString(),
FactorExpression.Get<0>()->GetUniqueID(), FactorExpression.Get<1>());
}
}
}
void UInterchangeGenericMaterialPipeline::HandleMaskNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* MaskFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
MaskFactoryNode->SetCustomExpressionClassName(UMaterialExpressionComponentMask::StaticClass()->GetName());
bool bRChannel = false;
ShaderNode->GetBooleanAttribute(Mask::Attributes::R.ToString(), bRChannel);
bool bGChannel = false;
ShaderNode->GetBooleanAttribute(Mask::Attributes::G.ToString(), bGChannel);
bool bBChannel = false;
ShaderNode->GetBooleanAttribute(Mask::Attributes::B.ToString(), bBChannel);
bool bAChannel = false;
ShaderNode->GetBooleanAttribute(Mask::Attributes::A.ToString(), bAChannel);
bool bIsAnyMaskChannelSet = bRChannel || bGChannel || bBChannel || bAChannel;
if(bIsAnyMaskChannelSet)
{
// R
{
const FName RMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionComponentMask, R);
MaskFactoryNode->AddBooleanAttribute(RMemberName.ToString(), bRChannel);
MaskFactoryNode->AddApplyAndFillDelegates<bool>(RMemberName.ToString(), UMaterialExpressionComponentMask::StaticClass(), RMemberName);
}
// G
{
const FName GMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionComponentMask, G);
MaskFactoryNode->AddBooleanAttribute(GMemberName.ToString(), bGChannel);
MaskFactoryNode->AddApplyAndFillDelegates<bool>(GMemberName.ToString(), UMaterialExpressionComponentMask::StaticClass(), GMemberName);
}
// B
{
const FName BMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionComponentMask, B);
MaskFactoryNode->AddBooleanAttribute(BMemberName.ToString(), bBChannel);
MaskFactoryNode->AddApplyAndFillDelegates<bool>(BMemberName.ToString(), UMaterialExpressionComponentMask::StaticClass(), BMemberName);
}
// A
{
const FName AMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionComponentMask, A);
MaskFactoryNode->AddBooleanAttribute(AMemberName.ToString(), bAChannel);
MaskFactoryNode->AddApplyAndFillDelegates<bool>(AMemberName.ToString(), UMaterialExpressionComponentMask::StaticClass(), AMemberName);
}
}
// Input
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> InputExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Mask::Inputs::Input.ToString(), MaskFactoryNode->GetUniqueID());
if(InputExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(MaskFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionComponentMask, Input).ToString(),
InputExpression.Get<0>()->GetUniqueID(), InputExpression.Get<1>());
}
}
}
void UInterchangeGenericMaterialPipeline::HandleRotatorNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* RotatorFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
RotatorFactoryNode->SetCustomExpressionClassName(UMaterialExpressionRotator::StaticClass()->GetName());
// Coordinate
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> CoordinateExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Rotator::Inputs::Coordinate.ToString(), RotatorFactoryNode->GetUniqueID());
if(CoordinateExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(RotatorFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionRotator, Coordinate).ToString(),
CoordinateExpression.Get<0>()->GetUniqueID(), CoordinateExpression.Get<1>());
}
}
// Time
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> TimeExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Rotator::Inputs::Time.ToString(), RotatorFactoryNode->GetUniqueID());
if(TimeExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(RotatorFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionRotator, Time).ToString(),
TimeExpression.Get<0>()->GetUniqueID(), TimeExpression.Get<1>());
}
}
// CenterX
if(float CenterX; ShaderNode->GetFloatAttribute(Rotator::Attributes::CenterX.ToString(), CenterX))
{
const FName CenterXMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionRotator, CenterX);
RotatorFactoryNode->AddFloatAttribute(CenterXMemberName.ToString(), CenterX);
RotatorFactoryNode->AddApplyAndFillDelegates<float>(CenterXMemberName.ToString(), UMaterialExpressionRotator::StaticClass(), CenterXMemberName);
}
// CenterY
if(float CenterY; ShaderNode->GetFloatAttribute(Rotator::Attributes::CenterY.ToString(), CenterY))
{
const FName CenterYMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionRotator, CenterY);
RotatorFactoryNode->AddFloatAttribute(CenterYMemberName.ToString(), CenterY);
RotatorFactoryNode->AddApplyAndFillDelegates<float>(CenterYMemberName.ToString(), UMaterialExpressionRotator::StaticClass(), CenterYMemberName);
}
// Speed
if(float Speed; ShaderNode->GetFloatAttribute(Rotator::Attributes::Speed.ToString(), Speed))
{
const FName SpeedMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionRotator, Speed);
RotatorFactoryNode->AddFloatAttribute(SpeedMemberName.ToString(), Speed);
RotatorFactoryNode->AddApplyAndFillDelegates<float>(SpeedMemberName.ToString(), UMaterialExpressionRotator::StaticClass(), SpeedMemberName);
}
}
void UInterchangeGenericMaterialPipeline::HandleRotateAboutAxisNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* RotateAboutAxisFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
RotateAboutAxisFactoryNode->SetCustomExpressionClassName(UMaterialExpressionRotateAboutAxis::StaticClass()->GetName());
// NormalizedRotationAxis
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> NormalizedRotationAxisExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, RotateAboutAxis::Inputs::NormalizedRotationAxis.ToString(), RotateAboutAxisFactoryNode->GetUniqueID());
if (NormalizedRotationAxisExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(RotateAboutAxisFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionRotateAboutAxis, NormalizedRotationAxis).ToString(),
NormalizedRotationAxisExpression.Get<0>()->GetUniqueID(), NormalizedRotationAxisExpression.Get<1>());
}
}
// PivotPoint
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> RotateAboutAxisExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, RotateAboutAxis::Inputs::PivotPoint.ToString(), RotateAboutAxisFactoryNode->GetUniqueID());
if (RotateAboutAxisExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(RotateAboutAxisFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionRotateAboutAxis, PivotPoint).ToString(),
RotateAboutAxisExpression.Get<0>()->GetUniqueID(), RotateAboutAxisExpression.Get<1>());
}
}
// Position
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> PositionExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, RotateAboutAxis::Inputs::Position.ToString(), RotateAboutAxisFactoryNode->GetUniqueID());
if (PositionExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(RotateAboutAxisFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionRotateAboutAxis, Position).ToString(),
PositionExpression.Get<0>()->GetUniqueID(), PositionExpression.Get<1>());
}
}
// RotationAngle
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> RotationAngleExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, RotateAboutAxis::Inputs::RotationAngle.ToString(), RotateAboutAxisFactoryNode->GetUniqueID());
if (RotationAngleExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(RotateAboutAxisFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionRotateAboutAxis, RotationAngle).ToString(),
RotationAngleExpression.Get<0>()->GetUniqueID(), RotationAngleExpression.Get<1>());
}
}
// Period (By default the period is 1, but the angle is in radians, let's default it to 2Pi
{
float Period = 2.f * UE_PI;
ShaderNode->GetFloatAttribute(RotateAboutAxis::Attributes::Period.ToString(), Period);
const FName PeriodMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionRotateAboutAxis, Period);
RotateAboutAxisFactoryNode->AddFloatAttribute(PeriodMemberName.ToString(), Period);
RotateAboutAxisFactoryNode->AddApplyAndFillDelegates<float>(PeriodMemberName.ToString(), UMaterialExpressionRotateAboutAxis::StaticClass(), PeriodMemberName);
}
}
void UInterchangeGenericMaterialPipeline::HandleTimeNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* TimeFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
TimeFactoryNode->SetCustomExpressionClassName(UMaterialExpressionTime::StaticClass()->GetName());
// IgnorePause
if(bool bIgnorePause; ShaderNode->GetBooleanAttribute(Time::Attributes::IgnorePause.ToString(), bIgnorePause))
{
const FName IgnorePauseMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionTime, bIgnorePause);
TimeFactoryNode->AddBooleanAttribute(IgnorePauseMemberName.ToString(), bIgnorePause);
TimeFactoryNode->AddApplyAndFillDelegates<bool>(IgnorePauseMemberName.ToString(), UMaterialExpressionTime::StaticClass(), IgnorePauseMemberName);
}
// OverridePeriod
if(bool bOverridePeriod; ShaderNode->GetBooleanAttribute(Time::Attributes::OverridePeriod.ToString(), bOverridePeriod))
{
const FName OverridePeriodMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionTime, bOverride_Period);
TimeFactoryNode->AddBooleanAttribute(OverridePeriodMemberName.ToString(), bOverridePeriod);
TimeFactoryNode->AddApplyAndFillDelegates<bool>(OverridePeriodMemberName.ToString(), UMaterialExpressionTime::StaticClass(), OverridePeriodMemberName);
}
// Period
if(float Period; ShaderNode->GetFloatAttribute(Time::Attributes::Period.ToString(), Period))
{
const FName PeriodMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionTime, Period);
TimeFactoryNode->AddFloatAttribute(PeriodMemberName.ToString(), Period);
TimeFactoryNode->AddApplyAndFillDelegates<float>(PeriodMemberName.ToString(), UMaterialExpressionTime::StaticClass(), PeriodMemberName);
}
}
void UInterchangeGenericMaterialPipeline::HandleTransformPositionNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* TransformPositionFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
TransformPositionFactoryNode->SetCustomExpressionClassName(UMaterialExpressionTransformPosition::StaticClass()->GetName());
// Input
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> InputExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, TransformPosition::Inputs::Input.ToString(), TransformPositionFactoryNode->GetUniqueID());
if(InputExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(TransformPositionFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionTransformPosition, Input).ToString(),
InputExpression.Get<0>()->GetUniqueID(), InputExpression.Get<1>());
}
}
// TransformSourceType
if(int32 TransformSourceType; ShaderNode->GetInt32Attribute(TransformPosition::Attributes::TransformSourceType.ToString(), TransformSourceType))
{
const FName TransformSourceTypeMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionTransformPosition, TransformSourceType);
TransformPositionFactoryNode->AddInt32Attribute(TransformSourceTypeMemberName.ToString(),TransformSourceType);
TransformPositionFactoryNode->AddApplyAndFillDelegates<int32>(TransformSourceTypeMemberName.ToString(), UMaterialExpressionTransformPosition::StaticClass(), TransformSourceTypeMemberName);
}
// TransformType
if(int32 TransformType; ShaderNode->GetInt32Attribute(TransformPosition::Attributes::TransformType.ToString(), TransformType))
{
const FName TransformTypeMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionTransformPosition, TransformType);
TransformPositionFactoryNode->AddInt32Attribute(TransformTypeMemberName.ToString(), TransformType);
TransformPositionFactoryNode->AddApplyAndFillDelegates<int32>(TransformTypeMemberName.ToString(), UMaterialExpressionTransformPosition::StaticClass(), TransformTypeMemberName);
}
}
void UInterchangeGenericMaterialPipeline::HandleTransformVectorNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* TransformVectorFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
TransformVectorFactoryNode->SetCustomExpressionClassName(UMaterialExpressionTransform::StaticClass()->GetName());
// Input
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> InputExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, TransformVector::Inputs::Input.ToString(), TransformVectorFactoryNode->GetUniqueID());
if(InputExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(TransformVectorFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionTransform, Input).ToString(),
InputExpression.Get<0>()->GetUniqueID(), InputExpression.Get<1>());
}
}
// TransformSourceType
if(int32 TransformSourceType; ShaderNode->GetInt32Attribute(TransformVector::Attributes::TransformSourceType.ToString(), TransformSourceType))
{
const FName TransformSourceTypeMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionTransform, TransformSourceType);
TransformVectorFactoryNode->AddInt32Attribute(TransformSourceTypeMemberName.ToString(), TransformSourceType);
TransformVectorFactoryNode->AddApplyAndFillDelegates<int32>(TransformSourceTypeMemberName.ToString(), UMaterialExpressionTransform::StaticClass(), TransformSourceTypeMemberName);
}
// TransformType
if(int32 TransformType; ShaderNode->GetInt32Attribute(TransformVector::Attributes::TransformType.ToString(), TransformType))
{
const FName TransformTypeMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionTransform, TransformType);
TransformVectorFactoryNode->AddInt32Attribute(TransformTypeMemberName.ToString(), TransformType);
TransformVectorFactoryNode->AddApplyAndFillDelegates<int32>(TransformTypeMemberName.ToString(), UMaterialExpressionTransform::StaticClass(), TransformTypeMemberName);
}
}
void UInterchangeGenericMaterialPipeline::HandleNoiseNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* NoiseFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
NoiseFactoryNode->SetCustomExpressionClassName(UMaterialExpressionNoise::StaticClass()->GetName());
// Position
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> PositionExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Noise::Inputs::Position.ToString(), NoiseFactoryNode->GetUniqueID());
if(PositionExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(NoiseFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, Position).ToString(),
PositionExpression.Get<0>()->GetUniqueID(), PositionExpression.Get<1>());
}
}
// FilterWidth
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> FilterWidthExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Noise::Inputs::FilterWidth.ToString(), NoiseFactoryNode->GetUniqueID());
if(FilterWidthExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(NoiseFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, FilterWidth).ToString(),
FilterWidthExpression.Get<0>()->GetUniqueID(), FilterWidthExpression.Get<1>());
}
}
// Scale
if(float Scale; ShaderNode->GetFloatAttribute(Noise::Attributes::Scale.ToString(), Scale))
{
const FName ScaleMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, Scale);
NoiseFactoryNode->AddFloatAttribute(ScaleMemberName.ToString(), Scale);
NoiseFactoryNode->AddApplyAndFillDelegates<float>(ScaleMemberName.ToString(), UMaterialExpressionNoise::StaticClass(), ScaleMemberName);
}
// Quality
if(int32 Quality; ShaderNode->GetInt32Attribute(Noise::Attributes::Quality.ToString(), Quality))
{
const FName QualityMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, Quality);
NoiseFactoryNode->AddInt32Attribute(QualityMemberName.ToString(), Quality);
NoiseFactoryNode->AddApplyAndFillDelegates<int32>(QualityMemberName.ToString(), UMaterialExpressionNoise::StaticClass(), QualityMemberName);
}
// Noise Function
if(int32 NoiseFunction; ShaderNode->GetInt32Attribute(Noise::Attributes::Function.ToString(), NoiseFunction))
{
const FName NoiseFunctionMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, NoiseFunction);
NoiseFactoryNode->AddInt32Attribute(NoiseFunctionMemberName.ToString(), NoiseFunction);
NoiseFactoryNode->AddApplyAndFillDelegates<int32>(NoiseFunctionMemberName.ToString(), UMaterialExpressionNoise::StaticClass(), NoiseFunctionMemberName);
}
// Turbulence
if(bool bTurbulence; ShaderNode->GetBooleanAttribute(Noise::Attributes::Turbulence.ToString(), bTurbulence))
{
const FName TurbulenceMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, bTurbulence);
NoiseFactoryNode->AddBooleanAttribute(TurbulenceMemberName.ToString(), bTurbulence);
NoiseFactoryNode->AddApplyAndFillDelegates<bool>(TurbulenceMemberName.ToString(), UMaterialExpressionNoise::StaticClass(), TurbulenceMemberName);
}
// Levels
if(int32 Levels; ShaderNode->GetInt32Attribute(Noise::Attributes::Levels.ToString(), Levels))
{
const FName LevelsMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, Levels);
NoiseFactoryNode->AddInt32Attribute(LevelsMemberName.ToString(), Levels);
NoiseFactoryNode->AddApplyAndFillDelegates<int32>(LevelsMemberName.ToString(), UMaterialExpressionNoise::StaticClass(), LevelsMemberName);
}
// Output Min
if(float OutputMin; ShaderNode->GetFloatAttribute(Noise::Attributes::OutputMin.ToString(), OutputMin))
{
const FName OutputMinMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, OutputMin);
NoiseFactoryNode->AddFloatAttribute(OutputMinMemberName.ToString(), OutputMin);
NoiseFactoryNode->AddApplyAndFillDelegates<float>(OutputMinMemberName.ToString(), UMaterialExpressionNoise::StaticClass(), OutputMinMemberName);
}
// Output Max
if(float OutputMax; ShaderNode->GetFloatAttribute(Noise::Attributes::OutputMax.ToString(), OutputMax))
{
const FName OutputMaxMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, OutputMax);
NoiseFactoryNode->AddFloatAttribute(OutputMaxMemberName.ToString(), OutputMax);
NoiseFactoryNode->AddApplyAndFillDelegates<float>(OutputMaxMemberName.ToString(), UMaterialExpressionNoise::StaticClass(), OutputMaxMemberName);
}
// Level Scale
if(float LevelScale; ShaderNode->GetFloatAttribute(Noise::Attributes::LevelScale.ToString(), LevelScale))
{
const FName LevelScaleMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, LevelScale);
NoiseFactoryNode->AddFloatAttribute(LevelScaleMemberName.ToString(), LevelScale);
NoiseFactoryNode->AddApplyAndFillDelegates<float>(LevelScaleMemberName.ToString(), UMaterialExpressionNoise::StaticClass(), LevelScaleMemberName);
}
// Tiling
if(bool bTiling; ShaderNode->GetBooleanAttribute(Noise::Attributes::Tiling.ToString(), bTiling))
{
const FName TilingMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, bTiling);
NoiseFactoryNode->AddBooleanAttribute(TilingMemberName.ToString(), bTiling);
NoiseFactoryNode->AddApplyAndFillDelegates<bool>(TilingMemberName.ToString(), UMaterialExpressionNoise::StaticClass(), TilingMemberName);
}
// Levels
if(int32 RepeatSize; ShaderNode->GetInt32Attribute(Noise::Attributes::RepeatSize.ToString(), RepeatSize))
{
const FName RepeatSizeMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionNoise, RepeatSize);
NoiseFactoryNode->AddInt32Attribute(RepeatSizeMemberName.ToString(), RepeatSize);
NoiseFactoryNode->AddApplyAndFillDelegates<int32>(RepeatSizeMemberName.ToString(), UMaterialExpressionNoise::StaticClass(), RepeatSizeMemberName);
}
}
void UInterchangeGenericMaterialPipeline::HandleVectorNoiseNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* NoiseFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
NoiseFactoryNode->SetCustomExpressionClassName(UMaterialExpressionVectorNoise::StaticClass()->GetName());
// Position
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> PositionExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, VectorNoise::Inputs::Position.ToString(), NoiseFactoryNode->GetUniqueID());
if(PositionExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(NoiseFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionVectorNoise, Position).ToString(),
PositionExpression.Get<0>()->GetUniqueID(), PositionExpression.Get<1>());
}
}
// Noise Function
if(int32 NoiseFunction; ShaderNode->GetInt32Attribute(VectorNoise::Attributes::Function.ToString(), NoiseFunction))
{
const FName NoiseFunctionMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionVectorNoise, NoiseFunction);
NoiseFactoryNode->AddInt32Attribute(NoiseFunctionMemberName.ToString(), NoiseFunction);
NoiseFactoryNode->AddApplyAndFillDelegates<int32>(NoiseFunctionMemberName.ToString(), UMaterialExpressionVectorNoise::StaticClass(), NoiseFunctionMemberName);
}
// Quality
if(int32 Quality; ShaderNode->GetInt32Attribute(VectorNoise::Attributes::Quality.ToString(), Quality))
{
const FName QualityMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionVectorNoise, Quality);
NoiseFactoryNode->AddInt32Attribute(QualityMemberName.ToString(), Quality);
NoiseFactoryNode->AddApplyAndFillDelegates<int32>(QualityMemberName.ToString(), UMaterialExpressionVectorNoise::StaticClass(), QualityMemberName);
}
// Tiling
if(bool bTiling; ShaderNode->GetBooleanAttribute(VectorNoise::Attributes::Tiling.ToString(), bTiling))
{
const FName TilingMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionVectorNoise, bTiling);
NoiseFactoryNode->AddBooleanAttribute(TilingMemberName.ToString(), bTiling);
NoiseFactoryNode->AddApplyAndFillDelegates<bool>(TilingMemberName.ToString(), UMaterialExpressionVectorNoise::StaticClass(), TilingMemberName);
}
// Tile Size
if(int32 TileSize; ShaderNode->GetInt32Attribute(VectorNoise::Attributes::Function.ToString(), TileSize))
{
const FName TileSizeMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionVectorNoise, TileSize);
NoiseFactoryNode->AddInt32Attribute(TileSizeMemberName.ToString(), TileSize);
NoiseFactoryNode->AddApplyAndFillDelegates<int32>(TileSizeMemberName.ToString(), UMaterialExpressionVectorNoise::StaticClass(), TileSizeMemberName);
}
}
void UInterchangeGenericMaterialPipeline::HandleSwizzleNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* SwizzleFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
SwizzleFactoryNode->SetCustomExpressionClassName(UMaterialExpressionMaterialXSwizzle::StaticClass()->GetName());
// Input
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> InputExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Swizzle::Inputs::Input.ToString(), SwizzleFactoryNode->GetUniqueID());
if(InputExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(SwizzleFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionMaterialXSwizzle, Input).ToString(),
InputExpression.Get<0>()->GetUniqueID(), InputExpression.Get<1>());
}
}
// Channels
if(FString Channels; ShaderNode->GetStringAttribute(Swizzle::Attributes::Channels.ToString(), Channels))
{
const FName ChannelsMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionMaterialXSwizzle, Channels);
SwizzleFactoryNode->AddStringAttribute(ChannelsMemberName.ToString(), Channels);
SwizzleFactoryNode->AddApplyAndFillDelegates<FString>(ChannelsMemberName.ToString(), UMaterialExpressionMaterialXSwizzle::StaticClass(), ChannelsMemberName);
}
}
void UInterchangeGenericMaterialPipeline::HandleSwitchNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* SwitchFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
SwitchFactoryNode->SetCustomExpressionClassName(UMaterialExpressionSwitch::StaticClass()->GetName());
// SwitchValue
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> SwitchValueExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Switch::Inputs::Value.ToString(), SwitchFactoryNode->GetUniqueID());
if (SwitchValueExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(SwitchFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionSwitch, SwitchValue).ToString(),
SwitchValueExpression.Get<0>()->GetUniqueID(), SwitchValueExpression.Get<1>());
}
}
// Default
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> DefaultExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Switch::Inputs::Default.ToString(), SwitchFactoryNode->GetUniqueID());
if (DefaultExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(SwitchFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionSwitch, Default).ToString(),
DefaultExpression.Get<0>()->GetUniqueID(), DefaultExpression.Get<1>());
}
}
// Inputs
if (int32 InputCount; ShaderNode->GetInt32Attribute(Switch::Attributes::InputCount.ToString(), InputCount))
{
SwitchFactoryNode->AddInt32Attribute(Switch::Attributes::InputCount.ToString(), InputCount);
for (int32 Index = 0; Index < InputCount; ++Index)
{
if (FString InputName; ShaderNode->GetStringAttribute(Switch::Inputs::InputName.ToString() + FString::FromInt(Index), InputName))
{
SwitchFactoryNode->AddStringAttribute(Switch::Inputs::InputName.ToString() + FString::FromInt(Index), InputName);
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> InputExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, InputName, SwitchFactoryNode->GetUniqueID());
if (InputExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(SwitchFactoryNode, InputName, InputExpression.Get<0>()->GetUniqueID(), InputExpression.Get<1>());
}
}
}
}
}
void UInterchangeGenericMaterialPipeline::HandleSlabBSDFNode(const UInterchangeShaderNode* ShaderNode, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* SlabBSDFFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
using namespace UE::Interchange::InterchangeGenericMaterialPipeline::Private;
SlabBSDFFactoryNode->SetCustomExpressionClassName(UMaterialExpressionSubstrateSlabBSDF::StaticClass()->GetName());
// ensure that SlabBSDF expression have the same members define as Interchange;
#define SubstrateSlabBSDF_MEMBER_CHECKED(X) GET_MEMBER_NAME_STRING_CHECKED(UMaterialExpressionSubstrateSlabBSDF, X), SlabBSDF::Inputs::X.ToString()
const TSet<FString> SlabInputs{
SubstrateSlabBSDF_MEMBER_CHECKED(Anisotropy),
SubstrateSlabBSDF_MEMBER_CHECKED(DiffuseAlbedo),
SubstrateSlabBSDF_MEMBER_CHECKED(EmissiveColor),
SubstrateSlabBSDF_MEMBER_CHECKED(F0),
SubstrateSlabBSDF_MEMBER_CHECKED(F90),
SubstrateSlabBSDF_MEMBER_CHECKED(FuzzAmount),
SubstrateSlabBSDF_MEMBER_CHECKED(FuzzColor),
SubstrateSlabBSDF_MEMBER_CHECKED(FuzzRoughness),
SubstrateSlabBSDF_MEMBER_CHECKED(GlintUV),
SubstrateSlabBSDF_MEMBER_CHECKED(GlintValue),
SubstrateSlabBSDF_MEMBER_CHECKED(Normal),
SubstrateSlabBSDF_MEMBER_CHECKED(Roughness),
SubstrateSlabBSDF_MEMBER_CHECKED(SecondRoughness),
SubstrateSlabBSDF_MEMBER_CHECKED(SecondRoughnessWeight),
SubstrateSlabBSDF_MEMBER_CHECKED(SSSMFP),
SubstrateSlabBSDF_MEMBER_CHECKED(SSSMFPScale),
SubstrateSlabBSDF_MEMBER_CHECKED(SSSPhaseAnisotropy),
SubstrateSlabBSDF_MEMBER_CHECKED(Tangent),
};
#undef SubstrateSlabBSDF_MEMBER_CHECKED
TArray<FString> Inputs;
UInterchangeShaderPortsAPI::GatherInputs(ShaderNode, Inputs);
for (const FString& InputName : Inputs)
{
if(SlabInputs.Contains(InputName))
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> InputExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, InputName, SlabBSDFFactoryNode->GetUniqueID());
if (InputExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(SlabBSDFFactoryNode, InputName, InputExpression.Get<0>()->GetUniqueID(), InputExpression.Get<1>());
}
}
}
// Specular Profile
AddSpecularProfileToFactoryNode(ShaderNode, SlabBSDFFactoryNode, BaseNodeContainer);
}
/** Trigonometry expressions are unitless and sets the period to 1 by default to let the user sets their period*/
void UInterchangeGenericMaterialPipeline::HandleTrigonometryNode(const UInterchangeShaderNode* ShaderNode, UClass* StaticClass, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, UInterchangeMaterialExpressionFactoryNode* TrigonometryFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
using StringView =
#if PLATFORM_WIDECHAR_IS_CHAR16
std::u16string_view;
#else
std::wstring_view;
#endif
// ensure that trig expressions have the Input and Period members;
static_assert(StringView(GET_MEMBER_NAME_STRING_CHECKED(UMaterialExpressionSine, Input)) == StringView(GET_MEMBER_NAME_STRING_CHECKED(UMaterialExpressionCosine, Input)));
static_assert(StringView(GET_MEMBER_NAME_STRING_CHECKED(UMaterialExpressionSine, Input)) == StringView(GET_MEMBER_NAME_STRING_CHECKED(UMaterialExpressionTangent, Input)));
static_assert(StringView(GET_MEMBER_NAME_STRING_CHECKED(UMaterialExpressionSine, Period)) == StringView(GET_MEMBER_NAME_STRING_CHECKED(UMaterialExpressionCosine, Period)));
static_assert(StringView(GET_MEMBER_NAME_STRING_CHECKED(UMaterialExpressionSine, Period)) == StringView(GET_MEMBER_NAME_STRING_CHECKED(UMaterialExpressionTangent, Period)));
TrigonometryFactoryNode->SetCustomExpressionClassName(StaticClass->GetName());
// Input
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> InputExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, Trigonometry::Inputs::Input.ToString(), TrigonometryFactoryNode->GetUniqueID());
if (InputExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(TrigonometryFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionSine, Input).ToString(),
InputExpression.Get<0>()->GetUniqueID(), InputExpression.Get<1>());
}
}
// Period (should be 2Pi, unlike sine, a period of 0 always return cos(0) / tan(0) instead of cos(Input)/tan(Input), to be consistent over all 3, let's just set by default 2Pi
{
float Period = 2.f * UE_PI;
ShaderNode->GetFloatAttribute(Trigonometry::Attributes::Period.ToString(), Period);
const FName PeriodMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionSine, Period);
TrigonometryFactoryNode->AddFloatAttribute(PeriodMemberName.ToString(), Period);
TrigonometryFactoryNode->AddApplyAndFillDelegates<float>(PeriodMemberName.ToString(), StaticClass, PeriodMemberName);
}
}
void UInterchangeGenericMaterialPipeline::HandleScalarParameterNode(const UInterchangeShaderNode* ShaderNode, UInterchangeMaterialExpressionFactoryNode* ScalarParameterFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
const FString ParameterKey = UInterchangeShaderPortsAPI::MakeInputParameterKey(ScalarParameter::Attributes::DefaultValue.ToString());
float InputValue;
if (ShaderNode->GetFloatAttribute(ParameterKey, InputValue))
{
ScalarParameterFactoryNode->SetCustomExpressionClassName(UMaterialExpressionScalarParameter::StaticClass()->GetName());
const FName DefaultValueMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionScalarParameter, DefaultValue);
ScalarParameterFactoryNode->AddFloatAttribute(DefaultValueMemberName.ToString(), InputValue);
ScalarParameterFactoryNode->AddApplyAndFillDelegates<float>(DefaultValueMemberName.ToString(), UMaterialExpressionScalarParameter::StaticClass(), DefaultValueMemberName);
}
ScalarParameterFactoryNode->SetDisplayLabel(ShaderNode->GetDisplayLabel());
}
void UInterchangeGenericMaterialPipeline::HandleVectorParameterNode(const UInterchangeShaderNode* ShaderNode, UInterchangeMaterialExpressionFactoryNode* VectorParameterFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
const FString ParameterKey = UInterchangeShaderPortsAPI::MakeInputParameterKey(VectorParameter::Attributes::DefaultValue.ToString());
FLinearColor InputValue;
if (ShaderNode->GetLinearColorAttribute(ParameterKey, InputValue))
{
VectorParameterFactoryNode->SetCustomExpressionClassName(UMaterialExpressionVectorParameter::StaticClass()->GetName());
const FName DefaultValueMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionVectorParameter, DefaultValue);
VectorParameterFactoryNode->AddLinearColorAttribute(DefaultValueMemberName.ToString(), InputValue);
VectorParameterFactoryNode->AddApplyAndFillDelegates<FLinearColor>(DefaultValueMemberName.ToString(), UMaterialExpressionVectorParameter::StaticClass(), DefaultValueMemberName);
}
VectorParameterFactoryNode->SetDisplayLabel(ShaderNode->GetDisplayLabel());
}
void UInterchangeGenericMaterialPipeline::HandleStaticBooleanParameterNode(const UInterchangeShaderNode* ShaderNode, UInterchangeMaterialExpressionFactoryNode* StaticBoolParameterFactoryNode)
{
using namespace UE::Interchange::Materials::Standard::Nodes;
const FString ParameterKey = UInterchangeShaderPortsAPI::MakeInputParameterKey(StaticBoolParameter::Attributes::DefaultValue.ToString());
bool InputValue;
if (ShaderNode->GetBooleanAttribute(ParameterKey, InputValue))
{
StaticBoolParameterFactoryNode->SetCustomExpressionClassName(UMaterialExpressionStaticBoolParameter::StaticClass()->GetName());
const FName DefaultValueMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionStaticBoolParameter, DefaultValue);
StaticBoolParameterFactoryNode->AddBooleanAttribute(DefaultValueMemberName.ToString(), InputValue);
StaticBoolParameterFactoryNode->AddApplyAndFillDelegates<bool>(DefaultValueMemberName.ToString(), UMaterialExpressionStaticBoolParameter::StaticClass(), DefaultValueMemberName);
}
StaticBoolParameterFactoryNode->SetDisplayLabel(ShaderNode->GetDisplayLabel());
}
UInterchangeMaterialExpressionFactoryNode* UInterchangeGenericMaterialPipeline::CreateMaterialExpressionForShaderNode(UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode,
const UInterchangeShaderNode* ShaderNode, const FString& ParentUid)
{
using namespace UE::Interchange::Materials::Standard;
// If we recognize the shader node type
// - Create material expression for specific node type
//
// If we don't recognize the shader node type
// - Create material expression by trying to match the node type to a material expression class name
const FString MaterialExpressionUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(ShaderNode->GetUniqueID());
UInterchangeMaterialExpressionFactoryNode* MaterialExpression = Cast<UInterchangeMaterialExpressionFactoryNode>(BaseNodeContainer->GetFactoryNode(MaterialExpressionUid));
if (MaterialExpression != nullptr)
{
return MaterialExpression;
}
// Create function call expression if applicable
MaterialExpression = CreateFunctionCallExpression(ShaderNode, MaterialExpressionUid, MaterialFactoryNode);
if (MaterialExpression)
{
return MaterialExpression;
}
MaterialExpression = NewObject<UInterchangeMaterialExpressionFactoryNode>(BaseNodeContainer);
if (!MaterialExpression)
{
return nullptr;
}
FString ShaderType;
ShaderNode->GetCustomShaderType(ShaderType);
BaseNodeContainer->SetupNode(MaterialExpression, MaterialExpressionUid, ShaderNode->GetDisplayLabel(), EInterchangeNodeContainerType::FactoryData, ParentUid);
if (*ShaderType == Nodes::FlattenNormal::Name)
{
HandleFlattenNormalNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if (*ShaderType == Nodes::MakeFloat3::Name)
{
HandleMakeFloat3Node(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if (*ShaderType == Nodes::Lerp::Name)
{
HandleLerpNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::Mask::Name)
{
HandleMaskNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::Noise::Name)
{
HandleNoiseNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::NormalFromHeightMap::Name)
{
HandleNormalFromHeightMapNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::RotateAboutAxis::Name)
{
HandleRotateAboutAxisNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::Rotator::Name)
{
HandleRotatorNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if (*ShaderType == Nodes::Switch::Name)
{
HandleSwitchNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::Swizzle::Name)
{
HandleSwizzleNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if (*ShaderType == Nodes::TextureCoordinate::Name)
{
HandleTextureCoordinateNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::TextureObject::Name)
{
HandleTextureObjectNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if (*ShaderType == Nodes::TextureSample::Name)
{
HandleTextureSampleNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::Time::Name)
{
HandleTimeNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::TransformPosition::Name)
{
HandleTransformPositionNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::TransformVector::Name)
{
HandleTransformVectorNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::VectorNoise::Name)
{
HandleVectorNoiseNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if (*ShaderType == Nodes::SlabBSDF::Name)
{
HandleSlabBSDFNode(ShaderNode, MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::Cosine::Name)
{
HandleTrigonometryNode(ShaderNode, UMaterialExpressionCosine::StaticClass(), MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::Sine::Name)
{
HandleTrigonometryNode(ShaderNode, UMaterialExpressionSine::StaticClass(), MaterialFactoryNode, MaterialExpression);
}
else if(*ShaderType == Nodes::Tangent::Name)
{
HandleTrigonometryNode(ShaderNode, UMaterialExpressionTangent::StaticClass(), MaterialFactoryNode, MaterialExpression);
}
else if (*ShaderType == Nodes::ScalarParameter::Name)
{
HandleScalarParameterNode(ShaderNode, MaterialExpression);
}
else if (*ShaderType == Nodes::VectorParameter::Name)
{
HandleVectorParameterNode(ShaderNode, MaterialExpression);
}
else if (*ShaderType == Nodes::StaticBoolParameter::Name)
{
HandleStaticBooleanParameterNode(ShaderNode, MaterialExpression);
}
else if (ensure(!ShaderType.IsEmpty()))
{
const FString ExpressionClassName = TEXT("MaterialExpression") + ShaderType;
MaterialExpression->SetCustomExpressionClassName(ExpressionClassName);
TArray<FString> Inputs;
UInterchangeShaderPortsAPI::GatherInputs(ShaderNode, Inputs);
for (const FString& InputName : Inputs)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> InputExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, InputName, MaterialExpressionUid);
if (InputExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(MaterialExpression, InputName, InputExpression.Get<0>()->GetUniqueID(), InputExpression.Get<1>());
}
}
}
MaterialExpression->AddTargetNodeUid(ShaderNode->GetUniqueID());
if (*ShaderType == Nodes::TextureSample::Name
|| *ShaderType == Nodes::TextureObject::Name)
{
FString TextureUid;
if (*ShaderType == Nodes::TextureSample::Name)
{
const bool bIsAParameter = UInterchangeShaderPortsAPI::HasParameter(ShaderNode, Nodes::TextureSample::Inputs::Texture);
ShaderNode->GetStringAttribute(CreateInputKey(Nodes::TextureSample::Inputs::Texture.ToString(), bIsAParameter), TextureUid);
}
else if (*ShaderType == Nodes::TextureObject::Name)
{
const bool bIsAParameter = UInterchangeShaderPortsAPI::HasParameter(ShaderNode, Nodes::TextureObject::Inputs::Texture);
ShaderNode->GetStringAttribute(CreateInputKey(Nodes::TextureObject::Inputs::Texture.ToString(),bIsAParameter), TextureUid);
}
// Make the material factory node have a dependency on the texture factory node so that the texture asset gets created first
if (const UInterchangeTextureNode* TextureNode = Cast<const UInterchangeTextureNode>(BaseNodeContainer->GetNode(TextureUid)))
{
TArray<FString> TextureNodeTargets;
TextureNode->GetTargetNodeUids(TextureNodeTargets);
if (TextureNodeTargets.Num() > 0)
{
FString TextureFactoryNodeUid = TextureNodeTargets[0];
if (BaseNodeContainer->IsNodeUidValid(TextureFactoryNodeUid))
{
TArray<FString> FactoryDependencies;
MaterialFactoryNode->GetFactoryDependencies(FactoryDependencies);
if (!FactoryDependencies.Contains(TextureFactoryNodeUid))
{
MaterialFactoryNode->AddFactoryDependencyUid(TextureFactoryNodeUid);
}
}
}
}
}
return MaterialExpression;
}
UInterchangeMaterialExpressionFactoryNode* UInterchangeGenericMaterialPipeline::CreateExpressionNode(const FString& ExpressionName, const FString& ParentUid, UClass* MaterialExpressionClass)
{
const FString MaterialExpressionUid = ParentUid + TEXT("\\") + ExpressionName;
UInterchangeMaterialExpressionFactoryNode* MaterialExpressionFactoryNode = NewObject<UInterchangeMaterialExpressionFactoryNode>(BaseNodeContainer);
MaterialExpressionFactoryNode->SetCustomExpressionClassName(MaterialExpressionClass->GetName());
BaseNodeContainer->SetupNode(MaterialExpressionFactoryNode, MaterialExpressionUid, ExpressionName, EInterchangeNodeContainerType::FactoryData, ParentUid);
return MaterialExpressionFactoryNode;
}
UInterchangeMaterialExpressionFactoryNode* UInterchangeGenericMaterialPipeline::HandleFloatInput(const UInterchangeShaderNode* ShaderNode, const FString& InputName, const FString& ParentUid, bool bIsAParameter)
{
if (bIsAParameter)
{
return CreateScalarParameterExpression(ShaderNode, InputName, ParentUid);
}
else
{
return CreateConstantExpression(ShaderNode, InputName, ParentUid);
}
}
UInterchangeMaterialExpressionFactoryNode* UInterchangeGenericMaterialPipeline::CreateConstantExpression(const UInterchangeShaderNode* ShaderNode, const FString& InputName, const FString& ParentUid)
{
UInterchangeMaterialExpressionFactoryNode* MaterialExpressionFactoryNode = CreateExpressionNode(InputName, ParentUid, UMaterialExpressionConstant::StaticClass());
float InputValue;
if (ShaderNode->GetFloatAttribute(UInterchangeShaderPortsAPI::MakeInputValueKey(InputName), InputValue))
{
const FName RMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionConstant, R);
MaterialExpressionFactoryNode->AddFloatAttribute(RMemberName.ToString(), InputValue);
MaterialExpressionFactoryNode->AddApplyAndFillDelegates<float>(RMemberName.ToString(), UMaterialExpressionConstant::StaticClass(), RMemberName);
}
return MaterialExpressionFactoryNode;
}
UInterchangeMaterialExpressionFactoryNode* UInterchangeGenericMaterialPipeline::CreateScalarParameterExpression(const UInterchangeShaderNode* ShaderNode, const FString& InputName, const FString& ParentUid)
{
using namespace UE::Interchange::Materials::Standard;
UInterchangeMaterialExpressionFactoryNode* MaterialExpressionFactoryNode = CreateExpressionNode(InputName, ParentUid, UMaterialExpressionScalarParameter::StaticClass());
float InputValue;
if (ShaderNode->GetFloatAttribute(UInterchangeShaderPortsAPI::MakeInputParameterKey(InputName), InputValue))
{
const FName DefaultValueMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionScalarParameter, DefaultValue);
MaterialExpressionFactoryNode->AddFloatAttribute(DefaultValueMemberName.ToString(), InputValue);
MaterialExpressionFactoryNode->AddApplyAndFillDelegates<float>(DefaultValueMemberName.ToString(), UMaterialExpressionScalarParameter::StaticClass(), DefaultValueMemberName);
}
if(FString DisplayLabel = ShaderNode->GetDisplayLabel(); DisplayLabel.IsEmpty())
{
MaterialExpressionFactoryNode->SetDisplayLabel(InputName);
}
else
{
MaterialExpressionFactoryNode->SetDisplayLabel(DisplayLabel);
}
return MaterialExpressionFactoryNode;
}
UInterchangeMaterialExpressionFactoryNode* UInterchangeGenericMaterialPipeline::HandleLinearColorInput(const UInterchangeShaderNode* ShaderNode, const FString& InputName, const FString& ParentUid, bool bIsAParameter)
{
if (bIsAParameter)
{
return CreateVectorParameterExpression(ShaderNode, InputName, ParentUid);
}
else
{
return CreateConstant3VectorExpression(ShaderNode, InputName, ParentUid);
}
}
UInterchangeMaterialExpressionFactoryNode* UInterchangeGenericMaterialPipeline::CreateConstant3VectorExpression(const UInterchangeShaderNode* ShaderNode, const FString& InputName, const FString& ParentUid)
{
UInterchangeMaterialExpressionFactoryNode* MaterialExpressionFactoryNode = CreateExpressionNode(InputName, ParentUid, UMaterialExpressionConstant3Vector::StaticClass());
FLinearColor InputValue;
if (ShaderNode->GetLinearColorAttribute(UInterchangeShaderPortsAPI::MakeInputValueKey(InputName), InputValue))
{
const FName ConstantMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionConstant3Vector, Constant);
MaterialExpressionFactoryNode->AddLinearColorAttribute(ConstantMemberName.ToString(), InputValue);
MaterialExpressionFactoryNode->AddApplyAndFillDelegates<FLinearColor>(ConstantMemberName.ToString(), UMaterialExpressionConstant3Vector::StaticClass(), ConstantMemberName);
}
return MaterialExpressionFactoryNode;
}
UInterchangeMaterialExpressionFactoryNode* UInterchangeGenericMaterialPipeline::CreateVectorParameterExpression(const UInterchangeShaderNode* ShaderNode, const FString& InputName, const FString& ParentUid)
{
UInterchangeMaterialExpressionFactoryNode* MaterialExpressionFactoryNode = CreateExpressionNode(InputName, ParentUid, UMaterialExpressionVectorParameter::StaticClass());
FLinearColor InputValue;
if (ShaderNode->GetLinearColorAttribute(UInterchangeShaderPortsAPI::MakeInputParameterKey(InputName), InputValue))
{
const FName DefaultValueName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionVectorParameter, DefaultValue);
MaterialExpressionFactoryNode->AddLinearColorAttribute(DefaultValueName.ToString(), InputValue);
MaterialExpressionFactoryNode->AddApplyAndFillDelegates<FLinearColor>(DefaultValueName.ToString(), UMaterialExpressionVectorParameter::StaticClass(), DefaultValueName);
}
if(FString DisplayLabel = ShaderNode->GetDisplayLabel(); DisplayLabel.IsEmpty())
{
MaterialExpressionFactoryNode->SetDisplayLabel(InputName);
}
else
{
MaterialExpressionFactoryNode->SetDisplayLabel(DisplayLabel);
}
return MaterialExpressionFactoryNode;
}
UInterchangeMaterialExpressionFactoryNode* UInterchangeGenericMaterialPipeline::CreateStaticBooleanParameterExpression(const UInterchangeShaderNode* ShaderNode, const FString& InputName, const FString& ParentUid)
{
UInterchangeMaterialExpressionFactoryNode* MaterialExpressionFactoryNode = CreateExpressionNode(InputName, ParentUid, UMaterialExpressionStaticBoolParameter::StaticClass());
bool InputValue;
if (ShaderNode->GetBooleanAttribute(InputName, InputValue))
{
const FName DefaultValueMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionStaticBoolParameter, DefaultValue);
MaterialExpressionFactoryNode->AddBooleanAttribute(DefaultValueMemberName.ToString(), InputValue);
MaterialExpressionFactoryNode->AddApplyAndFillDelegates<bool>(DefaultValueMemberName.ToString(), UMaterialExpressionStaticBoolParameter::StaticClass(), DefaultValueMemberName);
}
MaterialExpressionFactoryNode->SetDisplayLabel(InputName);
return MaterialExpressionFactoryNode;
}
UInterchangeMaterialExpressionFactoryNode* UInterchangeGenericMaterialPipeline::CreateVector2ParameterExpression(const UInterchangeShaderNode* ShaderNode, const FString& InputName, const FString& ParentUid)
{
FVector2f InputValue;
if (ShaderNode->GetAttribute<FVector2f>(UInterchangeShaderPortsAPI::MakeInputValueKey(InputName), InputValue))
{
UInterchangeMaterialExpressionFactoryNode* VectorParameterFactoryNode = CreateExpressionNode(InputName, ParentUid, UMaterialExpressionVectorParameter::StaticClass());
const FName DefaultValueMemberName = GET_MEMBER_NAME_CHECKED(UMaterialExpressionVectorParameter, DefaultValue);
VectorParameterFactoryNode->AddLinearColorAttribute(DefaultValueMemberName.ToString(), FLinearColor(InputValue.X, InputValue.Y, 0.f));
VectorParameterFactoryNode->AddApplyAndFillDelegates<FLinearColor>(DefaultValueMemberName.ToString(), UMaterialExpressionVectorParameter::StaticClass(), DefaultValueMemberName);
// Defaults to R&G
UInterchangeMaterialExpressionFactoryNode* ComponentMaskFactoryNode = CreateExpressionNode(InputName + TEXT("_Mask"), ParentUid, UMaterialExpressionComponentMask::StaticClass());
UInterchangeShaderPortsAPI::ConnectDefaultOuputToInput(ComponentMaskFactoryNode, GET_MEMBER_NAME_CHECKED(UMaterialExpressionComponentMask, Input).ToString(),
VectorParameterFactoryNode->GetUniqueID() );
return ComponentMaskFactoryNode;
}
return nullptr;
}
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> UInterchangeGenericMaterialPipeline::CreateMaterialExpressionForInput(UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode, const UInterchangeShaderNode* ShaderNode, const FString& InputName, const FString& ParentUid)
{
// Make sure we don't create an expression for an input if it already has one
if (UInterchangeShaderPortsAPI::HasInput(MaterialFactoryNode, *InputName))
{
return TTuple<UInterchangeMaterialExpressionFactoryNode*, FString>{};
}
// If we have a connection
// - Create material expression for the connected shader node
//
// If we don't have a connection
// - Create material expression for the input value
UInterchangeMaterialExpressionFactoryNode* MaterialExpressionFactoryNode = nullptr;
int32 ExpressionContextIndex = MaterialExpressionCreationContextStack.AddDefaulted();
FString ConnectedShaderNodeUid;
if (UInterchangeShaderPortsAPI::GetInputConnection(ShaderNode, InputName, ConnectedShaderNodeUid, MaterialExpressionCreationContextStack[ExpressionContextIndex].OutputName))
{
if (const UInterchangeShaderNode* ConnectedShaderNode = Cast<const UInterchangeShaderNode>(BaseNodeContainer->GetNode(ConnectedShaderNodeUid)))
{
MaterialExpressionFactoryNode = CreateMaterialExpressionForShaderNode(MaterialFactoryNode, ConnectedShaderNode, ParentUid);
}
}
else
{
const bool bIsAParameter = UInterchangeShaderPortsAPI::HasParameter(ShaderNode, FName(InputName));
switch(UInterchangeShaderPortsAPI::GetInputType(ShaderNode, InputName, bIsAParameter))
{
case UE::Interchange::EAttributeTypes::Float:
MaterialExpressionFactoryNode = HandleFloatInput(ShaderNode, InputName, ParentUid, bIsAParameter);
break;
case UE::Interchange::EAttributeTypes::LinearColor:
MaterialExpressionFactoryNode = HandleLinearColorInput(ShaderNode, InputName, ParentUid, bIsAParameter);
break;
case UE::Interchange::EAttributeTypes::Vector2f:
MaterialExpressionFactoryNode = CreateVector2ParameterExpression(ShaderNode, InputName, ParentUid);
break;
case UE::Interchange::EAttributeTypes::Bool:
MaterialExpressionFactoryNode = CreateStaticBooleanParameterExpression(ShaderNode, InputName, ParentUid);
break;
}
if (MaterialExpressionFactoryNode)
{
FString MaterialExpressionName;
if (AttributeStorageNode && AttributeStorageNode->GetStringAttribute(ShaderNode->GetUniqueID(), MaterialExpressionName))
{
MaterialExpressionFactoryNode->SetDisplayLabel(MaterialExpressionName);
}
}
}
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> Result {MaterialExpressionFactoryNode, MaterialExpressionCreationContextStack[ExpressionContextIndex].OutputName};
MaterialExpressionCreationContextStack.Pop();
return Result;
}
UInterchangeMaterialFactoryNode* UInterchangeGenericMaterialPipeline::CreateMaterialFactoryNode(const UInterchangeShaderGraphNode* ShaderGraphNode)
{
UInterchangeMaterialFactoryNode* MaterialFactoryNode = Cast<UInterchangeMaterialFactoryNode>( CreateBaseMaterialFactoryNode(ShaderGraphNode, UInterchangeMaterialFactoryNode::StaticClass()) );
UInterchangeUserDefinedAttributesAPI::DuplicateAllUserDefinedAttribute(ShaderGraphNode, MaterialFactoryNode, false);
if (bOverrideDisplacement)
{
MaterialFactoryNode->SetCustomDisplacementCenter(OverrideDisplacementCenter);
}
if(HandleSubstrate(ShaderGraphNode, MaterialFactoryNode))
{
return MaterialFactoryNode;
}
// Handle the case where the material will be connected through the material attributes input
if (HandleBxDFInput(ShaderGraphNode, MaterialFactoryNode))
{
// No need to proceed any further
return MaterialFactoryNode;
}
if (HandleUnlitModel(ShaderGraphNode, MaterialFactoryNode))
{
// No need to proceed any further
return MaterialFactoryNode;
}
if (!HandleMetalRoughnessModel(ShaderGraphNode, MaterialFactoryNode))
{
if (!HandleSpecGlossModel(ShaderGraphNode, MaterialFactoryNode))
{
if (!HandlePhongModel(ShaderGraphNode, MaterialFactoryNode))
{
HandleLambertModel(ShaderGraphNode, MaterialFactoryNode);
}
}
}
// Can't have different shading models
// Favor translucency over coats (clear coat, sheen, etc.) since it tends to have a bigger impact visually
if (!HandleThinTranslucent(ShaderGraphNode, MaterialFactoryNode))
{
if (!HandleClearCoat(ShaderGraphNode, MaterialFactoryNode))
{
if(!HandleSheen(ShaderGraphNode, MaterialFactoryNode))
{
HandleSubsurface(ShaderGraphNode, MaterialFactoryNode);
}
}
}
HandleCommonParameters(ShaderGraphNode, MaterialFactoryNode);
return MaterialFactoryNode;
}
UInterchangeMaterialInstanceFactoryNode* UInterchangeGenericMaterialPipeline::CreateMaterialInstanceFactoryNode(const UInterchangeShaderGraphNode* ShaderGraphNode)
{
using namespace UE::Interchange::InterchangeGenericMaterialPipeline::Private;
UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode =
Cast<UInterchangeMaterialInstanceFactoryNode>( CreateBaseMaterialFactoryNode(ShaderGraphNode, UInterchangeMaterialInstanceFactoryNode::StaticClass()) );
UInterchangeUserDefinedAttributesAPI::DuplicateAllUserDefinedAttribute(ShaderGraphNode, MaterialInstanceFactoryNode, false);
TFunction<void(const FString&)> ChooseParent = [this, MaterialInstanceFactoryNode, ShaderGraphNode](const FString& Model) -> void
{
FString ParentRootName;
if (HasThinTranslucency(ShaderGraphNode))
{
ParentRootName = TEXT("ThinTranslucentMaterial_");
}
else if (HasClearCoat(ShaderGraphNode))
{
ParentRootName = TEXT("ClearCoatMaterial_");
}
else if (HasSheen(ShaderGraphNode))
{
ParentRootName = TEXT("SheenMaterial_");
}
else if (HasSubsurface(ShaderGraphNode))
{
ParentRootName = TEXT("SubsurfaceMaterial_");
}
else
{
ParentRootName = TEXT("PBRSurfaceMaterial_");
}
const FString ParentAssetPath = TEXT("/InterchangeAssets/Materials/") + ParentRootName + Model + TEXT(".") + ParentRootName + Model;
MaterialInstanceFactoryNode->SetCustomParent(ParentAssetPath);
};
if (UMaterialInterface* ParentMaterialObj = Cast<UMaterialInterface>(ParentMaterial.TryLoad()))
{
MaterialInstanceFactoryNode->SetCustomParent(ParentMaterialObj->GetPathName());
}
else if (IsSpecGlossModel(ShaderGraphNode))
{
ChooseParent(TEXT("SG"));
}
else if (IsMetalRoughModel(ShaderGraphNode))
{
ChooseParent(TEXT("MR"));
}
else if (IsPhongModel(ShaderGraphNode))
{
MaterialInstanceFactoryNode->SetCustomParent(TEXT("/InterchangeAssets/Materials/PhongSurfaceMaterial.PhongSurfaceMaterial"));
}
else if (IsLambertModel(ShaderGraphNode))
{
MaterialInstanceFactoryNode->SetCustomParent(TEXT("/InterchangeAssets/Materials/LambertSurfaceMaterial.LambertSurfaceMaterial"));
}
else if (IsUnlitModel(ShaderGraphNode))
{
MaterialInstanceFactoryNode->SetCustomParent(TEXT("/InterchangeAssets/Materials/UnlitMaterial.UnlitMaterial"));
}
else
{
// Default to PBR
MaterialInstanceFactoryNode->SetCustomParent(TEXT("/InterchangeAssets/Materials/PBRSurfaceMaterial.PBRSurfaceMaterial"));
}
#if WITH_EDITOR
const UClass* MaterialClass = IsRunningGame() ? UMaterialInstanceDynamic::StaticClass() : UMaterialInstanceConstant::StaticClass();
MaterialInstanceFactoryNode->SetCustomInstanceClassName(MaterialClass->GetPathName());
#else
MaterialInstanceFactoryNode->SetCustomInstanceClassName(UMaterialInstanceDynamic::StaticClass()->GetPathName());
#endif
VisitShaderGraphNode(ShaderGraphNode, MaterialInstanceFactoryNode);
AddSpecularProfileToFactoryNode(ShaderGraphNode, MaterialInstanceFactoryNode, BaseNodeContainer);
return MaterialInstanceFactoryNode;
}
void UInterchangeGenericMaterialPipeline::CreateSpecularProfileFactoryNode(const UInterchangeSpecularProfileNode* SpecularProfileNode)
{
const FString FactoryNodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(SpecularProfileNode->GetUniqueID());
UInterchangeSpecularProfileFactoryNode* FactoryNode = NewObject<UInterchangeSpecularProfileFactoryNode>(BaseNodeContainer, NAME_None);
BaseNodeContainer->SetupNode(FactoryNode, FactoryNodeUid, SpecularProfileNode->GetDisplayLabel(), EInterchangeNodeContainerType::FactoryData);
FactoryNode->SetEnabled(true);
if (uint8 Format; SpecularProfileNode->GetCustomFormat(Format))
{
FactoryNode->SetCustomFormat(ESpecularProfileFormat{ Format });
}
if (FString TextureUid; SpecularProfileNode->GetCustomTexture(TextureUid))
{
if(BaseNodeContainer->GetNode(TextureUid))
{
FactoryNode->SetCustomTexture(TextureUid);
FactoryNode->AddFactoryDependencyUid(UInterchangeFactoryBaseNode::BuildFactoryNodeUid(TextureUid));
}
}
FactoryNode->AddTargetNodeUid(SpecularProfileNode->GetUniqueID());
SpecularProfileNode->AddTargetNodeUid(FactoryNode->GetUniqueID());
}
void UInterchangeGenericMaterialPipeline::VisitShaderGraphNode(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode) const
{
TArray<FString> Inputs;
UInterchangeShaderPortsAPI::GatherInputs(ShaderGraphNode, Inputs);
// We don't want to visit the whole shader graph for every input, for example with a StandardSurface with 31 inputs, the MaterialFunction is connected to all inputs of the Material but should be visited only once
TSet<const UInterchangeShaderNode*> VisitedNodes;
for(const FString& InputName : Inputs)
{
VisitShaderInput(ShaderGraphNode, MaterialInstanceFactoryNode, InputName, VisitedNodes);
}
}
void UInterchangeGenericMaterialPipeline::VisitShaderNode(const UInterchangeShaderNode* ShaderNode, UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode, TSet<const UInterchangeShaderNode*>& VisitedNodes) const
{
using namespace UE::Interchange::Materials::Standard::Nodes;
if(FString ShaderType; ShaderNode->GetCustomShaderType(ShaderType))
{
if(*ShaderType == ScalarParameter::Name)
{
return VisitScalarParameterNode(ShaderNode, MaterialInstanceFactoryNode);
}
else if (*ShaderType == TextureSample::Name)
{
return VisitTextureSampleNode(ShaderNode, MaterialInstanceFactoryNode);
}
else if(*ShaderType == VectorParameter::Name)
{
return VisitVectorParameterNode(ShaderNode, MaterialInstanceFactoryNode);
}
}
{
TArray<FString> Inputs;
UInterchangeShaderPortsAPI::GatherInputs(ShaderNode, Inputs);
for(const FString & InputName: Inputs)
{
VisitShaderInput(ShaderNode, MaterialInstanceFactoryNode, InputName, VisitedNodes);
}
}
}
void UInterchangeGenericMaterialPipeline::VisitShaderInput(const UInterchangeShaderNode* ShaderNode, UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode, const FString& InputName, TSet<const UInterchangeShaderNode*>& VisitedNodes) const
{
if(VisitedNodes.Find(ShaderNode))
{
return;
}
const bool bIsAParameter = UInterchangeShaderPortsAPI::HasParameter(ShaderNode, FName(InputName));
FString ConnectedShaderNodeUid;
FString OutputName;
if (UInterchangeShaderPortsAPI::GetInputConnection(ShaderNode, InputName, ConnectedShaderNodeUid, OutputName))
{
const UInterchangeShaderNode* ConnectedShaderNode = Cast<const UInterchangeShaderNode>(BaseNodeContainer->GetNode(ConnectedShaderNodeUid));
if (ConnectedShaderNode && !VisitedNodes.Find(ConnectedShaderNode))
{
VisitShaderNode(ConnectedShaderNode, MaterialInstanceFactoryNode, VisitedNodes);
VisitedNodes.Emplace(ConnectedShaderNode);
}
}
else
{
switch(UInterchangeShaderPortsAPI::GetInputType(ShaderNode, InputName, bIsAParameter))
{
case UE::Interchange::EAttributeTypes::Float:
{
if(float InputValue; ShaderNode->GetFloatAttribute(CreateInputKey(InputName, bIsAParameter), InputValue))
{
MaterialInstanceFactoryNode->AddFloatAttribute(CreateInputKey(InputName, bIsAParameter), InputValue);
}
}
break;
case UE::Interchange::EAttributeTypes::LinearColor:
{
if(FLinearColor InputValue; ShaderNode->GetLinearColorAttribute(CreateInputKey(InputName, bIsAParameter), InputValue))
{
MaterialInstanceFactoryNode->AddLinearColorAttribute(CreateInputKey(InputName, bIsAParameter), InputValue);
}
}
break;
}
}
}
void UInterchangeGenericMaterialPipeline::VisitScalarParameterNode(const UInterchangeShaderNode* ShaderNode, UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode) const
{
using namespace UE::Interchange::Materials::Standard::Nodes;
const bool bIsAParameter = UInterchangeShaderPortsAPI::HasParameter(ShaderNode, ScalarParameter::Attributes::DefaultValue);
if(float DefaultValue; ShaderNode->GetFloatAttribute(CreateInputKey(ScalarParameter::Attributes::DefaultValue.ToString(), bIsAParameter), DefaultValue))
{
MaterialInstanceFactoryNode->AddFloatAttribute(CreateInputKey(ShaderNode->GetDisplayLabel(), bIsAParameter), DefaultValue);
}
}
void UInterchangeGenericMaterialPipeline::VisitTextureSampleNode(const UInterchangeShaderNode* ShaderNode, UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode) const
{
using namespace UE::Interchange::Materials::Standard::Nodes;
const bool bIsAParameter = UInterchangeShaderPortsAPI::HasParameter(ShaderNode, TextureSample::Inputs::Texture);
FString TextureUid;
if (ShaderNode->GetStringAttribute(CreateInputKey(TextureSample::Inputs::Texture.ToString(),bIsAParameter), TextureUid))
{
if (!TextureUid.IsEmpty())
{
FString TextureFactoryUid;
if (const UInterchangeTextureNode* TextureNode = Cast<const UInterchangeTextureNode>(BaseNodeContainer->GetNode(TextureUid)))
{
TArray<FString> TextureTargetNodes;
TextureNode->GetTargetNodeUids(TextureTargetNodes);
if (TextureTargetNodes.Num() > 0)
{
TextureFactoryUid = TextureTargetNodes[0];
MaterialInstanceFactoryNode->AddStringAttribute(CreateInputKey(ShaderNode->GetDisplayLabel(), bIsAParameter), TextureFactoryUid);
MaterialInstanceFactoryNode->AddFactoryDependencyUid(TextureFactoryUid);
}
}
}
}
}
void UInterchangeGenericMaterialPipeline::VisitVectorParameterNode(const UInterchangeShaderNode* ShaderNode, UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode) const
{
using namespace UE::Interchange::Materials::Standard::Nodes;
const bool bIsAParameter = UInterchangeShaderPortsAPI::HasParameter(ShaderNode, VectorParameter::Attributes::DefaultValue);
if(FLinearColor DefaultValue; ShaderNode->GetLinearColorAttribute(CreateInputKey(VectorParameter::Attributes::DefaultValue.ToString(), bIsAParameter), DefaultValue))
{
MaterialInstanceFactoryNode->AddLinearColorAttribute(CreateInputKey(ShaderNode->GetDisplayLabel(), true), DefaultValue);
}
}
FString UInterchangeGenericMaterialPipeline::GetTextureUidAttributeFromShaderNode(const UInterchangeShaderNode* ShaderNode, FName ParameterName, bool& OutIsAParameter) const
{
OutIsAParameter = UInterchangeShaderPortsAPI::HasParameter(ShaderNode, ParameterName);
FString TextureUid;
ShaderNode->GetStringAttribute(CreateInputKey(ParameterName.ToString(), OutIsAParameter), TextureUid);
return TextureUid;
}
FString UInterchangeGenericMaterialPipeline::CreateInputKey(const FString& InputName, bool bIsAParameter) const
{
if (bIsAParameter)
{
return UInterchangeShaderPortsAPI::MakeInputParameterKey(InputName);
}
else
{
return UInterchangeShaderPortsAPI::MakeInputValueKey(InputName);
}
}
bool UInterchangeGenericMaterialPipeline::HandleBxDFInput(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials;
if (!ShaderGraphNode || !UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Common::Parameters::BxDF))
{
return false;
}
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Common::Parameters::BxDF.ToString(), MaterialFactoryNode->GetUniqueID());
ensure(ExpressionFactoryNode.Get<0>());
if (ExpressionFactoryNode.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(MaterialFactoryNode, Common::Parameters::BxDF.ToString(), ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
// Make sure the bUseMaterialAttributes property of the material is set to true
static const FName UseMaterialAttributesMemberName = GET_MEMBER_NAME_CHECKED(UMaterial, bUseMaterialAttributes);
MaterialFactoryNode->AddBooleanAttribute(UseMaterialAttributesMemberName.ToString(), true);
MaterialFactoryNode->AddApplyAndFillDelegates<FString>(UseMaterialAttributesMemberName.ToString(), UMaterialExpressionMaterialFunctionCall::StaticClass(), UseMaterialAttributesMemberName);
return true;
}
UInterchangeMaterialFunctionFactoryNode* UInterchangeGenericMaterialPipeline::CreateMaterialFunctionFactoryNode(const UInterchangeShaderGraphNode* ShaderGraphNode)
{
UInterchangeMaterialFunctionFactoryNode* FactoryNode = Cast<UInterchangeMaterialFunctionFactoryNode>(CreateBaseMaterialFactoryNode(ShaderGraphNode, UInterchangeMaterialFunctionFactoryNode::StaticClass()));
TArray<FString> InputNames;
UInterchangeShaderPortsAPI::GatherInputs(ShaderGraphNode, InputNames);
for (const FString& InputName : InputNames)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(FactoryNode, ShaderGraphNode, InputName, FactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(FactoryNode, InputName, ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
}
return FactoryNode;
}
bool UInterchangeGenericMaterialPipeline::IsUnlitModel(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
using namespace UE::Interchange::Materials::Unlit;
const bool bHasUnlitColorInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Parameters::UnlitColor);
return bHasUnlitColorInput;
}
bool UInterchangeGenericMaterialPipeline::HandleUnlitModel(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials;
bool bShadingModelHandled = false;
// Unlit Color
{
const bool bHasInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Unlit::Parameters::UnlitColor);
if (bHasInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> ExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Unlit::Parameters::UnlitColor.ToString(), MaterialFactoryNode->GetUniqueID());
if (ExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToEmissiveColor(ExpressionFactoryNode.Get<0>()->GetUniqueID(), ExpressionFactoryNode.Get<1>());
}
//gltf allows unlit color to be also translucent:
{
const bool bHasOpacityInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, Common::Parameters::Opacity);
if (bHasOpacityInput)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> OpacityExpressionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, Common::Parameters::Opacity.ToString(), MaterialFactoryNode->GetUniqueID());
if (OpacityExpressionFactoryNode.Get<0>())
{
MaterialFactoryNode->ConnectOutputToOpacity(OpacityExpressionFactoryNode.Get<0>()->GetUniqueID(), OpacityExpressionFactoryNode.Get<1>());
}
using namespace UE::Interchange::InterchangeGenericMaterialPipeline;
Private::UpdateBlendModeBasedOnOpacityAttributes(ShaderGraphNode, MaterialFactoryNode);
}
}
bShadingModelHandled = true;
}
}
if (bShadingModelHandled)
{
MaterialFactoryNode->SetCustomShadingModel(EMaterialShadingModel::MSM_Unlit);
}
return bShadingModelHandled;
}
bool UInterchangeGenericMaterialPipeline::HandleSubstrate(const UInterchangeShaderGraphNode* ShaderGraphNode, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::Materials;
bool bShadingModelHandled = false;
if(UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, SubstrateMaterial::Parameters::FrontMaterial))
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> FrontMaterialFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, SubstrateMaterial::Parameters::FrontMaterial.ToString(), MaterialFactoryNode->GetUniqueID());
ensure(FrontMaterialFactoryNode.Get<0>());
if(FrontMaterialFactoryNode.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(MaterialFactoryNode, SubstrateMaterial::Parameters::FrontMaterial.ToString(), FrontMaterialFactoryNode.Get<0>()->GetUniqueID(), FrontMaterialFactoryNode.Get<1>());
}
if(UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, SubstrateMaterial::Parameters::OpacityMask))
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> OpacityMaskFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, SubstrateMaterial::Parameters::OpacityMask.ToString(), MaterialFactoryNode->GetUniqueID());
ensure(OpacityMaskFactoryNode.Get<0>());
if(OpacityMaskFactoryNode.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(MaterialFactoryNode, SubstrateMaterial::Parameters::OpacityMask.ToString(), OpacityMaskFactoryNode.Get<0>()->GetUniqueID(), OpacityMaskFactoryNode.Get<1>());
}
}
if(UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, SubstrateMaterial::Parameters::Displacement))
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> DisplacementFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, SubstrateMaterial::Parameters::Displacement.ToString(), MaterialFactoryNode->GetUniqueID());
ensure(DisplacementFactoryNode.Get<0>());
if(DisplacementFactoryNode.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(MaterialFactoryNode, SubstrateMaterial::Parameters::Displacement.ToString(), DisplacementFactoryNode.Get<0>()->GetUniqueID(), DisplacementFactoryNode.Get<1>());
}
if (float DisplacementCenter; !bOverrideDisplacement && ShaderGraphNode->GetCustomDisplacementCenterMode(DisplacementCenter))
{
MaterialFactoryNode->SetCustomDisplacementCenter(DisplacementCenter);
}
}
if(UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, SubstrateMaterial::Parameters::Occlusion))
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> OcclusionFactoryNode =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderGraphNode, SubstrateMaterial::Parameters::Occlusion.ToString(), MaterialFactoryNode->GetUniqueID());
ensure(OcclusionFactoryNode.Get<0>());
if(OcclusionFactoryNode.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(MaterialFactoryNode, SubstrateMaterial::Parameters::Occlusion.ToString(), OcclusionFactoryNode.Get<0>()->GetUniqueID(), OcclusionFactoryNode.Get<1>());
}
}
if(EBlendMode BlendMode; ShaderGraphNode->GetCustomBlendMode(reinterpret_cast<int&>(BlendMode)))
{
MaterialFactoryNode->SetCustomBlendMode(BlendMode);
if(BlendMode == BLEND_TranslucentColoredTransmittance)
{
MaterialFactoryNode->SetCustomTranslucencyLightingMode(ETranslucencyLightingMode::TLM_SurfacePerPixelLighting);
MaterialFactoryNode->SetCustomRefractionMethod(ERefractionMode::RM_IndexOfRefraction);
}
}
bShadingModelHandled = true;
}
return bShadingModelHandled;
}
UInterchangeMaterialExpressionFactoryNode* UInterchangeGenericMaterialPipeline::CreateFunctionCallExpression(const UInterchangeShaderNode* ShaderNode, const FString& MaterialExpressionUid, UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode)
{
using namespace UE::Interchange::InterchangeGenericMaterialPipeline::Private;
const UInterchangeFunctionCallShaderNode* FunctionCallShaderNode = Cast<UInterchangeFunctionCallShaderNode>(ShaderNode);
if (!FunctionCallShaderNode)
{
return nullptr;
}
UInterchangeMaterialFunctionCallExpressionFactoryNode* FunctionCallFactoryNode = NewObject<UInterchangeMaterialFunctionCallExpressionFactoryNode>(BaseNodeContainer);
if (!FunctionCallFactoryNode)
{
return nullptr;
}
// Check whether the MaterialFunction attribute is valid
FString MaterialFunctionAttribute;
if (FunctionCallShaderNode->GetCustomMaterialFunction(MaterialFunctionAttribute))
{
if (!BaseNodeContainer->GetNode(MaterialFunctionAttribute))
{
if (!FPackageName::IsValidObjectPath(MaterialFunctionAttribute))
{
MaterialFunctionAttribute.Empty();
}
}
}
// Nothing to do if the MaterialFunction attribute is not valid
if (MaterialFunctionAttribute.IsEmpty())
{
// TODO: Log a warning
return nullptr;
}
BaseNodeContainer->SetupNode(FunctionCallFactoryNode, MaterialExpressionUid, ShaderNode->GetDisplayLabel(), EInterchangeNodeContainerType::FactoryData);
if (BaseNodeContainer->GetNode(MaterialFunctionAttribute))
{
const FString MaterialFunctionFactoryNodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(MaterialFunctionAttribute);
FunctionCallFactoryNode->SetCustomMaterialFunctionDependency(MaterialFunctionFactoryNodeUid);
UClass* CustomExpressionClass = UMaterialExpressionMaterialFunctionCall::StaticClass();
FunctionCallFactoryNode->SetCustomExpressionClassName(CustomExpressionClass->GetName());
}
else if (FPackageName::IsValidObjectPath(MaterialFunctionAttribute))
{
FunctionCallFactoryNode->SetCustomMaterialFunctionDependency(MaterialFunctionAttribute);
UpdateFunctionCallExpression(*FunctionCallFactoryNode, MaterialFunctionAttribute);
}
TArray<FString> Inputs;
UInterchangeShaderPortsAPI::GatherInputs(ShaderNode, Inputs);
for (const FString& InputName : Inputs)
{
TTuple<UInterchangeMaterialExpressionFactoryNode*, FString> InputExpression =
CreateMaterialExpressionForInput(MaterialFactoryNode, ShaderNode, InputName, MaterialExpressionUid);
if (InputExpression.Get<0>())
{
UInterchangeShaderPortsAPI::ConnectOuputToInputByName(FunctionCallFactoryNode, InputName, InputExpression.Get<0>()->GetUniqueID(), InputExpression.Get<1>());
}
}
return FunctionCallFactoryNode;
}
namespace UE::Interchange::Materials::HashUtils
{
void FDuplicateMaterialHelper::ResetHashData()
{
AccumulatedHash = 0;
MaterialHash = 0;
bIsDuplicate = false;
if (AttributeStorageNode)
{
AttributeStorageNode->MarkAsGarbage();
AttributeStorageNode = nullptr;
}
AttributeStorageNode = NewObject<UInterchangeBaseNode>();
LeafInputAttributeKeys.Empty();
LeafInputShaderNodes.Empty();
#if UE_BUILD_DEBUG
if (HashDebugData)
{
HashDebugData->Reset();
}
#endif
}
void FDuplicateMaterialHelper::ComputMaterialHash(const UInterchangeShaderGraphNode* ShaderGraphNode)
{
MaterialHash = ComputeShaderGraphNodeHash(ShaderGraphNode);
if (ParentMaterialFactoryMap.Contains(MaterialHash))
{
bIsDuplicate = true;
}
}
int32 FDuplicateMaterialHelper::ComputeShaderGraphNodeHash(const UInterchangeShaderGraphNode* ShaderGraphNode)
{
using namespace UE::Interchange::Materials::HashUtils;
/* Two Sided*/
bool bTwoSided;
ShaderGraphNode->GetCustomTwoSided(bTwoSided);
int32 Hash = GetTypeHash(bTwoSided);
ADD_LOG_MESSAGE(TEXT("TwoSided: {0}, Hash: {1}"), bTwoSided, Hash);
/* Use Material Attributes*/
bool bUseMaterialAttributes = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, UE::Interchange::Materials::Common::Parameters::BxDF);
Hash = HashCombine(Hash, GetTypeHash(bUseMaterialAttributes));
ADD_LOG_MESSAGE(TEXT("Use Material Attributes: {0}, Hash: {1}"), bUseMaterialAttributes, Hash);
/* Blend Mode */
TEnumAsByte<EBlendMode> BlendMode = GetShaderGraphNodeBlendMode(ShaderGraphNode);
Hash = HashCombine(Hash, GetTypeHash(BlendMode));
ADD_LOG_MESSAGE(TEXT("Blend Mode: {0}, Hash: {1}"), (uint8)BlendMode, Hash);
/* Is Thin Surface */
Hash = HashCombine(Hash, GetTypeHash(BlendMode == EBlendMode::BLEND_Translucent));
ADD_LOG_MESSAGE(TEXT("Is Thin Surface: {0}, Hash: {1}"), (BlendMode == EBlendMode::BLEND_Translucent), Hash);
/* EDatasmithShadingModel: uint8 */
Hash = HashCombine(Hash, GetTypeHash(GetShaderGraphNodeShadingModel(ShaderGraphNode)));
ADD_LOG_MESSAGE(TEXT("Shading Model: {0}, Hash: {1}"), GetShaderGraphNodeShadingModel(ShaderGraphNode), Hash);
Hash = HashCombine(Hash, ComputeShaderNodeHash(ShaderGraphNode));
ADD_LOG_MESSAGE(TEXT("ShaderHash: {0}"), Hash);
return Hash;
}
uint8 FDuplicateMaterialHelper::GetShaderGraphNodeShadingModel(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
if (GenericMaterialPipeline.HasThinTranslucency(ShaderGraphNode))
{
return 1;
}
else if (GenericMaterialPipeline.HasSubsurface(ShaderGraphNode))
{
return 2;
}
else if (GenericMaterialPipeline.HasClearCoat(ShaderGraphNode))
{
return 3;
}
else if (GenericMaterialPipeline.IsUnlitModel(ShaderGraphNode))
{
return 4;
}
return 0;
}
TEnumAsByte<EBlendMode> FDuplicateMaterialHelper::GetShaderGraphNodeBlendMode(const UInterchangeShaderGraphNode* ShaderGraphNode) const
{
using namespace UE::Interchange::Materials;
TEnumAsByte<EBlendMode> BlendMode = EBlendMode::BLEND_Opaque;
if (GenericMaterialPipeline.HasThinTranslucency(ShaderGraphNode))
{
BlendMode = EBlendMode::BLEND_Translucent;
}
else if (GenericMaterialPipeline.HasSubsurface(ShaderGraphNode))
{
BlendMode = EBlendMode::BLEND_Opaque;
}
else
{
const bool bHasOpacityInput = UInterchangeShaderPortsAPI::HasInput(ShaderGraphNode, UE::Interchange::Materials::Common::Parameters::Opacity);
if (bHasOpacityInput && GenericMaterialPipeline.IsUnlitModel(ShaderGraphNode))
{
float OpacityClipValue;
if (ShaderGraphNode->GetCustomOpacityMaskClipValue(OpacityClipValue))
{
BlendMode = EBlendMode::BLEND_Masked;
}
else
{
BlendMode = EBlendMode::BLEND_Translucent;
}
}
}
return BlendMode;
}
int32 FDuplicateMaterialHelper::ComputeShaderNodeHash(const UInterchangeShaderNode* ShaderNode)
{
int32 Hash = 0;
FString ShaderTypeName;
ShaderNode->GetCustomShaderType(ShaderTypeName);
TArray<FString> Inputs;
UInterchangeShaderPortsAPI::GatherInputs(ShaderNode, Inputs);
if (!ShaderTypeName.IsEmpty())
{
using namespace UE::Interchange::Materials;
PUSH_NODE_ADDRESS_WITHOUT_CHECKPOINT(ShaderTypeName)
Hash = HashCombineCustom(Hash, GetTypeHash(ShaderTypeName));
ADD_LOG_MESSAGE(TEXT("{0}, Accumulated Hash: {1}"), ShaderTypeName, AccumulatedHash)
TArray<FInterchangeUserDefinedAttributeInfo> UserDefinedAttributes = UInterchangeUserDefinedAttributesAPI::GetUserDefinedAttributeInfos(ShaderNode);
if (UserDefinedAttributes.Num())
{
for (const auto& UserDefinedAttribute : UserDefinedAttributes)
{
Hash = HashCombineCustom(Hash, GetTypeHash(UserDefinedAttribute.Type));
Hash = HashCombineCustom(Hash, GetTypeHash(UserDefinedAttribute.Name));
ADD_LOG_MESSAGE(TEXT("UDA[Type: {0}, Name: {1}], Accumulated Hash: {2}"), (int32)UserDefinedAttribute.Type, UserDefinedAttribute.Name, AccumulatedHash)
const FString UserDefinedAttributeType = AttributeTypeToString(UserDefinedAttribute.Type);
if (UserDefinedAttribute.Type == UE::Interchange::EAttributeTypes::String)
{
const FString InputValueKey = (UInterchangeUserDefinedAttributesAPI::MakeUserDefinedPropertyValueKey(UserDefinedAttribute.Name, UserDefinedAttribute.RequiresDelegate)).Key;
const FString OverrideParameterNameAttributeKey = FInterchangeMaterialInstanceOverridesAPI::MakeOverrideParameterName(ShaderNode->GetDisplayLabel());
SetupOverridableTextureParameter(ShaderNode, InputValueKey, OverrideParameterNameAttributeKey);
}
}
}
}
else
{
if (const UInterchangeFunctionCallShaderNode* FunctionCallNode = Cast<UInterchangeFunctionCallShaderNode>(ShaderNode))
{
FString MaterialFunction;
if (FunctionCallNode->GetCustomMaterialFunction(MaterialFunction) && !MaterialFunction.IsEmpty())
{
Hash = HashCombineCustom(Hash, GetTypeHash(MaterialFunction));
ADD_LOG_MESSAGE(TEXT("MF[{0}], Accumulate Hash: {1}"), MaterialFunction, AccumulatedHash);
#if UE_BUILD_DEBUG
FString MaterialFunctionName;
FString Discard;
if (MaterialFunction.Split(TEXT("."), &Discard, &MaterialFunctionName))
{
PUSH_NODE_ADDRESS_WITHOUT_CHECKPOINT(FString::Printf(TEXT("MaterialFunction[%s]"), *MaterialFunctionName));
}
else
{
PUSH_NODE_ADDRESS_WITHOUT_CHECKPOINT(TEXT("MaterialFunction"));
}
#endif
}
}
}
if (!Inputs.IsEmpty())
{
for (const FString& InputName : Inputs)
{
PUSH_NODE_ADDRESS(FString::Printf(TEXT("[%s]"), *InputName))
ADD_NODE_ADDRESS_MESSAGE();
Hash = HashCombineCustom(Hash, ComputeShaderInputHash(ShaderNode, InputName));
POP_NODE_ADDRESSES()
}
}
return Hash;
}
int32 FDuplicateMaterialHelper::ComputeShaderInputHash(const UInterchangeShaderNode* ShaderNode, const FString& InputName)
{
int32 Hash = 0;
FString ConnectedShaderNodeUid;
FString OutputName;
if (UInterchangeShaderPortsAPI::GetInputConnection(ShaderNode, InputName, ConnectedShaderNodeUid, OutputName))
{
if (const UInterchangeShaderNode* ConnectedShaderNode = Cast<const UInterchangeShaderNode>(GenericMaterialPipeline.BaseNodeContainer->GetNode(ConnectedShaderNodeUid)))
{
Hash = HashCombineCustom(Hash, ComputeShaderNodeHash(ConnectedShaderNode));
}
if (!OutputName.IsEmpty())
{
Hash = HashCombineCustom(Hash, GetTypeHash(OutputName));
}
}
else
{
const bool bIsAParameter = UInterchangeShaderPortsAPI::HasParameter(ShaderNode, FName(InputName));
const UE::Interchange::EAttributeTypes InputType = UInterchangeShaderPortsAPI::GetInputType(ShaderNode, InputName, bIsAParameter);
Hash = HashCombineCustom(Hash, GetTypeHash(InputType));
ADD_LOG_MESSAGE(TEXT("{0}, Accumulated Hash: {1}"), AttributeTypeToString(InputType), AccumulatedHash);
// Just setup all the Parameters as overridable parameters. Do not include the values in the Hash
if (bIsAParameter)
{
using namespace UE::Interchange::Materials::HashUtils;
const FString ParameterKey = UInterchangeShaderPortsAPI::MakeInputParameterKey(InputName);
const FString OverridableParameterNameKey = FInterchangeMaterialInstanceOverridesAPI::MakeOverrideParameterName(ShaderNode->GetDisplayLabel());
switch (InputType)
{
case UE::Interchange::EAttributeTypes::Float:
{
SetupOverridableScalarParameter(ShaderNode, ParameterKey, OverridableParameterNameKey);
break;
}
case UE::Interchange::EAttributeTypes::LinearColor:
{
SetupOverridableVectorParameter(ShaderNode, ParameterKey, OverridableParameterNameKey);
break;
}
case UE::Interchange::EAttributeTypes::Bool:
{
SetupOverridableStaticBoolParameter(ShaderNode, ParameterKey, OverridableParameterNameKey);
break;
}
case UE::Interchange::EAttributeTypes::String:
{
SetupOverridableTextureParameter(ShaderNode, ParameterKey, OverridableParameterNameKey);
break;
}
}
}
else
{
const FString InputValueKey = UInterchangeShaderPortsAPI::MakeInputValueKey(InputName);
switch (InputType)
{
case UE::Interchange::EAttributeTypes::Float:
{
float InputValue;
if (ShaderNode->GetFloatAttribute(InputValueKey, InputValue))
{
Hash = HashCombineCustom(Hash, GetTypeHash(InputValue));
ADD_LOG_MESSAGE(TEXT("Unnamed Float({0}), Accumulated Hash: {1}"), FString::SanitizeFloat(InputValue), AccumulatedHash);
}
break;
}
case UE::Interchange::EAttributeTypes::LinearColor:
{
FLinearColor InputValue;
if (ShaderNode->GetLinearColorAttribute(InputValueKey, InputValue))
{
Hash = HashCombineCustom(Hash, GetTypeHash(InputValue));
ADD_LOG_MESSAGE(TEXT("Unnamed LinearColor({0}), Accumulated Hash: {1}"), InputValue.ToString(), AccumulatedHash)
}
break;
}
case UE::Interchange::EAttributeTypes::String:
{
FString InputValue;
if (ShaderNode->GetStringAttribute(InputValueKey, InputValue))
{
Hash = HashCombineCustom(Hash, GetTypeHash(InputValue));
ADD_LOG_MESSAGE(TEXT("Unnamed String({0}), Accumulated Hash: {1}"), InputValue, AccumulatedHash)
}
break;
}
}
}
}
return Hash;
}
void FDuplicateMaterialHelper::SetupOverridableScalarParameter(const UInterchangeShaderNode* ShaderNode, const FString& ParameterKey, const FString& OverridableParameterNameKey)
{
float InputValue;
if (ShaderNode->GetFloatAttribute(ParameterKey, InputValue))
{
const UE::Interchange::FAttributeKey AttributeKey(OverridableParameterNameKey);
if (!AttributeStorageNode->HasAttribute(AttributeKey))
{
AttributeStorageNode->AddFloatAttribute(OverridableParameterNameKey, InputValue);
LeafInputAttributeKeys.Add(AttributeKey);
LeafInputShaderNodes.Emplace(ShaderNode);
ADD_LOG_MESSAGE(TEXT("Scalar Parameter: {0}({1})"), ShaderNode->GetDisplayLabel(), FString::SanitizeFloat(InputValue));
}
}
}
void FDuplicateMaterialHelper::SetupOverridableVectorParameter(const UInterchangeShaderNode* ShaderNode, const FString& ParameterKey, const FString& OverridableParameterNameKey)
{
FLinearColor InputValue;
if (ShaderNode->GetLinearColorAttribute(ParameterKey, InputValue))
{
const UE::Interchange::FAttributeKey AttributeKey(OverridableParameterNameKey);
if (!AttributeStorageNode->HasAttribute(AttributeKey))
{
AttributeStorageNode->AddLinearColorAttribute(OverridableParameterNameKey, InputValue);
LeafInputAttributeKeys.Add(AttributeKey);
LeafInputShaderNodes.Emplace(ShaderNode);
ADD_LOG_MESSAGE(TEXT("Vector Parameter: {0}({1})"), ShaderNode->GetDisplayLabel(), InputValue.ToString());
}
}
}
void FDuplicateMaterialHelper::SetupOverridableStaticBoolParameter(const UInterchangeShaderNode* ShaderNode, const FString& ParameterKey, const FString& OverridableParameterNameKey)
{
bool InputValue;
if (ShaderNode->GetBooleanAttribute(ParameterKey, InputValue))
{
const UE::Interchange::FAttributeKey AttributeKey(OverridableParameterNameKey);
if (!AttributeStorageNode->HasAttribute(AttributeKey))
{
AttributeStorageNode->AddBooleanAttribute(OverridableParameterNameKey, InputValue);
LeafInputAttributeKeys.Add(AttributeKey);
LeafInputShaderNodes.Emplace(ShaderNode);
ADD_LOG_MESSAGE(TEXT("Bool Parameter: {0}({1})"), ShaderNode->GetDisplayLabel(), InputValue);
}
}
}
void FDuplicateMaterialHelper::SetupOverridableTextureParameter(const UInterchangeShaderNode* ShaderNode, const FString& InputKey, const FString& OverridableParameterNameKey)
{
FString InputValue;
if (ShaderNode->GetStringAttribute(InputKey, InputValue))
{
const UE::Interchange::FAttributeKey AttributeKey(OverridableParameterNameKey);
if (!AttributeStorageNode->HasAttribute(AttributeKey))
{
if (!FPackageName::IsValidObjectPath(InputValue))
{
// Material Factory expects Texture Factory Uid as opposed to Texture Uid
const FString TextureFactoryUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(InputValue);
AttributeStorageNode->AddStringAttribute(OverridableParameterNameKey, TextureFactoryUid);
}
else
{
AttributeStorageNode->AddStringAttribute(OverridableParameterNameKey, InputValue);
}
LeafInputAttributeKeys.Add(AttributeKey);
LeafInputShaderNodes.Emplace(ShaderNode);
ADD_LOG_MESSAGE(TEXT("Texture Parameter: {0}({1})"), ShaderNode->GetDisplayLabel(), InputValue);
}
}
}
int32 FDuplicateMaterialHelper::HashCombineCustom(int32 Hash, int32 CombineWith)
{
Hash = HashCombine(Hash, CombineWith);
AccumulatedHash = HashCombine(AccumulatedHash, CombineWith);
return Hash;
}
void FDuplicateMaterialHelper::CopyLeafInputsToFactoryNode(UInterchangeBaseMaterialFactoryNode* FactoryNode)
{
UInterchangeBaseNode::CopyStorageAttributes(AttributeStorageNode, FactoryNode, LeafInputAttributeKeys);
}
UInterchangeBaseMaterialFactoryNode* FDuplicateMaterialHelper::CreateFactoryForDuplicateMaterials(const UInterchangeShaderGraphNode* ShaderGraphNode, bool bImportUnusedMaterial, bool bCreateMaterialInstanceForParent)
{
UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode = nullptr;
if (IsDuplicate())
{
MaterialFactoryNode = CreateMaterialInstanceFactoryFromReference(ShaderGraphNode);
}
else
{
MaterialFactoryNode = CreateMaterialFactory(ShaderGraphNode);
MaterialFactoryNode->SetEnabled(bImportUnusedMaterial);
if (bCreateMaterialInstanceForParent)
{
MaterialFactoryNode = CreateMaterialInstanceFactoryForParent(ShaderGraphNode);
}
}
return MaterialFactoryNode;
}
UInterchangeBaseMaterialFactoryNode* FDuplicateMaterialHelper::CreateMaterialFactory(const UInterchangeShaderGraphNode* ShaderGraphNode)
{
UInterchangeBaseMaterialFactoryNode* MaterialFactoryNode = GenericMaterialPipeline.CreateMaterialFactoryNode(ShaderGraphNode);
ParentMaterialFactoryMap.Emplace(MaterialHash, MaterialFactoryNode);
CopyLeafInputsToFactoryNode(MaterialFactoryNode);
return MaterialFactoryNode;
}
UInterchangeMaterialInstanceFactoryNode* FDuplicateMaterialHelper::CreateMaterialInstanceFactoryFromReference(const UInterchangeShaderGraphNode* ShaderGraphNode)
{
const UInterchangeBaseMaterialFactoryNode* ParentMaterialFactory = nullptr;
if (UInterchangeBaseMaterialFactoryNode** ParentMaterialFactoryEntry = ParentMaterialFactoryMap.Find(MaterialHash))
{
ParentMaterialFactory = *ParentMaterialFactoryEntry;
}
ensure(ParentMaterialFactory);
if (!ParentMaterialFactory)
{
return nullptr;
}
UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode =
Cast<UInterchangeMaterialInstanceFactoryNode>(GenericMaterialPipeline.CreateBaseMaterialFactoryNode(ShaderGraphNode, UInterchangeMaterialInstanceFactoryNode::StaticClass()));
ensure(MaterialInstanceFactoryNode);
if (ParentMaterialFactory)
{
MaterialInstanceFactoryNode->SetCustomParent(ParentMaterialFactory->GetUniqueID());
MaterialInstanceFactoryNode->AddFactoryDependencyUid(ParentMaterialFactory->GetUniqueID());
}
for (const auto& LeafInputKey : LeafInputAttributeKeys)
{
UE::Interchange::EAttributeTypes AttributeType = AttributeStorageNode->GetAttributeType(LeafInputKey);
switch (AttributeType)
{
case UE::Interchange::EAttributeTypes::Float:
{
float ParentValue;
float CurrentValue;
if (!AttributeStorageNode->GetFloatAttribute(LeafInputKey.Key, CurrentValue))
{
continue;
}
if (!ParentMaterialFactory->GetFloatAttribute(LeafInputKey.Key, ParentValue))
{
continue;
}
if (ParentValue != CurrentValue)
{
MaterialInstanceFactoryNode->AddFloatAttribute(LeafInputKey.Key, CurrentValue);
}
}
break;
case UE::Interchange::EAttributeTypes::LinearColor:
{
FLinearColor ParentValue;
FLinearColor CurrentValue;
if (!AttributeStorageNode->GetLinearColorAttribute(LeafInputKey.Key, CurrentValue))
{
continue;
}
if (!ParentMaterialFactory->GetLinearColorAttribute(LeafInputKey.Key, ParentValue))
{
continue;
}
if (ParentValue != CurrentValue)
{
MaterialInstanceFactoryNode->AddLinearColorAttribute(LeafInputKey.Key, CurrentValue);
}
}
break;
case UE::Interchange::EAttributeTypes::String:
{
FString ParentValue;
FString CurrentValue;
if (!AttributeStorageNode->GetStringAttribute(LeafInputKey.Key, CurrentValue))
{
continue;
}
if (!ParentMaterialFactory->GetStringAttribute(LeafInputKey.Key, ParentValue))
{
continue;
}
if (ParentValue != CurrentValue)
{
MaterialInstanceFactoryNode->AddStringAttribute(LeafInputKey.Key, CurrentValue);
}
}
break;
}
}
return MaterialInstanceFactoryNode;
}
UInterchangeMaterialInstanceFactoryNode* FDuplicateMaterialHelper::CreateMaterialInstanceFactoryForParent(const UInterchangeShaderGraphNode* ShaderGraphNode)
{
const UInterchangeBaseMaterialFactoryNode* ParentMaterialFactory = ParentMaterialFactoryMap[MaterialHash];
UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode =
Cast<UInterchangeMaterialInstanceFactoryNode>(GenericMaterialPipeline.CreateBaseMaterialFactoryNode(ShaderGraphNode, UInterchangeMaterialInstanceFactoryNode::StaticClass(), true));
if (ParentMaterialFactory)
{
MaterialInstanceFactoryNode->SetCustomParent(ParentMaterialFactory->GetUniqueID());
MaterialInstanceFactoryNode->AddFactoryDependencyUid(ParentMaterialFactory->GetUniqueID());
}
return MaterialInstanceFactoryNode;
}
}
#undef ADD_LOG_MESSAGE
#undef ADD_NODE_ADDRESS_MESSAGE
#undef PUSH_NODE_ADDRESS
#undef PUSH_NODE_ADDRESS_WITHOUT_CHECKPOINT
#undef POP_NODE_ADDRESSES
#undef LOCTEXT_NAMESPACE