PCG节点处理的是数据流,也就是点云,点云到底是啥?笼统地说就是一个个携带着信息的点组成的集合。但是在具体是使用过程中,我们还得了解这些”携带着信息的点“是如何被层层包装起来的。本文中老王就和大家一边拆解源代码一边做实验,尝试着深入理解一下PCG中的数据流。如有错误,敬请指正!
文章目录
- 1. FPCGDataCollection
- 2. FPCGTaggedData
- 3. UPCGData
- 3.1 UPCGSpatialData
- 3.1.1 UPCGPointData
- 3.1.2 UPCGSpatialDataWithPointCache
- 4. UPCGMetadata
- 5. FPCGPoint
- 6. 小结
1. FPCGDataCollection
Execute
和Execute with Context
共同的输入参数,是Input
,它的类型是FPCGDataCollection
,我们先看一下它源代码中最核心的部分:
USTRUCT(BlueprintType)
struct PCG_API FPCGDataCollection
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Data)
TArray<FPCGTaggedData> TaggedData;
}
一个TaggedData
的数组,每一个TaggedData
,对应着这个节点的一个输入连接,我们可以写个简单的代码试验一下:
LogBlueprintUserMessages: [BP_CustomNode_C_20] Tagged Data 数量:3
接下来我们看一下FPCGTaggedData
的内容。
2. FPCGTaggedData
我们先看一下FPCGTaggedData
源代码中最核心的部分:
USTRUCT(BlueprintType)
struct PCG_API FPCGTaggedData
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Data)
TObjectPtr<const UPCGData> Data = nullptr;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Data)
TSet<FString> Tags;
};
如上文所述每个FPCGTaggedData
对应着一条输入连接,每一条输入连接可能来自一个Actor
、一个Spline
、一个Landscape
以及上一级节点的加工,那么每一条输入就会携带者原始Actor的Tag或者后添加上去的Tag。因此FPCGTaggedData
包含最重要的两部分信息就是UPCGData
的指针和Tag的集合。
我们再看一下什么是UPCGData
3. UPCGData
UPCGData
是PCG中各种类型信息的基类
UCLASS(BlueprintType, ClassGroup = (Procedural))
class PCG_API UPCGData : public UObject
{
GENERATED_BODY()
public:
/** Unique ID for this object instance. */
UPROPERTY(Transient)
uint64 UID = 0;
private:
/** Serves unique ID values to instances of this object. */
static inline std::atomic<uint64> UIDCounter{ 1 };
}
UPCGData
最核心的部分就是让每一个UPCGData
都有一个全局唯一的UID
,也就是说无论你的UPCGData
是在何时生成(获取)的,它的UID
都是全局唯一的。从上面的类图可以看到,在PCG中,我们经常使用的Data类型,不仅都派生自UPCGData
,它们实际也都派生自UPCGSpatialData
。
3.1 UPCGSpatialData
空间相关的UPCGData
UCLASS(Abstract, BlueprintType, ClassGroup = (Procedural))
class PCG_API UPCGSpatialData : public UPCGData
{
GENERATED_BODY()
// Not accessible through blueprint to make sure the constness is preserved
UPROPERTY(VisibleAnywhere, Category = Metadata)
TObjectPtr<UPCGMetadata> Metadata = nullptr;
}
相对于UPCGData
,每个UPCGSpatialData
多了一个UPCGMetadata
,UPCGMetadata
的作用就是允许用户自定义特性Attributes
UPCGSpatialData
类有两个大的派生分支UPCGPointData
和UPCGSpatialDataWithPointCache
3.1.1 UPCGPointData
UCLASS(BlueprintType, ClassGroup = (Procedural))
class PCG_API UPCGPointData : public UPCGSpatialData
{
GENERATED_BODY()
UPROPERTY()
TArray<FPCGPoint> Points;
}
UPCGPointData
中最重要的就是包含FPCGPoint
数组,也就是说一个UPCGPointData
对着一组Point
。
3.1.2 UPCGSpatialDataWithPointCache
UCLASS(Abstract, ClassGroup = (Procedural))
class PCG_API UPCGSpatialDataWithPointCache : public UPCGSpatialData
{
GENERATED_BODY()
UPROPERTY(Transient)
mutable TArray<TObjectPtr<const UPCGPointData>> CachedBoundedPointData;
}
UPCGSpatialDataWithPointCache
包含了一个UPCGPointData
数组(间接包含了若干个FPCGPoint
数组)
4. UPCGMetadata
注意:
UPCGMetadata
并不派生自UPCGData
先看一下核心代码:
UCLASS(BlueprintType)
class PCG_API UPCGMetadata : public UObject
{
GENERATED_BODY()
UPROPERTY()
TObjectPtr<const UPCGMetadata> Parent;
UPROPERTY()
TSet<TWeakObjectPtr<const UPCGMetadata>> OtherParents;
TMap<FName, FPCGMetadataAttributeBase*> Attributes;
}
UPCGMetadata
也就是元数据,包含着Attributes的名称和值的Map,以及这些Attributes的来源,加上针对Attributes的各种操作。
在官方的文档中,自定义的特性叫做Attribute
,而Point
原生的属性叫做Property
。Property
直接定义在FPCGPoint
中。
5. FPCGPoint
USTRUCT(BlueprintType)
struct PCG_API FPCGPoint
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Properties)
FTransform Transform;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Properties)
float Density = 1.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Properties)
FVector BoundsMin = -FVector::One();
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Properties)
FVector BoundsMax = FVector::One();
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Properties)
FVector4 Color = FVector4::One();
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Properties, meta = (ClampMin = "0", ClampMax = "1"))
float Steepness = 0.5f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Properties)
int32 Seed = 0;
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "Properties|Metadata")
int64 MetadataEntry = -1;
};
在FPCGPoint
中除了属性Property
的定义,还有一个int64
类型的MetadataEntry
,这个MetadataEntry
应该就对应着Point
和Metadata
(也就是Attribute
)的关联方式。但是具体的算法还需要进一步研究。
/** Adds a unique entry key to the metadata */
UFUNCTION(BlueprintCallable, Category = "PCG|Metadata")
int64 AddEntry(int64 ParentEntryKey = -1);
int64 UPCGMetadata::AddEntry(int64 ParentEntry)
{
FWriteScopeLock ScopeLock(ItemLock);
return ParentKeys.Add(ParentEntry) + ItemKeyOffset;
}
6. 小结
Execute
的Input
是一个FPCGDataCollection
,它包含多个FPCGTaggedData
- 每个
FPCGTaggedData
对应一个输入源 FPCGTaggedData
夹带着一串Tag和一个UPCGData
(多数时候实际是其派生类UPCGSpatialData
)- 每个
UPCGSpatialData
(UPCGPointData
或UPCGSpatialDataWithPointCache
)对应着一组Point
- 每个
Point
已包含固有属性(Property
)通过int64
的MetadataEntry
关联到自己的特性(Attribute
)