#include "OwnerWorldLoader.h" #include "StreamingWorld.h" #include "StandaloneResearchTunnels.h" #include "StandaloneShapeWorld.h" #include "VoxelDensityProfile.h" #ifdef TEXT #undef TEXT #endif #ifndef NOMINMAX #define NOMINMAX #endif #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma comment(lib,"d3d11.lib") #pragma comment(lib,"dxgi.lib") #pragma comment(lib,"d3dcompiler.lib") #pragma comment(lib,"user32.lib") #pragma comment(lib,"gdi32.lib") namespace { struct V3 { float X=0.0f,Y=0.0f,Z=0.0f; }; V3 Add(V3 A,V3 B){return {A.X+B.X,A.Y+B.Y,A.Z+B.Z};} V3 Sub(V3 A,V3 B){return {A.X-B.X,A.Y-B.Y,A.Z-B.Z};} V3 Mul(V3 A,float S){return {A.X*S,A.Y*S,A.Z*S};} 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))));} #include "FarSurvivalAudit.h" 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::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 Parent(VertexCount); std::vector 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]=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::max(),std::numeric_limits::max(),std::numeric_limits::max()}; double Max[3]={-std::numeric_limits::max(),-std::numeric_limits::max(),-std::numeric_limits::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 Components(VertexCount); for(size_t I=0;I(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=VertexCount||B>=VertexCount||C>=VertexCount)continue; ++Components[Find(A)].Triangles; } for(size_t I=0;I(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;Rootstd::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(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(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(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;} M4 Perspective(float Fov,float Aspect,float NearZ,float FarZ) { M4 R{};const float Y=1.0f/std::tan(Fov*0.5f); R.M[0][0]=Y/Aspect;R.M[1][1]=Y;R.M[2][2]=FarZ/(FarZ-NearZ); R.M[2][3]=1.0f;R.M[3][2]=-NearZ*FarZ/(FarZ-NearZ);return R; } M4 View(V3 Position,V3 Forward,V3 Right,V3 Up) { M4 R=Identity(); R.M[0][0]=Right.X;R.M[1][0]=Right.Y;R.M[2][0]=Right.Z;R.M[3][0]=-Dot(Right,Position); R.M[0][1]=Up.X;R.M[1][1]=Up.Y;R.M[2][1]=Up.Z;R.M[3][1]=-Dot(Up,Position); R.M[0][2]=Forward.X;R.M[1][2]=Forward.Y;R.M[2][2]=Forward.Z;R.M[3][2]=-Dot(Forward,Position); return R; } struct FGpuVertex { float Position[3]; float Normal[3]; }; struct FConstants { M4 ViewProjection; float Camera[3]; float FogStart; float Forward[3]; float FogEnd; float LightDirection[3]; float Ambient; float Padding[4]; }; constexpr int32 MaxLevels=8; struct FStandaloneBatchKey { int32 Level=0,X=0,Y=0,Z=0; bool operator==(const FStandaloneBatchKey& O)const {return Level==O.Level&&X==O.X&&Y==O.Y&&Z==O.Z;} }; struct FStandaloneBatchKeyHash { size_t operator()(const FStandaloneBatchKey& K)const noexcept { uint64 H=static_cast(K.Level); H=(H*0x9e3779b185ebca87ull)^static_cast(K.X); H=(H*0x9e3779b185ebca87ull)^static_cast(K.Y); H=(H*0x9e3779b185ebca87ull)^static_cast(K.Z); return static_cast(H^(H>>32)); } }; struct FResidentTileMesh { std::vector Vertices; std::vector Indices; uint64 Samples=0; double WorkerSeconds=0.0; }; struct FStreamingGpuBatch { ID3D11Buffer* VertexBuffer=nullptr; ID3D11Buffer* IndexBuffer=nullptr; UINT IndexCount=0; uint64 VertexCount=0; }; struct FCrossingWork { uint64 SubmittedJobs=0; uint64 PendingJobs=0; uint64 FinishedJobs=0; uint64 CancelledQueuedJobs=0; uint64 StaleResults=0; uint64 Tiles=0; uint64 Samples=0; double WorkerSeconds=0.0; std::chrono::steady_clock::time_point StartTime{}; bool bReported=false; }; uint64 HashStandaloneFile(const std::string& Path) { std::ifstream File(Path,std::ios::binary); if(!File)return 0; uint64 Hash=14695981039346656037ull; char Buffer[65536]; while(File) { File.read(Buffer,sizeof(Buffer)); const std::streamsize Count=File.gcount(); for(std::streamsize I=0;I(Buffer[I]); Hash*=1099511628211ull; } } return Hash; } FStandaloneBatchKey BatchForTile(const FVoxelTileKey& Tile) { return {Tile.Level,VoxelClipmapDesiredTiles::FloorDiv(Tile.Coord.X,8), VoxelClipmapDesiredTiles::FloorDiv(Tile.Coord.Y,8),Tile.Coord.Z}; } bool PrepareStandaloneDensityWorld(FStandaloneOwnerWorld& World,int32 Seed, bool bRoundTunnels,bool bEnhancedWalls,std::string& Error) { World.Generator.reset();World.Manager.reset(); World.Settings->Seed=Seed;World.Seed=Seed; // Keep the owner's walkable rooms, tunnels, and strate passages intact. The research // round field composes as additional air after the owner field, so it can widen/connect // spaces but cannot remove the authored entrance-to-exit route. FStandaloneResearchTunnels::Set(bRoundTunnels,bEnhancedWalls); World.Manager=std::make_unique(); if(!World.Manager->Initialize(World.Settings.get(),Seed)) {Error="standalone copied strate manager failed while applying viewer settings";return false;} World.Generator=std::make_unique(); World.Generator->InitializeSettings(World.Settings.get()); World.Generator->SetStrateManager(World.Manager.get()); return true; } struct FApp { HWND Window=nullptr; ID3D11Device* Device=nullptr; ID3D11DeviceContext* Context=nullptr; IDXGISwapChain* SwapChain=nullptr; ID3D11RenderTargetView* RenderTarget=nullptr; ID3D11DepthStencilView* DepthView=nullptr; ID3D11VertexShader* VertexShader=nullptr; ID3D11PixelShader* PixelShader=nullptr; ID3D11InputLayout* InputLayout=nullptr; ID3D11Buffer* Constants=nullptr; ID3D11RasterizerState* SolidRasterizer=nullptr; ID3D11RasterizerState* WireRasterizer=nullptr; std::unique_ptr World; std::unique_ptr ShapeWorld; std::unique_ptr PendingFullShapeWorld; std::unique_ptr Streamer; std::thread FullShapeBuildThread; std::atomic bFullShapeBuildFinished{false}; bool bFullShapeBuildSucceeded=false; std::string FullShapeBuildError; std::chrono::steady_clock::time_point FullShapeBuildStart{}; FStandaloneMeshCache MeshCache; std::string DataPath; bool bHasCameraOverride=false; V3 CameraPositionOverride{}; float CameraYawOverride=0.0f,CameraPitchOverride=0.0f; std::unordered_map ResidentTiles; std::unordered_map,FStandaloneBatchKeyHash> BatchMembers; std::unordered_map StreamingBatches; std::unordered_set DirtyBatchSet; std::unordered_map CrossingWork; std::vector DesiredTiles; std::vector InitialNeighborhoodTiles; int32 DesiredByLevel[MaxLevels+1]{}; uint64 GeneratedByLevel[MaxLevels+1]{}; uint64 SamplesByLevel[MaxLevels+1]{}; double WorkerSecondsByLevel[MaxLevels+1]{}; uint64 ProfileCyclesByLevel[MaxLevels+1][VoxelDensityProfile::BucketCount]{}; std::vector TileSecondsByLevel[MaxLevels+1]; uint64 CrossingIndex=0; bool bGenerationRunning=false; bool bRebuildPending=false; bool bParityChecked=false; bool bRoundTunnels=false; bool bRoundTunnelEnhanced=true; bool bShapeWorld=false; bool bShapePipelineActive=false; bool bNeighborhoodReadyReported=false; bool bShapeAuditOnly=false; bool bShapeCompare=false; bool bFarShapeStagePending=false; FStandaloneShapeWorld::EDetailVariant ShapeDetailVariant=FStandaloneShapeWorld::EDetailVariant::Coherent; bool bWireframe=false; bool bCaptured=false; bool bAutoCaptureWritten=false; bool bFullDetailReported=false; bool bInitialClipmapReported=false; bool bProfileAttribution=false; bool bCompareEvaluators=false; bool bParityOnly=false; bool bDensityHash=false; bool bTestOneChunkCrossing=false; bool bCrossingMoveStarted=false; bool bCrossingMoveFinished=false; bool bCrossingClosePosted=false; std::chrono::steady_clock::time_point CrossingMoveStopTime{}; bool bCaptureAfterClipmap=false; bool bQuitAfterClipmap=false; bool bPrintedClipOverride=false; bool bForceGenerate=false; std::string CapturePath; std::string MeshCachePath; std::chrono::steady_clock::time_point ColdProcessStart{}; std::chrono::steady_clock::time_point GenerationStart{}; bool bShowLevel[MaxLevels+1]={true,true,true,true,true,true,true,true,true}; bool Keys[256]{}; int32 Seed=0; int32 WorkerCount=8; int32 ClipRadius=3; int32 MaxClipLevelOverride=-1; int32 Width=1280,Height=800; float Yaw=0.0f,Pitch=0.0f,MoveSpeed=8.0f; V3 Position{}; POINT LastMouse{}; std::chrono::steady_clock::time_point PreviousFrame=std::chrono::steady_clock::now(); double FrameMs=0.0; uint64 TotalVertices=0; uint64 TotalIndices=0; FIntVector BuildTargetChunk=FIntVector::ZeroValue; int32 MaxBatchUploadsPerFrame=4; double BatchUploadBudgetMilliseconds=2.0; ~FApp(){ StopGeneration(true);ReleaseMeshes(); if(SolidRasterizer)SolidRasterizer->Release();if(WireRasterizer)WireRasterizer->Release(); if(Constants)Constants->Release();if(InputLayout)InputLayout->Release(); if(VertexShader)VertexShader->Release();if(PixelShader)PixelShader->Release(); if(RenderTarget)RenderTarget->Release();if(DepthView)DepthView->Release(); if(SwapChain)SwapChain->Release();if(Context)Context->Release();if(Device)Device->Release(); } void ReleaseMeshes() { for(auto& Pair:StreamingBatches) { if(Pair.second.VertexBuffer)Pair.second.VertexBuffer->Release(); if(Pair.second.IndexBuffer)Pair.second.IndexBuffer->Release(); } StreamingBatches.clear();BatchMembers.clear();ResidentTiles.clear(); DirtyBatchSet.clear();DesiredTiles.clear();CrossingWork.clear(); for(int32 I=0;I<=MaxLevels;++I) {DesiredByLevel[I]=0;GeneratedByLevel[I]=0;SamplesByLevel[I]=0;WorkerSecondsByLevel[I]=0.0; std::fill(std::begin(ProfileCyclesByLevel[I]),std::end(ProfileCyclesByLevel[I]),0); TileSecondsByLevel[I].clear();} TotalVertices=0;TotalIndices=0; } void StopGeneration(bool bCancel=false) { (void)bCancel; if(Streamer)Streamer->Stop(); Streamer.reset(); if(FullShapeBuildThread.joinable())FullShapeBuildThread.join(); PendingFullShapeWorld.reset(); bFullShapeBuildFinished.store(false,std::memory_order_relaxed); bFullShapeBuildSucceeded=false; FullShapeBuildError.clear(); InitialNeighborhoodTiles.clear(); bShapePipelineActive=false; bNeighborhoodReadyReported=false; bFarShapeStagePending=false; bGenerationRunning=false; } V3 Forward() const {return Normalize({std::sin(Yaw)*std::cos(Pitch),std::cos(Yaw)*std::cos(Pitch),std::sin(Pitch)});} FIntVector CameraChunk() const {return StandaloneWorldMesher::CameraChunk(FVector(Position.X*4.0,Position.Y*4.0,Position.Z*4.0));} bool NeedsRecenter(const FIntVector& Center) const { return CameraChunk()!=Center; } bool MakeBuffer(ID3D11Buffer** Out,const void* Data,size_t Bytes,UINT Bind) { if(Bytes==0||Bytes>std::numeric_limits::max())return false; D3D11_BUFFER_DESC Desc{};Desc.ByteWidth=static_cast(Bytes); Desc.Usage=D3D11_USAGE_DEFAULT;Desc.BindFlags=Bind; D3D11_SUBRESOURCE_DATA Initial{};Initial.pSysMem=Data; return SUCCEEDED(Device->CreateBuffer(&Desc,&Initial,Out)); } static void AppendPngU32(std::vector& Out,uint32 Value) { Out.push_back(static_cast(Value >> 24)); Out.push_back(static_cast(Value >> 16)); Out.push_back(static_cast(Value >> 8)); Out.push_back(static_cast(Value)); } static uint32 PngCrc32(const uint8* Data,size_t Count) { uint32 Crc=0xffffffffu; for(size_t I=0;I>1)^((Crc&1)?0xedb88320u:0u); } return Crc^0xffffffffu; } static void AppendPngChunk(std::vector& Png,const char Type[4],const std::vector& Data) { AppendPngU32(Png,static_cast(Data.size())); const size_t CrcStart=Png.size(); Png.insert(Png.end(),Type,Type+4); Png.insert(Png.end(),Data.begin(),Data.end()); AppendPngU32(Png,PngCrc32(Png.data()+CrcStart,Png.size()-CrcStart)); } bool SaveBackBufferPng(const std::string& Path) { ID3D11Texture2D* BackBuffer=nullptr; if(FAILED(SwapChain->GetBuffer(0,IID_PPV_ARGS(&BackBuffer))))return false; D3D11_TEXTURE2D_DESC Desc{};BackBuffer->GetDesc(&Desc); Desc.Usage=D3D11_USAGE_STAGING;Desc.BindFlags=0;Desc.CPUAccessFlags=D3D11_CPU_ACCESS_READ;Desc.MiscFlags=0; ID3D11Texture2D* Staging=nullptr; const HRESULT CreateResult=Device->CreateTexture2D(&Desc,nullptr,&Staging); if(FAILED(CreateResult)){BackBuffer->Release();return false;} Context->CopyResource(Staging,BackBuffer);BackBuffer->Release(); D3D11_MAPPED_SUBRESOURCE Mapped{}; if(FAILED(Context->Map(Staging,0,D3D11_MAP_READ,0,&Mapped))){Staging->Release();return false;} const size_t RowBytes=static_cast(Desc.Width)*4; std::vector Raw((RowBytes+1)*Desc.Height); for(UINT Y=0;Y(Y)*(RowBytes+1); Row[0]=0; std::memcpy(Row+1,static_cast(Mapped.pData)+static_cast(Y)*Mapped.RowPitch,RowBytes); } Context->Unmap(Staging,0);Staging->Release(); std::vector Zlib{0x78,0x01}; uint32 AdlerA=1,AdlerB=0; for(const uint8 Byte:Raw){AdlerA=(AdlerA+Byte)%65521u;AdlerB=(AdlerB+AdlerA)%65521u;} for(size_t Offset=0;Offset(std::min(65535,Raw.size()-Offset)); const bool bFinal=Offset+Block==Raw.size(); Zlib.push_back(bFinal?1:0); Zlib.push_back(static_cast(Block));Zlib.push_back(static_cast(Block>>8)); const uint16 NotBlock=static_cast(~Block); Zlib.push_back(static_cast(NotBlock));Zlib.push_back(static_cast(NotBlock>>8)); Zlib.insert(Zlib.end(),Raw.begin()+Offset,Raw.begin()+Offset+Block); Offset+=Block; } AppendPngU32(Zlib,(AdlerB<<16)|AdlerA); std::vector Png{137,80,78,71,13,10,26,10}; std::vector Ihdr;AppendPngU32(Ihdr,Desc.Width);AppendPngU32(Ihdr,Desc.Height); Ihdr.insert(Ihdr.end(),{8,6,0,0,0});AppendPngChunk(Png,"IHDR",Ihdr); AppendPngChunk(Png,"IDAT",Zlib);AppendPngChunk(Png,"IEND",{}); std::ofstream File(Path,std::ios::binary); if(!File)return false; File.write(reinterpret_cast(Png.data()),static_cast(Png.size())); return File.good(); } bool Resize(UINT NewWidth,UINT NewHeight) { if(!Device||NewWidth==0||NewHeight==0)return false; Width=static_cast(NewWidth);Height=static_cast(NewHeight); if(RenderTarget){RenderTarget->Release();RenderTarget=nullptr;} if(DepthView){DepthView->Release();DepthView=nullptr;} if(SwapChain->ResizeBuffers(0,NewWidth,NewHeight,DXGI_FORMAT_UNKNOWN,0)!=S_OK)return false; ID3D11Texture2D* Back=nullptr; if(FAILED(SwapChain->GetBuffer(0,IID_PPV_ARGS(&Back))))return false; const HRESULT RtvResult=Device->CreateRenderTargetView(Back,nullptr,&RenderTarget);Back->Release(); if(FAILED(RtvResult))return false; D3D11_TEXTURE2D_DESC DepthDesc{};DepthDesc.Width=NewWidth;DepthDesc.Height=NewHeight; DepthDesc.MipLevels=1;DepthDesc.ArraySize=1;DepthDesc.Format=DXGI_FORMAT_D24_UNORM_S8_UINT; DepthDesc.SampleDesc.Count=1;DepthDesc.Usage=D3D11_USAGE_DEFAULT;DepthDesc.BindFlags=D3D11_BIND_DEPTH_STENCIL; ID3D11Texture2D* Depth=nullptr; if(FAILED(Device->CreateTexture2D(&DepthDesc,nullptr,&Depth)))return false; const HRESULT DsvResult=Device->CreateDepthStencilView(Depth,nullptr,&DepthView);Depth->Release(); return SUCCEEDED(DsvResult); } bool InitializeDevice() { DXGI_SWAP_CHAIN_DESC Desc{};Desc.BufferCount=1;Desc.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM; Desc.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;Desc.OutputWindow=Window;Desc.SampleDesc.Count=1; Desc.Windowed=TRUE;Desc.SwapEffect=DXGI_SWAP_EFFECT_DISCARD; D3D_FEATURE_LEVEL Feature{}; if(FAILED(D3D11CreateDeviceAndSwapChain(nullptr,D3D_DRIVER_TYPE_HARDWARE,nullptr,0,nullptr,0, D3D11_SDK_VERSION,&Desc,&SwapChain,&Device,&Feature,&Context)))return false; const char* ShaderSource=R"( cbuffer C:register(b0){float4x4 VP;float3 Camera;float FogStart;float3 Forward;float FogEnd;float3 LightDir;float Ambient;float4 Pad;} struct I{float3 p:POSITION;float3 n:NORMAL;}; struct O{float4 p:SV_POSITION;float3 world:TEXCOORD0;float3 n:NORMAL;}; O VS(I i){O o;o.p=mul(float4(i.p,1),VP);o.world=i.p;o.n=normalize(i.n);return o;} float4 PS(O i):SV_TARGET{ float3 N=normalize(i.n);float3 V=normalize(Camera-i.world);float3 L=normalize(-LightDir); float Diffuse=saturate(dot(N,L));float Head=saturate(dot(N,V)); float Dist=length(Camera-i.world);float Atten=1.0-saturate(Dist/95.0); float3 Rock=lerp(float3(0.30,0.22,0.16),float3(0.48,0.43,0.34),saturate(N.z*0.5+0.5)); // Keep the camera fill broad and low-amplitude. The old squared, high-intensity // lobe made the 25 cm density-lattice normal variation read as horizontal bands. Head=Head*Atten; float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75; float Fog=saturate((Dist-FogStart)/(FogEnd-FogStart)); Lit=lerp(Lit,float3(0.10,0.14,0.19),Fog);return float4(Lit,1); })"; ID3DBlob* VsCode=nullptr;ID3DBlob* PsCode=nullptr;ID3DBlob* Errors=nullptr; HRESULT Hr=D3DCompile(ShaderSource,std::strlen(ShaderSource),nullptr,nullptr,nullptr,"VS","vs_5_0",0,0,&VsCode,&Errors); if(FAILED(Hr)){if(Errors)std::fprintf(stderr,"Vertex shader compile: %s\n",static_cast(Errors->GetBufferPointer()));if(Errors)Errors->Release();return false;} Hr=D3DCompile(ShaderSource,std::strlen(ShaderSource),nullptr,nullptr,nullptr,"PS","ps_5_0",0,0,&PsCode,&Errors); if(FAILED(Hr)){if(Errors)std::fprintf(stderr,"Pixel shader compile: %s\n",static_cast(Errors->GetBufferPointer()));if(Errors)Errors->Release();VsCode->Release();return false;} Hr=Device->CreateVertexShader(VsCode->GetBufferPointer(),VsCode->GetBufferSize(),nullptr,&VertexShader); if(SUCCEEDED(Hr))Hr=Device->CreatePixelShader(PsCode->GetBufferPointer(),PsCode->GetBufferSize(),nullptr,&PixelShader); D3D11_INPUT_ELEMENT_DESC Elements[]={{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0}, {"NORMAL",0,DXGI_FORMAT_R32G32B32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0}}; if(SUCCEEDED(Hr))Hr=Device->CreateInputLayout(Elements,2,VsCode->GetBufferPointer(),VsCode->GetBufferSize(),&InputLayout); VsCode->Release();PsCode->Release();if(FAILED(Hr))return false; D3D11_BUFFER_DESC ConstantDesc{};ConstantDesc.ByteWidth=sizeof(FConstants);ConstantDesc.Usage=D3D11_USAGE_DEFAULT; ConstantDesc.BindFlags=D3D11_BIND_CONSTANT_BUFFER; if(FAILED(Device->CreateBuffer(&ConstantDesc,nullptr,&Constants)))return false; D3D11_RASTERIZER_DESC Raster{};Raster.FillMode=D3D11_FILL_SOLID;Raster.CullMode=D3D11_CULL_NONE; Raster.DepthClipEnable=TRUE;if(FAILED(Device->CreateRasterizerState(&Raster,&SolidRasterizer)))return false; Raster.FillMode=D3D11_FILL_WIREFRAME;if(FAILED(Device->CreateRasterizerState(&Raster,&WireRasterizer)))return false; return Resize(Width,Height); } size_t PrintParity() { if(bParityChecked||!World)return 0; bParityChecked=true;size_t MismatchCount=0; std::map Kinds; for(size_t I=0;IDensitySamples.size();++I) { const FStandaloneParitySample& S=World->DensitySamples[I]; const float Actual=World->Generator->GetDensityAt(static_cast(S.PositionVoxels.X), static_cast(S.PositionVoxels.Y),static_cast(S.PositionVoxels.Z)); ++Kinds[S.Kind]; if(std::bit_cast(Actual)!=std::bit_cast(S.PluginDensity)) { ++MismatchCount; std::fprintf(stderr,"PARITY MISMATCH %s strate=%d voxels=(%.6f,%.6f,%.6f) plugin=%.9g standalone=%.9g\n", S.Kind.c_str(),S.StrateIndex,S.PositionVoxels.X,S.PositionVoxels.Y,S.PositionVoxels.Z,S.PluginDensity,Actual); } } std::printf("Density parity: %zu/%zu bit-identical; mismatches=%zu\n", World->DensitySamples.size()-MismatchCount,World->DensitySamples.size(),MismatchCount); for(const auto& [Kind,Count]:Kinds)std::printf(" %-24s %zu\n",Kind.c_str(),Count); std::fflush(stdout); std::fflush(stdout); return MismatchCount; } bool ConfigureWorld(std::string& Error) { FStandaloneShapeWorldAccess::SetActive(nullptr); ShapeWorld.reset(); World=std::make_unique(); if(!StandaloneOwnerLoader::Load(DataPath,*World,Error))return false; if(!bParityChecked&&Seed==World->Seed&&!bRoundTunnels)PrintParity(); const int32 ExportClipRadius=World->ClipRadius; World->ClipRadius=ClipRadius; World->Settings->ClipRadius=ClipRadius; if(!bPrintedClipOverride) { bPrintedClipOverride=true; const double FullDetailWidth=static_cast(2*ClipRadius+2) *CHUNK_SIZE*VOXEL_SIZE/100.0; std::printf("Standalone clip override: exported radius=%d (112 m cube) -> radius=%d (%.0f m LOD0 cube), voxel size=25 cm.\n", ExportClipRadius,ClipRadius,FullDetailWidth); } if(!PrepareStandaloneDensityWorld(*World,Seed,bRoundTunnels, bRoundTunnelEnhanced,Error))return false; if(MaxClipLevelOverride>=0) { World->MaxClipLevel=MaxClipLevelOverride; World->Settings->MaxClipLevel=MaxClipLevelOverride; std::printf("Standalone max clip level override applied after world reload: max_level=%d.\n", MaxClipLevelOverride); } // Compile one world field before streaming. The streamer already prioritizes the // player's neighborhood, so a second near-only field would only duplicate work. if(bShapeWorld&&!BuildShapeWorldForCamera(Error,true,false))return false; LoadMeshCache(); return true; } bool BuildShapeWorldForCamera(std::string& Error,bool bIncludeFarWorld=false, bool bNeighborhoodOnly=false) { const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0); const FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(*World,CameraVoxels); FIntVector Min(INT32_MAX,INT32_MAX,INT32_MAX),Max(INT32_MIN,INT32_MIN,INT32_MIN); FIntVector ClipMin(INT32_MAX,INT32_MAX,INT32_MAX),ClipMax(INT32_MIN,INT32_MIN,INT32_MIN); uint64 FineTiles=0; InitialNeighborhoodTiles.clear(); const FIntVector CenterChunk=Desired.CenterChunk; for(const FVoxelTileKey& Tile:Desired.Tiles) { const FIntVector Origin=Tile.OriginVoxels(); const int32 Extent=Tile.ExtentVoxels(); ClipMin.X=std::min(ClipMin.X,Origin.X);ClipMin.Y=std::min(ClipMin.Y,Origin.Y);ClipMin.Z=std::min(ClipMin.Z,Origin.Z); ClipMax.X=std::max(ClipMax.X,Origin.X+Extent);ClipMax.Y=std::max(ClipMax.Y,Origin.Y+Extent);ClipMax.Z=std::max(ClipMax.Z,Origin.Z+Extent); if(bNeighborhoodOnly) { if(Tile.Level!=0||std::abs(Tile.Coord.X-CenterChunk.X)>1 ||std::abs(Tile.Coord.Y-CenterChunk.Y)>1 ||std::abs(Tile.Coord.Z-CenterChunk.Z)>1)continue; InitialNeighborhoodTiles.push_back(Tile); } else if(Tile.Level>=World->FullResClipLevels)continue; Min.X=std::min(Min.X,Origin.X);Min.Y=std::min(Min.Y,Origin.Y);Min.Z=std::min(Min.Z,Origin.Z); Max.X=std::max(Max.X,Origin.X+Extent);Max.Y=std::max(Max.Y,Origin.Y+Extent);Max.Z=std::max(Max.Z,Origin.Z+Extent); ++FineTiles; } if(!FineTiles){Error="no full-detail tiles are available for shape compilation";return false;} if(bNeighborhoodOnly) { if(InitialNeighborhoodTiles.size()!=27) {Error="the 3x3x3 player neighborhood does not contain 27 LOD0 tiles";return false;} constexpr int32 NearBuildPad=64; Min.X-=NearBuildPad;Min.Y-=NearBuildPad;Min.Z-=NearBuildPad; Max.X+=NearBuildPad;Max.Y+=NearBuildPad;Max.Z+=NearBuildPad; } ShapeWorld=std::make_unique(); const int32 ShapeWorkers=bNeighborhoodOnly?(WorkerCount>=8?2:1):WorkerCount; if(!ShapeWorld->Build(*World,Min,Max,ClipMin,ClipMax,ShapeWorkers, ShapeDetailVariant,bIncludeFarWorld,Error)) {ShapeWorld.reset();return false;} FStandaloneShapeWorldAccess::SetActive(ShapeWorld.get()); bShapePipelineActive=bNeighborhoodOnly; bNeighborhoodReadyReported=false; bFarShapeStagePending=bShapeWorld&&!bNeighborhoodOnly&&!ShapeWorld->HasFarWorld(); std::printf("[ShapeRegion] fine_tiles=%llu bounds=(%d,%d,%d)..(%d,%d,%d) clip=(%d,%d,%d)..(%d,%d,%d)\n", static_cast(FineTiles),Min.X,Min.Y,Min.Z,Max.X,Max.Y,Max.Z, ClipMin.X,ClipMin.Y,ClipMin.Z,ClipMax.X,ClipMax.Y,ClipMax.Z); return true; } bool StartFullShapeBuild(const std::vector& FullDesired,std::string& Error) { FIntVector FineMin(INT32_MAX,INT32_MAX,INT32_MAX),FineMax(INT32_MIN,INT32_MIN,INT32_MIN); FIntVector ClipMin(INT32_MAX,INT32_MAX,INT32_MAX),ClipMax(INT32_MIN,INT32_MIN,INT32_MIN); for(const FVoxelTileKey& Tile:FullDesired) { const FIntVector Origin=Tile.OriginVoxels();const int32 Extent=Tile.ExtentVoxels(); ClipMin.X=std::min(ClipMin.X,Origin.X);ClipMin.Y=std::min(ClipMin.Y,Origin.Y);ClipMin.Z=std::min(ClipMin.Z,Origin.Z); ClipMax.X=std::max(ClipMax.X,Origin.X+Extent);ClipMax.Y=std::max(ClipMax.Y,Origin.Y+Extent);ClipMax.Z=std::max(ClipMax.Z,Origin.Z+Extent); if(Tile.Level>=World->FullResClipLevels)continue; FineMin.X=std::min(FineMin.X,Origin.X);FineMin.Y=std::min(FineMin.Y,Origin.Y);FineMin.Z=std::min(FineMin.Z,Origin.Z); FineMax.X=std::max(FineMax.X,Origin.X+Extent);FineMax.Y=std::max(FineMax.Y,Origin.Y+Extent);FineMax.Z=std::max(FineMax.Z,Origin.Z+Extent); } if(FineMin.X==INT32_MAX||ClipMin.X==INT32_MAX) {Error="the full shape build has no desired detail or clipmap tiles";return false;} PendingFullShapeWorld=std::make_unique(); FullShapeBuildError.clear();bFullShapeBuildSucceeded=false; bFullShapeBuildFinished.store(false,std::memory_order_relaxed); const int32 FullWorkers=WorkerCount>=8?6:WorkerCount; FullShapeBuildStart=std::chrono::steady_clock::now(); FullShapeBuildThread=std::thread([this,FineMin,FineMax,ClipMin,ClipMax,FullWorkers]() { bFullShapeBuildSucceeded=PendingFullShapeWorld->Build(*World,FineMin,FineMax, ClipMin,ClipMax,FullWorkers,ShapeDetailVariant,true,FullShapeBuildError); bFullShapeBuildFinished.store(true,std::memory_order_release); }); std::printf("[ShapePipelineBuildStart] neighborhood_tiles=%zu near_mesh_workers=%d full_shape_workers=%d\n", InitialNeighborhoodTiles.size(),WorkerCount>=8?2:1,FullWorkers); std::fflush(stdout); return true; } FStandaloneMeshCacheMetadata MakeMeshCacheMetadata()const { FStandaloneMeshCacheMetadata Metadata; Metadata.SourceDataHash=HashStandaloneFile(DataPath); Metadata.Seed=Seed;Metadata.ClipRadius=ClipRadius; Metadata.FullResClipLevels=World->FullResClipLevels; Metadata.CoarseTileCells=World->CoarseTileCells; Metadata.StrateContentCutMinLevel=World->StrateContentCutMinLevel; Metadata.SkirtCells=World->SkirtCells; Metadata.LODOctaveDrop=World->LODOctaveDrop; Metadata.bGenerateSkirts=World->bGenerateSkirts?1:0; Metadata.AlgorithmVersion=FStandaloneMeshCache::CurrentVersion; Metadata.TunnelMode=bRoundTunnels?(bRoundTunnelEnhanced?2u:1u):0u; return Metadata; } void LoadMeshCache() { MeshCache.Clear(); if(bForceGenerate||bShapeWorld||!World)return; const char* ModeSuffix=!bRoundTunnels?"_base": (bRoundTunnelEnhanced?"_round_detailed":"_round_legacy"); const std::filesystem::path CachePath=std::filesystem::path(DataPath).parent_path() /(std::string("VoxelForge_seed")+std::to_string(Seed)+"_r" +std::to_string(ClipRadius)+ModeSuffix+".vfmesh"); MeshCachePath=CachePath.string(); const auto Begin=std::chrono::steady_clock::now(); std::string CacheError; if(MeshCache.Load(MeshCachePath,MakeMeshCacheMetadata(),CacheError)) { const double Seconds=std::chrono::duration( std::chrono::steady_clock::now()-Begin).count(); std::printf("[MeshCache] loaded %zu tiles, %.2f MiB in %.3f s from %s\n", MeshCache.Num(),static_cast(MeshCache.GetBytesLoaded())/1048576.0, Seconds,MeshCachePath.c_str()); std::fflush(stdout); } else if(CacheError!="cache file is missing or unreadable") std::printf("[MeshCache] ignored %s: %s\n",MeshCachePath.c_str(),CacheError.c_str()); } void StartGeneration() { StopGeneration(); ReleaseMeshes(); GenerationStart=std::chrono::steady_clock::now(); std::string Error; if(!ConfigureWorld(Error)) {std::fprintf(stderr,"World load failed: %s\n",Error.c_str());MessageBoxA(Window,Error.c_str(),"VoxelForge viewer",MB_ICONERROR);return;} const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0); BuildTargetChunk=StandaloneWorldMesher::CameraChunk(CameraVoxels); bFullDetailReported=false;bInitialClipmapReported=false;bAutoCaptureWritten=false; if(bProfileAttribution) { VoxelDensityProfile::SetMode(VoxelDensityProfile::EMode::Attribution,128); VoxelDensityProfile::CalibrateTimer(); VoxelDensityProfile::Reset(); std::printf("Density stage profiling enabled (Attribution, sample interval 128).\n"); } const FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(*World,CameraVoxels); DesiredTiles=Desired.Tiles; for(int32 I=0;I<=MaxLevels;++I)DesiredByLevel[I]=0; for(const FVoxelTileKey& Tile:DesiredTiles)++DesiredByLevel[Tile.Level]; const int32 StreamWorkers=bShapePipelineActive?(WorkerCount>=8?2:1):WorkerCount; Streamer=std::make_unique(*World,StreamWorkers,false, (!bShapeWorld&&MeshCache.IsLoaded())?&MeshCache:nullptr, bFarShapeStagePending?World->FullResClipLevels-1:-1); if(bShapePipelineActive)Streamer->SetDesiredTileFilter(InitialNeighborhoodTiles); const FStandaloneStreamingUpdate Update=Streamer->Start(CameraVoxels,{},CrossingIndex); if(bShapePipelineActive&&Update.DesiredTiles!=static_cast(InitialNeighborhoodTiles.size())) { StopGeneration(); Error="the initial neighborhood streamer did not accept all 27 tiles"; std::fprintf(stderr,"World load failed: %s\n",Error.c_str()); return; } FCrossingWork& InitialWork=CrossingWork[CrossingIndex]; InitialWork.SubmittedJobs=Update.NewJobs;InitialWork.PendingJobs=Update.NewJobs; InitialWork.StartTime=GenerationStart; bGenerationRunning=true;bRebuildPending=false; const char* TunnelMode=!bRoundTunnels?"":(bRoundTunnelEnhanced ?" with detailed round/winding tunnels":" with legacy round/winding tunnels"); std::printf("Streaming seed %d%s%s: %d desired tiles, %d full-detail tiles, %d workers; upload budget %d batches / %.1f ms per frame.\n", Seed,TunnelMode,bShapeWorld?" [shape intervals]":"",Update.DesiredTiles, Update.FullResTiles,Streamer->GetWorkerCount(),MaxBatchUploadsPerFrame,BatchUploadBudgetMilliseconds); if(bShapePipelineActive) { std::string ShapeError; if(!StartFullShapeBuild(DesiredTiles,ShapeError)) { StopGeneration(); std::fprintf(stderr,"World load failed: %s\n",ShapeError.c_str()); return; } } std::fflush(stdout); } void RequestSeed(int32 Delta) { const uint32 Next=static_cast(Seed)+static_cast(Delta); Seed=static_cast(Next);bRebuildPending=true; std::printf("Seed changed to %d; replacing the current streaming world.\n",Seed); } void StartFarShapeGeneration() { if(!bFarShapeStagePending||!ShapeWorld||ShapeWorld->HasFarWorld()||!Streamer)return; ShapeWorld->BuildFarWorld(); bFarShapeStagePending=false; Streamer->Stop(); std::vector ResidentKeys;ResidentKeys.reserve(ResidentTiles.size()); for(const auto& Pair:ResidentTiles)ResidentKeys.push_back(Pair.first); Streamer.reset(); Streamer=std::make_unique(*World,WorkerCount,false,nullptr,-1); const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0); ++CrossingIndex; const FStandaloneStreamingUpdate Update=Streamer->Start(CameraVoxels,ResidentKeys,CrossingIndex); FCrossingWork& Work=CrossingWork[CrossingIndex]; Work.SubmittedJobs=Update.NewJobs;Work.PendingJobs=Update.NewJobs; Work.StartTime=std::chrono::steady_clock::now(); bGenerationRunning=true; std::printf("[FarStreamStart] desired=%d retained=%d new_jobs=%d workers=%d\n", Update.DesiredTiles,Update.RetainedResidentTiles,Update.NewJobs,WorkerCount); std::fflush(stdout); } void TryCompleteShapePipeline() { if(!bShapePipelineActive||InitialNeighborhoodTiles.empty())return; bool bNeighborhoodUploaded=true; for(const FVoxelTileKey& Key:InitialNeighborhoodTiles) if(!ResidentTiles.contains(Key)){bNeighborhoodUploaded=false;break;} bNeighborhoodUploaded &= DirtyBatchSet.empty(); if(bNeighborhoodUploaded&&!bNeighborhoodReadyReported) { bNeighborhoodReadyReported=true; const double Elapsed=std::chrono::duration( std::chrono::steady_clock::now()-ColdProcessStart).count(); std::printf("[NeighborhoodReady] %.3fs after cold process start; tiles=%zu/%zu vertices=%llu indices=%llu\n", Elapsed,InitialNeighborhoodTiles.size(),InitialNeighborhoodTiles.size(), static_cast(TotalVertices), static_cast(TotalIndices)); std::fflush(stdout); } if(!bNeighborhoodReadyReported ||!bFullShapeBuildFinished.load(std::memory_order_acquire))return; if(FullShapeBuildThread.joinable())FullShapeBuildThread.join(); if(!bFullShapeBuildSucceeded||!PendingFullShapeWorld) { bShapePipelineActive=false;bGenerationRunning=false; std::fprintf(stderr,"[ShapePipeline] full shape build failed: %s\n", FullShapeBuildError.c_str()); PostMessageA(Window,WM_CLOSE,0,0); return; } const double FullShapeSeconds=std::chrono::duration( std::chrono::steady_clock::now()-FullShapeBuildStart).count(); std::vector ResidentKeys;ResidentKeys.reserve(ResidentTiles.size()); for(const auto& Pair:ResidentTiles)ResidentKeys.push_back(Pair.first); if(Streamer)Streamer->Stop(); Streamer.reset(); ShapeWorld=std::move(PendingFullShapeWorld); FStandaloneShapeWorldAccess::SetActive(ShapeWorld.get()); bFarShapeStagePending=false;bShapePipelineActive=false; const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0); Streamer=std::make_unique(*World,WorkerCount,false,nullptr,-1); ++CrossingIndex; const FStandaloneStreamingUpdate Update=Streamer->Start(CameraVoxels,ResidentKeys,CrossingIndex); if(Update.DesiredTiles!=static_cast(DesiredTiles.size())) { Streamer->Stop();Streamer.reset();bGenerationRunning=false; std::fprintf(stderr,"[ShapePipeline] full streamer accepted %d of %zu desired tiles.\n", Update.DesiredTiles,DesiredTiles.size()); PostMessageA(Window,WM_CLOSE,0,0); return; } FCrossingWork& Work=CrossingWork[CrossingIndex]; Work.SubmittedJobs=Update.NewJobs;Work.PendingJobs=Update.NewJobs; Work.StartTime=std::chrono::steady_clock::now(); bGenerationRunning=true; std::printf("[ShapePipelineFullReady] full_shape=%.3fs retained=%d new_jobs=%d workers=%d; filling outward\n", FullShapeSeconds,Update.RetainedResidentTiles,Update.NewJobs,WorkerCount); std::fflush(stdout); } void MarkBatchDirty(const FStandaloneBatchKey& Key) {DirtyBatchSet.insert(Key);} static bool TileKeyLess(const FVoxelTileKey& A,const FVoxelTileKey& B) { if(A.Level!=B.Level)return A.Levelsecond; Items.erase(std::remove(Items.begin(),Items.end(),Key),Items.end()); if(Items.empty())BatchMembers.erase(Members); } MarkBatchDirty(Batch); ResidentTiles.erase(Resident); } void RecenterStreaming() { if(!World||!Streamer)return; const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0); FStandaloneDesiredTileSet NewDesired=StandaloneWorldMesher::BuildDesiredSet(*World,CameraVoxels); if(NewDesired.CenterChunk==BuildTargetChunk)return; if(bShapeWorld) { bRebuildPending=true; std::printf("[ShapeRegion] camera crossed its compiled full-detail bounds; rebuilding the interval region.\n"); return; } const FIntVector Previous=BuildTargetChunk; std::unordered_set NewSet; NewSet.reserve(NewDesired.Tiles.size()*2+1); for(const FVoxelTileKey& Key:NewDesired.Tiles)NewSet.insert(Key); std::vector Leavers; for(const auto& Pair:ResidentTiles)if(!NewSet.contains(Pair.first))Leavers.push_back(Pair.first); for(const FVoxelTileKey& Key:Leavers)RemoveResidentTile(Key); DesiredTiles=std::move(NewDesired.Tiles); for(int32 I=0;I<=MaxLevels;++I)DesiredByLevel[I]=0; for(const FVoxelTileKey& Tile:DesiredTiles)++DesiredByLevel[Tile.Level]; std::vector ResidentKeys;ResidentKeys.reserve(ResidentTiles.size()); for(const auto& Pair:ResidentTiles)ResidentKeys.push_back(Pair.first); ++CrossingIndex; const FStandaloneStreamingUpdate Update=Streamer->UpdateDesired(CameraVoxels,ResidentKeys,CrossingIndex); for(const auto& Pair:Update.CancelledByCrossing) { FCrossingWork& Work=CrossingWork[Pair.first]; Work.PendingJobs=Work.PendingJobs>=Pair.second?Work.PendingJobs-Pair.second:0; Work.CancelledQueuedJobs+=Pair.second; } FCrossingWork& Work=CrossingWork[CrossingIndex]; Work.SubmittedJobs=Update.NewJobs;Work.PendingJobs=Update.NewJobs; Work.StartTime=std::chrono::steady_clock::now(); BuildTargetChunk=Update.CenterChunk;bGenerationRunning=true; std::printf("[ChunkCrossing] id=%llu center=(%d,%d,%d) delta=(%d,%d,%d) desired=%d retained=%d new_jobs=%d dropped_resident=%zu cancelled_queued=%d full_detail=%d\n", static_cast(CrossingIndex),Update.CenterChunk.X,Update.CenterChunk.Y,Update.CenterChunk.Z, Update.CenterChunk.X-Previous.X,Update.CenterChunk.Y-Previous.Y,Update.CenterChunk.Z-Previous.Z, Update.DesiredTiles,Update.RetainedResidentTiles,Update.NewJobs,Leavers.size(), Update.CancelledQueuedJobs,Update.FullResTiles); std::fflush(stdout); ReportCrossingWork(); } bool RebuildBatch(const FStandaloneBatchKey& Key) { auto MembersIt=BatchMembers.find(Key); auto Existing=StreamingBatches.find(Key); if(MembersIt==BatchMembers.end()||MembersIt->second.empty()) { if(Existing!=StreamingBatches.end()) { TotalVertices-=Existing->second.VertexCount;TotalIndices-=Existing->second.IndexCount; if(Existing->second.VertexBuffer)Existing->second.VertexBuffer->Release(); if(Existing->second.IndexBuffer)Existing->second.IndexBuffer->Release(); StreamingBatches.erase(Existing); } return true; } std::vector Members=MembersIt->second; std::sort(Members.begin(),Members.end(),TileKeyLess); size_t VertexCount=0,IndexCount=0; for(const FVoxelTileKey& Tile:Members) { const auto It=ResidentTiles.find(Tile);if(It==ResidentTiles.end())continue; VertexCount+=It->second.Vertices.size();IndexCount+=It->second.Indices.size(); } if(VertexCount==0||IndexCount==0) { if(Existing!=StreamingBatches.end()) { TotalVertices-=Existing->second.VertexCount;TotalIndices-=Existing->second.IndexCount; if(Existing->second.VertexBuffer)Existing->second.VertexBuffer->Release(); if(Existing->second.IndexBuffer)Existing->second.IndexBuffer->Release(); StreamingBatches.erase(Existing); } return true; } if(VertexCount>std::numeric_limits::max()||IndexCount>std::numeric_limits::max()) {std::fprintf(stderr,"LOD %d batch exceeded D3D11 limits.\n",Key.Level);return false;} std::vector Vertices;Vertices.reserve(VertexCount); std::vector Indices;Indices.reserve(IndexCount); for(const FVoxelTileKey& Tile:Members) { const auto It=ResidentTiles.find(Tile);if(It==ResidentTiles.end())continue; const uint32 Base=static_cast(Vertices.size()); for(const FStandaloneCpuVertex& V:It->second.Vertices) Vertices.push_back({{V.Position[0],V.Position[1],V.Position[2]},{V.Normal[0],V.Normal[1],V.Normal[2]}}); for(const uint32 Index:It->second.Indices)Indices.push_back(Base+Index); } FStreamingGpuBatch NewBatch; const bool bVertex=MakeBuffer(&NewBatch.VertexBuffer,Vertices.data(),Vertices.size()*sizeof(FGpuVertex),D3D11_BIND_VERTEX_BUFFER); const bool bIndex=bVertex&&MakeBuffer(&NewBatch.IndexBuffer,Indices.data(),Indices.size()*sizeof(uint32),D3D11_BIND_INDEX_BUFFER); if(!bIndex) { if(NewBatch.VertexBuffer)NewBatch.VertexBuffer->Release(); std::fprintf(stderr,"D3D11 upload failed for LOD %d batch (%d,%d,%d).\n",Key.Level,Key.X,Key.Y,Key.Z); return false; } NewBatch.IndexCount=static_cast(Indices.size());NewBatch.VertexCount=Vertices.size(); if(Existing!=StreamingBatches.end()) { TotalVertices-=Existing->second.VertexCount;TotalIndices-=Existing->second.IndexCount; if(Existing->second.VertexBuffer)Existing->second.VertexBuffer->Release(); if(Existing->second.IndexBuffer)Existing->second.IndexBuffer->Release(); Existing->second=NewBatch; } else StreamingBatches.emplace(Key,NewBatch); TotalVertices+=NewBatch.VertexCount;TotalIndices+=NewBatch.IndexCount; return true; } void UploadDirtyBatches() { if(DirtyBatchSet.empty())return; std::vector Dirty(DirtyBatchSet.begin(),DirtyBatchSet.end()); const FIntVector Center=Streamer?Streamer->CenterChunk():BuildTargetChunk; auto Distance=[&](const FStandaloneBatchKey& K) { const double Scale=static_cast(1<=MaxBatchUploadsPerFrame)break; if(Uploaded>0&&BatchUploadBudgetMilliseconds>0.0 &&std::chrono::duration(std::chrono::steady_clock::now()-Start).count()>=BatchUploadBudgetMilliseconds)break; if(!DirtyBatchSet.contains(Key))continue; if(RebuildBatch(Key)){DirtyBatchSet.erase(Key);++Uploaded;} } } bool IsFullDetailUploaded()const { if(!Streamer||World->FullResClipLevels<=0)return false; int32 ResidentFullRes=0; for(const auto& Pair:ResidentTiles)if(Pair.first.LevelFullResClipLevels)++ResidentFullRes; if(ResidentFullRes!=Streamer->FullResDesiredCount())return false; for(const FStandaloneBatchKey& Key:DirtyBatchSet)if(Key.LevelFullResClipLevels)return false; return true; } void ReportTileTimings() { for(int32 L=0;L<=World->MaxClipLevel;++L) { if(GeneratedByLevel[L](DesiredByLevel[L])||DesiredByLevel[L]==0)continue; std::vector& Times=TileSecondsByLevel[L];if(Times.empty())continue; std::sort(Times.begin(),Times.end()); const double Average=WorkerSecondsByLevel[L]/static_cast(GeneratedByLevel[L]); const double P50=Times[(Times.size()-1)*50/100]; const double P95=Times[(Times.size()-1)*95/100]; std::printf("[TileTiming] LOD=%d generated=%llu target=%d samples=%.2fM worker_sum=%.3fs avg=%.3fms p50=%.3fms p95=%.3fms\n", L,static_cast(GeneratedByLevel[L]),DesiredByLevel[L], static_cast(SamplesByLevel[L])/1.0e6,WorkerSecondsByLevel[L],Average*1000.0,P50*1000.0,P95*1000.0); if(bProfileAttribution) { std::printf("[TileProfile] LOD=%d",L); for(size_t I=0;I(ProfileCyclesByLevel[L][I]) *FPlatformTime::GetSecondsPerCycle64()*1000.0; if(Ms>0.001)std::printf(" %s=%.3fms", VoxelDensityProfile::BucketName(static_cast(I)),Ms); } std::printf("\n"); } Times.clear(); } } void ReportCrossingWork() { for(auto& Pair:CrossingWork) { FCrossingWork& Work=Pair.second; if(Work.bReported||Work.PendingJobs!=0)continue; Work.bReported=true; const double AverageMs=Work.FinishedJobs>0 ?Work.WorkerSeconds*1000.0/static_cast(Work.FinishedJobs):0.0; const double WallSeconds=Work.StartTime.time_since_epoch().count()!=0 ?std::chrono::duration(std::chrono::steady_clock::now()-Work.StartTime).count():0.0; std::printf("[ChunkCrossingWork] id=%llu submitted=%llu finished=%llu generated=%llu cancelled_queued=%llu stale=%llu samples=%.2fM worker_sum=%.3fs wall=%.3fs avg=%.3fms\n", static_cast(Pair.first), static_cast(Work.SubmittedJobs), static_cast(Work.FinishedJobs), static_cast(Work.Tiles), static_cast(Work.CancelledQueuedJobs), static_cast(Work.StaleResults), static_cast(Work.Samples)/1.0e6,Work.WorkerSeconds,WallSeconds,AverageMs); } } void PumpGeneration() { if(bRebuildPending){StartGeneration();return;} if(!Streamer)return; std::vector Ready=Streamer->TakeReady(); for(FStandaloneCpuTile& Tile:Ready) { const int32 Level=Tile.Key.Level; FCrossingWork& Crossing=CrossingWork[Tile.RequestedCrossing]; if(Crossing.PendingJobs>0)--Crossing.PendingJobs; ++Crossing.FinishedJobs;Crossing.Samples+=Tile.DensitySamples;Crossing.WorkerSeconds+=Tile.WorkerSeconds; if(!Streamer->IsDesired(Tile.Key)||ResidentTiles.contains(Tile.Key)) {++Crossing.StaleResults;continue;} ++Crossing.Tiles; ++GeneratedByLevel[Level];SamplesByLevel[Level]+=Tile.DensitySamples;WorkerSecondsByLevel[Level]+=Tile.WorkerSeconds; if(bProfileAttribution)for(size_t I=0;Isecond.bReported) { bCrossingClosePosted=true; std::printf("[CrossingTestComplete] crossing work and its bounded uploads drained; closing this test viewer.\n"); std::fflush(stdout); PostMessageA(Window,WM_CLOSE,0,0); } } if(!bShapePipelineActive&&!bFullDetailReported&&IsFullDetailUploaded()) { bFullDetailReported=true; const double Elapsed=std::chrono::duration(std::chrono::steady_clock::now()-ColdProcessStart).count(); std::printf("[FullDetailReady] %.3f s after cold process start; resident=%zu/%d tiles through LOD%d.\n", Elapsed,ResidentTiles.size(),Streamer->FullResDesiredCount(),World->FullResClipLevels-1); ReportTileTimings();std::fflush(stdout); StartFarShapeGeneration(); } if(!bInitialClipmapReported&&ResidentTiles.size()==DesiredTiles.size()&&DirtyBatchSet.empty()) { bInitialClipmapReported=true; const double Elapsed=std::chrono::duration(std::chrono::steady_clock::now()-ColdProcessStart).count(); std::printf("[InitialClipmapReady] %.3f s after cold process start; %zu tiles, %llu vertices, %llu indices, %zu GPU batches.\n", Elapsed,ResidentTiles.size(),static_cast(TotalVertices), static_cast(TotalIndices),StreamingBatches.size()); ReportTileTimings();std::fflush(stdout); if(bQuitAfterClipmap&&CapturePath.empty())PostMessageA(Window,WM_CLOSE,0,0); } bGenerationRunning=ResidentTiles.size()!=DesiredTiles.size()||!DirtyBatchSet.empty(); } void CaptureRenderedFrame() { const bool bReadyToCapture=bCaptureAfterClipmap?bInitialClipmapReported:IsFullDetailUploaded(); if(CapturePath.empty()||bAutoCaptureWritten||!bReadyToCapture)return; bAutoCaptureWritten=SaveBackBufferPng(CapturePath); if(bAutoCaptureWritten) { const double Elapsed=std::chrono::duration(std::chrono::steady_clock::now()-GenerationStart).count(); std::printf("Back-buffer capture saved: %s (%.3f s after generation start; full detail uploaded).\n",CapturePath.c_str(),Elapsed); std::fflush(stdout);PostMessageA(Window,WM_CLOSE,0,0); } else std::fprintf(stderr,"Back-buffer capture failed: %s\n",CapturePath.c_str()); } void Capture(bool bOn) { bCaptured=bOn; if(bOn){SetCapture(Window);ShowCursor(FALSE);GetCursorPos(&LastMouse);POINT C{Width/2,Height/2};ClientToScreen(Window,&C);SetCursorPos(C.x,C.y);} else{ReleaseCapture();ShowCursor(TRUE);} } void UpdateTitle() { uint64 VisibleIndices=0;for(const auto& Pair:StreamingBatches)if(bShowLevel[Pair.first.Level])VisibleIndices+=Pair.second.IndexCount; std::ostringstream S;S<<"VoxelForge standalone | seed "<=CrossingMoveStopTime) { Keys[VK_SHIFT]=false;Keys['W']=false; bCrossingMoveStarted=false;bCrossingMoveFinished=true; std::printf("[CrossingTestMove] movement stopped; waiting for crossing work and uploads.\n"); std::fflush(stdout); } const float Dt=std::clamp(std::chrono::duration(Now-PreviousFrame).count(),0.0f,0.1f); PreviousFrame=Now;FrameMs=Dt*1000.0; const V3 F=Forward(),R=Normalize(Cross(F,{0,0,1})),U=Normalize(Cross(R,F)); const float Step=MoveSpeed*Dt*(Keys[VK_SHIFT]?4.0f:1.0f); if(Keys['W'])Position=Add(Position,Mul(F,Step));if(Keys['S'])Position=Sub(Position,Mul(F,Step)); if(Keys['A'])Position=Sub(Position,Mul(R,Step));if(Keys['D'])Position=Add(Position,Mul(R,Step)); if(Keys[VK_SPACE]||Keys['E'])Position.Z+=Step;if(Keys[VK_CONTROL]||Keys['Q'])Position.Z-=Step; if(Streamer&&NeedsRecenter(BuildTargetChunk))RecenterStreaming(); const float Clear[4]={0.055f,0.075f,0.105f,1.0f}; Context->OMSetRenderTargets(1,&RenderTarget,DepthView);Context->ClearRenderTargetView(RenderTarget,Clear); Context->ClearDepthStencilView(DepthView,D3D11_CLEAR_DEPTH,1.0f,0); D3D11_VIEWPORT Viewport{0,0,static_cast(Width),static_cast(Height),0,1};Context->RSSetViewports(1,&Viewport); Context->RSSetState(bWireframe?WireRasterizer:SolidRasterizer);Context->IASetInputLayout(InputLayout); Context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); FConstants C{};const M4 V=View(Position,F,R,U);const M4 P=Perspective(1.04719755f,static_cast(Width)/std::max(1,Height),0.05f,5000.0f); M4 Combined{};for(int I=0;I<4;++I)for(int J=0;J<4;++J)for(int K=0;K<4;++K)Combined.M[I][J]+=V.M[I][K]*P.M[K][J]; C.ViewProjection=Transpose(Combined);C.Camera[0]=Position.X;C.Camera[1]=Position.Y;C.Camera[2]=Position.Z; C.Forward[0]=F.X;C.Forward[1]=F.Y;C.Forward[2]=F.Z;C.LightDirection[0]=-0.5f;C.LightDirection[1]=-0.72f;C.LightDirection[2]=-0.48f; C.FogStart=260.0f;C.FogEnd=2600.0f;C.Ambient=0.36f; Context->UpdateSubresource(Constants,0,nullptr,&C,0,0);Context->VSSetConstantBuffers(0,1,&Constants);Context->PSSetConstantBuffers(0,1,&Constants); Context->VSSetShader(VertexShader,nullptr,0);Context->PSSetShader(PixelShader,nullptr,0); for(const auto& Pair:StreamingBatches) { const FStreamingGpuBatch& L=Pair.second; if(!bShowLevel[Pair.first.Level]||!L.VertexBuffer||!L.IndexBuffer||!L.IndexCount)continue; const UINT Stride=sizeof(FGpuVertex),Offset=0;Context->IASetVertexBuffers(0,1,&L.VertexBuffer,&Stride,&Offset); Context->IASetIndexBuffer(L.IndexBuffer,DXGI_FORMAT_R32_UINT,0);Context->DrawIndexed(L.IndexCount,0,0); } CaptureRenderedFrame(); SwapChain->Present(1,0);UpdateTitle(); } }; FApp* GApp=nullptr; LRESULT CALLBACK WindowProc(HWND H,UINT Message,WPARAM W,LPARAM L) { if(!GApp)return DefWindowProc(H,Message,W,L); switch(Message) { case WM_SIZE:if(W!=SIZE_MINIMIZED)GApp->Resize(LOWORD(L),HIWORD(L));return 0; case WM_KEYDOWN: { const bool bWasDown=(L&(1ll<<30))!=0;if(W<256)GApp->Keys[W]=true; if(W==VK_ESCAPE){if(GApp->bCaptured)GApp->Capture(false);else DestroyWindow(H);} if(!bWasDown&&W=='F')GApp->bWireframe=!GApp->bWireframe; if(!bWasDown&&W=='T'){GApp->bRoundTunnels=!GApp->bRoundTunnels;GApp->bRebuildPending=true;} if(!bWasDown&&W=='V'){GApp->bShapeWorld=!GApp->bShapeWorld;GApp->bRebuildPending=true;} if(!bWasDown&&W=='G'&&GApp->bRoundTunnels) { GApp->bRoundTunnelEnhanced=!GApp->bRoundTunnelEnhanced; GApp->bRebuildPending=true; std::printf("Round-tunnel comparison: %s wall field.\n", GApp->bRoundTunnelEnhanced?"detailed/smoothed":"legacy smooth tube"); } if(!bWasDown&&(W=='N'||W=='P'))GApp->RequestSeed(W=='N'?1:-1); if(!bWasDown&&W>='1'&&W<='8'){const int32 I=static_cast(W-'1');GApp->bShowLevel[I]=!GApp->bShowLevel[I];} if(!bWasDown&&W=='9')GApp->bShowLevel[8]=!GApp->bShowLevel[8]; if(!bWasDown&&W=='0')for(bool& B:GApp->bShowLevel)B=!B; return 0; } case WM_KEYUP:if(W<256)GApp->Keys[W]=false;return 0; case WM_RBUTTONDOWN:GApp->Capture(true);return 0; case WM_RBUTTONUP:GApp->Capture(false);return 0; case WM_MOUSEMOVE: if(GApp->bCaptured){POINT C{GApp->Width/2,GApp->Height/2};ClientToScreen(H,&C);POINT P{GET_X_LPARAM(L),GET_Y_LPARAM(L)},Screen=P;ClientToScreen(H,&Screen);const int32 DX=Screen.x-C.x,DY=Screen.y-C.y;if(DX||DY){GApp->Yaw+=DX*0.0035f;GApp->Pitch=std::clamp(GApp->Pitch-DY*0.0035f,-1.55f,1.55f);SetCursorPos(C.x,C.y);}}return 0; case WM_MOUSEWHEEL:GApp->MoveSpeed=std::clamp(GApp->MoveSpeed*(GET_WHEEL_DELTA_WPARAM(W)>0?1.25f:0.8f),0.25f,1000.0f);return 0; case WM_DESTROY:if(GApp->Streamer)GApp->Streamer->Stop();PostQuitMessage(0);return 0; } return DefWindowProc(H,Message,W,L); } std::filesystem::path DefaultDataPath() { wchar_t Module[MAX_PATH]{};GetModuleFileNameW(nullptr,Module,MAX_PATH); const std::filesystem::path Exe(Module); return Exe.parent_path().parent_path()/L"data"/L"owner_world.json"; } int RunTileBenchmark(FStandaloneOwnerWorld& World,V3 CameraPosition,int32 Level, int32 Count,bool bProfile,bool bCompareEvaluators,bool bDensityHash) { const FVector CameraVoxels(CameraPosition.X*4.0,CameraPosition.Y*4.0,CameraPosition.Z*4.0); const FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraVoxels); std::vector Tiles;Tiles.reserve(static_cast(std::max(Count,1))); for(const FVoxelTileKey& Tile:Desired.Tiles) if(Tile.Level==Level&&static_cast(Tiles.size())(World.SkirtCells);Mesher.LODOctaveDrop=World.LODOctaveDrop; const int32 BandMin=World.StrateContentCutMinLevel<9&&Desired.bHasStrate ?Desired.StrateBottomChunkZ*CHUNK_SIZE:MIN_int32; const int32 BandMax=World.StrateContentCutMinLevel<9&&Desired.bHasStrate ?(Desired.StrateTopChunkZ+1)*CHUNK_SIZE:MAX_int32; if(bCompareEvaluators) { const bool bOriginalFused=VoxelGenLOD::IsUseFusedEvaluatorEnabled(); uint64 Mismatches=0,ComparedSamples=0,MeshCountMismatches=0; float MaxAbsoluteDifference=0.0f; size_t FirstMismatch=SIZE_MAX; FVoxelTileKey FirstMismatchTile{}; VoxelGenLOD::SetUseFusedEvaluatorEnabled(true); for(const FVoxelTileKey& Tile:Tiles) { const int32 Cells=Level FusedDensities,OpStackDensities; int64 FusedSampleCalls=0,OpStackSampleCalls=0; FVoxelMeshData FusedMesh=Mesher.GenerateMesh(Tile.OriginVoxels(),Step,Cells,nullptr, BandMin,BandMax,&FusedSampleCalls,nullptr,nullptr,&FusedDensities); VoxelGenLOD::SetUseFusedEvaluatorEnabled(false); FVoxelMeshData OpStackMesh=Mesher.GenerateMesh(Tile.OriginVoxels(),Step,Cells,nullptr, BandMin,BandMax,&OpStackSampleCalls,nullptr,nullptr,&OpStackDensities); VoxelGenLOD::SetUseFusedEvaluatorEnabled(true); if(FusedMesh.Vertices.Num()!=OpStackMesh.Vertices.Num() || FusedMesh.Triangles.Num()!=OpStackMesh.Triangles.Num())++MeshCountMismatches; const size_t SharedSamples=std::min(FusedDensities.size(),OpStackDensities.size()); ComparedSamples+=SharedSamples; for(size_t I=0;I(FusedDensities[I])!=std::bit_cast(OpStackDensities[I])) { if(FirstMismatch==SIZE_MAX){FirstMismatch=I;FirstMismatchTile=Tile;} ++Mismatches; MaxAbsoluteDifference=std::max(MaxAbsoluteDifference, std::abs(FusedDensities[I]-OpStackDensities[I])); } } Mismatches+=static_cast(std::max(FusedDensities.size(),OpStackDensities.size())-SharedSamples); } std::printf("[EvaluatorCompare] LOD=%d tiles=%zu compared_samples=%llu density_mismatches=%llu first_tile=(%d,%d,%d) first_index=%llu max_abs=%.9g mesh_count_mismatch_tiles=%llu original_fused=%d\n", Level,Tiles.size(),static_cast(ComparedSamples), static_cast(Mismatches), FirstMismatchTile.Coord.X,FirstMismatchTile.Coord.Y,FirstMismatchTile.Coord.Z, static_cast(FirstMismatch),MaxAbsoluteDifference, static_cast(MeshCountMismatches),bOriginalFused?1:0); VoxelGenLOD::SetUseFusedEvaluatorEnabled(bOriginalFused); std::fflush(stdout); return Mismatches==0?0:5; } std::vector TileSeconds;TileSeconds.reserve(Tiles.size()); uint64 Samples=0,Vertices=0,Indices=0; uint64 DensityHash=14695981039346656037ull; const auto TotalStart=std::chrono::steady_clock::now(); for(const FVoxelTileKey& Tile:Tiles) { const auto TileStart=std::chrono::steady_clock::now(); const int32 Cells=Level DebugDensitySamples; FVoxelMeshData Mesh=Mesher.GenerateMesh(Tile.OriginVoxels(),Step,Cells,nullptr, BandMin,BandMax,&TileSamples,nullptr,nullptr, bDensityHash?&DebugDensitySamples:nullptr); Samples+=static_cast(FMath::Max(0,TileSamples)); Vertices+=static_cast(Mesh.Vertices.Num()); Indices+=static_cast(Mesh.Triangles.Num()); if(bDensityHash) { for(const float Density:DebugDensitySamples) { DensityHash^=std::bit_cast(Density); DensityHash*=1099511628211ull; } } TileSeconds.push_back(std::chrono::duration( std::chrono::steady_clock::now()-TileStart).count()); } const double TotalSeconds=std::chrono::duration( std::chrono::steady_clock::now()-TotalStart).count(); const VoxelDensityProfile::FSnapshot After=VoxelDensityProfile::SnapshotCurrentThread(); std::sort(TileSeconds.begin(),TileSeconds.end()); const double Average=TotalSeconds/static_cast(Tiles.size()); std::printf("[TileBenchmark] LOD=%d generated=%zu samples=%.2fM worker_sum=%.3fs avg=%.3fms p50=%.3fms p95=%.3fms vertices=%llu indices=%llu profiling=%s\n", Level,Tiles.size(),static_cast(Samples)/1.0e6,TotalSeconds,Average*1000.0, TileSeconds[(TileSeconds.size()-1)*50/100]*1000.0, TileSeconds[(TileSeconds.size()-1)*95/100]*1000.0, static_cast(Vertices),static_cast(Indices), bProfile?"Attribution":"off"); if(bDensityHash) { std::printf("[TileDensityHash] LOD=%d tiles=%zu hash=0x%016llx\n",Level,Tiles.size(), static_cast(DensityHash)); } if(bProfile) { std::printf("[TileProfile] LOD=%d",Level); for(int32 I=0;I=Before.Cycles[I] ?After.Cycles[I]-Before.Cycles[I]:0; const double Ms=static_cast(CycleDelta) *FPlatformTime::GetSecondsPerCycle64()*1000.0; if(Ms>0.001)std::printf(" %s=%.3fms",VoxelDensityProfile::BucketName( static_cast(I)),Ms); } std::printf("\n"); const auto CounterDelta=[&](VoxelDensityProfile::ECounter Counter) { const size_t I=static_cast(Counter); return After.Counters[I]>=Before.Counters[I] ?After.Counters[I]-Before.Counters[I]:0; }; std::printf("[TileCounters] sdf_builds=%llu graph_tile_builds=%llu graph_core_builds=%llu op_block_builds=%llu\n", static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfCacheBuild)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::RoomGraphBuildGeneratorTile)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::RoomGraphBuildGeneratorTunnelCore)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::OpBlockBuilds))); std::printf("[SdfRebuildCauses] strate=%llu seed=%llu fingerprint=%llu layout=%llu manager=%llu terrain_ops=%llu fused_policy=%llu tile_policy=%llu tile_identity=%llu bounds_x=%llu bounds_y=%llu tile_window_accepted=%llu tile_window_rejected=%llu\n", static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfRebuildStrate)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfRebuildSeed)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfRebuildFingerprint)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfRebuildLayout)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfRebuildManagerLifetime)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfRebuildTerrainOps)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfRebuildFusedPolicy)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfRebuildTilePolicy)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfRebuildTileIdentity)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfRebuildBoundsX)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfRebuildBoundsY)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfTileWindowAccepted)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::SdfTileWindowRejected))); std::printf("[TilePolicyKeyChanges] origin=%llu step=%llu cells=%llu fingerprint=%llu layout=%llu manager=%llu terrain_ops=%llu fused_path=%llu context=%llu\n", static_cast(CounterDelta(VoxelDensityProfile::ECounter::TilePolicyKeyOrigin)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::TilePolicyKeyStep)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::TilePolicyKeyCells)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::TilePolicyKeyFingerprint)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::TilePolicyKeyLayout)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::TilePolicyKeyManager)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::TilePolicyKeyTerrainOps)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::TilePolicyKeyFusedPath)), static_cast(CounterDelta(VoxelDensityProfile::ECounter::TilePolicyKeyContext))); } std::fflush(stdout); return 0; } struct FComparePoint { double X=0.0,Y=0.0,Z=0.0; }; struct FCompareBucketHash { size_t operator()(const FIntVector& K)const noexcept { uint64 H=static_cast(K.X)*0x9E3779B185EBCA87ull; H^=static_cast(static_cast(K.Y))*0xC2B2AE3D27D4EB4Full; H^=static_cast(static_cast(K.Z))*0x165667B19E3779F9ull; return static_cast(H^(H>>32)); } }; struct FDirectedSurfaceDistance { double Sum=0.0,Maximum=0.0; uint64 Count=0,Unmatched=0; }; FDirectedSurfaceDistance DirectedSurfaceDistance( const std::vector& From,const std::vector& To, double BucketWidth=1.0,int32 SearchCells=3) { FDirectedSurfaceDistance Result; if(From.empty()||To.empty()){Result.Unmatched=From.size();return Result;} std::unordered_map,FCompareBucketHash> Buckets; Buckets.reserve(To.size()); const auto BucketOf=[=](const FComparePoint& P) { return FIntVector(static_cast(std::floor(P.X/BucketWidth)), static_cast(std::floor(P.Y/BucketWidth)), static_cast(std::floor(P.Z/BucketWidth))); }; for(const FComparePoint& P:To)Buckets[BucketOf(P)].push_back(&P); for(const FComparePoint& P:From) { const FIntVector Center=BucketOf(P); double BestSquared=std::numeric_limits::infinity(); for(int32 Z=-SearchCells;Z<=SearchCells;++Z) for(int32 Y=-SearchCells;Y<=SearchCells;++Y) for(int32 X=-SearchCells;X<=SearchCells;++X) { const auto It=Buckets.find(Center+FIntVector(X,Y,Z)); if(It==Buckets.end())continue; for(const FComparePoint* Candidate:It->second) { const double DX=P.X-Candidate->X,DY=P.Y-Candidate->Y,DZ=P.Z-Candidate->Z; BestSquared=std::min(BestSquared,DX*DX+DY*DY+DZ*DZ); } } if(std::isfinite(BestSquared)) { const double Distance=std::sqrt(BestSquared); Result.Sum+=Distance;Result.Maximum=std::max(Result.Maximum,Distance);++Result.Count; } else ++Result.Unmatched; } return Result; } int RunOwnerShapeComparison(FStandaloneOwnerWorld& World,const FStandaloneShapeWorld& Shape, V3 CameraPosition,float CameraYaw,float CameraPitch, int32 Workers,int32 LevelFilter,int32 TileLimit) { if(FStandaloneShapeWorldAccess::GetActive()!=&Shape) {std::fprintf(stderr,"Shape comparison requires the compiled interval world to be active.\n");return 11;} if(LevelFilter>=World.FullResClipLevels&&!Shape.HasFarWorld()) const_cast(Shape).BuildFarWorld(); const FVector CameraVoxels(CameraPosition.X*4.0,CameraPosition.Y*4.0,CameraPosition.Z*4.0); Shape.AuditViewSurface(CameraPosition.X,CameraPosition.Y,CameraPosition.Z, CameraYaw,CameraPitch); { const int32 Offsets[][3]={{0,0,0},{4,0,0},{-4,0,0},{0,4,0},{0,-4,0},{0,0,4},{0,0,-4}}; for(const auto& Offset:Offsets) { const float X=static_cast(CameraVoxels.X+Offset[0]); const float Y=static_cast(CameraVoxels.Y+Offset[1]); const float Z=static_cast(CameraVoxels.Z+Offset[2]); float OwnerDensity=0.0f; {FStandaloneShapeWorldAccess::FScopedBypass Bypass; OwnerDensity=World.Generator->GetDensityAt(X,Y,Z);} const float ShapeDensity=Shape.DensityAt(X,Y,Z); std::printf("[ShapeProbe] voxels=(%.1f,%.1f,%.1f) owner=%.5f shape=%.5f owner_air=%d shape_air=%d protected_air=%d solid_overlay=%d\n", X,Y,Z,OwnerDensity,ShapeDensity,OwnerDensity>=0.0f?1:0,ShapeDensity>=0.0f?1:0, Shape.DebugCriticalAirAt(X,Y,Z)?1:0,Shape.DebugSolidOverlayAt(X,Y,Z)?1:0); } } const FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraVoxels); std::vector Tiles; for(const FVoxelTileKey& Tile:Desired.Tiles) if(LevelFilter<0?Tile.Level0&&Tiles.size()>static_cast(TileLimit)) { std::sort(Tiles.begin(),Tiles.end(),[&](const FVoxelTileKey& A,const FVoxelTileKey& B) { const FIntVector AO=A.OriginVoxels(),BO=B.OriginVoxels(); const double ADX=AO.X+A.ExtentVoxels()*0.5-CameraVoxels.X; const double ADY=AO.Y+A.ExtentVoxels()*0.5-CameraVoxels.Y; const double ADZ=AO.Z+A.ExtentVoxels()*0.5-CameraVoxels.Z; const double BDX=BO.X+B.ExtentVoxels()*0.5-CameraVoxels.X; const double BDY=BO.Y+B.ExtentVoxels()*0.5-CameraVoxels.Y; const double BDZ=BO.Z+B.ExtentVoxels()*0.5-CameraVoxels.Z; return ADX*ADX+ADY*ADY+ADZ*ADZ(TileLimit)); } if(Tiles.empty()){std::fprintf(stderr,"No tiles are available for the requested shape comparison level.\n");return 12;} struct FTileComparison { uint64 Samples=0,Changed=0,MeshMismatch=0; uint64 OldHash=14695981039346656037ull,NewHash=14695981039346656037ull; std::vector OldSurface,NewSurface; }; std::vector Results(Tiles.size()); int32 MinChunkZ=MIN_int32,MaxChunkZ=MAX_int32,TopChunkZ=0,BottomChunkZ=0; if(World.Settings->bClampViewToStrate&&World.Manager->GetStrateChunkZBounds( StandaloneWorldMesher::CameraChunk(CameraVoxels).Z,TopChunkZ,BottomChunkZ)) {MinChunkZ=BottomChunkZ*CHUNK_SIZE;MaxChunkZ=(TopChunkZ+1)*CHUNK_SIZE;} std::atomic Next{0}; std::vector Threads; const int32 ThreadCount=std::clamp(Workers,1,16); Threads.reserve(ThreadCount); for(int32 Worker=0;Worker(World.SkirtCells); Mesher.LODOctaveDrop=World.LODOctaveDrop; for(;;) { const size_t Index=Next.fetch_add(1,std::memory_order_relaxed); if(Index>=Tiles.size())break; const FVoxelTileKey& Tile=Tiles[Index]; const int32 Cells=Tile.Level NewDensities,OldDensities; FVoxelMeshData NewMesh=Mesher.GenerateMesh(Tile.OriginVoxels(),Step,Cells,nullptr, MinChunkZ,MaxChunkZ,nullptr,nullptr,nullptr,&NewDensities); FVoxelMeshData OldMesh; { FStandaloneShapeWorldAccess::FScopedBypass Bypass; OldMesh=Mesher.GenerateMesh(Tile.OriginVoxels(),Step,Cells,nullptr, MinChunkZ,MaxChunkZ,nullptr,nullptr,nullptr,&OldDensities); } FTileComparison& R=Results[Index]; const size_t Shared=std::min(NewDensities.size(),OldDensities.size()); R.Samples=Shared; R.MeshMismatch=std::max(NewDensities.size(),OldDensities.size())-Shared; for(size_t I=0;I(NewDensities[I]);R.NewHash*=1099511628211ull; R.OldHash^=std::bit_cast(OldDensities[I]);R.OldHash*=1099511628211ull; } constexpr size_t VertexStride=4; R.NewSurface.reserve(static_cast(NewMesh.Vertices.Num())/VertexStride+1); for(int32 I=0;I(VertexStride)) {const FVector& P=NewMesh.Vertices[I];R.NewSurface.push_back({P.X*0.01,P.Y*0.01,P.Z*0.01});} R.OldSurface.reserve(static_cast(OldMesh.Vertices.Num())/VertexStride+1); for(int32 I=0;I(VertexStride)) {const FVector& P=OldMesh.Vertices[I];R.OldSurface.push_back({P.X*0.01,P.Y*0.01,P.Z*0.01});} } }); } for(std::thread& Thread:Threads)Thread.join(); uint64 Samples=0,Changed=0,MeshMismatch=0; uint64 OldHash=14695981039346656037ull,NewHash=14695981039346656037ull; size_t OldVertexCount=0,NewVertexCount=0; std::vector OldSurface,NewSurface; for(const FTileComparison& R:Results) { Samples+=R.Samples;Changed+=R.Changed;MeshMismatch+=R.MeshMismatch; OldHash^=R.OldHash;OldHash*=1099511628211ull; NewHash^=R.NewHash;NewHash*=1099511628211ull; OldVertexCount+=R.OldSurface.size();NewVertexCount+=R.NewSurface.size(); } OldSurface.reserve(OldVertexCount);NewSurface.reserve(NewVertexCount); for(FTileComparison& R:Results) { OldSurface.insert(OldSurface.end(),R.OldSurface.begin(),R.OldSurface.end()); NewSurface.insert(NewSurface.end(),R.NewSurface.begin(),R.NewSurface.end()); } const FDirectedSurfaceDistance OldToNew=DirectedSurfaceDistance(OldSurface,NewSurface,1.0,3); const FDirectedSurfaceDistance NewToOld=DirectedSurfaceDistance(NewSurface,OldSurface,1.0,3); const uint64 DistanceSamples=OldToNew.Count+NewToOld.Count; const double MeanDistance=DistanceSamples ?(OldToNew.Sum+NewToOld.Sum)/static_cast(DistanceSamples):0.0; std::printf("[OwnerShapeCompare] tiles=%zu fullres_levels=%d level_filter=%d lattice_samples=%llu changed_signs=%llu changed_share=%.6f%% old_hash=0x%016llx shape_hash=0x%016llx mesh_sample_mismatch=%llu old_surface_samples=%zu shape_surface_samples=%zu symmetric_vertex_mean_m=%.4f symmetric_vertex_max_m=%.4f unmatched_surface_samples=%llu vertex_stride=4 workers=%d\n", Tiles.size(),World.FullResClipLevels,LevelFilter,static_cast(Samples), static_cast(Changed),Samples?100.0*Changed/Samples:0.0, static_cast(OldHash),static_cast(NewHash), static_cast(MeshMismatch),OldSurface.size(),NewSurface.size(), MeanDistance,std::max(OldToNew.Maximum,NewToOld.Maximum), static_cast(OldToNew.Unmatched+NewToOld.Unmatched),ThreadCount); std::fflush(stdout); return Samples>0&&MeshMismatch==0?0:13; } int RunRoundWallComparison(FStandaloneOwnerWorld& World,V3 CameraPosition,int32 TileCount) { const FVector CameraVoxels(CameraPosition.X*4.0,CameraPosition.Y*4.0,CameraPosition.Z*4.0); const FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraVoxels); std::vector Tiles;Tiles.reserve(static_cast(TileCount)); for(const FVoxelTileKey& Tile:Desired.Tiles) if(Tile.Level==0&&static_cast(Tiles.size()) Densities; std::vector Surface; }; std::vector Legacy(Tiles.size()); std::vector Detailed(Tiles.size()); UVoxelMarchingCubesMesher Mesher; Mesher.SetGenerator(World.Generator.get());Mesher.bGenerateSkirts=World.bGenerateSkirts; Mesher.SkirtCells=static_cast(World.SkirtCells);Mesher.LODOctaveDrop=World.LODOctaveDrop; int32 MinChunkZ=MIN_int32,MaxChunkZ=MAX_int32,TopChunkZ=0,BottomChunkZ=0; if(World.Settings->bClampViewToStrate&&World.Manager->GetStrateChunkZBounds( StandaloneWorldMesher::CameraChunk(CameraVoxels).Z,TopChunkZ,BottomChunkZ)) { MinChunkZ=BottomChunkZ*CHUNK_SIZE; MaxChunkZ=(TopChunkZ+1)*CHUNK_SIZE; } uint64 TotalSamples=0,ChangedSigns=0; uint64 LegacyHash=14695981039346656037ull,DetailedHash=14695981039346656037ull; const auto Evaluate=[&](bool bEnhanced,std::vector& Out) { FStandaloneResearchTunnels::Set(true,bEnhanced); for(size_t I=0;I(Mesh.Vertices.Num())); for(const FVector& P:Mesh.Vertices) Out[I].Surface.push_back({P.X*0.01,P.Y*0.01,P.Z*0.01}); } }; Evaluate(false,Legacy); Evaluate(true,Detailed); FDirectedSurfaceDistance LegacyToDetailed,DetailedToLegacy; std::vector AllLegacySurface,AllDetailedSurface; for(size_t TileIndex=0;TileIndex(A.Densities[I]);LegacyHash*=1099511628211ull; DetailedHash^=std::bit_cast(B.Densities[I]);DetailedHash*=1099511628211ull; } AllLegacySurface.insert(AllLegacySurface.end(),A.Surface.begin(),A.Surface.end()); AllDetailedSurface.insert(AllDetailedSurface.end(),B.Surface.begin(),B.Surface.end()); } LegacyToDetailed=DirectedSurfaceDistance(AllLegacySurface,AllDetailedSurface); DetailedToLegacy=DirectedSurfaceDistance(AllDetailedSurface,AllLegacySurface); const uint64 DistanceSamples=LegacyToDetailed.Count+DetailedToLegacy.Count; const double MeanDistance=DistanceSamples>0 ?(LegacyToDetailed.Sum+DetailedToLegacy.Sum)/static_cast(DistanceSamples):0.0; const double Hausdorff=std::max(LegacyToDetailed.Maximum,DetailedToLegacy.Maximum); std::printf("[RoundTunnelCompare] tiles=%zu lattice_samples=%llu changed_signs=%llu changed_share=%.6f%% legacy_density_hash=0x%016llx detailed_density_hash=0x%016llx old_vertices=%zu new_vertices=%zu symmetric_vertex_mean_m=%.4f symmetric_vertex_max_m=%.4f unmatched_vertices=%llu\n", Tiles.size(),static_cast(TotalSamples), static_cast(ChangedSigns),TotalSamples?100.0*ChangedSigns/TotalSamples:0.0, static_cast(LegacyHash),static_cast(DetailedHash), AllLegacySurface.size(),AllDetailedSurface.size(),MeanDistance,Hausdorff, static_cast(LegacyToDetailed.Unmatched+DetailedToLegacy.Unmatched)); FStandaloneResearchTunnels::Set(true,true); return TotalSamples>0&&LegacyToDetailed.Unmatched+DetailedToLegacy.Unmatched==0?0:10; } void HashWord(uint64& Hash,uint64 Word,int32 Bytes=8) { for(int32 I=0;I(Word>>(I*8)); Hash*=1099511628211ull; } } struct FHashedTile { FVoxelTileKey Key; uint64 Hash=14695981039346656037ull; }; bool TileKeyLessForHash(const FVoxelTileKey& A,const FVoxelTileKey& B) { if(A.Level!=B.Level)return A.Level& Rows) { std::sort(Rows.begin(),Rows.end(),[](const FHashedTile& A,const FHashedTile& B) {return TileKeyLessForHash(A.Key,B.Key);}); uint64 Hash=14695981039346656037ull; for(const FHashedTile& Row:Rows) { HashWord(Hash,static_cast(Row.Key.Level),4); HashWord(Hash,static_cast(Row.Key.Coord.X),4); HashWord(Hash,static_cast(Row.Key.Coord.Y),4); HashWord(Hash,static_cast(Row.Key.Coord.Z),4); HashWord(Hash,Row.Hash); } return Hash; } int RunPipelinedStreamingBenchmark(FStandaloneOwnerWorld& World,const FVector& CameraPositionVoxels, int32 WorkerCount,int32 MaxLevel,FStandaloneShapeWorld::EDetailVariant DetailVariant, bool bHashDensity,bool bSkipUvAndColors,bool bSkipMaterialGroups,bool bSkipUV,bool bSkipColors, std::chrono::steady_clock::time_point ColdStart) { using Clock=std::chrono::steady_clock; FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraPositionVoxels); if(MaxLevel>=0) Desired.Tiles.erase(std::remove_if(Desired.Tiles.begin(),Desired.Tiles.end(), [&](const FVoxelTileKey& Tile){return Tile.Level>MaxLevel;}),Desired.Tiles.end()); FIntVector FineMin(INT32_MAX,INT32_MAX,INT32_MAX),FineMax(INT32_MIN,INT32_MIN,INT32_MIN); FIntVector ClipMin(INT32_MAX,INT32_MAX,INT32_MAX),ClipMax(INT32_MIN,INT32_MIN,INT32_MIN); for(const FVoxelTileKey& Tile:Desired.Tiles) { const FIntVector Origin=Tile.OriginVoxels();const int32 Extent=Tile.ExtentVoxels(); ClipMin.X=std::min(ClipMin.X,Origin.X);ClipMin.Y=std::min(ClipMin.Y,Origin.Y);ClipMin.Z=std::min(ClipMin.Z,Origin.Z); ClipMax.X=std::max(ClipMax.X,Origin.X+Extent);ClipMax.Y=std::max(ClipMax.Y,Origin.Y+Extent);ClipMax.Z=std::max(ClipMax.Z,Origin.Z+Extent); if(Tile.Level Neighborhood; Neighborhood.reserve(32); for(const FVoxelTileKey& Tile:Desired.Tiles) if(Tile.Level==0&&std::abs(Tile.Coord.X-CenterChunk.X)<=1 &&std::abs(Tile.Coord.Y-CenterChunk.Y)<=1&&std::abs(Tile.Coord.Z-CenterChunk.Z)<=1) Neighborhood.insert(Tile); if(Neighborhood.size()!=27) {std::fprintf(stderr,"[StreamPipeline] expected 27 player-neighborhood tiles, found %zu.\n",Neighborhood.size());return 7;} FStandaloneShapeWorld Shape; std::string BuildError; const bool bIncludeFarWorld=MaxLevel<0||MaxLevel>=World.FullResClipLevels; const auto ShapeBuildStart=Clock::now(); if(!Shape.Build(World,FineMin,FineMax,ClipMin,ClipMax,WorkerCount, DetailVariant,bIncludeFarWorld,BuildError)) {std::fprintf(stderr,"[StreamPipeline] full shape build failed: %s\n",BuildError.c_str());return 2;} const auto ShapeReady=Clock::now(); const double ShapeSeconds=std::chrono::duration(ShapeReady-ShapeBuildStart).count(); FStandaloneShapeWorldAccess::SetActive(&Shape); FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel, bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors); const auto StreamStart=Clock::now(); const FStandaloneStreamingUpdate Update=Streamer.Start(CameraPositionVoxels); if(Update.DesiredTiles!=static_cast(Desired.Tiles.size())) { Streamer.Stop();FStandaloneShapeWorldAccess::SetActive(nullptr); std::fprintf(stderr,"[StreamPipeline] expected %zu tiles but queued %d.\n", Desired.Tiles.size(),Update.DesiredTiles);return 7; } std::vector DensityRows,MeshRows,PayloadRows,TopologyRows; int32 Finished=0,NeighborhoodFinished=0,FullDetailFinished=0; int32 FullDetailExpected=0; for(const FVoxelTileKey& Tile:Desired.Tiles) if(Tile.Level Ready=Streamer.TakeReady(); if(Ready.empty()){std::this_thread::sleep_for(std::chrono::milliseconds(1));continue;} for(const FStandaloneCpuTile& Tile:Ready) { ++Finished; WholeViewReady=std::max(WholeViewReady,Tile.CompletedAt); if(Neighborhood.contains(Tile.Key)) { ++NeighborhoodFinished; NeighborhoodReady=std::max(NeighborhoodReady,Tile.CompletedAt); } if(Tile.Key.Level(Neighborhood.size())) { std::printf("[PipelineMilestone] neighborhood_ready=%.3fs generated=%d/27\n", std::chrono::duration(NeighborhoodReady-ColdStart).count(),NeighborhoodFinished); bNeighborhoodReported=true; } if(!bFullDetailReported&&FullDetailFinished==FullDetailExpected) { std::printf("[PipelineMilestone] full_detail_ready=%.3fs generated=%d/%d\n", std::chrono::duration(FullDetailReady-ColdStart).count(), FullDetailFinished,FullDetailExpected); bFullDetailReported=true; } } Streamer.Stop(); const double NeighborhoodSeconds=std::chrono::duration(NeighborhoodReady-ColdStart).count(); const double NeighborhoodMeshSeconds=std::chrono::duration(NeighborhoodReady-ShapeReady).count(); const double FullDetailSeconds=std::chrono::duration(FullDetailReady-ColdStart).count(); const double WholeViewSeconds=std::chrono::duration(WholeViewReady-ColdStart).count(); const double StreamSeconds=std::chrono::duration(WholeViewReady-StreamStart).count(); std::printf("[StreamPipeline] workers=%d shape=%.3fs neighborhood_mesh=%.3fs neighborhood_ready=%.3fs full_detail_ready=%.3fs whole_view_ready=%.3fs neighborhood_tiles=%d/27 full_detail_tiles=%d/%d whole_tiles=%d/%zu stream_wall=%.3fs cache_hits=0 full_payload=1\n", WorkerCount,ShapeSeconds,NeighborhoodMeshSeconds,NeighborhoodSeconds, FullDetailSeconds,WholeViewSeconds,NeighborhoodFinished, FullDetailFinished,FullDetailExpected,Finished,Desired.Tiles.size(),StreamSeconds); bool bHashesPassing=true; if(bHashDensity) { bHashesPassing=DensityRows.size()==Desired.Tiles.size() &&MeshRows.size()==Desired.Tiles.size() &&PayloadRows.size()==Desired.Tiles.size() &&TopologyRows.size()==Desired.Tiles.size(); std::vector FullDensity,FullMesh,FullPayload,FullTopology; for(const FHashedTile& Row:DensityRows)if(Row.Key.Level(HashTileRows(FullDensity)), static_cast(HashTileRows(FullMesh)), static_cast(HashTileRows(FullPayload)), static_cast(HashTileRows(FullTopology))); std::printf("[PipelineWholeViewHash] tiles=%zu density=0x%016llx mesh=0x%016llx payload=0x%016llx topology=0x%016llx\n", Desired.Tiles.size(),static_cast(HashTileRows(DensityRows)), static_cast(HashTileRows(MeshRows)), static_cast(HashTileRows(PayloadRows)), static_cast(HashTileRows(TopologyRows))); } FStandaloneShapeWorldAccess::SetActive(nullptr); return NeighborhoodFinished==static_cast(Neighborhood.size()) &&FullDetailFinished==FullDetailExpected&&Finished==Update.DesiredTiles&&bHashesPassing?0:6; } int RunStreamingBenchmark(FStandaloneOwnerWorld& World,const FVector& CameraPositionVoxels, int32 WorkerCount,int32 MaxLevel,bool bRoundTunnels, bool bEnhancedWalls,const FStandaloneMeshCache* Cache, bool bHashDensity,bool bSkipUvAndColors,bool bSkipMaterialGroups, bool bSkipUV,bool bSkipColors, bool bShardAudit,bool bFarSurvivalAudit,bool bFarAuditBadShell, float CameraYaw,float CameraPitch, const std::string& BakeCachePath, const FStandaloneMeshCacheMetadata& CacheMetadata) { FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraPositionVoxels); if(MaxLevel>=0) Desired.Tiles.erase(std::remove_if(Desired.Tiles.begin(),Desired.Tiles.end(), [&](const FVoxelTileKey& Tile){return Tile.Level>MaxLevel;}),Desired.Tiles.end()); if(Desired.Tiles.size()>1000000) { std::fprintf(stderr,"Stream benchmark rejected an unsafe desired set of %zu tiles.\n", Desired.Tiles.size()); return 7; } const int32 FullDetailExpected=static_cast(std::count_if( Desired.Tiles.begin(),Desired.Tiles.end(),[&](const FVoxelTileKey& Tile) {return Tile.Level FarAudit; if(bFarSurvivalAudit) { const FStandaloneShapeWorld* Shape=FStandaloneShapeWorldAccess::GetActive(); if(!Shape||!Shape->HasFarWorld()) {std::fprintf(stderr,"Far survival audit requires an active shape world with far-parent data.\n");return 15;} FarAudit=std::make_unique(World,*Shape,Desired.Tiles,bFarAuditBadShell); } std::array,MaxLevels+1> HashRows; std::array,MaxLevels+1> MeshHashRows; std::array,MaxLevels+1> PayloadHashRows; std::array,MaxLevels+1> TopologyHashRows; std::array SamplesByLevel{}; std::array VerticesByLevel{}; std::array IndicesByLevel{}; std::array TilesByLevel{}; std::array WorkerSecondsByLevel{}; std::array FirstCompletionByLevel{}; std::array LastCompletionByLevel{}; std::array OutputBytesByLevel{}; std::array MeshPayloadAllocatedBytesByLevel{}; FirstCompletionByLevel.fill(std::numeric_limits::infinity()); std::unordered_set PlayerNeighborhoodTiles; for(const FVoxelTileKey& Tile:Desired.Tiles) if(Tile.Level==0&&std::abs(Tile.Coord.X-Desired.CenterChunk.X)<=1 &&std::abs(Tile.Coord.Y-Desired.CenterChunk.Y)<=1 &&std::abs(Tile.Coord.Z-Desired.CenterChunk.Z)<=1) PlayerNeighborhoodTiles.insert(Tile); std::unordered_set PlayerNeighborhoodReady; auto PlayerNeighborhoodCompleted=std::chrono::steady_clock::time_point{}; std::vector CacheTiles; if(!BakeCachePath.empty())CacheTiles.reserve(Desired.Tiles.size()); const auto Start=std::chrono::steady_clock::now(); FStandaloneStreamingGenerator Streamer(World,WorkerCount, bHashDensity||!BakeCachePath.empty(),Cache,MaxLevel, bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors); const FStandaloneStreamingUpdate Update=Streamer.Start(CameraPositionVoxels); const double LOD0Width=static_cast(2*World.ClipRadius+2) *CHUNK_SIZE*VOXEL_SIZE/100.0; std::printf("[StreamBenchmarkConfig] clip_radius=%d lod0_cube=%.0fm voxel=25cm fullres_levels=%d max_level_filter=%d workers=%d full_detail_tiles=%d desired_tiles=%d variant=%s uv=%s colors=%s material_groups=%s hash=%s\n", World.ClipRadius,LOD0Width,World.FullResClipLevels,MaxLevel<0?World.MaxClipLevel:MaxLevel, Streamer.GetWorkerCount(),FullDetailExpected,Update.DesiredTiles, bRoundTunnels?(bEnhancedWalls?"round-detailed":"round-legacy"):"base", (bSkipUvAndColors||bSkipUV)?"skipped":"full", (bSkipUvAndColors||bSkipColors)?"skipped":"full", bSkipMaterialGroups?"skipped":"full", (bHashDensity||!BakeCachePath.empty())?"on":"off"); std::fflush(stdout); int32 Finished=0,FullDetailFinished=0; bool bFullDetailTimeReported=false; double FullDetailSeconds=0.0; const int32 DesiredTotal=Update.DesiredTiles; FMeshSheetAudit MeshSheetStats; uint64 MeshAuditedTiles=0; while(Finished Ready=Streamer.TakeReady(); if(Ready.empty()) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } for(FStandaloneCpuTile& Tile:Ready) { if(FarAudit)FarAudit->AuditTile(Tile); ++Finished; const int32 Level=Tile.Key.Level; if(PlayerNeighborhoodTiles.contains(Tile.Key)) { PlayerNeighborhoodReady.insert(Tile.Key); if(PlayerNeighborhoodReady.size()==PlayerNeighborhoodTiles.size()) PlayerNeighborhoodCompleted=Tile.CompletedAt; } if(Level>=0&&Level<=MaxLevels) { SamplesByLevel[Level]+=Tile.DensitySamples; VerticesByLevel[Level]+=Tile.Vertices.size(); IndicesByLevel[Level]+=Tile.Indices.size(); ++TilesByLevel[Level]; WorkerSecondsByLevel[Level]+=Tile.WorkerSeconds; const double CompletedSeconds=std::chrono::duration(Tile.CompletedAt-Start).count(); FirstCompletionByLevel[Level]=std::min(FirstCompletionByLevel[Level],CompletedSeconds); LastCompletionByLevel[Level]=std::max(LastCompletionByLevel[Level],CompletedSeconds); OutputBytesByLevel[Level]+=static_cast(Tile.Vertices.capacity())*sizeof(FStandaloneCpuVertex) +static_cast(Tile.Indices.capacity())*sizeof(uint32); MeshPayloadAllocatedBytesByLevel[Level]+=Tile.MeshPayloadAllocatedBytes; if(Tile.bHasDensityHash)HashRows[Level].push_back({Tile.Key,Tile.DensityHash}); if(Tile.bHasMeshHash)MeshHashRows[Level].push_back({Tile.Key,Tile.MeshHash}); if(Tile.bHasPayloadHash)PayloadHashRows[Level].push_back({Tile.Key,Tile.PayloadHash}); if(Tile.bHasTopologyHash)TopologyHashRows[Level].push_back({Tile.Key,Tile.TopologyHash}); } if(Level=FullDetailExpected) { bFullDetailTimeReported=true; FullDetailSeconds=std::chrono::duration( std::chrono::steady_clock::now()-Start).count(); std::vector FullRows; std::vector FullMeshRows; std::vector FullPayloadRows; std::vector FullTopologyRows; for(int32 Level=0;Level(HashTileRows(FullRows)), static_cast(HashTileRows(FullMeshRows)), static_cast(HashTileRows(FullPayloadRows)), static_cast(HashTileRows(FullTopologyRows))); else std::printf("[StreamFullDetail] wall=%.3fs generated=%d/%d resident_tiles_cpu_only=1 hash=off\n", FullDetailSeconds,FullDetailFinished,FullDetailExpected); std::fflush(stdout); } } const double FullClipSeconds=std::chrono::duration( std::chrono::steady_clock::now()-Start).count(); if(!PlayerNeighborhoodTiles.empty()&&PlayerNeighborhoodReady.size()==PlayerNeighborhoodTiles.size()) std::printf("[PlayerNeighborhood] lod0_tiles=%zu/%zu mesh_ready=%.3fs collision_cook=unavailable_in_standalone\n", PlayerNeighborhoodReady.size(),PlayerNeighborhoodTiles.size(), std::chrono::duration(PlayerNeighborhoodCompleted-Start).count()); else std::printf("[PlayerNeighborhood] lod0_tiles=%zu/%zu mesh_ready=missing collision_cook=unavailable_in_standalone\n", PlayerNeighborhoodReady.size(),PlayerNeighborhoodTiles.size()); const double TotalWorkerSeconds=std::accumulate(WorkerSecondsByLevel.begin(),WorkerSecondsByLevel.end(),0.0); const uint64 TotalMeshPayloadAllocatedBytes=std::accumulate( MeshPayloadAllocatedBytesByLevel.begin(),MeshPayloadAllocatedBytesByLevel.end(),uint64{0}); const uint64 TotalViewerOutputBytes=std::accumulate( OutputBytesByLevel.begin(),OutputBytesByLevel.end(),uint64{0}); const double WorkerBusyPercent=100.0*TotalWorkerSeconds /(std::max(1,WorkerCount)*std::max(0.001,FullClipSeconds)); std::vector AllRows; std::vector AllMeshRows; std::vector AllPayloadRows; std::vector AllTopologyRows; if(bHashDensity||!BakeCachePath.empty()) for(int32 Level=0;Level<=MaxLevels;++Level) { AllRows.insert(AllRows.end(),HashRows[Level].begin(),HashRows[Level].end()); AllMeshRows.insert(AllMeshRows.end(),MeshHashRows[Level].begin(),MeshHashRows[Level].end()); AllPayloadRows.insert(AllPayloadRows.end(),PayloadHashRows[Level].begin(),PayloadHashRows[Level].end()); AllTopologyRows.insert(AllTopologyRows.end(),TopologyHashRows[Level].begin(),TopologyHashRows[Level].end()); } if(bHashDensity||!BakeCachePath.empty()) std::printf("[StreamFullClipmap] wall=%.3fs generated=%d/%d worker_sum=%.3fs worker_busy=%.1f%% worker_idle=%.1f%% payload_alloc=%.2fMiB viewer_output=%.2fMiB tiles=%d density_hash=0x%016llx mesh_hash=0x%016llx payload_hash=0x%016llx topology_hash=0x%016llx\n", FullClipSeconds,Finished,DesiredTotal,TotalWorkerSeconds,WorkerBusyPercent, std::max(0.0,100.0-WorkerBusyPercent), static_cast(TotalMeshPayloadAllocatedBytes)/1048576.0, static_cast(TotalViewerOutputBytes)/1048576.0, DesiredTotal,static_cast(HashTileRows(AllRows)), static_cast(HashTileRows(AllMeshRows)), static_cast(HashTileRows(AllPayloadRows)), static_cast(HashTileRows(AllTopologyRows))); else std::printf("[StreamFullClipmap] wall=%.3fs generated=%d/%d worker_sum=%.3fs worker_busy=%.1f%% worker_idle=%.1f%% payload_alloc=%.2fMiB viewer_output=%.2fMiB tiles=%d hash=off\n", FullClipSeconds,Finished,DesiredTotal,TotalWorkerSeconds,WorkerBusyPercent, std::max(0.0,100.0-WorkerBusyPercent), static_cast(TotalMeshPayloadAllocatedBytes)/1048576.0, static_cast(TotalViewerOutputBytes)/1048576.0,DesiredTotal); std::printf("[StreamCacheUse] hits=%llu requested=%d\n", static_cast(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(MeshAuditedTiles), static_cast(MeshSheetStats.Components), static_cast(MeshSheetStats.SmallComponents), static_cast(MeshSheetStats.SmallComponentsInView), static_cast(MeshSheetStats.BoundarySmallComponentsInView), static_cast(MeshSheetStats.ThinSheets), static_cast(MeshSheetStats.TrianglesInThinSheets), MeshSheetStats.ThinSheets==0&&MeshSheetStats.SmallComponentsInView==0?"PASS":"FAIL"); for(int32 Level=0;Level<=MaxLevels;++Level) { if(TilesByLevel[Level]==0)continue; if(bHashDensity||!BakeCachePath.empty()) std::printf("[StreamLevel] lod=%d tiles=%llu samples=%.3fM worker_sum=%.3fs first_ready=%.3fs complete=%.3fs worker_busy=%.1f%% payload_alloc=%.2fMiB viewer_output=%.2fMiB vertices=%llu indices=%llu density_hash=0x%016llx mesh_hash=0x%016llx payload_hash=0x%016llx topology_hash=0x%016llx\n", Level,static_cast(TilesByLevel[Level]), static_cast(SamplesByLevel[Level])/1.0e6,WorkerSecondsByLevel[Level], FirstCompletionByLevel[Level],LastCompletionByLevel[Level], 100.0*WorkerSecondsByLevel[Level]/(WorkerCount*std::max(0.001,LastCompletionByLevel[Level])), static_cast(MeshPayloadAllocatedBytesByLevel[Level])/1048576.0, static_cast(OutputBytesByLevel[Level])/1048576.0, static_cast(VerticesByLevel[Level]), static_cast(IndicesByLevel[Level]), static_cast(HashTileRows(HashRows[Level])), static_cast(HashTileRows(MeshHashRows[Level])), static_cast(HashTileRows(PayloadHashRows[Level])), static_cast(HashTileRows(TopologyHashRows[Level]))); else std::printf("[StreamLevel] lod=%d tiles=%llu samples=%.3fM worker_sum=%.3fs first_ready=%.3fs complete=%.3fs worker_busy=%.1f%% payload_alloc=%.2fMiB viewer_output=%.2fMiB vertices=%llu indices=%llu hash=off\n", Level,static_cast(TilesByLevel[Level]), static_cast(SamplesByLevel[Level])/1.0e6,WorkerSecondsByLevel[Level], FirstCompletionByLevel[Level],LastCompletionByLevel[Level], 100.0*WorkerSecondsByLevel[Level]/(WorkerCount*std::max(0.001,LastCompletionByLevel[Level])), static_cast(MeshPayloadAllocatedBytesByLevel[Level])/1048576.0, static_cast(OutputBytesByLevel[Level])/1048576.0, static_cast(VerticesByLevel[Level]), static_cast(IndicesByLevel[Level])); } if(!BakeCachePath.empty()) { std::string CacheError; if(!FStandaloneMeshCache::Save(BakeCachePath,CacheMetadata,CacheTiles,CacheError)) { std::fprintf(stderr,"[MeshCacheBake] failed: %s\n",CacheError.c_str()); return 8; } const uint64 Bytes=std::filesystem::file_size(BakeCachePath); std::printf("[MeshCacheBake] saved %zu tiles, %.2f MiB to %s\n",CacheTiles.size(), static_cast(Bytes)/1048576.0,BakeCachePath.c_str()); } if(Finished!=DesiredTotal||FullDetailFinished!=FullDetailExpected)return 6; if(bShardAudit&&(MeshSheetStats.ThinSheets!=0||MeshSheetStats.SmallComponentsInView!=0))return 13; if(FarAudit) { FarAudit->Finalize(); FarAudit->Print(); if(!FarAudit->Passing())return 14; } return 0; } int RunEditTransactionBenchmark(FStandaloneOwnerWorld& World,FStandaloneShapeWorld& Shape, const FVector& CameraPositionVoxels,int32 WorkerCount) { using Clock=std::chrono::steady_clock; // Place the spell across the LOD0/LOD1 clipmap seam so one edit exercises every visible // level it intersects, instead of timing only the center LOD0 tiles. const double X=CameraPositionVoxels.X+3.0*CHUNK_SIZE; const double Y=CameraPositionVoxels.Y,Z=CameraPositionVoxels.Z; std::vector Brushes(3); Brushes[0].Shape=FStandaloneShapeWorld::FEditBrush::EShape::Sphere; Brushes[0].Operation=FStandaloneShapeWorld::FEditBrush::EOperation::Carve; Brushes[0].A={X,Y,Z};Brushes[0].Radius=20.0; Brushes[1].Shape=FStandaloneShapeWorld::FEditBrush::EShape::RoundedBox; Brushes[1].Operation=FStandaloneShapeWorld::FEditBrush::EOperation::Carve; Brushes[1].A={X,Y,Z+12.0};Brushes[1].HalfExtent={11.0,11.0,4.0};Brushes[1].Rounding=3.0; Brushes[2].Shape=FStandaloneShapeWorld::FEditBrush::EShape::Capsule; Brushes[2].Operation=FStandaloneShapeWorld::FEditBrush::EOperation::AddRock; Brushes[2].A={X-22.0,Y,Z-8.0};Brushes[2].B={X+22.0,Y,Z-8.0};Brushes[2].Radius=3.0; const auto EditStart=Clock::now(); Shape.SetEditTransaction(std::move(Brushes)); FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraPositionVoxels); const FIntVector EditMin=Shape.GetEditBoundsMin(),EditMax=Shape.GetEditBoundsMax(); std::vector Affected;Affected.reserve(128); std::array AffectedByLevel{},FinishedByLevel{}; for(const FVoxelTileKey& Tile:Desired.Tiles) { const FIntVector Origin=Tile.OriginVoxels();const int32 Extent=Tile.ExtentVoxels(); const int32 Pad=std::max(2,Tile.StepVoxels()); if(Origin.X-Pad>EditMax.X||Origin.X+Extent+PadEditMax.Y||Origin.Y+Extent+PadEditMax.Z||Origin.Z+Extent+Pad=0&&Tile.Level<=MaxLevels)++AffectedByLevel[Tile.Level]; } if(Affected.empty()) { std::fprintf(stderr,"[EditTransaction] failed: no desired tiles intersect the edit bounds.\n"); return 9; } FStandaloneStreamingGenerator Streamer(World,WorkerCount,false,nullptr,-1); Streamer.SetDesiredTileFilter(Affected); const auto Start=Clock::now(); const FStandaloneStreamingUpdate Update=Streamer.Start(CameraPositionVoxels); int32 Finished=0; double WorkerSeconds=0.0; while(Finished Ready=Streamer.TakeReady(); if(Ready.empty()){std::this_thread::sleep_for(std::chrono::milliseconds(1));continue;} for(const FStandaloneCpuTile& Tile:Ready) { ++Finished;WorkerSeconds+=Tile.WorkerSeconds; if(Tile.Key.Level>=0&&Tile.Key.Level<=MaxLevels)++FinishedByLevel[Tile.Key.Level]; } } const double RemeshWall=std::chrono::duration(Clock::now()-Start).count(); Streamer.Stop(); std::printf("[EditTransaction] operation=crater_batch transaction_count=1 brushes=3 diameter=10.0m offset_from_player=%.2fm affected_tiles=%d/%zu workers=%d cache_hits=%llu remesh_wall=%.3fs worker_sum=%.3fs edit_to_ready=%.3fs status=%s\n", 3.0*CHUNK_SIZE*VOXEL_SIZE/100.0, Finished,Affected.size(),WorkerCount,static_cast(Streamer.GetCacheHits()), RemeshWall,WorkerSeconds,std::chrono::duration(Clock::now()-EditStart).count(), Finished==static_cast(Affected.size())?"PASS":"FAIL"); for(int32 Level=0;Level<=MaxLevels;++Level) if(AffectedByLevel[Level]) std::printf("[EditTransactionLevel] lod=%d tiles=%llu remeshed=%llu status=%s\n",Level, static_cast(AffectedByLevel[Level]), static_cast(FinishedByLevel[Level]), AffectedByLevel[Level]==FinishedByLevel[Level]?"PASS":"FAIL"); return Finished==static_cast(Affected.size())?0:10; } } int main(int Argc,char** Argv) { const auto ColdProcessStart=std::chrono::steady_clock::now(); FApp App;App.DataPath=DefaultDataPath().string();App.ColdProcessStart=ColdProcessStart; std::string JoinedCommandLine; for(int32 I=1;I(Value.size()),Value.data());return 2;} } else if(Arg=="--round-tunnels")App.bRoundTunnels=true; else if(Arg=="--round-tunnels-legacy") {App.bRoundTunnels=true;App.bRoundTunnelEnhanced=false;} else if(Arg=="--stream-benchmark")bStreamBenchmark=true; else if(Arg=="--stream-pipeline") {bStreamPipeline=true;bStreamBenchmark=true;App.bShapeWorld=true;App.bForceGenerate=true;} else if(Arg=="--edit-benchmark") {bEditBenchmark=true;bStreamBenchmark=true;App.bShapeWorld=true;} else if(Arg=="--stream-hash")bStreamHash=true; else if(Arg=="--skip-uv-colors")bSkipUvAndColors=true; else if(Arg=="--skip-uv")bSkipUV=true; else if(Arg=="--skip-colors")bSkipColors=true; else if(Arg=="--skip-material-groups")bSkipMaterialGroups=true; else if(Arg=="--shard-audit")bShardAudit=true; else if(Arg=="--far-survival-audit")bFarSurvivalAudit=true,App.bShapeWorld=true; else if(Arg=="--far-audit-bad-shell") {bFarAuditBadShell=true;bFarSurvivalAudit=true;App.bShapeWorld=true;} else if(Arg=="--compare-round-walls") {bRoundWallCompare=true;App.bRoundTunnels=true;} else if(Arg.starts_with("--round-compare-tiles=")) RoundCompareTileCount=std::max(1,std::atoi(std::string(Arg.substr(22)).c_str())); else if(Arg=="--force-generate")App.bForceGenerate=true; else if(Arg.starts_with("--bake-mesh-cache=")) {BakeMeshCachePath=std::string(Arg.substr(18));App.bForceGenerate=true;} else if(Arg.starts_with("--stream-max-level=")) StreamBenchmarkMaxLevel=std::atoi(std::string(Arg.substr(19)).c_str()); else if(Arg.starts_with("--max-clip-level=")) MaxClipLevelOverride=std::clamp( std::atoi(std::string(Arg.substr(17)).c_str()),0,8); else if(Arg=="--test-one-chunk-crossing")App.bTestOneChunkCrossing=true; else if(Arg=="--capture-after-clipmap")App.bCaptureAfterClipmap=true; else if(Arg=="--quit-after-clipmap")App.bQuitAfterClipmap=true; else if(Arg.starts_with("--workers=")) { const int32 Parsed=std::atoi(std::string(Arg.substr(10)).c_str()); 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); else if(Arg.starts_with("--benchmark-level=")) BenchmarkLevel=std::atoi(std::string(Arg.substr(18)).c_str()); else if(Arg.starts_with("--benchmark-count=")) BenchmarkCount=std::max(1,std::atoi(std::string(Arg.substr(18)).c_str())); else if(Arg.starts_with("--camera=")) { const std::string Values(Arg.substr(9)); float X=0,Y=0,Z=0,YawDegrees=0,PitchDegrees=0; if(std::sscanf(Values.c_str(),"%f,%f,%f,%f,%f",&X,&Y,&Z,&YawDegrees,&PitchDegrees)==5) { App.bHasCameraOverride=true;App.CameraPositionOverride={X,Y,Z}; App.CameraYawOverride=YawDegrees*0.017453292519943295f; App.CameraPitchOverride=PitchDegrees*0.017453292519943295f; } else {std::fprintf(stderr,"Invalid --camera value: %s\n",Values.c_str());return 2;} } else if(!Arg.empty()&&Arg.front()!='-')App.DataPath=std::string(Arg); } FStandaloneShapeWorld::SetDetailProfileEnabled(bProfileDetailBand); std::string Error; App.World=std::make_unique(); if(!StandaloneOwnerLoader::Load(App.DataPath,*App.World,Error)){std::fprintf(stderr,"Owner data load failed: %s\n",Error.c_str());return 2;} App.Seed=App.World->Seed; if(App.bParityOnly) { VoxelGenLOD::InitializeConsoleSwitches(); return App.PrintParity()==0?0:1; } App.Position={static_cast(App.World->ViewerStartVoxels.X*0.25), static_cast(App.World->ViewerStartVoxels.Y*0.25),static_cast(App.World->ViewerStartVoxels.Z*0.25)}; if(App.bHasCameraOverride){App.Position=App.CameraPositionOverride;App.Yaw=App.CameraYawOverride;App.Pitch=App.CameraPitchOverride;} const int32 ExportClipRadius=App.World->ClipRadius; const int32 ExportMaxClipLevel=App.World->MaxClipLevel; App.World->ClipRadius=App.ClipRadius; App.World->Settings->ClipRadius=App.ClipRadius; if(App.bRoundTunnels&&!PrepareStandaloneDensityWorld(*App.World,App.Seed, App.bRoundTunnels,App.bRoundTunnelEnhanced,Error)) {std::fprintf(stderr,"Standalone world setup failed: %s\n",Error.c_str());return 2;} if(MaxClipLevelOverride>=0) { App.MaxClipLevelOverride=MaxClipLevelOverride; App.World->MaxClipLevel=MaxClipLevelOverride; App.World->Settings->MaxClipLevel=MaxClipLevelOverride; std::printf("Standalone max clip level override: exported=%d -> max_level=%d.\n", ExportMaxClipLevel,MaxClipLevelOverride); } const bool bNeedFarShapeWorld=bStreamBenchmark ?(StreamBenchmarkMaxLevel<0||StreamBenchmarkMaxLevel>=App.World->FullResClipLevels) :(App.bShapeCompare?ShapeCompareLevel>=App.World->FullResClipLevels :(BenchmarkLevel>=App.World->FullResClipLevels)); if(App.bShapeWorld&&(App.bShapeAuditOnly||App.bShapeCompare||bStreamBenchmark||BenchmarkLevel>=0) &&!bStreamPipeline &&!App.BuildShapeWorldForCamera(Error,bNeedFarShapeWorld)) {std::fprintf(stderr,"Shape interval build failed: %s\n",Error.c_str());return 2;} if(App.bShapeAuditOnly) { if(bPassageDetailAuditOnly) return App.ShapeWorld->AuditSurfaceBandFamilies(true)?0:8; const bool bSurfacePassing=App.ShapeWorld->AuditViewSurface(App.Position.X,App.Position.Y, App.Position.Z,App.Yaw,App.Pitch); return App.ShapeWorld->IsDetailAuditPassing()&&bSurfacePassing?0:7; } if(App.bShapeCompare) return RunOwnerShapeComparison(*App.World,*App.ShapeWorld,App.Position,App.Yaw,App.Pitch,App.WorkerCount, ShapeCompareLevel,ShapeCompareTileLimit); if(bRoundWallCompare) return RunRoundWallComparison(*App.World,App.Position,RoundCompareTileCount); if(bStreamBenchmark) { if(bStreamPipeline) return RunPipelinedStreamingBenchmark(*App.World, FVector(App.Position.X*4.0,App.Position.Y*4.0,App.Position.Z*4.0), App.WorkerCount,StreamBenchmarkMaxLevel,App.ShapeDetailVariant, bStreamHash||!BakeMeshCachePath.empty(),bSkipUvAndColors,bSkipMaterialGroups, bSkipUV,bSkipColors,ColdProcessStart); if(bEditBenchmark) return RunEditTransactionBenchmark(*App.World,*App.ShapeWorld, FVector(App.Position.X*4.0,App.Position.Y*4.0,App.Position.Z*4.0),App.WorkerCount); const double FullDetailWidth=static_cast(2*App.ClipRadius+2) *CHUNK_SIZE*VOXEL_SIZE/100.0; std::printf("Standalone clip override: exported radius=%d (112 m cube) -> radius=%d (%.0f m LOD0 cube), voxel size=25 cm.\n", ExportClipRadius,App.ClipRadius,FullDetailWidth); if(!App.bForceGenerate&&!App.bShapeWorld)App.LoadMeshCache(); return RunStreamingBenchmark(*App.World, 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,bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,bShardAudit, bFarSurvivalAudit,bFarAuditBadShell,App.Yaw,App.Pitch, BakeMeshCachePath,App.MakeMeshCacheMetadata()); } if(BenchmarkLevel>=0) return RunTileBenchmark(*App.World,App.Position,BenchmarkLevel,BenchmarkCount, App.bProfileAttribution,App.bCompareEvaluators,App.bDensityHash); App.World.reset(); WNDCLASSA Class{};Class.lpfnWndProc=WindowProc;Class.hInstance=GetModuleHandleA(nullptr);Class.lpszClassName="VoxelForgeStandaloneWorld"; Class.hCursor=LoadCursor(nullptr,IDC_ARROW);RegisterClassA(&Class); RECT Rect{0,0,App.Width,App.Height};AdjustWindowRect(&Rect,WS_OVERLAPPEDWINDOW,FALSE); App.Window=CreateWindowA(Class.lpszClassName,"VoxelForge standalone world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT, Rect.right-Rect.left,Rect.bottom-Rect.top,nullptr,nullptr,Class.hInstance,nullptr); GApp=&App; if(!App.Window||!App.InitializeDevice()){MessageBoxA(nullptr,"Direct3D 11 initialization failed.","VoxelForge standalone",MB_ICONERROR);return 3;} ShowWindow(App.Window,SW_SHOW);UpdateWindow(App.Window); std::printf("Controls: hold right mouse to look; WASD move; Space/Ctrl (E/Q) up/down; Shift faster; wheel changes speed; F wireframe; G toggles legacy/detailed round walls; 1-9 toggle LOD; 0 toggle all levels; N/P change seed; T swaps base/round tunnels; V swaps owner density/shape intervals; Esc quit.\n"); std::printf("Owner data: %s; starting at first-strate open-space sample (%.2f, %.2f, %.2f) m.\n", App.DataPath.c_str(),App.Position.X,App.Position.Y,App.Position.Z); std::fflush(stdout); App.StartGeneration(); MSG Message{}; while(Message.message!=WM_QUIT) { if(PeekMessage(&Message,nullptr,0,0,PM_REMOVE)){TranslateMessage(&Message);DispatchMessage(&Message);} else App.Draw(); } GApp=nullptr; return 0; }