Announcement

Collapse
No announcement yet.

Material Expression with Multiple Outputs in C++

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Material Expression with Multiple Outputs in C++

    I'm trying to write in c++ a custom material expression that will output 3 results in 3 different output pins. In the attached image you can see the idea I'm trying to follow: basically convert a material function to a material expression in order to don't see what is inside of it.
    So far I managed to create the node with all the pins I need and in the .cpp file I already have the function and the 3 different results. I'm stuck because I can't find a solution to distribute the 3 results in the 3 output pins.
    Is there anyone who can help me?
    Thank you!

    This is the example code for the node in the attached image (.h and .cpp files):

    "CustomNodeMultiOutput.h"
    Code:
    // Fill out your copyright notice in the Description page of Project Settings.
    
    #pragma once
    
    #include "CoreMinimal.h"
    #include "Materials/MaterialExpression.h"
    #include "UObject/ObjectMacros.h"
    #include "MaterialExpressionIO.h"
    #include "CustomNodeMultiOutput.generated.h"
    
    UCLASS(MinimalAPI)
    class UMaterialExpressionCustomNodeMultiOutput : public UMaterialExpression
    {
    GENERATED_UCLASS_BODY()
    
    UPROPERTY()
    TArray<FString> ParamNames;
    
    UPROPERTY(meta = (RequiredInput = "false", ToolTip = "Random"))
    FExpressionInput Input1;
    
    UPROPERTY(meta = (RequiredInput = "false", ToolTip = "Tiling"))
    FExpressionInput Input2;
    
    UPROPERTY(meta = (RequiredInput = "false", ToolTip = "Divisions"))
    FExpressionInput Input3;
    
    UPROPERTY(EditAnywhere, Category = MaterialExpressionEndless_UVs, meta = (OverridingInputProperty = "Random"))
    float Input1Deafult;
    
    UPROPERTY(EditAnywhere, Category = MaterialExpressionEndless_UVs, meta = (OverridingInputProperty = "Tiling"))
    float Input2Deafult;
    
    UPROPERTY(EditAnywhere, Category = MaterialExpressionEndless_UVs, meta = (OverridingInputProperty = "Divisions"))
    int32 Input3Deafult;
    
    //~ Begin UMaterialExpression Interface
    #if WITH_EDITOR
    virtual const TArray<FExpressionInput*> GetInputs() override;
    virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) override;
    virtual void GetCaption(TArray<FString>& OutCaptions) const override;
    virtual TArray<FExpressionOutput>& GetOutputs() override;
    virtual FText GetKeywords() const override { return FText::FromString(TEXT("SOA")); }
    #endif
    //~ End UMaterialExpression Interface
    };

    "CustomNodeMultiOutput.cpp"

    Code:
    // Fill out your copyright notice in the Description page of Project Settings.
    
    
    #include "CustomNodeMultiOutput.h"
    #include "CoreMinimal.h"
    #include "MaterialExpressionIO.h"
    #include "Materials/MaterialExpression.h"
    #include "MaterialCompiler.h"
    #include "Materials/MaterialExpressionVectorNoise.h"
    
    #if WITH_EDITOR
    #include "MaterialGraph/MaterialGraphNode_Comment.h"
    #include "MaterialGraph/MaterialGraphNode.h"
    #endif //WITH_EDITOR
    
    #define LOCTEXT_NAMESPACE "MaterialExpression"
    
    UMaterialExpressionCustomNodeMultiOutput::UMaterialExpressionCustomNodeMultiOutput(const FObjectInitializer& ObjectInitializer)
        : Super(ObjectInitializer)
    {
        // Structure to hold one-time initialization
        struct FConstructorStatics
        {
            FText NAME_Utility;
            FText NAME_SOA;
            FConstructorStatics()
                : NAME_Utility(LOCTEXT("Utility", "Utility"))
                , NAME_SOA(LOCTEXT("SOA", "SOA"))
            {
            }
        };
        static FConstructorStatics ConstructorStatics;
    
        bShowOutputNameOnPin = true;
        bHidePreviewWindow = true;
    
        // Parameters Default values
        Input1Deafult = 1.0f;
        Input2Deafult = 4.0f;
        Input3Deafult = 5.0f;
    
        ParamNames.Add(TEXT("Result 1"));
        ParamNames.Add(TEXT("Result 2"));
        ParamNames.Add(TEXT("Result 3"));
    
    #if WITH_EDITORONLY_DATA
        MenuCategories.Add(ConstructorStatics.NAME_Utility);
        MenuCategories.Add(ConstructorStatics.NAME_SOA);
    
        Outputs.Reset();
        Outputs.Add(FExpressionOutput(TEXT(""), 1, 1, 1, 1, 0));
        Outputs.Add(FExpressionOutput(TEXT(""), 1, 1, 1, 1, 0));
        Outputs.Add(FExpressionOutput(TEXT(""), 1, 1, 1, 1, 0));
    
    #endif
    }
    
    
    TArray<FExpressionOutput>& UMaterialExpressionCustomNodeMultiOutput::GetOutputs()
    {
        Outputs[0].OutputName = *(ParamNames[0]);
        Outputs[1].OutputName = *(ParamNames[1]);
        Outputs[2].OutputName = *(ParamNames[2]);
        return Outputs;
    }
    
    #if WITH_EDITOR
    int32 UMaterialExpressionCustomNodeMultiOutput::Compile(class FMaterialCompiler* Compiler, int32 OutputIndex)
    {
        // if the input is hooked up, use it, otherwise use the internal constant
        float Input1Value = Input1.GetTracedInput().Expression ? Input1.Compile(Compiler) : Compiler->Constant(Input1Deafult);
        // if the input is hooked up, use it, otherwise use the internal constant
        float Input2Value = Input2.GetTracedInput().Expression ? Input2.Compile(Compiler) : Compiler->Constant(Input2Deafult);
        // if the input is hooked up, use it, otherwise use the internal constant
        int32 Input3Value = Input3.GetTracedInput().Expression ? Input3.Compile(Compiler) : Compiler->Constant(Input3Deafult);
    
        //////////////////////////////////////////////
        // FUNCTION example
        //////////////////////////////////////////////
    
        int32 value1 = Compiler->Mul(Input1Value, Input2Value);
        int32 Result1 = Compiler->Add(value1, Input2Value);                // Output 1
    
        int32 Result2 = Compiler->AppendVector(value1, Input2Value);    // Output 2
    
        int32 Result3 = Compiler->Div(Result2, Input3Value);            // Output 3
    
        return 0; //right now I don't know how to return the 3 results and pass them to the 3 output pins
    }
    
    const TArray<FExpressionInput*> UMaterialExpressionCustomNodeMultiOutput::GetInputs()
    {
        TArray<FExpressionInput*> OutInputs;
    
        int32 InputIndex = 0;
        while (FExpressionInput * Ptr = GetInput(InputIndex++))
        {
            OutInputs.Add(Ptr);
        }
    
        return OutInputs;
    }
    
    // Material Expression Node Name
    void UMaterialExpressionCustomNodeMultiOutput::GetCaption(TArray<FString>& OutCaptions) const
    {
        OutCaptions.Add(TEXT("Custom MultiOutput"));
    }
    #endif // WITH_EDITOR
Working...
X