// 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< 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> 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> 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; // Physical width of one vertical strate-layout chunk. This is independent of the // Core tile width (32^3 voxels) and of the source generator's voxel resolution. // 8 m preserves the owner's current SurfaceWorld extent at 25 cm Core voxels. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Strates|World Assembly", meta = (ClampMin = "0.25", ClampMax = "64.0", Units = "m")) float StrateLayoutChunkSizeMeters = 8.0f; //========================================================================= // 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; };