《Electric Dreams》项目中提供了一些自定义节点和子图(文件位置:“/Content/PCG/Assets/PCGCustomNodes”),这些节点和子图在《Electric Dreams》被广泛使用,对于理解《Electric Dreams》非常重要,而且它们可以直接移植到新的项目中使用。所以写个博客分析一下
文章目录
- 前导文章
- Passthrough节点
- 作用
- Execute with Context
- PointNormalToColor节点
- 作用
- Point Loop Body
- Execute with Context
- PointFromPCGVolume节点
- 作用
- Execute with Context
前导文章
《虚幻引擎程序化资源生成框架PCG 之 UPCGBlueprintElement源码笔记(一)》
《虚幻引擎程序化资源生成框架PCG 之 UPCGBlueprintElement源码笔记(二)数据流》
Passthrough节点
作用
数据流的开关
Execute with Context
用Enabled
控制是否将从Input
输入进来的从Output
输出,当Enabled
为false
的时候,输出的PCGDataCollection
中的Tagged Data
数组将会是一个空数组。
PointNormalToColor节点
作用
将Point法线存入Color属性
Point Loop Body
先看一下它的Point Loop Body
Point Loop Body的逻辑就是将每一个Point的Up Vector存储在Color属性中,然后把修改过的Point输出。
Execute with Context
注释见上图,在PointLoopBody后面有一个Initialize from Data
节点,代码如下:
void UPCGSpatialData::InitializeFromData(const UPCGSpatialData* InSource, const UPCGMetadata* InMetadataParentOverride, bool bInheritMetadata, bool bInheritAttributes)
{
if (InSource && TargetActor.IsExplicitlyNull())
{
TargetActor = InSource->TargetActor;
}
if (!Metadata)
{
Metadata = NewObject<UPCGMetadata>(this);
}
if (!bInheritMetadata || InMetadataParentOverride || InSource)
{
const UPCGMetadata* ParentMetadata = bInheritMetadata ? (InMetadataParentOverride ? InMetadataParentOverride : (InSource ? InSource->Metadata : nullptr)) : nullptr;
Metadata->Initialize(ParentMetadata, bInheritAttributes);
}
else
{
UE_LOG(LogPCG, Warning, TEXT("InitializeFromData has both no source and no metadata override"));
}
}
Initialize from Data
做了两件事:
- 将源Data中的
TargetActor
赋值给新PCGSpatialData
的TargetActor
- 用源Data的metadata初始化新
PCGSpatialData
的metadata
PointFromPCGVolume节点
作用
使用Context
中Source
的Component
或者Component
的几何信息(Transform
和Bound
)构造1个PCGPoint
。
Execute with Context
我们先看一下GetComponent
和GetOriginalComponent
UPCGComponent* UPCGBlueprintHelpers::GetComponent(FPCGContext& Context)
{
return Context.SourceComponent.Get();
}
UPCGComponent* UPCGBlueprintHelpers::GetOriginalComponent(FPCGContext& Context)
{
if (Context.SourceComponent.IsValid() &&
Cast<APCGPartitionActor>(Context.SourceComponent->GetOwner()) &&
Cast<APCGPartitionActor>(Context.SourceComponent->GetOwner())->GetOriginalComponent(Context.SourceComponent.Get()))
{
return Cast<APCGPartitionActor>(Context.SourceComponent->GetOwner())->GetOriginalComponent(Context.SourceComponent.Get());
}
else
{
return Context.SourceComponent.Get();
}
}
再看看它是如何获取GetActorLocalBoundsPCG
的
UPCGBlueprintHelpers::GetActorLocalBoundsPCG
FBox UPCGBlueprintHelpers::GetActorLocalBoundsPCG(AActor* InActor, bool bIgnorePCGCreatedComponents)
{
return PCGHelpers::GetActorLocalBounds(InActor, bIgnorePCGCreatedComponents);
}
PCGHelpers::GetActorLocalBounds
FBox GetActorLocalBounds(const AActor* InActor, bool bIgnorePCGCreatedComponents)
{
// Specialized version of CalculateComponentsBoundingBoxInLocalScape that skips over PCG generated components
// This is to ensure stable bounds and no timing issues (cleared ISMs, etc.)
FBox Box(EForceInit::ForceInit);
const bool bNonColliding = true;
const bool bIncludeFromChildActors = true;
if (InActor)
{
const FTransform& ActorToWorld = InActor->GetTransform();
const FTransform WorldToActor = ActorToWorld.Inverse();
InActor->ForEachComponent<UPrimitiveComponent>(bIncludeFromChildActors, [bNonColliding, bIgnorePCGCreatedComponents, &WorldToActor, &Box](const UPrimitiveComponent* InPrimComp)
{
if ((bNonColliding || InPrimComp->IsCollisionEnabled()) &&
(!bIgnorePCGCreatedComponents || !InPrimComp->ComponentTags.Contains(DefaultPCGTag)))
{
const FTransform ComponentToActor = InPrimComp->GetComponentTransform() * WorldToActor;
Box += InPrimComp->CalcBounds(ComponentToActor).GetBox();
}
});
}
else
{
UE_LOG(LogPCG, Error, TEXT("Actor is invalid in GetActorLocalBounds"));
}
return Box;
}
所谓LocalBounds
就是把所属Actor的所有PrimitiveComponent叠加起来获得最大的FBox