977 lines
33 KiB
C++
977 lines
33 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MaterialImportUtils.h"
|
|
|
|
|
|
#include "DatasmithMaterialElements.h"
|
|
#include "Engine/Texture.h"
|
|
#include "ReferenceMaterials/DatasmithReferenceMaterial.h"
|
|
#include "ReferenceMaterials/DatasmithReferenceMaterialManager.h"
|
|
#include "ReferenceMaterials/DatasmithReferenceMaterialSelector.h"
|
|
|
|
#include "Materials/Material.h"
|
|
#include "Materials/MaterialInstanceDynamic.h"
|
|
|
|
#include "SceneImporter.h"
|
|
|
|
namespace DatasmithRuntime
|
|
{
|
|
const TCHAR* MATERIAL_HOST = TEXT("_Runtime_");
|
|
|
|
namespace EPbrTexturePropertySlot
|
|
{
|
|
enum Type
|
|
{
|
|
BumpMapSlot = 0,
|
|
DiffuseMapSlot = 1,
|
|
NormalMapSlot = 2,
|
|
EmissiveMapSlot = 3,
|
|
RoughnessMapSlot = 4,
|
|
OpacityMapSlot = 5,
|
|
RefractionMapSlot = 6,
|
|
MetallicMapSlot = 7,
|
|
SpecularMapSlot = 8,
|
|
MaxSlots = 9,
|
|
};
|
|
}
|
|
|
|
FName PbrTexturePropertyNames[EPbrTexturePropertySlot::MaxSlots] =
|
|
{
|
|
TEXT("BumpMap"),
|
|
TEXT("DiffuseMap"),
|
|
TEXT("NormalMap"),
|
|
TEXT("EmissiveMap"),
|
|
TEXT("RoughnessMap"),
|
|
TEXT("OpacityMap"),
|
|
TEXT("RefractionMap"),
|
|
TEXT("MetallicMap"),
|
|
TEXT("SpecularMap"),
|
|
};
|
|
|
|
constexpr const TCHAR* OpaqueMaterialPath = TEXT("/DatasmithRuntime/Materials/M_PbrOpaque.M_PbrOpaque");
|
|
constexpr const TCHAR* OpaqueMaterialPath_2Sided = TEXT("/DatasmithRuntime/Materials/M_PbrOpaque_2Sided.M_PbrOpaque_2Sided");
|
|
constexpr const TCHAR* TranslucentMaterialPath = TEXT("/DatasmithRuntime/Materials/M_PbrTranslucent.M_PbrTranslucent");
|
|
constexpr const TCHAR* TranslucentMaterialPath_2Sided = TEXT("/DatasmithRuntime/Materials/M_PbrTranslucent_2Sided.M_PbrTranslucent_2Sided");
|
|
|
|
struct FMaterialParameters
|
|
{
|
|
TMap< FName, int32 > VectorParams;
|
|
TMap< FName, int32 > ScalarParams;
|
|
TMap< FName, int32 > TextureParams;
|
|
TMap< FName, int32 > BoolParams;
|
|
};
|
|
|
|
extern const FString TexturePrefix;
|
|
extern const FString MaterialPrefix;
|
|
extern const FString MeshPrefix;
|
|
|
|
static TMap< UMaterialInterface*, FMaterialParameters > MaterialParametersCache;
|
|
|
|
const FMaterialParameters& GetMaterialParameters(UMaterialInterface* Material)
|
|
{
|
|
check(Material);
|
|
|
|
if (const FMaterialParameters* Parameters = MaterialParametersCache.Find(Material))
|
|
{
|
|
return *Parameters;
|
|
}
|
|
|
|
FMaterialParameters Parameters;
|
|
|
|
TArray<FMaterialParameterInfo> ParameterInfos;
|
|
TArray<FGuid> ParameterIds;
|
|
Material->GetAllScalarParameterInfo(ParameterInfos, ParameterIds);
|
|
|
|
for (FMaterialParameterInfo& ParameterInfo : ParameterInfos)
|
|
{
|
|
Parameters.ScalarParams.Add(ParameterInfo.Name, ParameterInfo.Index);
|
|
}
|
|
|
|
ParameterInfos.Reset();
|
|
ParameterIds.Reset();
|
|
Material->GetAllVectorParameterInfo(ParameterInfos, ParameterIds);
|
|
|
|
for (FMaterialParameterInfo& ParameterInfo : ParameterInfos)
|
|
{
|
|
Parameters.VectorParams.Add(ParameterInfo.Name, ParameterInfo.Index);
|
|
}
|
|
|
|
ParameterInfos.Reset();
|
|
ParameterIds.Reset();
|
|
Material->GetAllTextureParameterInfo(ParameterInfos, ParameterIds);
|
|
|
|
for (FMaterialParameterInfo& ParameterInfo : ParameterInfos)
|
|
{
|
|
Parameters.TextureParams.Add(ParameterInfo.Name, ParameterInfo.Index);
|
|
}
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
ParameterInfos.Reset();
|
|
ParameterIds.Reset();
|
|
Material->GetAllStaticSwitchParameterInfo(ParameterInfos, ParameterIds);
|
|
|
|
for (FMaterialParameterInfo& ParameterInfo : ParameterInfos)
|
|
{
|
|
Parameters.BoolParams.Add(ParameterInfo.Name, ParameterInfo.Index);
|
|
}
|
|
#endif
|
|
|
|
FMaterialParameters& ParametersRef = MaterialParametersCache.Add(Material);
|
|
ParametersRef = MoveTemp(Parameters);
|
|
|
|
return ParametersRef;
|
|
}
|
|
|
|
int32 ProcessMaterialElement(TSharedPtr< IDatasmithMaterialInstanceElement > ReferenceMaterialElement, FTextureCallback TextureCallback)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(DatasmithRuntime::ProcessMaterialElement);
|
|
|
|
// Must be updated if FDatasmithMaterialImporter::GetMaterialRequirements changes
|
|
int32 MaterialRequirement = EMaterialRequirements::RequiresNormals | EMaterialRequirements::RequiresTangents;
|
|
|
|
if (!ReferenceMaterialElement.IsValid())
|
|
{
|
|
return MaterialRequirement;
|
|
}
|
|
|
|
TSharedPtr< FDatasmithReferenceMaterialSelector > MaterialSelector = FDatasmithReferenceMaterialManager::Get().GetSelector(MATERIAL_HOST);
|
|
|
|
UMaterialInterface* Material = nullptr;
|
|
|
|
if (ReferenceMaterialElement->GetMaterialType() == EDatasmithReferenceMaterialType::Custom)
|
|
{
|
|
FDatasmithReferenceMaterial CustomReferenceMaterial; // ReferenceMaterial might point on this so keep them in the same scope
|
|
|
|
CustomReferenceMaterial.FromSoftObjectPath(FSoftObjectPath(ReferenceMaterialElement->GetCustomMaterialPathName()));
|
|
|
|
if (CustomReferenceMaterial.IsValid())
|
|
{
|
|
Material = CustomReferenceMaterial.GetMaterial();
|
|
}
|
|
}
|
|
else if (MaterialSelector.IsValid() && MaterialSelector->IsValid())
|
|
{
|
|
const FDatasmithReferenceMaterial& ReferenceMaterial = MaterialSelector->GetReferenceMaterial(ReferenceMaterialElement);
|
|
|
|
if (ReferenceMaterial.IsValid())
|
|
{
|
|
Material = ReferenceMaterial.GetMaterial();
|
|
}
|
|
}
|
|
|
|
if (Material)
|
|
{
|
|
const TMap< FName, int32 >& TextureParams = GetMaterialParameters(Material).TextureParams;
|
|
|
|
for (int Index = 0; Index < ReferenceMaterialElement->GetPropertiesCount(); ++Index)
|
|
{
|
|
const TSharedPtr< IDatasmithKeyValueProperty > Property = ReferenceMaterialElement->GetProperty(Index);
|
|
const FName PropertyName(Property->GetName());
|
|
|
|
if (TextureParams.Contains(PropertyName))
|
|
{
|
|
FString TextureName;
|
|
if ( MaterialSelector->GetTexture( Property, TextureName ) )
|
|
{
|
|
TextureCallback(TexturePrefix + TextureName, Index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return MaterialRequirement;
|
|
}
|
|
|
|
bool LoadReferenceMaterial(UMaterialInstanceDynamic* MaterialInstance, TSharedPtr<IDatasmithMaterialInstanceElement>& MaterialElement)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(DatasmithRuntime::LoadReferenceMaterial);
|
|
|
|
FDatasmithReferenceMaterialManager& MaterialManager = FDatasmithReferenceMaterialManager::Get();
|
|
const FString Host = MaterialManager.GetHostFromString( MATERIAL_HOST );
|
|
TSharedPtr< FDatasmithReferenceMaterialSelector > MaterialSelector = MaterialManager.GetSelector( MATERIAL_HOST );
|
|
|
|
UMaterialInterface* ParentMaterial = nullptr;
|
|
|
|
{
|
|
if ( MaterialElement->GetMaterialType() == EDatasmithReferenceMaterialType::Custom )
|
|
{
|
|
FDatasmithReferenceMaterial CustomReferenceMaterial;
|
|
|
|
CustomReferenceMaterial.FromSoftObjectPath( FSoftObjectPath( MaterialElement->GetCustomMaterialPathName() ) );
|
|
ParentMaterial = CustomReferenceMaterial.GetMaterial();
|
|
}
|
|
else if ( MaterialSelector.IsValid() )
|
|
{
|
|
const FDatasmithReferenceMaterial& DatasmithReferenceMaterial = MaterialSelector->GetReferenceMaterial(MaterialElement);
|
|
ParentMaterial = DatasmithReferenceMaterial.GetMaterial();
|
|
}
|
|
}
|
|
|
|
if (ParentMaterial == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
MaterialInstance->Parent = ParentMaterial;
|
|
|
|
const FMaterialParameters& MaterialParameters = GetMaterialParameters(ParentMaterial);
|
|
|
|
for (int Index = 0; Index < MaterialElement->GetPropertiesCount(); ++Index)
|
|
{
|
|
const TSharedPtr< IDatasmithKeyValueProperty >& Property = MaterialElement->GetProperty(Index);
|
|
FName PropertyName(Property->GetName());
|
|
|
|
// Vector Params
|
|
if ( MaterialParameters.VectorParams.Contains(PropertyName) )
|
|
{
|
|
FLinearColor Color;
|
|
if ( MaterialSelector->GetColor( Property, Color ) )
|
|
{
|
|
MaterialInstance->SetVectorParameterValue(PropertyName, Color);
|
|
}
|
|
}
|
|
// Scalar Params
|
|
else if ( MaterialParameters.ScalarParams.Contains(PropertyName) )
|
|
{
|
|
float Value;
|
|
if ( MaterialSelector->GetFloat( Property, Value ) )
|
|
{
|
|
MaterialInstance->SetScalarParameterValue(PropertyName, Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TSharedPtr<IDatasmithUEPbrMaterialElement> ValidatePbrMaterial( TSharedPtr<IDatasmithUEPbrMaterialElement> PbrMaterialElement, FSceneImporter& SceneImporter )
|
|
{
|
|
// Assuming Pbr materials using material attributes are layered materials
|
|
if (PbrMaterialElement->GetUseMaterialAttributes())
|
|
{
|
|
for (int32 Index = 0; Index < PbrMaterialElement->GetExpressionsCount(); ++Index)
|
|
{
|
|
IDatasmithMaterialExpression* Expression = PbrMaterialElement->GetExpression(Index);
|
|
if (Expression && Expression->IsSubType(EDatasmithMaterialExpressionType::FunctionCall))
|
|
{
|
|
IDatasmithMaterialExpressionFunctionCall* FunctionCall = static_cast<IDatasmithMaterialExpressionFunctionCall*>(Expression);
|
|
TSharedPtr< IDatasmithElement > ElementPtr = SceneImporter.GetElementFromName(MaterialPrefix + FunctionCall->GetFunctionPathName());
|
|
ensure(ElementPtr.IsValid() && ElementPtr->IsA( EDatasmithElementType::UEPbrMaterial ));
|
|
|
|
return StaticCastSharedPtr<IDatasmithUEPbrMaterialElement>(ElementPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
return PbrMaterialElement;
|
|
}
|
|
|
|
// Class that try to evaluate Datasmith PBR materials so we cant create a Twinmotion material that fit relatively well.
|
|
// Twinmotion cannot implement real PBR shader. Because we cannot compile them.
|
|
class FDatasmithInputValue // TODO: #todo_tm [CodingStyle] RichardY CDatasmithPBRMaterialEvaluator
|
|
{
|
|
public:
|
|
// Texture informations evaluated
|
|
class FTexture
|
|
{
|
|
public:
|
|
FString TextureName;
|
|
int32 CoordinateIndex = 0;
|
|
bool bMirrorU = false;
|
|
bool bMirrorV = false;
|
|
float Fading = 1.0f;
|
|
float UTiling = 1.0f;
|
|
float VTiling = 1.0f;
|
|
float UTilingPivot = 0.5f;
|
|
float VTilingPivot = 0.5f;
|
|
float UOffset = 0.0f;
|
|
float VOffset = 0.0f;
|
|
float URotationPivot = 0.5f;
|
|
float VRotationPivot = 0.5f;
|
|
float Rotation = 0.0f;
|
|
float UVOffset = 0.0f;
|
|
bool bUseAlpha = false;
|
|
bool bIsBumpMap = false;
|
|
|
|
// Constructor
|
|
FTexture(const TCHAR* InName): TextureName(InName) {}
|
|
};
|
|
|
|
FLinearColor Numeric = FLinearColor(ForceInit); // Numeric value (scalar, color or vector)
|
|
bool bNumericValid = false; // If numerical value is valid
|
|
TUniquePtr<FTexture> Texture; // If we have a Texture defined TODO: #todo_tm [CodingStyle] RichardY TUniquePtr
|
|
|
|
// Empty constructor
|
|
FDatasmithInputValue() {}
|
|
|
|
// Constructor with an expression
|
|
FDatasmithInputValue(const IDatasmithExpressionInput& InInput)
|
|
{
|
|
EvaluateInput(InInput);
|
|
}
|
|
|
|
// Evaluate this expression
|
|
void EvaluateInput(const IDatasmithExpressionInput& InInput); // TODO: #todo_tm [CodingStyle] RichardY EvaluateExpression
|
|
|
|
// Set scalar value (value is set for all components)
|
|
void Set(float InScalar) { Numeric.R = Numeric.G = Numeric.B = Numeric.A = InScalar; bNumericValid = true; }
|
|
|
|
// Set a numeric value (color or vector)
|
|
void Set(const FLinearColor& InNumeric) { Numeric = InNumeric; bNumericValid = true; }
|
|
|
|
// Add Texture information for the specified Texture name
|
|
void Set(const TCHAR* InTextureName) { Texture = MakeUnique<FTexture>(InTextureName); }
|
|
|
|
// Get scalar value (We use index 0, but normally all components are equals)
|
|
float GetScalar() const { return Numeric.Component(0); }
|
|
};
|
|
|
|
int32 ProcessMaterialElement( IDatasmithUEPbrMaterialElement* PbrMaterialElement, FTextureCallback TextureCallback)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(DatasmithRuntime::ProcessPbrMaterialElement);
|
|
|
|
// Must be updated if FDatasmithMaterialImporter::GetMaterialRequirements changes
|
|
int32 MaterialRequirement = EMaterialRequirements::RequiresNormals | EMaterialRequirements::RequiresTangents;
|
|
|
|
if (!PbrMaterialElement)
|
|
{
|
|
return MaterialRequirement;
|
|
}
|
|
|
|
TFunction<void(const IDatasmithExpressionInput&)> ParseExpression;
|
|
ParseExpression = [&TextureCallback, &ParseExpression](const IDatasmithExpressionInput& Input) -> void
|
|
{
|
|
if (const IDatasmithMaterialExpression* MaterialExpression = Input.GetExpression())
|
|
{
|
|
if (MaterialExpression->IsSubType(EDatasmithMaterialExpressionType::Texture))
|
|
{
|
|
const IDatasmithMaterialExpressionTexture* TextureExpression = static_cast<const IDatasmithMaterialExpressionTexture*>(MaterialExpression);
|
|
TextureCallback(TexturePrefix + TextureExpression->GetTexturePathName(), -1);
|
|
}
|
|
|
|
for (int32 InputIndex = 0; InputIndex < MaterialExpression->GetInputCount(); ++InputIndex)
|
|
{
|
|
if (MaterialExpression->GetInput(InputIndex))
|
|
{
|
|
ParseExpression(*MaterialExpression->GetInput(InputIndex));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
TFunction<void(IDatasmithExpressionInput&, int32)> FindTexture;
|
|
FindTexture = [TextureCallback](IDatasmithExpressionInput& Input, int32 SlotIndex) -> void
|
|
{
|
|
FDatasmithInputValue InputValue(Input);
|
|
|
|
if (InputValue.Texture && !InputValue.Texture->TextureName.IsEmpty())
|
|
{
|
|
if (!InputValue.Texture->TextureName.StartsWith(TEXT("/")))
|
|
{
|
|
TextureCallback(TexturePrefix + InputValue.Texture->TextureName, InputValue.Texture->bIsBumpMap ? EPbrTexturePropertySlot::BumpMapSlot : SlotIndex);
|
|
}
|
|
}
|
|
};
|
|
|
|
FindTexture(PbrMaterialElement->GetBaseColor(), EPbrTexturePropertySlot::DiffuseMapSlot);
|
|
FindTexture(PbrMaterialElement->GetOpacity(), EPbrTexturePropertySlot::OpacityMapSlot);
|
|
FindTexture(PbrMaterialElement->GetNormal(), EPbrTexturePropertySlot::NormalMapSlot);
|
|
FindTexture(PbrMaterialElement->GetRoughness(), EPbrTexturePropertySlot::RoughnessMapSlot);
|
|
FindTexture(PbrMaterialElement->GetRefraction(), EPbrTexturePropertySlot::RefractionMapSlot);
|
|
FindTexture(PbrMaterialElement->GetEmissiveColor(), EPbrTexturePropertySlot::EmissiveMapSlot);
|
|
FindTexture(PbrMaterialElement->GetMetallic(), EPbrTexturePropertySlot::MetallicMapSlot);
|
|
FindTexture(PbrMaterialElement->GetSpecular(), EPbrTexturePropertySlot::SpecularMapSlot);
|
|
|
|
return MaterialRequirement;
|
|
}
|
|
|
|
// Borrowed from TM, CDatasmithMaterialImportEntry::EvalUEPbrMaterial()
|
|
bool LoadPbrMaterial(IDatasmithUEPbrMaterialElement& UEPbrMaterial, UMaterialInstanceDynamic* MaterialInstance)
|
|
{
|
|
if (!UEPbrMaterial.GetMaterialFunctionOnly())
|
|
{
|
|
TFunction<void(const FDatasmithInputValue::FTexture*, int32)> SetTextureParams;
|
|
SetTextureParams = [MaterialInstance](const FDatasmithInputValue::FTexture* Texture, int32 SlotIndex) -> void
|
|
{
|
|
if (Texture && !Texture->TextureName.IsEmpty())
|
|
{
|
|
if (Texture->TextureName.StartsWith(TEXT("/")))
|
|
{
|
|
FSoftObjectPath SoftObject(*Texture->TextureName);
|
|
if (UTexture* TextureAsset = Cast<UTexture>(SoftObject.TryLoad()))
|
|
{
|
|
MaterialInstance->SetTextureParameterValue(PbrTexturePropertyNames[SlotIndex], TextureAsset);
|
|
}
|
|
}
|
|
|
|
FString RootName(PbrTexturePropertyNames[SlotIndex].ToString());
|
|
MaterialInstance->SetScalarParameterValue(*(RootName + TEXT("Fading")), Texture->Fading);
|
|
MaterialInstance->SetScalarParameterValue(*(RootName + TEXT("_UOffset")), Texture->UOffset);
|
|
MaterialInstance->SetScalarParameterValue(*(RootName + TEXT("_VOffset")), Texture->VOffset);
|
|
MaterialInstance->SetScalarParameterValue(*(RootName + TEXT("_UTiling")), Texture->UTiling);
|
|
MaterialInstance->SetScalarParameterValue(*(RootName + TEXT("_VTiling")), Texture->VTiling);
|
|
MaterialInstance->SetScalarParameterValue(*(RootName + TEXT("_UTilingPivot")), Texture->UTilingPivot);
|
|
MaterialInstance->SetScalarParameterValue(*(RootName + TEXT("_VTilingPivot")), Texture->VTilingPivot);
|
|
MaterialInstance->SetScalarParameterValue(*(RootName + TEXT("_RotAngle")), Texture->Rotation);
|
|
MaterialInstance->SetScalarParameterValue(*(RootName + TEXT("_URotPivot")), Texture->URotationPivot);
|
|
MaterialInstance->SetScalarParameterValue(*(RootName + TEXT("_VRotPivot")), Texture->VRotationPivot);
|
|
}
|
|
};
|
|
|
|
FDatasmithInputValue OpacityValue(UEPbrMaterial.GetOpacity());
|
|
FDatasmithInputValue RefractionValue(UEPbrMaterial.GetRefraction());
|
|
|
|
bool bNeedsTranslucent = (OpacityValue.bNumericValid && OpacityValue.GetScalar() != 1.0f) || OpacityValue.Texture || RefractionValue.bNumericValid || RefractionValue.Texture;
|
|
|
|
if (UEPbrMaterial.GetTwoSided())
|
|
{
|
|
FSoftObjectPath SoftObject(bNeedsTranslucent ? TranslucentMaterialPath_2Sided : OpaqueMaterialPath_2Sided);
|
|
MaterialInstance->Parent = Cast<UMaterial>(SoftObject.TryLoad());
|
|
}
|
|
else
|
|
{
|
|
FSoftObjectPath SoftObject(bNeedsTranslucent ? TranslucentMaterialPath : OpaqueMaterialPath);
|
|
MaterialInstance->Parent = Cast<UMaterial>(SoftObject.TryLoad());
|
|
}
|
|
check(MaterialInstance->Parent);
|
|
|
|
// PBR Material are too complex to be fully supported with simple Twinmotion material
|
|
{
|
|
if (OpacityValue.bNumericValid && OpacityValue.GetScalar() != 1.0f)
|
|
{
|
|
MaterialInstance->SetScalarParameterValue(TEXT("Opacity"), OpacityValue.GetScalar());
|
|
}
|
|
|
|
SetTextureParams(OpacityValue.Texture.Get(), EPbrTexturePropertySlot::OpacityMapSlot);
|
|
}
|
|
|
|
{
|
|
if (RefractionValue.bNumericValid && RefractionValue.GetScalar() != 0.0f)
|
|
{
|
|
MaterialInstance->SetScalarParameterValue(TEXT("Refraction"), RefractionValue.GetScalar());
|
|
}
|
|
|
|
SetTextureParams(RefractionValue.Texture.Get(), EPbrTexturePropertySlot::RefractionMapSlot);
|
|
}
|
|
|
|
{
|
|
FDatasmithInputValue InputValue(UEPbrMaterial.GetBaseColor());
|
|
|
|
if (InputValue.bNumericValid)
|
|
{
|
|
MaterialInstance->SetVectorParameterValue(TEXT("DiffuseColor"), InputValue.Numeric);
|
|
}
|
|
|
|
SetTextureParams(InputValue.Texture.Get(), EPbrTexturePropertySlot::DiffuseMapSlot);
|
|
}
|
|
|
|
{
|
|
FDatasmithInputValue InputValue(UEPbrMaterial.GetRoughness());
|
|
|
|
if (InputValue.bNumericValid)
|
|
{
|
|
MaterialInstance->SetScalarParameterValue(TEXT("Roughness"), InputValue.GetScalar());
|
|
}
|
|
|
|
SetTextureParams(InputValue.Texture.Get(), EPbrTexturePropertySlot::RoughnessMapSlot);
|
|
}
|
|
|
|
{
|
|
FDatasmithInputValue InputValue(UEPbrMaterial.GetNormal());
|
|
|
|
if (InputValue.Texture)
|
|
{
|
|
if (InputValue.Texture->bIsBumpMap)
|
|
{
|
|
SetTextureParams(InputValue.Texture.Get(), EPbrTexturePropertySlot::BumpMapSlot);
|
|
}
|
|
else
|
|
{
|
|
SetTextureParams(InputValue.Texture.Get(), EPbrTexturePropertySlot::NormalMapSlot);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
FDatasmithInputValue InputValue(UEPbrMaterial.GetMetallic());
|
|
|
|
if (InputValue.bNumericValid && InputValue.GetScalar() != 0.0f)
|
|
{
|
|
MaterialInstance->SetScalarParameterValue(TEXT("Metallic"), InputValue.GetScalar());
|
|
}
|
|
|
|
SetTextureParams(InputValue.Texture.Get(), EPbrTexturePropertySlot::MetallicMapSlot);
|
|
}
|
|
|
|
{
|
|
FDatasmithInputValue InputValue(UEPbrMaterial.GetSpecular());
|
|
|
|
if (InputValue.bNumericValid && InputValue.GetScalar() != 0.0f)
|
|
{
|
|
MaterialInstance->SetScalarParameterValue(TEXT("Specular"), InputValue.GetScalar());
|
|
}
|
|
|
|
SetTextureParams(InputValue.Texture.Get(), EPbrTexturePropertySlot::SpecularMapSlot);
|
|
}
|
|
|
|
// GetWorldDisplacement() & GetAmbientOcclusion() Not supported
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Code borrowed from TwinMotion: Project\Source\TwinmotionCore\Private\Import\DatasmithInputValue.cpp
|
|
// Static class that traverse PBR materials expressions trying to evaluated it
|
|
class FDatasmithExpressionEvaluator
|
|
{
|
|
public:
|
|
// Evaluate this expression input
|
|
static void EvaluateInput(const IDatasmithExpressionInput& InExpressionInput, FDatasmithInputValue& OutValue);
|
|
|
|
protected:
|
|
// Evaluate this expression input (input can be nullptr)
|
|
static void EvaluateInput(const IDatasmithExpressionInput* InExpressionInput, FDatasmithInputValue& OutValue);
|
|
|
|
// Evaluate this expression
|
|
static void EvaluateExpression(const IDatasmithMaterialExpression& InExpression, FDatasmithInputValue& OutValue);
|
|
|
|
// Evaluate is a basic Function (multiply, add, ...)
|
|
static void EvalExpressionGeneric(const IDatasmithMaterialExpressionGeneric& InGenericExpression, FDatasmithInputValue& OutValue);
|
|
|
|
// Evaluate is a Unreal Engine Function
|
|
static void EvalExpressionFctCall(const IDatasmithMaterialExpressionFunctionCall& InFctCallExpression, FDatasmithInputValue& OutValue);
|
|
|
|
// Evaluate all children of this expression
|
|
static void EvalExpressionChildren(const IDatasmithMaterialExpression& InExpression, FDatasmithInputValue* InOutInputs);
|
|
};
|
|
|
|
|
|
// Evaluate this expression input (input can be nullptr)
|
|
void FDatasmithExpressionEvaluator::EvaluateInput(const IDatasmithExpressionInput* InExpressionInput, FDatasmithInputValue& OutValue)
|
|
{
|
|
// Do we have an input ?
|
|
if (InExpressionInput == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
EvaluateInput(*InExpressionInput, OutValue);
|
|
}
|
|
|
|
|
|
// Evaluate this expression input
|
|
void FDatasmithExpressionEvaluator::EvaluateInput(const IDatasmithExpressionInput& InExpressionInput, FDatasmithInputValue& OutValue)
|
|
{
|
|
// Do we have an expression ?
|
|
const IDatasmithMaterialExpression* ExpressionPtr = InExpressionInput.GetExpression();
|
|
if (ExpressionPtr == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Evaluate the expression
|
|
EvaluateExpression(*ExpressionPtr, OutValue);
|
|
|
|
// Select component
|
|
int32 outputIndex = InExpressionInput.GetOutputIndex();
|
|
if (outputIndex != 0)
|
|
{
|
|
OutValue.Numeric.R = OutValue.Numeric.G = OutValue.Numeric.B = OutValue.Numeric.A = OutValue.Numeric.Component(outputIndex - 1);
|
|
if (outputIndex == 4 && OutValue.Texture)
|
|
{
|
|
OutValue.Texture->bUseAlpha = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Evaluate this expression
|
|
void FDatasmithExpressionEvaluator::EvaluateExpression(const IDatasmithMaterialExpression& InExpression, FDatasmithInputValue& OutValue)
|
|
{
|
|
// Call the evaluator depending of the expression type
|
|
if (InExpression.IsSubType(EDatasmithMaterialExpressionType::ConstantBool))
|
|
{
|
|
const IDatasmithMaterialExpressionBool& boolExpression = static_cast<const IDatasmithMaterialExpressionBool&>(InExpression);
|
|
OutValue.Set(boolExpression.GetBool() ? 1.0f : 0.0f);
|
|
}
|
|
else if (InExpression.IsSubType(EDatasmithMaterialExpressionType::ConstantColor))
|
|
{
|
|
const IDatasmithMaterialExpressionColor& ColorExpression = static_cast<const IDatasmithMaterialExpressionColor&>(InExpression);
|
|
OutValue.Set(ColorExpression.GetColor());
|
|
}
|
|
else if (InExpression.IsSubType(EDatasmithMaterialExpressionType::ConstantScalar))
|
|
{
|
|
const IDatasmithMaterialExpressionScalar& ScalarExpression = static_cast<const IDatasmithMaterialExpressionScalar&>(InExpression);
|
|
OutValue.Set(ScalarExpression.GetScalar());
|
|
}
|
|
else if (InExpression.IsSubType(EDatasmithMaterialExpressionType::FlattenNormal))
|
|
{
|
|
const IDatasmithMaterialExpressionFlattenNormal& NormalExpression = static_cast<const IDatasmithMaterialExpressionFlattenNormal&>(InExpression);
|
|
FDatasmithInputValue Normal(NormalExpression.GetNormal());
|
|
FDatasmithInputValue Flatness(NormalExpression.GetFlatness());
|
|
|
|
OutValue = MoveTemp(Normal); // TODO: Evaluate FlattenNormal
|
|
if (!OutValue.bNumericValid && Flatness.bNumericValid)
|
|
{
|
|
OutValue.Set(Flatness.Numeric);
|
|
}
|
|
}
|
|
else if (InExpression.IsSubType(EDatasmithMaterialExpressionType::FunctionCall))
|
|
{
|
|
const IDatasmithMaterialExpressionFunctionCall& FctCallExpression = static_cast<const IDatasmithMaterialExpressionFunctionCall&>(InExpression);
|
|
EvalExpressionFctCall(FctCallExpression, OutValue);
|
|
}
|
|
else if (InExpression.IsSubType(EDatasmithMaterialExpressionType::Generic))
|
|
{
|
|
const IDatasmithMaterialExpressionGeneric& GenericExpression = static_cast<const IDatasmithMaterialExpressionGeneric&>(InExpression);
|
|
EvalExpressionGeneric(GenericExpression, OutValue);
|
|
}
|
|
else if (InExpression.IsSubType(EDatasmithMaterialExpressionType::Texture))
|
|
{
|
|
const IDatasmithMaterialExpressionTexture& TextureExpression = static_cast<const IDatasmithMaterialExpressionTexture&>(InExpression);
|
|
OutValue.Set(TextureExpression.GetTexturePathName());
|
|
EvaluateInput(TextureExpression.GetInputCoordinate(), OutValue);
|
|
}
|
|
else if (InExpression.IsSubType(EDatasmithMaterialExpressionType::TextureCoordinate))
|
|
{
|
|
const IDatasmithMaterialExpressionTextureCoordinate& CoordinateExpression = static_cast<const IDatasmithMaterialExpressionTextureCoordinate&>(InExpression);
|
|
if (!OutValue.Texture)
|
|
{
|
|
OutValue.Set(TEXT(""));
|
|
}
|
|
OutValue.Texture->CoordinateIndex = CoordinateExpression.GetCoordinateIndex();
|
|
OutValue.Texture->UTiling = CoordinateExpression.GetUTiling();
|
|
OutValue.Texture->VTiling = CoordinateExpression.GetVTiling();
|
|
}
|
|
else
|
|
{
|
|
OutValue.bNumericValid = false;
|
|
}
|
|
}
|
|
|
|
|
|
// Evaluate is a basic Function (multiply, add, ...)
|
|
void FDatasmithExpressionEvaluator::EvalExpressionGeneric(const IDatasmithMaterialExpressionGeneric& InGenericExpression, FDatasmithInputValue& OutValue)
|
|
{
|
|
bool bInvalid = false;
|
|
const TCHAR* Expression = InGenericExpression.GetExpressionName();
|
|
|
|
if (InGenericExpression.GetInputCount() >= 3)
|
|
{
|
|
if (FCString::Stricmp(Expression, TEXT("LinearInterpolate")) == 0)
|
|
{
|
|
EvaluateInput(InGenericExpression.GetInput(0), OutValue);
|
|
FDatasmithInputValue InputB;
|
|
EvaluateInput(InGenericExpression.GetInput(1), InputB);
|
|
FDatasmithInputValue InputAlpha;
|
|
EvaluateInput(InGenericExpression.GetInput(2), InputAlpha);
|
|
|
|
// If Texture is on the second node, make it first
|
|
if (!OutValue.Texture && InputB.Texture)
|
|
{
|
|
Swap(InputB, OutValue);
|
|
if (InputAlpha.bNumericValid)
|
|
{
|
|
InputAlpha.Set(1.0f - InputAlpha.GetScalar());
|
|
}
|
|
}
|
|
|
|
if (!InputAlpha.bNumericValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (OutValue.bNumericValid && InputB.bNumericValid)
|
|
{
|
|
OutValue.Set(FMath::Lerp(OutValue.Numeric, InputB.Numeric, InputAlpha.GetScalar()));
|
|
}
|
|
else if (OutValue.Texture)
|
|
{
|
|
OutValue.Texture->Fading = InputAlpha.GetScalar();
|
|
}
|
|
return;
|
|
}
|
|
if (FCString::Stricmp(Expression, TEXT("StaticSwitch")) == 0)
|
|
{
|
|
FDatasmithInputValue InputToggle;
|
|
EvaluateInput(InGenericExpression.GetInput(2), InputToggle);
|
|
if (!InputToggle.bNumericValid || InputToggle.GetScalar() > 0.5)
|
|
{
|
|
EvaluateInput(InGenericExpression.GetInput(0), OutValue);
|
|
}
|
|
else
|
|
{
|
|
EvaluateInput(InGenericExpression.GetInput(1), OutValue);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (FCString::Stricmp(Expression, TEXT("TextureObjectParameter")) == 0)
|
|
{
|
|
const TSharedPtr< IDatasmithKeyValueProperty >& TextureProperty = InGenericExpression.GetPropertyByName(TEXT("Texture"));
|
|
if (TextureProperty.IsValid())
|
|
{
|
|
OutValue.Set(TextureProperty->GetValue());
|
|
}
|
|
}
|
|
else if (FCString::Stricmp(Expression, TEXT("Fresnel")) == 0)
|
|
{
|
|
OutValue.Set(0.1f); // Not supported
|
|
}
|
|
else if (FCString::Stricmp(Expression, TEXT("VertexNormalWS")) == 0)
|
|
{
|
|
OutValue.Set(1.0f); // Not supported
|
|
}
|
|
else if (InGenericExpression.GetInputCount() >= 1)
|
|
{
|
|
EvaluateInput(InGenericExpression.GetInput(0), OutValue);
|
|
if (!OutValue.bNumericValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (FCString::Stricmp(Expression, TEXT("OneMinus")) == 0)
|
|
{
|
|
OutValue.Set(FLinearColor(1.0f, 1.0f, 1.0f, 1.0f) - OutValue.Numeric);
|
|
}
|
|
else if (FCString::Stricmp(Expression, TEXT("Desaturation")) == 0)
|
|
{
|
|
OutValue.Numeric = OutValue.Numeric.Desaturate(1.0);
|
|
}
|
|
else if (FCString::Stricmp(Expression, TEXT("Noise")) == 0)
|
|
{
|
|
OutValue.Set(0.5); // Not supported
|
|
}
|
|
else if (InGenericExpression.GetInputCount() == 1 && FCString::Stricmp(Expression, TEXT("Power")) == 0)
|
|
{
|
|
// Power with a scalar component
|
|
const TSharedPtr< IDatasmithKeyValueProperty >& ExponentProperty = InGenericExpression.GetPropertyByName(TEXT("ConstExponent"));
|
|
if (!ExponentProperty.IsValid())
|
|
{
|
|
OutValue.bNumericValid = false;
|
|
return;
|
|
}
|
|
float Exponent = FCString::Atof(ExponentProperty->GetValue());
|
|
FLinearColor PowValue1(FPlatformMath::Pow(OutValue.Numeric.R, Exponent),
|
|
FPlatformMath::Pow(OutValue.Numeric.G, Exponent),
|
|
FPlatformMath::Pow(OutValue.Numeric.B, Exponent),
|
|
FPlatformMath::Pow(OutValue.Numeric.A, Exponent));
|
|
OutValue.Set(PowValue1);
|
|
}
|
|
else if (InGenericExpression.GetInputCount() >= 2)
|
|
{
|
|
FDatasmithInputValue Input1;
|
|
EvaluateInput(InGenericExpression.GetInput(1), Input1);
|
|
if (!Input1.bNumericValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (FCString::Stricmp(Expression, TEXT("Add")) == 0)
|
|
{
|
|
OutValue.Numeric += Input1.Numeric;
|
|
}
|
|
else if (FCString::Stricmp(Expression, TEXT("Multiply")) == 0)
|
|
{
|
|
OutValue.Numeric *= Input1.Numeric;
|
|
}
|
|
else if (FCString::Stricmp(Expression, TEXT("Power")) == 0)
|
|
{
|
|
FLinearColor PowValue(FPlatformMath::Pow(OutValue.Numeric.R, Input1.Numeric.R),
|
|
FPlatformMath::Pow(OutValue.Numeric.G, Input1.Numeric.G),
|
|
FPlatformMath::Pow(OutValue.Numeric.B, Input1.Numeric.B),
|
|
FPlatformMath::Pow(OutValue.Numeric.A, Input1.Numeric.A));
|
|
OutValue.Set(PowValue);
|
|
}
|
|
else if (FCString::Stricmp(Expression, TEXT("Max")) == 0)
|
|
{
|
|
FLinearColor MaxValue(FMath::Max(OutValue.Numeric.R, Input1.Numeric.R),
|
|
FMath::Max(OutValue.Numeric.G, Input1.Numeric.G),
|
|
FMath::Max(OutValue.Numeric.B, Input1.Numeric.B),
|
|
FMath::Max(OutValue.Numeric.A, Input1.Numeric.A));
|
|
OutValue.Set(MaxValue);
|
|
}
|
|
else if (FCString::Stricmp(Expression, TEXT("AppendVector")) == 0)
|
|
{
|
|
FLinearColor Constant2VectorValue(OutValue.Numeric.R, Input1.Numeric.R, 0, 0);
|
|
OutValue.Set(Constant2VectorValue);
|
|
}
|
|
else if (InGenericExpression.GetInputCount() >= 3)
|
|
{
|
|
FDatasmithInputValue Input2;
|
|
EvaluateInput(*InGenericExpression.GetInput(2), Input2);
|
|
if (!Input2.bNumericValid)
|
|
{
|
|
OutValue.bNumericValid = false;
|
|
return;
|
|
}
|
|
|
|
bInvalid = true;
|
|
}
|
|
else
|
|
{
|
|
bInvalid = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bInvalid = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bInvalid = true;
|
|
}
|
|
|
|
if (bInvalid)
|
|
{
|
|
OutValue.bNumericValid = false;
|
|
}
|
|
}
|
|
|
|
|
|
// Evaluate is a Unreal Engine Function
|
|
void FDatasmithExpressionEvaluator::EvalExpressionFctCall(const IDatasmithMaterialExpressionFunctionCall& InFctCallExpression, FDatasmithInputValue& OutValue)
|
|
{
|
|
const TCHAR* FctPathName = InFctCallExpression.GetFunctionPathName();
|
|
int32 InputCount = InFctCallExpression.GetInputCount();
|
|
if (InputCount == 0)
|
|
{
|
|
if (FCString::Strstr(FctPathName, TEXT("LocalPosition")))
|
|
{
|
|
OutValue.Set(FLinearColor(1.0, 2.0, 3.0, 1.0)); // Not really supported
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
TArray<FDatasmithInputValue> Inputs;
|
|
Inputs.SetNum(InputCount);
|
|
|
|
Inputs[0] = MoveTemp(OutValue); // Copy first input result in output
|
|
EvalExpressionChildren(InFctCallExpression, &Inputs[0]);
|
|
OutValue = MoveTemp(Inputs[0]); // Copy first input result in output
|
|
|
|
if (FCString::Strstr(FctPathName, TEXT("UVEdit.UVEdit")))
|
|
{
|
|
FDatasmithInputValue::FTexture* Texture = OutValue.Texture.Get();
|
|
if (Texture == nullptr)
|
|
{
|
|
OutValue.bNumericValid = false;
|
|
return;
|
|
}
|
|
|
|
// Input 0 must be a TextureCoordinate so Index, UTiling and VTiling are already set.
|
|
|
|
if (InputCount > 1 && Inputs[1].bNumericValid)
|
|
{
|
|
Texture->UTilingPivot = Inputs[1].Numeric.Component(0);
|
|
Texture->VTilingPivot = Inputs[1].Numeric.Component(1);
|
|
}
|
|
if (InputCount > 2 && Inputs[2].bNumericValid)
|
|
{
|
|
Texture->UTiling = Inputs[2].Numeric.Component(0);
|
|
Texture->VTiling = Inputs[2].Numeric.Component(1);
|
|
}
|
|
if (InputCount > 3 && Inputs[3].bNumericValid)
|
|
{
|
|
Texture->bMirrorU = Inputs[3].GetScalar() != 0.0f;
|
|
}
|
|
if (InputCount > 4 && Inputs[4].bNumericValid)
|
|
{
|
|
Texture->bMirrorV = Inputs[4].GetScalar() != 0.0f;
|
|
}
|
|
if (InputCount > 5 && Inputs[5].bNumericValid)
|
|
{
|
|
Texture->URotationPivot = Inputs[5].Numeric.Component(0);
|
|
Texture->VRotationPivot = Inputs[5].Numeric.Component(1);
|
|
}
|
|
if (InputCount > 6 && Inputs[6].bNumericValid)
|
|
{
|
|
Texture->Rotation = Inputs[6].GetScalar();
|
|
}
|
|
if (InputCount > 7 && Inputs[7].bNumericValid)
|
|
{
|
|
Texture->UOffset = Inputs[7].Numeric.Component(0);
|
|
Texture->VOffset = Inputs[7].Numeric.Component(1);
|
|
}
|
|
}
|
|
else if (FCString::Strstr(FctPathName, TEXT("FlattenNormal")))
|
|
{
|
|
if (InputCount > 1 && Inputs[1].bNumericValid)
|
|
{
|
|
if (!OutValue.bNumericValid)
|
|
{
|
|
OutValue.Set(Inputs[1].Numeric);
|
|
}
|
|
}
|
|
}
|
|
else if (FCString::Strstr(FctPathName, TEXT("NormalFromHeightmap.NormalFromHeightmap")))
|
|
{
|
|
if (InputCount > 1 && Inputs[1].bNumericValid) // Unused
|
|
{
|
|
}
|
|
if (InputCount > 2 && Inputs[2].bNumericValid) // UVOffset
|
|
{
|
|
if (!OutValue.Texture)
|
|
{
|
|
OutValue.Set(TEXT(""));
|
|
}
|
|
OutValue.Texture->UVOffset = Inputs[2].GetScalar();
|
|
}
|
|
if (InputCount > 3 && Inputs[3].bNumericValid) // UVCoordinatesInput
|
|
{
|
|
if (Inputs[3].Texture)
|
|
{
|
|
if (OutValue.Texture)
|
|
{
|
|
OutValue.Texture->CoordinateIndex = Inputs[3].Texture->CoordinateIndex;
|
|
OutValue.Texture->UTiling = Inputs[3].Texture->UTiling;
|
|
OutValue.Texture->VTiling = Inputs[3].Texture->VTiling;
|
|
}
|
|
else
|
|
{
|
|
OutValue.Texture = MoveTemp(Inputs[3].Texture);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (FCString::Strstr(FctPathName, TEXT("ConvertFromDiffSpec.ConvertFromDiffSpec")))
|
|
{
|
|
if (OutValue.bNumericValid)
|
|
{
|
|
if (InputCount > 1 && Inputs[1].bNumericValid) // Unused
|
|
{
|
|
// Simple approximation
|
|
OutValue.Set(FMath::Lerp(OutValue.Numeric, OutValue.Numeric + Inputs[1].Numeric, 0.5));
|
|
}
|
|
else
|
|
OutValue.bNumericValid = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutValue.bNumericValid = false;
|
|
}
|
|
}
|
|
|
|
|
|
void FDatasmithExpressionEvaluator::EvalExpressionChildren(const IDatasmithMaterialExpression& InExpression, FDatasmithInputValue* InOutInputs)
|
|
{
|
|
int32 InputCount = InExpression.GetInputCount();
|
|
for (int32 Index = 0; Index < InputCount; ++Index)
|
|
{
|
|
EvaluateInput(InExpression.GetInput(Index), InOutInputs[Index]);
|
|
}
|
|
}
|
|
|
|
|
|
// Evaluate this expression
|
|
void FDatasmithInputValue::EvaluateInput(const IDatasmithExpressionInput& InInput)
|
|
{
|
|
FDatasmithExpressionEvaluator::EvaluateInput(InInput, *this);
|
|
}
|
|
}
|