Initial commit: Vertical Partition Streaming plugin (UE 5.7)
Bounded vertical route partition / streaming for handcrafted spiral climb maps (Only Up / Only Climb). Not World Partition, not Level Instances as core arch. - Z-cell segmentation + optional XY subcells, editor builder + commandlet - NonDestructive visibility backend + streaming-level backend - Offline HLOD proxies: MergedMesh / SimplifiedProxy (ProxyLOD) - Universal Level Instance + Packed Level Actor + HISM support (flatten+merge) - Vertical-aware runtime streamer (full/preload/HLOD/unload, hysteresis, velocity prediction, fall-risk), WP-style debug viz + vp.* console - Safe-by-default: only static decor streams; sky/lights/gameplay stay persistent Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -0,0 +1,31 @@
|
||||
// Copyright IHY. Vertical Partition Streaming plugin.
|
||||
//
|
||||
// World-Partition-style debug: coloured cell boxes, ids, actor counts, problem
|
||||
// actor highlights, and an on-screen runtime overlay. Driven by the vp.Debug /
|
||||
// vp.DebugDraw / vp.DebugOverlay console variables (and the volume's debug flags).
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "VerticalPartitionDebugComponent.generated.h"
|
||||
|
||||
class UVerticalPartitionRuntimeSubsystem;
|
||||
|
||||
UCLASS(ClassGroup=(VerticalPartition), meta=(BlueprintSpawnableComponent))
|
||||
class VERTICALPARTITION_API UVerticalPartitionDebugComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UVerticalPartitionDebugComponent();
|
||||
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
/** 3D coloured cell boxes + ids + counts (the spec's DrawDebugCells). */
|
||||
void DrawDebugCells(UVerticalPartitionRuntimeSubsystem* Sub) const;
|
||||
/** On-screen text block of FVerticalRuntimeStats. */
|
||||
void DrawOverlay(UVerticalPartitionRuntimeSubsystem* Sub) const;
|
||||
|
||||
private:
|
||||
UVerticalPartitionRuntimeSubsystem* GetSub() const;
|
||||
};
|
||||
@ -0,0 +1,50 @@
|
||||
// Copyright IHY. Vertical Partition Streaming plugin.
|
||||
//
|
||||
// The build artifact: a data asset describing every generated cell plus a snapshot
|
||||
// of the settings and volume bounds the build was run with. The runtime streamer
|
||||
// consumes this; the editor builder produces it.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "VerticalPartitionTypes.h"
|
||||
#include "VerticalPartitionDescriptor.generated.h"
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class VERTICALPARTITION_API UVerticalPartitionDescriptor : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Map this descriptor was built from (for rebuild / out-of-date checks). */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FSoftObjectPath SourceLevel;
|
||||
|
||||
/** Settings snapshot used at build time (runtime ranges may be overridden live). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="VerticalPartition")
|
||||
FVerticalPartitionSettings Settings;
|
||||
|
||||
/** World bounds of the partition volume at build time. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FBox VolumeBounds = FBox(ForceInit);
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
EVerticalConversionMode BuiltMode = EVerticalConversionMode::NonDestructive;
|
||||
|
||||
/** Content hash of the source actors at build time (for incremental rebuild). */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int64 SourceContentHash = 0;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
TArray<FVerticalCellDescriptor> Cells;
|
||||
|
||||
/** Last build report (kept for the UI / Dump Report). */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FVerticalPartitionBuildReport LastReport;
|
||||
|
||||
/** Find a cell descriptor by id. Returns nullptr if absent. */
|
||||
const FVerticalCellDescriptor* FindCell(const FVerticalCellId& Id) const;
|
||||
int32 FindCellIndex(const FVerticalCellId& Id) const;
|
||||
|
||||
bool HasFullPackages() const;
|
||||
};
|
||||
6
Source/VerticalPartition/Public/VerticalPartitionLog.h
Normal file
6
Source/VerticalPartition/Public/VerticalPartitionLog.h
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright IHY. Vertical Partition Streaming plugin.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
VERTICALPARTITION_API DECLARE_LOG_CATEGORY_EXTERN(LogVerticalPartition, Log, All);
|
||||
62
Source/VerticalPartition/Public/VerticalPartitionManager.h
Normal file
62
Source/VerticalPartition/Public/VerticalPartitionManager.h
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright IHY. Vertical Partition Streaming plugin.
|
||||
//
|
||||
// In-world coordinator. One per streamed level (auto-spawned by the volume at
|
||||
// BeginPlay, or placed manually). Determines the tracked player's current cell,
|
||||
// drives the runtime subsystem at the configured interval, and hosts the debug
|
||||
// component. Keep this lightweight: the heavy lifting is in the subsystem.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "VerticalPartitionTypes.h"
|
||||
#include "VerticalPartitionManager.generated.h"
|
||||
|
||||
class UVerticalPartitionDescriptor;
|
||||
class UVerticalPartitionDebugComponent;
|
||||
class AVerticalPartitionVolume;
|
||||
|
||||
UCLASS()
|
||||
class VERTICALPARTITION_API AVerticalPartitionManager : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AVerticalPartitionManager();
|
||||
|
||||
/** Descriptor to stream. If unset, the manager searches the level for a volume. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="VerticalPartition")
|
||||
TSoftObjectPtr<UVerticalPartitionDescriptor> Descriptor;
|
||||
|
||||
/** Optional explicit volume (otherwise the first one found is used). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="VerticalPartition")
|
||||
TObjectPtr<AVerticalPartitionVolume> Volume;
|
||||
|
||||
/** Pawn whose position drives streaming. Defaults to local player pawn each tick. */
|
||||
UPROPERTY(BlueprintReadWrite, Category="VerticalPartition")
|
||||
TWeakObjectPtr<APawn> TrackedPawn;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, Category="VerticalPartition")
|
||||
TObjectPtr<UVerticalPartitionDebugComponent> DebugComponent;
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
|
||||
/** Snap streaming to the tracked pawn immediately (checkpoint / teleport). */
|
||||
UFUNCTION(BlueprintCallable, Category="VerticalPartition")
|
||||
void RequestSnap() { bSnapNextUpdate = true; }
|
||||
|
||||
/** Teleport the tracked pawn to the centre of a Z cell (debug). */
|
||||
UFUNCTION(BlueprintCallable, Category="VerticalPartition")
|
||||
void TeleportToCell(int32 Z);
|
||||
|
||||
private:
|
||||
float UpdateAccumulator = 0.f;
|
||||
bool bSnapNextUpdate = true; // snap on the first update
|
||||
bool bHavePrevPos = false;
|
||||
FVector PrevTrackedPos = FVector::ZeroVector;
|
||||
FVector SmoothedVelocity = FVector::ZeroVector;
|
||||
|
||||
APawn* ResolveTrackedPawn();
|
||||
bool BuildContext(struct FVerticalStreamingContext& OutCtx, float DeltaSeconds, bool& bOutTeleport);
|
||||
};
|
||||
@ -0,0 +1,119 @@
|
||||
// Copyright IHY. Vertical Partition Streaming plugin.
|
||||
//
|
||||
// The runtime streaming engine. World-scoped, holds the per-cell state machine,
|
||||
// the async load queue, the proxy actors and the live statistics. Driven each
|
||||
// update tick by AVerticalPartitionManager (subsystems are not ticked directly).
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Subsystems/WorldSubsystem.h"
|
||||
#include "VerticalPartitionTypes.h"
|
||||
#include "VerticalPartitionStatics.h"
|
||||
#include "VerticalPartitionRuntimeSubsystem.generated.h"
|
||||
|
||||
class UVerticalPartitionDescriptor;
|
||||
class ULevelStreaming;
|
||||
class AStaticMeshActor;
|
||||
|
||||
/** Per-cell live runtime record. Plain struct (C++-only, same-module access). */
|
||||
struct FVerticalCellRuntime
|
||||
{
|
||||
FVerticalCellId Id;
|
||||
int32 DescriptorIndex = INDEX_NONE;
|
||||
FBox Bounds = FBox(ForceInit);
|
||||
|
||||
EVerticalCellState State = EVerticalCellState::Unloaded;
|
||||
EVerticalCellDesiredState Desired = EVerticalCellDesiredState::Unloaded;
|
||||
|
||||
/** Backend B (DestructiveCommit): the streaming sub-level for the full actors. */
|
||||
TWeakObjectPtr<ULevelStreaming> StreamingLevel;
|
||||
/** Backend A (NonDestructive): the original persistent-level actors of this cell. */
|
||||
TArray<TWeakObjectPtr<AActor>> ResolvedActors;
|
||||
/** Shown while the cell is HLOD. */
|
||||
TWeakObjectPtr<AStaticMeshActor> ProxyActor;
|
||||
|
||||
bool bUsesStreamingBackend = false; // true => backend B
|
||||
bool bPreloadRequested = false;
|
||||
bool bProxyMissingWarned = false;
|
||||
|
||||
/** Debug lock: when set, Desired is pinned to ForcedState. */
|
||||
bool bForced = false;
|
||||
EVerticalCellDesiredState ForcedState = EVerticalCellDesiredState::Full;
|
||||
|
||||
float EstimatedMemoryMB = 0.f;
|
||||
double LastChangeTime = 0.0;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class VERTICALPARTITION_API UVerticalPartitionRuntimeSubsystem : public UWorldSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
static UVerticalPartitionRuntimeSubsystem* Get(const UObject* WorldContextObject);
|
||||
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
// ---- lifecycle (called by the manager) ----
|
||||
void RegisterDescriptor(UVerticalPartitionDescriptor* InDescriptor, const FBox& InVolumeBounds);
|
||||
void UnregisterDescriptor();
|
||||
bool IsReady() const { return Descriptor != nullptr; }
|
||||
|
||||
/** Main entry point. Recomputes desired states and steps the machine within the
|
||||
* per-update async budget. bSnap forces immediate convergence (teleport/checkpoint). */
|
||||
void UpdateStreaming(const FVerticalStreamingContext& Ctx, float DeltaSeconds, bool bSnap = false);
|
||||
|
||||
// ---- live range overrides (console) ----
|
||||
FVerticalPartitionSettings& GetMutableSettings() { return EffectiveSettings; }
|
||||
const FVerticalPartitionSettings& GetSettings() const { return EffectiveSettings; }
|
||||
void SetFullRange(int32 Below, int32 Above);
|
||||
void SetHLODRange(int32 Below, int32 Above);
|
||||
void SetCellHeight(float NewHeight);
|
||||
|
||||
// ---- debug forcing ----
|
||||
void ForceCellFull(int32 Z);
|
||||
void ForceCellHLOD(int32 Z);
|
||||
void UnforceCell(int32 Z);
|
||||
void UnforceAll();
|
||||
void ShowOnlyCell(int32 Z); // force one Full, everything else Unloaded
|
||||
|
||||
// ---- introspection ----
|
||||
const FVerticalRuntimeStats& GetStats() const { return Stats; }
|
||||
const TMap<FVerticalCellId, FVerticalCellRuntime>& GetCellRuntimes() const { return Cells; }
|
||||
const FBox& GetVolumeBounds() const { return VolumeBounds; }
|
||||
UVerticalPartitionDescriptor* GetDescriptor() const { return Descriptor; }
|
||||
int32 GetPlayerCellZ() const { return Stats.PlayerCellZ; }
|
||||
|
||||
// ---- reporting (console) ----
|
||||
void DumpRuntimeStats() const;
|
||||
void DumpCells() const;
|
||||
void DumpWarnings() const;
|
||||
|
||||
private:
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UVerticalPartitionDescriptor> Descriptor = nullptr;
|
||||
|
||||
FVerticalPartitionSettings EffectiveSettings;
|
||||
FBox VolumeBounds = FBox(ForceInit);
|
||||
|
||||
TMap<FVerticalCellId, FVerticalCellRuntime> Cells;
|
||||
FVerticalRuntimeStats Stats;
|
||||
|
||||
bool bShowOnlyMode = false;
|
||||
int32 ShowOnlyZ = 0;
|
||||
|
||||
// ---- state-machine helpers (the spec's key runtime functions) ----
|
||||
void ApplyDesiredState(FVerticalCellRuntime& Cell, bool bSnap, int32& LoadBudget, int32& UnloadBudget);
|
||||
void LoadCellFullAsync(FVerticalCellRuntime& Cell, bool bVisible);
|
||||
void ShowCellHLOD(FVerticalCellRuntime& Cell);
|
||||
void UnloadCell(FVerticalCellRuntime& Cell);
|
||||
void PollStreaming(FVerticalCellRuntime& Cell);
|
||||
|
||||
void SetActorGroupActive(FVerticalCellRuntime& Cell, bool bVisible, bool bCollision, bool bTick);
|
||||
AStaticMeshActor* EnsureProxy(FVerticalCellRuntime& Cell);
|
||||
void HideProxy(FVerticalCellRuntime& Cell);
|
||||
ULevelStreaming* EnsureStreamingLevel(FVerticalCellRuntime& Cell);
|
||||
|
||||
void RebuildStatsFromCells();
|
||||
FVerticalCellRuntime* FindCellByZ(int32 Z); // first cell at this Z (XY=0 path)
|
||||
};
|
||||
36
Source/VerticalPartition/Public/VerticalPartitionSettings.h
Normal file
36
Source/VerticalPartition/Public/VerticalPartitionSettings.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright IHY. Vertical Partition Streaming plugin.
|
||||
//
|
||||
// Project-level defaults + global debug toggles. Appears under
|
||||
// Project Settings -> Plugins -> Vertical Partition. Per-volume overrides live on
|
||||
// AVerticalPartitionVolume; this provides the defaults a freshly placed volume gets.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DeveloperSettings.h"
|
||||
#include "VerticalPartitionTypes.h"
|
||||
#include "VerticalPartitionSettings.generated.h"
|
||||
|
||||
UCLASS(Config=Game, DefaultConfig, meta=(DisplayName="Vertical Partition"))
|
||||
class VERTICALPARTITION_API UVerticalPartitionDeveloperSettings : public UDeveloperSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UVerticalPartitionDeveloperSettings();
|
||||
|
||||
virtual FName GetCategoryName() const override { return FName(TEXT("Plugins")); }
|
||||
|
||||
/** Defaults applied to a newly placed AVerticalPartitionVolume. */
|
||||
UPROPERTY(Config, EditAnywhere, Category="Defaults")
|
||||
FVerticalPartitionSettings DefaultSettings;
|
||||
|
||||
/** Master debug switch mirrored by the vp.Debug console variable. */
|
||||
UPROPERTY(Config, EditAnywhere, Category="Debug")
|
||||
bool bDebugEnabledByDefault = false;
|
||||
|
||||
/** Frame time (ms) above which a streaming update is logged as a hitch. */
|
||||
UPROPERTY(Config, EditAnywhere, Category="Debug", meta=(ClampMin="1.0", Units="ms"))
|
||||
float HitchThresholdMS = 8.0f;
|
||||
|
||||
static const UVerticalPartitionDeveloperSettings* Get();
|
||||
};
|
||||
71
Source/VerticalPartition/Public/VerticalPartitionStatics.h
Normal file
71
Source/VerticalPartition/Public/VerticalPartitionStatics.h
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright IHY. Vertical Partition Streaming plugin.
|
||||
//
|
||||
// Pure cell math shared by the editor builder and the runtime streamer. No state.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "VerticalPartitionTypes.h"
|
||||
#include "VerticalPartitionStatics.generated.h"
|
||||
|
||||
/** Context the desired-state policy needs beyond the raw cell index. */
|
||||
struct FVerticalStreamingContext
|
||||
{
|
||||
int32 PlayerCellZ = 0;
|
||||
int32 PlayerCellX = 0;
|
||||
int32 PlayerCellY = 0;
|
||||
float PlayerVelocityZ = 0.f; // cm/s, +up
|
||||
float PlayerWorldZ = 0.f; // cm, exact (for hysteresis at boundaries)
|
||||
float CameraPitchDeg = 0.f; // +looking up
|
||||
bool bUseXY = false;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class VERTICALPARTITION_API UVerticalPartitionStatics : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Z index of a world Z (cm) relative to the volume floor. */
|
||||
UFUNCTION(BlueprintPure, Category="VerticalPartition")
|
||||
static int32 GetCellZFromWorldZ(float WorldZ, float VolumeMinZ, float CellHeight);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category="VerticalPartition")
|
||||
static int32 GetCellXFromWorldX(float WorldX, float VolumeMinX, float XYCellSize);
|
||||
UFUNCTION(BlueprintPure, Category="VerticalPartition")
|
||||
static int32 GetCellYFromWorldY(float WorldY, float VolumeMinY, float XYCellSize);
|
||||
|
||||
/** Cell id for a single point (actor origin). */
|
||||
static FVerticalCellId GetCellIdFromPoint(const FVector& Point, const FBox& VolumeBounds, const FVerticalPartitionSettings& S);
|
||||
|
||||
/** Cell id for a bounds box. Uses the dominant-overlap cell (center of the
|
||||
* clamped intersection) so a box straddling a boundary lands deterministically.
|
||||
* bOutCrossesMultiple reports whether the box spans more than one cell. */
|
||||
static FVerticalCellId GetCellIdFromBounds(const FBox& ActorBounds, const FBox& VolumeBounds, const FVerticalPartitionSettings& S, bool& bOutCrossesMultiple);
|
||||
|
||||
/** World-space bounds of a cell, clamped to the volume when requested. */
|
||||
static FBox GetCellBounds(const FVerticalCellId& Cell, const FBox& VolumeBounds, const FVerticalPartitionSettings& S);
|
||||
|
||||
/** Number of Z cells the volume spans (>=1). */
|
||||
static int32 GetNumZCells(const FBox& VolumeBounds, const FVerticalPartitionSettings& S);
|
||||
|
||||
/** THE policy. Maps a cell relative to the player to its desired representation,
|
||||
* honouring full/preload/HLOD/unload bands, hysteresis, velocity prediction,
|
||||
* fall-risk-below, camera direction and the above-player priority bias.
|
||||
* CurrentState is the cell's live state and is what makes hysteresis sticky. */
|
||||
static EVerticalCellDesiredState GetDesiredStateForCell(
|
||||
const FVerticalCellId& Cell,
|
||||
const FVerticalStreamingContext& Ctx,
|
||||
const FVerticalPartitionSettings& S,
|
||||
EVerticalCellState CurrentState = EVerticalCellState::Unloaded);
|
||||
|
||||
/** True when a cell sits in the preload band (load full data, keep it hidden). */
|
||||
static bool ShouldPreloadFull(const FVerticalCellId& Cell, const FVerticalStreamingContext& Ctx, const FVerticalPartitionSettings& S);
|
||||
|
||||
/** Human-readable colour for a runtime state (debug draw / overlay). */
|
||||
UFUNCTION(BlueprintPure, Category="VerticalPartition")
|
||||
static FLinearColor GetStateColor(EVerticalCellState State);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category="VerticalPartition")
|
||||
static FString WarningTypeToString(EVerticalWarningType Type);
|
||||
};
|
||||
486
Source/VerticalPartition/Public/VerticalPartitionTypes.h
Normal file
486
Source/VerticalPartition/Public/VerticalPartitionTypes.h
Normal file
@ -0,0 +1,486 @@
|
||||
// Copyright IHY. Vertical Partition Streaming plugin.
|
||||
//
|
||||
// Shared data contract for the whole plugin: cell identity, states, the tunable
|
||||
// settings block, descriptors, and build/runtime report structs.
|
||||
//
|
||||
// This is a BOUNDED VERTICAL ROUTE partition system for a hand-crafted spiral
|
||||
// climbing map. It is NOT an infinite open-world (World Partition) system and it
|
||||
// does NOT use Level Instances as its core architecture.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "VerticalPartitionTypes.generated.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cell identity
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Integer grid coordinate of a cell. Z is the primary (vertical) axis; X/Y are
|
||||
* only used when bUseXYSubCells is enabled. */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FVerticalCellId
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="VerticalPartition")
|
||||
int32 X = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="VerticalPartition")
|
||||
int32 Y = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="VerticalPartition")
|
||||
int32 Z = 0;
|
||||
|
||||
FVerticalCellId() = default;
|
||||
FVerticalCellId(int32 InX, int32 InY, int32 InZ) : X(InX), Y(InY), Z(InZ) {}
|
||||
|
||||
bool operator==(const FVerticalCellId& Other) const
|
||||
{
|
||||
return X == Other.X && Y == Other.Y && Z == Other.Z;
|
||||
}
|
||||
bool operator!=(const FVerticalCellId& Other) const { return !(*this == Other); }
|
||||
|
||||
FString ToString() const { return FString::Printf(TEXT("X%d_Y%d_Z%d"), X, Y, Z); }
|
||||
|
||||
/** Stable, sortable label used for generated package names ("Cell_Z0010" / "Cell_X0_Y0_Z0010"). */
|
||||
FString ToLabel(bool bWithXY) const
|
||||
{
|
||||
return bWithXY
|
||||
? FString::Printf(TEXT("Cell_X%d_Y%d_Z%04d"), X, Y, Z)
|
||||
: FString::Printf(TEXT("Cell_Z%04d"), Z);
|
||||
}
|
||||
|
||||
friend uint32 GetTypeHash(const FVerticalCellId& C)
|
||||
{
|
||||
return HashCombine(HashCombine(::GetTypeHash(C.X), ::GetTypeHash(C.Y)), ::GetTypeHash(C.Z));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// State machine enums
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Live runtime state of a cell inside the streaming state machine. */
|
||||
UENUM(BlueprintType)
|
||||
enum class EVerticalCellState : uint8
|
||||
{
|
||||
Unloaded,
|
||||
LoadingHLOD,
|
||||
HLOD,
|
||||
PreloadingFull,
|
||||
LoadingFull,
|
||||
Full,
|
||||
Unloading,
|
||||
Error
|
||||
};
|
||||
|
||||
/** The representation the streamer *wants* a cell to be in this update. */
|
||||
UENUM(BlueprintType)
|
||||
enum class EVerticalCellDesiredState : uint8
|
||||
{
|
||||
Unloaded,
|
||||
HLOD,
|
||||
Full
|
||||
};
|
||||
|
||||
/** How Build/Convert mutates the source level. */
|
||||
UENUM(BlueprintType)
|
||||
enum class EVerticalConversionMode : uint8
|
||||
{
|
||||
/** Source level untouched. Cells stream by toggling the existing in-level actors
|
||||
* (visibility/collision/tick) + showing proxies. Safe, no memory savings. */
|
||||
NonDestructive,
|
||||
/** Actors are baked out into per-cell streaming sub-level packages and removed
|
||||
* from the persistent level. True memory streaming. A backup is taken first. */
|
||||
DestructiveCommit
|
||||
};
|
||||
|
||||
/** What to do with an actor that does not fit cleanly into a single cell. */
|
||||
UENUM(BlueprintType)
|
||||
enum class ELargeActorPolicy : uint8
|
||||
{
|
||||
/** Leave it in the persistent (always-loaded) level. */
|
||||
KeepInPersistentLevel,
|
||||
/** Assign it to the cell that owns the largest fraction of its bounds. */
|
||||
AssignToDominantCell,
|
||||
/** Keep it only as part of HLOD proxies, never as a full streamed actor. */
|
||||
DuplicateAsHLODOnly,
|
||||
/** Emit a warning — splitting a single actor across cells is not supported. */
|
||||
SplitNotSupportedWarning,
|
||||
/** Drop it from partitioning entirely. */
|
||||
ExcludeFromPartition
|
||||
};
|
||||
|
||||
/** How a cell's HLOD proxy is baked from its static geometry. */
|
||||
UENUM(BlueprintType)
|
||||
enum class EVerticalHLODMethod : uint8
|
||||
{
|
||||
/** One merged static mesh, original materials preserved (exact silhouette, heavier).
|
||||
* HISM/ISM instances are flattened into the single mesh. */
|
||||
MergedMesh,
|
||||
/** Proxy LOD: simplified geometry + a single baked atlas material (cheapest, true HLOD).
|
||||
* Requires the ProxyLODPlugin to be enabled; falls back to MergedMesh otherwise. */
|
||||
SimplifiedProxy
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EVerticalIssueSeverity : uint8
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
};
|
||||
|
||||
/** Classifies an analysis/validation finding so UI can group and filter them. */
|
||||
UENUM(BlueprintType)
|
||||
enum class EVerticalWarningType : uint8
|
||||
{
|
||||
None,
|
||||
ActorCrossesMultipleCells,
|
||||
ActorTooLarge,
|
||||
ActorSimulatesPhysics,
|
||||
ActorHasBlueprintTick,
|
||||
ActorDynamicMobility,
|
||||
ActorHasExternalReferences,
|
||||
ActorAttachedAcrossCells,
|
||||
ActorDependsOnOtherCell,
|
||||
ActorNeedsManualReview,
|
||||
ActorHasAudioLightNav,
|
||||
ActorNotStreamSafe,
|
||||
ActorHasUnsavedChanges,
|
||||
CellTooManyActors,
|
||||
CellTooManyDrawCalls,
|
||||
HLODGenerationFailed,
|
||||
PackageSaveFailed,
|
||||
NoVolume,
|
||||
DescriptorOutOfDate
|
||||
};
|
||||
|
||||
/** Editor action dispatched from the volume's detail-panel buttons / console. */
|
||||
UENUM()
|
||||
enum class EVerticalPartitionAction : uint8
|
||||
{
|
||||
Analyze,
|
||||
Build,
|
||||
RebuildChangedCells,
|
||||
RebuildHLOD,
|
||||
Validate,
|
||||
PreviewChunks,
|
||||
ClearGenerated,
|
||||
Commit,
|
||||
RestoreBackup,
|
||||
DumpReport,
|
||||
SpawnHLODPreview,
|
||||
ClearPreview
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tunable settings (one flat, category-grouped block reused everywhere)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Everything tunable about segmentation, runtime streaming ranges, conversion
|
||||
* and debug. Embedded in AVerticalPartitionVolume (per-volume) and snapshotted
|
||||
* into UVerticalPartitionDescriptor at build time. Defaults are provided by
|
||||
* UVerticalPartitionDeveloperSettings (project settings). */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FVerticalPartitionSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// ===== Segmentation =====
|
||||
/** Height (cm) of one Z cell. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Segmentation", meta=(ClampMin="100.0", Units="cm"))
|
||||
float CellHeight = 5000.f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Segmentation")
|
||||
bool bUseXYSubCells = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Segmentation", meta=(EditCondition="bUseXYSubCells", ClampMin="100.0", Units="cm"))
|
||||
float XYCellSize = 10000.f;
|
||||
|
||||
/** Snap the generated grid to the volume bounds (no cells outside the volume). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Segmentation")
|
||||
bool bClampToVolumeBounds = true;
|
||||
|
||||
/** Only actors whose origin/bounds fall inside the volume are partitioned. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Segmentation")
|
||||
bool bIncludeOnlyActorsInsideVolume = true;
|
||||
|
||||
/** Assign by full actor bounds (true) or by actor origin point (false). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Segmentation")
|
||||
bool bUseActorBoundsInsteadOfOrigin = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Segmentation")
|
||||
ELargeActorPolicy LargeActorPolicy = ELargeActorPolicy::AssignToDominantCell;
|
||||
|
||||
/** An actor is "large" when its Z extent exceeds this many cell heights. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Segmentation", meta=(ClampMin="1.0"))
|
||||
float LargeActorCellSpan = 1.5f;
|
||||
|
||||
/** SAFETY (recommended ON): only stream pure STATIC DECOR. Actors with lights,
|
||||
* sky atmosphere / Ultra Dynamic Sky, sky-light, fog, post-process, audio,
|
||||
* particles, reflection captures, decals, physics, replication, movable mobility,
|
||||
* and Level Instances are kept in the persistent level and are NEVER hidden by
|
||||
* streaming. Turn OFF only if every actor inside the volume is safe to hide. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Segmentation")
|
||||
bool bStreamOnlyStaticDecor = true;
|
||||
|
||||
/** Treat Level Instances / Packed Level Actors as streamable units: their static
|
||||
* geometry (incl. HISM/ISM, flattened recursively) is baked into per-cell HLOD and
|
||||
* their static child actors are hidden/shown by streaming. Lights / gameplay inside
|
||||
* an LI stay persistent. Off => LIs are left fully persistent. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Segmentation")
|
||||
bool bStreamLevelInstances = true;
|
||||
|
||||
// ===== Runtime streaming ranges (in cells, measured from the player cell) =====
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Full", meta=(ClampMin="0"))
|
||||
int32 FullLoadCellsBelow = 1;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Full", meta=(ClampMin="0"))
|
||||
int32 FullLoadCellsAbove = 1;
|
||||
|
||||
/** Begin async-loading full data (kept resident, not yet shown) this many cells out. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Preload", meta=(ClampMin="0"))
|
||||
int32 PreloadCellsBelow = 2;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Preload", meta=(ClampMin="0"))
|
||||
int32 PreloadCellsAbove = 3;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|HLOD", meta=(ClampMin="0"))
|
||||
int32 HLODLoadCellsBelow = 4;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|HLOD", meta=(ClampMin="0"))
|
||||
int32 HLODLoadCellsAbove = 5;
|
||||
|
||||
/** Beyond this many cells the cell is fully unloaded. Should be >= HLOD range. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Unload", meta=(ClampMin="0"))
|
||||
int32 UnloadCellsBelow = 5;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Unload", meta=(ClampMin="0"))
|
||||
int32 UnloadCellsAbove = 6;
|
||||
|
||||
/** Z distance (cm) the player must move past a boundary before a downgrade fires. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Hysteresis", meta=(ClampMin="0.0", Units="cm"))
|
||||
float HysteresisDistanceZ = 500.f;
|
||||
|
||||
/** Extra cells kept at their current (higher) state to avoid thrash near edges. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Hysteresis", meta=(ClampMin="0"))
|
||||
int32 HysteresisCells = 1;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Budget", meta=(ClampMin="0.0", Units="s"))
|
||||
float UpdateInterval = 0.2f;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Budget", meta=(ClampMin="1"))
|
||||
int32 MaxAsyncLoadsPerFrame = 1;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Budget", meta=(ClampMin="1"))
|
||||
int32 MaxAsyncUnloadsPerFrame = 1;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Budget", meta=(ClampMin="0"))
|
||||
int32 MaxVisibleHLODCells = 24;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Budget", meta=(ClampMin="0"))
|
||||
int32 MaxFullCells = 8;
|
||||
|
||||
// ===== Priority / prediction =====
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Priority")
|
||||
bool bPrioritizeAbovePlayer = true;
|
||||
/** Keep cells below the player loaded longer (the player can always fall back down). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Priority")
|
||||
bool bKeepBelowPlayerLoadedOnFallRisk = true;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Priority")
|
||||
bool bUseCameraDirectionPriority = false;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Priority")
|
||||
bool bUsePlayerVelocityPrediction = true;
|
||||
/** How many seconds of velocity to extrapolate the player position for prediction. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Streaming|Priority", meta=(ClampMin="0.0", Units="s"))
|
||||
float VelocityPredictionSeconds = 0.75f;
|
||||
|
||||
// ===== Conversion =====
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Conversion")
|
||||
EVerticalConversionMode ConversionMode = EVerticalConversionMode::NonDestructive;
|
||||
|
||||
/** Content root for generated cell maps / proxy meshes / descriptor. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Conversion")
|
||||
FString GeneratedPackageRoot = TEXT("/Game/VPGenerated");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Conversion")
|
||||
bool bCreateBackup = true;
|
||||
|
||||
/** When set, only actors of these classes are considered for partitioning. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Conversion")
|
||||
TArray<TSoftClassPtr<AActor>> IncludeOnlyClasses;
|
||||
|
||||
/** Actors of these classes are always excluded from partitioning. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Conversion")
|
||||
TArray<TSoftClassPtr<AActor>> ExcludeClasses;
|
||||
|
||||
// ===== HLOD =====
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="HLOD")
|
||||
bool bGenerateHLODProxies = true;
|
||||
/** Only static-mesh actors below this draw-call-ish triangle budget are merged. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="HLOD", meta=(ClampMin="0"))
|
||||
int32 HLODMaxSourceActorsPerCell = 4096;
|
||||
|
||||
/** MergedMesh (default, exact) or SimplifiedProxy (cheaper, needs ProxyLODPlugin). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="HLOD")
|
||||
EVerticalHLODMethod HLODMethod = EVerticalHLODMethod::MergedMesh;
|
||||
|
||||
/** Target screen size (px) for SimplifiedProxy generation. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="HLOD", meta=(ClampMin="1", ClampMax="1200", EditCondition="HLODMethod==EVerticalHLODMethod::SimplifiedProxy"))
|
||||
int32 ProxyScreenSize = 300;
|
||||
|
||||
// ===== Debug =====
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Debug")
|
||||
bool bDrawVolumeBounds = true;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Debug")
|
||||
bool bDrawZSlices = true;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Debug")
|
||||
bool bDrawXYCells = false;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Debug")
|
||||
bool bDrawCellIds = true;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Debug")
|
||||
bool bDrawActorCounts = true;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Debug")
|
||||
bool bDrawProblemActors = true;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Debug")
|
||||
bool bShowRuntimeOverlay = true;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Descriptor data (serialized in UVerticalPartitionDescriptor)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** One generated cell: bounds, the package that holds its full actors, its proxy
|
||||
* asset, and the source actors that fed it. */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FVerticalCellDescriptor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FVerticalCellId CellId;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FBox Bounds = FBox(ForceInit);
|
||||
|
||||
/** Streaming sub-level package for the full representation (DestructiveCommit only). */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FSoftObjectPath FullCellPackage;
|
||||
|
||||
/** Offline-generated proxy static mesh shown while the cell is in HLOD range. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FSoftObjectPath HLODProxyAsset;
|
||||
|
||||
/** Soft paths to the original actors assigned to this cell (NonDestructive backend). */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
TArray<FSoftObjectPath> SourceActors;
|
||||
|
||||
/** Actors in *other* cells that this cell hard-references (cross-cell dependencies). */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
TArray<FSoftObjectPath> Dependencies;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 ActorCount = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 StaticMeshCount = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 BlueprintActorCount = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 LightCount = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 DynamicActorCount = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
float EstimatedMemoryMB = 0.f;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
bool bHasWarnings = false;
|
||||
};
|
||||
|
||||
/** A single analysis / validation finding. */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FVerticalPartitionWarning
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
EVerticalWarningType Type = EVerticalWarningType::None;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
EVerticalIssueSeverity Severity = EVerticalIssueSeverity::Warning;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FString Message;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FSoftObjectPath Actor;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FVerticalCellId Cell;
|
||||
};
|
||||
|
||||
/** An error is a finding that blocks (or partially blocks) the build. Same shape. */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FVerticalPartitionError
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
EVerticalWarningType Type = EVerticalWarningType::None;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FString Message;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FSoftObjectPath Actor;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FVerticalCellId Cell;
|
||||
};
|
||||
|
||||
/** Result of Analyze / Build, surfaced in the editor UI and dumpable to a report. */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FVerticalPartitionBuildReport
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 TotalActorsScanned = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 ActorsInsideVolume = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 ActorsExcluded = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 GeneratedCells = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 EmptyCells = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 WarningCount = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 ErrorCount = 0;
|
||||
|
||||
/** The Z cell (and XY sub-cell) with the most actors, for the summary panel. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 LargestCellActorCount = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
FVerticalCellId LargestCell;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
TArray<FVerticalPartitionWarning> Warnings;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
TArray<FVerticalPartitionError> Errors;
|
||||
|
||||
void Reset() { *this = FVerticalPartitionBuildReport(); }
|
||||
};
|
||||
|
||||
/** Live runtime counters surfaced by the debug overlay / vp.DumpState. */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FVerticalRuntimeStats
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 FullCellsLoaded = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 HLODCellsVisible = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 CellsUnloaded = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 CellsLoading = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 ActiveAsyncLoads = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
float EstimatedMemoryMB = 0.f;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
float LastUpdateTimeMS = 0.f;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
float LastStreamingTimeMS = 0.f;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 HitchesOverThreshold = 0;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
int32 PlayerCellZ = 0;
|
||||
};
|
||||
89
Source/VerticalPartition/Public/VerticalPartitionVolume.h
Normal file
89
Source/VerticalPartition/Public/VerticalPartitionVolume.h
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright IHY. Vertical Partition Streaming plugin.
|
||||
//
|
||||
// The authoring volume. Drop one around the whole playable spiral, tune the
|
||||
// settings, and press Build. Bounds come from a UBoxComponent so they are trivial
|
||||
// to read at both editor and runtime (no BSP brush).
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "VerticalPartitionTypes.h"
|
||||
#include "VerticalPartitionVolume.generated.h"
|
||||
|
||||
class UBoxComponent;
|
||||
class UVerticalPartitionDescriptor;
|
||||
class AVerticalPartitionVolume;
|
||||
|
||||
/**
|
||||
* Decouples the runtime volume from the editor-only builder. The editor module
|
||||
* installs Handler in StartupModule; the volume's CallInEditor buttons and the
|
||||
* vp.* build console commands route through it. Lives in the runtime module so the
|
||||
* volume can reference it, but is only ever populated in editor builds.
|
||||
*/
|
||||
struct VERTICALPARTITION_API FVerticalPartitionEditorBridge
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
static TFunction<void(AVerticalPartitionVolume*, EVerticalPartitionAction)> Handler;
|
||||
static void Execute(AVerticalPartitionVolume* Volume, EVerticalPartitionAction Action)
|
||||
{
|
||||
if (Handler) { Handler(Volume, Action); }
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
UCLASS(HideCategories=(Collision, Physics, Navigation, HLOD, Input, Replication))
|
||||
class VERTICALPARTITION_API AVerticalPartitionVolume : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AVerticalPartitionVolume();
|
||||
|
||||
/** Box that defines the bounded playable region to partition. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="VerticalPartition")
|
||||
TObjectPtr<UBoxComponent> Box;
|
||||
|
||||
/** Per-volume settings (seeded from project defaults on placement). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="VerticalPartition", meta=(ShowOnlyInnerProperties))
|
||||
FVerticalPartitionSettings Settings;
|
||||
|
||||
/** Descriptor produced by Build; consumed by the runtime manager. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="VerticalPartition")
|
||||
TSoftObjectPtr<UVerticalPartitionDescriptor> Descriptor;
|
||||
|
||||
/** World-space bounds of the volume (box transform * extent). */
|
||||
UFUNCTION(BlueprintPure, Category="VerticalPartition")
|
||||
FBox GetVolumeBounds() const;
|
||||
|
||||
virtual void OnConstruction(const FTransform& Transform) override;
|
||||
|
||||
#if WITH_EDITOR
|
||||
// ---- Detail-panel buttons (mirror the editor tool window) ----
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|1 - Analyze")
|
||||
void AnalyzeCurrentLevel();
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|2 - Build")
|
||||
void BuildVerticalPartition();
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|2 - Build")
|
||||
void RebuildChangedCells();
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|2 - Build")
|
||||
void RebuildHLOD();
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|3 - Validate")
|
||||
void ValidateCells();
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|3 - Validate")
|
||||
void PreviewChunks();
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|3 - Validate")
|
||||
void SpawnHLODPreview();
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|3 - Validate")
|
||||
void ClearPreview();
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|4 - Commit")
|
||||
void CommitConversion();
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|4 - Commit")
|
||||
void RestoreFromBackup();
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|5 - Maintenance")
|
||||
void ClearGeneratedData();
|
||||
UFUNCTION(CallInEditor, Category="Vertical Partition|5 - Maintenance")
|
||||
void DumpReport();
|
||||
|
||||
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
#endif
|
||||
};
|
||||
Reference in New Issue
Block a user