在使用了PCGBlueprintElement蓝图编写PCG节点以及C++编写节点后,认为通过C++可以更方便的访问和操作数据,尤其是在PCG这种对属性以及数学运算有一定相关需求的场景。而通过C++也可以拥有更好的性能、选择普通处理或使用PCG中提供的多线程处理等多种方式。

PCG-C++中有两个重要的基类,分别是UPCGSettings和IPCGElement。

  • U字开头的UPCGSettings负责在PCGGraph中保存节点的名字、输入输出口信息、节点上的设置等内容,以及创建IPCGElement的功能。
  • IPCGElement则负责PCG的计算,可能是单线程的,也可能是多线程的,看具体实现。

 

编写样例:给一组数据生成一个确定的Seed序列

需求:PCG的多个样例与随机节点中都使用了位置作为Seed其中的一项计算值,这样会导致修改位置后Seed被修改,点云也会重新进行随机计算,没有办法修改已经制作好的外观的位置。

实现:

#pragma once

#include "CoreMinimal.h"
#include "PCGSettings.h"
#include "Elements/PCGPointOperationElementBase.h"
#include "UObject/Object.h"
#include "PCGDeterministicRandomSettings.generated.h"

UCLASS(Blueprintable, BlueprintType)
class HYPERPCG_API UPCGDeterministicRandomSettings : public UPCGSettings
{
    GENERATED_BODY()

public:
    UPCGDeterministicRandomSettings();
    virtual FPCGElementPtr CreateElement() const override;
    
#if WITH_EDITOR
    virtual FName GetDefaultNodeName() const override { return "DeterministicRandom"; }
    virtual EPCGSettingsType GetType() const override { return EPCGSettingsType::Spatial; }
#endif
    
    virtual TArray<FPCGPinProperties> InputPinProperties() const override;
    virtual TArray<FPCGPinProperties> OutputPinProperties() const override;
    
};

class FPCGDeterministicRandomElement : public IPCGElement
{
public:
    virtual bool ExecuteInternal(FPCGContext* Context) const override;
};

GetDefaultNodeName和GetType这几个都要在WITH_EDITOR内,否则这

cpp实现部分

#include "PCGDeterministicRandomSettings.h"

#include "PCGContext.h"
#include "Data/PCGPointData.h"
#include "Helpers/PCGHelpers.h"

UPCGDeterministicRandomSettings::UPCGDeterministicRandomSettings()
{
    bUseSeed = true;
}

FPCGElementPtr UPCGDeterministicRandomSettings::CreateElement() const
{
    return MakeShared<FPCGDeterministicRandomElement>();
    
}

TArray<FPCGPinProperties> UPCGDeterministicRandomSettings::InputPinProperties() const
{
    TArray<FPCGPinProperties> PinProperties;
    FPCGPinProperties& InputPinProperty = PinProperties.Emplace_GetRef(PCGPinConstants::DefaultInputLabel, EPCGDataType::PointOrParam);
    InputPinProperty.SetRequiredPin();
    return PinProperties;
}

TArray<FPCGPinProperties> UPCGDeterministicRandomSettings::OutputPinProperties() const
{
    TArray<FPCGPinProperties> PinProperties;
    PinProperties.Emplace(PCGPinConstants::DefaultOutputLabel, EPCGDataType::PointOrParam);

    return PinProperties;
}

bool FPCGDeterministicRandomElement::ExecuteInternal(FPCGContext* Context) const
{
    auto Settings = Context->GetInputSettings<UPCGDeterministicRandomSettings>();

    FRandomStream RandomStream = Context->GetSeed();

    auto InputData = Context->InputData.GetInputsByPin(PCGPinConstants::DefaultInputLabel);
    auto& OutputData = Context->OutputData.TaggedData;
    
    for (auto& Source : InputData)
    {
        auto& Output = OutputData.Emplace_GetRef(Source);
        Output.Pin = PCGPinConstants::DefaultOutputLabel;
        auto DataPtr = Cast<UPCGPointData>(Output.Data->DuplicateData());

        for (FPCGPoint& MutablePoint : DataPtr->GetMutablePoints())
        {
            MutablePoint.Seed = RandomStream.GetCurrentSeed();
        }
        
        Output.Data = DataPtr;
    }

    return true;
    
}

为了序列的稳定则直接使用循环计算而不是多线程。