LNK2019 Error occurs when I am trying to add a new material node in a blank plugin template.

Hello

I am trying to add new types of material node in a blank plugin template, such as an new “add” node.

Here is how it looks like:

image

However, when I try to build the project, Error LNK2019 occurs.

Error	LNK2019	unresolved external symbol "public: class UE::HLSLTree::FExpression const * __cdecl UE::HLSLTree::FTree::NewBinaryOp(enum UE::HLSLTree::EOperation,class UE::HLSLTree::FExpression const *,class UE::HLSLTree::FExpression const *)" (?NewBinaryOp@FTree@HLSLTree@UE@@QEAAPEBVFExpression@23@W4EOperation@23@PEBV423@1@Z) referenced in function "private: virtual bool __cdecl UUMaterialExpressionAddCustom::GenerateHLSLExpression(class FMaterialHLSLGenerator &,class UE::HLSLTree::FScope &,int,class UE::HLSLTree::FExpression const * &)const " (?GenerateHLSLExpression@UUMaterialExpressionAddCustom@@EEBA_NAEAVFMaterialHLSLGenerator@@AEAVFScope@HLSLTree@UE@@HAEAPEBVFExpression@45@@Z)	WorkPlace	E:\UnrealProjects\WorkPlace\Intermediate\ProjectFiles\Module.CustomMaterialNode.cpp.obj	1	

Error	LNK2019	unresolved external symbol "public: class UE::HLSLTree::FTree & __cdecl FMaterialHLSLGenerator::GetTree(void)const " (?GetTree@FMaterialHLSLGenerator@@QEBAAEAVFTree@HLSLTree@UE@@XZ) referenced in function "private: virtual bool __cdecl UUMaterialExpressionAddCustom::GenerateHLSLExpression(class FMaterialHLSLGenerator &,class UE::HLSLTree::FScope &,int,class UE::HLSLTree::FExpression const * &)const " (?GenerateHLSLExpression@UUMaterialExpressionAddCustom@@EEBA_NAEAVFMaterialHLSLGenerator@@AEAVFScope@HLSLTree@UE@@HAEAPEBVFExpression@45@@Z)	WorkPlace	E:\UnrealProjects\WorkPlace\Intermediate\ProjectFiles\Module.CustomMaterialNode.cpp.obj	1	

The code of the new type of material node is almost same to the original UMaterialExpressionAdd. Here is my code of UMaterialExpressionAddCustom.h and UMaterialExpressionAddCustom.cpp.

#pragma once

#include "CoreMinimal.h"
#include "MaterialExpressionIO.h"
#include "Materials/MaterialExpression.h"
#include "UMaterialExpressionAddCustom.generated.h"

UCLASS()
class CUSTOMMATERIALNODE_API UUMaterialExpressionAddCustom : public UMaterialExpression
{
	GENERATED_BODY()
	
	UUMaterialExpressionAddCustom(const FObjectInitializer& ObjectInitializer);

	UPROPERTY(meta = (RequiredInput = "false", ToolTip = "Defaults to 'ConstA' if not specified"))
	FExpressionInput A;

	UPROPERTY(meta = (RequiredInput = "false", ToolTip = "Defaults to 'ConstB' if not specified"))
	FExpressionInput B;

	UPROPERTY(EditAnywhere, Category=MaterialExpressionAdd, meta=(OverridingInputProperty = "A"))
	float ConstA;

	UPROPERTY(EditAnywhere, Category=MaterialExpressionAdd, meta=(OverridingInputProperty = "B"))
	float ConstB;

#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("AddCustom")); }

	virtual bool GenerateHLSLExpression(FMaterialHLSLGenerator& Generator, UE::HLSLTree::FScope& Scope, int32 OutputIndex, UE::HLSLTree::FExpression const*& OutExpression) const override;
#endif // WITH_EDITOR
};

#include "UMaterialExpressionAddCustom.h"
#include "MaterialCompiler.h"
#include "MaterialHLSLGenerator.h"
#include "HLSLTree/HLSLTree.h"

#define LOCTEXT_NAMESPACE "MaterialExpression2"

UUMaterialExpressionAddCustom::UUMaterialExpressionAddCustom(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// Structure to hold one-time initialization
	struct FConstructorStatics
	{
		FText NAME_Math;
		FConstructorStatics()
			: NAME_Math(LOCTEXT( "Math", "Math" ))
		{
		}
	};
	static FConstructorStatics ConstructorStatics;

	ConstA = 0.0f;
	ConstB = 1.0f;

#if WITH_EDITORONLY_DATA
	MenuCategories.Add(ConstructorStatics.NAME_Math);
#endif
}

