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)
{