Returns the array of node names that the material has

I want to find some material with a specific node, can I get the arrangement of the nodes that material itself has in python?

I don’t see anything in the Python API documentation that lets you search for a specific node, but I do see a function to get information on a parameter. If your node uses a specific parameter, you could search for it this way.

Here is the link to the page I found this on in case you want to look for any other useful functions:
https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/MaterialInterface?application_version=4.27#unreal.MaterialInterface

Alternatively, I’m seeing some potential here:
https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/MaterialEditingLibrary?highlight=get%20materialexpression&application_version=4.27

None of these functions directly returns a specific material function, but you may still be able to get some results. For instance, I can see a method where you use the get_inputs_for_material_expression, and if it returns null, then your material does not contain the expression. However, it looks like this method requires an active material editor, so you would first have to code the material graph opening up.

2 Likes

I typed this earlier but accidentally hit delete…grrr (need undo epic!)

The ‘nodes’ aren’t code, they are xml-bodies that represent calls/pure-functions that are compiled-down into native-code for the GPU. eg:

Summary

Begin Object Class=/Script/UnrealEd.MaterialGraphNode Name=“MaterialGraphNode_2” ExportPath=“/Script/UnrealEd.MaterialGraphNode’/Engine/Transient.M_Tower_Highlight:MaterialGraph_0.MaterialGraphNode_2’”
Begin Object Class=/Script/Engine.MaterialExpressionMaterialFunctionCall Name=“MaterialExpressionMaterialFunctionCall_0” ExportPath=“/Script/Engine.MaterialExpressionMaterialFunctionCall’/Engine/Transient.M_Tower_Highlight:MaterialGraph_0.MaterialGraphNode_2.MaterialExpressionMaterialFunctionCall_0’”
End Object
Begin Object Name=“MaterialExpressionMaterialFunctionCall_0” ExportPath=“/Script/Engine.MaterialExpressionMaterialFunctionCall’/Engine/Transient.M_Tower_Highlight:MaterialGraph_0.MaterialGraphNode_2.MaterialExpressionMaterialFunctionCall_0’”
MaterialFunction=“/Script/Engine.MaterialFunction’/Game/Materials/MF_SDF_AA.MF_SDF_AA’”
FunctionInputs(0)=(ExpressionInputId=EE49EF2E4233F32F23DAC4BE05A2C7F4,Input=(Expression=“/Script/Engine.MaterialExpressionSaturate’MaterialExpressionSaturate_2’”,InputName=“SDF”))
FunctionInputs(1)=(ExpressionInputId=BA99EEC24C99C22AC1C416B825916021,Input=(Expression=“/Script/Engine.MaterialExpressionConstant’MaterialExpressionConstant_1’”,InputName=“Threshold”))
FunctionInputs(2)=(ExpressionInputId=84EBF17040B5F5EACDF3C09285B8BF1E,Input=(Expression=“/Script/Engine.MaterialExpressionConstant’MaterialExpressionConstant_0’”,InputName=“Width”))
FunctionOutputs(0)=(ExpressionOutputId=05B634674CFD2FD433AD6B91D335B9D2,Output=(OutputName=“Result”))
MaterialExpressionEditorX=624
MaterialExpressionEditorY=-144
MaterialExpressionGuid=DAB5391543A6E82EA3FA06AE9E0D5DBD
Material=“/Script/UnrealEd.PreviewMaterial’/Engine/Transient.M_Tower_Highlight’”
Outputs(0)=(OutputName=“Result”)
End Object
MaterialExpression=“/Script/Engine.MaterialExpressionMaterialFunctionCall’MaterialExpressionMaterialFunctionCall_0’”
NodePosX=624
NodePosY=-144
NodeGuid=CDC5CBAA4303D558CE6BBC9E72E4E322
CustomProperties Pin (PinId=46C09AD34263C499942433BC0FA22933,PinName=“SDF (S)”,PinType.PinCategory=“required”,PinType.PinSubCategory=“”,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(MaterialGraphNode_3 665EAC564615E86BDC1193AA6E0CE83C,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=2EA7A53C4F10B7D3105F22818203D9D7,PinName=“Threshold (S)”,PinType.PinCategory=“optional”,PinType.PinSubCategory=“”,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(MaterialGraphNode_18 09F36D974ED6EC93428D51A6DDCF88CC,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=C68337DC4CB271DDDC29DF9EEB62CE56,PinName=“Width (S)”,PinType.PinCategory=“optional”,PinType.PinSubCategory=“”,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(MaterialGraphNode_17 6FFCBFDF40120A953B3F8AA41D10DE1B,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=99A016044FE8A71A1B0E1CBB3194C8A2,PinName=“Result”,Direction=“EGPD_Output”,PinType.PinCategory=“”,PinType.PinSubCategory=“”,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(MaterialGraphNode_Root_0 772CABB448415F9B449D42B86AC4C680,MaterialGraphNode_19 632A801E49332B28FF3E618F9A654D1F,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object

Never tinkered with python, but if you can get the contents of the material/material-function you ought to be able just-search text against the name of the node you want.

Even opening the .uasset-file in Notepad++ gives you node-names:

Summary

line 57 is a function called STRATA_EFFECT (as in the literal name of the blue node I am calling) and Strata.On is a switch-node to toggle the function (hence it comes first):

it also puts a list of functions-used at least:
Engine/Functions/Engine_MaterialFunctions02/Utility/BreakOutFloat2Components øþ¨N
/Engine/Functions/Engine_MaterialFunctions02/Utility/BreakOutFloat3Components $[ΐN
/Engine/Functions/Engine_MaterialFunctions02/Utility/BreakOutFloat4Components :äà9G
/Engine/Functions/Engine_MaterialFunctions02/Utility/DebugFloat2Values cÉ¥“G

You can attack it at that level at least.

1 Like

Hi again @TumHardy!

I wanted to follow up on this again for you or anyone else who stumbles onto this thread. After needing to do the same thing, I’ve learned a lot.

To start off, I couldn’t find a way to search for a specific material expression within a material. Maybe there is a way to do this, but I didn’t see anything. Instead, you need to load the material and get all of the material expressions. I couldn’t find a way to differentiate between material expressions of the same type, so the method I used was to get their input parameters and their values to check if it was the one I was looking for.

Now, this entire process is a lot of for loops with a large amount of inputs. I simplified this down as much as possible by only searching for a material once then saving it to an array if it contained the node I needed, so I only needed to find the material once when iterating over a bunch of meshes. I also made sure I was only getting the input names and values of the specific node type I was searching for, so I didn’t need to get all of the input parameters of every material expression in the material.

In the end, this is what my setup looked like:



With this being the logic of my main python script that gets every material expression:

import unreal

if name == ‘main’:
# load the material
material = unreal.EditorAssetLibrary.load_asset(matpath)

it = unreal.ObjectIterator(unreal.MaterialExpression)

ocl_exp = []

# iterate over all loaded material expressions, append all that belong to material.
all_material_expressions = []
for material_expression in it:
    if material_expression.get_outer() == material:
        all_material_expressions.append(material_expression)

# print out all expressions we stored.
for material_expression in all_material_expressions:
    ocl_exp.append(material_expression)