standalone: VoxelForgeCore library -- the fast generator behind a clean C++20 API, output unchanged
- Standalone/Core: public header VoxelForgeCore.h (no Unreal types), static library target, ResolvedWorldSnapshot (from the schema-v1 JSON), World (compile, generateTile, density/sign, point/segment solid, classifyBox), host-supplied JobScheduler (the core starts no threads), sequenced, idempotent EditTransaction returning affected tiles per LOD. - The viewer, benchmarks, edits and audits run on Core; StreamingWorld stays host code. - Same as round 11: 11 hash rows at W1 and W8, routes 5642/5642, look/passage/far audits pass, bad-shell control fails, six fixed captures byte-identical; cold W8 1.025 s vs 1.041 s reference. - SHARED_CPP_LIBRARY_PLAN.md rewritten for a UBT module compiling the same sources via shims. Open issue before Unreal: Core's implementation still compiles the Compat stand-ins named like Unreal types (TArray, FVector...); inside a UE binary that is an ODR clash. Must be namespaced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
631dd57eb4
commit
557f6aac30
22 files changed
+6185
-5063
No files matched your search
@@ -1,7 +1,7 @@
|
||||
#include "OwnerWorldLoader.h"
|
||||
#include "StreamingWorld.h"
|
||||
#include "StandaloneResearchTunnels.h"
|
||||
#include "StandaloneShapeWorld.h"
|
||||
#include "StandaloneCoreScheduler.h"
|
||||
#include "../Core/src/StandaloneSnapshotAdapter.h"
|
||||
#include "VoxelDensityProfile.h"
|
||||
|
||||
#ifdef TEXT
|
||||
@@ -313,13 +313,14 @@ namespace
|
||||
// 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<UVoxelStrateManager>();
|
||||
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<UVoxelGenerator>();
|
||||
World.Generator->InitializeSettings(World.Settings.get());
|
||||
World.Generator->SetStrateManager(World.Manager.get());
|
||||
World.Generator->bStandaloneResearchRoundTunnels=bRoundTunnels;
|
||||
World.Generator->bStandaloneResearchEnhancedWalls=bEnhancedWalls;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -337,9 +338,11 @@ namespace
|
||||
ID3D11Buffer* Constants=nullptr;
|
||||
ID3D11RasterizerState* SolidRasterizer=nullptr;
|
||||
ID3D11RasterizerState* WireRasterizer=nullptr;
|
||||
std::unique_ptr<FStandaloneOwnerWorld> World;
|
||||
std::unique_ptr<FStandaloneShapeWorld> ShapeWorld;
|
||||
std::unique_ptr<FStandaloneShapeWorld> PendingFullShapeWorld;
|
||||
std::shared_ptr<FStandaloneOwnerWorld> World;
|
||||
VoxelForgeCore::ResolvedWorldSnapshot Snapshot;
|
||||
std::unique_ptr<VoxelForgeCore::World> ShapeWorld;
|
||||
std::unique_ptr<VoxelForgeCore::World> PendingFullShapeWorld;
|
||||
FStandaloneCoreScheduler CoreScheduler;
|
||||
std::unique_ptr<FStandaloneStreamingGenerator> Streamer;
|
||||
std::thread FullShapeBuildThread;
|
||||
std::atomic<bool> bFullShapeBuildFinished{false};
|
||||
@@ -376,13 +379,14 @@ namespace
|
||||
bool bShapeAuditOnly=false;
|
||||
bool bShapeCompare=false;
|
||||
bool bFarShapeStagePending=false;
|
||||
FStandaloneShapeWorld::EDetailVariant ShapeDetailVariant=FStandaloneShapeWorld::EDetailVariant::Coherent;
|
||||
VoxelForgeCore::DetailVariant ShapeDetailVariant=VoxelForgeCore::DetailVariant::coherent;
|
||||
bool bWireframe=false;
|
||||
bool bCaptured=false;
|
||||
bool bAutoCaptureWritten=false;
|
||||
bool bFullDetailReported=false;
|
||||
bool bInitialClipmapReported=false;
|
||||
bool bProfileAttribution=false;
|
||||
bool bProfileDetailBand=false;
|
||||
bool bCompareEvaluators=false;
|
||||
bool bParityOnly=false;
|
||||
bool bDensityHash=false;
|
||||
@@ -649,9 +653,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
|
||||
bool ConfigureWorld(std::string& Error)
|
||||
{
|
||||
FStandaloneShapeWorldAccess::SetActive(nullptr);
|
||||
ShapeWorld.reset();
|
||||
World=std::make_unique<FStandaloneOwnerWorld>();
|
||||
Snapshot={};
|
||||
World=std::make_shared<FStandaloneOwnerWorld>();
|
||||
if(!StandaloneOwnerLoader::Load(DataPath,*World,Error))return false;
|
||||
if(!bParityChecked&&Seed==World->Seed&&!bRoundTunnels)PrintParity();
|
||||
const int32 ExportClipRadius=World->ClipRadius;
|
||||
@@ -718,15 +722,23 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
Min.X-=NearBuildPad;Min.Y-=NearBuildPad;Min.Z-=NearBuildPad;
|
||||
Max.X+=NearBuildPad;Max.Y+=NearBuildPad;Max.Z+=NearBuildPad;
|
||||
}
|
||||
ShapeWorld=std::make_unique<FStandaloneShapeWorld>();
|
||||
Snapshot=VoxelForgeCore::MakeStandaloneSnapshot(World);
|
||||
if(!Snapshot){Error="the standalone adapter could not make a resolved core snapshot";return false;}
|
||||
ShapeWorld=std::make_unique<VoxelForgeCore::World>(Snapshot);
|
||||
const int32 ShapeWorkers=bNeighborhoodOnly?(WorkerCount>=8?2:1):WorkerCount;
|
||||
if(!ShapeWorld->Build(*World,Min,Max,ClipMin,ClipMax,ShapeWorkers,
|
||||
ShapeDetailVariant,bIncludeFarWorld,Error))
|
||||
VoxelForgeCore::ShapeBuildRequest Request;
|
||||
Request.minVoxel={Min.X,Min.Y,Min.Z};Request.maxVoxel={Max.X,Max.Y,Max.Z};
|
||||
Request.clipMinVoxel={ClipMin.X,ClipMin.Y,ClipMin.Z};
|
||||
Request.clipMaxVoxel={ClipMax.X,ClipMax.Y,ClipMax.Z};
|
||||
Request.workerLimit=static_cast<uint32>(ShapeWorkers);Request.detail=ShapeDetailVariant;
|
||||
Request.includeFarWorld=bIncludeFarWorld;Request.roundTunnels=bRoundTunnels;
|
||||
Request.enhancedTunnelWalls=bRoundTunnelEnhanced;
|
||||
Request.profileDetailBand=bProfileDetailBand;
|
||||
if(!ShapeWorld->compile(Request,CoreScheduler,Error))
|
||||
{ShapeWorld.reset();return false;}
|
||||
FStandaloneShapeWorldAccess::SetActive(ShapeWorld.get());
|
||||
bShapePipelineActive=bNeighborhoodOnly;
|
||||
bNeighborhoodReadyReported=false;
|
||||
bFarShapeStagePending=bShapeWorld&&!bNeighborhoodOnly&&!ShapeWorld->HasFarWorld();
|
||||
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<unsigned long long>(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);
|
||||
@@ -748,15 +760,24 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
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<FStandaloneShapeWorld>();
|
||||
PendingFullShapeWorld=std::make_unique<VoxelForgeCore::World>(Snapshot);
|
||||
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);
|
||||
VoxelForgeCore::ShapeBuildRequest Request;
|
||||
Request.minVoxel={FineMin.X,FineMin.Y,FineMin.Z};
|
||||
Request.maxVoxel={FineMax.X,FineMax.Y,FineMax.Z};
|
||||
Request.clipMinVoxel={ClipMin.X,ClipMin.Y,ClipMin.Z};
|
||||
Request.clipMaxVoxel={ClipMax.X,ClipMax.Y,ClipMax.Z};
|
||||
Request.workerLimit=static_cast<uint32>(FullWorkers);Request.detail=ShapeDetailVariant;
|
||||
Request.includeFarWorld=true;Request.roundTunnels=bRoundTunnels;
|
||||
Request.enhancedTunnelWalls=bRoundTunnelEnhanced;
|
||||
Request.profileDetailBand=bProfileDetailBand;
|
||||
bFullShapeBuildSucceeded=PendingFullShapeWorld->compile(
|
||||
Request,CoreScheduler,FullShapeBuildError);
|
||||
bFullShapeBuildFinished.store(true,std::memory_order_release);
|
||||
});
|
||||
std::printf("[ShapePipelineBuildStart] neighborhood_tiles=%zu near_mesh_workers=%d full_shape_workers=%d\n",
|
||||
@@ -826,12 +847,20 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
const FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(*World,CameraVoxels);
|
||||
DesiredTiles=Desired.Tiles;
|
||||
if(ShapeWorld)
|
||||
{
|
||||
const auto CoreTiles=ShapeWorld->desiredTiles({CameraVoxels.X,CameraVoxels.Y,CameraVoxels.Z});
|
||||
DesiredTiles.clear();DesiredTiles.reserve(CoreTiles.size());
|
||||
for(const auto& Key:CoreTiles)
|
||||
DesiredTiles.emplace_back(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod);
|
||||
}
|
||||
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<FStandaloneStreamingGenerator>(*World,StreamWorkers,false,
|
||||
(!bShapeWorld&&MeshCache.IsLoaded())?&MeshCache:nullptr,
|
||||
bFarShapeStagePending?World->FullResClipLevels-1:-1);
|
||||
bFarShapeStagePending?World->FullResClipLevels-1:-1,
|
||||
false,false,false,false,ShapeWorld.get());
|
||||
if(bShapePipelineActive)Streamer->SetDesiredTileFilter(InitialNeighborhoodTiles);
|
||||
const FStandaloneStreamingUpdate Update=Streamer->Start(CameraVoxels,{},CrossingIndex);
|
||||
if(bShapePipelineActive&&Update.DesiredTiles!=static_cast<int32>(InitialNeighborhoodTiles.size()))
|
||||
@@ -872,14 +901,15 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
|
||||
void StartFarShapeGeneration()
|
||||
{
|
||||
if(!bFarShapeStagePending||!ShapeWorld||ShapeWorld->HasFarWorld()||!Streamer)return;
|
||||
ShapeWorld->BuildFarWorld();
|
||||
if(!bFarShapeStagePending||!ShapeWorld||ShapeWorld->hasFarWorld()||!Streamer)return;
|
||||
ShapeWorld->buildFarWorld();
|
||||
bFarShapeStagePending=false;
|
||||
Streamer->Stop();
|
||||
std::vector<FVoxelTileKey> ResidentKeys;ResidentKeys.reserve(ResidentTiles.size());
|
||||
for(const auto& Pair:ResidentTiles)ResidentKeys.push_back(Pair.first);
|
||||
Streamer.reset();
|
||||
Streamer=std::make_unique<FStandaloneStreamingGenerator>(*World,WorkerCount,false,nullptr,-1);
|
||||
Streamer=std::make_unique<FStandaloneStreamingGenerator>(*World,WorkerCount,false,nullptr,-1,
|
||||
false,false,false,false,ShapeWorld.get());
|
||||
const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0);
|
||||
++CrossingIndex;
|
||||
const FStandaloneStreamingUpdate Update=Streamer->Start(CameraVoxels,ResidentKeys,CrossingIndex);
|
||||
@@ -929,11 +959,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
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<FStandaloneStreamingGenerator>(*World,WorkerCount,false,nullptr,-1);
|
||||
Streamer=std::make_unique<FStandaloneStreamingGenerator>(*World,WorkerCount,false,nullptr,-1,
|
||||
false,false,false,false,ShapeWorld.get());
|
||||
++CrossingIndex;
|
||||
const FStandaloneStreamingUpdate Update=Streamer->Start(CameraVoxels,ResidentKeys,CrossingIndex);
|
||||
if(Update.DesiredTiles!=static_cast<int32>(DesiredTiles.size()))
|
||||
@@ -1370,13 +1400,24 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
|
||||
int RunTileBenchmark(FStandaloneOwnerWorld& World,V3 CameraPosition,int32 Level,
|
||||
int32 Count,bool bProfile,bool bCompareEvaluators,bool bDensityHash)
|
||||
int32 Count,bool bProfile,bool bCompareEvaluators,bool bDensityHash,
|
||||
VoxelForgeCore::World* CoreWorld)
|
||||
{
|
||||
const FVector CameraVoxels(CameraPosition.X*4.0,CameraPosition.Y*4.0,CameraPosition.Z*4.0);
|
||||
const FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraVoxels);
|
||||
std::vector<FVoxelTileKey> Tiles;Tiles.reserve(static_cast<size_t>(std::max(Count,1)));
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
if(Tile.Level==Level&&static_cast<int32>(Tiles.size())<Count)Tiles.push_back(Tile);
|
||||
if(CoreWorld)
|
||||
{
|
||||
const auto CoreTiles=CoreWorld->desiredTiles({CameraVoxels.X,CameraVoxels.Y,CameraVoxels.Z});
|
||||
for(const auto& Key:CoreTiles)
|
||||
if(Key.lod==Level&&static_cast<int32>(Tiles.size())<Count)
|
||||
Tiles.emplace_back(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
if(Tile.Level==Level&&static_cast<int32>(Tiles.size())<Count)Tiles.push_back(Tile);
|
||||
}
|
||||
if(Tiles.empty())
|
||||
{std::fprintf(stderr,"No LOD %d tiles are in the current desired set.\n",Level);return 4;}
|
||||
|
||||
@@ -1386,8 +1427,10 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
FParse::Value(FCommandLine::Get(),"voxel.TileCacheWindow=",TileCacheWindow);
|
||||
FParse::Value(FCommandLine::Get(),"voxel.TileCacheWindowMinLOD=",TileCacheWindowMinLOD);
|
||||
FParse::Value(FCommandLine::Get(),"voxel.RoomGraphCacheSlots=",RoomGraphCacheSlots);
|
||||
std::printf("[BenchmarkConfig] tile_cache_window=%d fused_min_lod=%d room_graph_cache_slots=%d command='%s'\n",
|
||||
TileCacheWindow,TileCacheWindowMinLOD,RoomGraphCacheSlots,FCommandLine::Get());
|
||||
std::printf("[BenchmarkConfig] tile_cache_window=%d fused_min_lod=%d room_graph_cache_slots=%d generator=%s command='%s'\n",
|
||||
TileCacheWindow,TileCacheWindowMinLOD,RoomGraphCacheSlots,
|
||||
CoreWorld&&!bCompareEvaluators?"VoxelForgeCore":"compatibility-diagnostic",
|
||||
FCommandLine::Get());
|
||||
VoxelDensityProfile::SetMode(bProfile?VoxelDensityProfile::EMode::Attribution
|
||||
:VoxelDensityProfile::EMode::Disabled,128);
|
||||
if(bProfile)VoxelDensityProfile::CalibrateTimer();
|
||||
@@ -1458,12 +1501,35 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const int32 Step=FMath::Max(1,Tile.ExtentVoxels()/Cells);
|
||||
int64 TileSamples=0;
|
||||
std::vector<float> DebugDensitySamples;
|
||||
FVoxelMeshData Mesh=Mesher.GenerateMesh(Tile.OriginVoxels(),Step,Cells,nullptr,
|
||||
BandMin,BandMax,&TileSamples,nullptr,nullptr,
|
||||
bDensityHash?&DebugDensitySamples:nullptr);
|
||||
uint64 TileVertices=0,TileIndices=0;
|
||||
if(CoreWorld&&!bCompareEvaluators)
|
||||
{
|
||||
VoxelForgeCore::TileGenerationOptions Options;
|
||||
Options.bandMinZVoxel=BandMin;Options.bandMaxZVoxel=BandMax;
|
||||
Options.captureDensitySamples=bDensityHash;
|
||||
std::string CoreError;
|
||||
auto Mesh=CoreWorld->generateTile(
|
||||
{{Tile.Coord.X,Tile.Coord.Y,Tile.Coord.Z},Tile.Level},Options,CoreError);
|
||||
if(!Mesh)
|
||||
{
|
||||
std::fprintf(stderr,"[VoxelForgeCore] benchmark tile generation failed: %s\n",CoreError.c_str());
|
||||
return 8;
|
||||
}
|
||||
TileSamples=static_cast<int64>(Mesh->densitySampleCount);
|
||||
TileVertices=Mesh->vertices.size();TileIndices=Mesh->indices.size();
|
||||
if(bDensityHash)DebugDensitySamples=std::move(Mesh->debugDensitySamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
FVoxelMeshData Mesh=Mesher.GenerateMesh(Tile.OriginVoxels(),Step,Cells,nullptr,
|
||||
BandMin,BandMax,&TileSamples,nullptr,nullptr,
|
||||
bDensityHash?&DebugDensitySamples:nullptr);
|
||||
TileVertices=static_cast<uint64>(Mesh.Vertices.Num());
|
||||
TileIndices=static_cast<uint64>(Mesh.Triangles.Num());
|
||||
}
|
||||
Samples+=static_cast<uint64>(FMath::Max<int64>(0,TileSamples));
|
||||
Vertices+=static_cast<uint64>(Mesh.Vertices.Num());
|
||||
Indices+=static_cast<uint64>(Mesh.Triangles.Num());
|
||||
Vertices+=TileVertices;
|
||||
Indices+=TileIndices;
|
||||
if(bDensityHash)
|
||||
{
|
||||
for(const float Density:DebugDensitySamples)
|
||||
@@ -1607,17 +1673,15 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
return Result;
|
||||
}
|
||||
|
||||
int RunOwnerShapeComparison(FStandaloneOwnerWorld& World,const FStandaloneShapeWorld& Shape,
|
||||
int RunOwnerShapeComparison(FStandaloneOwnerWorld& World,VoxelForgeCore::World& 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<FStandaloneShapeWorld&>(Shape).BuildFarWorld();
|
||||
if(!Shape.isCompiled())
|
||||
{std::fprintf(stderr,"Shape comparison requires a compiled core world.\n");return 11;}
|
||||
if(LevelFilter>=World.FullResClipLevels&&!Shape.hasFarWorld())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);
|
||||
Shape.auditViewSurface({CameraVoxels.X,CameraVoxels.Y,CameraVoxels.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)
|
||||
@@ -1626,17 +1690,18 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const float Y=static_cast<float>(CameraVoxels.Y+Offset[1]);
|
||||
const float Z=static_cast<float>(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);
|
||||
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);
|
||||
Shape.debugCriticalAirAt({X,Y,Z})?1:0,Shape.debugSolidOverlayAt({X,Y,Z})?1:0);
|
||||
}
|
||||
}
|
||||
const FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraVoxels);
|
||||
std::vector<FVoxelTileKey> CoreDesired;
|
||||
for(const auto& Key:Shape.desiredTiles({CameraVoxels.X,CameraVoxels.Y,CameraVoxels.Z}))
|
||||
CoreDesired.emplace_back(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod);
|
||||
std::vector<FVoxelTileKey> Tiles;
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
for(const FVoxelTileKey& Tile:CoreDesired)
|
||||
if(LevelFilter<0?Tile.Level<World.FullResClipLevels:Tile.Level==LevelFilter)
|
||||
Tiles.push_back(Tile);
|
||||
if(TileLimit>0&&Tiles.size()>static_cast<size_t>(TileLimit))
|
||||
@@ -1670,7 +1735,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
|
||||
std::atomic<size_t> Next{0};
|
||||
std::vector<std::thread> Threads;
|
||||
const int32 ThreadCount=std::clamp(Workers,1,16);
|
||||
const int32 ThreadCount=std::clamp(Workers,1,8);
|
||||
Threads.reserve(ThreadCount);
|
||||
for(int32 Worker=0;Worker<ThreadCount;++Worker)
|
||||
{
|
||||
@@ -1688,15 +1753,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const FVoxelTileKey& Tile=Tiles[Index];
|
||||
const int32 Cells=Tile.Level<World.FullResClipLevels?CHUNK_SIZE:World.CoarseTileCells;
|
||||
const int32 Step=std::max(1,Tile.ExtentVoxels()/Cells);
|
||||
std::vector<float> NewDensities,OldDensities;
|
||||
FVoxelMeshData NewMesh=Mesher.GenerateMesh(Tile.OriginVoxels(),Step,Cells,nullptr,
|
||||
MinChunkZ,MaxChunkZ,nullptr,nullptr,nullptr,&NewDensities);
|
||||
VoxelForgeCore::TileGenerationOptions ShapeOptions;
|
||||
ShapeOptions.bandMinZVoxel=MinChunkZ;ShapeOptions.bandMaxZVoxel=MaxChunkZ;
|
||||
ShapeOptions.captureDensitySamples=true;
|
||||
std::string CoreError;
|
||||
auto NewMesh=Shape.generateTile({{Tile.Coord.X,Tile.Coord.Y,Tile.Coord.Z},Tile.Level},
|
||||
ShapeOptions,CoreError);
|
||||
std::vector<float> NewDensities=NewMesh?std::move(NewMesh->debugDensitySamples):std::vector<float>{};
|
||||
std::vector<float> OldDensities;
|
||||
FVoxelMeshData OldMesh;
|
||||
{
|
||||
FStandaloneShapeWorldAccess::FScopedBypass Bypass;
|
||||
OldMesh=Mesher.GenerateMesh(Tile.OriginVoxels(),Step,Cells,nullptr,
|
||||
MinChunkZ,MaxChunkZ,nullptr,nullptr,nullptr,&OldDensities);
|
||||
}
|
||||
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;
|
||||
@@ -1708,9 +1775,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
R.OldHash^=std::bit_cast<uint32>(OldDensities[I]);R.OldHash*=1099511628211ull;
|
||||
}
|
||||
constexpr size_t VertexStride=4;
|
||||
R.NewSurface.reserve(static_cast<size_t>(NewMesh.Vertices.Num())/VertexStride+1);
|
||||
for(int32 I=0;I<NewMesh.Vertices.Num();I+=static_cast<int32>(VertexStride))
|
||||
{const FVector& P=NewMesh.Vertices[I];R.NewSurface.push_back({P.X*0.01,P.Y*0.01,P.Z*0.01});}
|
||||
if(NewMesh)
|
||||
{
|
||||
R.NewSurface.reserve(NewMesh->vertices.size()/VertexStride+1);
|
||||
for(size_t I=0;I<NewMesh->vertices.size();I+=VertexStride)
|
||||
{const auto& P=NewMesh->vertices[I].position;R.NewSurface.push_back({P.x*0.01,P.y*0.01,P.z*0.01});}
|
||||
}
|
||||
R.OldSurface.reserve(static_cast<size_t>(OldMesh.Vertices.Num())/VertexStride+1);
|
||||
for(int32 I=0;I<OldMesh.Vertices.Num();I+=static_cast<int32>(VertexStride))
|
||||
{const FVector& P=OldMesh.Vertices[I];R.OldSurface.push_back({P.X*0.01,P.Y*0.01,P.Z*0.01});}
|
||||
@@ -1779,9 +1849,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
uint64 TotalSamples=0,ChangedSigns=0;
|
||||
uint64 LegacyHash=14695981039346656037ull,DetailedHash=14695981039346656037ull;
|
||||
const bool PreviousRoundTunnels=World.Generator->bStandaloneResearchRoundTunnels;
|
||||
const bool PreviousEnhancedWalls=World.Generator->bStandaloneResearchEnhancedWalls;
|
||||
const auto Evaluate=[&](bool bEnhanced,std::vector<FComparisonTile>& Out)
|
||||
{
|
||||
FStandaloneResearchTunnels::Set(true,bEnhanced);
|
||||
World.Generator->bStandaloneResearchRoundTunnels=true;
|
||||
World.Generator->bStandaloneResearchEnhancedWalls=bEnhanced;
|
||||
for(size_t I=0;I<Tiles.size();++I)
|
||||
{
|
||||
const FVoxelTileKey& Tile=Tiles[I];
|
||||
@@ -1795,6 +1868,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
};
|
||||
Evaluate(false,Legacy);
|
||||
Evaluate(true,Detailed);
|
||||
World.Generator->bStandaloneResearchRoundTunnels=PreviousRoundTunnels;
|
||||
World.Generator->bStandaloneResearchEnhancedWalls=PreviousEnhancedWalls;
|
||||
FDirectedSurfaceDistance LegacyToDetailed,DetailedToLegacy;
|
||||
std::vector<FComparePoint> AllLegacySurface,AllDetailedSurface;
|
||||
for(size_t TileIndex=0;TileIndex<Tiles.size();++TileIndex)
|
||||
@@ -1823,7 +1898,6 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
static_cast<unsigned long long>(LegacyHash),static_cast<unsigned long long>(DetailedHash),
|
||||
AllLegacySurface.size(),AllDetailedSurface.size(),MeanDistance,Hausdorff,
|
||||
static_cast<unsigned long long>(LegacyToDetailed.Unmatched+DetailedToLegacy.Unmatched));
|
||||
FStandaloneResearchTunnels::Set(true,true);
|
||||
return TotalSamples>0&&LegacyToDetailed.Unmatched+DetailedToLegacy.Unmatched==0?0:10;
|
||||
}
|
||||
|
||||
@@ -1866,12 +1940,14 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
return Hash;
|
||||
}
|
||||
|
||||
int RunPipelinedStreamingBenchmark(FStandaloneOwnerWorld& World,const FVector& CameraPositionVoxels,
|
||||
int32 WorkerCount,int32 MaxLevel,FStandaloneShapeWorld::EDetailVariant DetailVariant,
|
||||
int RunPipelinedStreamingBenchmark(std::shared_ptr<FStandaloneOwnerWorld> OwnerWorld,
|
||||
const FVector& CameraPositionVoxels,int32 WorkerCount,int32 MaxLevel,
|
||||
VoxelForgeCore::DetailVariant DetailVariant,const VoxelForgeCore::JobScheduler& Scheduler,
|
||||
bool bHashDensity,bool bSkipUvAndColors,bool bSkipMaterialGroups,bool bSkipUV,bool bSkipColors,
|
||||
std::chrono::steady_clock::time_point ColdStart)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
FStandaloneOwnerWorld& World=*OwnerWorld;
|
||||
FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraPositionVoxels);
|
||||
if(MaxLevel>=0)
|
||||
Desired.Tiles.erase(std::remove_if(Desired.Tiles.begin(),Desired.Tiles.end(),
|
||||
@@ -1902,24 +1978,30 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
if(Neighborhood.size()!=27)
|
||||
{std::fprintf(stderr,"[StreamPipeline] expected 27 player-neighborhood tiles, found %zu.\n",Neighborhood.size());return 7;}
|
||||
|
||||
FStandaloneShapeWorld Shape;
|
||||
VoxelForgeCore::World Shape(VoxelForgeCore::MakeStandaloneSnapshot(OwnerWorld));
|
||||
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))
|
||||
VoxelForgeCore::ShapeBuildRequest ShapeRequest;
|
||||
ShapeRequest.minVoxel={FineMin.X,FineMin.Y,FineMin.Z};
|
||||
ShapeRequest.maxVoxel={FineMax.X,FineMax.Y,FineMax.Z};
|
||||
ShapeRequest.clipMinVoxel={ClipMin.X,ClipMin.Y,ClipMin.Z};
|
||||
ShapeRequest.clipMaxVoxel={ClipMax.X,ClipMax.Y,ClipMax.Z};
|
||||
ShapeRequest.workerLimit=static_cast<uint32>(std::clamp(WorkerCount,1,8));
|
||||
ShapeRequest.detail=DetailVariant;ShapeRequest.includeFarWorld=bIncludeFarWorld;
|
||||
ShapeRequest.roundTunnels=World.Generator->bStandaloneResearchRoundTunnels;
|
||||
ShapeRequest.enhancedTunnelWalls=World.Generator->bStandaloneResearchEnhancedWalls;
|
||||
if(!Shape.compile(ShapeRequest,Scheduler,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<double>(ShapeReady-ShapeBuildStart).count();
|
||||
FStandaloneShapeWorldAccess::SetActive(&Shape);
|
||||
|
||||
FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel,
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors);
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape);
|
||||
const auto StreamStart=Clock::now();
|
||||
const FStandaloneStreamingUpdate Update=Streamer.Start(CameraPositionVoxels);
|
||||
if(Update.DesiredTiles!=static_cast<int32>(Desired.Tiles.size()))
|
||||
{
|
||||
Streamer.Stop();FStandaloneShapeWorldAccess::SetActive(nullptr);
|
||||
Streamer.Stop();
|
||||
std::fprintf(stderr,"[StreamPipeline] expected %zu tiles but queued %d.\n",
|
||||
Desired.Tiles.size(),Update.DesiredTiles);return 7;
|
||||
}
|
||||
@@ -2005,11 +2087,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
static_cast<unsigned long long>(HashTileRows(PayloadRows)),
|
||||
static_cast<unsigned long long>(HashTileRows(TopologyRows)));
|
||||
}
|
||||
FStandaloneShapeWorldAccess::SetActive(nullptr);
|
||||
return NeighborhoodFinished==static_cast<int32>(Neighborhood.size())
|
||||
&&FullDetailFinished==FullDetailExpected&&Finished==Update.DesiredTiles&&bHashesPassing?0:6;
|
||||
}
|
||||
int RunStreamingBenchmark(FStandaloneOwnerWorld& World,const FVector& CameraPositionVoxels,
|
||||
int RunStreamingBenchmark(FStandaloneOwnerWorld& World,VoxelForgeCore::World* CoreWorld,
|
||||
const FVector& CameraPositionVoxels,
|
||||
int32 WorkerCount,int32 MaxLevel,bool bRoundTunnels,
|
||||
bool bEnhancedWalls,const FStandaloneMeshCache* Cache,
|
||||
bool bHashDensity,bool bSkipUvAndColors,bool bSkipMaterialGroups,
|
||||
@@ -2020,6 +2102,14 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const FStandaloneMeshCacheMetadata& CacheMetadata)
|
||||
{
|
||||
FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraPositionVoxels);
|
||||
if(CoreWorld)
|
||||
{
|
||||
const auto CoreTiles=CoreWorld->desiredTiles(
|
||||
{CameraPositionVoxels.X,CameraPositionVoxels.Y,CameraPositionVoxels.Z});
|
||||
Desired.Tiles.clear();Desired.Tiles.reserve(CoreTiles.size());
|
||||
for(const auto& Key:CoreTiles)
|
||||
Desired.Tiles.emplace_back(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod);
|
||||
}
|
||||
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());
|
||||
@@ -2035,10 +2125,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::unique_ptr<FFarSurvivalAudit> 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<FFarSurvivalAudit>(World,*Shape,Desired.Tiles,bFarAuditBadShell);
|
||||
if(!CoreWorld||!CoreWorld->hasFarWorld())
|
||||
{std::fprintf(stderr,"Far survival audit requires a compiled core world with far-parent data.\n");return 15;}
|
||||
FarAudit=std::make_unique<FFarSurvivalAudit>(World,*CoreWorld,Desired.Tiles,bFarAuditBadShell);
|
||||
}
|
||||
std::array<std::vector<FHashedTile>,MaxLevels+1> HashRows;
|
||||
std::array<std::vector<FHashedTile>,MaxLevels+1> MeshHashRows;
|
||||
@@ -2067,7 +2156,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const auto Start=std::chrono::steady_clock::now();
|
||||
FStandaloneStreamingGenerator Streamer(World,WorkerCount,
|
||||
bHashDensity||!BakeCachePath.empty(),Cache,MaxLevel,
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors);
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,CoreWorld);
|
||||
const FStandaloneStreamingUpdate Update=Streamer.Start(CameraPositionVoxels);
|
||||
const double LOD0Width=static_cast<double>(2*World.ClipRadius+2)
|
||||
*CHUNK_SIZE*VOXEL_SIZE/100.0;
|
||||
@@ -2279,47 +2368,58 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RunEditTransactionBenchmark(FStandaloneOwnerWorld& World,FStandaloneShapeWorld& Shape,
|
||||
const FVector& CameraPositionVoxels,int32 WorkerCount)
|
||||
int RunEditTransactionBenchmark(FStandaloneOwnerWorld& World,VoxelForgeCore::World& 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<FStandaloneShapeWorld::FEditBrush> 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();
|
||||
const auto CoreDesired=Shape.desiredTiles(
|
||||
{CameraPositionVoxels.X,CameraPositionVoxels.Y,CameraPositionVoxels.Z});
|
||||
Desired.Tiles.clear();Desired.Tiles.reserve(CoreDesired.size());
|
||||
std::vector<VoxelForgeCore::TileKey> CoreKeys;CoreKeys.reserve(CoreDesired.size());
|
||||
for(const auto& Key:CoreDesired)
|
||||
{
|
||||
Desired.Tiles.emplace_back(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod);
|
||||
CoreKeys.push_back(Key);
|
||||
}
|
||||
VoxelForgeCore::EditTransaction Transaction;Transaction.sequence=1;Transaction.brushes.resize(3);
|
||||
Transaction.brushes[0].shape=VoxelForgeCore::EditBrush::Shape::sphere;
|
||||
Transaction.brushes[0].operation=VoxelForgeCore::EditBrush::Operation::carve;
|
||||
Transaction.brushes[0].a={X,Y,Z};Transaction.brushes[0].radius=20.0;
|
||||
Transaction.brushes[1].shape=VoxelForgeCore::EditBrush::Shape::roundedBox;
|
||||
Transaction.brushes[1].operation=VoxelForgeCore::EditBrush::Operation::carve;
|
||||
Transaction.brushes[1].a={X,Y,Z+12.0};Transaction.brushes[1].halfExtent={11.0,11.0,4.0};
|
||||
Transaction.brushes[1].rounding=3.0;
|
||||
Transaction.brushes[2].shape=VoxelForgeCore::EditBrush::Shape::capsule;
|
||||
Transaction.brushes[2].operation=VoxelForgeCore::EditBrush::Operation::addRock;
|
||||
Transaction.brushes[2].a={X-22.0,Y,Z-8.0};Transaction.brushes[2].b={X+22.0,Y,Z-8.0};
|
||||
Transaction.brushes[2].radius=3.0;
|
||||
const auto EditStart=Clock::now();
|
||||
std::string EditError;
|
||||
const auto Edit=Shape.applyEditTransaction(Transaction,CoreKeys,EditError);
|
||||
if(!Edit){std::fprintf(stderr,"[EditTransaction] rejected: %s\n",EditError.c_str());return 9;}
|
||||
std::vector<FVoxelTileKey> Affected;Affected.reserve(128);
|
||||
std::array<uint64,MaxLevels+1> AffectedByLevel{},FinishedByLevel{};
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
for(const auto& LodTiles:Edit->affectedByLod)
|
||||
{
|
||||
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+Pad<EditMin.X
|
||||
||Origin.Y-Pad>EditMax.Y||Origin.Y+Extent+Pad<EditMin.Y
|
||||
||Origin.Z-Pad>EditMax.Z||Origin.Z+Extent+Pad<EditMin.Z)continue;
|
||||
Affected.push_back(Tile);
|
||||
if(Tile.Level>=0&&Tile.Level<=MaxLevels)++AffectedByLevel[Tile.Level];
|
||||
for(const auto& Key:LodTiles)
|
||||
{
|
||||
const FVoxelTileKey Tile(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod);
|
||||
Affected.push_back(Tile);
|
||||
if(Tile.Level>=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);
|
||||
FStandaloneStreamingGenerator Streamer(World,WorkerCount,false,nullptr,-1,
|
||||
false,false,false,false,&Shape);
|
||||
Streamer.SetDesiredTileFilter(Affected);
|
||||
const auto Start=Clock::now();
|
||||
const FStandaloneStreamingUpdate Update=Streamer.Start(CameraPositionVoxels);
|
||||
@@ -2400,8 +2500,8 @@ int main(int Argc,char** Argv)
|
||||
else if(Arg.starts_with("--shape-detail="))
|
||||
{
|
||||
const std::string_view Value=Arg.substr(15);
|
||||
if(Value=="smooth")App.ShapeDetailVariant=FStandaloneShapeWorld::EDetailVariant::Smooth;
|
||||
else if(Value=="per-voxel")App.ShapeDetailVariant=FStandaloneShapeWorld::EDetailVariant::PerVoxel;
|
||||
if(Value=="smooth")App.ShapeDetailVariant=VoxelForgeCore::DetailVariant::smooth;
|
||||
else if(Value=="per-voxel")App.ShapeDetailVariant=VoxelForgeCore::DetailVariant::perVoxel;
|
||||
else if(Value!="coherent"){std::fprintf(stderr,"Invalid --shape-detail value: %.*s\n",static_cast<int>(Value.size()),Value.data());return 2;}
|
||||
}
|
||||
else if(Arg=="--round-tunnels")App.bRoundTunnels=true;
|
||||
@@ -2439,7 +2539,7 @@ int main(int Argc,char** Argv)
|
||||
else if(Arg.starts_with("--workers="))
|
||||
{
|
||||
const int32 Parsed=std::atoi(std::string(Arg.substr(10)).c_str());
|
||||
App.WorkerCount=std::clamp(Parsed,1,16);
|
||||
App.WorkerCount=std::clamp(Parsed,1,8);
|
||||
}
|
||||
else if(Arg.starts_with("--clip-radius="))
|
||||
App.ClipRadius=std::clamp(std::atoi(std::string(Arg.substr(14)).c_str()),1,64);
|
||||
@@ -2461,9 +2561,9 @@ int main(int Argc,char** Argv)
|
||||
}
|
||||
else if(!Arg.empty()&&Arg.front()!='-')App.DataPath=std::string(Arg);
|
||||
}
|
||||
FStandaloneShapeWorld::SetDetailProfileEnabled(bProfileDetailBand);
|
||||
App.bProfileDetailBand=bProfileDetailBand;
|
||||
std::string Error;
|
||||
App.World=std::make_unique<FStandaloneOwnerWorld>();
|
||||
App.World=std::make_shared<FStandaloneOwnerWorld>();
|
||||
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)
|
||||
@@ -2500,10 +2600,11 @@ int main(int Argc,char** Argv)
|
||||
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;
|
||||
return App.ShapeWorld->auditSurfaceFamilies(true)?0:8;
|
||||
const bool bSurfacePassing=App.ShapeWorld->auditViewSurface(
|
||||
{App.Position.X*4.0,App.Position.Y*4.0,App.Position.Z*4.0},
|
||||
App.Yaw,App.Pitch);
|
||||
return App.ShapeWorld->auditDetailPassing()&&bSurfacePassing?0:7;
|
||||
}
|
||||
if(App.bShapeCompare)
|
||||
return RunOwnerShapeComparison(*App.World,*App.ShapeWorld,App.Position,App.Yaw,App.Pitch,App.WorkerCount,
|
||||
@@ -2513,9 +2614,9 @@ int main(int Argc,char** Argv)
|
||||
if(bStreamBenchmark)
|
||||
{
|
||||
if(bStreamPipeline)
|
||||
return RunPipelinedStreamingBenchmark(*App.World,
|
||||
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,
|
||||
App.WorkerCount,StreamBenchmarkMaxLevel,App.ShapeDetailVariant,App.CoreScheduler,
|
||||
bStreamHash||!BakeMeshCachePath.empty(),bSkipUvAndColors,bSkipMaterialGroups,
|
||||
bSkipUV,bSkipColors,ColdProcessStart);
|
||||
if(bEditBenchmark)
|
||||
@@ -2526,7 +2627,7 @@ int main(int Argc,char** Argv)
|
||||
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,
|
||||
return RunStreamingBenchmark(*App.World,App.ShapeWorld.get(),
|
||||
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,
|
||||
@@ -2536,7 +2637,7 @@ int main(int Argc,char** Argv)
|
||||
}
|
||||
if(BenchmarkLevel>=0)
|
||||
return RunTileBenchmark(*App.World,App.Position,BenchmarkLevel,BenchmarkCount,
|
||||
App.bProfileAttribution,App.bCompareEvaluators,App.bDensityHash);
|
||||
App.bProfileAttribution,App.bCompareEvaluators,App.bDensityHash,App.ShapeWorld.get());
|
||||
App.World.reset();
|
||||
|
||||
WNDCLASSA Class{};Class.lpfnWndProc=WindowProc;Class.hInstance=GetModuleHandleA(nullptr);Class.lpszClassName="VoxelForgeStandaloneWorld";
|
||||
|
||||
Reference in new issue
Block a user