Help! Unreal Engine 5.1.1 programmatically create a UMaterial: deprecated code

Hi, i’m creating a editor tool for unreal engine, this tool should create a new Material (UMaterial) and add node (aka UMaterialExpression) to its expressions and then connect this node to the outputs (like BaseColor, Roughness, Metallic, Opacity).

I started creating this tool in Unreal Engine 4 and everything was fine, the code to do this was:

//UsedMaterial is a UMaterial* defined in the .h as a class variable.
//and it is created like this:
			FString MaterialBaseName = "M_Material_" + GetName();
			FString PackageName = "/Game/";
			PackageName += MaterialBaseName;
			UPackage* Package = CreatePackage(NULL, *PackageName);

			UMaterialFactoryNew* MaterialFactory = NewObject<UMaterialFactoryNew>(); // Create an unreal material asset
			UsedMaterial = (UMaterial*)MaterialFactory->FactoryCreateNew(UMaterial::StaticClass(), Package, *MaterialBaseName, RF_Standalone | RF_Public, NULL, GWarn);



//Create a Texture Sample Node
UMaterialExpressionTextureSample* TextureRTMaskExpression = NewObject< UMaterialExpressionTextureSample>(UsedMaterial);

UsedMaterial->Expressions.Add(TextureRTMaskExpression);

//UsedTextureMask is a UTextureRenderTarget2D* alredy created at this point of code
//(it isn't null)
TextureRTMaskExpression->Texture = UsedTextureMask;

//... Other code
//finally i connect the UMaterial BaseColor expression to my //UMaterialExpressionTextureSample called 
//'TextureRTMaskExpression' created before
UsedMaterial->BaseColor.Expression = TextureRTMaskExpression;

All fine here, but now i’ve switched to Unreal Engine 5.1.1 and this property and methods are deprecated or modified.

first problem is:

UsedMaterial->BaseColor.Expression = TextureRTMaskExpression;

now BaseColor is called BaeColor_DEPRECATED and can’t be accessed.
The problem is i can’t find a new method or a property to di the same thing, and so my question is: how is managed now the material expression in c++?

second problem is this part:

UPackage* Package = CreatePackage(NULL, *PackageName);

This code line give me a warning like this:

warning C4996: ‘CreatePackage’: Use CreatePackage overload that does not take the first Outer parameter. Specifying non-null outers for UPackages is no longer supported. Please update your code to the new API before upgrading to the next release, otherwise your project will no longer compile.

if someone can explain me how the code i made for Unreal 4 is made now for Unreal 5.1.1 would be very helpfoul.

thank you in advance!

(And sorry if my english is not perfect, and sorry if there is alredya topic on this but i searched for hours here, in the documentation and on the internet but nothing)

2 Likes

Hi OPEsteban,

I found a bit of code in 5.1 (checked difference with 4.27) in TranslucentMaterialFactory.cpp line 56.

They get a UMaterialEditorOnlyData reference from the UMaterial and access them from there:

UMaterialEditorOnlyData& MaterialEditorOnly = *Material.GetEditorOnlyData();
...MaterialEditorOnly.BaseColor...

Hi, Thank you very much, i was checking the code too and found the same class type.
However i tried to use it like this:

UMaterialEditorOnlyData& Data = *InMaterialToGenerate->GetEditorOnlyData();
...
Data.BaseColor.Expression = TextureRTMaskExpression;

but unreal crash with this error::

Assertion failed: TextureReferenceIndex != INDEX_NONE [File:D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\Materials\HLSLMaterialTranslator.cpp] [Line: 6769]
Material expression called Compiler->Texture() without implementing UMaterialExpression::GetReferencedTexture properly

then i tried adding this lines:

UMaterialEditorOnlyData& Data = *InMaterialToGenerate->GetEditorOnlyData();


//Added these two
FMaterialExpressionCollection Expressions = Data.ExpressionCollection;
Expressions.AddExpression(TextureRTMaskExpression);

Data.BaseColor.Expression = TextureRTMaskExpression;

But crash with same error… my question now is, do i need to implement this method ( GetReferencedTexture() ) from UMaterialExpression but how? i don’t think that is needed to create a subclass of UMaterialExpressionTextureSample that implement and override that method… i’m so confused.

The texture isn’t null, and its created in the content browser correctly because i can see it, so the texture is fine, but assigning it to the material expression that i want.

PS. I alredy have this line of code after all the assignment:

FAssetRegistryModule::AssetCreated(InMaterialToGenerate);
Package->FullyLoad();
Package->SetDirtyFlag(true);

// Aggiornamento Materiale
InMaterialToGenerate->PreEditChange(NULL);
InMaterialToGenerate->PostEditChange();
	
FGlobalComponentReregisterContext RecreateComponents;

(I tried to remove this line of code but unreal crash with the same error)

i’ll continue to search in the code or on internet, if i make some progress i’ll post here, in the meanwhile i hope you or someone other know the solution to this problem.

thank you again!

FExpressionInput* BaseColorInput = UnrealMaterial->GetExpressionInputForProperty(EMaterialProperty::MP_BaseColor);

		UnrealMaterial->GetEditorOnlyData()->ExpressionCollection.Expressions.Add(TextureExpression);
		BaseColorInput->Expression = TextureExpression;

It works well for me.

3 Likes

thank you very much for this solution, i found another solution to my problem, the fix for my code was to add the & operator to this:

before:

UMaterialEditorOnlyData& Data = *UsedMaterial->GetEditorOnlyData();
FMaterialExpressionCollection MyExpressions = Data.ExpressionCollection;

after:

UMaterialEditorOnlyData& Data = *UsedMaterial->GetEditorOnlyData();
FMaterialExpressionCollection& MyExpressions = Data.ExpressionCollection; //Added here the & operator

