standalone: shape-world look round 2 -- shards gone, calmer layered relief, still 0.72 s / 1.6 s cold
- Removed the thin automatic room-support slabs that left floating caps (in-view detached thin sheets 9 -> 0; the audit now also fails on isolated small components). - Bounded 16-voxel floor/wall blend (18 rejected: it created a sub-voxel sheet), 3-voxel strata/cave blend, C1 tunnel radius profile (0 radius/slope steps at joins). - Surface relief: broad octave with amplitude modulation instead of uniform bumps. - Cold, no cache: 64 m full detail 0.924 s (8 workers) / 0.715 s (16); whole view 2.016 / 1.605 s. - 1-worker and 6-worker hashes match; 5,642/5,642 passage samples open. Not solved: regular bands in the narrow passage shafts, a few slivers on tile boundaries, faint wall/floor plates; close-view mean surface distance to the owner field got worse (see REPORT.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
7e3f926352
commit
97c0455939
12 files changed
+919
-108
No files matched your search
@@ -48,6 +48,162 @@ namespace
|
||||
float Dot(V3 A,V3 B){return A.X*B.X+A.Y*B.Y+A.Z*B.Z;}
|
||||
V3 Cross(V3 A,V3 B){return {A.Y*B.Z-A.Z*B.Y,A.Z*B.X-A.X*B.Z,A.X*B.Y-A.Y*B.X};}
|
||||
V3 Normalize(V3 A){return Mul(A,1.0f/std::sqrt(std::max(1.0e-20f,Dot(A,A))));}
|
||||
|
||||
struct FMeshSheetAudit
|
||||
{
|
||||
uint64 Components=0;
|
||||
uint64 SmallComponents=0;
|
||||
uint64 SmallComponentsInView=0;
|
||||
uint64 BoundarySmallComponentsInView=0;
|
||||
uint64 ThinSheets=0;
|
||||
uint64 TrianglesInThinSheets=0;
|
||||
double MinimumThicknessVoxels=std::numeric_limits<double>::max();
|
||||
};
|
||||
|
||||
FMeshSheetAudit AuditThinMeshSheets(const FStandaloneCpuTile& Tile,
|
||||
const FVector& CameraVoxels,float Yaw,float Pitch,bool bPrintCandidates)
|
||||
{
|
||||
FMeshSheetAudit Result;
|
||||
const size_t VertexCount=Tile.Vertices.size();
|
||||
if(VertexCount==0||Tile.Indices.size()<3)return Result;
|
||||
std::vector<uint32> Parent(VertexCount);
|
||||
std::vector<uint8> Rank(VertexCount,0);
|
||||
std::iota(Parent.begin(),Parent.end(),0u);
|
||||
auto Find=[&](uint32 Value)
|
||||
{
|
||||
uint32 Root=Value;
|
||||
while(Parent[Root]!=Root)Root=Parent[Root];
|
||||
while(Parent[Value]!=Value)
|
||||
{const uint32 Next=Parent[Value];Parent[Value]=Root;Value=Next;}
|
||||
return Root;
|
||||
};
|
||||
auto Union=[&](uint32 A,uint32 B)
|
||||
{
|
||||
A=Find(A);B=Find(B);if(A==B)return;
|
||||
if(Rank[A]<Rank[B])std::swap(A,B);
|
||||
Parent[B]=A;if(Rank[A]==Rank[B])++Rank[A];
|
||||
};
|
||||
const size_t TriangleCount=Tile.Indices.size()/3;
|
||||
for(size_t T=0;T<TriangleCount;++T)
|
||||
{
|
||||
const uint32 A=Tile.Indices[T*3],B=Tile.Indices[T*3+1],C=Tile.Indices[T*3+2];
|
||||
if(A>=VertexCount||B>=VertexCount||C>=VertexCount)continue;
|
||||
Union(A,B);Union(B,C);
|
||||
}
|
||||
struct FComponent
|
||||
{
|
||||
uint64 Triangles=0,Vertices=0;
|
||||
double Min[3]={std::numeric_limits<double>::max(),std::numeric_limits<double>::max(),std::numeric_limits<double>::max()};
|
||||
double Max[3]={-std::numeric_limits<double>::max(),-std::numeric_limits<double>::max(),-std::numeric_limits<double>::max()};
|
||||
double Sum[3]={0.0,0.0,0.0};
|
||||
double Cov[6]={0.0,0.0,0.0,0.0,0.0,0.0};
|
||||
};
|
||||
std::vector<FComponent> Components(VertexCount);
|
||||
for(size_t I=0;I<VertexCount;++I)
|
||||
{
|
||||
const uint32 Root=Find(static_cast<uint32>(I));
|
||||
FComponent& C=Components[Root];++C.Vertices;
|
||||
for(int32 A=0;A<3;++A)
|
||||
{
|
||||
const double P=Tile.Vertices[I].Position[A];
|
||||
C.Min[A]=std::min(C.Min[A],P);C.Max[A]=std::max(C.Max[A],P);C.Sum[A]+=P;
|
||||
}
|
||||
}
|
||||
for(size_t T=0;T<TriangleCount;++T)
|
||||
{
|
||||
const uint32 A=Tile.Indices[T*3],B=Tile.Indices[T*3+1],C=Tile.Indices[T*3+2];
|
||||
if(A>=VertexCount||B>=VertexCount||C>=VertexCount)continue;
|
||||
++Components[Find(A)].Triangles;
|
||||
}
|
||||
for(size_t I=0;I<VertexCount;++I)
|
||||
{
|
||||
const uint32 Root=Find(static_cast<uint32>(I));
|
||||
FComponent& C=Components[Root];
|
||||
if(C.Vertices==0)continue;
|
||||
const double P[3]={Tile.Vertices[I].Position[0],Tile.Vertices[I].Position[1],Tile.Vertices[I].Position[2]};
|
||||
const double D[3]={P[0]-C.Sum[0]/C.Vertices,P[1]-C.Sum[1]/C.Vertices,P[2]-C.Sum[2]/C.Vertices};
|
||||
C.Cov[0]+=D[0]*D[0];C.Cov[1]+=D[1]*D[1];C.Cov[2]+=D[2]*D[2];
|
||||
C.Cov[3]+=D[0]*D[1];C.Cov[4]+=D[0]*D[2];C.Cov[5]+=D[1]*D[2];
|
||||
}
|
||||
const double Camera[3]={CameraVoxels.X*0.25,CameraVoxels.Y*0.25,CameraVoxels.Z*0.25};
|
||||
const double CP=std::cos(Pitch),Forward[3]={std::sin(Yaw)*CP,std::cos(Yaw)*CP,std::sin(Pitch)};
|
||||
const double Right[3]={std::cos(Yaw),-std::sin(Yaw),0.0};
|
||||
const double Up[3]={-std::sin(Yaw)*std::sin(Pitch),-std::cos(Yaw)*std::sin(Pitch),std::cos(Pitch)};
|
||||
for(size_t Root=0;Root<VertexCount;++Root)
|
||||
{
|
||||
FComponent& C=Components[Root];if(C.Triangles==0||C.Vertices==0)continue;
|
||||
++Result.Components;
|
||||
const double Extent[3]={C.Max[0]-C.Min[0],C.Max[1]-C.Min[1],C.Max[2]-C.Min[2]};
|
||||
const double MaxExtent=std::max({Extent[0],Extent[1],Extent[2]})/0.25;
|
||||
const bool bSmall=C.Triangles<=256&&MaxExtent<=20.0;
|
||||
if(bSmall)++Result.SmallComponents;
|
||||
if(!bSmall||C.Vertices<4)continue;
|
||||
double A[3][3]={{C.Cov[0],C.Cov[3],C.Cov[4]},
|
||||
{C.Cov[3],C.Cov[1],C.Cov[5]},{C.Cov[4],C.Cov[5],C.Cov[2]}};
|
||||
for(int32 Sweep=0;Sweep<12;++Sweep)
|
||||
{
|
||||
int32 P=0,Q=1;
|
||||
if(std::abs(A[0][2])>std::abs(A[P][Q])){P=0;Q=2;}
|
||||
if(std::abs(A[1][2])>std::abs(A[P][Q])){P=1;Q=2;}
|
||||
if(std::abs(A[P][Q])<1.0e-16)break;
|
||||
const double Tau=(A[Q][Q]-A[P][P])/(2.0*A[P][Q]);
|
||||
const double T=std::copysign(1.0,Tau)/(std::abs(Tau)+std::sqrt(1.0+Tau*Tau));
|
||||
const double Cos=1.0/std::sqrt(1.0+T*T),Sin=T*Cos;
|
||||
const double App=A[P][P],Aqq=A[Q][Q],Apq=A[P][Q];
|
||||
A[P][P]=App-T*Apq;A[Q][Q]=Aqq+T*Apq;A[P][Q]=A[Q][P]=0.0;
|
||||
for(int32 K=0;K<3;++K)if(K!=P&&K!=Q)
|
||||
{
|
||||
const double Akp=A[K][P],Akq=A[K][Q];
|
||||
A[K][P]=A[P][K]=Cos*Akp-Sin*Akq;
|
||||
A[K][Q]=A[Q][K]=Sin*Akp+Cos*Akq;
|
||||
}
|
||||
}
|
||||
const double Variance=std::max(0.0,std::min({A[0][0],A[1][1],A[2][2]})/C.Vertices);
|
||||
const double ThicknessVoxels=3.5*std::sqrt(Variance)/0.25;
|
||||
const double Center[3]={C.Sum[0]/C.Vertices,C.Sum[1]/C.Vertices,C.Sum[2]/C.Vertices};
|
||||
const double Relative[3]={Center[0]-Camera[0],Center[1]-Camera[1],Center[2]-Camera[2]};
|
||||
const double Depth=Relative[0]*Forward[0]+Relative[1]*Forward[1]+Relative[2]*Forward[2];
|
||||
const double ScreenX=Relative[0]*Right[0]+Relative[1]*Right[1]+Relative[2]*Right[2];
|
||||
const double ScreenY=Relative[0]*Up[0]+Relative[1]*Up[1]+Relative[2]*Up[2];
|
||||
const bool bInView=Depth>0.0&&Depth<=64.0&&std::abs(ScreenX)<0.924*Depth
|
||||
&&std::abs(ScreenY)<0.5774*Depth;
|
||||
if(!bInView)continue;
|
||||
const FIntVector TileOrigin=Tile.Key.OriginVoxels();
|
||||
const int32 TileMin[3]={TileOrigin.X,TileOrigin.Y,TileOrigin.Z};
|
||||
const int32 TileMax[3]={TileOrigin.X+Tile.Key.ExtentVoxels(),
|
||||
TileOrigin.Y+Tile.Key.ExtentVoxels(),TileOrigin.Z+Tile.Key.ExtentVoxels()};
|
||||
bool bTouchesTileBoundary=false;
|
||||
for(int32 Axis=0;Axis<3;++Axis)
|
||||
{
|
||||
const double MinVoxels=C.Min[Axis]*4.0,MaxVoxels=C.Max[Axis]*4.0;
|
||||
bTouchesTileBoundary|=MinVoxels<=TileMin[Axis]+0.5
|
||||
||MaxVoxels>=TileMax[Axis]-0.5;
|
||||
}
|
||||
if(bTouchesTileBoundary)++Result.BoundarySmallComponentsInView;
|
||||
else ++Result.SmallComponentsInView;
|
||||
if(bPrintCandidates&&!bTouchesTileBoundary&&Result.SmallComponentsInView<=8)
|
||||
std::printf("[SmallMeshComponent] level=%d tile=(%d,%d,%d) tris=%llu size=(%.2f,%.2f,%.2f) vox pca_thickness=%.3f vox center=(%.2f,%.2f,%.2f)m\n",
|
||||
Tile.Key.Level,Tile.Key.Coord.X,Tile.Key.Coord.Y,Tile.Key.Coord.Z,
|
||||
static_cast<unsigned long long>(C.Triangles),Extent[0]/0.25,Extent[1]/0.25,
|
||||
Extent[2]/0.25,ThicknessVoxels,Center[0],Center[1],Center[2]);
|
||||
else if(bPrintCandidates&&bTouchesTileBoundary
|
||||
&&Result.BoundarySmallComponentsInView<=8)
|
||||
std::printf("[BoundaryMeshComponent] level=%d tile=(%d,%d,%d) tris=%llu size=(%.2f,%.2f,%.2f) vox pca_thickness=%.3f vox center=(%.2f,%.2f,%.2f)m\n",
|
||||
Tile.Key.Level,Tile.Key.Coord.X,Tile.Key.Coord.Y,Tile.Key.Coord.Z,
|
||||
static_cast<unsigned long long>(C.Triangles),Extent[0]/0.25,Extent[1]/0.25,
|
||||
Extent[2]/0.25,ThicknessVoxels,Center[0],Center[1],Center[2]);
|
||||
if(ThicknessVoxels>=1.0)continue;
|
||||
++Result.ThinSheets;Result.MinimumThicknessVoxels=std::min(Result.MinimumThicknessVoxels,ThicknessVoxels);
|
||||
Result.TrianglesInThinSheets+=C.Triangles;
|
||||
if(bPrintCandidates&&Result.ThinSheets==1)
|
||||
std::printf("[ThinMeshSheetCandidate] level=%d tile=(%d,%d,%d) tris=%llu size=(%.2f,%.2f,%.2f) vox pca_thickness=%.3f vox center=(%.2f,%.2f,%.2f)m\n",
|
||||
Tile.Key.Level,Tile.Key.Coord.X,Tile.Key.Coord.Y,Tile.Key.Coord.Z,
|
||||
static_cast<unsigned long long>(C.Triangles),Extent[0]/0.25,Extent[1]/0.25,Extent[2]/0.25,
|
||||
ThicknessVoxels,Center[0],Center[1],Center[2]);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
struct M4 { float M[4][4]{}; };
|
||||
M4 Identity(){M4 R{};for(int I=0;I<4;++I)R.M[I][I]=1.0f;return R;}
|
||||
M4 Transpose(M4 A){M4 B{};for(int I=0;I<4;++I)for(int J=0;J<4;++J)B.M[I][J]=A.M[J][I];return B;}
|
||||
@@ -1348,7 +1504,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
|
||||
std::atomic<size_t> Next{0};
|
||||
std::vector<std::thread> Threads;
|
||||
const int32 ThreadCount=std::clamp(Workers,1,8);
|
||||
const int32 ThreadCount=std::clamp(Workers,1,16);
|
||||
Threads.reserve(ThreadCount);
|
||||
for(int32 Worker=0;Worker<ThreadCount;++Worker)
|
||||
{
|
||||
@@ -1547,7 +1703,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
int RunStreamingBenchmark(FStandaloneOwnerWorld& World,const FVector& CameraPositionVoxels,
|
||||
int32 WorkerCount,int32 MaxLevel,bool bRoundTunnels,
|
||||
bool bEnhancedWalls,const FStandaloneMeshCache* Cache,
|
||||
bool bHashDensity,
|
||||
bool bHashDensity,bool bShardAudit,float CameraYaw,float CameraPitch,
|
||||
const std::string& BakeCachePath,
|
||||
const FStandaloneMeshCacheMetadata& CacheMetadata)
|
||||
{
|
||||
@@ -1588,6 +1744,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
bool bFullDetailTimeReported=false;
|
||||
double FullDetailSeconds=0.0;
|
||||
const int32 DesiredTotal=Update.DesiredTiles;
|
||||
FMeshSheetAudit MeshSheetStats;
|
||||
uint64 MeshAuditedTiles=0;
|
||||
while(Finished<DesiredTotal)
|
||||
{
|
||||
std::vector<FStandaloneCpuTile> Ready=Streamer.TakeReady();
|
||||
@@ -1610,6 +1768,23 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
if(Tile.bHasDensityHash)HashRows[Level].push_back({Tile.Key,Tile.DensityHash});
|
||||
}
|
||||
if(Level<World.FullResClipLevels)++FullDetailFinished;
|
||||
if(bShardAudit&&Level==0)
|
||||
{
|
||||
const FMeshSheetAudit TileAudit=AuditThinMeshSheets(Tile,CameraPositionVoxels,
|
||||
CameraYaw,CameraPitch,
|
||||
MeshSheetStats.ThinSheets==0
|
||||
&&MeshSheetStats.SmallComponentsInView
|
||||
+MeshSheetStats.BoundarySmallComponentsInView<8);
|
||||
MeshSheetStats.Components+=TileAudit.Components;
|
||||
MeshSheetStats.SmallComponents+=TileAudit.SmallComponents;
|
||||
MeshSheetStats.ThinSheets+=TileAudit.ThinSheets;
|
||||
MeshSheetStats.SmallComponentsInView+=TileAudit.SmallComponentsInView;
|
||||
MeshSheetStats.BoundarySmallComponentsInView+=TileAudit.BoundarySmallComponentsInView;
|
||||
MeshSheetStats.TrianglesInThinSheets+=TileAudit.TrianglesInThinSheets;
|
||||
MeshSheetStats.MinimumThicknessVoxels=std::min(MeshSheetStats.MinimumThicknessVoxels,
|
||||
TileAudit.MinimumThicknessVoxels);
|
||||
++MeshAuditedTiles;
|
||||
}
|
||||
if(!BakeCachePath.empty())CacheTiles.push_back(std::move(Tile));
|
||||
}
|
||||
if(!bFullDetailTimeReported&&FullDetailFinished>=FullDetailExpected)
|
||||
@@ -1646,6 +1821,16 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::accumulate(WorkerSecondsByLevel.begin(),WorkerSecondsByLevel.end(),0.0),DesiredTotal);
|
||||
std::printf("[StreamCacheUse] hits=%llu requested=%d\n",
|
||||
static_cast<unsigned long long>(Streamer.GetCacheHits()),DesiredTotal);
|
||||
if(bShardAudit)
|
||||
std::printf("[ThinMeshShardAudit] level=0 tiles=%llu view_components=%llu small_components=%llu isolated_in_view_small=%llu boundary_in_view_small=%llu thin_sheets=%llu sheet_triangles=%llu thickness_lt=1.000 vox pca_scale=3.5x_sigma status=%s\n",
|
||||
static_cast<unsigned long long>(MeshAuditedTiles),
|
||||
static_cast<unsigned long long>(MeshSheetStats.Components),
|
||||
static_cast<unsigned long long>(MeshSheetStats.SmallComponents),
|
||||
static_cast<unsigned long long>(MeshSheetStats.SmallComponentsInView),
|
||||
static_cast<unsigned long long>(MeshSheetStats.BoundarySmallComponentsInView),
|
||||
static_cast<unsigned long long>(MeshSheetStats.ThinSheets),
|
||||
static_cast<unsigned long long>(MeshSheetStats.TrianglesInThinSheets),
|
||||
MeshSheetStats.ThinSheets==0&&MeshSheetStats.SmallComponentsInView==0?"PASS":"FAIL");
|
||||
for(int32 Level=0;Level<=MaxLevels;++Level)
|
||||
{
|
||||
if(TilesByLevel[Level]==0)continue;
|
||||
@@ -1675,7 +1860,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::printf("[MeshCacheBake] saved %zu tiles, %.2f MiB to %s\n",CacheTiles.size(),
|
||||
static_cast<double>(Bytes)/1048576.0,BakeCachePath.c_str());
|
||||
}
|
||||
return (Finished==DesiredTotal&&FullDetailFinished==FullDetailExpected)?0:6;
|
||||
if(Finished!=DesiredTotal||FullDetailFinished!=FullDetailExpected)return 6;
|
||||
if(bShardAudit&&(MeshSheetStats.ThinSheets!=0||MeshSheetStats.SmallComponentsInView!=0))return 13;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1689,6 +1876,7 @@ int main(int Argc,char** Argv)
|
||||
int32 BenchmarkLevel=-1,BenchmarkCount=64,StreamBenchmarkMaxLevel=-1;
|
||||
int32 ShapeCompareLevel=-1,ShapeCompareTileLimit=0;
|
||||
bool bStreamBenchmark=false;
|
||||
bool bShardAudit=false;
|
||||
bool bStreamHash=false;
|
||||
bool bRoundWallCompare=false;
|
||||
int32 RoundCompareTileCount=16;
|
||||
@@ -1720,6 +1908,7 @@ int main(int Argc,char** Argv)
|
||||
{App.bRoundTunnels=true;App.bRoundTunnelEnhanced=false;}
|
||||
else if(Arg=="--stream-benchmark")bStreamBenchmark=true;
|
||||
else if(Arg=="--stream-hash")bStreamHash=true;
|
||||
else if(Arg=="--shard-audit")bShardAudit=true;
|
||||
else if(Arg=="--compare-round-walls")
|
||||
{bRoundWallCompare=true;App.bRoundTunnels=true;}
|
||||
else if(Arg.starts_with("--round-compare-tiles="))
|
||||
@@ -1735,7 +1924,7 @@ int main(int Argc,char** Argv)
|
||||
else if(Arg.starts_with("--workers="))
|
||||
{
|
||||
const int32 Parsed=std::atoi(std::string(Arg.substr(10)).c_str());
|
||||
App.WorkerCount=std::clamp(Parsed,1,8);
|
||||
App.WorkerCount=std::clamp(Parsed,1,16);
|
||||
}
|
||||
else if(Arg.starts_with("--clip-radius="))
|
||||
App.ClipRadius=std::clamp(std::atoi(std::string(Arg.substr(14)).c_str()),1,64);
|
||||
@@ -1804,7 +1993,7 @@ int main(int Argc,char** Argv)
|
||||
FVector(App.Position.X*4.0,App.Position.Y*4.0,App.Position.Z*4.0),
|
||||
App.WorkerCount,StreamBenchmarkMaxLevel,App.bRoundTunnels,
|
||||
App.bRoundTunnelEnhanced,(!App.bShapeWorld&&App.MeshCache.IsLoaded())?&App.MeshCache:nullptr,
|
||||
bStreamHash,BakeMeshCachePath,App.MakeMeshCacheMetadata());
|
||||
bStreamHash,bShardAudit,App.Yaw,App.Pitch,BakeMeshCachePath,App.MakeMeshCacheMetadata());
|
||||
}
|
||||
if(BenchmarkLevel>=0)
|
||||
return RunTileBenchmark(*App.World,App.Position,BenchmarkLevel,BenchmarkCount,
|
||||
|
||||
Reference in new issue
Block a user