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:
1 parent
8753f6dbe4
commit
55c26a05a7
28 files changed
+1432
-579
No files matched your search
@@ -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),
|
||||
|
||||
Reference in new issue
Block a user