有朋友询问:Gather(收集)、Merge(合并)、Union(并集)这三个运算节点,看名字有些相似,究竟区别是什么?目前还没有详细的官方文档,所以今天老王结合源代码,尝试分析一下。
文章目录
- Gather(收集)
- Gather的作用:
- Merge(合并)
- Merge的作用:
- Union(并集)
- Union的作用:
- 小结
Gather(收集)
bool FPCGGatherElement::ExecuteInternal(FPCGContext* Context) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(FPCGGatherElement::Execute);
Context->OutputData = Context->InputData;
return true;
}
Gather的作用:
FPCGDataCollection
:输入端所有FPCGDataCollection
合并成一个FPCGDataCollection
,对数据不做任何处理,所以最终输出端的FPCGDataCollection
数量为1。FPCGTaggedData
:最终输出端的FPCGTaggedData
数量为输入端所有FPCGDataCollection
中包含的FPCGTaggedData
数量总和。PCGPoint
:最终输出端的PCGPoint
数量为输入端所有FPCGDataCollection
中包含的PCGPoint
数量总和。
小结:
Gather
就是对FPCGDataCollection
简单汇总操作。
Merge(合并)
bool FPCGMergeElement::ExecuteInternal(FPCGContext* Context) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(FPCGMergeElement::Execute);
check(Context);
const UPCGMergeSettings* Settings = Context->GetInputSettings<UPCGMergeSettings>();
check(Settings);
const bool bMergeMetadata = Settings->bMergeMetadata;
TArray<FPCGTaggedData> Sources = Context->InputData.GetInputs();
TArray<FPCGTaggedData>& Outputs = Context->OutputData.TaggedData;
if (Sources.IsEmpty())
{
return true;
}
UPCGPointData* TargetPointData = nullptr;
FPCGTaggedData* TargetTaggedData = nullptr;
// Prepare data & metadata
// Done in two passes for futureproofing - expecting changes in the metadata attribute creation vs. usage in points
for (const FPCGTaggedData& Source : Sources)
{
const UPCGPointData* SourcePointData = Cast<const UPCGPointData>(Source.Data);
if (!SourcePointData)
{
PCGE_LOG(Error, GraphAndLog, LOCTEXT("UnsupportedDataType", "Unsupported data type in merge"));
continue;
}
if (!TargetPointData)
{
TargetPointData = NewObject<UPCGPointData>();
TargetPointData->InitializeFromData(SourcePointData, nullptr, bMergeMetadata);
TargetTaggedData = &(Outputs.Emplace_GetRef(Source));
TargetTaggedData->Data = TargetPointData;
}
else
{
if (bMergeMetadata)
{
TargetPointData->Metadata->AddAttributes(SourcePointData->Metadata);
}
check(TargetTaggedData);
TargetTaggedData->Tags.Append(Source.Tags); // TODO: only unique? if yes, fix union too
}
}
// No valid input types
if (!TargetPointData)
{
return true;
}
TArray<FPCGPoint>& TargetPoints = TargetPointData->GetMutablePoints();
for(int32 SourceIndex = 0; SourceIndex < Sources.Num(); ++SourceIndex)
{
const UPCGPointData* SourcePointData = Cast<const UPCGPointData>(Sources[SourceIndex].Data);
if (!SourcePointData)
{
continue;
}
int32 PointOffset = TargetPoints.Num();
TargetPoints.Append(SourcePointData->GetPoints());
if ((!bMergeMetadata || SourceIndex != 0) && !SourcePointData->GetPoints().IsEmpty())
{
TArrayView<FPCGPoint> TargetPointsSubset = MakeArrayView(&TargetPoints[PointOffset], SourcePointData->GetPoints().Num());
for (FPCGPoint& Point : TargetPointsSubset)
{
Point.MetadataEntry = PCGInvalidEntryKey;
}
if (bMergeMetadata && TargetPointData->Metadata && SourcePointData->Metadata && SourcePointData->Metadata->GetAttributeCount() > 0)
{
TargetPointData->Metadata->SetPointAttributes(MakeArrayView(SourcePointData->GetPoints()), SourcePointData->Metadata, TargetPointsSubset);
}
}
}
return true;
}
Merge的作用:
FPCGDataCollection
:最终输出端的FPCGDataCollection
数量为1。FPCGTaggedData
:最终输出端的FPCGTaggedData
数量也为1。输入端所有Tag
的并集和Attribute
的并集作为输出端FPCGTaggedData
的Tag
和Attribute
。如果两条FPCGTaggedData
中有同名Attribute
且类型不一致以第一个为准。PCGPoint
:最终输出端的PCGPoint
数量为输入端所有FPCGDataCollection
中包含的PCGPoint
数量总和。
小结:
Merge
是FPCGTaggedData
层级的合并。
Union(并集)
bool FPCGUnionElement::ExecuteInternal(FPCGContext* Context) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(FPCGUnionElement::Execute);
const UPCGUnionSettings* Settings = Context->GetInputSettings<UPCGUnionSettings>();
check(Settings);
TArray<FPCGTaggedData> Inputs = Context->InputData.GetInputs();
const EPCGUnionType Type = Settings->Type;
const EPCGUnionDensityFunction DensityFunction = Settings->DensityFunction;
TArray<FPCGTaggedData>& Outputs = Context->OutputData.TaggedData;
const UPCGSpatialData* FirstSpatialData = nullptr;
UPCGUnionData* UnionData = nullptr;
int32 UnionTaggedDataIndex = -1;
for (FPCGTaggedData& Input : Inputs)
{
const UPCGSpatialData* SpatialData = Cast<UPCGSpatialData>(Input.Data);
// Non-spatial data we're not going to touch
if (!SpatialData)
{
Outputs.Add(Input);
continue;
}
if (!FirstSpatialData)
{
FirstSpatialData = SpatialData;
UnionTaggedDataIndex = Outputs.Num();
Outputs.Add(Input);
continue;
}
FPCGTaggedData& UnionTaggedData = Outputs[UnionTaggedDataIndex];
// Create union or add to it
if (!UnionData)
{
UnionData = FirstSpatialData->UnionWith(SpatialData);
UnionData->SetType(Type);
UnionData->SetDensityFunction(DensityFunction);
UnionTaggedData.Data = UnionData;
}
else
{
UnionData->AddData(SpatialData);
UnionTaggedData.Tags.Append(Input.Tags);
}
UnionTaggedData.Data = UnionData;
}
return true;
}
UnionData = FirstSpatialData->UnionWith(SpatialData);
Union
的关键是对FPCGSpatialData
进行并集运算。符合条件的FPCGSpatialData
会合并成1个。
Union的作用:
FPCGDataCollection
:最终输出端的FPCGDataCollection
数量为1。FPCGTaggedData
:最终输出端的FPCGTaggedData
数量也为1。PCGPoint
:最终输出端的PCGPoint
数量≤输入端所有FPCGDataCollection
中包含的PCGPoint
数量总和。
小结
简单地说:Gather(收集)、Merge(合并)、Union(并集)依次是从FPCGDataCollection
层级、FPCGTaggedData
层级、PCGPoint
(FPCGSpatialData
)层级和“合并”运算。