在实际项目中,很多次遇到在材质中需要根据index选择输出对应的数据,一个两个用if节点去连问题不大,但是数据量多了之后连起来就满眼的连线,乱的一塌糊涂,所以有了封装一个switch的想法,本质上还是if的嵌套实现,但是由代码层面去创建hlsl代码,不用在材质编辑器中连的乱七八糟了
注:这里只做了float1,float2,float3,float4的选择实现,其它的需要可以自行扩展,
defResult为各种类别的0项,因为本质是做了循环的加法
defResult的初始化不能省略,因为if判断里面对类别进行了判断,如果类别不符合会返回index_none
最终效果
materialSwitch
1.创建个插件,空白的就好,用来放我们的自定义节点相关的内容
2.创建switch的节点实现
MaterialExpressionSwitch.h
// Copyright 2022-2023 Ace Software. All Rights Reserved. Unauthorized copying of this file, via any medium is strictly prohibited
#pragma once
#include "CoreMinimal.h"
#include "Materials/MaterialExpression.h"
#include "MaterialExpressionIO.h"
#if WITH_EDITOR
#include "MaterialCompiler.h"
#include "MaterialGraph/MaterialGraphNode_Comment.h"
#include "MaterialGraph/MaterialGraphNode.h"
#endif //WITH_EDITOR
#include "MaterialExpressionSwitch.generated.h"
struct FPropertyChangedEvent;
/**
*
*/
UCLASS(collapsecategories, hidecategories=Object)
class ACEMATERIAL_API UMaterialExpressionSwitch : public UMaterialExpression
{
GENERATED_UCLASS_BODY()
UPROPERTY(EditAnywhere, Category=AceMaterial)
TArray<FExpressionInput> Layers;
UPROPERTY(EditAnywhere, Category=AceMaterial, meta=(OverridingInputProperty = "Index"))
FExpressionInput Index;
//~ Begin UMaterialExpression Interface
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) override;
virtual void GetCaption(TArray<FString>& OutCaptions) const override;
virtual const TArray<FExpressionInput*> GetInputs() override;
virtual FName GetInputName(int32 InputIndex) const override;
virtual FExpressionInput* GetInput(int32 InputIndex) override;
#endif // WITH_EDITOR
//~ End UMaterialExpression Interface
};
MaterialExpressionSwitch.cpp
// Copyright 2022-2023 Ace Software. All Rights Reserved. Unauthorized copying of this file, via any medium is strictly prohibited
#include "MaterialExpressionSwitch.h"
#define LOCTEXT_NAMESPACE "MaterialExpression"
UMaterialExpressionSwitch::UMaterialExpressionSwitch(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// Structure to hold one-time initialization
struct FConstructorStatics
{
FText NAME_Math;
FConstructorStatics()
: NAME_Math(LOCTEXT("Switch", "Switch"))
{
}
};
static FConstructorStatics ConstructorStatics;
#if WITH_EDITORONLY_DATA
MenuCategories.Add(ConstructorStatics.NAME_Math);
#endif
}
#if WITH_EDITOR
void UMaterialExpressionSwitch::GetCaption(TArray<FString>& OutCaptions) const
{
OutCaptions.Add(TEXT("Switch"));
}
const TArray<FExpressionInput*> UMaterialExpressionSwitch::GetInputs()
{
TArray<FExpressionInput*> Result;
for (int32 LayerIdx = 0; LayerIdx<Layers.Num(); LayerIdx++)
{
Result.Add(&Layers[LayerIdx]);
}
Result.Add(&Index);
return Result;
}
FName UMaterialExpressionSwitch::GetInputName(int32 InputIndex) const
{
if(InputIndex<Layers.Num())
{
return *FString::Printf(TEXT("Layer%d"), InputIndex);
}
return TEXT("Index");
}
FExpressionInput* UMaterialExpressionSwitch::GetInput(int32 InputIndex)
{
if(InputIndex<Layers.Num())
return &Layers[InputIndex];
return &Index;
}
void UMaterialExpressionSwitch::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (UMaterialGraphNode* MatGraphNode = Cast<UMaterialGraphNode>(GraphNode))
{
MatGraphNode->RecreateAndLinkNode();
}
}
int32 UMaterialExpressionSwitch::Compile(FMaterialCompiler* Compiler, int32 OutputIndex)
{
int32 Result = Compiler->Constant(0);
if(!Index.Expression)
{
return Compiler->Errorf(TEXT("请输入选择的下标!!!"));
}
int32 indexCode=Index.Compile(Compiler);
if(Compiler->GetType(indexCode)!=MCT_Float)
{
return Compiler->Errorf(TEXT("value值需要输入数值,会向下取整!!!"));
}
if(Layers.Num()==0)
{
return INDEX_NONE;
//Result=Compiler->Errorf(TEXT("请添加需要选择的数据!!!"));
}
indexCode=Compiler->Floor(indexCode);
int32 defResult;
switch (Compiler->GetType(Layers[0].Compile(Compiler)))
{
case MCT_Float1:
defResult=Compiler->Constant(0);
break;
case MCT_Float2:
defResult=Compiler->Constant2(0,0);
break;
case MCT_Float3:
defResult=Compiler->Constant3(0,0,0);
break;
case MCT_Float4:
defResult=Compiler->Constant4(0,0,0,0);
break;
default:return INDEX_NONE;
}
int thCode=Compiler->Constant(0.0001f);
for (int32 i=0;i<Layers.Num();i++)
{
int32 tmpCode=Compiler->Floor(Compiler->Constant(i)) ;
int32 layerCode=Layers[i].Compile(Compiler);
int32 ifCode=Compiler->If(indexCode,tmpCode,defResult, layerCode,defResult,thCode);
Result=Compiler->Add(Result, ifCode);
}
return Result;
}
#endif