Files
vertical-partition-system-p…/Source/VerticalPartition/Public/VerticalPartitionRuntimeSubsystem.h
Bonchellon 04ad6d3f25 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>
2026-06-30 17:42:41 +03:00

120 lines
4.4 KiB
C++

// 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)
};