wip(assembler): M1 round 4 -- cap rebuilt (no hiding), sub-voxel audit, routes + mesh-free queries

Luna, M1 round 4 (resumed after the night stop):
- Cliff: sub-voxel audit (29,929 half-voxel XY columns, min run 3.75 voxels, 0 subvoxel runs;
  old taper control exposes 20,963) and a root-line shelf at the two-voxel minimum.
- Horizon slab = aliased LOD8 cap ceiling. The first fix hid the ceiling at LOD8 (challenged:
  enclosed, interior-lit strates); now the LOD8 cap is rebuilt deterministically from the
  native heightfield, skirts preserved; looking up and out shows the cap present.
- Routes: review manifest with 33 stable-ID route points + capsule/support constraints; capsule
  sweep 0 blocked samples / 0 unsupported centres, negative control detected.
- Server path: World::densityAt answers without render meshes. Physics cooking not available
  standalone.
- Hashes (evaluator + 2,226-tile stream) identical at W1/W8/W16. LOD8: 4.446 s for 529 tiles,
  14.25 M field calls, 165 unknown tiles.
- Captures at 60 fps cap + vsync (owner's GPU coil whine). One cliff probe crossed the ~8 GB
  guard (7.69 GiB) and was stopped before capturing.
My review: cap view matches legacy closely, no slab; native cliff is a clean attached ledge
(cleaner than legacy); a few small spikes on the ridge top.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-09 14:23:38 +02:00
1 parent 20c6d86f0e
commit 2d0cec90e4
164 files changed
+7503 -31

No files matched your search

@@ -83,7 +83,9 @@ namespace VoxelForgeCore
bool tileWasClassified = false;
bool meshingSkipped = false;
bool featureRefinedResolution = false;
std::uint64_t farCapTrianglesSuppressed = 0;
std::uint64_t capRefinedCells = 0;
std::uint64_t capRefinedTriangles = 0;
bool capRefined = false;
};
struct TileMesh
+24 -1
View File
@@ -794,7 +794,18 @@ inline void AppendFarConnectorShells(const FStandaloneShapeWorld& Shape,const FV
for(int32 I=0;I<CeilingStart;++I)CombinedTriangles.Add(Mesh.Triangles[I]);
for(const int32 Index:NewTriangles)CombinedTriangles.Add(OldSurfaceVertices+Index);
for(int32 I=CeilingStart;I<Mesh.Triangles.Num();++I)CombinedTriangles.Add(Mesh.Triangles[I]);
const int32 OldTriangleCount=Mesh.Triangles.Num()/3;
TArray<uint8> ExistingFaceMasks=Mesh.TriangleSkirtFaceMasks;
ExistingFaceMasks.SetNum(OldTriangleCount);
TArray<uint8> CombinedFaceMasks;CombinedFaceMasks.Reserve(
OldTriangleCount+static_cast<int32>(NewTriangles.size()/3));
for(int32 Triangle=0;Triangle<CeilingStart/3;++Triangle)
CombinedFaceMasks.Add(ExistingFaceMasks[Triangle]);
for(size_t Triangle=0;Triangle<NewTriangles.size()/3;++Triangle)CombinedFaceMasks.Add(0);
for(int32 Triangle=CeilingStart/3;Triangle<OldTriangleCount;++Triangle)
CombinedFaceMasks.Add(ExistingFaceMasks[Triangle]);
Mesh.Triangles=std::move(CombinedTriangles);
Mesh.TriangleSkirtFaceMasks=std::move(CombinedFaceMasks);
Mesh.NumSurfaceVertices=OldSurfaceVertices+AddedVertices;
}
@@ -1029,5 +1040,17 @@ inline void AppendFarStratumShells(const FStandaloneOwnerWorld& World,const FSta
for(int32 I=0;I<CeilingStart;++I)CombinedTriangles.Add(Mesh.Triangles[I]);
for(const int32 Index:NewTriangles)CombinedTriangles.Add(OldSurfaceVertices+Index);
for(int32 I=CeilingStart;I<Mesh.Triangles.Num();++I)CombinedTriangles.Add(Mesh.Triangles[I]);
Mesh.Triangles=std::move(CombinedTriangles);Mesh.NumSurfaceVertices=OldSurfaceVertices+AddedVertices;
const int32 OldTriangleCount=Mesh.Triangles.Num()/3;
TArray<uint8> ExistingFaceMasks=Mesh.TriangleSkirtFaceMasks;
ExistingFaceMasks.SetNum(OldTriangleCount);
TArray<uint8> CombinedFaceMasks;CombinedFaceMasks.Reserve(
OldTriangleCount+static_cast<int32>(NewTriangles.size()/3));
for(int32 Triangle=0;Triangle<CeilingStart/3;++Triangle)
CombinedFaceMasks.Add(ExistingFaceMasks[Triangle]);
for(size_t Triangle=0;Triangle<NewTriangles.size()/3;++Triangle)CombinedFaceMasks.Add(0);
for(int32 Triangle=CeilingStart/3;Triangle<OldTriangleCount;++Triangle)
CombinedFaceMasks.Add(ExistingFaceMasks[Triangle]);
Mesh.Triangles=std::move(CombinedTriangles);
Mesh.TriangleSkirtFaceMasks=std::move(CombinedFaceMasks);
Mesh.NumSurfaceVertices=OldSurfaceVertices+AddedVertices;
}
+27
View File
@@ -1153,6 +1153,33 @@ private:
}
public:
bool GetNativeSurfaceHeightsAt(float X,float Y,int32 ChunkZ,
float& OutGroundZ,float& OutCeilingZ) const
{
if(!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
||!std::isfinite(X)||!std::isfinite(Y))return false;
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
!=ECaveGeneratorType::SurfaceWorld)return false;
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
static_cast<float>((static_cast<double>(ChunkZ*CHUNK_SIZE+CHUNK_SIZE/2))*VOXEL_SIZE));
const auto* Plan=FindWorldAssemblyStratum(StrateIndex,ChunkZ);
if(!Plan)return false;
constexpr double Scale=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
const double MaxVoxel=static_cast<double>(std::numeric_limits<int64>::max())/Scale;
if(std::abs(static_cast<double>(X))>MaxVoxel||std::abs(static_cast<double>(Y))>MaxVoxel)
return false;
const auto ToFixed=[](float Value)
{return static_cast<int64>(std::llround(static_cast<double>(Value)
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));};
const VoxelForgeCore::Heightfield2D Block(WorldAssembly->recipe,*Plan);
if(!Block.isValid())return false;
const auto Column=Block.point(ToFixed(X),ToFixed(Y),
static_cast<int64>(ChunkZ)*CHUNK_SIZE*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
OutGroundZ=static_cast<float>(Column.groundHeightSubvoxels/Scale);
OutCeilingZ=static_cast<float>(Column.capHeightSubvoxels/Scale);
return std::isfinite(OutGroundZ)&&std::isfinite(OutCeilingZ);
}
EVoxelTileClass ClassifyBox(const FIntVector& Origin,int32 Step,int32 Cells) const
{ return ClassifyBoxDetailed(Origin,Step,Cells).Verdict; }
+278 -10
View File
@@ -214,6 +214,279 @@ namespace VoxelForgeCore
default:return FStandaloneShapeWorld::EDetailVariant::Coherent;
}
}
struct FNativeCapVertex
{
double XSub = 0.0;
double YSub = 0.0;
double ZSub = 0.0;
double GroundSub = 0.0;
FVector Normal = FVector(0.0,0.0,-1.0);
FColor Color = FColor::White;
};
struct FNativeCapRefinementStats
{
std::uint64_t Cells = 0;
std::uint64_t Triangles = 0;
bool Refined = false;
};
FVector NormalizeCapNormal(const FVector& Value)
{
const double Length=std::sqrt(Value.X*Value.X+Value.Y*Value.Y+Value.Z*Value.Z);
return Length>1.0e-12?FVector(Value.X/Length,Value.Y/Length,Value.Z/Length)
:FVector(0.0,0.0,-1.0);
}
FNativeCapVertex InterpolateCapVertex(const FNativeCapVertex& A,
const FNativeCapVertex& B,double T)
{
FNativeCapVertex Out;
Out.XSub=A.XSub+(B.XSub-A.XSub)*T;
Out.YSub=A.YSub+(B.YSub-A.YSub)*T;
Out.ZSub=A.ZSub+(B.ZSub-A.ZSub)*T;
Out.GroundSub=A.GroundSub+(B.GroundSub-A.GroundSub)*T;
Out.Normal=NormalizeCapNormal(A.Normal+(B.Normal-A.Normal)*T);
const auto LerpByte=[T](uint8 AValue,uint8 BValue)
{return static_cast<uint8>(std::clamp(std::lround(AValue+(BValue-AValue)*T),0l,255l));};
Out.Color=FColor(LerpByte(A.Color.R,B.Color.R),LerpByte(A.Color.G,B.Color.G),
LerpByte(A.Color.B,B.Color.B),LerpByte(A.Color.A,B.Color.A));
return Out;
}
template<typename DistanceFunction>
int32 ClipNativeCapPolygon(const FNativeCapVertex* Input,int32 InputCount,
FNativeCapVertex* Output,DistanceFunction Distance)
{
if(InputCount<=0)return 0;
int32 OutputCount=0;
FNativeCapVertex Previous=Input[InputCount-1];
double PreviousDistance=Distance(Previous);
bool bPreviousInside=PreviousDistance>=0.0;
for(int32 I=0;I<InputCount;++I)
{
const FNativeCapVertex Current=Input[I];
const double CurrentDistance=Distance(Current);
const bool bCurrentInside=CurrentDistance>=0.0;
if(bCurrentInside!=bPreviousInside)
{
const double Denominator=PreviousDistance-CurrentDistance;
const double T=std::abs(Denominator)>1.0e-15
?std::clamp(PreviousDistance/Denominator,0.0,1.0):0.5;
if(OutputCount<16)Output[OutputCount++]=InterpolateCapVertex(Previous,Current,T);
}
if(bCurrentInside&&OutputCount<16)Output[OutputCount++]=Current;
Previous=Current;PreviousDistance=CurrentDistance;bPreviousInside=bCurrentInside;
}
return OutputCount;
}
FNativeCapRefinementStats RefineLod8NativeCap(const FStandaloneOwnerWorld& Owner,
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,FVoxelMeshData& Mesh)
{
FNativeCapRefinementStats Stats;
if(Key.Level<8||!Owner.Generator)return Stats;
constexpr int32 GridStepVoxels=128;
constexpr int64 Scale=WorldAssemblySubvoxelsPerVoxel;
constexpr int64 GridStepSubvoxels=GridStepVoxels*Scale;
const FIntVector TileOrigin=Key.OriginVoxels();
const int32 TileExtent=Key.ExtentVoxels();
if(TileExtent<=0||TileExtent%GridStepVoxels!=0)return Stats;
const int32 Cells=TileExtent/GridStepVoxels;
const int64 TileMinZ=static_cast<int64>(TileOrigin.Z)*Scale;
const int64 TileMaxZ=static_cast<int64>(TileOrigin.Z+TileExtent)*Scale;
const int32 OldSurfaceVertices=std::clamp(Mesh.NumSurfaceVertices,0,Mesh.Vertices.Num());
const bool bHasUVs=Mesh.UVs.Num()==Mesh.Vertices.Num();
const bool bHasColors=Mesh.Colors.Num()==Mesh.Vertices.Num();
std::vector<FVector> AddedVertices;
std::vector<FVector> AddedNormals;
std::vector<FVector2f> AddedUVs;
std::vector<FColor> AddedColors;
std::vector<int32> AddedTriangles;
const int32 OldCeilingTriangleCount=std::max(0,Mesh.NumCeilingTriangles);
const int32 OldCeilingStart=std::max(0,Mesh.Triangles.Num()-OldCeilingTriangleCount*3);
for(const auto& Plan:Manifest.plan.strates)
{
const int64 StratumMinZ=static_cast<int64>(Plan.bottomChunkZ)*CHUNK_SIZE*Scale;
const int64 StratumMaxZ=(static_cast<int64>(Plan.topChunkZ)+1)*CHUNK_SIZE*Scale;
if(TileMaxZ<StratumMinZ||TileMinZ>StratumMaxZ)continue;
const VoxelForgeCore::Heightfield2D Block(Manifest.recipe,Plan);
if(!Block.isValid())continue;
struct FGridSample
{
double Ground=0.0,Cap=0.0;
FVector Normal=FVector(0.0,0.0,-1.0);
FColor Color=FColor::White;
int32 VertexIndex=INDEX_NONE;
};
std::vector<FGridSample> Grid(static_cast<std::size_t>(Cells+1)*(Cells+1));
const auto GridIndex=[Cells](int32 X,int32 Y)
{return static_cast<std::size_t>(Y)*(Cells+1)+X;};
for(int32 Y=0;Y<=Cells;++Y)
for(int32 X=0;X<=Cells;++X)
{
const int64 XSub=(static_cast<int64>(TileOrigin.X)+static_cast<int64>(X)*GridStepVoxels)*Scale;
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+static_cast<int64>(Y)*GridStepVoxels)*Scale;
const auto Column=Block.point(XSub,YSub,0);
auto& Sample=Grid[GridIndex(X,Y)];
Sample.Ground=static_cast<double>(Column.groundHeightSubvoxels);
Sample.Cap=static_cast<double>(Column.capHeightSubvoxels);
int32 Dominant=0,Neighbor=0;float Blend=0.0f;
Owner.Generator->GetBiomeMaterialAt(static_cast<float>(XSub)/Scale,
static_cast<float>(YSub)/Scale,static_cast<float>(Sample.Cap/Scale),
Dominant,Neighbor,Blend);
const uint8 R=static_cast<uint8>(std::clamp(Dominant,0,255));
const uint8 A=static_cast<uint8>(std::clamp(Neighbor,0,255));
const uint8 B=static_cast<uint8>(std::clamp(std::lround(Blend*255.0f),0l,255l));
Sample.Color=FColor(R,0,B,A);
}
for(int32 Y=0;Y<=Cells;++Y)
for(int32 X=0;X<=Cells;++X)
{
const int32 Left=std::max(0,X-1),Right=std::min(Cells,X+1);
const int32 Down=std::max(0,Y-1),Up=std::min(Cells,Y+1);
const double DX=static_cast<double>(Right-Left)*GridStepSubvoxels;
const double DY=static_cast<double>(Up-Down)*GridStepSubvoxels;
const double SlopeX=DX>0.0?(Grid[GridIndex(Right,Y)].Cap-Grid[GridIndex(Left,Y)].Cap)/DX:0.0;
const double SlopeY=DY>0.0?(Grid[GridIndex(X,Up)].Cap-Grid[GridIndex(X,Down)].Cap)/DY:0.0;
auto& Sample=Grid[GridIndex(X,Y)];
Sample.Normal=NormalizeCapNormal(FVector(SlopeX,SlopeY,-1.0));
Sample.Color.G=static_cast<uint8>(std::clamp(
std::lround((1.0-std::abs(Sample.Normal.Z))*255.0),0l,255l));
}
// Share regular grid vertices across neighboring cells. Only boundary cells
// that cross the solid or tile-Z limit need clipped extra vertices.
for(auto& Sample:Grid)
{
if(Sample.Cap<Sample.Ground||Sample.Cap<TileMinZ||Sample.Cap>TileMaxZ)continue;
Sample.VertexIndex=static_cast<int32>(AddedVertices.size());
const std::size_t Index=static_cast<std::size_t>(&Sample-Grid.data());
const int32 GX=static_cast<int32>(Index%static_cast<std::size_t>(Cells+1));
const int32 GY=static_cast<int32>(Index/static_cast<std::size_t>(Cells+1));
const double XSub=static_cast<double>(TileOrigin.X+GX*GridStepVoxels)*Scale;
const double YSub=static_cast<double>(TileOrigin.Y+GY*GridStepVoxels)*Scale;
AddedVertices.emplace_back(XSub/Scale*VOXEL_SIZE,YSub/Scale*VOXEL_SIZE,
Sample.Cap/Scale*VOXEL_SIZE);
AddedNormals.push_back(Sample.Normal);
if(bHasUVs)AddedUVs.emplace_back(static_cast<float>(XSub/Scale),
static_cast<float>(YSub/Scale));
if(bHasColors)AddedColors.push_back(Sample.Color);
}
for(int32 Y=0;Y<Cells;++Y)
for(int32 X=0;X<Cells;++X)
{
const int32 CellX[4]={X,X+1,X+1,X};
const int32 CellY[4]={Y,Y,Y+1,Y+1};
std::array<int32,4> GridVertices{};
bool bCompleteCell=true;
for(int32 Corner=0;Corner<4;++Corner)
{
GridVertices[Corner]=Grid[GridIndex(CellX[Corner],CellY[Corner])].VertexIndex;
bCompleteCell=bCompleteCell&&GridVertices[Corner]!=INDEX_NONE;
}
if(bCompleteCell)
{
AddedTriangles.insert(AddedTriangles.end(),{GridVertices[0],GridVertices[2],GridVertices[1],
GridVertices[0],GridVertices[3],GridVertices[2]});
++Stats.Cells;Stats.Triangles+=2;
continue;
}
std::array<FNativeCapVertex,16> PolygonA{},PolygonB{};
for(int32 Corner=0;Corner<4;++Corner)
{
const int32 GX=CellX[Corner],GY=CellY[Corner];
const auto& Sample=Grid[GridIndex(GX,GY)];
PolygonA[Corner].XSub=static_cast<double>(TileOrigin.X+GX*GridStepVoxels)*Scale;
PolygonA[Corner].YSub=static_cast<double>(TileOrigin.Y+GY*GridStepVoxels)*Scale;
PolygonA[Corner].ZSub=Sample.Cap;PolygonA[Corner].GroundSub=Sample.Ground;
PolygonA[Corner].Normal=Sample.Normal;PolygonA[Corner].Color=Sample.Color;
}
int32 Count=ClipNativeCapPolygon(PolygonA.data(),4,PolygonB.data(),
[](const auto& V){return V.ZSub-V.GroundSub;});
Count=ClipNativeCapPolygon(PolygonB.data(),Count,PolygonA.data(),
[TileMinZ](const auto& V){return V.ZSub-static_cast<double>(TileMinZ);});
Count=ClipNativeCapPolygon(PolygonA.data(),Count,PolygonB.data(),
[TileMaxZ](const auto& V){return static_cast<double>(TileMaxZ)-V.ZSub;});
if(Count<3)continue;
double TwiceArea=0.0;
for(int32 I=0;I<Count;++I)
{
const auto& A=PolygonB[I];const auto& B=PolygonB[(I+1)%Count];
TwiceArea+=A.XSub*B.YSub-B.XSub*A.YSub;
}
if(std::abs(TwiceArea)<1.0e-5)continue;
++Stats.Cells;
const int32 Base=static_cast<int32>(AddedVertices.size());
for(int32 I=0;I<Count;++I)
{
const auto& V=PolygonB[I];
AddedVertices.emplace_back(V.XSub/Scale*VOXEL_SIZE,
V.YSub/Scale*VOXEL_SIZE,V.ZSub/Scale*VOXEL_SIZE);
AddedNormals.push_back(V.Normal);
if(bHasUVs)AddedUVs.emplace_back(static_cast<float>(V.XSub/Scale),
static_cast<float>(V.YSub/Scale));
if(bHasColors)AddedColors.push_back(V.Color);
}
for(int32 Fan=1;Fan+1<Count;++Fan)
{
// XY cells are counter-clockwise; reverse each fan triangle so the
// ceiling's outward normal faces down into the enclosed strate.
AddedTriangles.insert(AddedTriangles.end(),{Base,Base+Fan+1,Base+Fan});
++Stats.Triangles;
}
}
}
if(AddedTriangles.empty())return Stats;
const int32 AddedVertexCount=static_cast<int32>(AddedVertices.size());
auto InsertSurfaceVertices=[&](auto& Existing,const auto& Added)
{
using TArrayType=std::remove_reference_t<decltype(Existing)>;
TArrayType Combined;Combined.Reserve(Existing.Num()+static_cast<int32>(Added.size()));
for(int32 I=0;I<OldSurfaceVertices;++I)Combined.Add(Existing[I]);
for(const auto& Value:Added)Combined.Add(Value);
for(int32 I=OldSurfaceVertices;I<Existing.Num();++I)Combined.Add(Existing[I]);
Existing=std::move(Combined);
};
InsertSurfaceVertices(Mesh.Vertices,AddedVertices);
InsertSurfaceVertices(Mesh.Normals,AddedNormals);
if(bHasUVs)InsertSurfaceVertices(Mesh.UVs,AddedUVs);
if(bHasColors)InsertSurfaceVertices(Mesh.Colors,AddedColors);
for(int32& Index:Mesh.Triangles)if(Index>=OldSurfaceVertices)Index+=AddedVertexCount;
const int32 OldTriangleCount=Mesh.Triangles.Num()/3;
TArray<uint8> OldMasks=Mesh.TriangleSkirtFaceMasks;
OldMasks.SetNum(OldTriangleCount);
TArray<int32> CombinedTriangles;CombinedTriangles.Reserve(
OldCeilingStart+OldCeilingTriangleCount*3+static_cast<int32>(AddedTriangles.size()));
TArray<uint8> CombinedMasks;CombinedMasks.Reserve(
OldTriangleCount+static_cast<int32>(AddedTriangles.size()/3));
for(int32 Triangle=0;Triangle<OldCeilingStart/3;++Triangle)
{
for(int32 Corner=0;Corner<3;++Corner)CombinedTriangles.Add(Mesh.Triangles[Triangle*3+Corner]);
CombinedMasks.Add(OldMasks[Triangle]);
}
uint32 PreservedCeilingTriangles=0;
for(int32 Triangle=OldCeilingStart/3;Triangle<OldTriangleCount;++Triangle)
{
if(OldMasks[Triangle]==0)continue;
for(int32 Corner=0;Corner<3;++Corner)CombinedTriangles.Add(Mesh.Triangles[Triangle*3+Corner]);
CombinedMasks.Add(OldMasks[Triangle]);++PreservedCeilingTriangles;
}
for(const int32 Index:AddedTriangles)CombinedTriangles.Add(OldSurfaceVertices+Index);
for(std::size_t I=0;I<AddedTriangles.size()/3;++I)CombinedMasks.Add(0);
Mesh.Triangles=std::move(CombinedTriangles);
Mesh.TriangleSkirtFaceMasks=std::move(CombinedMasks);
Mesh.NumSurfaceVertices=OldSurfaceVertices+AddedVertexCount;
Mesh.NumCeilingTriangles=static_cast<int32>(PreservedCeilingTriangles+Stats.Triangles);
Stats.Refined=true;
return Stats;
}
}
struct detail_StandaloneSnapshotAdapter
@@ -721,17 +994,12 @@ namespace VoxelForgeCore
AppendFarStratumShells(world,farShape,nativeKey,step,mesh);
AppendFarConnectorShells(farShape,nativeKey,step,world.CoarseTileCells,mesh);
}
if(bHasWorldAssembly&&key.lod>=8&&mesh.NumCeilingTriangles>0)
if(impl_->worldAssembly&&key.lod>=8)
{
// The finite sky cap remains in the composed field, queries, and local meshes.
// At the coarsest clipmap level its large planar triangles read as a detached
// horizon slab, so omit only that far render group after far-shell construction.
const int32 KeptTriangleCount=std::max(0,mesh.Triangles.Num()/3-mesh.NumCeilingTriangles);
const int32 KeptIndexCount=KeptTriangleCount*3;
out.diagnostics.farCapTrianglesSuppressed=static_cast<std::uint64_t>(mesh.NumCeilingTriangles);
mesh.Triangles.SetNum(KeptIndexCount);
mesh.TriangleSkirtFaceMasks.SetNum(KeptTriangleCount);
mesh.NumCeilingTriangles=0;
const auto CapStats=RefineLod8NativeCap(world,*impl_->worldAssembly,nativeKey,mesh);
out.diagnostics.capRefined=CapStats.Refined;
out.diagnostics.capRefinedCells=CapStats.Cells;
out.diagnostics.capRefinedTriangles=CapStats.Triangles;
}
out.densitySampleCount=static_cast<std::uint64_t>(std::max<int64>(0,sampleCount));