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,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)
|
||||
};
|
||||
Reference in New Issue
Block a user