UE5 Mass LOD

LOD重要数据结构

两套LOD:FMassSimulationLODFragment、FMassRepresentationLODFragment

USTRUCT()
struct MASSLOD_API FMassSimulationLODFragment : public FMassFragment
{
    GENERATED_BODY()
    /** 距离Viewer最近的距离平方*/
    float ClosestViewerDistanceSq = FLT_MAX;
    /**当前LOD */
    TEnumAsByte<EMassLOD::Type> LOD = EMassLOD::Max;
    /** 上一次的LOD*/
    TEnumAsByte<EMassLOD::Type> PrevLOD = EMassLOD::Max;
};

USTRUCT()
struct MASSREPRESENTATION_API FMassRepresentationLODFragment : public FMassFragment
{
    GENERATED_BODY()
    /** 当前LOD以及上一次的LOD */
    TEnumAsByte<EMassLOD::Type> LOD = EMassLOD::Max;
    TEnumAsByte<EMassLOD::Type> PrevLOD = EMassLOD::Max;
   
    /** 可见性 */
    EMassVisibility Visibility = EMassVisibility::Max;
    EMassVisibility PrevVisibility = EMassVisibility::Max;
    /** LOD重要度,值在0~3之间,值越小越重要 */
    float LODSignificance = 0.0f;
};

RepresentationLOD控制参数

USTRUCT()
struct FMassVisualizationLODParameters : public FMassSharedFragment
{
    GENERATED_BODY()
    
    UPROPERTY(EditAnywhere, Category = "Mass|LOD", config)
    float BaseLODDistance[EMassLOD::Max] = { 0.f, 1000.f, 2500.f, 10000.f };/** 视野外各级LOD的距离划分 */
    UPROPERTY(EditAnywhere, Category = "Mass|LOD", config)
    float VisibleLODDistance[EMassLOD::Max] = { 0.f, 2000.f, 4000.f, 15000.f };/** 视野内各级LOD的距离划分 */
    UPROPERTY(EditAnywhere, Category = "Mass|LOD", meta = (ClampMin = "0.0", UIMin = "0.0"), config)
    float BufferHysteresisOnDistancePercentage = 10.0f; //LOD切换边界距离迟滞百分比   
    UPROPERTY(EditAnywhere, Category = "Mass|LOD", config)
    int32 LODMaxCount[EMassLOD::Max] = {50, 100, 500, MAX_int32};/** 各级LOD最大数量 */    
    UPROPERTY(EditAnywhere, Category = "Mass|LOD", meta = (ClampMin = "0.0", UIMin = "0.0"), config)
    float DistanceToFrustum = 0.0f;/** 远离视锥多远可以认为可见 */
    /** Once visible how much further than DistanceToFrustum does the entities need to be before being cull again */
    UPROPERTY(EditAnywhere, Category = "Mass|LOD", meta = (ClampMin = "0.0", UIMin = "0.0"), config)
    float DistanceToFrustumHysteresis = 0.0f;
    UPROPERTY(EditAnywhere, Category = "Mass|LOD", meta = (BaseStruct = "/Script/MassEntity.MassTag"))
    TObjectPtr<UScriptStruct> FilterTag = nullptr;/** 筛选tag */
};

SimulationLOD控制参数

USTRUCT()
struct MASSLOD_API FMassSimulationLODParameters : public FMassSharedFragment
{
    GENERATED_BODY()
    FMassSimulationLODParameters();
    
    UPROPERTY(EditAnywhere, Category = "LOD", config)
    float LODDistance[EMassLOD::Max];    /** 各级LOD距离边界 */
    UPROPERTY(EditAnywhere, Category = "LOD", meta = (ClampMin = "0.0", UIMin = "0.0"), config)
    float BufferHysteresisOnDistancePercentage = 10.0f; /** LOD切换边界距离迟滞百分比   */   
    UPROPERTY(EditAnywhere, Category = "LOD", config)
    int32 LODMaxCount[EMassLOD::Max]; /** 各级LOD最大数量 */       
    UPROPERTY(EditAnywhere, Category = "LOD", config)
    bool bSetLODTags = false; /** 是否设置Tag */
};

各级LOD所对应的LOD Tag,根据FMassSimulationLODFragment的LOD更新对应Tag。

