j
This commit is contained in:
@@ -14,6 +14,8 @@
|
||||
|
||||
class UVoxelStrateManager;
|
||||
class UVoxelStrateDefinition;
|
||||
class UVoxelBiomeDefinition;
|
||||
class UVoxelGenerator;
|
||||
class UExponentialHeightFogComponent;
|
||||
class USkyLightComponent;
|
||||
|
||||
@@ -23,23 +25,31 @@ class VOXELFORGE_API UVoxelAtmosphereManager : public UObject
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Create the managed fog + skylight components on the owner actor. */
|
||||
void Initialize(AActor* InOwner, UVoxelStrateManager* InStrateManager);
|
||||
/** Create the managed fog + skylight components on the owner actor. Generator supplies
|
||||
* the dominant-biome query so atmosphere can vary by biome within a strate. */
|
||||
void Initialize(AActor* InOwner, UVoxelStrateManager* InStrateManager, UVoxelGenerator* InGenerator);
|
||||
|
||||
/** Call each frame with the player's world position. Cheap: only reacts on strate change. */
|
||||
/** Call each frame with the player's world position. Cheap: only reacts on strate OR
|
||||
* dominant-biome change. */
|
||||
void UpdateForPlayer(const FVector& PlayerWorldPos);
|
||||
|
||||
/** Tear down spawned layer actors + reset (season reset / shutdown). */
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
void ApplyStrate(const UVoxelStrateDefinition* Def);
|
||||
// Full strate apply: layer actors + atmosphere BP + fog/sky. Biome retints fog/sky.
|
||||
void ApplyStrate(const UVoxelStrateDefinition* Def, const UVoxelBiomeDefinition* Biome);
|
||||
// Just the managed fog + skylight (biome override beats strate when set).
|
||||
void ApplyFogSky(const UVoxelStrateDefinition* Def, const UVoxelBiomeDefinition* Biome);
|
||||
|
||||
TWeakObjectPtr<AActor> Owner;
|
||||
|
||||
UPROPERTY()
|
||||
UVoxelStrateManager* StrateManager = nullptr;
|
||||
|
||||
UPROPERTY()
|
||||
UVoxelGenerator* Generator = nullptr;
|
||||
|
||||
UPROPERTY()
|
||||
UExponentialHeightFogComponent* Fog = nullptr;
|
||||
|
||||
@@ -59,4 +69,7 @@ private:
|
||||
|
||||
// Which strate's atmosphere is currently applied (INT32_MIN = none yet).
|
||||
int32 CurrentStrateIndex = INT32_MIN;
|
||||
|
||||
// Dominant biome currently driving fog/sky (identity token for change detection).
|
||||
TWeakObjectPtr<const UVoxelBiomeDefinition> CurrentBiome;
|
||||
};
|
||||
|
||||
@@ -6,22 +6,39 @@
|
||||
// - PopulateChunk(coord, meshdata) after a chunk's mesh is applied
|
||||
// - ClearChunk(coord) when a chunk unloads / is re-meshed
|
||||
// - ClearAll() on regenerate / season reset
|
||||
// - SetActiveStrate(playerStrate) each Tick (no-op unless the strate changed)
|
||||
//
|
||||
// DETERMINISM: every placement decision is a pure hash of (chunk, surface index,
|
||||
// entry index, seed), so the same world re-populates identically. Spawning itself
|
||||
// must run on the game thread (UWorld::SpawnActor), which it does — ApplyMeshToChunk
|
||||
// is game-thread.
|
||||
//
|
||||
// RENDERING PATHS per decoration entry (FStrateDecoration):
|
||||
// - ActorClass → real actors (lights, logic, interaction). Keep MaxLODLevel=0.
|
||||
// - InstancedMesh → HISM instances batched per chunk: no tick, no per-actor cost,
|
||||
// engine-culled. Safe to allow at LOD 1-2 so visual props don't
|
||||
// pop out with LOD0 (emissive materials still glow at distance).
|
||||
//
|
||||
// STRATE LIGHT CULLING: a light in another strate can never legitimately reach the
|
||||
// player (seals + bedrock gap are solid), but a shadowless light BLEEDS through rock
|
||||
// and a shadowed one pays full cost to render black. So light components on spawned
|
||||
// decoration actors are only visible while the player is in the SAME strate — the
|
||||
// analytical form of occlusion culling, computed once per strate change.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "VoxelTypes.h"
|
||||
#include "VoxelStrateTypes.h" // FStrateDecoration (resolved per dominant biome)
|
||||
#include "VoxelContentManager.generated.h"
|
||||
|
||||
class UVoxelStrateManager;
|
||||
class UVoxelStrateDefinition;
|
||||
class UVoxelGenerator;
|
||||
class UStaticMesh;
|
||||
class UStaticMeshComponent;
|
||||
class UHierarchicalInstancedStaticMeshComponent;
|
||||
class UMaterialInterface;
|
||||
|
||||
UCLASS()
|
||||
class VOXELFORGE_API UVoxelContentManager : public UObject
|
||||
@@ -29,34 +46,55 @@ class VOXELFORGE_API UVoxelContentManager : public UObject
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Wire up services. Owner is the AVoxelWorld actor that owns spawned content. */
|
||||
void Initialize(AActor* InOwner, UVoxelStrateManager* InStrateManager, int32 InSeed);
|
||||
/** Wire up services. Owner is the AVoxelWorld actor that owns spawned content.
|
||||
* Generator supplies the dominant-biome query for per-biome content selection. */
|
||||
void Initialize(AActor* InOwner, UVoxelStrateManager* InStrateManager,
|
||||
UVoxelGenerator* InGenerator, int32 InSeed);
|
||||
|
||||
/** Update the seed used for placement hashing (season reset). */
|
||||
void SetSeed(int32 InSeed) { Seed = InSeed; }
|
||||
|
||||
/** Populate decorations + water for a chunk. Clears any previous content first.
|
||||
* Decorations are only scattered at LOD 0 (near chunks); water is placed at any LOD. */
|
||||
* Each decoration entry spawns only while LOD <= its MaxLODLevel; water at any LOD. */
|
||||
void PopulateChunk(const FIntVector& ChunkCoord, const FVoxelMeshData& MeshData, int32 LODLevel = 0);
|
||||
|
||||
/** Destroy all spawned content (actors + water plane) for one chunk. */
|
||||
/** Destroy all spawned content (actors + instances + water plane) for one chunk. */
|
||||
void ClearChunk(const FIntVector& ChunkCoord);
|
||||
|
||||
/** Destroy all spawned content for every chunk. */
|
||||
void ClearAll();
|
||||
|
||||
/** Player strate changed → toggle decoration lights (strict: lights only live in the
|
||||
* player's strate). Cheap no-op while the strate stays the same — call every Tick. */
|
||||
void SetActiveStrate(int32 PlayerStrateIndex);
|
||||
|
||||
private:
|
||||
void SpawnDecorations(const FIntVector& ChunkCoord, const FVoxelMeshData& MeshData,
|
||||
const UVoxelStrateDefinition* Def, TArray<TWeakObjectPtr<AActor>>& Out);
|
||||
void SpawnWater(const FIntVector& ChunkCoord, const UVoxelStrateDefinition* Def);
|
||||
const TArray<FStrateDecoration>& Decorations, int32 LODLevel,
|
||||
TArray<TWeakObjectPtr<AActor>>& Out);
|
||||
void SpawnWater(const FIntVector& ChunkCoord, const UVoxelStrateDefinition* Def,
|
||||
UMaterialInterface* WaterMaterial);
|
||||
|
||||
/** Strate index a chunk belongs to (center-Z lookup). */
|
||||
int32 GetChunkStrateIndex(const FIntVector& ChunkCoord) const;
|
||||
|
||||
/** Show/hide every light component on a decoration actor. */
|
||||
static void SetActorLightsEnabled(AActor* Actor, bool bEnabled);
|
||||
|
||||
TWeakObjectPtr<AActor> Owner;
|
||||
|
||||
UPROPERTY()
|
||||
UVoxelStrateManager* StrateManager = nullptr;
|
||||
|
||||
UPROPERTY()
|
||||
UVoxelGenerator* Generator = nullptr;
|
||||
|
||||
int32 Seed = 0;
|
||||
|
||||
// Player's current strate (INT32_MIN until first SetActiveStrate). Lights on spawned
|
||||
// actors are enabled iff their chunk's strate matches this.
|
||||
int32 ActiveStrateIndex = INT32_MIN;
|
||||
|
||||
// Engine unit plane (/Engine/BasicShapes/Plane) reused for every water surface.
|
||||
UPROPERTY()
|
||||
UStaticMesh* PlaneMesh = nullptr;
|
||||
@@ -64,6 +102,9 @@ private:
|
||||
// Spawned decoration/ambient actors per chunk (weak — they live in the level).
|
||||
TMap<FIntVector, TArray<TWeakObjectPtr<AActor>>> SpawnedActors;
|
||||
|
||||
// HISM components per chunk (weak — registered components are owned by the actor).
|
||||
TMap<FIntVector, TArray<TWeakObjectPtr<UHierarchicalInstancedStaticMeshComponent>>> ChunkInstances;
|
||||
|
||||
// Water surface component per chunk.
|
||||
UPROPERTY()
|
||||
TMap<FIntVector, UStaticMeshComponent*> WaterPlanes;
|
||||
|
||||
@@ -11,12 +11,14 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "VoxelTypes.h"
|
||||
#include "VoxelStrateTypes.h"
|
||||
#include "VoxelBiomeTypes.h"
|
||||
#include "VoxelGenerator.generated.h"
|
||||
|
||||
// Forward decls (évite les includes transitifs)
|
||||
class UVoxelSettings;
|
||||
class UVoxelStrateManager;
|
||||
class UVoxelDiffLayer;
|
||||
class UVoxelBiomeDefinition;
|
||||
|
||||
/**
|
||||
* UVoxelGenerator
|
||||
@@ -103,9 +105,18 @@ public:
|
||||
/**
|
||||
* Densité pour une strate SurfaceWorld — terrain à ciel ouvert (collines,
|
||||
* montagnes, plages) sous un plafond solide, avec nappe d'eau optionnelle.
|
||||
*
|
||||
* Biome output-blend: the heightfield is evaluated with ParamsD (the voxel's dominant
|
||||
* biome) and, when NeighborWeight > 0, also with ParamsN (its nearest neighbour); the
|
||||
* two surface HEIGHTS are lerped. This stays seamless across ANY param difference
|
||||
* (frequencies included). With no biomes, pass ParamsD == ParamsN and weight 0 →
|
||||
* bit-identical to the single-param terrain. Structural fields (Z bounds, seal, base,
|
||||
* water level) must be equal in both (forced from the strate).
|
||||
*/
|
||||
float GetSurfaceDensity(float WorldX, float WorldY, float WorldZ,
|
||||
const FSurfaceGenerationParams& Params) const;
|
||||
const FSurfaceGenerationParams& ParamsD,
|
||||
const FSurfaceGenerationParams& ParamsN,
|
||||
float NeighborWeight) const;
|
||||
|
||||
/**
|
||||
* Densité pour une strate VerticalShafts — puits verticaux pleine hauteur,
|
||||
@@ -120,4 +131,67 @@ public:
|
||||
*/
|
||||
float GetFloatingIslandDensity(float WorldX, float WorldY, float WorldZ,
|
||||
const FFloatingIslandParams& Params) const;
|
||||
|
||||
//=========================================================================
|
||||
// CLIMATE & BIOME FIELDS (pure XY, deterministic, window-invariant)
|
||||
//=========================================================================
|
||||
|
||||
/**
|
||||
* Relief ("elevation") field at a world XY → [0,1], contrast-shaped & smoothed.
|
||||
* The single source of truth for the relief map M: SurfaceWorld terrain and the
|
||||
* biome climate map both call this, so they agree when given the same freq/contrast.
|
||||
*/
|
||||
float SampleRelief(float WorldX, float WorldY, float Frequency, float Contrast) const;
|
||||
|
||||
/**
|
||||
* Moisture field at a world XY → [0,1]. The second climate axis for biome placement.
|
||||
*/
|
||||
float SampleMoisture(float WorldX, float WorldY, float Frequency) const;
|
||||
|
||||
/**
|
||||
* Resolve the biome at a world XY: dominant cell + nearest neighbour + blend weight.
|
||||
* Warped Voronoi over a jittered grid; each cell's biome is chosen by its site's
|
||||
* (relief, moisture) against the context's climate boxes. PURE function of (XY, seed,
|
||||
* Ctx) — no chunk-window dependence (CODEMAP §8.4). Returns an empty sample when the
|
||||
* context has no biomes. Used by the 2D preview bake and (next) the density path.
|
||||
*/
|
||||
FBiomeSample SampleBiomeAt(float WorldX, float WorldY, const FBiomeContext& Ctx) const;
|
||||
|
||||
/**
|
||||
* Hot-path biome resolve: the dominant + neighbour biome and blend weight at a world
|
||||
* XY for the given strate slice. Uses (and lazily rebuilds) Cache — a box-validated
|
||||
* per-chunk grid — so the noise-heavy cell classification happens once per chunk, not
|
||||
* per voxel. Bit-identical to SampleBiomeAt, so the baked preview matches the terrain.
|
||||
*/
|
||||
FBiomeSample ResolveBiomeSampleAt(float WorldX, float WorldY, int32 ChunkZ,
|
||||
const FBiomeContext& Ctx, FChunkBiomeCache& Cache) const;
|
||||
|
||||
/**
|
||||
* Dominant biome ASSET at a world XY for the given strate slice (chunk Z), or nullptr
|
||||
* when the strate has no biomes / the point is outside the strate range. Game-thread
|
||||
* helper for content + atmosphere selection — uses the same field as the density path.
|
||||
*/
|
||||
const UVoxelBiomeDefinition* GetDominantBiomeAt(float WorldX, float WorldY, int32 ChunkZ) const;
|
||||
|
||||
private:
|
||||
/** Pick the biome (index into Ctx.Biomes) for a Voronoi site, by its climate. */
|
||||
int32 ClassifyBiomeAtSite(float SiteX, float SiteY, const FBiomeContext& Ctx, uint32 SiteHash) const;
|
||||
|
||||
/** The SurfaceWorld heightfield: world XY → terrain surface Z (voxel coords). Pure
|
||||
* per-XY; the part that's evaluated per biome and blended in GetSurfaceDensity. */
|
||||
float ComputeSurfaceTerrainZ(float WorldX, float WorldY, const FSurfaceGenerationParams& Params) const;
|
||||
|
||||
/** The SurfaceWorld sky-cap ceiling surface Z at a world XY (also pure per-XY). */
|
||||
float ComputeSurfaceCeiling(float WorldX, float WorldY, const FSurfaceGenerationParams& Params) const;
|
||||
|
||||
/** Final SurfaceWorld density from a column's precomputed terrain Z + ceiling: the
|
||||
* cheap per-voxel Z-combine + origin spine + boundary seal + passage carving. The
|
||||
* XY-only work (terrain/ceiling) is done once per column and cached (T1.a). */
|
||||
float SurfaceDensityFromColumn(float WorldX, float WorldY, float WorldZ,
|
||||
float TerrainZ, float CeilSurf,
|
||||
const FSurfaceGenerationParams& Structural) const;
|
||||
|
||||
/** (Re)build the per-chunk biome cell grid covering chunk (X,Y) footprint + margin. */
|
||||
void RebuildBiomeGrid(int32 ChunkX, int32 ChunkY, int32 ChunkZ,
|
||||
const FBiomeContext& Ctx, FChunkBiomeCache& Cache) const;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
// STREAMING (distance de vue)
|
||||
//=========================================================================
|
||||
|
||||
// En CHUNKS (CHUNK_SIZE=32). Couverture linéaire = ViewDistanceXY × 32 × 25 cm.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming")
|
||||
int32 ViewDistanceXY = 16;
|
||||
|
||||
@@ -35,7 +36,10 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming")
|
||||
int32 MaxConcurrentTasks = 16;
|
||||
|
||||
// Nombre max de meshes appliqués par frame (évite les stutters d'upload GPU).
|
||||
// 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;
|
||||
|
||||
@@ -47,8 +51,10 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|LOD")
|
||||
int32 LOD0Distance = 4;
|
||||
|
||||
// Distance en chunks pour LOD1 (demi-résolution, step=2).
|
||||
// Au-delà → LOD2 (quart-résolution, step=4).
|
||||
// Distance en chunks pour LOD1 (demi-résolution, step=2). Au-delà → LOD2 (quart-rés,
|
||||
// step=4). LOD2 = le plus lointain ; ces chunks ne projettent PLUS d'ombre (cf.
|
||||
// ApplyMeshToChunk) → rapprocher LOD0/LOD1 pousse plus de chunks dans la bande
|
||||
// LOD2 sans-ombre = moins de draws (levier fps gratuit, à doser visuellement).
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|LOD")
|
||||
int32 LOD1Distance = 8;
|
||||
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "VoxelStrateTypes.h"
|
||||
#include "VoxelBiomeTypes.h"
|
||||
#include "VoxelStrateDefinition.generated.h"
|
||||
|
||||
class UVoxelBiomeDefinition;
|
||||
|
||||
/**
|
||||
* UVoxelStrateDefinition — The content bag for a strate type.
|
||||
*
|
||||
@@ -147,6 +150,24 @@ public:
|
||||
meta = (EditCondition = "GeneratorType == ECaveGeneratorType::FloatingIslands"))
|
||||
FFloatingIslandParams FloatingIslandParams;
|
||||
|
||||
//=========================================================================
|
||||
// BIOMES (vary terrain & content WITHIN this strate — any archetype)
|
||||
//=========================================================================
|
||||
// Optional. When empty, this strate generates exactly as before (no biome field,
|
||||
// bit-identical output). When populated, a deterministic world-XY biome field
|
||||
// (warped Voronoi + climate, see FBiomeMapParams) assigns regions; each biome can
|
||||
// supply its OWN archetype params (a mini-strate-variant, output-blended for surface)
|
||||
// plus its own decorations / atmosphere / water. Surface terrain wired today;
|
||||
// content/atmosphere work for any archetype.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Strate|Biomes")
|
||||
TArray<UVoxelBiomeDefinition*> Biomes;
|
||||
|
||||
// World-XY biome field tuning (cell size, border warp/blend, climate fields).
|
||||
// Only relevant when Biomes is non-empty. Bake AVoxelWorld::BakeBiomePreview to
|
||||
// see the resulting map before regenerating the world.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Strate|Biomes")
|
||||
FBiomeMapParams BiomeMapParams;
|
||||
|
||||
//=========================================================================
|
||||
// DISTURBANCES (the "wow" layer — applies on top of ANY archetype)
|
||||
//=========================================================================
|
||||
|
||||
@@ -227,6 +227,14 @@ public:
|
||||
FVerticalShaftParams GetVerticalShaftParamsForChunk(const FIntVector& ChunkCoord) const;
|
||||
FFloatingIslandParams GetFloatingIslandParamsForChunk(const FIntVector& ChunkCoord) const;
|
||||
|
||||
/**
|
||||
* Flatten the strate's Biomes[] + BiomeMapParams into a POD FBiomeContext for the
|
||||
* biome field. Returns an empty (invalid) context when the strate has no biomes —
|
||||
* callers treat that as "biomes disabled" and fall back to base params. The result
|
||||
* is window-invariant (depends only on the strate at this Z, not the chunk window).
|
||||
*/
|
||||
FBiomeContext GetBiomeContextForChunk(const FIntVector& ChunkCoord) const;
|
||||
|
||||
/**
|
||||
* World-space Z (voxel coords) of this strate's water surface, or -FLT_MAX if the
|
||||
* strate has no water. Derived from the active archetype's WaterLevelRelative and
|
||||
|
||||
@@ -304,6 +304,16 @@ struct VOXELFORGE_API FStrateGenerationParams
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Worm Tunnels")
|
||||
float WormStrength = 10.0f;
|
||||
|
||||
// Worms only carve within this distance (voxels) of the room/tunnel network, fading
|
||||
// smoothly to zero at the edge. Keeps worms as organic braids and shortcuts that HUG
|
||||
// the cave system instead of spraying disconnected noise pockets through the whole
|
||||
// strate (the far-field "confetti"). 0 = unlimited (legacy unmasked behaviour).
|
||||
// 16 → tight braiding right along rooms/tunnels
|
||||
// 24 → braids + short noodle shortcuts (good default)
|
||||
// 48+ → loose, wandering side-passages
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Worm Tunnels", meta = (ClampMin = "0.0"))
|
||||
float WormNetworkRange = 24.0f;
|
||||
|
||||
// ===== CAVE MORPHOLOGY (room-and-corridor) =====
|
||||
//
|
||||
// Cave shape is defined by SDF (Signed Distance Field) primitives:
|
||||
@@ -489,6 +499,16 @@ struct VOXELFORGE_API FStrateGenerationParams
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cave Morphology|Tunnels", meta = (ClampMin = "0.0", ClampMax = "1.0"))
|
||||
float TunnelHorizontalBias = 0.5f;
|
||||
|
||||
// Topology of the guaranteed tunnel network.
|
||||
// true → each room links to its best candidate among rooms CLOSER to (0,0): the whole
|
||||
// network becomes a tree rooted at the origin room — every cave is reachable
|
||||
// from the spine hub, tunnels flow inward like tributaries (intentional descent
|
||||
// structure). TunnelDensity still adds loops on top.
|
||||
// false → legacy nearest-neighbor pairing: organic scattered clusters, but connectivity
|
||||
// between clusters is NOT guaranteed (isolated pockets are common).
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cave Morphology|Tunnels")
|
||||
bool bTunnelsFlowTowardOrigin = true;
|
||||
|
||||
// How much tunnel endpoints shift up/down within rooms (0-1).
|
||||
// Fraction of the room's vertical radius. Each tunnel endpoint gets
|
||||
// a hash-derived Z offset, so tunnels enter rooms at different heights.
|
||||
@@ -884,6 +904,7 @@ struct VOXELFORGE_API FStrateGenerationParams
|
||||
Result.WormHorizontalBias = FMath::Lerp(A.WormHorizontalBias, B.WormHorizontalBias, Alpha);
|
||||
Result.WormThreshold = FMath::Lerp(A.WormThreshold, B.WormThreshold, Alpha);
|
||||
Result.WormStrength = FMath::Lerp(A.WormStrength, B.WormStrength, Alpha);
|
||||
Result.WormNetworkRange = FMath::Lerp(A.WormNetworkRange, B.WormNetworkRange, Alpha);
|
||||
// Cave morphology
|
||||
Result.RoomSpacing = FMath::Lerp(A.RoomSpacing, B.RoomSpacing, Alpha);
|
||||
Result.RoomDensity = FMath::Lerp(A.RoomDensity, B.RoomDensity, Alpha);
|
||||
@@ -903,6 +924,7 @@ struct VOXELFORGE_API FStrateGenerationParams
|
||||
Result.MaxTunnelLength = FMath::Lerp(A.MaxTunnelLength, B.MaxTunnelLength, Alpha);
|
||||
Result.TunnelWarpStrength = FMath::Lerp(A.TunnelWarpStrength, B.TunnelWarpStrength, Alpha);
|
||||
Result.TunnelHorizontalBias = FMath::Lerp(A.TunnelHorizontalBias, B.TunnelHorizontalBias, Alpha);
|
||||
Result.bTunnelsFlowTowardOrigin = (Alpha < 0.5f) ? A.bTunnelsFlowTowardOrigin : B.bTunnelsFlowTowardOrigin;
|
||||
Result.TunnelEndpointZOffset = FMath::Lerp(A.TunnelEndpointZOffset, B.TunnelEndpointZOffset, Alpha);
|
||||
Result.SDFBlendRadius = FMath::Lerp(A.SDFBlendRadius, B.SDFBlendRadius, Alpha);
|
||||
Result.WaterLevelRelative = FMath::Lerp(A.WaterLevelRelative, B.WaterLevelRelative, Alpha);
|
||||
@@ -976,6 +998,7 @@ struct VOXELFORGE_API FStrateGenerationParams
|
||||
// Forward declaration — the actual data asset lives in VoxelTerrainOpDefinition.h.
|
||||
// We only need a soft pointer here, so no #include needed.
|
||||
class UVoxelTerrainOpDefinition;
|
||||
class UStaticMesh;
|
||||
|
||||
/**
|
||||
* FStrateTerrainOpEntry — A reference to a terrain operation with a weight.
|
||||
@@ -1276,6 +1299,43 @@ struct VOXELFORGE_API FSurfaceGenerationParams
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface|Shape", meta = (ClampMin = "0.0"))
|
||||
float SurfaceRoughness = 3.0f;
|
||||
|
||||
// ----- Macro relief & landforms -----
|
||||
|
||||
// Domain-warp the heightfield query by this many voxels of XY displacement before
|
||||
// sampling continents/mountains. Bends straight coastlines and ridgelines into winding,
|
||||
// organic landforms. 0 = no warp (axis-aligned blobby noise, the old look).
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface|Macro", meta = (ClampMin = "0.0"))
|
||||
float HeightWarpStrength = 35.0f;
|
||||
|
||||
// Frequency of the domain-warp noise. Lower = broader, sweeping bends.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface|Macro")
|
||||
float HeightWarpFrequency = 0.008f;
|
||||
|
||||
// Macro "relief" map frequency: a very-low-frequency field that makes some regions flat
|
||||
// plains and others mountainous highlands — distinct terrain depending on where you
|
||||
// stand. (The cheap, continuous precursor to a full biome system.) Lower = larger regions.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface|Macro")
|
||||
float ReliefFrequency = 0.0015f;
|
||||
|
||||
// How strongly the relief map modulates terrain (0-1). 0 = uniform everywhere (old
|
||||
// behaviour); 1 = full plains <-> mountains variation across the world.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface|Macro", meta = (ClampMin = "0.0", ClampMax = "1.0"))
|
||||
float ReliefStrength = 0.7f;
|
||||
|
||||
// Contrast of the relief map. >1 sharpens the plains/highland boundary (more distinct
|
||||
// regions); ~1 keeps it a gradual blend.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface|Macro", meta = (ClampMin = "0.25", ClampMax = "4.0"))
|
||||
float ReliefContrast = 1.6f;
|
||||
|
||||
// Plateau/mesa terracing strength (0-1), applied in high-relief regions only. 0 = off
|
||||
// (smooth slopes). Crank up for stepped mesas and layered cliffs in the mountainous areas.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface|Macro", meta = (ClampMin = "0.0", ClampMax = "1.0"))
|
||||
float TerraceStrength = 0.0f;
|
||||
|
||||
// Height of each terrace step in voxels (when TerraceStrength > 0).
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface|Macro", meta = (ClampMin = "1.0"))
|
||||
float TerraceHeight = 12.0f;
|
||||
|
||||
// ----- Water -----
|
||||
|
||||
// Water table height as a fraction of strate height (0 = no water). Valleys below
|
||||
@@ -1600,10 +1660,26 @@ struct VOXELFORGE_API FStrateDecoration
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// The actor class to spawn (e.g., BP_Stalactite, BP_CrystalCluster)
|
||||
// The actor class to spawn (e.g., BP_Stalactite, BP_CrystalCluster).
|
||||
// Real actors: lights, logic, interaction. They cost game-thread time per instance —
|
||||
// keep MaxLODLevel at 0 for these, and prefer InstancedMesh for pure visual props.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Decoration")
|
||||
TSubclassOf<AActor> ActorClass;
|
||||
|
||||
// INSTANCED path: if set, this entry renders as batched HISM instances instead of
|
||||
// spawning ActorClass (which is then ignored). No tick, no per-actor overhead,
|
||||
// engine-culled — orders of magnitude cheaper. Use for everything that doesn't need
|
||||
// logic/lights/interaction; an emissive material still glows at distance without a light.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Decoration")
|
||||
UStaticMesh* InstancedMesh = nullptr;
|
||||
|
||||
// Spawn while the chunk's LOD <= this (0 = LOD0 only, the old behaviour).
|
||||
// Lets instanced visual props persist on LOD1-2 chunks instead of popping out with
|
||||
// LOD0. NOTE: placement samples the LOD's mesh vertices, so instances re-scatter
|
||||
// slightly on LOD transitions (masked by the terrain's own LOD pop).
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Decoration", meta = (ClampMin = "0", ClampMax = "2"))
|
||||
int32 MaxLODLevel = 0;
|
||||
|
||||
// Which surface type this decoration can be placed on
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Decoration")
|
||||
ESurfaceType SurfacePlacement = ESurfaceType::Any;
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
//
|
||||
// Taille de chunk: 32^3 = 32 768 voxels.
|
||||
// Pourquoi 32 ? Puissance de 2 → astuces bit à bit + bon alignement GPU.
|
||||
// VOXEL_SIZE = 25 cm/voxel (Unreal travaille en centimètres).
|
||||
// VOXEL_SIZE = 25 cm/voxel (Unreal travaille en centimètres). 1 chunk = 8 m.
|
||||
//
|
||||
// NOTE: 64³ a été essayé ("B", 2026-06-16) pour couper les draw calls (8× moins de
|
||||
// chunks) mais le streaming devenait trop saccadé (briques 8× plus lourdes → applies
|
||||
// + spawns de contenu en gros à-coups sur le game thread). Reverté à 32³ : le fps se
|
||||
// règle côté RENDU (ombres off sur LOD lointain, voir ApplyMeshToChunk), pas via la
|
||||
// taille de chunk. La taille de chunk reste le levier streaming-vs-draws si besoin.
|
||||
|
||||
constexpr int32 CHUNK_SIZE = 32;
|
||||
constexpr int32 CHUNK_SIZE_SQUARED = CHUNK_SIZE * CHUNK_SIZE; // 1024
|
||||
@@ -142,8 +148,8 @@ inline float SmoothStep01(float x)
|
||||
return x * x * (3.0f - 2.0f * x);
|
||||
}
|
||||
|
||||
// UE's PerlinNoise3D renvoie ~[-0.8, 0.8] — ce facteur remet à ~[-1, 1]
|
||||
// pour correspondre aux attentes des formules de densité.
|
||||
// Le coeur de bruit (VoxelNoise::Perlin3D, T2.a) renvoie ~[-0.8, 0.8] comme l'ancien
|
||||
// FMath::PerlinNoise3D — ce facteur remet à ~[-1, 1] pour les formules de densité.
|
||||
constexpr float VOXEL_NOISE_SCALE = 1.25f;
|
||||
|
||||
//=============================================================================
|
||||
|
||||
@@ -273,6 +273,37 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel World|Debug")
|
||||
bool bDebugDrawPassages = false;
|
||||
|
||||
//=========================================================================
|
||||
// BIOME MAP PREVIEW (bake the XY biome field to a PNG — works without PIE)
|
||||
//=========================================================================
|
||||
// Tune biome layout (cell size, warp, climate boxes) without flying around: set
|
||||
// the strate + window, click Bake, open Saved/BiomePreview.png. Uses a transient
|
||||
// generator seeded from VoxelSettings, so it works in the editor with no PIE.
|
||||
|
||||
/** The strate whose Biomes[] + BiomeMapParams to preview. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Biome Preview")
|
||||
UVoxelStrateDefinition* BiomePreviewStrate = nullptr;
|
||||
|
||||
/** Width/height of the sampled window in VOXELS (centred on BiomePreviewCenter). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Biome Preview", meta = (ClampMin = "1.0"))
|
||||
float BiomePreviewWorldSize = 16000.0f;
|
||||
|
||||
/** Centre of the preview window in voxel coords (X,Y). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Biome Preview")
|
||||
FVector2D BiomePreviewCenter = FVector2D::ZeroVector;
|
||||
|
||||
/** Output image resolution (pixels per side). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Biome Preview", meta = (ClampMin = "64", ClampMax = "2048"))
|
||||
int32 BiomePreviewResolution = 512;
|
||||
|
||||
/** Which field to visualise: biome debug colours, relief, or moisture. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Biome Preview")
|
||||
EBiomePreviewChannel BiomePreviewChannel = EBiomePreviewChannel::Biome;
|
||||
|
||||
/** Bake the selected channel to Saved/BiomePreview.png. */
|
||||
UFUNCTION(CallInEditor, BlueprintCallable, Category = "Biome Preview")
|
||||
void BakeBiomePreview();
|
||||
|
||||
#if WITH_EDITOR
|
||||
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user