Creating Custom Material Expression in C++

Hello friends,

I’m trying to create a new custom material expression that acts as switch node but lerps between colors/values and the node can take as many inputs as I need by adding + Add button that adds inputs in an array.


image

The class in C++ inherits from UMaterialExpression same as switch node and I saw I should override compile function. I did that but I saw there is no change in the material output and I just get 0 for no reason I tried to copy paste the switch node compile function into Palette compile function but same no change I don’t understand why and how.

// MaterialExpression.h
USTRUCT()
struct FPaletteCustomInput {
	GENERATED_USTRUCT_BODY()
	UPROPERTY(EditAnywhere, Category = CustomInput)
		FName InputName;
	UPROPERTY()
		FExpressionInput Input;
};

/**
 *
 */
UCLASS(collapsecategories, hidecategories = Object, MinimalAPI)
class UMaterialExpressionPalette : public UMaterialExpression {
	GENERATED_UCLASS_BODY()
	UPROPERTY(EditAnywhere, Category = UMaterialExpressionPalette)
		FString Description;
	UPROPERTY(meta = (RequiredInput = "false", ToolTip = "Defaults to 'ConstAlpha' if not specified"))
		FExpressionInput Alpha;
	UPROPERTY(EditAnywhere, Category = UMaterialExpressionPalette, meta = (OverridingInputProperty = "Alpha"))
		float ConstAlpha;
	UPROPERTY(EditAnywhere, Category = UMaterialExpressionPalette)
		TArray<struct FPaletteCustomInput> Inputs;
	#if WITH_EDITOR
		virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
		void RebuildOutputs();
	#endif // WITH_EDITOR
		virtual void Serialize(FStructuredArchive::FRecord Record) override;
	////~ End UObject Interface.

	//~ Begin UMaterialExpression Interface
	#if WITH_EDITOR
		virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) override;
		virtual void GetCaption(TArray<FString>& OutCaptions) const override;
		virtual FText GetCreationName() const override { return FText::FromString(TEXT("Palette")); }
		virtual TArrayView<FExpressionInput*> GetInputsView() override;
		virtual FExpressionInput* GetInput(int32 InputIndex) override;
		virtual FName GetInputName(int32 InputIndex) const override;
		virtual uint32 GetInputType(int32 InputIndex) override { return MCT_Float3; }
	#endif // WITH_EDITOR
	//~ End UMaterialExpression Interface
};
// UMaterialExpression.cpp
int32 UMaterialExpressionPalette::Compile(FMaterialCompiler* Compiler, int32 OutputIndex) {
        TArray<int32> CompiledInputs;
uint32 Value = this->Alpha.Compile(Compiler), Default = Compiler->Constant(0.0);
for (int32 i = 0; i < Inputs.Num(); i++)
{
	if (!Inputs[i].Input.GetTracedInput().Expression)
	{
		return Compiler->Errorf(TEXT("Texture Multiplexer missing input %d (%s)"), i + 1, *Inputs[i].InputName.ToString());
	}
	int32 InputCode = Inputs[i].Input.Compile(Compiler);
	if (InputCode < 0)
	{
		return InputCode;
	}
	CompiledInputs.Add(InputCode);
}
return Compiler->Switch(Value, Default, CompiledInputs);

for now I tried to use switch node logic but still no change, and even though I can’t implement the lerping logic here because in the code below it shows that I need to get alpha value as floating point and the only way I can is overriding the value of alpha variable through UPROPERTY which is not ideal because I can’t use a texture to use it as alpha and I want the expression to work with textures as well as the switch node does so I can have an output like this.

// Native C++
struct Color {
	float R, G, B;
	Color operator+(Color Other) {
		return Color(R + Other.R, G + Other.G, B + Other.B);
	}
	Color operator-(Color Other) {
		return Color(R - Other.R, G - Other.G, B - Other.B);
	}
	Color operator*(float Alpha) {
		return Color(R * Alpha, G * Alpha, B * Alpha);
	}
};
Color Lerp(Color A, Color B, float Alpha) {
	return Color(A + ((B - A) * Alpha));
}
Color ColorPalette(const std::vector<Color>& Colors, float Alpha) {
	if (Colors.size() == 1 || Alpha <= 0) {
		return Colors.front();
	}
	if (Alpha >= 1) {
		return Colors.back();
	}
	float ScaledAlpha = Alpha * (Colors.size() - 1);
	int Index = ScaledAlpha;
	float A = ScaledAlpha - Index;
	return Lerp(Colors[Index], Colors[Index + 1], A);
}

I want to know if I’m missing something i tried even to modify the source code in the following files:
MaterialCompiler.h
HLSLMaterialTranslator.h/.cpp

but still nothing seems to work.

Thanks

1 Like

Hi did you found a solution , it’s interresting?
I am doing something similar

Have nice day

Sorry I’m busy nowadays with collage so I ignored it, it needs time to solve it and searching in the source code for this takes a lot of time.

1 Like

Hi! I came across your question while working on a nearly identical custom node. After some trial and error, I was able to get it working. If you’re still interested, feel free to check it out or use it in your project. Let me know if you have any questions or need any details!