#undef LOCTEXT_NAMESPACE

#if WITH_EDITOR

int32 UUMaterialExpressionAddCustom::Compile(FMaterialCompiler* Compiler, int32 OutputIndex)
{
	// if the input is hooked up, use it, otherwise use the internal constant
	int32 Arg1 = A.GetTracedInput().Expression ? A.Compile(Compiler) : Compiler->Constant(ConstA);
	// if the input is hooked up, use it, otherwise use the internal constant
	int32 Arg2 = B.GetTracedInput().Expression ? B.Compile(Compiler) : Compiler->Constant(ConstB);

	return Compiler->Add(Arg1, Arg2);
}


void UUMaterialExpressionAddCustom::GetCaption(TArray<FString>& OutCaptions) const
{
	FString ret = TEXT("AddCustom");

	FExpressionInput ATraced = A.GetTracedInput();
	FExpressionInput BTraced = B.GetTracedInput();
	if(!ATraced.Expression || !BTraced.Expression)
	{
		ret += TEXT("(");
		ret += ATraced.Expression ? TEXT(",") : FString::Printf( TEXT("%.4g,"), ConstA);
		ret += BTraced.Expression ? TEXT(")") : FString::Printf( TEXT("%.4g)"), ConstB);
	}

	OutCaptions.Add(ret);
}

bool UUMaterialExpressionAddCustom::GenerateHLSLExpression(FMaterialHLSLGenerator& Generator,
	UE::HLSLTree::FScope& Scope, int32 OutputIndex, UE::HLSLTree::FExpression const*& OutExpression) const
{
	
	const UE::HLSLTree::FExpression* Lhs = A.AcquireHLSLExpressionOrConstant(Generator, Scope, ConstA);
	const UE::HLSLTree::FExpression* Rhs = B.AcquireHLSLExpressionOrConstant(Generator, Scope, ConstB);
	if (!Lhs || !Rhs)
	{
		return false;
	}
	///////////////!!!!!!!!!!!!!!!!!!!!!!
	OutExpression = Generator.GetTree().NewAdd(Lhs, Rhs); // Something Wrong Here
	///////////////!!!!!!!!!!!!!!!!!!!!!!
	return true;
}

#endif

After some tests, I find the error comes from OutExpression = Generator.GetTree().NewAdd(Lhs, Rhs);.

Every time I try to use the method of FMaterialHLSLGenerator,the error will appear.

But if I delete this line, the project can be built successfully.

I guess it is not the problem about Module Dependency, because I have add Engine module to the build.cs file, which include the classes of Materials. Here is the content of build.cs file.

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class CustomMaterialNode : ModuleRules
{
	public CustomMaterialNode(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
			
		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core",
				"Engine",
				"CoreUObject",
				
				// ... add other public dependencies that you statically link with here ...
			}
			);
		
		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"Core",
				"CoreUObject",
				"Engine",
				
				// ... add private dependencies that you statically link with here ...	
			}
			);
		
		
	}
}

Any help will be greatly appreciated

Did you end up resolving the issues?

I think you can solve this error by adding the “UnrealEd” module as a dependency to your plugin, since this module contains the implementation of these classes.

To do this, you need to add the following line to your plugin’s .uplugin file, inside the “Modules” section:

{
    "Name": "YourPluginName",
    "Type": "Runtime",
    "LoadingPhase": "Default",
    "AdditionalDependencies": [
        "UnrealEd"
    ]
}

After adding the “UnrealEd” module as a dependency, you need to rebuild your plugin to ensure that the changes are picked up. Then, you can try building your project again and see if the error has been resolved.

I am getting the same Linker error as PureWhite_216 but my new code is not in a plugin.
I just created a C++ class in my project derived from UMaterialExpressionAdd. I wanted to play around a bit.
There doesn’t seem to be any compile errors but I see Linker errors for each usage of UE::HLSLTree::.
I added “UnrealEd” to my build.cs file as EliasWick suggested but that didn’t help.
When I commented out the UE:HLSLTree code, it compiles and I can see my new Add node in the Material Editor.
Thanks for any help.

It seems that any method that I want to use and is declared under Engine\private will produce a linker error.
I am assuming I am missing something in my Build.cs file.

In my build.cs file I have “Engine” in both Public and Private sections:

public class ExtendEditor : ModuleRules
{
public ExtendEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

	PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

	PrivateDependencyModuleNames.AddRange(new string[] { "Engine"  });

	// Uncomment if you are using Slate UI
	// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
	
	// Uncomment if you are using online features
	// PrivateDependencyModuleNames.Add("OnlineSubsystem");

	// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}

}

Thanks again for any help