wip(L15, side branch): extended far gate + rejected far-mesh candidates (null result)

Refine x2 at LOD4-8 saves 13.6% but breaks the 1 px rule, shading and swaps, and opens holes;
station x2 and ray removal open connector gaps. Not for assembler/world. See Linux15/REPORT.md.

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 03:29:13 +02:00
1 parent a176d0743a
commit a6185b1a3f
17 files changed
+938 -115

No files matched your search

@@ -184,6 +184,12 @@ namespace VoxelForgeCore
std::uint64_t farConnectorColumnEvaluations = 0;
double farStratumColumnComputeMilliseconds = 0.0;
double farConnectorColumnComputeMilliseconds = 0.0;
// Opt-in quality audit at the L14 route-wall stations and four axial rays.
std::uint64_t farConnectorAuditRays = 0;
std::uint64_t farConnectorAuditHoles = 0;
std::uint64_t farConnectorAuditFloating = 0;
std::vector<std::uint64_t> farConnectorAuditHoleIds;
std::vector<std::uint64_t> farConnectorAuditFloatingIds;
std::uint64_t farShellTrianglesAdded = 0;
std::uint64_t horizonGroundTriangles = 0;
std::uint64_t horizonGroundTrianglesRemoved = 0;
@@ -250,7 +256,8 @@ namespace VoxelForgeCore
inline constexpr std::int64_t WorldAssemblySubvoxelsPerVoxel = 256;
inline constexpr std::uint32_t WorldAssemblySchemaVersion = 1;
// Version 6 binds every compiled tile page to the world's base density evaluator.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 6;
// Version 7 adds optional, display-only far-mesh quality declarations.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 7;
enum class FieldChannel : std::uint32_t
{
@@ -312,6 +319,17 @@ namespace VoxelForgeCore
std::int64_t biomeClimateReliefWavelengthSubvoxels = 256 * 3333;
std::int64_t biomeClimateMoistureWavelengthSubvoxels = 256 * 2500;
std::int64_t biomeClimateReliefContrastQ16 = 2ll << 16;
// Optional display-only mesh budget. Zero leaves the L14 far-mesh path unchanged.
// Budget is in 1/1000 pixel at the closest display distance.
std::uint32_t farDisplayGeometryBudgetMillipixels = 0;
// Scale/mask change only display refinement density at the selected LODs.
// A zero scale or mask leaves the current refinement grids intact.
std::uint32_t farRefinementGridScale = 0;
std::uint32_t farRefinementLodMask = 0;
// Connector scale 0 and ray mask 0 preserve the current station/ray pattern.
std::uint32_t farConnectorStationScale = 0;
std::uint32_t farConnectorRayMask = 0;
bool farConnectorReuseStationRadii = false;
std::vector<WorldAssemblyBiomeProfileV1> biomes;
std::uint64_t contentHash = 0;
};
@@ -1099,6 +1117,16 @@ namespace VoxelForgeCore
// Incremente quand le plafond lointain change. Les hashes LOD4-8 incluent cette version;
// densite et hashes LOD0-3 restent inchanges.
std::uint32_t farHorizonVersion = 3;
// Research-only override of the recipe declaration; used by same-LOD controls.
bool overrideFarDisplayMesh = false;
std::uint32_t farDisplayGeometryBudgetMillipixels = 0;
std::uint32_t farRefinementGridScale = 0;
std::uint32_t farRefinementLodMask = 0;
std::uint32_t farConnectorStationScale = 0;
std::uint32_t farConnectorRayMask = 0;
bool farConnectorReuseStationRadii = false;
bool diagnosticRemoveFarConnectorPanels = false;
bool diagnosticAuditFarConnectorCoverage = false;
// Linux research controls for reproducing L5 negative controls. Defaults preserve output.
std::uint32_t diagnosticGlobalOctaveDrop = 0;
std::uint32_t diagnosticCellStride = 1;
+120 -4
View File
@@ -242,7 +242,8 @@ inline bool FindFarConnectorRadiusNearNominal(const FStandaloneShapeWorld& Shape
}
inline void AppendFarConnectorShells(const FStandaloneShapeWorld& Shape,const FVoxelTileKey& Key,
int32 Step,int32 Cells,FVoxelMeshData& Mesh)
int32 Step,int32 Cells,FVoxelMeshData& Mesh,int32 StationScale=1,
uint32 RayMask=0x0fu,bool bReuseStationRadii=false)
{
if(Key.Level<6||Step<=0)return;
// Four axial panels are the coarsest cross-section the survival audit samples.
@@ -313,7 +314,8 @@ inline void AppendFarConnectorShells(const FStandaloneShapeWorld& Shape,const FV
if(!bIntersects||TMax-TMin<1.0e-6)continue;
// LOD7 and LOD8 share a 256-voxel connector station grid; near LOD keeps 128-voxel checks.
// LOD7 et LOD8 partagent des stations aux 256 voxels; pres du joueur, garder les pas de 128.
const double ConnectorStationStep=Key.Level>=7?std::max(256.0,static_cast<double>(Step)):128.0;
const double ConnectorStationStep=(Key.Level>=7?std::max(256.0,static_cast<double>(Step)):128.0)
*std::max(1,StationScale);
const int32 AxialSampleCount=std::max(1,static_cast<int32>(std::ceil(Length/ConnectorStationStep)));
std::vector<uint8> MissingMasks(static_cast<size_t>(AxialSampleCount),0);
std::vector<std::array<double,4>> SourceRadii(static_cast<size_t>(AxialSampleCount));
@@ -332,6 +334,7 @@ inline void AppendFarConnectorShells(const FStandaloneShapeWorld& Shape,const FV
const double DetectionTolerance=MatchTolerance*0.98;
for(int32 DirectionIndex=0;DirectionIndex<4;++DirectionIndex)
{
if((RayMask&(1u<<DirectionIndex))==0)continue;
const FVector& Direction=RadialDirections[DirectionIndex];
if(!ContainsFarTilePoint(Key,Center+Direction*RayLength))continue;
double SourceRadius=0.0,SourceUncertainty=0.0;
@@ -388,7 +391,13 @@ inline void AppendFarConnectorShells(const FStandaloneShapeWorld& Shape,const FV
{Ring.Radii[DirectionIndex]=Connector.Radius;continue;}
double Radius=Connector.Radius;
double RadiusUncertainty=0.0;
if(!FindFarConnectorRadiusNearNominal(Shape,Center,
const int32 NearestStation=std::clamp(static_cast<int32>(std::lround(
T*AxialSampleCount-0.5)),0,AxialSampleCount-1);
const double ReusedRadius=bReuseStationRadii
?SourceRadii[static_cast<size_t>(NearestStation)][DirectionIndex]:0.0;
if(ReusedRadius>0.0)
Radius=ReusedRadius;
else if(!FindFarConnectorRadiusNearNominal(Shape,Center,
RadialDirections[DirectionIndex],Connector.Radius,Radius,RadiusUncertainty)
&&!FindFarConnectorSurfaceRadius(Shape,Center,RadialDirections[DirectionIndex],
Connector.Radius,Radius))
@@ -432,7 +441,8 @@ inline void AppendFarConnectorShells(const FStandaloneShapeWorld& Shape,const FV
}
}
std::vector<FRing> Rings;
const double MaxRingStep=Key.Level>=7?std::max(256.0,static_cast<double>(Step)):128.0;
const double MaxRingStep=(Key.Level>=7?std::max(256.0,static_cast<double>(Step)):128.0)
*std::max(1,StationScale);
double CurrentT=RunMin;
Rings.push_back(MakeRing(CurrentT,RunStart,RunEnd,RunRadiusMask));
while(CurrentT<RunMax-1.0e-7)
@@ -814,6 +824,112 @@ inline void AppendFarConnectorShells(const FStandaloneShapeWorld& Shape,const FV
Mesh.NumSurfaceVertices=OldSurfaceVertices+AddedVertices;
}
struct FFarConnectorCoverageAudit
{
std::uint64_t Rays=0;
std::uint64_t Holes=0;
std::uint64_t Floating=0;
std::vector<std::uint64_t> HoleIds;
std::vector<std::uint64_t> FloatingIds;
};
inline std::uint64_t FarConnectorAuditRayId(const FVoxelTileKey& Key,uint32 Connector,
int32 Station,int32 Direction)
{
std::uint64_t Hash=14695981039346656037ull;
const auto Add=[&](std::uint64_t Value,int Bytes)
{
for(int Byte=0;Byte<Bytes;++Byte)
{Hash^=static_cast<uint8>(Value>>(Byte*8));Hash*=1099511628211ull;}
};
Add(static_cast<uint32>(Key.Coord.X),4);Add(static_cast<uint32>(Key.Coord.Y),4);
Add(static_cast<uint32>(Key.Coord.Z),4);Add(static_cast<uint32>(Key.Level),4);
Add(Connector,4);Add(static_cast<uint32>(Station),4);Add(static_cast<uint32>(Direction),4);
return Hash?Hash:1;
}
inline FFarConnectorCoverageAudit AuditFarConnectorCoverage(const FStandaloneShapeWorld& Shape,
const FVoxelTileKey& Key,int32 Step,int32 Cells,const FVoxelMeshData& Mesh)
{
FFarConnectorCoverageAudit Audit;
if(Key.Level<6||Step<=0||Mesh.Triangles.Num()<3)return Audit;
const FIntVector TileOrigin=Key.OriginVoxels();
const int32 TileExtent=Key.ExtentVoxels();
FFarMeshSegmentIndex MeshIndex(Mesh,TileOrigin,TileExtent,Cells,Step);
const auto& Connectors=Shape.GetFarConnectorSegments();
std::vector<uint32> CandidateIndices;
Shape.FindFarConnectorCandidates(
FIntVector(TileOrigin.X-64,TileOrigin.Y-64,TileOrigin.Z-64),
FIntVector(TileOrigin.X+TileExtent+64,TileOrigin.Y+TileExtent+64,TileOrigin.Z+TileExtent+64),
CandidateIndices);
for(const uint32 ConnectorIndex:CandidateIndices)
{
if(ConnectorIndex>=Connectors.size())continue;
const auto& Connector=Connectors[ConnectorIndex];
const FVector Delta=Connector.End-Connector.Start;
const double Length=std::sqrt(Delta.X*Delta.X+Delta.Y*Delta.Y+Delta.Z*Delta.Z);
if(Length<=1.0e-6)continue;
const FVector Axis=Delta/Length;
FVector U=FVector::CrossProduct(Axis,FVector::UpVector);
if(U.SizeSquared()<0.01)U=FVector::CrossProduct(Axis,FVector::RightVector);
U.Normalize();
FVector V=FVector::CrossProduct(Axis,U);V.Normalize();
const FVector Directions[4]={U,U*-1.0,V,V*-1.0};
const double Deltas[3]={Delta.X,Delta.Y,Delta.Z};
const double Starts[3]={Connector.Start.X,Connector.Start.Y,Connector.Start.Z};
const double Low[3]={TileOrigin.X-Connector.Radius,TileOrigin.Y-Connector.Radius,
TileOrigin.Z-Connector.Radius};
const double High[3]={TileOrigin.X+TileExtent+Connector.Radius,
TileOrigin.Y+TileExtent+Connector.Radius,TileOrigin.Z+TileExtent+Connector.Radius};
double TMin=0.0,TMax=1.0;
bool bIntersects=true;
for(int32 AxisIndex=0;AxisIndex<3;++AxisIndex)
{
if(std::abs(Deltas[AxisIndex])<1.0e-9)
{if(Starts[AxisIndex]<Low[AxisIndex]||Starts[AxisIndex]>High[AxisIndex])bIntersects=false;continue;}
double A=(Low[AxisIndex]-Starts[AxisIndex])/Deltas[AxisIndex];
double B=(High[AxisIndex]-Starts[AxisIndex])/Deltas[AxisIndex];
if(A>B)std::swap(A,B);
TMin=std::max(TMin,A);TMax=std::min(TMax,B);
if(TMin>TMax){bIntersects=false;break;}
}
if(!bIntersects||TMax-TMin<1.0e-6)continue;
const double StationStep=Key.Level>=7?std::max(256.0,static_cast<double>(Step)):128.0;
const int32 StationCount=std::max(1,static_cast<int32>(std::ceil(Length/StationStep)));
const double RayLength=Connector.Radius*2.0+32.0;
const double MatchTolerance=std::max(8.0,static_cast<double>(Connector.Radius)*0.35);
const double DetectionTolerance=MatchTolerance*0.98;
const double SourceRadiusBand=std::max(16.0,static_cast<double>(Connector.Radius)*0.75);
for(int32 Station=0;Station<StationCount;++Station)
{
const FVector Center=Connector.Start+Delta*((static_cast<double>(Station)+0.5)/StationCount);
if(!ContainsFarTilePoint(Key,Center))continue;
for(int32 DirectionIndex=0;DirectionIndex<4;++DirectionIndex)
{
if(!ContainsFarTilePoint(Key,Center+Directions[DirectionIndex]*RayLength))continue;
double SourceRadius=0.0,Uncertainty=0.0;
const bool bSource=FindFarConnectorRadiusNearNominal(Shape,Center,
Directions[DirectionIndex],Connector.Radius,SourceRadius,Uncertainty)
||FindFarConnectorSurfaceRadius(Shape,Center,Directions[DirectionIndex],
Connector.Radius,SourceRadius);
const double MeshDifference=MeshIndex.NearestHitDifference(Center,
Directions[DirectionIndex],RayLength,bSource?SourceRadius:Connector.Radius);
if(bSource&&std::abs(SourceRadius-Connector.Radius)<=SourceRadiusBand)
{
++Audit.Rays;
if(!std::isfinite(MeshDifference)||MeshDifference>DetectionTolerance)
Audit.HoleIds.push_back(FarConnectorAuditRayId(Key,ConnectorIndex,Station,DirectionIndex));
}
else if(std::isfinite(MeshDifference)&&MeshDifference<=DetectionTolerance)
Audit.FloatingIds.push_back(FarConnectorAuditRayId(Key,ConnectorIndex,Station,DirectionIndex));
}
}
}
Audit.Holes=Audit.HoleIds.size();
Audit.Floating=Audit.FloatingIds.size();
return Audit;
}
struct FFarMeshColumnIndex
{
FFarMeshColumnIndex(const FVoxelMeshData& InMesh,const FIntVector& InOrigin,int32 InExtent,
+61 -13
View File
@@ -343,7 +343,7 @@ namespace VoxelForgeCore
FNativeCapRefinementStats RefineFarNativeGround(const FStandaloneOwnerWorld& Owner,
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,int32 MeshStepVoxels,
int32 InteriorStepOverride,FVoxelMeshData& Mesh)
int32 InteriorStepOverride,int32 RefinementGridScale,FVoxelMeshData& Mesh)
{
FNativeCapRefinementStats Stats;
if(Key.Level<5||!Owner.Generator)return Stats;
@@ -354,11 +354,12 @@ namespace VoxelForgeCore
const int32 RequestedStep=Key.Level==8
?(InteriorStepOverride>0?InteriorStepOverride:MeshStepVoxels)
:std::min(BoundaryStepVoxels,1<<Key.Level);
const int32 ScaledRequestedStep=RequestedStep*std::max(1,RefinementGridScale);
const int32 GridStepVoxels=Key.Level==8
?((RequestedStep<BoundaryStepVoxels||RequestedStep%BoundaryStepVoxels!=0
||TileExtent<=0||TileExtent%RequestedStep!=0)
?BoundaryStepVoxels:std::max(BoundaryStepVoxels,RequestedStep))
:RequestedStep;
?((ScaledRequestedStep<BoundaryStepVoxels||ScaledRequestedStep%BoundaryStepVoxels!=0
||TileExtent<=0||TileExtent%ScaledRequestedStep!=0)
?BoundaryStepVoxels:std::max(BoundaryStepVoxels,ScaledRequestedStep))
:ScaledRequestedStep;
if(TileExtent<=0||TileExtent%GridStepVoxels!=0)return Stats;
const std::vector<int32> GridCoordinates=
BuildNativeHorizonGridCoordinates(TileExtent,GridStepVoxels);
@@ -644,7 +645,7 @@ namespace VoxelForgeCore
FNativeCapRefinementStats RefineFarNativeCap(const FStandaloneOwnerWorld& Owner,
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,int32 MeshStepVoxels,
int32 InteriorStepOverride,FVoxelMeshData& Mesh)
int32 InteriorStepOverride,int32 RefinementGridScale,FVoxelMeshData& Mesh)
{
FNativeCapRefinementStats Stats;
if(Key.Level<4||!Owner.Generator)return Stats;
@@ -654,11 +655,12 @@ namespace VoxelForgeCore
:(Key.Level<5?std::min(BoundaryStepVoxels,1<<Key.Level):BoundaryStepVoxels);
const FIntVector TileOrigin=Key.OriginVoxels();
const int32 TileExtent=Key.ExtentVoxels();
const int32 ScaledRequestedStep=RequestedStep*std::max(1,RefinementGridScale);
const int32 EffectiveGridStep=Key.Level==8
?((RequestedStep<BoundaryStepVoxels||RequestedStep%BoundaryStepVoxels!=0
||TileExtent<=0||TileExtent%RequestedStep!=0)
?BoundaryStepVoxels:std::max(BoundaryStepVoxels,RequestedStep))
:RequestedStep;
?((ScaledRequestedStep<BoundaryStepVoxels||ScaledRequestedStep%BoundaryStepVoxels!=0
||TileExtent<=0||TileExtent%ScaledRequestedStep!=0)
?BoundaryStepVoxels:std::max(BoundaryStepVoxels,ScaledRequestedStep))
:ScaledRequestedStep;
constexpr int64 Scale=WorldAssemblySubvoxelsPerVoxel;
if(TileExtent<=0||TileExtent%EffectiveGridStep!=0)return Stats;
const std::vector<int32> GridCoordinates=
@@ -1606,6 +1608,37 @@ namespace VoxelForgeCore
options.legacyDirectMappedColumnCache||key.lod<=2);
if(!impl_||!impl_->owner)
{ error="resolved world snapshot is empty";return std::nullopt; }
const WorldAssemblyRecipeV1* FarRecipe=impl_->worldAssembly?&impl_->worldAssembly->recipe:nullptr;
std::uint32_t FarDisplayBudget=FarRecipe?FarRecipe->farDisplayGeometryBudgetMillipixels:0;
std::uint32_t FarRefinementScale=FarRecipe?FarRecipe->farRefinementGridScale:0;
std::uint32_t FarRefinementLodMask=FarRecipe?FarRecipe->farRefinementLodMask:0;
std::uint32_t FarConnectorStationScale=FarRecipe?FarRecipe->farConnectorStationScale:0;
std::uint32_t FarConnectorRayMask=FarRecipe?FarRecipe->farConnectorRayMask:0;
bool bFarConnectorReuseStationRadii=FarRecipe&&FarRecipe->farConnectorReuseStationRadii;
if(options.overrideFarDisplayMesh)
{
FarDisplayBudget=options.farDisplayGeometryBudgetMillipixels;
FarRefinementScale=options.farRefinementGridScale;
FarRefinementLodMask=options.farRefinementLodMask;
FarConnectorStationScale=options.farConnectorStationScale;
FarConnectorRayMask=options.farConnectorRayMask;
bFarConnectorReuseStationRadii=options.farConnectorReuseStationRadii;
}
const auto ValidFarScale=[](std::uint32_t Value)
{return Value==0||Value==2||Value==4||Value==8;};
if((FarDisplayBudget!=0&&FarDisplayBudget!=1000)
||!ValidFarScale(FarRefinementScale)
||!ValidFarScale(FarConnectorStationScale)
||(FarRefinementLodMask&~0x1f0u)!=0||FarConnectorRayMask>0x0fu
||(FarRefinementScale!=0&&FarRefinementLodMask==0))
{error="far display mesh override is outside the recipe's supported 1 px settings";return std::nullopt;}
const std::uint32_t ActiveFarRefinementScale=FarDisplayBudget==1000
&&(FarRefinementLodMask&(1u<<std::clamp(key.lod,0,31)))!=0
?FarRefinementScale:0;
const int32 ActiveConnectorStationScale=FarDisplayBudget==1000
&&FarConnectorStationScale!=0?static_cast<int32>(FarConnectorStationScale):1;
const uint32 ActiveConnectorRayMask=FarDisplayBudget==1000
&&FarConnectorRayMask!=0?FarConnectorRayMask:0x0fu;
const bool bShapeReady=impl_->shapeReady.load(std::memory_order_acquire);
const bool bNativeOnly=!bShapeReady||options.diagnosticForceNativeOnly;
if(!bShapeReady&&!options.diagnosticForceNativeOnly&&!isTileShapeFree(key))
@@ -2135,7 +2168,10 @@ namespace VoxelForgeCore
BeforeConnectorColumnComputeMilliseconds=Columns.columnComputeMilliseconds;
}
const auto ConnectorStart=std::chrono::steady_clock::now();
AppendFarConnectorShells(farShape,nativeKey,step,world.CoarseTileCells,mesh);
if(!options.diagnosticRemoveFarConnectorPanels)
AppendFarConnectorShells(farShape,nativeKey,step,world.CoarseTileCells,mesh,
ActiveConnectorStationScale,ActiveConnectorRayMask,
FarDisplayBudget==1000&&bFarConnectorReuseStationRadii);
const auto AfterConnector=FStandaloneShapeWorld::GetThreadClassificationStats();
out.diagnostics.farConnectorShellMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-ConnectorStart).count();
@@ -2161,6 +2197,16 @@ namespace VoxelForgeCore
}
out.diagnostics.farShellTrianglesAdded+=static_cast<std::uint64_t>(
std::max(0,mesh.Triangles.Num()/3-BeforeConnectorTriangles));
if(options.diagnosticAuditFarConnectorCoverage)
{
const auto Audit=AuditFarConnectorCoverage(farShape,nativeKey,step,
world.CoarseTileCells,mesh);
out.diagnostics.farConnectorAuditRays=Audit.Rays;
out.diagnostics.farConnectorAuditHoles=Audit.Holes;
out.diagnostics.farConnectorAuditFloating=Audit.Floating;
out.diagnostics.farConnectorAuditHoleIds=Audit.HoleIds;
out.diagnostics.farConnectorAuditFloatingIds=Audit.FloatingIds;
}
}
out.diagnostics.farShellMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-FarShellStart).count();
@@ -2168,12 +2214,14 @@ namespace VoxelForgeCore
{
const auto HorizonStart=std::chrono::steady_clock::now();
FNativeCapRefinementStats GroundStats;
const int32 ActiveRefinementScale=ActiveFarRefinementScale>1
?static_cast<int32>(ActiveFarRefinementScale):1;
const double ColumnMillisecondsBeforeGround=
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
const auto GroundStart=std::chrono::steady_clock::now();
if(key.lod>=4)
GroundStats=RefineFarNativeGround(world,*impl_->worldAssembly,nativeKey,step,
options.lod8HorizonInteriorStepVoxels,mesh);
options.lod8HorizonInteriorStepVoxels,ActiveRefinementScale,mesh);
const double GroundWallMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-GroundStart).count();
const double ColumnMillisecondsAfterGround=
@@ -2186,7 +2234,7 @@ namespace VoxelForgeCore
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
const auto CapStart=std::chrono::steady_clock::now();
const auto CapStats=RefineFarNativeCap(world,*impl_->worldAssembly,nativeKey,step,
options.lod8HorizonInteriorStepVoxels,mesh);
options.lod8HorizonInteriorStepVoxels,ActiveRefinementScale,mesh);
const double CapWallMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-CapStart).count();
const double ColumnMillisecondsAfterCap=
@@ -244,6 +244,24 @@ namespace world_assembly_detail
|| recipe.biomeBorderBlendSubvoxels < 0
|| recipe.biomeBorderBlendSubvoxels > recipe.biomeCellSizeSubvoxels)
{ error = "world assembly biome-map parameters are outside the supported fixed-scale range"; return false; }
if (recipe.evaluatorVersion >= 7)
{
constexpr std::uint32_t FarLodMask = 0x1f0u; // LOD4..8.
const bool anyFarSetting = recipe.farDisplayGeometryBudgetMillipixels != 0
|| recipe.farRefinementGridScale != 0 || recipe.farRefinementLodMask != 0
|| recipe.farConnectorStationScale != 0 || recipe.farConnectorRayMask != 0
|| recipe.farConnectorReuseStationRadii;
const auto validScale = [](std::uint32_t value)
{ return value == 0 || value == 2 || value == 4 || value == 8; };
if ((anyFarSetting && recipe.farDisplayGeometryBudgetMillipixels != 1000)
|| recipe.farDisplayGeometryBudgetMillipixels > 1000
|| !validScale(recipe.farRefinementGridScale)
|| (recipe.farRefinementLodMask & ~FarLodMask) != 0
|| !validScale(recipe.farConnectorStationScale)
|| recipe.farConnectorRayMask > 0x0fu
|| (recipe.farRefinementGridScale != 0 && recipe.farRefinementLodMask == 0))
{ error = "far display mesh declaration must use the 1 px budget and supported LOD, scale, and ray values"; return false; }
}
std::vector<std::uint32_t> ids;
ids.reserve(recipe.biomes.size());
for (const auto& biome : recipe.biomes)
@@ -431,6 +449,16 @@ namespace world_assembly_detail
HashWord(hash, static_cast<std::uint64_t>(recipe.biomeClimateMoistureWavelengthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.biomeClimateReliefContrastQ16), 8);
}
if (recipe.evaluatorVersion >= 7)
{
HashWord(hash, 0x4641524449535037ull, 8);
HashWord(hash, recipe.farDisplayGeometryBudgetMillipixels, 4);
HashWord(hash, recipe.farRefinementGridScale, 4);
HashWord(hash, recipe.farRefinementLodMask, 4);
HashWord(hash, recipe.farConnectorStationScale, 4);
HashWord(hash, recipe.farConnectorRayMask, 4);
HashWord(hash, recipe.farConnectorReuseStationRadii ? 1u : 0u, 1);
}
HashWord(hash, recipe.biomes.size(), 4);
for (const auto& biome : recipe.biomes)
{
@@ -641,6 +669,16 @@ namespace world_assembly_detail
|| !ReadInteger(value.Get("climate_moisture_wavelength_q"), recipe.biomeClimateMoistureWavelengthSubvoxels)
|| !ReadInteger(value.Get("climate_relief_contrast_q16"), recipe.biomeClimateReliefContrastQ16)))
{ error = "world assembly recipe is missing evaluator-2 climate parameters"; return false; }
if (recipe.evaluatorVersion >= 7
&& (!ReadU32(value.Get("far_display_budget_millipixels"), recipe.farDisplayGeometryBudgetMillipixels)
|| !ReadU32(value.Get("far_refinement_grid_scale"), recipe.farRefinementGridScale)
|| !ReadU32(value.Get("far_refinement_lod_mask"), recipe.farRefinementLodMask)
|| !ReadU32(value.Get("far_connector_station_scale"), recipe.farConnectorStationScale)
|| !ReadU32(value.Get("far_connector_ray_mask"), recipe.farConnectorRayMask)
|| !std::holds_alternative<bool>(value.Get("far_connector_reuse_station_radii").Data)))
{ error = "world assembly recipe is missing evaluator-7 far display mesh parameters"; return false; }
if (recipe.evaluatorVersion >= 7)
recipe.farConnectorReuseStationRadii = std::get<bool>(value.Get("far_connector_reuse_station_radii").Data);
for (const auto& item : value.Get("biomes").AsArray())
{
WorldAssemblyBiomeProfileV1 biome;
@@ -702,6 +740,14 @@ namespace world_assembly_detail
out << ",\"climate_relief_wavelength_q\":" << recipe.biomeClimateReliefWavelengthSubvoxels
<< ",\"climate_moisture_wavelength_q\":" << recipe.biomeClimateMoistureWavelengthSubvoxels
<< ",\"climate_relief_contrast_q16\":" << recipe.biomeClimateReliefContrastQ16;
if (recipe.evaluatorVersion >= 7)
out << ",\"far_display_budget_millipixels\":" << recipe.farDisplayGeometryBudgetMillipixels
<< ",\"far_refinement_grid_scale\":" << recipe.farRefinementGridScale
<< ",\"far_refinement_lod_mask\":" << recipe.farRefinementLodMask
<< ",\"far_connector_station_scale\":" << recipe.farConnectorStationScale
<< ",\"far_connector_ray_mask\":" << recipe.farConnectorRayMask
<< ",\"far_connector_reuse_station_radii\":"
<< (recipe.farConnectorReuseStationRadii ? "true" : "false");
out << ",\"content_hash\":\"" << HashText(recipe.contentHash) << "\",\"biomes\":[";
for (std::size_t i = 0; i < recipe.biomes.size(); ++i)
{
@@ -0,0 +1,195 @@
# Linux15 report
## Result
Null result. No far-mesh shortcut passed the full 1 px, shading, swap, and topology gates, so no
recipe enables one by default. Evaluator version 6→7 adds the optional `far_display_mesh` declaration;
omitting it keeps the L14 output path. The controls are in the shared Core layer and do not touch
LOD0–2, exact queries, collision, edits, or server state.
Candidates measured:
- Refinement grid scale ×2 at LOD4, 5, 6, 7, and 8. The all-tile LOD4 and LOD5 runs reject; the
remaining per-LOD probes exceed 1 px or change shading, and the LOD6–8 candidates worsen at
least one adjacent swap.
- Reusing connector station radii leaves geometry p99 at 1.07 px but reaches 15.23 px maximum;
shading delta reaches 0.667 and the normal changes by 73.3°.
- Coarsening refinement at LOD4–8 is the largest measured saving, but exceeds the display budget.
Halving connector station density also saves time but opens gaps. Removing
any one ray direction also opens gaps, and the removed-panel control finds 99 new holes.
## Screen-space gate
The gate samples 65×65 surface grids across all 1,145 displayed LOD3–8 tiles (5,208,134 samples;
skirts excluded). Geometry is projected normal displacement against today's same-LOD mesh at its
closest display distance, using 90° horizontal FOV and 2,560 px (0.000613592 rad/px). Shading delta
is the maximum of fixed-light normalized Lambert change and material selector/blend-channel change;
the limit is 1/255. Each number below is p50/p99/max. Exact-path baseline values are all zero.
The gate rejects geometry max above 1 px, shading max above 1/255, any worse per-sample swap,
missing surface, or new hole/crack/floater. Its own validation fails if S13, stride-2, missing
surface, floating surface, missing fine-ring, or removed-panel controls are not rejected; the
panel control must expose at least one new connector hole.
| LOD | Tiles | Geometry px: baseline → refine×2 all | Shading delta: baseline → refine×2 all | Refine×2 holes / cracks / floating |
|---:|---:|---:|---:|---:|
| 3 | 384 | 0/0/0 → 0/0/0 | 0/0/0 → 0/0/0 | 0 / 0 / 0 |
| 4 | 320 | 0/0/0 → 0/2.370/606.562 | 0/0/0 → 0/0.023529/1 | 254 / 400 / 254 |
| 5 | 144 | 0/0/0 → 0/12.728/95.453 | 0/0/0 → 0/1/1 | 1,183 / 76 / 138 |
| 6 | 96 | 0/0/0 → 0.023/13.408/799.596 | 0/0/0 → 0/1/1 | 476 / 0 / 201 |
| 7 | 48 | 0/0/0 → 0/13.242/248.905 | 0/0/0 → 0/1/1 | 30 / 0 / 1 |
| 8 | 153 | 0/0/0 → 0.158/19.250/187.465 | 0/0/0 → 0.003922/1/1 | 45 / 0 / 946 |
The selected-patch swap report compares each baseline with refine×2 all. Values are p50/p99/max px.
| Swap (distance) | Before | After refine×2 all |
|---|---:|---:|
| 3→2 (96 m) | 0.611/3.767/6.045 | 0.611/3.767/6.045 |
| 4→3 (192 m) | 0.752/4.630/7.513 | 0.752/4.630/7.513 |
| 5→4 (384 m) | 0/9.580/31.783 | 2.435/32.321/86.615 |
| 6→5 (768 m) | 0/9.754/70.340 | 0.343/19.142/78.077 |
| 7→6 (1,536 m) | 0/9.040/42.355 | 0.200/15.109/50.994 |
| 8→7 (3,072 m) | 0.826/18.030/47.182 | 1.528/24.547/64.449 |
The station×2 LOD6 screen scan measures 0/2/813.571 px, shading 0/0/1, with 1,349 holes and 1,690
floaters. Its connector audit counts missing ray stations by stable tile/ray/station ID: holes 8→221
(216 new), floating classifications 464→405 (111 new IDs). In the refine×2 all row above, the
screen scan reports those all-tile holes, cracks, and floaters. Station×2 leaves all six selected-patch
swap p50/p99/max rows at baseline values, including 6→5 at 0/9.754/70.340 px. All four ray directions are required:
the four one-ray-removed
controls create 1,008, 1,022, 1,171, and 1,158 new holes. The explicit panel-removal control creates
99 new holes. The exact-path audit has zero new holes, cracks, or floating pieces; its raw connector
audit counts remain 8 holes and 464 pre-existing floating classifications.
The LOD4×2 and LOD5×2 candidates also fail in isolation. Reusing station radii misses the LOD6
geometry and shading limits. The four one-ray-removed masks add 1,008, 1,022, 1,171, and 1,158
connector holes, respectively.
## Negative controls
- The S13 global-octave-drop and stride-2 column-interpolation controls are rejected at every LOD3–8.
- Missing displayed surface, a floating surface, and missing fine-ring surface controls are rejected.
- Removing connector panels is rejected with 99 new holes. The exact connector path has zero new
station IDs classified as holes, cracks, or floaters.
## Speed
Five interleaved W12 pairs per candidate, hashing off, same 2,553-tile L1 view.
`whole_view_ready` is the metric. Both candidates fail the quality gate.
Station×2 connector stations:
| Pair | L14 baseline | Station×2 | Change |
|---:|---:|---:|---:|
| 1 | 1.320 s | 1.304 s | −1.2% |
| 2 | 1.363 s | 1.330 s | −2.4% |
| 3 | 1.371 s | 1.332 s | −2.8% |
| 4 | 1.393 s | 1.341 s | −3.7% |
| 5 | 1.391 s | 1.345 s | −3.3% |
| Median | 1.371 s | 1.332 s | −2.8% (3.3% median paired gain) |
Refinement grid scale ×2 at LOD4–8:
| Pair | L14 baseline | Refine×2 all | Change |
|---:|---:|---:|---:|
| 1 | 1.305 s | 1.164 s | −10.8% |
| 2 | 1.336 s | 1.177 s | −11.9% |
| 3 | 1.362 s | 1.169 s | −14.2% |
| 4 | 1.378 s | 1.178 s | −14.5% |
| 5 | 1.384 s | 1.190 s | −14.0% |
| Median | 1.362 s | 1.177 s | −13.6% (14.0% median paired gain) |
## Hashes and invariants
The undeclared/default recipe path matches L14. LOD0–2 density/mesh/payload/topology hashes:
| LOD | Density | Mesh | Payload | Topology |
|---:|---|---|---|---|
| 0 | `769f2a706d585a72` | `33eeeb39fa443816` | `645621a1d968983f` | `c2457ed905036c9d` |
| 1 | `69cc5a5c0a2c60af` | `b48335f5410c57d5` | `d15c5480162ab8ba` | `feca1bd025823604` |
| 2 | `85deed5683a77202` | `53469895987aeea2` | `e55f8ae7e1532033` | `d5feb12a0e85c09b` |
Exact queries (density / unclamped / detail / collision) remain
`e4d4a8eb95b35933 / e4d4a8eb95b35933 / efcba20c0c70c415 / e604823a249029bf`. The 24 m crossing's
432 moved tiles match L14 at W1/W12 with Clang off and GCC default:
`337ca62131576728 / 7612f48e2ddbbaab / 69a67ed4add83b87 / f11ac1ebd0f7ddb8` (D/M/P/T).
Its LOD0–2 per-LOD hashes also match the three rows above.
Determinism passed 28/28 configurations: SurfaceWorld seed 0 and FloatingIslands seed 0, Clang 19
FP-contract off and GCC 14 default, W1/W8/W12 and five W12 repeats. All per-LOD and whole-view
hashes match L14. L13 route parity passed for SurfaceWorld seeds 0, 1, and 17 and FloatingIslands
seed 0; page, native-only, higher-LOD, edit, and history mismatch counts are zero.
The L3 FP-contract preamble/self-test, L4 `FLazy`, L7 shared cache, L9/L12 proofs, L11 readiness and
crash fix, L13 route binding, and CLAUDE.md async rules remain in place. The evaluator and optional
recipe declaration are shared by all recipes. Builds and runs used CPUs 0–11, up to W12, normal
priority, 6 GiB address-space limits, and the unchanged `powersave` governor. Peak RSS was 904,400 KB
(GCC build); peak runtime RSS was 736,432 KB. No run exceeded the time or memory limits.
## Capture pairs
JPEGs are 1280×720 and each is under 74 KB. Before is L14; after is the rejected refinement×2
candidate applied at LOD4–8.
The L1 view uses `(0,-35,-487.94) m`, yaw 0°, pitch −22°. Horizon uses the same location at pitch −2°.
The mesa frame aims from the L1 location at the L6 mesa band (`(-472,-1432,-463.151) m` in the
gate) with yaw −161° and pitch 0.9°.
| View | Before | After refine×2 all |
|---|---|---|
| L1 camera | [l1-camera-before.jpg](captures/l1-camera-before.jpg) (59 KB) | [l1-camera-after-refineall.jpg](captures/l1-camera-after-refineall.jpg) (57 KB) |
| Horizon | [horizon-before.jpg](captures/horizon-before.jpg) (63 KB) | [horizon-after-refineall.jpg](captures/horizon-after-refineall.jpg) (60 KB) |
| LOD6 mesa rim | [lod6-mesa-rim-before.jpg](captures/lod6-mesa-rim-before.jpg) (74 KB) | [lod6-mesa-rim-after-refineall.jpg](captures/lod6-mesa-rim-after-refineall.jpg) (67 KB) |
## Failed and retried runs
- The first Clang build wrapper exited 127 because `/usr/bin/time` is absent; the direct Clang
`-ffp-contract=off` build succeeded. GCC 14 default also built successfully.
- A later sequential-build monitor was interrupted after Clang completed and before its GCC step
launched. Clang and GCC were rebuilt separately with the same compiler settings and 6 GiB cap.
- The first hash matrix omitted `--seed=0` and was discarded. A second FloatingIslands matrix used
the SurfaceWorld spawn camera; it was rerun with the L14 auto-surface camera. The corrected matrix
passed all 28 configurations.
- Initial SurfaceWorld route probes used no camera for seed 0 (the 2,370-tile bound), omitted
`--region-crossing` for seed 0 (shape compiler safety bound), and used the fixed seed-0 camera for
seed 1 (LOD2/exact-query parity mismatch; seed 17 was not run with that setup). They were rerun
with L14's camera selection and region-crossing option; all four final route audits passed. The
first 24 m crossing also omitted `--region-crossing` and exceeded the shape-build safety bound;
all four corrected compiler/worker combinations passed.
- The first connector audit compared aggregate counts. It was replaced with stable per-ray station
ID comparison before the final gate. The initial L4 selected-tile gate missed a changed tile; the
all-desired-tile coverage gate found and rejected it. The full-view gate is used in the table above.
- The first JPEG encoding pass did not set CPU affinity or an address-space cap. All six files were
re-encoded under CPUs 0–11 and the 6 GiB cap; sizes and pixels stayed the same.
- The first five speed pairs included screen-gate color capture on ordinary streaming tiles. That
diagnostic copy is now enabled only for gate runs; all five cost pairs were rerun and the table
above uses only the final logs. The final exact gate passed and station×2 remained rejected.
- No builds, captures, final audits, or cost runs timed out or breached their memory limits. The
intentional candidate rejections are listed above, not treated as infrastructure failures.
Evidence logs are in `Saved/Linux`: the final baseline and station×2 all-tile gates are
`l15-final-gatecolors-exact.log` and `l15-final-gatecolors-station2.log`; the best-payoff
refinement×2 all-tile gate is `l15-final-gatecolors-refineall.log`. Single-LOD refinement coverage is in
`l15-gate-alltiles-refine-l4x2.log` and `l15-gate-alltiles-refine-l5x2.log`. Single-LOD refinement
and connector-ray probes are `l15-gate-refine-l{x}x2.log`, `l15-gate-reuse.log`, and
`l15-gate-rays{14,13,11,7}.log`. Hash, route, and crossing evidence is in
`l15-final-hash-audit.txt`, `l15-final-route-*.log`, and `l15-final-crossing-24m-*.log`; final
station×2 speed pairs are `l15-final-speed-pair{1..5}-{before,after}.log`; refinement×2 pairs are
`l15-final-speed-refineall-pair{1..5}-{before,after}.log`.
## Note for the owner
The default far terrain stays the same because every measured reduction exceeded the 1 px geometry
or shading limit, worsened a swap, or opened holes. Refinement×2 across LOD4–8 saves 13.6% by the
median whole-view ratio, but creates hundreds-pixel errors and visible gaps, so the captures show it
for review only and it is not enabled.
## Orchestrator review (Claude)
- I looked at the LOD6 mesa-rim pair. After refine×2, the jagged crests are visibly smoothed, black
holes appear in the cap/ceiling, and a dark sliver shows on the left horizon. That confirms the
gate's rejection: the instrument is right this time, not the world.
- **Not merged into `assembler/world`:** the candidate knobs and the evaluator 6→7 bump add no default
output and would invalidate version stamps for nothing. The full L15 code (the extended gate with
shading, swap and connector-ID audits, and the candidate controls) is kept on side branch
`wip/l15-far-budget`. `assembler/world` stays at L14 code plus this report.
- Conclusion of L14+L15: far terrain has no exact or within-budget saving left with today's design. The
remaining far cost is the price of the current refinement and connector-repair geometry.
Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

+393 -92
View File
@@ -42,6 +42,7 @@ namespace StandaloneScreenSpaceGate
double MaximumSlopeDegrees=0.0;
std::array<std::uint64_t,4> SlopeSamples{};
std::array<double,4> SlopeP99{},SlopeMaximum{};
std::vector<double> SamplePixels;
bool Valid=false;
};
@@ -52,6 +53,14 @@ namespace StandaloneScreenSpaceGate
double SlopeY=0.0;
};
struct FMeshSurfaceHit
{
double Z=0.0;
std::array<double,3> Normal{};
std::array<std::uint8_t,4> Color{};
bool Ceiling=false;
};
struct FViewBasis
{
std::array<double,3> Right{};
@@ -162,7 +171,7 @@ namespace StandaloneScreenSpaceGate
{
int Side=65;
double MinimumX=0.0,MaximumX=0.0,MinimumY=0.0,MaximumY=0.0;
std::vector<std::vector<double>> Hits;
std::vector<std::vector<FMeshSurfaceHit>> Hits;
FHeightGrid()=default;
FHeightGrid(int InSide,double MinX,double MaxX,double MinY,double MaxY)
@@ -175,13 +184,13 @@ namespace StandaloneScreenSpaceGate
double Y(int Iy) const
{return MinimumY+(MaximumY-MinimumY)*Iy/(Side-1);}
void Add(int Ix,int Iy,double Z)
void Add(int Ix,int Iy,const FMeshSurfaceHit& Hit)
{
auto& Values=Hits[static_cast<std::size_t>(Iy)*Side+Ix];
Values.push_back(Z);
Values.push_back(Hit);
}
const std::vector<double>& At(int Ix,int Iy) const
const std::vector<FMeshSurfaceHit>& At(int Ix,int Iy) const
{return Hits[static_cast<std::size_t>(Iy)*Side+Ix];}
};
@@ -246,6 +255,8 @@ namespace StandaloneScreenSpaceGate
{
if(!Tile||Tile->Indices.size()<3)continue;
const std::size_t TriangleCount=Tile->Indices.size()/3;
const std::size_t CeilingStart=TriangleCount
-std::min<std::size_t>(TriangleCount,Tile->CeilingTriangleCount);
for(std::size_t Triangle=0;Triangle<TriangleCount;++Triangle)
{
if(Triangle<Tile->TriangleSkirtFaceMasks.size()
@@ -274,15 +285,33 @@ namespace StandaloneScreenSpaceGate
const double W1=((C[1]-A[1])*(X-C[0])+(A[0]-C[0])*(Y-C[1]))/Denominator;
const double W2=1.0-W0-W1;
if(W0<-1e-6||W1<-1e-6||W2<-1e-6)continue;
Grid.Add(Ix,Iy,W0*A[2]+W1*B[2]+W2*C[2]);
const auto& VA=Tile->Vertices[I0];
const auto& VB=Tile->Vertices[I1];
const auto& VC=Tile->Vertices[I2];
std::array<double,3> Normal{};
for(int Axis=0;Axis<3;++Axis)
Normal[Axis]=W0*VA.Normal[Axis]+W1*VB.Normal[Axis]+W2*VC.Normal[Axis];
const double NormalLength=std::sqrt(Normal[0]*Normal[0]
+Normal[1]*Normal[1]+Normal[2]*Normal[2]);
if(NormalLength>1.0e-12)
for(double& Component:Normal)Component/=NormalLength;
std::array<std::uint8_t,4> Color{};
if(Tile->VertexColors.size()==Tile->Vertices.size())
for(int Channel=0;Channel<4;++Channel)
Color[Channel]=static_cast<std::uint8_t>(std::clamp(std::lround(
W0*Tile->VertexColors[I0][Channel]
+W1*Tile->VertexColors[I1][Channel]
+W2*Tile->VertexColors[I2][Channel]),0l,255l));
Grid.Add(Ix,Iy,{W0*A[2]+W1*B[2]+W2*C[2],Normal,Color,Triangle>=CeilingStart});
}
}
}
for(auto& Values:Grid.Hits)
{
std::sort(Values.begin(),Values.end());
Values.erase(std::unique(Values.begin(),Values.end(),[](double A,double B)
{return std::abs(A-B)<0.02;}),Values.end());
std::stable_sort(Values.begin(),Values.end(),[](const FMeshSurfaceHit& A,
const FMeshSurfaceHit& B){return A.Z<B.Z;});
Values.erase(std::unique(Values.begin(),Values.end(),[](const FMeshSurfaceHit& A,
const FMeshSurfaceHit& B){return std::abs(A.Z-B.Z)<0.02;}),Values.end());
}
return Grid;
}
@@ -335,14 +364,14 @@ namespace StandaloneScreenSpaceGate
return Roots;
}
inline bool MatchNearest(const std::vector<double>& Hits,const std::vector<bool>& Used,
inline bool MatchNearest(const std::vector<FMeshSurfaceHit>& Hits,const std::vector<bool>& Used,
double Root,std::size_t& OutIndex,double& OutError)
{
double Best=std::numeric_limits<double>::infinity();std::size_t BestIndex=Hits.size();
for(std::size_t I=0;I<Hits.size();++I)
if(I>=Used.size()||!Used[I])
{
const double Error=std::abs(Hits[I]-Root);
const double Error=std::abs(Hits[I].Z-Root);
if(Error<Best){Best=Error;BestIndex=I;}
}
if(BestIndex==Hits.size())return false;
@@ -371,7 +400,7 @@ namespace StandaloneScreenSpaceGate
continue;
}
Used[HitIndex]=true;
AddError(Accumulator,MeshGrid.At(Ix,Iy)[HitIndex]-Root.ZMeters,
AddError(Accumulator,MeshGrid.At(Ix,Iy)[HitIndex].Z-Root.ZMeters,
Root,PixelBudgetMeters,View,MeshGrid.X(Ix),MeshGrid.Y(Iy));
++Row.Samples;
}
@@ -399,7 +428,7 @@ namespace StandaloneScreenSpaceGate
std::vector<bool> UsedReference(Reference.size(),false),UsedMesh(Mesh.size(),false);
for(std::size_t ReferenceIndex=0;ReferenceIndex<Reference.size();++ReferenceIndex)
{
const double ReferenceZ=Reference[ReferenceIndex];
const double ReferenceZ=Reference[ReferenceIndex].Z;
UsedReference[ReferenceIndex]=true;
std::size_t MeshIndex=0;double MeshError=0.0;
if(!MatchNearest(Mesh,UsedMesh,ReferenceZ,MeshIndex,MeshError))
@@ -416,7 +445,7 @@ namespace StandaloneScreenSpaceGate
if(BestRoot!=Roots.end()&&std::abs(BestRoot->ZMeters-ReferenceZ)<1.0)
Root=*BestRoot;
}
AddError(Accumulator,Mesh[MeshIndex]-Reference[ReferenceIndex],
AddError(Accumulator,Mesh[MeshIndex].Z-Reference[ReferenceIndex].Z,
Root,PixelBudgetMeters,View,ReferenceGrid.X(Ix),ReferenceGrid.Y(Iy));
}
for(std::size_t I=0;I<Mesh.size();++I)
@@ -426,6 +455,110 @@ namespace StandaloneScreenSpaceGate
return Row;
}
struct FMeshComparisonRow
{
FErrorRow Geometry;
std::uint64_t Holes=0,Cracks=0,Floating=0,MaterialMismatches=0;
double ShadeP50=0.0,ShadeP99=0.0,ShadeMaximum=0.0;
double NormalDegreesP50=0.0,NormalDegreesP99=0.0,NormalDegreesMaximum=0.0;
std::vector<double> GeometryPixels,ShadeValues,NormalAngles;
bool Valid=false;
};
inline std::array<double,3> UnitNormal(const FMeshSurfaceHit& Hit)
{
std::array<double,3> N=Hit.Normal;
const double Length=std::sqrt(N[0]*N[0]+N[1]*N[1]+N[2]*N[2]);
if(Length<=1.0e-12)return {0.0,0.0,Hit.Ceiling?-1.0:1.0};
for(double& Component:N)Component/=Length;
return N;
}
inline FMeshComparisonRow CompareMeshes(const FHeightGrid& Reference,
const FHeightGrid& Candidate,const FVoxelTileKey& Patch,double VoxelMeters,
double PixelBudgetMeters,const FViewBasis& View)
{
FMeshComparisonRow Row;Row.Valid=Reference.Side==Candidate.Side;
if(!Row.Valid)return Row;
FErrorAccumulator Geometry;
std::vector<double> ShadeValues,NormalAngles;
const std::size_t CellCount=static_cast<std::size_t>(Reference.Side)*Reference.Side;
ShadeValues.reserve(CellCount);NormalAngles.reserve(CellCount);
const double LightLength=std::sqrt(0.3*0.3+(-0.4)*(-0.4)+0.85*0.85);
const std::array<double,3> Light={0.3/LightLength,-0.4/LightLength,0.85/LightLength};
for(int Iy=0;Iy<Reference.Side;++Iy)
for(int Ix=0;Ix<Reference.Side;++Ix)
{
const auto& Before=Reference.At(Ix,Iy);
const auto& After=Candidate.At(Ix,Iy);
std::vector<bool> Used(After.size(),false);
for(std::size_t I=0;I<Before.size();++I)
{
std::size_t Matched=0;double Difference=0.0;
if(!MatchNearest(After,Used,Before[I].Z,Matched,Difference))
{
++Row.Holes;AddMissing(Geometry);ShadeValues.push_back(1.0);
NormalAngles.push_back(180.0);continue;
}
Used[Matched]=true;
const FMeshSurfaceHit& A=Before[I];
const FMeshSurfaceHit& B=After[Matched];
const auto NA=UnitNormal(A),NB=UnitNormal(B);
const double Dot=std::clamp(NA[0]*NB[0]+NA[1]*NB[1]+NA[2]*NB[2],-1.0,1.0);
constexpr double Pi=3.14159265358979323846;
const double Angle=std::acos(Dot)*180.0/Pi;
NormalAngles.push_back(Angle);
const double DiffuseA=std::max(0.0,NA[0]*Light[0]+NA[1]*Light[1]+NA[2]*Light[2]);
const double DiffuseB=std::max(0.0,NB[0]*Light[0]+NB[1]*Light[1]+NB[2]*Light[2]);
const bool bMaterialChanged=A.Color[0]!=B.Color[0]||A.Color[3]!=B.Color[3]
||A.Ceiling!=B.Ceiling;
if(bMaterialChanged)++Row.MaterialMismatches;
const double MaterialDelta=bMaterialChanged?1.0:std::max(
std::abs(static_cast<double>(A.Color[1])-B.Color[1]),
std::abs(static_cast<double>(A.Color[2])-B.Color[2]))/255.0;
ShadeValues.push_back(std::max(std::abs(DiffuseA-DiffuseB),MaterialDelta));
const double NX=NA[2]<0.0?NA[0]/std::max(1.0e-12,-NA[2])
:-NA[0]/std::max(1.0e-12,NA[2]);
const double NY=NA[2]<0.0?NA[1]/std::max(1.0e-12,-NA[2])
:-NA[1]/std::max(1.0e-12,NA[2]);
AddError(Geometry,B.Z-A.Z,{A.Z,NX,NY},PixelBudgetMeters,View,
Reference.X(Ix),Reference.Y(Iy));
}
for(std::size_t I=0;I<After.size();++I)
if(!Used[I])
{++Row.Floating;AddMissing(Geometry);ShadeValues.push_back(1.0);NormalAngles.push_back(180.0);}
}
for(int Iy=0;Iy<Reference.Side;++Iy)
for(int Ix=0;Ix<Reference.Side;++Ix)
{
const std::size_t Index=static_cast<std::size_t>(Iy)*Reference.Side+Ix;
const auto CheckEdge=[&](int NX,int NY)
{
if(NX>=Reference.Side||NY>=Reference.Side)return;
const auto& A=Reference.Hits[Index];
const auto& B=Reference.At(NX,NY);
const auto& CA=Candidate.Hits[Index];
const auto& CB=Candidate.At(NX,NY);
if(!A.empty()&&!B.empty()&&(CA.empty()!=CB.empty()))++Row.Cracks;
};
CheckEdge(Ix+1,Iy);CheckEdge(Ix,Iy+1);
}
Row.Geometry.Lod=Patch.Level;Row.Geometry.Valid=Row.Valid;
Row.Geometry.Missing=Row.Holes;Row.Geometry.Floating=Row.Floating;
FinalizeError(Row.Geometry,Geometry);
Row.ShadeP50=Percentile(ShadeValues,0.50);
Row.ShadeP99=Percentile(ShadeValues,0.99);
Row.ShadeMaximum=ShadeValues.empty()?0.0:*std::max_element(ShadeValues.begin(),ShadeValues.end());
Row.NormalDegreesP50=Percentile(NormalAngles,0.50);
Row.NormalDegreesP99=Percentile(NormalAngles,0.99);
Row.NormalDegreesMaximum=NormalAngles.empty()?0.0
:*std::max_element(NormalAngles.begin(),NormalAngles.end());
Row.GeometryPixels=std::move(Geometry.Pixels);
Row.ShadeValues=std::move(ShadeValues);
Row.NormalAngles=std::move(NormalAngles);
return Row;
}
inline FPopRow ComparePop(const FHeightGrid& Coarse,const FHeightGrid& Fine,
const FVoxelTileKey& Patch,double VoxelMeters,double PixelBudgetMeters,
const VoxelForgeCore::WorldAssemblyManifest& Assembly,const FViewBasis& View)
@@ -452,11 +585,12 @@ namespace StandaloneScreenSpaceGate
}
CoarseUsed[CoarseIndex]=true;
FineUsed[FineIndex]=true;
AddError(Accumulator,CoarseHits[CoarseIndex]-FineHits[FineIndex],
AddError(Accumulator,CoarseHits[CoarseIndex].Z-FineHits[FineIndex].Z,
Root,PixelBudgetMeters,View,Coarse.X(Ix),Coarse.Y(Iy));
++Row.Samples;
}
}
Row.SamplePixels=Accumulator.Pixels;
FErrorRow Stats;Stats.Lod=Patch.Level;Stats.Valid=true;
FinalizeError(Stats,Accumulator);
Row.Samples=Stats.Samples;Row.P50=Stats.P50;Row.P99=Stats.P99;Row.Maximum=Stats.Maximum;
@@ -472,6 +606,16 @@ namespace StandaloneScreenSpaceGate
return Row;
}
inline bool PopNotWorseAtAnySample(const FPopRow& Before,const FPopRow& After)
{
if(!Before.Valid||!After.Valid||Before.Samples<16
||Before.SamplePixels.size()!=After.SamplePixels.size()
||After.Cracks>Before.Cracks||After.Floating>Before.Floating)return false;
for(std::size_t I=0;I<Before.SamplePixels.size();++I)
if(After.SamplePixels[I]>Before.SamplePixels[I]+1.0e-4)return false;
return true;
}
inline std::array<double,9> ClosestDisplayedDistancesMeters(
const FStandaloneOwnerWorld& World,const FVector& CameraVoxels,
std::uint32_t VoxelSizeMicrons)
@@ -544,6 +688,7 @@ namespace StandaloneScreenSpaceGate
inline bool Run(const std::array<std::optional<FStandaloneCpuTile>,9>& SampleTiles,
FStandaloneStreamingGenerator& Streamer,
const std::vector<FVoxelTileKey>& DesiredKeys,
const VoxelForgeCore::WorldAssemblyManifest& Assembly,
const std::array<double,9>& Distances,std::uint32_t VoxelSizeMicrons,int MaxLod,
const std::array<std::int64_t,9>& PixelBudgetsSubvoxels,
@@ -552,17 +697,26 @@ namespace StandaloneScreenSpaceGate
const double VoxelMeters=static_cast<double>(VoxelSizeMicrons)/1000000.0;
const FViewBasis View=MakeViewBasis(CameraYawDegrees,CameraPitchDegrees);
std::array<double,9> Budgets{};
std::array<FErrorRow,9> ErrorRows{};
std::array<FPopRow,9> PopRows{};
VoxelForgeCore::TileGenerationOptions BaselineOptions;
BaselineOptions.overrideFarDisplayMesh=true;
VoxelForgeCore::TileGenerationOptions CandidateOptions;
CandidateOptions.overrideFarDisplayMesh=true;
CandidateOptions.farDisplayGeometryBudgetMillipixels=Assembly.recipe.farDisplayGeometryBudgetMillipixels;
CandidateOptions.farRefinementGridScale=Assembly.recipe.farRefinementGridScale;
CandidateOptions.farRefinementLodMask=Assembly.recipe.farRefinementLodMask;
CandidateOptions.farConnectorStationScale=Assembly.recipe.farConnectorStationScale;
CandidateOptions.farConnectorRayMask=Assembly.recipe.farConnectorRayMask;
CandidateOptions.farConnectorReuseStationRadii=Assembly.recipe.farConnectorReuseStationRadii;
bool GatePass=true;
std::printf("[ScreenSpaceGate] angular_pixel=%.9f rad (horizontal_fov=90deg,width=2560) lod>=3_limit=1.0px sampling=65x65 skirts=excluded metric=local_surface_normal_projected field_reference=direct-Heightfield2D-roots normal_probe_vox=1 view_yaw_deg=%.3f view_pitch_deg=%.3f\n",
constexpr double ShadeLimit=1.0/255.0;
std::printf("[ScreenSpaceGate] angular_pixel=%.9f rad (horizontal_fov=90deg,width=2560) lod>=3_geometry_limit=1.0px same_lod_reference=today mesh sampling=65x65 skirts=excluded shading=max(lambert-delta,material-selector-or-blend-delta) shade_limit=1/255 normal_probe_vox=1 view_yaw_deg=%.3f view_pitch_deg=%.3f\n",
OnePixelRadians,CameraYawDegrees,CameraPitchDegrees);
for(int Lod=0;Lod<=MaxLod&&Lod<9;++Lod)
{
Budgets[Lod]=Distances[Lod]*OnePixelRadians;
std::printf("[ScreenSpaceDistance] lod=%d closest_m=%.3f pixel_budget_m=%.6f pixel_budget_cm=%.3f pixel_budget_subvoxels=%lld rule=%s\n",
Lod,Distances[Lod],Budgets[Lod],Budgets[Lod]*100.0,
static_cast<long long>(PixelBudgetsSubvoxels[Lod]),Lod<3?"exact-2cm":"1px");
static_cast<long long>(PixelBudgetsSubvoxels[Lod]),Lod<3?"exact-2cm":"same_lod_1px");
}
const double LOD3CellMeters=static_cast<double>(CHUNK_SIZE*(1<<3))*VoxelMeters/64.0;
PrintKnownFailureControl(LOD3CellMeters,Distances[3],View);
@@ -578,115 +732,117 @@ namespace StandaloneScreenSpaceGate
const FStandaloneCpuTile* CoarsePtr=&CoarseTile;
const std::span<const FStandaloneCpuTile* const> CoarseSpan(&CoarsePtr,1);
const FHeightGrid CoarseGrid=Rasterize(CoarseSpan,CoarseTile.Key,VoxelMeters);
const FStandaloneCpuTile ExactTile=Streamer.GenerateDiagnostic(
CoarseTile.Key,0,1,true);
const FStandaloneCpuTile* ExactPointer=&ExactTile;
const std::span<const FStandaloneCpuTile* const> ExactSpan(&ExactPointer,1);
const FHeightGrid ExactGrid=Rasterize(ExactSpan,ExactTile.Key,VoxelMeters);
ErrorRows[Lod]=CompareToReference(CoarseGrid,ExactGrid,CoarseTile.Key,VoxelMeters,
Budgets[Lod],Assembly,View);
const FErrorRow& Error=ErrorRows[Lod];
FStandaloneCpuTile BaselineTile=Streamer.GenerateDiagnostic(
CoarseTile.Key,0,1,true,&BaselineOptions,true);
const FStandaloneCpuTile* BaselinePointer=&BaselineTile;
const std::span<const FStandaloneCpuTile* const> BaselineSpan(&BaselinePointer,1);
const FHeightGrid BaselineGrid=Rasterize(BaselineSpan,BaselineTile.Key,VoxelMeters);
const FMeshComparisonRow MeshDelta=CompareMeshes(BaselineGrid,CoarseGrid,
CoarseTile.Key,VoxelMeters,Budgets[Lod],View);
const FErrorRow& Error=MeshDelta.Geometry;
const bool LodPass=Error.Valid&&Error.Missing==0&&Error.Floating==0
&&Error.Samples>=16&&Error.Maximum<=1.0;
&&MeshDelta.Cracks==0&&Error.Samples>=16&&Error.Maximum<=1.0
&&MeshDelta.ShadeMaximum<=ShadeLimit;
GatePass=GatePass&&LodPass;
std::printf("[ScreenSpaceGateLOD] lod=%d key=(%d,%d,%d) samples=%llu normal_px_p50=%.4f p99=%.4f max=%.4f vertical_px_p50=%.4f p99=%.4f max=%.4f max_normal_m=%.6f max_xy_m=(%.3f,%.3f) max_slope_deg=%.2f missing=%llu floating=%llu status=%s\n",
std::printf("[ScreenSpaceGateLOD] lod=%d key=(%d,%d,%d) samples=%llu geometry_px_p50=%.4f p99=%.4f max=%.4f shading_delta_p50=%.6f p99=%.6f max=%.6f normal_deg_p50=%.4f p99=%.4f max=%.4f material_mismatch=%llu holes=%llu cracks=%llu floating=%llu status=%s\n",
Lod,CoarseTile.Key.Coord.X,CoarseTile.Key.Coord.Y,CoarseTile.Key.Coord.Z,
static_cast<unsigned long long>(Error.Samples),Error.P50,Error.P99,Error.Maximum,
Error.VerticalP50,Error.VerticalP99,Error.VerticalMaximum,
Error.MaximumNormalDistanceMeters,Error.MaximumX,Error.MaximumY,
Error.MaximumSlopeDegrees,
static_cast<unsigned long long>(Error.Missing),static_cast<unsigned long long>(Error.Floating),
MeshDelta.ShadeP50,MeshDelta.ShadeP99,MeshDelta.ShadeMaximum,
MeshDelta.NormalDegreesP50,MeshDelta.NormalDegreesP99,MeshDelta.NormalDegreesMaximum,
static_cast<unsigned long long>(MeshDelta.MaterialMismatches),
static_cast<unsigned long long>(MeshDelta.Holes),
static_cast<unsigned long long>(MeshDelta.Cracks),
static_cast<unsigned long long>(MeshDelta.Floating),
LodPass?"PASS":"REJECT");
PrintSlopeTail("same_lod",Lod,Error.SlopeSamples,Error.SlopeP99,Error.SlopeMaximum);
const FErrorRow FieldError=CompareToExact(CoarseGrid,CoarseTile.Key,VoxelMeters,
Budgets[Lod],Assembly,View);
const bool FieldPass=FieldError.Valid&&FieldError.Missing==0&&FieldError.Floating==0
&&FieldError.Samples>=16&&FieldError.Maximum<=1.0;
GatePass=GatePass&&FieldPass;
std::printf("[ScreenSpaceGateFieldLOD] lod=%d samples=%llu normal_px_p50=%.4f p99=%.4f max=%.4f vertical_px_p50=%.4f p99=%.4f max=%.4f max_normal_m=%.6f max_xyz_m=(%.3f,%.3f,%.3f) max_slope_deg=%.2f missing=%llu floating=%llu status=%s\n",
std::printf("[ScreenSpaceGateFieldLOD] lod=%d samples=%llu field_geometry_px_p50=%.4f p99=%.4f max=%.4f missing=%llu floating=%llu status=INFO_ONLY\n",
Lod,static_cast<unsigned long long>(FieldError.Samples),FieldError.P50,
FieldError.P99,FieldError.Maximum,FieldError.VerticalP50,FieldError.VerticalP99,
FieldError.VerticalMaximum,FieldError.MaximumNormalDistanceMeters,
FieldError.MaximumX,FieldError.MaximumY,FieldError.MaximumDisplayedZ,
FieldError.MaximumSlopeDegrees,
FieldError.P99,FieldError.Maximum,
static_cast<unsigned long long>(FieldError.Missing),
static_cast<unsigned long long>(FieldError.Floating),FieldPass?"PASS":"REJECT");
PrintSlopeTail("field",Lod,FieldError.SlopeSamples,FieldError.SlopeP99,
FieldError.SlopeMaximum);
static_cast<unsigned long long>(FieldError.Floating));
bool FaultControlsPass=true;
const FHeightGrid MissingGrid(ExactGrid.Side,ExactGrid.MinimumX,ExactGrid.MaximumX,
ExactGrid.MinimumY,ExactGrid.MaximumY);
const FErrorRow MissingError=CompareToReference(MissingGrid,ExactGrid,
CoarseTile.Key,VoxelMeters,Budgets[Lod],Assembly,View);
const bool MissingRejected=MissingError.Missing>0&&MissingError.Maximum>1.0;
const FHeightGrid MissingGrid(BaselineGrid.Side,BaselineGrid.MinimumX,BaselineGrid.MaximumX,
BaselineGrid.MinimumY,BaselineGrid.MaximumY);
const FMeshComparisonRow MissingError=CompareMeshes(BaselineGrid,MissingGrid,
CoarseTile.Key,VoxelMeters,Budgets[Lod],View);
const bool MissingRejected=MissingError.Holes>0&&MissingError.Geometry.Maximum>1.0;
FaultControlsPass=FaultControlsPass&&MissingRejected;
std::printf("[ScreenSpaceFaultInjection] lod=%d case=missing-displayed-surface missing=%llu status=%s\n",
Lod,static_cast<unsigned long long>(MissingError.Missing),
std::printf("[ScreenSpaceFaultInjection] lod=%d case=missing-displayed-surface holes=%llu status=%s\n",
Lod,static_cast<unsigned long long>(MissingError.Holes),
MissingRejected?"REJECTED_AS_REQUIRED":"GATE_FALSE_NEGATIVE");
FStandaloneCpuTile FloatingTile=CoarseTile;
FStandaloneCpuTile FloatingTile=BaselineTile;
const std::uint32_t FloatingVertexBase=static_cast<std::uint32_t>(FloatingTile.Vertices.size());
for(FStandaloneCpuVertex Vertex:CoarseTile.Vertices)
for(FStandaloneCpuVertex Vertex:BaselineTile.Vertices)
{
Vertex.Position[2]+=2.0f;
FloatingTile.Vertices.push_back(Vertex);
}
for(const std::uint32_t Index:CoarseTile.Indices)
FloatingTile.VertexColors.insert(FloatingTile.VertexColors.end(),
BaselineTile.VertexColors.begin(),BaselineTile.VertexColors.end());
for(const std::uint32_t Index:BaselineTile.Indices)
FloatingTile.Indices.push_back(FloatingVertexBase+Index);
FloatingTile.TriangleSkirtFaceMasks.insert(FloatingTile.TriangleSkirtFaceMasks.end(),
CoarseTile.TriangleSkirtFaceMasks.begin(),CoarseTile.TriangleSkirtFaceMasks.end());
BaselineTile.TriangleSkirtFaceMasks.begin(),BaselineTile.TriangleSkirtFaceMasks.end());
const FStandaloneCpuTile* FloatingPointer=&FloatingTile;
const std::span<const FStandaloneCpuTile* const> FloatingSpan(&FloatingPointer,1);
const FHeightGrid FloatingGrid=Rasterize(FloatingSpan,CoarseTile.Key,VoxelMeters);
const FErrorRow FloatingError=CompareToReference(FloatingGrid,ExactGrid,
CoarseTile.Key,VoxelMeters,Budgets[Lod],Assembly,View);
const bool FloatingRejected=FloatingError.Floating>0||FloatingError.Maximum>1.0;
const FHeightGrid FloatingGrid=Rasterize(FloatingSpan,BaselineTile.Key,VoxelMeters);
const FMeshComparisonRow FloatingError=CompareMeshes(BaselineGrid,FloatingGrid,
CoarseTile.Key,VoxelMeters,Budgets[Lod],View);
const bool FloatingRejected=FloatingError.Floating>0||FloatingError.Geometry.Maximum>1.0;
FaultControlsPass=FaultControlsPass&&FloatingRejected;
std::printf("[ScreenSpaceFaultInjection] lod=%d case=2m-floating-surface floating=%llu max_px=%.4f status=%s\n",
Lod,static_cast<unsigned long long>(FloatingError.Floating),FloatingError.Maximum,
Lod,static_cast<unsigned long long>(FloatingError.Floating),FloatingError.Geometry.Maximum,
FloatingRejected?"REJECTED_AS_REQUIRED":"GATE_FALSE_NEGATIVE");
std::vector<FStandaloneCpuTile> FineTiles;
FineTiles.reserve(8);
std::vector<FStandaloneCpuTile> FineBeforeTiles,FineAfterTiles;
FineBeforeTiles.reserve(8);FineAfterTiles.reserve(8);
for(int Dz=0;Dz<=1;++Dz)
for(int Dy=0;Dy<=1;++Dy)
for(int Dx=0;Dx<=1;++Dx)
{
const FVoxelTileKey FineKey(FIntVector(2*CoarseTile.Key.Coord.X+Dx,
2*CoarseTile.Key.Coord.Y+Dy,2*CoarseTile.Key.Coord.Z+Dz),Lod-1);
FStandaloneCpuTile FineTile=Streamer.GenerateDiagnostic(FineKey);
if(!FineTile.Vertices.empty())FineTiles.push_back(std::move(FineTile));
FStandaloneCpuTile FineBefore=Streamer.GenerateDiagnostic(
FineKey,0,1,true,&BaselineOptions,true);
FStandaloneCpuTile FineAfter=Streamer.GenerateDiagnostic(
FineKey,0,1,true,nullptr,true);
if(!FineBefore.Vertices.empty())FineBeforeTiles.push_back(std::move(FineBefore));
if(!FineAfter.Vertices.empty())FineAfterTiles.push_back(std::move(FineAfter));
}
std::vector<const FStandaloneCpuTile*> FinePointers;
FinePointers.reserve(FineTiles.size());
for(const auto& Tile:FineTiles)FinePointers.push_back(&Tile);
const auto FineSpan=std::span<const FStandaloneCpuTile* const>(
FinePointers.data(),FinePointers.size());
const FHeightGrid FineGrid=Rasterize(FineSpan,CoarseTile.Key,VoxelMeters);
PopRows[Lod]=ComparePop(CoarseGrid,FineGrid,CoarseTile.Key,VoxelMeters,
std::vector<const FStandaloneCpuTile*> FineBeforePointers,FineAfterPointers;
FineBeforePointers.reserve(FineBeforeTiles.size());FineAfterPointers.reserve(FineAfterTiles.size());
for(const auto& Tile:FineBeforeTiles)FineBeforePointers.push_back(&Tile);
for(const auto& Tile:FineAfterTiles)FineAfterPointers.push_back(&Tile);
const auto FineBeforeSpan=std::span<const FStandaloneCpuTile* const>(
FineBeforePointers.data(),FineBeforePointers.size());
const auto FineAfterSpan=std::span<const FStandaloneCpuTile* const>(
FineAfterPointers.data(),FineAfterPointers.size());
const FHeightGrid FineBeforeGrid=Rasterize(FineBeforeSpan,CoarseTile.Key,VoxelMeters);
const FHeightGrid FineAfterGrid=Rasterize(FineAfterSpan,CoarseTile.Key,VoxelMeters);
const FPopRow PopBefore=ComparePop(BaselineGrid,FineBeforeGrid,CoarseTile.Key,VoxelMeters,
Budgets[Lod],Assembly,View);
const FPopRow& Pop=PopRows[Lod];
const bool PopPass=Pop.Valid&&Pop.Cracks==0&&Pop.Floating==0&&Pop.Maximum<=1.0;
const FPopRow PopAfter=ComparePop(CoarseGrid,FineAfterGrid,CoarseTile.Key,VoxelMeters,
Budgets[Lod],Assembly,View);
const bool PopPass=PopNotWorseAtAnySample(PopBefore,PopAfter);
GatePass=GatePass&&PopPass;
std::printf("[ScreenSpaceSwap] lod=%d_to_%d samples=%llu normal_px_p50=%.4f p99=%.4f max=%.4f vertical_px_p50=%.4f p99=%.4f max=%.4f max_normal_m=%.6f max_xyz_m=(%.3f,%.3f,%.3f) max_slope_deg=%.2f cracks=%llu floating=%llu status=%s\n",
Lod,Lod-1,static_cast<unsigned long long>(Pop.Samples),
Pop.P50,Pop.P99,Pop.Maximum,Pop.VerticalP50,Pop.VerticalP99,Pop.VerticalMaximum,
Pop.MaximumNormalDistanceMeters,Pop.MaximumX,Pop.MaximumY,
Pop.MaximumDisplayedZ,
Pop.MaximumSlopeDegrees,static_cast<unsigned long long>(Pop.Cracks),
static_cast<unsigned long long>(Pop.Floating),
std::printf("[ScreenSpaceSwap] swap=%d_to_%d distance_m=%.3f samples_before=%llu pop_before_px_p50=%.4f p99=%.4f max=%.4f pop_after_px_p50=%.4f p99=%.4f max=%.4f missing_root_samples_before=%llu after=%llu status=%s\n",
Lod,Lod-1,Distances[Lod],static_cast<unsigned long long>(PopBefore.Samples),
PopBefore.P50,PopBefore.P99,PopBefore.Maximum,PopAfter.P50,PopAfter.P99,PopAfter.Maximum,
static_cast<unsigned long long>(PopBefore.Cracks),static_cast<unsigned long long>(PopAfter.Cracks),
PopPass?"PASS":"REJECT");
PrintSlopeTail("swap",Lod,Pop.SlopeSamples,Pop.SlopeP99,Pop.SlopeMaximum);
const FHeightGrid MissingFineGrid(CoarseGrid.Side,CoarseGrid.MinimumX,
CoarseGrid.MaximumX,CoarseGrid.MinimumY,CoarseGrid.MaximumY);
const FPopRow MissingFineError=ComparePop(CoarseGrid,MissingFineGrid,
const FHeightGrid MissingFineGrid(BaselineGrid.Side,BaselineGrid.MinimumX,
BaselineGrid.MaximumX,BaselineGrid.MinimumY,BaselineGrid.MaximumY);
const FPopRow MissingFineError=ComparePop(BaselineGrid,MissingFineGrid,
CoarseTile.Key,VoxelMeters,Budgets[Lod],Assembly,View);
const bool CrackRejected=MissingFineError.Cracks>0&&MissingFineError.Maximum>1.0;
FaultControlsPass=FaultControlsPass&&CrackRejected;
GatePass=GatePass&&FaultControlsPass;
std::printf("[ScreenSpaceFaultInjection] lod=%d case=missing-fine-ring-surface cracks=%llu max_px=%.4f status=%s\n",
Lod,static_cast<unsigned long long>(MissingFineError.Cracks),
MissingFineError.Maximum,CrackRejected?"REJECTED_AS_REQUIRED":"GATE_FALSE_NEGATIVE");
@@ -695,25 +851,170 @@ namespace StandaloneScreenSpaceGate
const auto CheckNegative=[&](const char* Name,std::uint32_t Drop,std::uint32_t Stride)
{
FStandaloneCpuTile Bad=Streamer.GenerateDiagnostic(
CoarseTile.Key,Drop,Stride,true);
CoarseTile.Key,Drop,Stride,true,nullptr,true);
const FStandaloneCpuTile* BadPointer=&Bad;
const std::span<const FStandaloneCpuTile* const> BadSpan(&BadPointer,1);
const FHeightGrid BadGrid=Rasterize(BadSpan,Bad.Key,VoxelMeters);
const FErrorRow BadError=CompareToReference(BadGrid,ExactGrid,
CoarseTile.Key,VoxelMeters,Budgets[Lod],Assembly,View);
const bool Rejected=BadError.Missing>0||BadError.Floating>0
||BadError.Samples<16||BadError.Maximum>1.0;
const FMeshComparisonRow BadError=CompareMeshes(BaselineGrid,BadGrid,
CoarseTile.Key,VoxelMeters,Budgets[Lod],View);
const bool Rejected=BadError.Holes>0||BadError.Floating>0||BadError.Cracks>0
||BadError.Geometry.Samples<16||BadError.Geometry.Maximum>1.0
||BadError.ShadeMaximum>ShadeLimit;
NegativeControlsPass=NegativeControlsPass&&Rejected;
std::printf("[ScreenSpaceNegative] lod=%d case=%s p50_px=%.4f p99_px=%.4f max_px=%.4f missing=%llu floating=%llu status=%s\n",
Lod,Name,BadError.P50,BadError.P99,BadError.Maximum,
static_cast<unsigned long long>(BadError.Missing),
std::printf("[ScreenSpaceNegative] lod=%d case=%s p50_px=%.4f p99_px=%.4f max_px=%.4f shade_max=%.6f holes=%llu cracks=%llu floating=%llu status=%s\n",
Lod,Name,BadError.Geometry.P50,BadError.Geometry.P99,BadError.Geometry.Maximum,
BadError.ShadeMaximum,static_cast<unsigned long long>(BadError.Holes),
static_cast<unsigned long long>(BadError.Cracks),
static_cast<unsigned long long>(BadError.Floating),
Rejected?"REJECTED_AS_REQUIRED":"GATE_FALSE_NEGATIVE");
};
CheckNegative("s13-global-octave-drop",1,1);
CheckNegative("stride-2-column-interpolation",0,2);
GatePass=GatePass&&NegativeControlsPass;
GatePass=GatePass&&FaultControlsPass&&NegativeControlsPass;
}
struct FCoverageRow
{
std::uint64_t Tiles=0,Holes=0,Cracks=0,Floating=0,MaterialMismatches=0;
std::vector<double> GeometryPixels,ShadeValues,NormalAngles;
};
std::array<FCoverageRow,9> Coverage;
for(const FVoxelTileKey& Key:DesiredKeys)
{
const int Lod=Key.Level;
if(Lod<3||Lod>MaxLod||Lod>=9)continue;
FStandaloneCpuTile BaselineTile=Streamer.GenerateDiagnostic(
Key,0,1,true,&BaselineOptions,true);
FStandaloneCpuTile CandidateTile=Streamer.GenerateDiagnostic(
Key,0,1,true,&CandidateOptions,true);
const FStandaloneCpuTile* BaselinePointer=&BaselineTile;
const FStandaloneCpuTile* CandidatePointer=&CandidateTile;
const std::span<const FStandaloneCpuTile* const> BaselineSpan(&BaselinePointer,1);
const std::span<const FStandaloneCpuTile* const> CandidateSpan(&CandidatePointer,1);
const FHeightGrid BaselineGrid=Rasterize(BaselineSpan,Key,VoxelMeters);
const FHeightGrid CandidateGrid=Rasterize(CandidateSpan,Key,VoxelMeters);
const FMeshComparisonRow Delta=CompareMeshes(BaselineGrid,CandidateGrid,
Key,VoxelMeters,Budgets[Lod],View);
auto& Row=Coverage[Lod];
++Row.Tiles;
Row.Holes+=Delta.Holes;Row.Cracks+=Delta.Cracks;Row.Floating+=Delta.Floating;
Row.MaterialMismatches+=Delta.MaterialMismatches;
Row.GeometryPixels.insert(Row.GeometryPixels.end(),
Delta.GeometryPixels.begin(),Delta.GeometryPixels.end());
Row.ShadeValues.insert(Row.ShadeValues.end(),Delta.ShadeValues.begin(),Delta.ShadeValues.end());
Row.NormalAngles.insert(Row.NormalAngles.end(),Delta.NormalAngles.begin(),Delta.NormalAngles.end());
}
for(int Lod=3;Lod<=MaxLod&&Lod<9;++Lod)
{
auto& Row=Coverage[Lod];
const double GeometryP50=Percentile(Row.GeometryPixels,0.50);
const double GeometryP99=Percentile(Row.GeometryPixels,0.99);
const double GeometryMaximum=Row.GeometryPixels.empty()?0.0
:*std::max_element(Row.GeometryPixels.begin(),Row.GeometryPixels.end());
const double ShadeP50=Percentile(Row.ShadeValues,0.50);
const double ShadeP99=Percentile(Row.ShadeValues,0.99);
const double ShadeMaximum=Row.ShadeValues.empty()?0.0
:*std::max_element(Row.ShadeValues.begin(),Row.ShadeValues.end());
const double NormalP50=Percentile(Row.NormalAngles,0.50);
const double NormalP99=Percentile(Row.NormalAngles,0.99);
const double NormalMaximum=Row.NormalAngles.empty()?0.0
:*std::max_element(Row.NormalAngles.begin(),Row.NormalAngles.end());
const bool LodPass=Row.Tiles>0&&Row.GeometryPixels.size()>=16
&&GeometryMaximum<=1.0&&ShadeMaximum<=ShadeLimit
&&Row.Holes==0&&Row.Cracks==0&&Row.Floating==0;
GatePass=GatePass&&LodPass;
std::printf("[ScreenSpaceGateCoverageLOD] lod=%d tiles=%llu geometry_samples=%llu geometry_px_p50=%.4f p99=%.4f max=%.4f shading_delta_p50=%.6f p99=%.6f max=%.6f normal_deg_p50=%.4f p99=%.4f max=%.4f material_mismatch=%llu holes=%llu cracks=%llu floating=%llu status=%s\n",
Lod,static_cast<unsigned long long>(Row.Tiles),
static_cast<unsigned long long>(Row.GeometryPixels.size()),
GeometryP50,GeometryP99,GeometryMaximum,ShadeP50,ShadeP99,ShadeMaximum,
NormalP50,NormalP99,NormalMaximum,
static_cast<unsigned long long>(Row.MaterialMismatches),
static_cast<unsigned long long>(Row.Holes),
static_cast<unsigned long long>(Row.Cracks),
static_cast<unsigned long long>(Row.Floating),LodPass?"PASS":"REJECT");
}
std::uint64_t RepairOpportunities=0,BaselineWallHoles=0,BaselineWallFloating=0;
std::uint64_t CandidateWallRays=0,CandidateWallHoles=0,CandidateWallFloating=0;
std::vector<std::uint64_t> BaselineHoleIds,CandidateHoleIds;
std::vector<std::uint64_t> BaselineFloatingIds,CandidateFloatingIds;
std::vector<std::uint64_t> CandidatePanelRemovalHoleIds;
bool bRanPanelRemovalControl=false;
for(const FVoxelTileKey& Key:DesiredKeys)
{
if(Key.Level<6||Key.Level>8)continue;
VoxelForgeCore::TileGenerationOptions NoPanelBaseline=BaselineOptions;
NoPanelBaseline.diagnosticRemoveFarConnectorPanels=true;
NoPanelBaseline.diagnosticAuditFarConnectorCoverage=true;
const FStandaloneCpuTile BaseNoPanels=Streamer.GenerateDiagnostic(Key,0,1,true,&NoPanelBaseline);
RepairOpportunities+=BaseNoPanels.CoreDiagnostics.farConnectorAuditHoles;
VoxelForgeCore::TileGenerationOptions AuditedBaseline=BaselineOptions;
AuditedBaseline.diagnosticAuditFarConnectorCoverage=true;
const FStandaloneCpuTile BaseRepaired=Streamer.GenerateDiagnostic(Key,0,1,true,&AuditedBaseline);
BaselineWallHoles+=BaseRepaired.CoreDiagnostics.farConnectorAuditHoles;
BaselineWallFloating+=BaseRepaired.CoreDiagnostics.farConnectorAuditFloating;
BaselineHoleIds.insert(BaselineHoleIds.end(),
BaseRepaired.CoreDiagnostics.farConnectorAuditHoleIds.begin(),
BaseRepaired.CoreDiagnostics.farConnectorAuditHoleIds.end());
BaselineFloatingIds.insert(BaselineFloatingIds.end(),
BaseRepaired.CoreDiagnostics.farConnectorAuditFloatingIds.begin(),
BaseRepaired.CoreDiagnostics.farConnectorAuditFloatingIds.end());
VoxelForgeCore::TileGenerationOptions AuditedCandidate=CandidateOptions;
AuditedCandidate.diagnosticAuditFarConnectorCoverage=true;
const FStandaloneCpuTile CandidateRepaired=Streamer.GenerateDiagnostic(Key,0,1,true,&AuditedCandidate);
CandidateWallRays+=CandidateRepaired.CoreDiagnostics.farConnectorAuditRays;
CandidateWallHoles+=CandidateRepaired.CoreDiagnostics.farConnectorAuditHoles;
CandidateWallFloating+=CandidateRepaired.CoreDiagnostics.farConnectorAuditFloating;
CandidateHoleIds.insert(CandidateHoleIds.end(),
CandidateRepaired.CoreDiagnostics.farConnectorAuditHoleIds.begin(),
CandidateRepaired.CoreDiagnostics.farConnectorAuditHoleIds.end());
CandidateFloatingIds.insert(CandidateFloatingIds.end(),
CandidateRepaired.CoreDiagnostics.farConnectorAuditFloatingIds.begin(),
CandidateRepaired.CoreDiagnostics.farConnectorAuditFloatingIds.end());
if(!bRanPanelRemovalControl&&BaseNoPanels.CoreDiagnostics.farConnectorAuditHoles>0)
{
AuditedCandidate.diagnosticRemoveFarConnectorPanels=true;
const FStandaloneCpuTile CandidateNoPanels=Streamer.GenerateDiagnostic(
Key,0,1,true,&AuditedCandidate);
CandidatePanelRemovalHoleIds=CandidateNoPanels.CoreDiagnostics.farConnectorAuditHoleIds;
bRanPanelRemovalControl=true;
}
}
std::sort(BaselineHoleIds.begin(),BaselineHoleIds.end());
std::sort(CandidateHoleIds.begin(),CandidateHoleIds.end());
std::sort(BaselineFloatingIds.begin(),BaselineFloatingIds.end());
std::sort(CandidateFloatingIds.begin(),CandidateFloatingIds.end());
const auto IsSubset=[](const std::vector<std::uint64_t>& A,const std::vector<std::uint64_t>& B)
{return std::includes(B.begin(),B.end(),A.begin(),A.end());};
std::sort(CandidatePanelRemovalHoleIds.begin(),CandidatePanelRemovalHoleIds.end());
const bool bNoNewHoles=IsSubset(CandidateHoleIds,BaselineHoleIds);
const bool bNoNewFloating=IsSubset(CandidateFloatingIds,BaselineFloatingIds);
const bool bPanelRemovalDetected=bRanPanelRemovalControl
&&!IsSubset(CandidatePanelRemovalHoleIds,CandidateHoleIds);
std::vector<std::uint64_t> NewHoleIds,NewFloatingIds,PanelRemovalNewHoleIds;
std::set_difference(CandidateHoleIds.begin(),CandidateHoleIds.end(),
BaselineHoleIds.begin(),BaselineHoleIds.end(),std::back_inserter(NewHoleIds));
std::set_difference(CandidateFloatingIds.begin(),CandidateFloatingIds.end(),
BaselineFloatingIds.begin(),BaselineFloatingIds.end(),std::back_inserter(NewFloatingIds));
std::set_difference(CandidatePanelRemovalHoleIds.begin(),CandidatePanelRemovalHoleIds.end(),
CandidateHoleIds.begin(),CandidateHoleIds.end(),std::back_inserter(PanelRemovalNewHoleIds));
const bool bRepairRowsPass=RepairOpportunities>0&&bNoNewHoles&&bNoNewFloating
&&bPanelRemovalDetected;
GatePass=GatePass&&bRepairRowsPass;
std::printf("[ScreenSpaceConnectorGate] lods=6..8 baseline_missing_ray_stations=%llu candidate_audited_rays=%llu baseline_holes=%llu candidate_holes=%llu new_holes=%llu baseline_floating=%llu candidate_floating=%llu new_floating=%llu panel_removal_new_holes=%llu status=%s\n",
static_cast<unsigned long long>(RepairOpportunities),
static_cast<unsigned long long>(CandidateWallRays),
static_cast<unsigned long long>(BaselineWallHoles),
static_cast<unsigned long long>(CandidateWallHoles),
static_cast<unsigned long long>(NewHoleIds.size()),
static_cast<unsigned long long>(BaselineWallFloating),
static_cast<unsigned long long>(CandidateWallFloating),
static_cast<unsigned long long>(NewFloatingIds.size()),
static_cast<unsigned long long>(PanelRemovalNewHoleIds.size()),
bRepairRowsPass?"PASS":"REJECT");
std::printf("[ScreenSpaceGateResult] status=%s\n",GatePass?"PASS":"REJECT");
return GatePass;
}
+2 -1
View File
@@ -412,6 +412,7 @@ namespace StandaloneStreamBenchmark
FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel,
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileColumnOverlap,
FarHorizonLOD8InteriorStepVoxels,bUseSharedColumnCache);
Streamer.SetDisplayAttributeCaptureEnabled(bScreenSpaceGate);
Streamer.SetUniformBlockProofAuditEnabled(bAuditUniformBlockProofs);
if(bScreenSpaceGate||bReportLodWorkProfile)Streamer.SetMesherStageProfilingEnabled(true);
const auto StreamStart=Clock::now();
@@ -1480,7 +1481,7 @@ namespace StandaloneStreamBenchmark
{
if(Assembly)
bScreenSpaceGatePassing=StandaloneScreenSpaceGate::Run(GateSampleTiles,
Streamer,*Assembly,ClosestDisplayDistances,World.CoreVoxelSizeMicrons,
Streamer,Desired.Tiles,*Assembly,ClosestDisplayDistances,World.CoreVoxelSizeMicrons,
World.MaxClipLevel,PixelBudgetsSubvoxels,CameraYawDegrees,
CameraPitchDegrees);
else
+32 -3
View File
@@ -43,9 +43,11 @@ struct FStandaloneCpuTile
FVoxelTileKey Key;
uint64 RequestedCrossing=0;
std::vector<FStandaloneCpuVertex> Vertices;
std::vector<std::array<uint8,4>> VertexColors;
std::vector<uint32> Indices;
std::vector<uint8> TriangleSkirtFaceMasks;
uint32 SurfaceVertexCount=0;
uint32 CeilingTriangleCount=0;
uint32 SkirtTriangleCount=0;
uint64 DensitySamples=0;
double WorkerSeconds=0.0;
@@ -653,12 +655,17 @@ public:
{bProfileMesherStages=Enabled;}
void SetUniformBlockProofAuditEnabled(bool Enabled)
{bAuditUniformBlockProofs=Enabled;}
void SetDisplayAttributeCaptureEnabled(bool Enabled)
{bCaptureDisplayAttributes=Enabled;}
FStandaloneCpuTile GenerateDiagnostic(const FVoxelTileKey& Key,
std::uint32_t GlobalOctaveDrop=0,std::uint32_t CellStride=1,
bool ForceGenerate=false)
bool ForceGenerate=false,
const VoxelForgeCore::TileGenerationOptions* FarDisplayOverride=nullptr,
bool CaptureDisplayAttributes=false)
{
FStandaloneTileJob Job;Job.Key=Key;Job.StartedAt=FClock::now();Job.QueuedAt=Job.StartedAt;
return Generate(Job,GlobalOctaveDrop,CellStride,ForceGenerate);
return Generate(Job,GlobalOctaveDrop,CellStride,ForceGenerate,FarDisplayOverride,
CaptureDisplayAttributes);
}
private:
@@ -778,7 +785,9 @@ private:
}
FStandaloneCpuTile Generate(const FStandaloneTileJob& Job,
std::uint32_t DiagnosticGlobalOctaveDrop=0,std::uint32_t DiagnosticCellStride=1,
bool ForceGenerate=false)
bool ForceGenerate=false,
const VoxelForgeCore::TileGenerationOptions* FarDisplayOverride=nullptr,
bool CaptureDisplayAttributes=false)
{
const auto StartTime=Job.StartedAt;
FStandaloneCpuTile Out;Out.Key=Job.Key;Out.RequestedCrossing=Job.RequestedCrossing;
@@ -829,6 +838,18 @@ private:
Options.diagnosticCellStride=std::max<std::uint32_t>(1,DiagnosticCellStride);
Options.diagnosticColumnInterpolationStride=
std::max<std::uint32_t>(1,DiagnosticCellStride);
if(FarDisplayOverride)
{
Options.overrideFarDisplayMesh=FarDisplayOverride->overrideFarDisplayMesh;
Options.farDisplayGeometryBudgetMillipixels=FarDisplayOverride->farDisplayGeometryBudgetMillipixels;
Options.farRefinementGridScale=FarDisplayOverride->farRefinementGridScale;
Options.farRefinementLodMask=FarDisplayOverride->farRefinementLodMask;
Options.farConnectorStationScale=FarDisplayOverride->farConnectorStationScale;
Options.farConnectorRayMask=FarDisplayOverride->farConnectorRayMask;
Options.farConnectorReuseStationRadii=FarDisplayOverride->farConnectorReuseStationRadii;
Options.diagnosticRemoveFarConnectorPanels=FarDisplayOverride->diagnosticRemoveFarConnectorPanels;
Options.diagnosticAuditFarConnectorCoverage=FarDisplayOverride->diagnosticAuditFarConnectorCoverage;
}
std::string Error;
auto Result=CoreWorld->generateTile(
{{Job.Key.Coord.X,Job.Key.Coord.Y,Job.Key.Coord.Z},Job.Key.Level},Options,Error);
@@ -842,6 +863,7 @@ private:
Out.DensitySamples=Result->densitySampleCount;
Out.CoreDiagnostics=Result->diagnostics;
Out.SurfaceVertexCount=Result->groups.surfaceVertexCount;
Out.CeilingTriangleCount=Result->groups.ceilingTriangleCount;
Out.SkirtTriangleCount=Result->groups.skirtTriangleCount;
Out.MeshPayloadAllocatedBytes=Result->meshPayloadAllocatedBytes;
Out.DensityHash=Result->densityHash;Out.MeshHash=Result->meshHash;
@@ -849,12 +871,18 @@ private:
Out.bHasDensityHash=Result->hasDensityHash;Out.bHasMeshHash=Result->hasMeshHash;
Out.bHasPayloadHash=Result->hasPayloadHash;Out.bHasTopologyHash=Result->hasTopologyHash;
Out.Vertices.reserve(Result->vertices.size());
const bool bCaptureAttributes=CaptureDisplayAttributes||bCaptureDisplayAttributes;
if(bCaptureAttributes)Out.VertexColors.reserve(Result->vertices.size());
for(const auto& Vertex:Result->vertices)
{
Out.Vertices.push_back({
{static_cast<float>(Vertex.position.x*0.01),static_cast<float>(Vertex.position.y*0.01),
static_cast<float>(Vertex.position.z*0.01)},
{static_cast<float>(Vertex.normal.x),static_cast<float>(Vertex.normal.y),
static_cast<float>(Vertex.normal.z)}});
if(bCaptureAttributes)
Out.VertexColors.push_back({Vertex.color.r,Vertex.color.g,Vertex.color.b,Vertex.color.a});
}
Out.Indices=std::move(Result->indices);
Out.TriangleSkirtFaceMasks=std::move(Result->skirtFaceMasks);
if(bProfile)
@@ -1001,6 +1029,7 @@ private:
bool bSkipUV=false;
bool bSkipColors=false;
bool bCaptureColumnCoordinates=false;
bool bCaptureDisplayAttributes=false;
bool bSharedColumnCache=true;
int32 Lod8HorizonInteriorStepVoxels=0;
bool bProfileMesherStages=false;
@@ -30,6 +30,34 @@ namespace StandaloneWorldAssemblyPrototype
return std::max<std::int64_t>(1,WorldAssemblyFixed(wavelength));
}
inline bool ApplyFarDisplayMeshDeclaration(const VFJson::Value& root,
VoxelForgeCore::WorldAssemblyRecipeV1& recipe,std::string& error)
{
const VFJson::Value& declaration=root.Get("far_display_mesh");
if(std::holds_alternative<std::nullptr_t>(declaration.Data))return true;
if(!declaration.IsObject())
{error="far_display_mesh must be an object when declared";return false;}
const auto ReadUnsigned=[&](const char* key,std::uint32_t& output)
{
const VFJson::Value& value=declaration.Get(key);
if(!value.IsNumber()||!std::isfinite(value.Number())||value.Number()<0.0
||value.Number()>static_cast<double>(UINT32_MAX)
||std::floor(value.Number())!=value.Number())return false;
output=static_cast<std::uint32_t>(value.Number());return true;
};
if(!ReadUnsigned("geometry_budget_millipixels",recipe.farDisplayGeometryBudgetMillipixels)
||!ReadUnsigned("refinement_grid_scale",recipe.farRefinementGridScale)
||!ReadUnsigned("refinement_lod_mask",recipe.farRefinementLodMask)
||!ReadUnsigned("connector_station_scale",recipe.farConnectorStationScale)
||!ReadUnsigned("connector_ray_mask",recipe.farConnectorRayMask))
{error="far_display_mesh declaration has a missing or non-integer parameter";return false;}
const VFJson::Value& reuse=declaration.Get("connector_reuse_station_radii");
if(!std::holds_alternative<bool>(reuse.Data))
{error="far_display_mesh connector_reuse_station_radii must be boolean";return false;}
recipe.farConnectorReuseStationRadii=std::get<bool>(reuse.Data);
return true;
}
inline uint64 WorldAssemblyFeatureId(uint32 seed,int64 x,int64 y,uint32 kind)
{
uint64 hash=14695981039346656037ull;
@@ -223,6 +251,7 @@ namespace StandaloneWorldAssemblyPrototype
recipe.biomeClimateReliefWavelengthSubvoxels=WorldAssemblyWavelength(map.ReliefFrequency,3333.0);
recipe.biomeClimateMoistureWavelengthSubvoxels=WorldAssemblyWavelength(map.MoistureFrequency,2500.0);
recipe.biomeClimateReliefContrastQ16=WorldAssemblyQ16(map.ReliefContrast);
if(!ApplyFarDisplayMeshDeclaration(root,recipe,error))return false;
const auto makeProfile=[&](uint32 stableId,const FSurfaceGenerationParams& params,
const FBiomeResolved* climate)
{