UE4 材质编辑器中材质结点的数据结构源码浅析
UE4 的材质编辑器中我们可以创建材质结点然后给它们连线来创建材质
本文将根据源码解析材质结点在内存中的数据结构
UEdGraph
先简单提一下 UEdGraph
它用来管理一张可视化的有向图,而且其实蓝图就是用这个东西来做的
一个 UEdGraph
对象会包括图中所有的 UEdGraphNode
也就是结点
一个结点包括位置等基础信息,然后它还有一个 UEdGraphPin
的指针数组
一个 UEdGraphPin
其实就是一个插槽,用一个字节表示是输入插槽还是输出插槽,然后持有它所属的 UEdGraphNode
指针,还有一个 UEdGraphNode
指针数组保存这个插槽连接到的结点
UMaterialGraph
它派生自 UEdGraph
,保存一个材质在编辑器中的所有结点形成的有向图。
虽然它主要用于管理由材质结点组成的可视化的有向图,但它也持有 UMaterial
指针用来获取材质输入插槽等信息。
然后用户在编辑器中对材质的修改都会作用到这个对象上。
它有一个 UMaterialGraphNode_Root
类型的成员变量 RootNode
。
这个 RootNode
其实就是编辑器中的材质结果结点。
通过这个结点向前递归我们可以找到这个材质结果结点连接的所有结点,不过其实它的基类 UEdGraph
本身就具有这样的功能。
UMaterialGraphNode_Base
根据名字我们知道它是材质结点的基类,派生自 UEdGraphNode
。
比如前面提到的 UMaterialGraphNode_Root
就是它的一个派生类;此外它的派生类还有 UMaterialGraphNode
,这个派生类用于管理材质结果结点之外的其他结点。
这两个派生类之间的主要区别在于获取插槽等结点数据的方式不同。
UMaterialGraphNode_Root
获取插槽数据是通过 UEdGraphNode
的 GetGraph
方法获取所属的 UEdGraph
指针,然后类型转换到 UMaterialGraph
指针,再通过 UMaterialGraph
的 MaterialInputs
得到材质结果结点上的各个输入。
下面贴一下它的 CreateInputPins
的实现:
void UMaterialGraphNode_Root::CreateInputPins()
{
UMaterialGraph* MaterialGraph = CastChecked<UMaterialGraph>(GetGraph());
for (const FMaterialInputInfo& MaterialInput : MaterialGraph->MaterialInputs)
{
UEdGraphPin* InputPin = CreatePin(EGPD_Input, UMaterialGraphSchema::PC_MaterialInput, *FString::Printf(TEXT("%d"), (int32)MaterialInput.GetProperty()), *MaterialInput.GetName().ToString());
}
}
然后 UMaterialGraphNode
管理的是其他结点,其实也就是表达式结点,它持有一个 UMaterialExpression
指针,于是通过这个指针直接就能拿到结点表达式的输入输出的类型和名称。
UMaterialExpression
是结点表达式。
因为不同类型的结点会具有不同的输入输出和功能,所以一个结点就是一个具有若干输入和输出表达式。
但是 UMaterialExpression
只是一个接口,不同表达式的输入输出不同,因此具体的表达式都会派生自 UMaterialExpression
,比如 UMaterialExpressionAdd
就是具体的加法表达式。
这里简单贴一下它的 CreateInputPins
的实现:
void UMaterialGraphNode::CreateInputPins()
{
const TArray<FExpressionInput*> ExpressionInputs = MaterialExpression->GetInputs();
for (int32 Index = 0; Index < ExpressionInputs.Num() ; ++Index)
{
FExpressionInput* Input = ExpressionInputs[Index];
FName InputName = MaterialExpression->GetInputName(Index);
InputName = GetShortenPinName(InputName);
const FName PinCategory = MaterialExpression->IsInputConnectionRequired(Index) ? UMaterialGraphSchema::PC_Required : UMaterialGraphSchema::PC_Optional;
UEdGraphPin* NewPin = CreatePin(EGPD_Input, PinCategory, InputName);
if (NewPin->PinName.IsNone())
{
// Makes sure pin has a name for lookup purposes but user will never see it
NewPin->PinName = CreateUniquePinName(TEXT("Input"));
NewPin->PinFriendlyName = SpaceText;
}
}
}