I have a problem with inputs and outputs (UMaterialExpression)

This is my first time using the UMaterialExpression class.
Seems to be a problem with the types of the inputs.
And the output names are not set in the node.
I don’t see the node title either.
I’m not sure why this happens. I’ve tried many things.

The node looks like this:

And these are the errors in the log:
Mat

These are the inputs types

These are the types and names of the outputs

And so I’m trying to name the node.

Does anyone know how to fix this?


ADDITIONAL INFO:

I’m trying to write this code in C++ (this node works perfectly).

This is the C++ code (obviously it’s broken).

WorldAlignedEmissiveMask.cpp (7.2 KB)

WorldAlignedEmissiveMask.h (3.5 KB)


Thank you so much!!


i have a new achivement

but i still need help!!

What version of Unreal Engine are you using? I think 5.6 because in 5.4 some functions have different signatures, for example there is a function uint32 GetOutputType(int32 OutputIndex) but there is no function EMaterialValueType GetOutputValueType(int32 OutputIndex) to override.

Yes, i’m using UE5.6

i think GetOutputValueType() is new. There is some old deprecated functions related to inputs/auputs in UE5.6

To display the node title, you need to override the GetCaption function. Once overridden, the title can be set as follows:

void UMaterialExpression::GetCaption(TArray<FString>& OutCaptions) const
{
	OutCaptions.Add(TEXT("NodeTitle"));
}

I assume that’s what you did. You can also add something like additional “headers” under the node title using the same array. I assume the order of the elements is reversed, so the last element of the array will be the main node title (not sure why it’s done that way). So if you want, you can write something like this:

void UMaterialExpression::GetCaption(TArray<FString>& OutCaptions) const
{
    OutCaptions.Add(TEXT("UnderTitle2"));
    OutCaptions.Add(TEXT("UnderTitle"));
    OutCaptions.Add(TEXT("NodeTitle"));
}

Or like this:

void UMaterialExpression::GetCaption(TArray<FString>& OutCaptions) const
{
    OutCaptions.Add(TEXT("UnderTitle\nUnderTitle2"));
    OutCaptions.Add(TEXT("NodeTitle"));
}

Which should give the same result as in the image below:
image

Now, to display the output names, it should be enough to set the variable bShowOutputNameOnPin to true. By default, it is false, because most built-in nodes that perform some calculations have only one output pin and therefore the name is hidden by design.
This property should be set in the constructor as follows:

UWorldAlignedEmissiveMask::UWorldAlignedEmissiveMask(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{
    ...
    bShowOutputNameOnPin = true;
    ...
}

After compiling, you may need to replace the old node with the new one on the node graph to see these changes.

Now about those errors you showed earlier. Obviously, this is related to errors you made somewhere in the code inside the GetMask function. The error occurs in this line of code:

const int32 WorldAlignedCoord = Compiler->Add(MaskXY, MaskXZ);

This is because MaskXY is “float2” while MaskXZ is “float4”.
The problem starts in the following line:

const int32 MaskedXZ = Compiler->ForceCast(
    Compiler->ComponentMask(WorldPosCode, 1, 0, 1, 0),
    MCT_Float3
);

You are correctly using ComponentMask which creates a “float2” from “float3”, then for some reason unknown to me you cast to “MCT_Float3” resulting in “float3”. So on this line you will have “float3 MaskXZ”, although I assume based on the variable name you meant to have “float2 MaskXZ”.
Now on the next line you did the following:

 const int32 CoordXZ = Compiler->Div(
    Compiler->AppendVector(MaskedXZ, Compiler->Constant(0.0f)),
    TileSizeCode);

Which returns “float4” because “AppendVector(float3, float) → float4”. I also don’t understand why you are using the AppendVector function. This function concatenates vectors. For example, if you have vectors float2(1, 2) and float2(4, 5), the function will return float4(1, 2, 4, 5). So I don’t see any point in using it here.

The “If” operator is written with errors as well. In my opinion, the way it is written will not give the desired result you wanted. In addition, the last argument of the Compiler->If function is “Threshold”. I think it should be very small, for example, 0.00001. “1” is a very large uncertainty.

I tried to fix these problems and ended up with the following function:

UWorldAlignedEmissiveMask::FMask UWorldAlignedEmissiveMask::GetMask(FMaterialCompiler* Compiler)
{
    FMask Result;

    const int32 WorldPosCode       = WorldPos.Compile(Compiler);
    const int32 AxisCode           = ProjectionAxisIndex.Compile(Compiler);
    const int32 TileSizeCode       = TileSize.Compile(Compiler);
    const int32 TexCode            = Tex.Compile(Compiler);

    const int32 CoordXY = Compiler->Div(Compiler->ComponentMask(WorldPosCode, 1, 1, 0, 0), TileSizeCode);
    const int32 CoordXZ = Compiler->Div(Compiler->ComponentMask(WorldPosCode, 1, 0, 1, 0), TileSizeCode);
    const int32 CoordYZ = Compiler->Div(Compiler->ComponentMask(WorldPosCode, 0, 1, 1, 0), TileSizeCode);

    // This one will be used if "AxisIndex >= 0.5"
    const int32 WorldAlignedCoordElse = Compiler->If(
        AxisCode,
        Compiler->Constant(1.5),
        CoordYZ,
        CoordYZ,
        CoordXZ,
        Compiler->Constant(0.00001)
        );
    
    const int32 WorldAlignedCoord = Compiler->If(
        AxisCode,
        Compiler->Constant(0.5),
        WorldAlignedCoordElse,
        WorldAlignedCoordElse,
        CoordXY,
        Compiler->Constant(0.00001)
    );
    const int32 SampleColor = Compiler->TextureSample(TexCode, WorldAlignedCoord, SAMPLERTYPE_Color);
    const int32 Intensity   = Compiler->Dot(SampleColor, Compiler->Constant3(1.0, 1.0, 1.0));


    
    const int32 WhiteMask = Compiler->SmoothStep(
        Compiler->Constant(WhiteThreshold),
        Compiler->Constant(WhiteThreshold + WhiteThresholdDelta),
        Intensity);

    Result.EmissiveOutput     = Compiler->Mul(Compiler->Constant3(1.0, 1.0, 1.0), WhiteMask);
    Result.OpacityMaskOutput  = WhiteMask;

    return Result;
}

Let me know if this solution works for you. I may have forgotten to mention something, so if something doesn’t work or you have any questions, feel free to ask.