SurfaceWorld and FloatingIslands plans generate at startup from the resolved seed, strate types and heights (hashed, cached). Imported JSON is an opt-in override (off by default); an invalid override shows an on-screen notice and falls back to legacy. BuildHost on staged owner assets: SurfaceWorld cross-system checks, islands at owner height (route, collision, anchors, BP edits), seed 137501, W1/W8/W16, Core-off legacy. XY-column cache verified (hashes kept); island LOD0 p95 91 ms vs 133 ms sprint deadline. Not exercised: season-seed path (no Season in staged settings), rendered notice pixels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
465 lines
28 KiB
C++
465 lines
28 KiB
C++
// VoxelSettings.h
|
||
// Settings du plugin VoxelForge.
|
||
//
|
||
// Data asset unique (assigné sur AVoxelWorld) qui regroupe tous les tuning
|
||
// du monde: seed, streaming, LOD, pool de strates, budgets de carving.
|
||
|
||
#pragma once
|
||
|
||
#include "CoreMinimal.h"
|
||
#include "Engine/DataAsset.h"
|
||
#include "VoxelStrateDefinition.h"
|
||
#include "VoxelSettings.generated.h"
|
||
|
||
// IWYU : pointeur seulement (VoxelMaterial). / Pointer-only use.
|
||
class UMaterialInterface;
|
||
class UVoxelSeasonAsset;
|
||
|
||
UCLASS(BlueprintType)
|
||
class UVoxelSettings : public UPrimaryDataAsset
|
||
{
|
||
GENERATED_BODY()
|
||
|
||
public:
|
||
|
||
/** Season seed/radius replace the mutable authored values only when Season is assigned. */
|
||
int32 GetEffectiveWorldSeed() const;
|
||
float GetEffectiveOriginSpineRadius() const;
|
||
float GetEffectiveWorldRadiusVoxels() const;
|
||
int32 GetEffectiveSeasonNumber() const;
|
||
|
||
/** Import a reviewed native SurfaceWorld plan; empty preserves the existing Core/legacy routes. */
|
||
VOXELFORGE_API bool SetWorldAssemblyManifestJson(const FString& InJson, FString& OutReport);
|
||
VOXELFORGE_API bool GetWorldAssemblyManifestJson(FString& OutCanonicalJson, FString& OutContentHash,
|
||
FString& OutError) const;
|
||
|
||
UFUNCTION(CallInEditor, Category = "Voxel|Strates")
|
||
void ImportWorldAssemblyManifestJson();
|
||
|
||
//=========================================================================
|
||
// STREAMING (distance de vue)
|
||
//=========================================================================
|
||
|
||
// En CHUNKS (CHUNK_SIZE=32). At 12.5 cm/voxel, each view-distance chunk covers 4 m.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming")
|
||
int32 ViewDistanceXY = 32;
|
||
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming")
|
||
int32 ViewDistanceUp = 10;
|
||
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming")
|
||
int32 ViewDistanceDown = 10;
|
||
|
||
// Nombre max de tâches de génération/mesh en parallèle.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming")
|
||
int32 MaxConcurrentTasks = 16;
|
||
|
||
// Optional hard cap for the generation worker budget. 0 keeps the safe automatic cap of
|
||
// logical cores minus two; a positive value makes that cap visible and authorable beside the
|
||
// task budget instead of hiding it in AVoxelWorld. The effective task count is still the
|
||
// minimum of this value and MaxConcurrentTasks.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming", meta = (ClampMin = "0"))
|
||
int32 MaxGenerationWorkerCores = 0;
|
||
|
||
// Keep this many task slots available for collision-critical, edited, and level-0 terrain while
|
||
// coarse horizon work is in flight. A positive reserve prevents a full far-field queue from
|
||
// delaying a new near tile because running tasks cannot be preempted. The runtime clamps the
|
||
// value below the effective task budget; 4 is the conservative default for the ship settings.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming", meta = (ClampMin = "0", ClampMax = "16"))
|
||
int32 NearTaskReserve = 4;
|
||
|
||
// Nombre max de meshes appliqués (upload GPU) par frame. Seuls les vrais applies
|
||
// comptent (chunks vides/périmés se vident gratuitement). À 32³ chaque apply est léger
|
||
// (~0.1 ms) — tunable en live sur l'asset : montez (8-16) si le remplissage traîne,
|
||
// baissez si l'apparition des chunks fait des à-coups.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming")
|
||
int32 MaxMeshAppliesPerFrame = 4;
|
||
|
||
// Wall-clock cap for the complete game-thread result drain, including component teardown and
|
||
// collision configuration. The count cap remains the second guard. A zero value disables this
|
||
// cap; the default keeps one burst of chunk application below a small frame-time budget without
|
||
// changing worker resolution or mesh quality.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming", meta = (ClampMin = "0.0"))
|
||
float MaxMeshApplyMilliseconds = 2.0f;
|
||
|
||
// Max tile teardowns (component destroy + content-actor Destroy) per frame. A fast traversal
|
||
// culls a whole shell of tiles at once; doing every destroy in one frame is a game-thread spike
|
||
// ("stuff torn down behind you"). This spreads it. The drain auto-scales up if the backlog grows
|
||
// (PendingUnload/4) so it never falls far behind — raise the floor if teardown lags at high speed.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming")
|
||
int32 MaxUnloadsPerFrame = 6;
|
||
|
||
// Independent level-0 collision radius around the locally controlled pawn. Render tiles outside
|
||
// this cube remain visible but do not queue Chaos cooks; registered streaming anchors keep their
|
||
// own authored collision boxes, and the collision gate can extend a landing-support column.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming", meta = (ClampMin = "1", ClampMax = "4"))
|
||
int32 PawnCollisionRadiusChunks = 2;
|
||
|
||
// Strate-aware VERTICAL streaming: don't load chunks into a strate you can't see.
|
||
// Seals + the inter-strate bedrock gap are light-tight (§8.7), so the strate above/below
|
||
// the one you're in is fully occluded — loading it just wastes scene primitives (the
|
||
// game-thread cost). When ON, the vertical view is clamped to the player's strate ±
|
||
// StrateViewMarginChunks. In the bedrock GAP itself the view is NOT clamped (you can see
|
||
// both sides through the descent passage there), so the next strate streams in ahead.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming")
|
||
bool bClampViewToStrate = true;
|
||
|
||
// Chunks of vertical look-ahead PAST the strate seal (each side) when bClampViewToStrate
|
||
// is on: keeps the descent tunnel / immediate transition loaded and acts as the trailing
|
||
// buffer of the strate you're leaving (it sheds naturally as you descend out of view).
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming", meta = (ClampMin = "0"))
|
||
int32 StrateViewMarginChunks = 6;
|
||
|
||
// STRATE CONTENT CUT (F17 separation) — a level-L tile is 2^L chunks TALL and can straddle a
|
||
// strate boundary: at coarse Steps the thin seal/gap solid between two strates' airs falls
|
||
// between lattice points (⇒ holes into the neighbour strate at far LOD) and one tile mixes
|
||
// both strates' materials. From this clip level UP, the mesher only meshes cells inside the
|
||
// PLAYER's strate Z-band (the other strates are sealed/enclosed ⇒ invisible from here anyway);
|
||
// loaded coarse tiles re-queue automatically when the band changes (strate transition).
|
||
// Default 1 = cut LOD1 and coarser. LOD0 is deliberately never content-cut because it owns pawn
|
||
// collision: cutting the passage, gap, or next-strate support tile would deadlock a walk at the
|
||
// current strate boundary. The runtime clamps legacy/hand-authored values below 1 to 1 as well.
|
||
// Raise only if a broader near-field view needs full coarse tiles. 9 = off.
|
||
// At ultra-coarse levels where one CELL is taller than the band itself, the tile is skipped
|
||
// entirely (see LoadTile) — cell-granular cutting there could only render garbage.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming", meta = (ClampMin = "1", ClampMax = "9"))
|
||
int32 StrateContentCutMinLevel = 1;
|
||
|
||
/**
|
||
* Return the effective content-cut level used by runtime streaming.
|
||
*
|
||
* Existing data assets can contain a serialized legacy value of 0 even though the property now
|
||
* documents 1 as the minimum. Keep the safety rule in code, at the consumer boundary: level 0
|
||
* tiles are the collision tiles and must never receive a strate content band.
|
||
*/
|
||
int32 GetEffectiveStrateContentCutMinLevel() const
|
||
{
|
||
return FMath::Clamp(FMath::Max(StrateContentCutMinLevel, 1), 1, 9);
|
||
}
|
||
|
||
//=========================================================================
|
||
// CLIPMAP (chunked-LOD streaming — supersedes the ViewDistance box above)
|
||
//=========================================================================
|
||
// Streaming loads concentric shells of tiles: level 0 = full-res chunks near the player,
|
||
// each coarser level doubles tile size (and reach). Total tile/draw/gen count stays ~flat
|
||
// regardless of how far you see — so this is the "see to the horizon cheaply" system.
|
||
// Total reach ≈ ClipRadius × CHUNK_SIZE × 2^MaxClipLevel voxels.
|
||
// Near full-res reach ≈ ClipRadius × CHUNK_SIZE voxels.
|
||
|
||
// Half-extent (in tiles, per axis) of EACH level's shell. Bigger = more tiles per level
|
||
// (more detail / overlap) but more components. 3 → a 7×7×7 box per level (minus the inner
|
||
// hole filled by the finer level).
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Clipmap", meta = (ClampMin = "1"))
|
||
int32 ClipRadius = 6;
|
||
|
||
// Coarsest LOD level. 0 = full-res only (no clipmap). Each level up doubles tile size &
|
||
// reach at ~constant cost — raise this to see much farther for cheap. (Level L step = 2^L.)
|
||
// Ship default: level 5, the measured horizon-distance balance at 1536 chunks.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Clipmap", meta = (ClampMin = "0", ClampMax = "8"))
|
||
int32 MaxClipLevel = 5;
|
||
|
||
// How many near levels mesh at FULL resolution (CHUNK_SIZE cells). Levels at or above this
|
||
// use the cheaper CoarseTileCells → MUCH faster gen for the far field (it's far, so the
|
||
// extra blockiness is hidden, and skirts cover the seams). 2 = levels 0,1 crisp; set to 1
|
||
// for the fastest gen (slightly harder LOD0→1 transition).
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Clipmap", meta = (ClampMin = "1"))
|
||
int32 FullResClipLevels = 2;
|
||
|
||
// Cells per axis for COARSE tiles (levels >= FullResClipLevels). Lower = cheaper gen, blockier
|
||
// far terrain. 16 = ~⅛ the gen samples of a full 32-cell tile; 8 = ~1/64. Tile EXTENT is
|
||
// unchanged (the shells still tile) — only the mesh resolution within a far tile drops.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Clipmap", meta = (ClampMin = "4", ClampMax = "32"))
|
||
int32 CoarseTileCells = 16;
|
||
|
||
// RENDER DISTANCE — custom horizontal reach, in CHUNKS (1 chunk = 4 m; 256 ≈ 1 km, 1536 ≈ 6 km).
|
||
// When > 0 and farther than the natural clipmap reach (ClipRadius × 2^MaxClipLevel chunks), the
|
||
// OUTERMOST shell keeps generating level-MaxClipLevel tiles outward until it covers this
|
||
// distance. Pick MaxClipLevel = the coarsest level that still renders strates correctly (one
|
||
// cell must fit inside a strate band — level ≥7 blanks via the too-coarse skip) and buy the
|
||
// remaining horizon here. Cost: the extra ring is all same-level tiles — tile/draw/gen count
|
||
// grows with (distance / 2^MaxClipLevel)², so each MaxClipLevel step down quadruples the ring.
|
||
// Ship default: 1536 chunks (about 6.1 km at 4 m/chunk). 0 = off (natural reach).
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Clipmap", meta = (ClampMin = "0"))
|
||
int32 RenderDistanceChunks = 1536;
|
||
|
||
// SKIRTS — seal the thin cracks where neighbouring clipmap shells (different resolutions) meet.
|
||
// A short wall is extruded into the solid from each surface edge on the tile's outer faces.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Clipmap")
|
||
bool bGenerateSkirts = true;
|
||
|
||
// Skirt depth in tile CELLS (× the tile's step). Four L0 cells preserve the former 0.5 m
|
||
// default depth at 12.5 cm per voxel. Raise if cracks still show; lower if skirts peek out.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Clipmap", meta = (ClampMin = "0.5", ClampMax = "8.0"))
|
||
float SkirtCells = 4.0f;
|
||
|
||
// T2.b — LOD-aware octave reduction. Octaves DROPPED from per-voxel volumetric noise
|
||
// (roughness, worms displacement, slab/maze/shaft/island detail) per Step doubling on
|
||
// coarse tiles: a Step=4 tile drops 2×this. Sub-cell octaves can't shape a coarse
|
||
// isosurface — they only cost CPU — so 1 shaves 30-50% off far-tile gen for a sub-cell
|
||
// isosurface shift (skirts already stitch bigger LOD seams). LOD0 is NEVER affected.
|
||
// 0 = off (every LOD samples full octaves — byte-identical to before this setting).
|
||
// Réduction d'octaves sur les tuiles lointaines ; 0 = désactivé, LOD0 jamais touché.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Clipmap", meta = (ClampMin = "0", ClampMax = "3"))
|
||
int32 LODOctaveDrop = 0;
|
||
|
||
//==========================================================================
|
||
// WORLD BOUNDS (bounded gigantic world in actor-space XY)
|
||
//==========================================================================
|
||
// Radius (voxels) of the bounded world in XY, measured from actor-space (0,0).
|
||
// 0 = unbounded (legacy behaviour, no edge seal).
|
||
//
|
||
// ⚠️ DEFAULTS TO 0 ON PURPOSE, and it is not the value the GDD wants.
|
||
// Existing DA_Settings / BP_VoxelSettings assets predate this property, so they do NOT serialize
|
||
// it — they silently inherit whatever C++ default is written here. A non-zero default would
|
||
// therefore wall in every existing world the next time the editor opens, with nothing in the
|
||
// asset to show why. The former 8192-voxel / 2.05 km target must be 16384 voxels at 12.5 cm.
|
||
// 8192 now means a 1.024 km radius.
|
||
// SET IT IN THE ASSET, deliberately, once.
|
||
//
|
||
// FR : 0 par défaut EXPRÈS — les assets existants n'ont pas cette propriété et héritent
|
||
// silencieusement de la valeur C++. La valeur de production visée (~8192) se règle dans l'asset.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|World Bounds", meta = (ClampMin = "0.0"))
|
||
float WorldRadiusVoxels = 0.0f;
|
||
|
||
// Solid ramp thickness at the radial world edge, in voxels.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|World Bounds", meta = (ClampMin = "0.0"))
|
||
float EdgeSealThickness = 128.0f;
|
||
|
||
//=========================================================================
|
||
// CONTENT — distance-based decoration grid (no LOD pop)
|
||
//=========================================================================
|
||
// Decorations are placed on a fixed WORLD XY cell grid (1 cell = 1 chunk footprint) and streamed
|
||
// by DISTANCE from the player, independent of which clipmap tile (LOD) currently meshes the ground
|
||
// under them. Each candidate column is ray-marched through the player's strate via the generator's
|
||
// density field and snapped to the real surface — so a given prop keeps the SAME world position at
|
||
// every LOD (no teleport/pop on tile swaps). Decorations exist only in the player's current strate.
|
||
|
||
// FAR-tier stream radius in cells (= chunks): how far FStrateDecoration entries set to EDecoStreamTier::Far
|
||
// (the default — trees, landmarks, rare props) stream out. Bigger = props visible farther + more
|
||
// spawn/march cost (but the far grid is COARSE — see DecorationFarSpacingVoxels — so far cost is cheap).
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Content", meta = (ClampMin = "1"))
|
||
int32 DecorationRadiusChunks = 12;
|
||
|
||
// NEAR-tier stream radius in cells (= chunks): how far EDecoStreamTier::Near entries (dense groundcover
|
||
// like grass) stream out. Keep this SHORT — near entries use the FINE grid (DecorationSpacingVoxels), so
|
||
// their cost is the steep one; bounding their radius keeps the far-region HISM build + memory small.
|
||
// (Repurposes the old vestigial DecorationActorRadiusChunks; same default.)
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Content", meta = (ClampMin = "1"))
|
||
int32 DecorationNearRadiusChunks = 6;
|
||
|
||
// Decoration cells are grouped into REGIONS of RxR cells, and ALL placements in a region share ONE
|
||
// HISM per mesh (instead of one HISM per cell per mesh). Regions load/unload as a unit, so clearing
|
||
// stays a plain DestroyComponent — no per-instance index remapping. This is the render-thread lever:
|
||
// component count (which InitViews walks every frame) drops by ~R^2. R=4 → ~16 regions in a radius-6
|
||
// disk vs 169 cells (~10x fewer components). Bigger R = fewer components but coarser pop-in + heavier
|
||
// one-shot cluster-tree build per region (apply is budgeted, so the build is amortised). MUST be >= 1.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Content", meta = (ClampMin = "1", ClampMax = "16"))
|
||
int32 DecorationRegionSizeCells = 8;
|
||
|
||
// NEAR-tier column spacing (in voxels) within a cell — the FINE grid. MUST divide CHUNK_SIZE (32):
|
||
// 4 → 8×8=64 columns/cell. Smaller = denser placement potential + more march cost. SpawnDensity rolls
|
||
// per column-crossing. Used by EDecoStreamTier::Near entries (and is the legacy single-grid spacing).
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Content", meta = (ClampMin = "1", ClampMax = "32"))
|
||
int32 DecorationSpacingVoxels = 8;
|
||
|
||
// FAR-tier column spacing (in voxels) within a cell — the COARSE grid. MUST divide CHUNK_SIZE (32):
|
||
// 16 → 2×2=4 columns/cell (16× fewer worker ray-marches than a spacing-4 grid). This is the lever that
|
||
// makes a RARE prop visible at every distance cheap: the far grid samples sparsely, so supporting a
|
||
// low-SpawnDensity landmark across the full radius costs a fraction of the fine grid. DEFAULTS to the
|
||
// fine value (4) so existing worlds are byte-identical until you raise it; bump to 8–16 for cheap far props.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Content", meta = (ClampMin = "1", ClampMax = "32"))
|
||
int32 DecorationFarSpacingVoxels = 8;
|
||
|
||
// COARSE vertical march step (in voxels) when searching a column for surface crossings. The crossing
|
||
// Z is then bisection-refined, so accuracy is independent of this — raise it (4-8) to cut the scan
|
||
// cost (the column is ray-marched on a WORKER thread, but a smaller step still means more samples).
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Content", meta = (ClampMin = "1", ClampMax = "8"))
|
||
int32 DecorationMarchStepVoxels = 4;
|
||
|
||
// Max surface crossings placed per column (caps cave columns that pierce many floors/ceilings;
|
||
// surface worlds have 1). Bounds worst-case march/spawn cost.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Content", meta = (ClampMin = "1", ClampMax = "16"))
|
||
int32 DecorationMaxCrossingsPerColumn = 4;
|
||
|
||
// Stop marching a column after this many voxels of CONTIGUOUS SOLID once it has already entered the
|
||
// open space — i.e. cap how far into bedrock BELOW the floor (or below a cavern) we keep scanning.
|
||
// The top cap/seal and the air above the ground are always marched first (this only trims the dead
|
||
// rock underneath), so surface worlds still get their ground and caves still find layered floors.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Content", meta = (ClampMin = "8"))
|
||
int32 DecorationColumnDepthVoxels = 320;
|
||
|
||
// Budget: how many completed decoration REGIONS to APPLY (game-thread HISM build + SpawnActor) per
|
||
// frame. A region is applied once ALL its cells have finished marching, in one batched AddInstances
|
||
// per mesh; this throttles that burst. The expensive ray-march runs async on workers. (Named "Cells"
|
||
// for asset back-compat — the apply unit is now a region of DecorationRegionSizeCells^2 cells.)
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming", meta = (ClampMin = "1"))
|
||
int32 MaxDecorationCellsPerFrame = 2;
|
||
|
||
// Max decoration ray-march tasks in flight at once. Keeps decoration marching from crowding the
|
||
// mesh-gen workers (which are the streaming bottleneck). 0 disables decorations entirely.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming", meta = (ClampMin = "0"))
|
||
int32 MaxConcurrentDecorationTasks = 4;
|
||
|
||
//=========================================================================
|
||
// LIGHTING — DENSITY VOLUME (mini-sun raymarched shadows)
|
||
//=========================================================================
|
||
// A player-centred CLIPMAP of the density field, uploaded to the GPU so the terrain
|
||
// material can RAYMARCH it toward the "mini-sun" orbs → from-the-orb, crisp, dynamic
|
||
// shadows under FORWARD rendering (Lumen/DF off the table). The volume is the load-bearing
|
||
// prerequisite: density is CPU-only (GetDensityAt), so we stream it onto the GPU here.
|
||
// Concentric levels: level 0 = full-res near the player (step 1), each level up doubles the
|
||
// sampling step & reach (fine near / coarse far — exactly what shadow rays want). Filled on
|
||
// WORKER threads (re-evaluating GetDensityAt → deterministic, carves auto-picked-up), with
|
||
// toroidal incremental refill on movement and localized refill on carve. See VoxelDensityVolume.
|
||
|
||
// Master switch. OFF = no volume built, no fill tasks, no GPU cost (terrain unlit by orbs).
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Lighting")
|
||
bool bEnableDensityVolume = false;
|
||
|
||
// Per-axis resolution of EACH clip level (cells). Memory per level ≈ Res³ bytes (R8).
|
||
// 256 → ~16 MiB/level; 512 → ~128 MiB/level. Doubling Res preserves metre reach at 12.5 cm.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Lighting", meta = (ClampMin = "32", ClampMax = "512"))
|
||
int32 DensityVolumeResolution = 256;
|
||
|
||
// Number of concentric clip levels. Level L samples every (1<<L) voxels and covers
|
||
// Res·(1<<L) voxels. At 12.5 cm, Res=512 → near 64 m (full-res) out to ~256 m (coarse).
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Lighting", meta = (ClampMin = "1", ClampMax = "5"))
|
||
int32 DensityVolumeLevels = 3;
|
||
|
||
// A fill box is split into Z-slabs of at most this many cells per task, so no single task is
|
||
// huge (a full level refill on startup/teleport fans out across workers). Lower = more, smaller
|
||
// tasks (better parallelism / latency); higher = fewer, fatter tasks.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Lighting", meta = (ClampMin = "1", ClampMax = "64"))
|
||
int32 DensityVolumeFillSlabCells = 8;
|
||
|
||
// Per-pixel shadow-march step count toward the orb (the terrain material reads this). More = crisper
|
||
// occlusion at grazing angles but higher GPU cost. 64 is a sane start; tune against the look/cost.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Lighting", meta = (ClampMin = "4", ClampMax = "256"))
|
||
int32 DensityVolumeMarchSteps = 64;
|
||
|
||
// Upload the clipmap to GPU R8 volume textures (so the terrain material can march it). OFF = the
|
||
// CPU volume still streams (debug-draw works) but nothing reaches the GPU — the safe fallback if
|
||
// the runtime Texture3D RHI path misbehaves on a given engine build.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Lighting")
|
||
bool bDensityVolumeGPUUpload = true;
|
||
|
||
// DEBUG (step 1a verification, no GPU): draw small boxes for SOLID cells of level 0 within
|
||
// DensityVolumeDebugRadiusCells of the player, so you can confirm the volume holds terrain-shaped
|
||
// solidity, follows you, and updates on carve — BEFORE the GPU upload + material march land.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Lighting|Debug")
|
||
bool bDebugDrawDensityVolume = false;
|
||
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Lighting|Debug", meta = (ClampMin = "1", ClampMax = "32"))
|
||
int32 DensityVolumeDebugRadiusCells = 12;
|
||
|
||
//=========================================================================
|
||
// RENDERING
|
||
//=========================================================================
|
||
|
||
// Matériau global par défaut (peut être override par strate).
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Rendering")
|
||
UMaterialInterface* VoxelMaterial;
|
||
|
||
//=========================================================================
|
||
// STRATES
|
||
//=========================================================================
|
||
|
||
// Seed du monde. Pilote toute la génération procédurale.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Strates")
|
||
int32 Seed = 0;
|
||
|
||
// Optional reviewed/cooked season. When set, its manifest owns layout, seed, bounds and
|
||
// density recipes; the authored StratePool/FixedStrates path remains untouched when unset.
|
||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Voxel|Strates")
|
||
TSoftObjectPtr<UVoxelSeasonAsset> Season;
|
||
|
||
// Optional reviewed Core SurfaceWorld and FloatingIslands plans. Canonical JSON stays in this
|
||
// cooked settings asset; its Core FNV digest enters the season hash only while Core is enabled.
|
||
// Plans Core SurfaceWorld et FloatingIslands facultatifs. Le JSON canonique est cuit ici ;
|
||
// son hash FNV Core entre dans le hash de saison uniquement lorsque Core est actif.
|
||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Voxel|Strates", meta = (MultiLine = true))
|
||
FString WorldAssemblyManifestJson;
|
||
|
||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Voxel|Strates")
|
||
FString WorldAssemblyManifestHash;
|
||
|
||
/** Use imported JSON above instead of planning from the resolved settings and layout. */
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Strates|World Assembly",
|
||
meta = (DisplayName = "Use Imported World Assembly Override"))
|
||
bool bUseWorldAssemblyManifestOverride = false;
|
||
|
||
#if WITH_EDITORONLY_DATA
|
||
UPROPERTY(EditAnywhere, Category = "Voxel|Strates|World Assembly", meta = (FilePathFilter = "json"))
|
||
FFilePath SourceWorldAssemblyManifestJson;
|
||
#endif
|
||
|
||
// Numéro de saison (incrementé par ChangeSeed). Purement informatif côté gameplay.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Strates")
|
||
int32 CurrentSeason = 1;
|
||
|
||
// Pool de définitions de strates disponibles pour l'assignation aléatoire.
|
||
// Chaque définition a sa propre hauteur — les strates ne sont PAS uniformes.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Strates")
|
||
TArray<TSoftObjectPtr<UVoxelStrateDefinition>> StratePool;
|
||
|
||
// Strates fixées: {index → définition}. Ex: {5, CrystalCaverns} =
|
||
// la strate 5 est toujours CrystalCaverns. Les autres sont tirées du pool.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Strates")
|
||
TMap<int32, TSoftObjectPtr<UVoxelStrateDefinition>> FixedStrates;
|
||
|
||
// Nombre total de strates à générer vers le bas.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Strates", meta = (ClampMin = "1"))
|
||
int32 TotalStrates = 10;
|
||
|
||
// Vertical SEPARATION between consecutive strates, in chunks of solid bedrock.
|
||
// 0 = strates touch (just the seals between them). Higher = a thick rock layer
|
||
// between each biome that the player must dig through at (0,0), and that the
|
||
// auto-carved passages tunnel across.
|
||
// 0 → adjacent layers (default) · 1-2 → a real bedrock band · 4+ → deep separation
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Strates", meta = (ClampMin = "0", ClampMax = "16"))
|
||
int32 InterStrateGapChunks = 0;
|
||
|
||
//=========================================================================
|
||
// SPINE & INTER-STRATE CONNECTIONS (the (0,0) descent axis)
|
||
//=========================================================================
|
||
|
||
// Authored radius (voxels) of the guaranteed (0,0) landing room in EVERY strate, regardless
|
||
// of archetype. The §6.5 body-sized formulas turn it into a flat-floored room reserved for a
|
||
// future straight shaft of diameter 2 * this radius. At the default 28-voxel radius that is
|
||
// 56 voxels / 7 m, with a six-voxel / 0.75 m floor ledge around it. This setting does not bore the
|
||
// shaft today: lower seals remain solid until a progression passage is opened.
|
||
// 10-14 → compact landing room (default) · 20+ → broad landing room
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Spine", meta = (ClampMin = "0.0"))
|
||
float OriginSpineRadius = 28.0f;
|
||
|
||
// Auto-open the very top seal at (0,0) so the world starts with a hole to the
|
||
// surface (the entry shaft). Lower boundaries are NOT auto-opened — the player
|
||
// digs those. Disable for a fully sealed world the player must breach from above.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Spine")
|
||
bool bOpenSurfaceEntry = true;
|
||
|
||
// NOTE: inter-strate tunnel count + shape (style, width, length, placement, worming,
|
||
// spiral, cascade) are now PER-STRATE — see UVoxelStrateDefinition::PassageConfig.
|
||
// Each strate controls its own descent tunnels to the layer below it.
|
||
|
||
//=========================================================================
|
||
// CARVING (modifications du joueur)
|
||
//=========================================================================
|
||
|
||
// Nombre max d'opérations de modification par saison. 0 = illimité.
|
||
// 500 = édition légère, 2000 = mining modéré, 0 = créatif.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Carving",
|
||
meta = (ClampMin = "0"))
|
||
int32 MaxModifications = 0;
|
||
|
||
// Rayon max d'un brush (en voxels). Empêche les édits géants.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Carving",
|
||
meta = (ClampMin = "1.0"))
|
||
float MaxBrushRadius = 30.0f;
|
||
|
||
// Volume total maximum (somme des volumes de brush). 0 = illimité.
|
||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Carving",
|
||
meta = (ClampMin = "0.0"))
|
||
float MaxTotalVolume = 0.0f;
|
||
};
|