wip(assembler): round L22 -- one 8 m layout chunk for every strate; real ceiling -> content -> bedrock

StrateLayoutChunkSizeMeters (world setting, default 8 m; owner decision) is resolved once in
the director/adapter and standalone loader; every generator reads the resolved width (no
CHUNK_SIZE / Core-tile inference). Every strate gets solid ceiling and bedrock bands from its
BoundarySealThickness in the shared composition path (diggable by default); faces come from the
shared boundary path. Owner world: FloatingIslands +8..-120 m (128 m, now with a bedrock floor),
SurfaceWorld -120..-632 m (unchanged; full-detail and exact-query hashes equal L21); the 60 m gap is
gone. The strict layout validator passes on the real world, with 0 endpoint dispatch
disagreements. Spawn/benchmark camera derived from route.front. Ledges under the islands gone
(reviewer capture). Evaluator 7, recipe schema 2, layout snapshot 2, FI geometry 4.
Open: SW cap profiles exceed the ceiling band (owner call); transitions still Hard; Unreal
setting/manager/adapter edits uncompiled here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-20 12:03:14 +02:00
1 parent 8753f6dbe4
commit 55c26a05a7
28 files changed
+1432 -579

No files matched your search

@@ -322,7 +322,7 @@ namespace
const FVector ChunkCenter(
(ChunkCoord.X + 0.5f) * (float)CHUNK_SIZE,
(ChunkCoord.Y + 0.5f) * (float)CHUNK_SIZE,
(ChunkCoord.Z + 0.5f) * (float)CHUNK_SIZE);
(ChunkCoord.Z + 0.5f) * Manager->GetStrateLayoutChunkWidthVoxels());
const float ChunkRadius = (float)CHUNK_SIZE * 0.8660254f + 3.0f;
const TArray<FVoxelPassage>& Passages = Manager->GetPassages();
Cache.FloorProjections.SetNum(Passages.Num());
@@ -1104,7 +1104,7 @@ static bool VF_FindWalkableTunnelAir(
const FIntVector ChunkCoord(
FMath::FloorToInt(Position.X / (float)CHUNK_SIZE),
FMath::FloorToInt(Position.Y / (float)CHUNK_SIZE),
FMath::FloorToInt(Position.Z / (float)CHUNK_SIZE));
FMath::FloorToInt(Position.Z / Manager->GetStrateLayoutChunkWidthVoxels()));
const TArray<int32>& Nearby = VF_GetNearbyPassages(Manager, ChunkCoord);
const TArray<FVoxelPassage>& Passages = Manager->GetPassages();
const FVoxelPassage* PassageData = Passages.GetData();
@@ -1139,7 +1139,7 @@ static bool VF_IsAnyPassageFloorAt(
const FIntVector ChunkCoord(
FMath::FloorToInt(Position.X / (float)CHUNK_SIZE),
FMath::FloorToInt(Position.Y / (float)CHUNK_SIZE),
FMath::FloorToInt(Position.Z / (float)CHUNK_SIZE));
FMath::FloorToInt(Position.Z / Manager->GetStrateLayoutChunkWidthVoxels()));
const TArray<int32>& Nearby = VF_GetNearbyPassages(Manager, ChunkCoord);
const TArray<FVoxelPassage>& Passages = Manager->GetPassages();
const FVoxelPassage* PassageData = Passages.GetData();
@@ -1170,6 +1170,17 @@ bool UVoxelStrateManager::Initialize(UVoxelSettings* Settings, int32 WorldSeed)
UE_LOG(LogTemp, Error, TEXT("[StrateManager] No settings provided!"));
return false;
}
if (!FMath::IsFinite(Settings->StrateLayoutChunkSizeMeters)
|| Settings->StrateLayoutChunkSizeMeters < 0.25f
|| Settings->StrateLayoutChunkSizeMeters > 64.0f)
{
UE_LOG(LogTemp, Error, TEXT("[StrateManager] StrateLayoutChunkSizeMeters must be in [0.25, 64] m."));
return false;
}
StrateLayoutChunkSizeMicrometres = static_cast<uint32>(std::llround(
static_cast<double>(Settings->StrateLayoutChunkSizeMeters) * 1000000.0));
StrateLayoutChunkWidthVoxels = static_cast<float>(
static_cast<double>(StrateLayoutChunkSizeMicrometres) / (10000.0 * VOXEL_SIZE));
FVoxelSeasonManifest SeasonManifest;
bool bUseSeason = false;
@@ -1343,10 +1354,10 @@ bool UVoxelStrateManager::Initialize(UVoxelSettings* Settings, int32 WorldSeed)
Slot.Definition->FloatingIslandParams = SeasonStrate.Params.FloatingIslandParams;
Slot.HeightInChunks = SeasonStrate.HeightInChunks;
Slot.TopChunkZ = SeasonStrate.TopWorldZ / CHUNK_SIZE - 1;
Slot.BottomChunkZ = SeasonStrate.BottomWorldZ / CHUNK_SIZE;
if (SeasonStrate.TopWorldZ % CHUNK_SIZE != 0
|| SeasonStrate.BottomWorldZ % CHUNK_SIZE != 0
Slot.TopChunkZ = SeasonStrate.TopWorldZ / FMath::RoundToInt(StrateLayoutChunkWidthVoxels) - 1;
Slot.BottomChunkZ = SeasonStrate.BottomWorldZ / FMath::RoundToInt(StrateLayoutChunkWidthVoxels);
if (SeasonStrate.TopWorldZ % FMath::RoundToInt(StrateLayoutChunkWidthVoxels) != 0
|| SeasonStrate.BottomWorldZ % FMath::RoundToInt(StrateLayoutChunkWidthVoxels) != 0
|| Slot.TopChunkZ != CurrentTopZ
|| Slot.BottomChunkZ != CurrentTopZ - (Slot.HeightInChunks - 1))
{
@@ -1526,8 +1537,8 @@ bool UVoxelStrateManager::SetComposerOverrideForStrate(
}
VF_SetComposerRuntimeBounds(
Override->Params,
(float)(TargetSlot->TopChunkZ + 1) * CHUNK_SIZE,
(float)TargetSlot->BottomChunkZ * CHUNK_SIZE);
(float)(TargetSlot->TopChunkZ + 1) * StrateLayoutChunkWidthVoxels,
(float)TargetSlot->BottomChunkZ * StrateLayoutChunkWidthVoxels);
Override->bUseRecipe = bUseRecipe;
if (Recipe != nullptr)
{
@@ -1546,8 +1557,8 @@ bool UVoxelStrateManager::SetComposerOverrideForStrate(
Override->bUseRegions = true;
Override->Regions = *InRegions;
VF_RekeyStrateRegionManifest(Override->Regions, CandidateSeed, StrateIndex);
Override->Regions.StrateTopWorldZ = (float)(TargetSlot->TopChunkZ + 1) * CHUNK_SIZE;
Override->Regions.StrateBottomWorldZ = (float)TargetSlot->BottomChunkZ * CHUNK_SIZE;
Override->Regions.StrateTopWorldZ = (float)(TargetSlot->TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Override->Regions.StrateBottomWorldZ = (float)TargetSlot->BottomChunkZ * StrateLayoutChunkWidthVoxels;
VF_SetStrateArchetypeRuntimeBounds(Override->Params,
Override->Regions.StrateTopWorldZ,
Override->Regions.StrateBottomWorldZ);
@@ -1797,11 +1808,11 @@ void UVoxelStrateManager::GeneratePassages()
const FStratePassageConfig& Cfg = UpperDef->PassageConfig;
// Upper strate floor and lower strate ceiling (differ when there's a bedrock gap).
const float UpperTopZ = (float)(Upper.TopChunkZ + 1) * CHUNK_SIZE;
const float UpperBottomZ = (float)(Upper.BottomChunkZ) * CHUNK_SIZE;
const float LowerTopZ = (float)(Lower.TopChunkZ + 1) * CHUNK_SIZE;
const float UpperMax = (float)Upper.HeightInChunks * CHUNK_SIZE * 0.9f;
const float LowerMax = (float)Lower.HeightInChunks * CHUNK_SIZE * 0.9f;
const float UpperTopZ = (float)(Upper.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
const float UpperBottomZ = (float)(Upper.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float LowerTopZ = (float)(Lower.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
const float UpperMax = (float)Upper.HeightInChunks * StrateLayoutChunkWidthVoxels * 0.9f;
const float LowerMax = (float)Lower.HeightInChunks * StrateLayoutChunkWidthVoxels * 0.9f;
const float DistLo = FMath::Min(Cfg.DistanceMin, Cfg.DistanceMax);
const float DistHi = FMath::Max(Cfg.DistanceMin, Cfg.DistanceMax);
@@ -1931,7 +1942,7 @@ void UVoxelStrateManager::GeneratePassages()
LowerDef->FloatingIslandParams,
LowerQuerySeed,
LowerTopZ,
(float)(Lower.BottomChunkZ) * CHUNK_SIZE,
(float)(Lower.BottomChunkZ) * StrateLayoutChunkWidthVoxels,
PX,
PY,
MaxLateralSnap,
@@ -1991,7 +2002,7 @@ void UVoxelStrateManager::GeneratePassages()
if (bAimedLowerAtPlayerFitPoint)
{
const float LowerSealThickness = BoundarySealThicknessFor(*LowerDef);
const float LowerBottomZ = (float)(Lower.BottomChunkZ) * CHUNK_SIZE;
const float LowerBottomZ = (float)(Lower.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float InnerBottomZ = LowerBottomZ + LowerSealThickness;
const float InnerTopZ = LowerTopZ - LowerSealThickness;
if (VoxelMath::IsFinite(SuggestedLowerPoint.X)
@@ -2159,7 +2170,7 @@ void UVoxelStrateManager::GeneratePassages()
Cfg.MouthRadius,
LowerDoorDirection,
LowerTopZ,
(float)(Lower.BottomChunkZ) * CHUNK_SIZE,
(float)(Lower.BottomChunkZ) * StrateLayoutChunkWidthVoxels,
LowerDef ? BoundarySealThicknessFor(*LowerDef) : 0.0f,
bAimedLowerAtPlayerFitPoint);
Passage.UpperPoint = Passage.UpperLanding.StandingPoint;
@@ -2584,8 +2595,8 @@ void UVoxelStrateManager::GeneratePassages()
if (bOpenSurfaceEntry && OriginSpineRadius > 0.0f && StrateLayout.Num() > 0)
{
const FStrateSlot& Top = StrateLayout[0];
const float TopZ = (float)(Top.TopChunkZ + 1) * CHUNK_SIZE;
const float BottomZ = (float)Top.BottomChunkZ * CHUNK_SIZE;
const float TopZ = (float)(Top.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
const float BottomZ = (float)Top.BottomChunkZ * StrateLayoutChunkWidthVoxels;
const float TopSeal = Top.Definition
? BoundarySealThicknessFor(*Top.Definition) : 0.0f;
const VoxelPassageGeometry::FOriginLandingGeometry OriginLanding =
@@ -2608,7 +2619,7 @@ void UVoxelStrateManager::GeneratePassages()
// the top seal into the finite landing room; lower strate seals remain untouched.
Entry.Radius = FMath::Max(OriginSpineRadius,
VoxelPassageGeometry::MetersToVoxels(0.25f));
Entry.UpperPoint = FVector(0.0f, 0.0f, TopZ + CHUNK_SIZE);
Entry.UpperPoint = FVector(0.0f, 0.0f, TopZ + StrateLayoutChunkWidthVoxels);
// The capsule's lower tangent meets the room ceiling; it never bores through the
// room's floor or creates an origin column in strate 0.
Entry.LowerPoint = FVector(
@@ -2672,7 +2683,7 @@ float UVoxelStrateManager::EvaluateModifierSDF(float WorldX, float WorldY, float
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
// The shortlist is rebuilt once for a (manager, version, chunk) and is shared by the tube,
// landing, and floor paths. No source-fit stencil or topology search is allowed below it.
@@ -2818,7 +2829,7 @@ void UVoxelStrateManager::ApplyPassageModifier(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
const FVoxelPassage* PassageData = Passages.GetData();
const FVector Position(WorldX, WorldY, WorldZ);
@@ -2911,7 +2922,7 @@ void UVoxelStrateManager::ApplyPassageLandingAir(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
if (Nearby.Num() == 0) return;
@@ -3022,7 +3033,7 @@ void UVoxelStrateManager::ApplyPassageLandingFloorMC(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
const FVoxelPassage* PassageData = Passages.GetData();
const FVector Position(WorldX, WorldY, WorldZ);
@@ -3118,7 +3129,7 @@ void UVoxelStrateManager::ApplyPassageStructuralPostsMC(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
if (Nearby.Num() == 0) return;
@@ -3245,7 +3256,7 @@ void UVoxelStrateManager::ApplyPassageNativeFloorMC(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
if (Nearby.Num() == 0
|| VoxelPassageGeometry::VerticalShaftConnectorAirMarker())
@@ -3436,7 +3447,7 @@ void UVoxelStrateManager::ApplyPassageLandingRoomFloorMC(
const FIntVector ChunkCoord(
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE));
FMath::FloorToInt(WorldZ / StrateLayoutChunkWidthVoxels));
const TArray<int32>& Nearby = VF_GetNearbyPassages(this, ChunkCoord);
const FVoxelPassage* PassageData = Passages.GetData();
const FVector Position(WorldX, WorldY, WorldZ);
@@ -5910,8 +5921,8 @@ bool UVoxelStrateManager::AnyLandingFloorAtLattice(
for (const FStrateSlot& Slot : StrateLayout)
{
if (Slot.Definition == nullptr) continue;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float Seal = VF_BoundarySealThicknessForDefinition(*Slot.Definition);
const VoxelPassageGeometry::FOriginLandingGeometry Geometry =
VoxelPassageGeometry::BuildOriginLandingGeometry(
@@ -5953,8 +5964,8 @@ bool UVoxelStrateManager::AnyOriginLandingNearBox(
for (const FStrateSlot& Slot : StrateLayout)
{
if (Slot.Definition == nullptr) continue;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
if (VoxelPassageGeometry::OriginLandingRoomTouchesBox(
VoxelBox, TopZ, BottomZ,
VF_BoundarySealThicknessForDefinition(*Slot.Definition),
@@ -5974,8 +5985,8 @@ bool UVoxelStrateManager::AnyOriginLandingFloorNearBox(
for (const FStrateSlot& Slot : StrateLayout)
{
if (Slot.Definition == nullptr) continue;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
if (VoxelPassageGeometry::OriginLandingFloorTouchesBox(
VoxelBox, TopZ, BottomZ,
VF_BoundarySealThicknessForDefinition(*Slot.Definition),
@@ -5999,8 +6010,8 @@ bool UVoxelStrateManager::AnyOriginLandingNearLattice(
for (const FStrateSlot& Slot : StrateLayout)
{
if (Slot.Definition == nullptr) continue;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float Seal = VF_BoundarySealThicknessForDefinition(*Slot.Definition);
if (VoxelPassageGeometry::OriginLandingRoomTouchesLattice(
VoxelBox, LatticeOrigin, Step, TopZ, BottomZ, Seal, OriginSpineRadius,
@@ -6037,8 +6048,8 @@ bool UVoxelStrateManager::AnyOriginLandingAirNearLattice(
{
continue;
}
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float Seal = VF_BoundarySealThicknessForDefinition(*Slot.Definition);
const VoxelPassageGeometry::FOriginLandingGeometry Geometry =
VoxelPassageGeometry::BuildOriginLandingGeometry(
@@ -6114,8 +6125,8 @@ bool UVoxelStrateManager::AnyOriginLandingFloorNearLattice(
for (const FStrateSlot& Slot : StrateLayout)
{
if (Slot.Definition == nullptr) continue;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * CHUNK_SIZE;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (static_cast<float>(Slot.TopChunkZ) + 1.0f) * StrateLayoutChunkWidthVoxels;
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float Seal = VF_BoundarySealThicknessForDefinition(*Slot.Definition);
if (VoxelPassageGeometry::OriginLandingFloorTouchesLattice(
VoxelBox, LatticeOrigin, Step, TopZ, BottomZ, Seal, OriginSpineRadius))
@@ -6149,7 +6160,7 @@ int32 UVoxelStrateManager::FindSlotIndexForChunkZ(int32 ChunkZ) const
UVoxelStrateDefinition* UVoxelStrateManager::GetStrateAt(float WorldZ) const
{
// Convert world Z to chunk Z coordinate
int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / CHUNK_SIZE);
int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / StrateLayoutChunkWidthVoxels);
int32 SlotIdx = FindSlotIndexForChunkZ(ChunkZ);
if (SlotIdx >= 0)
{
@@ -6160,7 +6171,7 @@ UVoxelStrateDefinition* UVoxelStrateManager::GetStrateAt(float WorldZ) const
int32 UVoxelStrateManager::GetStrateIndex(float WorldZ) const
{
int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / CHUNK_SIZE);
int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / StrateLayoutChunkWidthVoxels);
int32 SlotIdx = FindSlotIndexForChunkZ(ChunkZ);
if (SlotIdx >= 0)
{
@@ -6319,9 +6330,9 @@ FSlabGenerationParams UVoxelStrateManager::GetSlabParamsForChunk(const FIntVecto
// Fill in the runtime Z bounds (voxel coordinates, same convention as
// FStrateGenerationParams::StrateTopWorldZ / StrateBottomWorldZ).
// TopChunkZ+1 because the top chunk's CEILING is at (TopChunkZ+1)*CHUNK_SIZE.
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * CHUNK_SIZE;
// TopChunkZ+1 because the top chunk's CEILING is at (TopChunkZ+1)*StrateLayoutChunkWidthVoxels.
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6354,8 +6365,8 @@ FMazeGenerationParams UVoxelStrateManager::GetMazeParamsForChunk(const FIntVecto
}
}
#endif
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6381,8 +6392,8 @@ FSurfaceGenerationParams UVoxelStrateManager::GetSurfaceParamsForChunk(const FIn
}
}
#endif
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6408,8 +6419,8 @@ FVerticalShaftParams UVoxelStrateManager::GetVerticalShaftParamsForChunk(const F
}
}
#endif
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6435,8 +6446,8 @@ FFloatingIslandParams UVoxelStrateManager::GetFloatingIslandParamsForChunk(const
}
}
#endif
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6470,14 +6481,14 @@ FBiomeContext UVoxelStrateManager::GetBiomeContextForChunk(const FIntVector& Chu
bool UVoxelStrateManager::GetStrateUnrealZRange(float WorldZ, float& OutTopZ, float& OutBottomZ) const
{
const int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / CHUNK_SIZE);
const int32 ChunkZ = FMath::FloorToInt((WorldZ / VOXEL_SIZE) / StrateLayoutChunkWidthVoxels);
const int32 SlotIdx = FindSlotIndexForChunkZ(ChunkZ);
if (SlotIdx < 0) return false;
const FStrateSlot& Slot = StrateLayout[SlotIdx];
// Voxel-space Z bounds → Unreal units. Ceiling = top chunk's upper edge.
OutTopZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE * VOXEL_SIZE;
OutBottomZ = (float)(Slot.BottomChunkZ) * CHUNK_SIZE * VOXEL_SIZE;
OutTopZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels * VOXEL_SIZE;
OutBottomZ = (float)(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels * VOXEL_SIZE;
return true;
}
@@ -6490,8 +6501,8 @@ FStrateDisturbanceParams UVoxelStrateManager::GetDisturbanceParamsForChunk(const
}
const FStrateSlot& Slot = StrateLayout[SlotIdx];
FStrateDisturbanceParams Result = Slot.Definition->Disturbances;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
return Result;
}
@@ -6523,8 +6534,8 @@ float UVoxelStrateManager::GetWaterLevelWorldZForChunk(const FIntVector& ChunkCo
}
if (Rel <= 0.0f) return -FLT_MAX;
const float BottomZ = (float)(Slot.BottomChunkZ) * CHUNK_SIZE;
const float TopZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
const float BottomZ = (float)(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
const float TopZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
return FMath::Lerp(BottomZ, TopZ, FMath::Clamp(Rel, 0.0f, 1.0f));
}
@@ -6568,8 +6579,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
FindComposerOverride(Slot.StrateIndex))
{
FStrateGenerationParams Result = Override->Params.TunnelNetworkParams;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
// A candidate is a hard replacement of this slot. Do not blend its rolled vector with an
// authored neighbour at a boundary; this is also how the offline fixture measures it.
return ResolveFinalParams(Result);
@@ -6579,8 +6590,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
if (SeasonStrates.IsValidIndex(SlotIdx))
{
FStrateGenerationParams Result = SeasonStrates[SlotIdx].Params.TunnelNetworkParams;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
Result.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * StrateLayoutChunkWidthVoxels;
// Season vectors are already final, measured slot records. Vertical structural posts own
// their boundary; blending them with another selected recipe would describe neither one.
return ResolveFinalParams(Result);
@@ -6595,10 +6606,17 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
// to seal the top and bottom with solid rock. This prevents caves from
// carving through strate boundaries.
//
// TopChunkZ=0, CHUNK_SIZE=32: top of the strate = chunk 0's top edge = voxel Z=32
// TopChunkZ=0: top of the strate is one resolved layout chunk above zero.
// BottomChunkZ=-3: bottom of the strate = chunk -3's bottom edge = voxel Z=-3*32 = -96
BaseParams.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * CHUNK_SIZE;
BaseParams.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * CHUNK_SIZE;
BaseParams.StrateTopWorldZ = (float)(Slot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
BaseParams.StrateBottomWorldZ = (float)(Slot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
// Core composes one field per strate. For this assembly, Gradient and Interleaved
// metadata use the declared Hard fallback across legacy parameter lookups too.
if (bHardTransitionFallbackForAssembly)
{
return ResolveFinalParams(BaseParams);
}
//=========================================================================
// BOUNDARY BLENDING (transition-type aware)
@@ -6671,8 +6689,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
if (EffectiveBlend > 0 && DistFromBottom < EffectiveBlend)
{
FStrateGenerationParams BelowParams = BuildParamsFromDefinition(BelowSlot.Definition);
BelowParams.StrateTopWorldZ = (float)(BelowSlot.TopChunkZ + 1) * CHUNK_SIZE;
BelowParams.StrateBottomWorldZ = (float)(BelowSlot.BottomChunkZ) * CHUNK_SIZE;
BelowParams.StrateTopWorldZ = (float)(BelowSlot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
BelowParams.StrateBottomWorldZ = (float)(BelowSlot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
// Linear alpha: 0 at EffectiveBlend chunks away, 1 at the boundary
float Alpha = 1.0f - ((float)DistFromBottom / (float)EffectiveBlend);
@@ -6709,8 +6727,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
if (DistFromBottom < CheckRange)
{
FStrateGenerationParams BelowParams = BuildParamsFromDefinition(BelowSlot.Definition);
BelowParams.StrateTopWorldZ = (float)(BelowSlot.TopChunkZ + 1) * CHUNK_SIZE;
BelowParams.StrateBottomWorldZ = (float)(BelowSlot.BottomChunkZ) * CHUNK_SIZE;
BelowParams.StrateTopWorldZ = (float)(BelowSlot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
BelowParams.StrateBottomWorldZ = (float)(BelowSlot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
// Sample 3D Perlin noise to warp the boundary position.
// XY frequency 0.15 gives medium-scale variation (~6-7 chunks per cycle).
@@ -6770,8 +6788,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
if (EffectiveBlend > 0 && DistFromTop < EffectiveBlend)
{
FStrateGenerationParams AboveParams = BuildParamsFromDefinition(AboveSlot.Definition);
AboveParams.StrateTopWorldZ = (float)(AboveSlot.TopChunkZ + 1) * CHUNK_SIZE;
AboveParams.StrateBottomWorldZ = (float)(AboveSlot.BottomChunkZ) * CHUNK_SIZE;
AboveParams.StrateTopWorldZ = (float)(AboveSlot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
AboveParams.StrateBottomWorldZ = (float)(AboveSlot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
float Alpha = 1.0f - ((float)DistFromTop / (float)EffectiveBlend);
Alpha = FMath::Clamp(Alpha, 0.0f, 1.0f);
@@ -6792,8 +6810,8 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
if (DistFromTop < CheckRange)
{
FStrateGenerationParams AboveParams = BuildParamsFromDefinition(AboveSlot.Definition);
AboveParams.StrateTopWorldZ = (float)(AboveSlot.TopChunkZ + 1) * CHUNK_SIZE;
AboveParams.StrateBottomWorldZ = (float)(AboveSlot.BottomChunkZ) * CHUNK_SIZE;
AboveParams.StrateTopWorldZ = (float)(AboveSlot.TopChunkZ + 1) * StrateLayoutChunkWidthVoxels;
AboveParams.StrateBottomWorldZ = (float)(AboveSlot.BottomChunkZ) * StrateLayoutChunkWidthVoxels;
// Same noise function but with a different seed offset to avoid
// symmetry between top and bottom boundaries of adjacent strates
@@ -391,6 +391,11 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Strates", meta = (ClampMin = "0", ClampMax = "16"))
int32 InterStrateGapChunks = 0;
// Physical width of one vertical strate-layout chunk, independent of Core's 32^3 tile.
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)
//=========================================================================
@@ -234,6 +234,10 @@ public:
*/
UFUNCTION(BlueprintCallable, Category = "Strate")
UVoxelStrateDefinition* GetStrateAt(float WorldZ) const;
float GetStrateLayoutChunkWidthVoxels() const { return StrateLayoutChunkWidthVoxels; }
uint32 GetStrateLayoutChunkSizeMicrometres() const { return StrateLayoutChunkSizeMicrometres; }
void SetHardTransitionFallbackForAssembly(bool bEnabled)
{ bHardTransitionFallbackForAssembly = bEnabled; }
/**
* Get the strate index at a world Z coordinate.
@@ -492,6 +496,10 @@ protected:
// The stacked strate layout (index 0 = topmost strate)
UPROPERTY()
TArray<FStrateSlot> StrateLayout;
// Resolved once from VoxelSettings::StrateLayoutChunkSizeMeters during Initialize().
float StrateLayoutChunkWidthVoxels = 32.0f;
uint32 StrateLayoutChunkSizeMicrometres = 8000000;
bool bHardTransitionFallbackForAssembly = false;
// Parallel to StrateLayout only while a cooked season is active. Plain immutable data is
// copied into worker-local caches on the existing layout-versioned refetch path.