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

354 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "InterchangeglTFPipeline.h"
#include "InterchangePipelineHelper.h"
#include "InterchangePipelineLog.h"
#include "InterchangeManager.h"
#include "InterchangeMaterialFactoryNode.h"
#include "InterchangeMaterialInstanceNode.h"
#include "InterchangeMeshFactoryNode.h"
#include "InterchangeShaderGraphNode.h"
#include "Nodes/InterchangeSourceNode.h"
#include "Gltf/InterchangeGLTFMaterial.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Misc/App.h"
const TArray<FString> UGLTFPipelineSettings::ExpectedMaterialInstanceIdentifiers = {TEXT("MI_Default_Opaque"), TEXT("MI_Default_Mask"), TEXT("MI_Default_Blend"),
TEXT("MI_Unlit_Opaque"), TEXT("MI_Unlit_Mask"), TEXT("MI_Unlit_Blend"),
TEXT("MI_ClearCoat_Opaque"), TEXT("MI_ClearCoat_Mask"), TEXT("MI_ClearCoat_Blend"),
TEXT("MI_Sheen_Opaque"), TEXT("MI_Sheen_Mask"), TEXT("MI_Sheen_Blend"),
TEXT("MI_Transmission"),
TEXT("MI_SpecularGlossiness_Opaque"), TEXT("MI_SpecularGlossiness_Mask"), TEXT("MI_SpecularGlossiness_Blend"),
TEXT("MI_Default_Opaque_DS"), TEXT("MI_Default_Mask_DS"), TEXT("MI_Default_Blend_DS"),
TEXT("MI_Unlit_Opaque_DS"), TEXT("MI_Unlit_Mask_DS"), TEXT("MI_Unlit_Blend_DS"),
TEXT("MI_ClearCoat_Opaque_DS"), TEXT("MI_ClearCoat_Mask_DS"), TEXT("MI_ClearCoat_Blend_DS"),
TEXT("MI_Sheen_Opaque_DS"), TEXT("MI_Sheen_Mask_DS"), TEXT("MI_Sheen_Blend_DS"),
TEXT("MI_Transmission_DS"),
TEXT("MI_SpecularGlossiness_Opaque_DS"), TEXT("MI_SpecularGlossiness_Mask_DS"), TEXT("MI_SpecularGlossiness_Blend_DS")};
TArray<FString> UGLTFPipelineSettings::ValidateMaterialInstancesAndParameters() const
{
TArray<FString> NotCoveredIdentifiersParameters;
//Check if all Material variations are covered:
TArray<FString> ExpectedIdentifiers = ExpectedMaterialInstanceIdentifiers;
TArray<FString> IdentifiersUsed;
MaterialParents.GetKeys(IdentifiersUsed);
for (const FString& Identifier : IdentifiersUsed)
{
ExpectedIdentifiers.Remove(Identifier);
}
for (const FString& ExpectedIdentifier : ExpectedIdentifiers)
{
NotCoveredIdentifiersParameters.Add(TEXT("[") + ExpectedIdentifier + TEXT("]: MaterialInstance not found for Identifier."));
}
for (const TPair<FString, FSoftObjectPath>& MaterialParent : MaterialParents)
{
TSet<FString> ExpectedParameters = GenerateExpectedParametersList(MaterialParent.Key);
if (UMaterialInstance* ParentMaterialInstance = Cast<UMaterialInstance>(MaterialParent.Value.TryLoad()))
{
TArray<FGuid> ParameterIds;
TArray<FMaterialParameterInfo> ScalarParameterInfos;
TArray<FMaterialParameterInfo> VectorParameterInfos;
TArray<FMaterialParameterInfo> TextureParameterInfos;
ParentMaterialInstance->GetAllScalarParameterInfo(ScalarParameterInfos, ParameterIds);
ParentMaterialInstance->GetAllVectorParameterInfo(VectorParameterInfos, ParameterIds);
ParentMaterialInstance->GetAllTextureParameterInfo(TextureParameterInfos, ParameterIds);
for (const FMaterialParameterInfo& ParameterInfo : ScalarParameterInfos)
{
ExpectedParameters.Remove(ParameterInfo.Name.ToString());
}
for (const FMaterialParameterInfo& ParameterInfo : VectorParameterInfos)
{
ExpectedParameters.Remove(ParameterInfo.Name.ToString());
}
for (const FMaterialParameterInfo& ParameterInfo : TextureParameterInfos)
{
ExpectedParameters.Remove(ParameterInfo.Name.ToString());
}
}
for (const FString& ExpectedParameter : ExpectedParameters)
{
NotCoveredIdentifiersParameters.Add(TEXT("[") + MaterialParent.Key + TEXT("]: Does not cover expected parameter: ") + ExpectedParameter + TEXT("."));
}
}
return NotCoveredIdentifiersParameters;
}
TSet<FString> UGLTFPipelineSettings::GenerateExpectedParametersList(const FString& Identifier) const
{
using namespace UE::Interchange::GLTFMaterials;
TSet<FString> ExpectedParameters;
auto AddTextureAndRelated = [&ExpectedParameters](const FString& TextureName)
{
ExpectedParameters.Add(TextureName);
ExpectedParameters.Add(TextureName + Inputs::PostFix::OffsetScale);
ExpectedParameters.Add(TextureName + Inputs::PostFix::Rotation);
ExpectedParameters.Add(TextureName + Inputs::PostFix::TexCoord);
ExpectedParameters.Add(TextureName + Inputs::PostFix::TilingMethod);
};
if (Identifier.Contains(TEXT("_Unlit")))
{
AddTextureAndRelated(Inputs::BaseColorTexture);
ExpectedParameters.Add(Inputs::BaseColorFactor);
return ExpectedParameters;
}
//Generic ones:
{
AddTextureAndRelated(Inputs::NormalTexture);
ExpectedParameters.Add(Inputs::NormalScale);
if (!Identifier.Contains(TEXT("Transmission")))
{
AddTextureAndRelated(Inputs::EmissiveTexture);
ExpectedParameters.Add(Inputs::EmissiveFactor);
ExpectedParameters.Add(Inputs::EmissiveStrength);
}
AddTextureAndRelated(Inputs::OcclusionTexture);
ExpectedParameters.Add(Inputs::OcclusionStrength);
if (!Identifier.Contains(TEXT("SpecularGlossiness")))
{
ExpectedParameters.Add(Inputs::IOR);
AddTextureAndRelated(Inputs::SpecularTexture);
ExpectedParameters.Add(Inputs::SpecularFactor);
}
}
//Based on ShadingModel:
if (Identifier.Contains(TEXT("Default")))
{
//MetalRoughness Specific:
AddTextureAndRelated(Inputs::BaseColorTexture);
ExpectedParameters.Add(Inputs::BaseColorFactor);
AddTextureAndRelated(Inputs::MetallicRoughnessTexture);
ExpectedParameters.Add(Inputs::MetallicFactor);
ExpectedParameters.Add(Inputs::RoughnessFactor);
}
else if (Identifier.Contains(TEXT("ClearCoat")))
{
AddTextureAndRelated(Inputs::ClearCoatTexture);
ExpectedParameters.Add(Inputs::ClearCoatFactor);
AddTextureAndRelated(Inputs::ClearCoatRoughnessTexture);
ExpectedParameters.Add(Inputs::ClearCoatRoughnessFactor);
AddTextureAndRelated(Inputs::ClearCoatNormalTexture);
ExpectedParameters.Add(Inputs::ClearCoatNormalScale);
}
else if (Identifier.Contains(TEXT("Sheen")))
{
AddTextureAndRelated(Inputs::SheenColorTexture);
ExpectedParameters.Add(Inputs::SheenColorFactor);
AddTextureAndRelated(Inputs::SheenRoughnessTexture);
ExpectedParameters.Add(Inputs::SheenRoughnessFactor);
}
else if (Identifier.Contains(TEXT("Transmission")))
{
AddTextureAndRelated(Inputs::TransmissionTexture);
ExpectedParameters.Add(Inputs::TransmissionFactor);
}
else if (Identifier.Contains(TEXT("SpecularGlossiness")))
{
AddTextureAndRelated(Inputs::DiffuseTexture);
ExpectedParameters.Add(Inputs::DiffuseFactor);
AddTextureAndRelated(Inputs::SpecularGlossinessTexture);
ExpectedParameters.Add(Inputs::SpecFactor);
ExpectedParameters.Add(Inputs::GlossinessFactor);
}
return ExpectedParameters;
}
UInterchangeMaterialInstanceFactoryNode* UGLTFPipelineSettings::BuildMaterialInstance(UInterchangeBaseNodeContainer* NodeContainer, const UInterchangeShaderGraphNode* ShaderGraphNode, const FString& OldFactoryNodeUId)
{
using namespace UE::Interchange::GLTFMaterials;
TArray<UE::Interchange::FAttributeKey> AttributeKeys;
ShaderGraphNode->GetAttributeKeys(AttributeKeys);
TMap<FString, UE::Interchange::FAttributeKey> GltfAttributeKeys;
for (const UE::Interchange::FAttributeKey& AttributeKey : AttributeKeys)
{
if (AttributeKey.ToString().Contains(InterchangeGltfMaterialAttributeIdentifier))
{
GltfAttributeKeys.Add(AttributeKey.ToString().Replace(*InterchangeGltfMaterialAttributeIdentifier, TEXT(""), ESearchCase::CaseSensitive), AttributeKey);
}
}
if (GltfAttributeKeys.Num() == 0)
{
return nullptr;
}
FString ParentIdentifier;
if (!ShaderGraphNode->GetStringAttribute(*(InterchangeGltfMaterialAttributeIdentifier + TEXT("ParentIdentifier")), ParentIdentifier))
{
return nullptr;
}
GltfAttributeKeys.Remove(TEXT("ParentIdentifier"));
FString Parent;
if (const FSoftObjectPath* ObjectPath = MaterialParents.Find(ParentIdentifier))
{
Parent = ObjectPath->GetAssetPathString();
}
else
{
UE_LOG(LogInterchangePipeline, Warning, TEXT("[Interchange] Failed to load MaterialParent for ParentIdentifier: %s"), *ParentIdentifier);
return nullptr;
}
FString MaterialFactoryNodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(ShaderGraphNode->GetUniqueID());
FString MaterialFactoryNodeName = ShaderGraphNode->GetDisplayLabel();
UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode = NewObject<UInterchangeMaterialInstanceFactoryNode>(NodeContainer);
NodeContainer->SetupAndReplaceFactoryNode(MaterialInstanceFactoryNode, MaterialFactoryNodeUid, MaterialFactoryNodeName, EInterchangeNodeContainerType::FactoryData, OldFactoryNodeUId);
MaterialInstanceFactoryNode->SetCustomParent(Parent);
UInterchangeEditorUtilitiesBase* EditorUtilities = UInterchangeManager::GetInterchangeManager().GetEditorUtilities();
const UClass* MaterialClass = (EditorUtilities && EditorUtilities->IsRuntimeOrPIE()) ? UMaterialInstanceDynamic::StaticClass() : UMaterialInstanceConstant::StaticClass();
MaterialInstanceFactoryNode->SetCustomInstanceClassName(MaterialClass->GetPathName());
for (const TPair<FString, UE::Interchange::FAttributeKey>& GltfAttributeKey : GltfAttributeKeys)
{
UE::Interchange::EAttributeTypes AttributeType = ShaderGraphNode->GetAttributeType(GltfAttributeKey.Value);
FString InputValueKey = UInterchangeShaderPortsAPI::MakeInputValueKey(GltfAttributeKey.Key);
//we are only using 4 attribute types for now:
switch (AttributeType)
{
case UE::Interchange::EAttributeTypes::Bool:
{
bool Value;
if (ShaderGraphNode->GetBooleanAttribute(GltfAttributeKey.Value.Key, Value))
{
MaterialInstanceFactoryNode->AddBooleanAttribute(InputValueKey, Value);
}
}
break;
case UE::Interchange::EAttributeTypes::Float:
{
float Value;
if (ShaderGraphNode->GetFloatAttribute(GltfAttributeKey.Value.Key, Value))
{
MaterialInstanceFactoryNode->AddFloatAttribute(InputValueKey, Value);
}
}
break;
case UE::Interchange::EAttributeTypes::LinearColor:
{
FLinearColor Value;
if (ShaderGraphNode->GetLinearColorAttribute(GltfAttributeKey.Value.Key, Value))
{
MaterialInstanceFactoryNode->AddLinearColorAttribute(InputValueKey, Value);
}
}
break;
case UE::Interchange::EAttributeTypes::String:
{
FString TextureUid;
if (ShaderGraphNode->GetStringAttribute(GltfAttributeKey.Value.Key, TextureUid))
{
FString FactoryTextureUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(TextureUid);
MaterialInstanceFactoryNode->AddStringAttribute(InputValueKey, FactoryTextureUid);
MaterialInstanceFactoryNode->AddFactoryDependencyUid(FactoryTextureUid);
}
}
break;
default:
break;
}
}
return MaterialInstanceFactoryNode;
}
UInterchangeGLTFPipeline::UInterchangeGLTFPipeline()
: GLTFPipelineSettings(UGLTFPipelineSettings::StaticClass()->GetDefaultObject<UGLTFPipelineSettings>())
{
}
void UInterchangeGLTFPipeline::AdjustSettingsForContext(const FInterchangePipelineContextParams& ContextParams)
{
Super::AdjustSettingsForContext(ContextParams);
TArray<FString> MaterialInstanceIssues = GLTFPipelineSettings->ValidateMaterialInstancesAndParameters();
for (const FString& MaterialInstanceIssue : MaterialInstanceIssues)
{
UE_LOG(LogInterchangePipeline, Warning, TEXT("%s"), *MaterialInstanceIssue);
}
}
void UInterchangeGLTFPipeline::ExecutePipeline(UInterchangeBaseNodeContainer* NodeContainer, const TArray<UInterchangeSourceData*>& InSourceDatas, const FString& ContentBasePath)
{
Super::ExecutePipeline(NodeContainer, InSourceDatas, ContentBasePath);
if (GLTFPipelineSettings)
{
TMap<FString, const UInterchangeShaderGraphNode*> MaterialFactoryNodeUidsToShaderGraphNodes;
auto FindGLTFShaderGraphNode = [&MaterialFactoryNodeUidsToShaderGraphNodes, &NodeContainer](const FString& NodeUid, UInterchangeFactoryBaseNode* /*Material or MaterialInstance*/ FactoryNode)
{
TArray<FString> TargetNodeUids;
FactoryNode->GetTargetNodeUids(TargetNodeUids);
for (const FString& TargetNodeUid : TargetNodeUids)
{
if (const UInterchangeShaderGraphNode* ShaderGraphNode = Cast<UInterchangeShaderGraphNode>(NodeContainer->GetNode(TargetNodeUid)))
{
FString ParentIdentifier;
if (ShaderGraphNode->GetStringAttribute(*(InterchangeGltfMaterialAttributeIdentifier + TEXT("ParentIdentifier")), ParentIdentifier))
{
MaterialFactoryNodeUidsToShaderGraphNodes.Add(NodeUid, ShaderGraphNode);
break;
}
}
}
};
NodeContainer->IterateNodesOfType<UInterchangeMaterialFactoryNode>([&MaterialFactoryNodeUidsToShaderGraphNodes, &NodeContainer, &FindGLTFShaderGraphNode](const FString& NodeUid, UInterchangeMaterialFactoryNode* MaterialFactoryNode)
{
FindGLTFShaderGraphNode(NodeUid, MaterialFactoryNode);
});
NodeContainer->IterateNodesOfType<UInterchangeMaterialInstanceFactoryNode>([&MaterialFactoryNodeUidsToShaderGraphNodes, &NodeContainer, &FindGLTFShaderGraphNode](const FString& NodeUid, UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode)
{
FindGLTFShaderGraphNode(NodeUid, MaterialInstanceFactoryNode);
});
for (const TPair<FString, const UInterchangeShaderGraphNode*>& ShaderGraphNode : MaterialFactoryNodeUidsToShaderGraphNodes)
{
UInterchangeMaterialInstanceFactoryNode* MaterialInstanceFactoryNode = GLTFPipelineSettings->BuildMaterialInstance(NodeContainer, ShaderGraphNode.Value, ShaderGraphNode.Key);
UInterchangeSourceNode* SourceNode = UInterchangeSourceNode::FindOrCreateUniqueInstance(NodeContainer);
UE::Interchange::PipelineHelper::FillSubPathFromSourceNode(MaterialInstanceFactoryNode, SourceNode);
}
}
}