then i was able to add all expression to my material using “MyExpressions” like this:

MyExpressions.AddExpression(/*UMaterialExpression HERE*/);

it worked.

i think your solution is better so i’ll mark your message as solution!
thank you again!

希望这些代码示例可以帮到你。

FString MaterialBaseName = "M_Material_created_by_code";
	FString PackageName = "/Game/Materials/";
	PackageName += MaterialBaseName;
    if(UPackage* Package = CreatePackage(*PackageName);
    	Package!=nullptr)
	{
		const auto ObjectFactory = NewObject<UMaterialFactoryNew>();

		if(UMaterial* Mtr = Cast<UMaterial>(ObjectFactory->FactoryCreateNew(UMaterial::StaticClass(),
		                                                                    Package,
		                                                                    FName(MaterialBaseName),
		                                                                    RF_Standalone | RF_Public,
		                                                                    nullptr,
		                                                                    GWarn));
		                                                                    Mtr != nullptr)
		{
			//这里像是再向引擎内部注册一个数字资产
			FAssetRegistryModule::AssetCreated(Mtr);

			//做一些设置,确保能够被正确加载和保存。

			Package->FullyLoad();
			Package->SetDirtyFlag(true);

			Mtr->PreEditChange(nullptr);
			Mtr->PostEditChange();

			UMaterialExpressionConstant* ZeroExpressionConstant =
				NewObject<UMaterialExpressionConstant>(Mtr);
			ZeroExpressionConstant->R = 0.f;
			ZeroExpressionConstant->MaterialExpressionEditorX = -600;;
			ZeroExpressionConstant->MaterialExpressionEditorY = 0;
			Mtr->GetEditorOnlyData()->ExpressionCollection.Expressions.Add(ZeroExpressionConstant);
			//Mtr->GetExpressionInputForProperty(EMaterialProperty::MP_Specular)->Expression = ZeroExpressionConstant;
			Mtr->GetEditorOnlyData()->Specular.Connect(0,ZeroExpressionConstant);
			TArray<FExpressionInput*> ZeroExpressionConstantInputs = ZeroExpressionConstant->GetInputs();
			TArray<FExpressionOutput>& ZeroExpressionConstantOutputs = ZeroExpressionConstant->GetOutputs();
			for (auto Input : ZeroExpressionConstantInputs)
			{
				UE_LOG(LogTemp,Warning,TEXT("-------------------------------------"));
			}
			for (auto Output : ZeroExpressionConstantOutputs)
			{
				UE_LOG(LogTemp,Warning,TEXT("-------------------------------------"));
			}
			
			// 创建并设置 Constant parameter node
			UMaterialExpressionVectorParameter* MaterialExpressionVectorParameter = 
				NewObject<UMaterialExpressionVectorParameter>(Mtr);
			MaterialExpressionVectorParameter->ParameterName = TEXT("VectorParameter");
			MaterialExpressionVectorParameter->DefaultValue = FLinearColor(FVector3d(0.2f, 0.0f, 0.5f));
			MaterialExpressionVectorParameter->MaterialExpressionEditorX = -600;
			MaterialExpressionVectorParameter->MaterialExpressionEditorY = 240;
			Mtr->GetEditorOnlyData()->ExpressionCollection.Expressions.Add(MaterialExpressionVectorParameter);




			UMaterialExpressionConstant* ZeroExpressionConstanMultiply =
				NewObject<UMaterialExpressionConstant>(Mtr);
			ZeroExpressionConstanMultiply->R = 0.f;
			ZeroExpressionConstanMultiply->MaterialExpressionEditorX = -600;;
			ZeroExpressionConstanMultiply->MaterialExpressionEditorY = 120;
			Mtr->GetEditorOnlyData()->ExpressionCollection.Expressions.Add(ZeroExpressionConstanMultiply);

		
			
			// 创建并设置 Multiply node
			UMaterialExpressionMultiply* MultiplyExpression = NewObject<UMaterialExpressionMultiply>(Mtr);
			MultiplyExpression->MaterialExpressionEditorX = -600;
			MultiplyExpression->MaterialExpressionEditorY = 480;
			MultiplyExpression->A.Connect(0,ZeroExpressionConstanMultiply);
			// 用这种方式可以选择要连接的输出编号
			MultiplyExpression->B.Connect(1, MaterialExpressionVectorParameter);
			Mtr->GetEditorOnlyData()->ExpressionCollection.Expressions.Add(MultiplyExpression);


			
			UMaterialExpressionConstant4Vector* MaterialExpressionConstant4Vector = NewObject<UMaterialExpressionConstant4Vector>(Mtr);
			MaterialExpressionConstant4Vector->MaterialExpressionEditorX = -600;
			MaterialExpressionConstant4Vector->MaterialExpressionEditorY = 580;
			MaterialExpressionConstant4Vector->Constant = FLinearColor(FVector3d(0.2f, 0.0f, 0.5f));
			Mtr->GetEditorOnlyData()->ExpressionCollection.Expressions.Add(MaterialExpressionConstant4Vector);
			Mtr->GetEditorOnlyData()->BaseColor.Connect(0,MaterialExpressionConstant4Vector);
			/*UMaterialExpressionTextureCoordinate* MaterialExpressionTextureCoordinate = NewObject<UMaterialExpressionTextureCoordinate>(Mtr);
			MaterialExpressionTextureCoordinate->UTiling = 1.f;
			MaterialExpressionTextureCoordinate->VTiling = 1.f;

			
			ExpressionInput = Mtr->GetExpressionInputForProperty(EMaterialProperty::MP_WorldPositionOffset);
			ExpressionInput->Expression = MaterialExpressionTextureCoordinate;*/

			

			
		}
		
	}
3 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.