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:
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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user