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

+176 -89
View File
@@ -33,6 +33,7 @@
class FStandaloneShapeWorld
{
public:
const VoxelForgeCore::ResolvedLayoutSnapshotV2* ResolvedLayout=nullptr;
struct FResolvedStratumBounds
{
double BottomZ=0.0;
@@ -47,12 +48,53 @@ public:
: static_cast<double>(Owner->CoreVoxelSizeMicrons) / 10000.0;
}
double GetCoreVoxelsPerOwnerChunk() const
double GetResolvedLayoutChunkWidthVoxels() const
{
const double OwnerVoxelSizeCm = Owner == nullptr || Owner->OwnerVoxelSizeMicrons == 0
? 0.5 * static_cast<double>(VOXEL_SIZE)
: static_cast<double>(Owner->OwnerVoxelSizeMicrons) / 10000.0;
return static_cast<double>(CHUNK_SIZE) * OwnerVoxelSizeCm / GetCoreVoxelSizeCm();
return ResolvedLayout&&ResolvedLayout->strateLayoutChunkWidthSubvoxels>0
?static_cast<double>(ResolvedLayout->strateLayoutChunkWidthSubvoxels)
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel
:Owner?static_cast<double>(Owner->StrateLayoutChunkWidthSubvoxels)
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel:0.0;
}
const VoxelForgeCore::ResolvedLayoutStratumV2* FindResolvedLayoutStratum(int32 StrateIndex) const
{
if(!ResolvedLayout)return nullptr;
const auto it=std::find_if(ResolvedLayout->strates.begin(),ResolvedLayout->strates.end(),
[StrateIndex](const auto& item){return item.strateIndex==StrateIndex;});
return it==ResolvedLayout->strates.end()?nullptr:&*it;
}
bool IsInsideResolvedStrateBoundaryBand(float Z) const
{
if(!ResolvedLayout||!Owner||!Owner->Manager||!std::isfinite(Z))return false;
const double worldZCentimetres=static_cast<double>(Z)*GetCoreVoxelSizeCm();
const int32 strateIndex=Owner->Manager->GetStrateIndex(static_cast<float>(worldZCentimetres));
const auto* stratum=FindResolvedLayoutStratum(strateIndex);
if(!stratum)return false;
const double zMicrometres=static_cast<double>(Z)*Owner->CoreVoxelSizeMicrons;
const auto solidAt=[&](const VoxelForgeCore::ResolvedLayoutBandV2& band)
{return band.presentInCurrentField&&zMicrometres>=band.bottomMicrometres
&&zMicrometres<band.topMicrometres;};
return solidAt(stratum->ceiling)||solidAt(stratum->bedrock);
}
float ApplyResolvedStrateBoundaryBand(float density,float X,float Y,float Z) const
{
(void)X;(void)Y;
if(!ResolvedLayout||!Owner||!Owner->Manager||!std::isfinite(Z))return density;
const double worldZCentimetres=static_cast<double>(Z)*GetCoreVoxelSizeCm();
const int32 strateIndex=Owner->Manager->GetStrateIndex(static_cast<float>(worldZCentimetres));
const auto* stratum=FindResolvedLayoutStratum(strateIndex);
if(!stratum)return density;
const double zMicrometres=static_cast<double>(Z)*Owner->CoreVoxelSizeMicrons;
const auto solidAt=[&](const VoxelForgeCore::ResolvedLayoutBandV2& band)
{return band.presentInCurrentField&&zMicrometres>=band.bottomMicrometres
&&zMicrometres<band.topMicrometres;};
const auto* band=solidAt(stratum->ceiling)?&stratum->ceiling
:solidAt(stratum->bedrock)?&stratum->bedrock:nullptr;
if(!band)return density;
return -std::max(0.05f,static_cast<float>(band->solidDensityQ16)/65536.0f);
}
static bool MakeResolvedStratumBounds(double BottomZ,double TopZ,double SealThickness,
@@ -67,6 +109,17 @@ public:
bool GetResolvedStratumBounds(const FStrateSlot& Slot,FResolvedStratumBounds& Out) const
{
if(!Owner||!Owner->Manager)return false;
if(const auto* resolved=FindResolvedLayoutStratum(Slot.StrateIndex))
{
const double coreMicrons=std::max<std::uint32_t>(1,Owner->CoreVoxelSizeMicrons);
const double bottom=static_cast<double>(resolved->interiorBottomMicrometres)/coreMicrons;
const double top=static_cast<double>(resolved->interiorTopMicrometres)/coreMicrons;
const double ceiling=static_cast<double>(resolved->ceiling.topMicrometres
-resolved->ceiling.bottomMicrometres)/coreMicrons;
const double bedrock=static_cast<double>(resolved->bedrock.topMicrometres
-resolved->bedrock.bottomMicrometres)/coreMicrons;
return MakeResolvedStratumBounds(bottom,top,std::max(ceiling,bedrock),Out);
}
const int32 MidChunkZ=(Slot.TopChunkZ+Slot.BottomChunkZ)/2;
if(bUseNativeManifestFields&&WorldAssembly
&&Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,MidChunkZ))
@@ -85,8 +138,9 @@ public:
}
const FStrateGenerationParams Params=Owner->Manager->GetGenerationParams(
FIntVector(0,0,MidChunkZ));
return MakeResolvedStratumBounds(static_cast<double>(Slot.BottomChunkZ)*CHUNK_SIZE,
static_cast<double>(Slot.TopChunkZ+1)*CHUNK_SIZE,
const double chunkWidth=GetResolvedLayoutChunkWidthVoxels();
return MakeResolvedStratumBounds(static_cast<double>(Slot.BottomChunkZ)*chunkWidth,
static_cast<double>(Slot.TopChunkZ+1)*chunkWidth,
static_cast<double>(Params.BoundarySealThickness),Out);
}
@@ -355,11 +409,12 @@ public:
bool bValidatePassageRoutes=true,
const std::vector<VoxelForgeCore::DetailBandTileRegion>& InGpuVisualDetailTiles={},
const std::vector<VoxelForgeCore::DetailBandTileRegion>& InCpuVisualDetailTiles={},
const VoxelForgeCore::WorldAssemblyManifest* InWorldAssembly=nullptr)
const VoxelForgeCore::WorldAssemblyManifest* InWorldAssembly=nullptr,
const VoxelForgeCore::ResolvedLayoutSnapshotV2* InResolvedLayout=nullptr)
{
using Clock=std::chrono::steady_clock;
const auto TotalStart=Clock::now();
Owner=&World;WorldAssembly=InWorldAssembly;
Owner=&World;WorldAssembly=InWorldAssembly;ResolvedLayout=InResolvedLayout;
WorkerCount=std::clamp(Workers,1,16);DetailVariant=Variant;
Scheduler=InScheduler;RoundTunnelsEnabled=bInRoundTunnels;
bUseNativeManifestFields=true;
@@ -516,10 +571,11 @@ public:
const FIntVector& ClipMaxVoxels,EDetailVariant Variant,
bool bInRoundTunnels,
const VoxelForgeCore::WorldAssemblyManifest* InWorldAssembly,
const VoxelForgeCore::ResolvedLayoutSnapshotV2* InResolvedLayout,
std::string& Error)
{
using Clock=std::chrono::steady_clock;
Owner=&World;WorldAssembly=InWorldAssembly;
Owner=&World;WorldAssembly=InWorldAssembly;ResolvedLayout=InResolvedLayout;
WorkerCount=1;DetailVariant=Variant;Scheduler=nullptr;
// Analytic pages retain their bounded owner bins for route bookkeeping. Once published,
// the Core world binds each page to its base shape as the single density evaluator.
@@ -555,18 +611,20 @@ public:
void InitializeNativeSurfaceOnly(FStandaloneOwnerWorld& World,
const VoxelForgeCore::WorldAssemblyManifest* InWorldAssembly,
const VoxelForgeCore::ResolvedLayoutSnapshotV2* InResolvedLayout,
const FIntVector& InMinimum,const FIntVector& InMaximum)
{
Owner=&World;WorldAssembly=InWorldAssembly;
Owner=&World;WorldAssembly=InWorldAssembly;ResolvedLayout=InResolvedLayout;
NativeOnlyMinimum=InMinimum;NativeOnlyMaximum=InMaximum;
bUseNativeManifestFields=true;bNativeOnly=true;bAnalyticOnly=true;Built=true;
NativeOnlyViolations.store(0,std::memory_order_relaxed);
}
bool BuildShapeDependencyProof(FStandaloneOwnerWorld& World,
const FIntVector& MinVoxels,const FIntVector& MaxVoxels,bool bInRoundTunnels)
const FIntVector& MinVoxels,const FIntVector& MaxVoxels,bool bInRoundTunnels,
const VoxelForgeCore::ResolvedLayoutSnapshotV2* InResolvedLayout=nullptr)
{
Owner=&World;WorkerCount=1;RoundTunnelsEnabled=bInRoundTunnels;
Owner=&World;ResolvedLayout=InResolvedLayout;WorkerCount=1;RoundTunnelsEnabled=bInRoundTunnels;
bAnalyticOnly=true;bNativeOnly=false;Stats={};
for(int32 Axis=0;Axis<3;++Axis)
{
@@ -1071,19 +1129,19 @@ public:
float NativeDensity=0.0f;
if(!NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
{RecordNativeOnlyViolation();return 0.0f;}
return NativeDensity;
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
}
float IslandDensity=0.0f;
if(NativeFloatingIslandDensityAt(X,Y,Z,IslandDensity))
{
++ThreadClassificationStats().NativeFloatFieldCalls;
return ApplyEditBrushes(IslandDensity,X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(IslandDensity,X,Y,Z),X,Y,Z);
}
float NativeDensity=0.0f;
if(NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
{
++ThreadClassificationStats().NativeFloatFieldCalls;
return ApplyEditBrushes(NativeDensity,X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
}
if(UsesLegacySurfaceField(X,Y,Z))
return LegacySurfaceDensityAt(X,Y,Z);
@@ -1104,7 +1162,7 @@ public:
if(UsesLegacySurfaceField(X,Y,Z))return "SurfaceWorld-legacy";
if(Owner&&Owner->Manager)
{
const int32 ChunkZ=FMath::FloorToInt(Z/static_cast<float>(CHUNK_SIZE));
const int32 ChunkZ=FMath::FloorToInt(Z/static_cast<float>(GetResolvedLayoutChunkWidthVoxels()));
const ECaveGeneratorType Type=Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ));
switch(Type)
{
@@ -1133,21 +1191,21 @@ public:
if(!NativeSurfaceDensityAt(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z),NativeDensity))
{RecordNativeOnlyViolation();return 0.0f;}
return NativeDensity;
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
}
float IslandDensity=0.0f;
if(NativeFloatingIslandDensityAt(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z),IslandDensity))
{
++ThreadClassificationStats().NativeLatticeFieldCalls;
return ApplyEditBrushes(IslandDensity,X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(IslandDensity,X,Y,Z),X,Y,Z);
}
float NativeDensity=0.0f;
if(NativeSurfaceDensityAt(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z),NativeDensity))
{
++ThreadClassificationStats().NativeLatticeFieldCalls;
return ApplyEditBrushes(NativeDensity,X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
}
if(UsesLegacySurfaceField(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z)))
@@ -1171,19 +1229,19 @@ public:
float NativeDensity=0.0f;
if(!NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
{RecordNativeOnlyViolation();return 0.0f;}
return NativeDensity;
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
}
float IslandDensity=0.0f;
if(NativeFloatingIslandDensityAt(X,Y,Z,IslandDensity))
{
++ThreadClassificationStats().NativeUnclampedFieldCalls;
return ApplyEditBrushes(IslandDensity,X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(IslandDensity,X,Y,Z),X,Y,Z);
}
float NativeDensity=0.0f;
if(NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
{
++ThreadClassificationStats().NativeUnclampedFieldCalls;
return ApplyEditBrushes(NativeDensity,X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
}
if(UsesLegacySurfaceField(X,Y,Z))
return LegacySurfaceDensityAt(X,Y,Z);
@@ -1224,6 +1282,12 @@ public:
OutDensity[Lane]=DensityAtLattice(XStart+Lane*XStep,Y,Z);
return;
}
if(IsInsideResolvedStrateBoundaryBand(static_cast<float>(Z)))
{
for(int32 Lane=0;Lane<Count;++Lane)
OutDensity[Lane]=DensityAtLattice(XStart+Lane*XStep,Y,Z);
return;
}
bool bHasLegacySurfaceSample=false;
bool bHasNativeIslandSample=false;
for(int32 Lane=0;Lane<Count;++Lane)
@@ -1307,13 +1371,14 @@ private:
float Density=static_cast<float>(Proxy)*(1.0f/256.0f);
if((Density<0.0f)!=bRock)
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),1.0f/256.0f);
return ApplyEditBrushes(Density,FX,FY,FZ);
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(Density,FX,FY,FZ),FX,FY,FZ);
}
// Match the fixed fallback to the edge of the stored proxy band. The
// displacement is tapered to zero there, so this avoids a magnitude
// jump along the candidate bit-mask boundary.
constexpr float ProxyBandEdge=DetailSupremum+0.125f;
return ApplyEditBrushes(bRock?-ProxyBandEdge:ProxyBandEdge,FX,FY,FZ);
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(
bRock?-ProxyBandEdge:ProxyBandEdge,FX,FY,FZ),FX,FY,FZ);
}
float AnalyticDensityAt(float X,float Y,float Z,bool bClampNearZero=true,
@@ -1329,14 +1394,21 @@ private:
if(OutFloorInfluence)*OutFloorInfluence=0.0f;
float IslandDensity=0.0f;
if(NativeFloatingIslandDensityAt(X,Y,Z,IslandDensity))
return bApplyEdits?ApplyEditBrushes(IslandDensity,X,Y,Z):IslandDensity;
{
const float composed=ApplyResolvedStrateBoundaryBand(IslandDensity,X,Y,Z);
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
}
if(UsesLegacySurfaceField(X,Y,Z))
{
float NativeDensity=0.0f;
if(NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
{
NativeDensity=ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z);
return bApplyEdits?ApplyEditBrushes(NativeDensity,X,Y,Z):NativeDensity;
}
const float Density=Owner->Generator->GetDensityAt(X,Y,Z);
return bApplyEdits?ApplyEditBrushes(Density,X,Y,Z):Density;
const float composed=ApplyResolvedStrateBoundaryBand(Density,X,Y,Z);
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
}
const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]
&&Z>=Min[2]&&Z<=Max[2];
@@ -1350,7 +1422,8 @@ private:
if((Density<0.0f)!=bRock||(bClampNearZero&&std::abs(Density)<0.05f))
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),
bClampNearZero?0.05f:1.0e-7f);
return bApplyEdits?ApplyEditBrushes(Density,X,Y,Z):Density;
const float composed=ApplyResolvedStrateBoundaryBand(Density,X,Y,Z);
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
}
ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false,bCriticalAir=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
@@ -1379,6 +1452,7 @@ private:
if((Density<0.0f)!=bRock||(bClampNearZero&&std::abs(Density)<0.05f))
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),
bClampNearZero?0.05f:1.0e-7f);
Density=ApplyResolvedStrateBoundaryBand(Density,X,Y,Z);
return bApplyEdits?ApplyEditBrushes(Density,X,Y,Z):Density;
}
@@ -1388,10 +1462,11 @@ public:
{
if(SharedDensitySource)
return SharedDensitySource->GetNativeSurfaceHeightsAt(X,Y,ChunkZ,OutGroundZ,OutCeilingZ);
if(!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
if(!WorldAssembly||!ResolvedLayout||!Owner||!Owner->Manager||!Owner->Generator
||!std::isfinite(X)||!std::isfinite(Y))return false;
const std::int64_t chunkMinimum=static_cast<std::int64_t>(ChunkZ)*CHUNK_SIZE;
const std::int64_t chunkMaximum=chunkMinimum+CHUNK_SIZE;
const double ChunkWidth=GetResolvedLayoutChunkWidthVoxels();
const std::int64_t chunkMinimum=static_cast<std::int64_t>(std::llround(ChunkZ*ChunkWidth));
const std::int64_t chunkMaximum=static_cast<std::int64_t>(std::llround((ChunkZ+1)*ChunkWidth));
if(bNativeOnly&&(!NativeOnlyContainsColumn(X,Y)
||chunkMaximum<NativeOnlyMinimum.Z||chunkMinimum>NativeOnlyMaximum.Z
||Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
@@ -1400,7 +1475,8 @@ public:
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
!=ECaveGeneratorType::SurfaceWorld)return false;
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
static_cast<float>((static_cast<double>(ChunkZ)*CHUNK_SIZE+CHUNK_SIZE/2)*VOXEL_SIZE));
static_cast<float>((static_cast<double>(ChunkZ)+0.5)
*ResolvedLayout->strateLayoutChunkSizeMicrometres/10000.0));
const auto* Plan=FindWorldAssemblyStratum(StrateIndex,ChunkZ);
if(!Plan)return false;
constexpr double Scale=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
@@ -1413,7 +1489,7 @@ public:
const VoxelForgeCore::Heightfield2D Block(WorldAssembly->recipe,*Plan);
if(!Block.isValid())return false;
const auto Column=Block.point(ToFixed(X),ToFixed(Y),
static_cast<int64>(ChunkZ)*CHUNK_SIZE*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
static_cast<int64>(ChunkZ)*ResolvedLayout->strateLayoutChunkWidthSubvoxels);
OutGroundZ=static_cast<float>(Column.groundHeightSubvoxels/Scale);
OutCeilingZ=static_cast<float>(Column.capHeightSubvoxels/Scale);
return std::isfinite(OutGroundZ)&&std::isfinite(OutCeilingZ);
@@ -1429,10 +1505,9 @@ public:
const int32 Pad=std::max(Step,1);
const int32 BMin=Origin.Z-Pad;
const int32 BMax=Origin.Z+(Cells+1)*Step;
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin)
/static_cast<float>(CHUNK_SIZE));
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax)
/static_cast<float>(CHUNK_SIZE));
const float LayoutWidth=static_cast<float>(GetResolvedLayoutChunkWidthVoxels());
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin)/LayoutWidth);
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax)/LayoutWidth);
const VoxelForgeCore::WorldAssemblyStratumPlanV1* Plan=nullptr;
for(int32 ChunkZ=FirstChunk;ChunkZ<=LastChunk;++ChunkZ)
{
@@ -1474,53 +1549,32 @@ public:
const VoxelForgeCore::WorldAssemblyFloatingIslandPlanV1* IslandPlan=nullptr;
bool bSurfaceOverlap=false,bIslandOverlap=false,bOtherStrateOverlap=false;
bool bIslandOtherStrateOverlap=false,bBadPlan=false;
if(Owner&&Owner->Manager)
if(Owner&&Owner->Manager&&ResolvedLayout)
{
// Keep the delivered SurfaceWorld chunk mapping unchanged.
// Conserver le mapping de chunks SurfaceWorld deja livre.
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin[2])
/static_cast<float>(CHUNK_SIZE));
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax[2])
/static_cast<float>(CHUNK_SIZE));
const float LayoutWidth=static_cast<float>(GetResolvedLayoutChunkWidthVoxels());
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin[2])/LayoutWidth);
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax[2])/LayoutWidth);
for(int32 ChunkZ=FirstChunk;ChunkZ<=LastChunk;++ChunkZ)
{
const ECaveGeneratorType Type=Owner->Manager->GetGeneratorTypeForChunk(
FIntVector(0,0,ChunkZ));
const ECaveGeneratorType Type=Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ));
const int32 StrateIndex=Owner->Manager->GetStrateIndex(static_cast<float>(
(static_cast<double>(ChunkZ)+0.5)*ResolvedLayout->strateLayoutChunkSizeMicrometres/10000.0));
if(Type==ECaveGeneratorType::SurfaceWorld)
{
bSurfaceOverlap=true;
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
static_cast<float>((static_cast<double>(ChunkZ)*CHUNK_SIZE+CHUNK_SIZE/2)*VOXEL_SIZE));
bSurfaceOverlap=true;bIslandOtherStrateOverlap=true;
const auto* Candidate=FindWorldAssemblyStratum(StrateIndex,ChunkZ);
if(!Candidate||(SurfacePlan&&SurfacePlan!=Candidate))
bBadPlan=true;
if(!Candidate||(SurfacePlan&&SurfacePlan!=Candidate))bBadPlan=true;
SurfacePlan=Candidate;
}
else if(Type!=ECaveGeneratorType::FloatingIslands)
bOtherStrateOverlap=true;
}
// Island strata preserve owner-authored physical bounds through the resolved scale ratio.
// Les strates d'iles conservent les bornes physiques owner via le rapport de resolution.
const double CoreVoxelsPerIslandChunk=GetCoreVoxelsPerOwnerChunk();
const int32 FirstIslandChunk=FMath::FloorToInt(static_cast<float>(BMin[2])
/static_cast<float>(CoreVoxelsPerIslandChunk));
const int32 LastIslandChunk=FMath::FloorToInt(static_cast<float>(BMax[2])
/static_cast<float>(CoreVoxelsPerIslandChunk));
for(int32 ChunkZ=FirstIslandChunk;ChunkZ<=LastIslandChunk;++ChunkZ)
{
const ECaveGeneratorType Type=Owner->Manager->GetGeneratorTypeForChunk(
FIntVector(0,0,ChunkZ));
if(Type==ECaveGeneratorType::FloatingIslands)
else if(Type==ECaveGeneratorType::FloatingIslands)
{
bIslandOverlap=true;
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
static_cast<float>((static_cast<double>(ChunkZ)*CHUNK_SIZE+CHUNK_SIZE/2)*VOXEL_SIZE));
bIslandOverlap=true;bOtherStrateOverlap=true;
const auto* Candidate=FindWorldAssemblyFloatingIslandStratum(StrateIndex,ChunkZ);
if(!Candidate||(IslandPlan&&IslandPlan!=Candidate))bBadPlan=true;
IslandPlan=Candidate;
}
else bIslandOtherStrateOverlap=true;
else
{bOtherStrateOverlap=true;bIslandOtherStrateOverlap=true;}
}
}
const auto Finish=[&](EVoxelTileClass Verdict,bool bUnknown=false)
@@ -1536,6 +1590,25 @@ public:
else ++Counts.MixedBoxes;
return Result;
};
if(!EditBrushes.empty()&&EditBoundsMin.X<=BMax[0]&&EditBoundsMax.X>=BMin[0]
&&EditBoundsMin.Y<=BMax[1]&&EditBoundsMax.Y>=BMin[1]
&&EditBoundsMin.Z<=BMax[2]&&EditBoundsMax.Z>=BMin[2])
return Finish(EVoxelTileClass::Mixed,true);
if(ResolvedLayout&&Owner)
{
const double ZMinMicrometres=static_cast<double>(BMin[2])*Owner->CoreVoxelSizeMicrons;
const double ZMaxMicrometres=static_cast<double>(BMax[2])*Owner->CoreVoxelSizeMicrons;
for(const auto& Stratum:ResolvedLayout->strates)
for(const auto* Band:{&Stratum.ceiling,&Stratum.bedrock})
if(Band->presentInCurrentField&&ZMaxMicrometres>=Band->bottomMicrometres
&&ZMinMicrometres<Band->topMicrometres)
{
if(ZMinMicrometres>=Band->bottomMicrometres
&&ZMaxMicrometres<Band->topMicrometres)
return Finish(EVoxelTileClass::AllSolid);
return Finish(EVoxelTileClass::Mixed);
}
}
if(bNativeOnly)
{
const bool bInsideProofBox=BMin[0]>=NativeOnlyMinimum.X
@@ -1555,10 +1628,6 @@ public:
||(bIslandOverlap&&(bIslandOtherStrateOverlap
||(!bSurfaceOverlap&&bOtherStrateOverlap))))
return Finish(EVoxelTileClass::Mixed,true);
if(!EditBrushes.empty()&&EditBoundsMin.X<=BMax[0]&&EditBoundsMax.X>=BMin[0]
&&EditBoundsMin.Y<=BMax[1]&&EditBoundsMax.Y>=BMin[1]
&&EditBoundsMin.Z<=BMax[2]&&EditBoundsMax.Z>=BMin[2])
return Finish(EVoxelTileClass::Mixed,true);
// The native interval path below is the exact field selected first by DensityAtLattice
// for SurfaceWorld and FloatingIslands. Legacy analytic primitives/layer bounds cannot
// modify that field; only the explicit edit transaction above can. Keep their guards on
@@ -2151,13 +2220,16 @@ private:
bool NativeSurfaceDensityAt(float X,float Y,float Z,float& OutDensity) const
{
if(!bUseNativeManifestFields||!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
if(!bUseNativeManifestFields||!WorldAssembly||!ResolvedLayout||!Owner||!Owner->Manager||!Owner->Generator
||!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z))return false;
const int32 ChunkZ=FMath::FloorToInt(Z/static_cast<float>(CHUNK_SIZE));
const float LayoutWidth=static_cast<float>(GetResolvedLayoutChunkWidthVoxels());
if(!(LayoutWidth>0.0f))return false;
const int32 ChunkZ=FMath::FloorToInt(Z/LayoutWidth);
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
!=ECaveGeneratorType::SurfaceWorld)return false;
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
static_cast<float>((static_cast<double>(ChunkZ)*CHUNK_SIZE+CHUNK_SIZE/2)*VOXEL_SIZE));
static_cast<float>((static_cast<double>(ChunkZ)+0.5)
*ResolvedLayout->strateLayoutChunkSizeMicrometres/10000.0));
const auto* Plan=FindWorldAssemblyStratum(StrateIndex,ChunkZ);
if(!Plan)return false;
constexpr double Scale=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
@@ -2177,16 +2249,18 @@ private:
bool NativeFloatingIslandDensityAt(float X,float Y,float Z,float& OutDensity) const
{
if(!bUseNativeManifestFields||!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
||!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z))
||!ResolvedLayout||!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z))
return false;
// Convert Core query Z back to the owner's legacy chunk coordinate.
// Convertir Z du Core vers la coordonnee de chunk legacy du proprietaire.
const double CoreVoxelsPerLegacyChunk=GetCoreVoxelsPerOwnerChunk();
const int32 ChunkZ=FMath::FloorToInt(Z/static_cast<float>(CoreVoxelsPerLegacyChunk));
const double LayoutChunkWidth=GetResolvedLayoutChunkWidthVoxels();
if(!(LayoutChunkWidth>0.0))return false;
const int32 ChunkZ=FMath::FloorToInt(Z/static_cast<float>(LayoutChunkWidth));
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
!=ECaveGeneratorType::FloatingIslands)return false;
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
static_cast<float>((static_cast<double>(ChunkZ)*CHUNK_SIZE+CHUNK_SIZE/2)*VOXEL_SIZE));
static_cast<float>((static_cast<double>(ChunkZ)+0.5)
*ResolvedLayout->strateLayoutChunkSizeMicrometres/10000.0));
const auto* Plan=FindWorldAssemblyFloatingIslandStratum(StrateIndex,ChunkZ);
if(!Plan)return false;
constexpr double Scale=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
@@ -2212,7 +2286,7 @@ private:
Density=std::min(Density,-std::max<std::int64_t>(1,Top-Zq));
OutDensity=static_cast<float>(static_cast<double>(Density)/Scale);
}
else
else if(Plan->manifest.recipe.geometryVersion<4)
{
OutDensity=static_cast<float>(static_cast<double>(Density)/Scale);
FResolvedStratumBounds Bounds;
@@ -2226,15 +2300,16 @@ private:
static_cast<float>(Bounds.SealThickness),Params.BaseDensity);
OutDensity=-InternalDensity;
}
else OutDensity=static_cast<float>(static_cast<double>(Density)/Scale);
return std::isfinite(OutDensity);
}
bool UsesLegacySurfaceField(float X,float Y,float Z) const
{
if(!Owner||!Owner->Manager||!Owner->Generator)return false;
const FIntVector ChunkCoord(FMath::FloorToInt(X/static_cast<float>(CHUNK_SIZE)),
FMath::FloorToInt(Y/static_cast<float>(CHUNK_SIZE)),
FMath::FloorToInt(Z/static_cast<float>(CHUNK_SIZE)));
const double LayoutChunkWidth=GetResolvedLayoutChunkWidthVoxels();
if(!(LayoutChunkWidth>0.0))return false;
const FIntVector ChunkCoord(0,0,FMath::FloorToInt(Z/static_cast<float>(LayoutChunkWidth)));
return Owner->Manager->GetGeneratorTypeForChunk(ChunkCoord)
==ECaveGeneratorType::SurfaceWorld;
}
@@ -2244,7 +2319,8 @@ private:
// SurfaceWorld consumes biome overrides, terrain ops, water level and its sky cap in
// the legacy stack. Reuse that immutable evaluator as an exact temporary bridge.
// SurfaceWorld lit biomes, ops de terrain, niveau d'eau et plafond; reutiliser ce champ.
return ApplyEditBrushes(Owner->Generator->GetDensityAt(X,Y,Z),X,Y,Z);
const float density=ApplyResolvedStrateBoundaryBand(Owner->Generator->GetDensityAt(X,Y,Z),X,Y,Z);
return ApplyEditBrushes(density,X,Y,Z);
}
float ApplyEditBrushes(float Density,double X,double Y,double Z) const
@@ -3571,8 +3647,9 @@ private:
auto Unit=[&](uint32 H){return static_cast<double>(Hash(H)>>8)*(1.0/16777216.0);};
for(const FStrateSlot& Slot:Layout)
{
const double ZBottom=static_cast<double>(Slot.BottomChunkZ)*CHUNK_SIZE;
const double ZTop=static_cast<double>(Slot.TopChunkZ+1)*CHUNK_SIZE;
const double LayoutChunkWidth=GetResolvedLayoutChunkWidthVoxels();
const double ZBottom=static_cast<double>(Slot.BottomChunkZ)*LayoutChunkWidth;
const double ZTop=static_cast<double>(Slot.TopChunkZ+1)*LayoutChunkWidth;
for(int32 GY=GY0;GY<=GY1;++GY)for(int32 GX=GX0;GX<=GX1;++GX)
{
uint32 H=Hash(Seed^static_cast<uint32>(GX)*0x9E3779B9u
@@ -4953,6 +5030,16 @@ public:
OutDensity[Lane]=DensityAtLattice(X,Y,ZStart+Lane*ZStep);
return;
}
bool bHasResolvedBoundaryBand=false;
for(int32 Lane=0;Lane<Count&&!bHasResolvedBoundaryBand;++Lane)
bHasResolvedBoundaryBand=IsInsideResolvedStrateBoundaryBand(
static_cast<float>(ZStart+Lane*ZStep));
if(bHasResolvedBoundaryBand)
{
for(int32 Lane=0;Lane<Count;++Lane)
OutDensity[Lane]=DensityAtLattice(X,Y,ZStart+Lane*ZStep);
return;
}
bool bHasLegacySurfaceSample=false;
for(int32 Lane=0;Lane<Count;++Lane)
bHasLegacySurfaceSample|=UsesLegacySurfaceField(static_cast<float>(X),