wip(assembler): round S10 -- LOD8 horizon sampled at MC spacing: LOD8 worker time 18.8 s -> 4.3 s
Planar far horizon samples the LOD8 interior at the MC cell spacing, keeping 128-voxel boundary strips; far recipe v3. LOD8 p95 59.8 -> 13.9 ms, spawn worker-seconds ~41 -> 26.9, full set 7.8 -> 7.1 s, ring 0.49 s, LOD0 1.43 s, 40 m/s ring 75/75. S3/S4 2 cm agreement, 100% cap coverage, W1/W8/W12 and W1/W8/W16 determinism pass. Report: Standalone/Research/WorldAssemblerM3/Round14. 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
e7586a1256
commit
b90d63d0f7
14 files changed
+616
-86
No files matched your search
@@ -11,6 +11,8 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
namespace vfcore::detail
|
||||
{
|
||||
@@ -247,6 +249,45 @@ namespace VoxelForgeCore
|
||||
bool Refined = false;
|
||||
};
|
||||
|
||||
std::vector<int32> BuildNativeHorizonGridCoordinates(int32 TileExtent,int32 InteriorStep)
|
||||
{
|
||||
constexpr int32 BoundaryStep=128;
|
||||
std::vector<int32> Coordinates;
|
||||
if(TileExtent<=0)return Coordinates;
|
||||
if(InteriorStep<=BoundaryStep)
|
||||
{
|
||||
Coordinates.reserve(static_cast<std::size_t>(TileExtent/InteriorStep)+1u);
|
||||
for(int32 Position=0;Position<TileExtent;Position+=InteriorStep)
|
||||
Coordinates.push_back(Position);
|
||||
if(Coordinates.empty()||Coordinates.back()!=TileExtent)Coordinates.push_back(TileExtent);
|
||||
return Coordinates;
|
||||
}
|
||||
if(TileExtent<2*InteriorStep)
|
||||
{
|
||||
Coordinates.reserve(static_cast<std::size_t>(TileExtent/BoundaryStep)+1u);
|
||||
for(int32 Position=0;Position<TileExtent;Position+=BoundaryStep)
|
||||
Coordinates.push_back(Position);
|
||||
if(Coordinates.empty()||Coordinates.back()!=TileExtent)Coordinates.push_back(TileExtent);
|
||||
return Coordinates;
|
||||
}
|
||||
|
||||
// Keep the shared 128-voxel boundary lattice for LOD transitions while the LOD8
|
||||
// interior follows the coarse MC cell spacing. Preserve the exact same coordinates
|
||||
// in neighboring tiles, independent of which worker builds either side.
|
||||
// Garder la bordure commune de 128 voxels, avec un interieur a la maille MC de LOD8.
|
||||
for(int32 Position=0;Position<InteriorStep&&Position<TileExtent;Position+=BoundaryStep)
|
||||
Coordinates.push_back(Position);
|
||||
if(Coordinates.empty()||Coordinates.back()!=InteriorStep)
|
||||
Coordinates.push_back(InteriorStep);
|
||||
for(int32 Position=InteriorStep+InteriorStep;Position<TileExtent-InteriorStep;
|
||||
Position+=InteriorStep)
|
||||
Coordinates.push_back(Position);
|
||||
for(int32 Position=TileExtent-InteriorStep;Position<TileExtent;Position+=BoundaryStep)
|
||||
if(Coordinates.empty()||Coordinates.back()!=Position)Coordinates.push_back(Position);
|
||||
if(Coordinates.empty()||Coordinates.back()!=TileExtent)Coordinates.push_back(TileExtent);
|
||||
return Coordinates;
|
||||
}
|
||||
|
||||
FVector NormalizeCapNormal(const FVector& Value)
|
||||
{
|
||||
const double Length=std::sqrt(Value.X*Value.X+Value.Y*Value.Y+Value.Z*Value.Z);
|
||||
@@ -299,17 +340,28 @@ namespace VoxelForgeCore
|
||||
}
|
||||
|
||||
FNativeCapRefinementStats RefineFarNativeGround(const FStandaloneOwnerWorld& Owner,
|
||||
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,FVoxelMeshData& Mesh)
|
||||
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,int32 MeshStepVoxels,
|
||||
int32 InteriorStepOverride,FVoxelMeshData& Mesh)
|
||||
{
|
||||
FNativeCapRefinementStats Stats;
|
||||
if(Key.Level<5||!Owner.Generator)return Stats;
|
||||
const int32 GridStepVoxels=std::min(128,1<<Key.Level);
|
||||
constexpr int32 BoundaryStepVoxels=128;
|
||||
constexpr int64 Scale=WorldAssemblySubvoxelsPerVoxel;
|
||||
const int64 GridStepSubvoxels=static_cast<int64>(GridStepVoxels)*Scale;
|
||||
const FIntVector TileOrigin=Key.OriginVoxels();
|
||||
const int32 TileExtent=Key.ExtentVoxels();
|
||||
const int32 RequestedStep=Key.Level==8
|
||||
?(InteriorStepOverride>0?InteriorStepOverride:MeshStepVoxels)
|
||||
:std::min(BoundaryStepVoxels,1<<Key.Level);
|
||||
const int32 GridStepVoxels=Key.Level==8
|
||||
?((RequestedStep<BoundaryStepVoxels||RequestedStep%BoundaryStepVoxels!=0
|
||||
||TileExtent<=0||TileExtent%RequestedStep!=0)
|
||||
?BoundaryStepVoxels:std::max(BoundaryStepVoxels,RequestedStep))
|
||||
:RequestedStep;
|
||||
if(TileExtent<=0||TileExtent%GridStepVoxels!=0)return Stats;
|
||||
const int32 Cells=TileExtent/GridStepVoxels;
|
||||
const std::vector<int32> GridCoordinates=
|
||||
BuildNativeHorizonGridCoordinates(TileExtent,GridStepVoxels);
|
||||
const int32 GridSize=static_cast<int32>(GridCoordinates.size());
|
||||
if(GridSize<2)return Stats;
|
||||
const double TileMinZ=static_cast<double>(TileOrigin.Z)*Scale;
|
||||
const double TileMaxZ=static_cast<double>(TileOrigin.Z+TileExtent)*Scale;
|
||||
const int32 OldSurfaceVertices=std::clamp(Mesh.NumSurfaceVertices,0,Mesh.Vertices.Num());
|
||||
@@ -324,8 +376,9 @@ namespace VoxelForgeCore
|
||||
|
||||
struct FGroundProfile
|
||||
{
|
||||
int32 Cells=0,GridStep=0;
|
||||
int32 GridStep=0;
|
||||
double MinimumZ=0.0,MaximumZ=0.0;
|
||||
std::vector<int32> Coordinates;
|
||||
std::vector<double> Ground,Cap;
|
||||
};
|
||||
std::vector<FGroundProfile> GroundProfiles;
|
||||
@@ -344,14 +397,14 @@ namespace VoxelForgeCore
|
||||
FColor Color=FColor::White;
|
||||
int32 VertexIndex=INDEX_NONE;
|
||||
};
|
||||
std::vector<FGridSample> Grid(static_cast<std::size_t>(Cells+1)*(Cells+1));
|
||||
const auto GridIndex=[Cells](int32 X,int32 Y)
|
||||
{return static_cast<std::size_t>(Y)*(Cells+1)+X;};
|
||||
for(int32 Y=0;Y<=Cells;++Y)
|
||||
for(int32 X=0;X<=Cells;++X)
|
||||
std::vector<FGridSample> Grid(static_cast<std::size_t>(GridSize)*GridSize);
|
||||
const auto GridIndex=[GridSize](int32 X,int32 Y)
|
||||
{return static_cast<std::size_t>(Y)*GridSize+X;};
|
||||
for(int32 Y=0;Y<GridSize;++Y)
|
||||
for(int32 X=0;X<GridSize;++X)
|
||||
{
|
||||
const int64 XSub=(static_cast<int64>(TileOrigin.X)+static_cast<int64>(X)*GridStepVoxels)*Scale;
|
||||
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+static_cast<int64>(Y)*GridStepVoxels)*Scale;
|
||||
const int64 XSub=(static_cast<int64>(TileOrigin.X)+GridCoordinates[X])*Scale;
|
||||
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+GridCoordinates[Y])*Scale;
|
||||
const auto Column=Block.point(XSub,YSub,0);
|
||||
auto& Sample=Grid[GridIndex(X,Y)];
|
||||
Sample.Ground=static_cast<double>(Column.groundHeightSubvoxels);
|
||||
@@ -364,13 +417,13 @@ namespace VoxelForgeCore
|
||||
static_cast<uint8>(std::clamp(std::lround(Blend*255.0f),0l,255l)),
|
||||
static_cast<uint8>(std::clamp(Neighbor,0,255)));
|
||||
}
|
||||
for(int32 Y=0;Y<=Cells;++Y)
|
||||
for(int32 X=0;X<=Cells;++X)
|
||||
for(int32 Y=0;Y<GridSize;++Y)
|
||||
for(int32 X=0;X<GridSize;++X)
|
||||
{
|
||||
const int32 Left=std::max(0,X-1),Right=std::min(Cells,X+1);
|
||||
const int32 Down=std::max(0,Y-1),Up=std::min(Cells,Y+1);
|
||||
const double DX=static_cast<double>(Right-Left)*GridStepSubvoxels;
|
||||
const double DY=static_cast<double>(Up-Down)*GridStepSubvoxels;
|
||||
const int32 Left=std::max(0,X-1),Right=std::min(GridSize-1,X+1);
|
||||
const int32 Down=std::max(0,Y-1),Up=std::min(GridSize-1,Y+1);
|
||||
const double DX=static_cast<double>(GridCoordinates[Right]-GridCoordinates[Left])*Scale;
|
||||
const double DY=static_cast<double>(GridCoordinates[Up]-GridCoordinates[Down])*Scale;
|
||||
const double SlopeX=DX>0.0?(Grid[GridIndex(Right,Y)].Ground
|
||||
-Grid[GridIndex(Left,Y)].Ground)/DX:0.0;
|
||||
const double SlopeY=DY>0.0?(Grid[GridIndex(X,Up)].Ground
|
||||
@@ -382,7 +435,7 @@ namespace VoxelForgeCore
|
||||
}
|
||||
|
||||
FGroundProfile Profile;
|
||||
Profile.Cells=Cells;Profile.GridStep=GridStepVoxels;
|
||||
Profile.GridStep=GridStepVoxels;Profile.Coordinates=GridCoordinates;
|
||||
Profile.MinimumZ=static_cast<double>(std::min(Plan.bottomChunkZ,Plan.topChunkZ))*CHUNK_SIZE;
|
||||
Profile.MaximumZ=static_cast<double>(std::max(Plan.bottomChunkZ,Plan.topChunkZ)+1)*CHUNK_SIZE;
|
||||
Profile.Ground.reserve(Grid.size());Profile.Cap.reserve(Grid.size());
|
||||
@@ -392,25 +445,25 @@ namespace VoxelForgeCore
|
||||
|
||||
// Partager les sommets du profil entre cellules reduit le payload d'horizon.
|
||||
// Share regular profile vertices across adjacent cells to bound horizon payload.
|
||||
for(int32 Y=0;Y<=Cells;++Y)
|
||||
for(int32 X=0;X<=Cells;++X)
|
||||
for(int32 Y=0;Y<GridSize;++Y)
|
||||
for(int32 X=0;X<GridSize;++X)
|
||||
{
|
||||
auto& Sample=Grid[GridIndex(X,Y)];
|
||||
if(Sample.Ground>=Sample.Cap||Sample.Ground<TileMinZ||Sample.Ground>TileMaxZ)continue;
|
||||
const int64 XSub=(static_cast<int64>(TileOrigin.X)+static_cast<int64>(X)*GridStepVoxels)*Scale;
|
||||
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+static_cast<int64>(Y)*GridStepVoxels)*Scale;
|
||||
const int64 XSub=(static_cast<int64>(TileOrigin.X)+GridCoordinates[X])*Scale;
|
||||
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+GridCoordinates[Y])*Scale;
|
||||
const double VoxelSizeCm=static_cast<double>(Owner.CoreVoxelSizeMicrons)/10000.0;
|
||||
Sample.VertexIndex=static_cast<int32>(AddedVertices.size());
|
||||
AddedVertices.emplace_back(static_cast<double>(TileOrigin.X+X*GridStepVoxels)*VoxelSizeCm,
|
||||
static_cast<double>(TileOrigin.Y+Y*GridStepVoxels)*VoxelSizeCm,
|
||||
AddedVertices.emplace_back(static_cast<double>(TileOrigin.X+GridCoordinates[X])*VoxelSizeCm,
|
||||
static_cast<double>(TileOrigin.Y+GridCoordinates[Y])*VoxelSizeCm,
|
||||
Sample.Ground/Scale*VoxelSizeCm);
|
||||
AddedNormals.push_back(Sample.Normal);
|
||||
if(bHasUVs)AddedUVs.emplace_back(static_cast<float>(XSub/Scale),static_cast<float>(YSub/Scale));
|
||||
if(bHasColors)AddedColors.push_back(Sample.Color);
|
||||
}
|
||||
|
||||
for(int32 Y=0;Y<Cells;++Y)
|
||||
for(int32 X=0;X<Cells;++X)
|
||||
for(int32 Y=0;Y+1<GridSize;++Y)
|
||||
for(int32 X=0;X+1<GridSize;++X)
|
||||
{
|
||||
const int32 CellX[4]={X,X+1,X+1,X};
|
||||
const int32 CellY[4]={Y,Y,Y+1,Y+1};
|
||||
@@ -433,8 +486,8 @@ namespace VoxelForgeCore
|
||||
{
|
||||
const int32 GX=CellX[Corner],GY=CellY[Corner];
|
||||
const auto& Sample=Grid[GridIndex(GX,GY)];
|
||||
PolygonA[Corner].XSub=static_cast<double>(TileOrigin.X+GX*GridStepVoxels)*Scale;
|
||||
PolygonA[Corner].YSub=static_cast<double>(TileOrigin.Y+GY*GridStepVoxels)*Scale;
|
||||
PolygonA[Corner].XSub=static_cast<double>(TileOrigin.X+GridCoordinates[GX])*Scale;
|
||||
PolygonA[Corner].YSub=static_cast<double>(TileOrigin.Y+GridCoordinates[GY])*Scale;
|
||||
PolygonA[Corner].GroundSub=Sample.Ground;
|
||||
PolygonA[Corner].CapSub=Sample.Cap;
|
||||
PolygonA[Corner].ZSub=Sample.Ground;
|
||||
@@ -487,16 +540,29 @@ namespace VoxelForgeCore
|
||||
const auto InterpolateProfile=[](const std::vector<double>& Values,const FGroundProfile& Profile,
|
||||
double LocalX,double LocalY)
|
||||
{
|
||||
const double GridX=std::clamp(LocalX/Profile.GridStep,0.0,static_cast<double>(Profile.Cells));
|
||||
const double GridY=std::clamp(LocalY/Profile.GridStep,0.0,static_cast<double>(Profile.Cells));
|
||||
const int32 X=std::min(Profile.Cells-1,static_cast<int32>(std::floor(GridX)));
|
||||
const int32 Y=std::min(Profile.Cells-1,static_cast<int32>(std::floor(GridY)));
|
||||
const double TX=GridX-X,TY=GridY-Y;
|
||||
const std::size_t Width=static_cast<std::size_t>(Profile.Cells+1);
|
||||
const std::size_t Width=Profile.Coordinates.size();
|
||||
const auto FindCell=[&](double Local)
|
||||
{
|
||||
const double Clamped=std::clamp(Local,static_cast<double>(Profile.Coordinates.front()),
|
||||
static_cast<double>(Profile.Coordinates.back()));
|
||||
const auto Upper=std::upper_bound(Profile.Coordinates.begin(),Profile.Coordinates.end(),Clamped);
|
||||
if(Upper==Profile.Coordinates.begin())return std::pair<std::size_t,double>{0u,0.0};
|
||||
const std::size_t Index=std::min<std::size_t>(Width-2u,
|
||||
static_cast<std::size_t>(Upper-Profile.Coordinates.begin()-1));
|
||||
const double Start=Profile.Coordinates[Index];
|
||||
const double Span=Profile.Coordinates[Index+1]-Profile.Coordinates[Index];
|
||||
return std::pair<std::size_t,double>{Index,Span>0.0?(Clamped-Start)/Span:0.0};
|
||||
};
|
||||
const auto [X,TX]=FindCell(LocalX);
|
||||
const auto [Y,TY]=FindCell(LocalY);
|
||||
const auto At=[&](int32 PX,int32 PY){return Values[static_cast<std::size_t>(PY)*Width+PX];};
|
||||
if(TX>=TY)
|
||||
return At(X,Y)*(1.0-TX)+At(X+1,Y)*(TX-TY)+At(X+1,Y+1)*TY;
|
||||
return At(X,Y)*(1.0-TY)+At(X+1,Y+1)*TX+At(X,Y+1)*(TY-TX);
|
||||
return At(static_cast<int32>(X),static_cast<int32>(Y))*(1.0-TX)
|
||||
+At(static_cast<int32>(X+1u),static_cast<int32>(Y))*(TX-TY)
|
||||
+At(static_cast<int32>(X+1u),static_cast<int32>(Y+1u))*TY;
|
||||
return At(static_cast<int32>(X),static_cast<int32>(Y))*(1.0-TY)
|
||||
+At(static_cast<int32>(X+1u),static_cast<int32>(Y+1u))*TX
|
||||
+At(static_cast<int32>(X),static_cast<int32>(Y+1u))*(TY-TX);
|
||||
};
|
||||
for(int32 Triangle=0;Triangle<OldSurfaceTriangleCount;++Triangle)
|
||||
{
|
||||
@@ -517,12 +583,13 @@ namespace VoxelForgeCore
|
||||
if(LocalX<0.0||LocalY<0.0||LocalX>TileExtent||LocalY>TileExtent)continue;
|
||||
for(const auto& Profile:GroundProfiles)
|
||||
{
|
||||
if(LocalX>Profile.Cells*Profile.GridStep||LocalY>Profile.Cells*Profile.GridStep)continue;
|
||||
if(LocalX>Profile.Coordinates.back()||LocalY>Profile.Coordinates.back())continue;
|
||||
const double Ground=InterpolateProfile(Profile.Ground,Profile,LocalX,LocalY);
|
||||
const double Cap=InterpolateProfile(Profile.Cap,Profile,LocalX,LocalY);
|
||||
if(Cap-Ground<=1.0||Ground<TileMinZ/Scale||Ground>TileMaxZ/Scale
|
||||
||Ground<Profile.MinimumZ||Ground>Profile.MaximumZ)continue;
|
||||
const double ReplacementBand=std::max(32.0,2.0*Profile.GridStep);
|
||||
const double ReplacementBand=std::max(32.0,
|
||||
2.0*std::min(Profile.GridStep,BoundaryStepVoxels));
|
||||
if(Z>=Ground-ReplacementBand)
|
||||
{
|
||||
RemoveOldGround[static_cast<std::size_t>(Triangle)]=1;
|
||||
@@ -574,17 +641,28 @@ namespace VoxelForgeCore
|
||||
}
|
||||
|
||||
FNativeCapRefinementStats RefineFarNativeCap(const FStandaloneOwnerWorld& Owner,
|
||||
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,FVoxelMeshData& Mesh)
|
||||
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,int32 MeshStepVoxels,
|
||||
int32 InteriorStepOverride,FVoxelMeshData& Mesh)
|
||||
{
|
||||
FNativeCapRefinementStats Stats;
|
||||
if(Key.Level<4||!Owner.Generator)return Stats;
|
||||
const int32 GridStepVoxels=Key.Level<5?std::min(128,1<<Key.Level):128;
|
||||
constexpr int64 Scale=WorldAssemblySubvoxelsPerVoxel;
|
||||
const int64 GridStepSubvoxels=static_cast<int64>(GridStepVoxels)*Scale;
|
||||
constexpr int32 BoundaryStepVoxels=128;
|
||||
const int32 RequestedStep=Key.Level==8
|
||||
?(InteriorStepOverride>0?InteriorStepOverride:MeshStepVoxels)
|
||||
:(Key.Level<5?std::min(BoundaryStepVoxels,1<<Key.Level):BoundaryStepVoxels);
|
||||
const FIntVector TileOrigin=Key.OriginVoxels();
|
||||
const int32 TileExtent=Key.ExtentVoxels();
|
||||
if(TileExtent<=0||TileExtent%GridStepVoxels!=0)return Stats;
|
||||
const int32 Cells=TileExtent/GridStepVoxels;
|
||||
const int32 EffectiveGridStep=Key.Level==8
|
||||
?((RequestedStep<BoundaryStepVoxels||RequestedStep%BoundaryStepVoxels!=0
|
||||
||TileExtent<=0||TileExtent%RequestedStep!=0)
|
||||
?BoundaryStepVoxels:std::max(BoundaryStepVoxels,RequestedStep))
|
||||
:RequestedStep;
|
||||
constexpr int64 Scale=WorldAssemblySubvoxelsPerVoxel;
|
||||
if(TileExtent<=0||TileExtent%EffectiveGridStep!=0)return Stats;
|
||||
const std::vector<int32> GridCoordinates=
|
||||
BuildNativeHorizonGridCoordinates(TileExtent,EffectiveGridStep);
|
||||
const int32 GridSize=static_cast<int32>(GridCoordinates.size());
|
||||
if(GridSize<2)return Stats;
|
||||
const int64 TileMinZ=static_cast<int64>(TileOrigin.Z)*Scale;
|
||||
const int64 TileMaxZ=static_cast<int64>(TileOrigin.Z+TileExtent)*Scale;
|
||||
const int32 OldSurfaceVertices=std::clamp(Mesh.NumSurfaceVertices,0,Mesh.Vertices.Num());
|
||||
@@ -612,14 +690,14 @@ namespace VoxelForgeCore
|
||||
FColor Color=FColor::White;
|
||||
int32 VertexIndex=INDEX_NONE;
|
||||
};
|
||||
std::vector<FGridSample> Grid(static_cast<std::size_t>(Cells+1)*(Cells+1));
|
||||
const auto GridIndex=[Cells](int32 X,int32 Y)
|
||||
{return static_cast<std::size_t>(Y)*(Cells+1)+X;};
|
||||
for(int32 Y=0;Y<=Cells;++Y)
|
||||
for(int32 X=0;X<=Cells;++X)
|
||||
std::vector<FGridSample> Grid(static_cast<std::size_t>(GridSize)*GridSize);
|
||||
const auto GridIndex=[GridSize](int32 X,int32 Y)
|
||||
{return static_cast<std::size_t>(Y)*GridSize+X;};
|
||||
for(int32 Y=0;Y<GridSize;++Y)
|
||||
for(int32 X=0;X<GridSize;++X)
|
||||
{
|
||||
const int64 XSub=(static_cast<int64>(TileOrigin.X)+static_cast<int64>(X)*GridStepVoxels)*Scale;
|
||||
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+static_cast<int64>(Y)*GridStepVoxels)*Scale;
|
||||
const int64 XSub=(static_cast<int64>(TileOrigin.X)+GridCoordinates[X])*Scale;
|
||||
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+GridCoordinates[Y])*Scale;
|
||||
const auto Column=Block.point(XSub,YSub,0);
|
||||
auto& Sample=Grid[GridIndex(X,Y)];
|
||||
Sample.Ground=static_cast<double>(Column.groundHeightSubvoxels);
|
||||
@@ -633,13 +711,13 @@ namespace VoxelForgeCore
|
||||
const uint8 B=static_cast<uint8>(std::clamp(std::lround(Blend*255.0f),0l,255l));
|
||||
Sample.Color=FColor(R,0,B,A);
|
||||
}
|
||||
for(int32 Y=0;Y<=Cells;++Y)
|
||||
for(int32 X=0;X<=Cells;++X)
|
||||
for(int32 Y=0;Y<GridSize;++Y)
|
||||
for(int32 X=0;X<GridSize;++X)
|
||||
{
|
||||
const int32 Left=std::max(0,X-1),Right=std::min(Cells,X+1);
|
||||
const int32 Down=std::max(0,Y-1),Up=std::min(Cells,Y+1);
|
||||
const double DX=static_cast<double>(Right-Left)*GridStepSubvoxels;
|
||||
const double DY=static_cast<double>(Up-Down)*GridStepSubvoxels;
|
||||
const int32 Left=std::max(0,X-1),Right=std::min(GridSize-1,X+1);
|
||||
const int32 Down=std::max(0,Y-1),Up=std::min(GridSize-1,Y+1);
|
||||
const double DX=static_cast<double>(GridCoordinates[Right]-GridCoordinates[Left])*Scale;
|
||||
const double DY=static_cast<double>(GridCoordinates[Up]-GridCoordinates[Down])*Scale;
|
||||
const double SlopeX=DX>0.0?(Grid[GridIndex(Right,Y)].Cap-Grid[GridIndex(Left,Y)].Cap)/DX:0.0;
|
||||
const double SlopeY=DY>0.0?(Grid[GridIndex(X,Up)].Cap-Grid[GridIndex(X,Down)].Cap)/DY:0.0;
|
||||
auto& Sample=Grid[GridIndex(X,Y)];
|
||||
@@ -655,10 +733,10 @@ namespace VoxelForgeCore
|
||||
if(Sample.Cap<Sample.Ground||Sample.Cap<TileMinZ||Sample.Cap>TileMaxZ)continue;
|
||||
Sample.VertexIndex=static_cast<int32>(AddedVertices.size());
|
||||
const std::size_t Index=static_cast<std::size_t>(&Sample-Grid.data());
|
||||
const int32 GX=static_cast<int32>(Index%static_cast<std::size_t>(Cells+1));
|
||||
const int32 GY=static_cast<int32>(Index/static_cast<std::size_t>(Cells+1));
|
||||
const double XSub=static_cast<double>(TileOrigin.X+GX*GridStepVoxels)*Scale;
|
||||
const double YSub=static_cast<double>(TileOrigin.Y+GY*GridStepVoxels)*Scale;
|
||||
const int32 GX=static_cast<int32>(Index%static_cast<std::size_t>(GridSize));
|
||||
const int32 GY=static_cast<int32>(Index/static_cast<std::size_t>(GridSize));
|
||||
const double XSub=static_cast<double>(TileOrigin.X+GridCoordinates[GX])*Scale;
|
||||
const double YSub=static_cast<double>(TileOrigin.Y+GridCoordinates[GY])*Scale;
|
||||
const double VoxelSizeCm=static_cast<double>(Owner.CoreVoxelSizeMicrons)/10000.0;
|
||||
AddedVertices.emplace_back(XSub/Scale*VoxelSizeCm,YSub/Scale*VoxelSizeCm,
|
||||
Sample.Cap/Scale*VoxelSizeCm);
|
||||
@@ -668,8 +746,8 @@ namespace VoxelForgeCore
|
||||
if(bHasColors)AddedColors.push_back(Sample.Color);
|
||||
}
|
||||
|
||||
for(int32 Y=0;Y<Cells;++Y)
|
||||
for(int32 X=0;X<Cells;++X)
|
||||
for(int32 Y=0;Y+1<GridSize;++Y)
|
||||
for(int32 X=0;X+1<GridSize;++X)
|
||||
{
|
||||
const int32 CellX[4]={X,X+1,X+1,X};
|
||||
const int32 CellY[4]={Y,Y,Y+1,Y+1};
|
||||
@@ -692,8 +770,8 @@ namespace VoxelForgeCore
|
||||
{
|
||||
const int32 GX=CellX[Corner],GY=CellY[Corner];
|
||||
const auto& Sample=Grid[GridIndex(GX,GY)];
|
||||
PolygonA[Corner].XSub=static_cast<double>(TileOrigin.X+GX*GridStepVoxels)*Scale;
|
||||
PolygonA[Corner].YSub=static_cast<double>(TileOrigin.Y+GY*GridStepVoxels)*Scale;
|
||||
PolygonA[Corner].XSub=static_cast<double>(TileOrigin.X+GridCoordinates[GX])*Scale;
|
||||
PolygonA[Corner].YSub=static_cast<double>(TileOrigin.Y+GridCoordinates[GY])*Scale;
|
||||
PolygonA[Corner].ZSub=Sample.Cap;PolygonA[Corner].GroundSub=Sample.Ground;
|
||||
PolygonA[Corner].Normal=Sample.Normal;PolygonA[Corner].Color=Sample.Color;
|
||||
}
|
||||
@@ -1400,7 +1478,8 @@ namespace VoxelForgeCore
|
||||
&&bHasNativeSurfaceHorizonPlan&&!bHasIslandPlanInTile;
|
||||
const bool bSkipFarShellRepair=key.lod>=7&&bUseNativeSurfaceHorizon;
|
||||
FStandaloneShapeWorld::ResetThreadClassificationStats();
|
||||
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats();
|
||||
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(
|
||||
options.captureColumnCoordinates&&key.lod==8);
|
||||
bool bNativeSurfaceTile=false;
|
||||
bool bNativeSurfaceBandSafe=false;
|
||||
if(bHasWorldAssembly)
|
||||
@@ -1427,7 +1506,7 @@ namespace VoxelForgeCore
|
||||
{
|
||||
out.diagnostics.meshingSkipped=true;
|
||||
const auto Counts=FStandaloneShapeWorld::GetThreadClassificationStats();
|
||||
const auto Columns=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
|
||||
const auto& Columns=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
|
||||
out.diagnostics.solidBoxes=Counts.SolidBoxes;
|
||||
out.diagnostics.airBoxes=Counts.AirBoxes;
|
||||
out.diagnostics.mixedBoxes=Counts.MixedBoxes;
|
||||
@@ -1571,9 +1650,18 @@ namespace VoxelForgeCore
|
||||
out.diagnostics.featureListMilliseconds=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-FeatureListStart).count();
|
||||
const ScopedWorldAssemblyFeatureQueriesV1 featureScope(featureQueries);
|
||||
const double ColumnMillisecondsBeforeMeshing=
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
const auto MarchingCubesStart=std::chrono::steady_clock::now();
|
||||
FVoxelMeshData mesh=mesher.GenerateMesh(origin,step,cells,nullptr,
|
||||
MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr,
|
||||
(options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr);
|
||||
const double MarchingCubesWallMilliseconds=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-MarchingCubesStart).count();
|
||||
const double ColumnMillisecondsAfterMeshing=
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
out.diagnostics.marchingCubesExclusiveMilliseconds=std::max(0.0,
|
||||
MarchingCubesWallMilliseconds-(ColumnMillisecondsAfterMeshing-ColumnMillisecondsBeforeMeshing));
|
||||
const double MeshingMilliseconds=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-MeshingStart).count();
|
||||
out.diagnostics.farHorizonVersion=key.lod>=4?options.farHorizonVersion:0;
|
||||
@@ -1612,9 +1700,29 @@ namespace VoxelForgeCore
|
||||
{
|
||||
const auto HorizonStart=std::chrono::steady_clock::now();
|
||||
FNativeCapRefinementStats GroundStats;
|
||||
const double ColumnMillisecondsBeforeGround=
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
const auto GroundStart=std::chrono::steady_clock::now();
|
||||
if(key.lod>=4)
|
||||
GroundStats=RefineFarNativeGround(world,*impl_->worldAssembly,nativeKey,mesh);
|
||||
const auto CapStats=RefineFarNativeCap(world,*impl_->worldAssembly,nativeKey,mesh);
|
||||
GroundStats=RefineFarNativeGround(world,*impl_->worldAssembly,nativeKey,step,
|
||||
options.lod8HorizonInteriorStepVoxels,mesh);
|
||||
const double GroundWallMilliseconds=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-GroundStart).count();
|
||||
const double ColumnMillisecondsAfterGround=
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
out.diagnostics.groundRefinementExclusiveMilliseconds=std::max(0.0,
|
||||
GroundWallMilliseconds-(ColumnMillisecondsAfterGround-ColumnMillisecondsBeforeGround));
|
||||
const double ColumnMillisecondsBeforeCap=
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
const auto CapStart=std::chrono::steady_clock::now();
|
||||
const auto CapStats=RefineFarNativeCap(world,*impl_->worldAssembly,nativeKey,step,
|
||||
options.lod8HorizonInteriorStepVoxels,mesh);
|
||||
const double CapWallMilliseconds=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-CapStart).count();
|
||||
const double ColumnMillisecondsAfterCap=
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
out.diagnostics.capRefinementExclusiveMilliseconds=std::max(0.0,
|
||||
CapWallMilliseconds-(ColumnMillisecondsAfterCap-ColumnMillisecondsBeforeCap));
|
||||
out.diagnostics.farHorizonMilliseconds=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-HorizonStart).count();
|
||||
out.diagnostics.capRefined=CapStats.Refined;
|
||||
@@ -1629,7 +1737,7 @@ namespace VoxelForgeCore
|
||||
if(bHasWorldAssembly)
|
||||
{
|
||||
const auto Counts=FStandaloneShapeWorld::GetThreadClassificationStats();
|
||||
const auto Columns=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
|
||||
const auto& Columns=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
|
||||
out.diagnostics.fieldCalls=Columns.pointCalls;
|
||||
out.diagnostics.floatFieldCalls=Counts.NativeFloatFieldCalls;
|
||||
out.diagnostics.latticeFieldCalls=Counts.NativeLatticeFieldCalls;
|
||||
@@ -1637,7 +1745,15 @@ namespace VoxelForgeCore
|
||||
out.diagnostics.columnCacheHits=Columns.columnHits;
|
||||
out.diagnostics.columnCacheMisses=Columns.columnMisses;
|
||||
out.diagnostics.columnComputeMilliseconds=Columns.columnComputeMilliseconds;
|
||||
out.diagnostics.columnEvaluationExclusiveMilliseconds=Columns.columnComputeMilliseconds;
|
||||
out.diagnostics.classificationMilliseconds=Counts.Milliseconds;
|
||||
if(options.captureColumnCoordinates&&key.lod==8)
|
||||
{
|
||||
out.diagnostics.columnEvaluationWorkerHash=static_cast<std::uint64_t>(
|
||||
std::hash<std::thread::id>{}(std::this_thread::get_id()))+1u;
|
||||
out.diagnostics.columnCoordinateCaptureComplete=Columns.coordinateCaptureComplete;
|
||||
out.diagnostics.evaluatedColumnCoordinates=Columns.evaluatedCoordinates;
|
||||
}
|
||||
if(bNativeSurfaceTile)
|
||||
{
|
||||
out.diagnostics.solidBoxes=Counts.SolidBoxes;
|
||||
@@ -1653,6 +1769,7 @@ namespace VoxelForgeCore
|
||||
}
|
||||
out.diagnostics.heightfieldBandLimited=bNativeHeightfieldBandLimited;
|
||||
out.diagnostics.meshingMilliseconds=MeshingMilliseconds;
|
||||
const auto MeshAssemblyStart=std::chrono::steady_clock::now();
|
||||
out.meshPayloadAllocatedBytes=static_cast<std::uint64_t>(mesh.Vertices.GetAllocatedSize())
|
||||
+static_cast<std::uint64_t>(mesh.Normals.GetAllocatedSize())
|
||||
+static_cast<std::uint64_t>(mesh.UVs.GetAllocatedSize())
|
||||
@@ -1696,6 +1813,8 @@ namespace VoxelForgeCore
|
||||
}
|
||||
out.hasDensityHash=out.hasMeshHash=out.hasPayloadHash=out.hasTopologyHash=true;
|
||||
}
|
||||
out.diagnostics.meshAssemblyMilliseconds=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-MeshAssemblyStart).count();
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -1487,6 +1487,17 @@ namespace world_assembly_detail
|
||||
HeightfieldColumnStats.columnComputeMilliseconds+=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-start).count();
|
||||
++HeightfieldColumnStats.columnMisses;
|
||||
if(HeightfieldColumnStats.captureCoordinates&&HeightfieldColumnStats.coordinateCaptureComplete)
|
||||
{
|
||||
try
|
||||
{
|
||||
HeightfieldColumnStats.evaluatedCoordinates.push_back({x,y});
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
HeightfieldColumnStats.coordinateCaptureComplete=false;
|
||||
}
|
||||
}
|
||||
entry={&recipe,&plan,recipe.contentHash,plan.contentHash,x,y,column};
|
||||
return column;
|
||||
}
|
||||
@@ -1954,10 +1965,19 @@ HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y,
|
||||
return out;
|
||||
}
|
||||
|
||||
void Heightfield2D::resetThreadColumnCacheStats() noexcept
|
||||
{ world_assembly_detail::HeightfieldColumnStats={}; }
|
||||
void Heightfield2D::resetThreadColumnCacheStats(bool captureCoordinates) noexcept
|
||||
{
|
||||
auto& Stats=world_assembly_detail::HeightfieldColumnStats;
|
||||
Stats.pointCalls=0;
|
||||
Stats.columnHits=0;
|
||||
Stats.columnMisses=0;
|
||||
Stats.columnComputeMilliseconds=0.0;
|
||||
Stats.captureCoordinates=captureCoordinates;
|
||||
Stats.coordinateCaptureComplete=true;
|
||||
Stats.evaluatedCoordinates.clear();
|
||||
}
|
||||
|
||||
HeightfieldColumnCacheStats Heightfield2D::threadColumnCacheStats() noexcept
|
||||
const HeightfieldColumnCacheStats& Heightfield2D::threadColumnCacheStats() noexcept
|
||||
{ return world_assembly_detail::HeightfieldColumnStats; }
|
||||
|
||||
HeightfieldBoxClass Heightfield2D::classifyLatticeBox(std::int64_t minimumX,
|
||||
|
||||
Reference in new issue
Block a user