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