USTRUCT()
struct MASSLOD_API FMassHighLODTag : public FMassTag
{
    GENERATED_BODY()
};
USTRUCT()
struct MASSLOD_API FMassMediumLODTag : public FMassTag
{
    GENERATED_BODY()
};
USTRUCT()
struct MASSLOD_API FMassLowLODTag : public FMassTag
{
    GENERATED_BODY()
};
USTRUCT()
struct MASSLOD_API FMassOffLODTag : public FMassTag
{
    GENERATED_BODY()
};

具体更新LOD Tag代码如下:

void UMassSimulationLODProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context)
{
    //....省略部分代码
    {
        TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("SetLODTags"))
        check(World);
        EntityQuerySetLODTag.ForEachEntityChunk(EntityManager, Context, [](FMassExecutionContext& Context)
        {
            TConstArrayView<FMassSimulationLODFragment> SimulationLODFragments = Context.GetMutableFragmentView<FMassSimulationLODFragment>();
            const int32 NumEntities = Context.GetNumEntities();
            for (int32 Index = 0; Index < NumEntities; ++Index)
            {
                const FMassSimulationLODFragment& EntityLOD = SimulationLODFragments[Index];
                if (EntityLOD.PrevLOD != EntityLOD.LOD)
                {
                    const FMassEntityHandle Entity = Context.GetEntity(Index);
                    UE::MassLOD::PushSwapTagsCommand(Context.Defer(), Entity, EntityLOD.PrevLOD, EntityLOD.LOD);
                }
            }
        });
    }
    //....省略部分代码
}

LODCollector阶段

UMassLODCollectorProcessor

UMassLODCollectorProcessor借助TMassLODCollector计算Entity距离viewer的最近距离以及距离viewer视锥的最近距离

USTRUCT()
struct MASSLOD_API FMassViewerInfoFragment : public FMassFragment
{
    GENERATED_BODY()
    
    float ClosestViewerDistanceSq = 0.0f;// 距离viewer的最近距离平方
    float ClosestDistanceToFrustum = 0.0f;// 距离viewer视锥的最近距离
};

template <bool bLocalViewersOnly>
void UMassLODCollectorProcessor::CollectLODForChunk(FMassExecutionContext& Context)
{
    TConstArrayView<FTransformFragment> LocationList = Context.GetFragmentView<FTransformFragment>();
    TArrayView<FMassViewerInfoFragment> ViewerInfoList = Context.GetMutableFragmentView<FMassViewerInfoFragment>();
    Collector.CollectLODInfo<FTransformFragment, FMassViewerInfoFragment, bLocalViewersOnly, true/*bCollectDistanceToViewer*/>(Context, LocationList, ViewerInfoList);
}

void UMassLODCollectorProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context)
{
    const UMassLODSubsystem& LODSubsystem = Context.GetSubsystemChecked<UMassLODSubsystem>();
    const TArray<FViewerInfo>& Viewers = LODSubsystem.GetViewers();
    Collector.PrepareExecution(Viewers);
    UWorld* World = EntityManager.GetWorld();
    check(World);
    if (World->IsNetMode(NM_DedicatedServer))
    {
        ExecuteInternal<false/*bLocalViewersOnly*/>(EntityManager, Context);
    }
    else
    {
        ExecuteInternal<true/*bLocalViewersOnly*/>(EntityManager, Context);
    }
}

TMassLODCollector

PrepareExecution缓存所有Viewer最新的信息(坐标、朝向、视锥)。视锥不包括最近最远平面。

template <typename FLODLogic>
void TMassLODCollector<FLODLogic>::PrepareExecution(TConstArrayView<FViewerInfo> ViewersInfo)
{
    CacheViewerInformation(ViewersInfo);
}

void FMassLODBaseLogic::CacheViewerInformation(TConstArrayView<FViewerInfo> ViewerInfos)
{
    if(Viewers.Num() < ViewerInfos.Num())
    {
        Viewers.AddDefaulted(ViewerInfos.Num() - Viewers.Num());
    }
    // Cache viewer info
    for (int ViewerIdx = 0; ViewerIdx < Viewers.Num(); ++ViewerIdx)
    {
        const FViewerInfo& Viewer =  ViewerInfos.IsValidIndex(ViewerIdx) ? ViewerInfos[ViewerIdx] : FViewerInfo();
        const FMassViewerHandle ViewerHandle =  Viewer.bEnabled ? Viewer.Handle : FMassViewerHandle();
        // Check if it is the same client as before
        FViewerLODInfo& ViewerLOD = Viewers[ViewerIdx];
        ViewerLOD.bClearData = Viewers[ViewerIdx].Handle != ViewerHandle;
        ViewerLOD.Handle = ViewerHand
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值