perf(core): tile-drain instrumentation; parallel FI sign-edge repair, FI first tile 1.28 -> 0.85 s (L55); R24
Opt-in --instrument-tile-drain records per-tile queue/dispatch/finish timelines, stage spans and counters (proof skips, X8 lanes, GradAt/midpoint memo, skirts, FI cache stats); <2% when off. Measured: SW 13.3 and FI 50.2 worker-s across LOD0-8, tails are work-saturated. FI sign-edge repair now computes in parallel and publishes in sorted serial order (exact output): sign-edge 535 -> 47 ms, FI first tile 1.28 -> 0.85 s, near ring 1.46 -> 1.04 s, whole view 6.09 -> 5.65 s. Proof block size, lane batching, SW midpoint memo and helper cap 12 measured and left off. fast/medium/full PASS, Clang and GCC. Research R24: slab + motif generator. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
e35a77f676
commit
aee1487410
13 files changed
+882
-74
No files matched your search
@@ -16,7 +16,9 @@
|
||||
#include "HAL/IConsoleManager.h"
|
||||
#include "Misc/CommandLine.h"
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <immintrin.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -546,7 +548,16 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
// la borne (un contact purement tangent est exclu — sa traversée serait hors bande).
|
||||
CzLo = FMath::Max(CzLo, FloorDivI((int64)BandZMinVox - OriginVoxels.Z, Step));
|
||||
CzHi = FMath::Min(CzHi, FloorDivI((int64)BandZMaxVox - OriginVoxels.Z, Step));
|
||||
if (CzHi < CzLo) { return MeshData; } // tuile entièrement hors bande → vide
|
||||
if (CzHi < CzLo)
|
||||
{
|
||||
if(OutStageTimings)
|
||||
{
|
||||
const uint64 Candidate=static_cast<uint64>(CellsPerAxis)*CellsPerAxis*CellsPerAxis;
|
||||
OutStageTimings->CandidateCells=Candidate;
|
||||
OutStageTimings->ZBandRejectedCells=Candidate;
|
||||
}
|
||||
return MeshData;
|
||||
} // tuile entièrement hors bande → vide
|
||||
}
|
||||
const int32 GzLo = CzLo - 1; // coins CzLo..CzHi+1, gradients ±1
|
||||
const int32 GzHi = CzHi + 2; // (== -1..GridDim sans bande)
|
||||
@@ -568,9 +579,10 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
&& (bNativeSurfaceTileProof || OutBlockProofAudit != nullptr
|
||||
|| bStandaloneShapeProof)));
|
||||
bool bCanSkipProofBlockGrid = Step <= 2 && bStandaloneShapeProof;
|
||||
const int32 ProofBlockCells=std::clamp(bStandaloneProofBlockCells,1,CellsPerAxis);
|
||||
if (bUseProofBlocks)
|
||||
{
|
||||
constexpr int32 BlockCells = 8;
|
||||
const int32 BlockCells = ProofBlockCells;
|
||||
BlocksPerAxis = (CellsPerAxis + BlockCells - 1) / BlockCells;
|
||||
BlockSkip.Init(0, BlocksPerAxis * BlocksPerAxis * BlocksPerAxis);
|
||||
bool bHaveUniformBlock = false;
|
||||
@@ -917,7 +929,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
RequiredGridSamples.SetNumUninitialized(MDim*MDim*MDim);
|
||||
FMemory::Memzero(RequiredGridSamples.GetData(),
|
||||
static_cast<SIZE_T>(RequiredGridSamples.Num())*sizeof(uint8));
|
||||
constexpr int32 BlockCells=8;
|
||||
const int32 BlockCells=ProofBlockCells;
|
||||
for(int32 BlockZ=0;BlockZ<BlocksPerAxis;++BlockZ)
|
||||
{
|
||||
const int32 BlockMinZ=BlockZ*BlockCells;
|
||||
@@ -945,6 +957,25 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
}
|
||||
}
|
||||
if(OutStageTimings)
|
||||
{
|
||||
const uint64 PlaneCells=static_cast<uint64>(CellsPerAxis)*CellsPerAxis;
|
||||
const int32 ActiveZ=FMath::Max(0,CzHi-CzLo+1);
|
||||
OutStageTimings->CandidateCells=PlaneCells*static_cast<uint64>(CellsPerAxis);
|
||||
OutStageTimings->ZBandRejectedCells=PlaneCells
|
||||
*static_cast<uint64>(CellsPerAxis-ActiveZ);
|
||||
if(bSkipProofBlockGrid)
|
||||
{
|
||||
for(int32 Z=GzLo;Z<=GzHi;++Z)
|
||||
for(int32 Y=-1;Y<=GridDim;++Y)
|
||||
for(int32 X=-1;X<=GridDim;++X)
|
||||
{
|
||||
const int32 Index=((Z+1)*MDim+(Y+1))*MDim+(X+1);
|
||||
if(RequiredGridSamples[Index]==0)
|
||||
++OutStageTimings->ProofSkippedDensitySamples;
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool bCapturePreEditGrid = OutPreEditDensityGrid != nullptr
|
||||
&& !bUseSharedDensityGrid
|
||||
&& Step == 1 && CellsPerAxis == CHUNK_SIZE
|
||||
@@ -1199,7 +1230,35 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
&&ActiveShapeWorld->IsAnalyticOnly()
|
||||
&&!bUseSharedDensityGrid&&!bCapturePreEditGrid&&!bSkipProofBlockGrid;
|
||||
const bool bUseShapeWorldXBatch=ActiveShapeWorld!=nullptr
|
||||
&&!bUseSharedDensityGrid&&!bCapturePreEditGrid&&!bSkipProofBlockGrid;
|
||||
&&!bUseSharedDensityGrid&&!bCapturePreEditGrid
|
||||
&&(!bSkipProofBlockGrid||bStandaloneEnableActiveLaneBatching);
|
||||
const auto SampleShapeWorldXBatch=[&](int32 GX0,int32 BatchCount,int32 gy,int32 gz)
|
||||
{
|
||||
float Densities[8]{};
|
||||
ActiveShapeWorld->DensityAtLatticeX8(OriginVoxels.X+GX0*Step,Step,
|
||||
OriginVoxels.Y+gy*Step,OriginVoxels.Z+gz*Step,BatchCount,Densities);
|
||||
if(OutStageTimings)
|
||||
{
|
||||
++OutStageTimings->X8DensityCalls;
|
||||
OutStageTimings->X8ActiveLanes+=static_cast<uint64>(BatchCount);
|
||||
}
|
||||
for(int32 Lane=0;Lane<BatchCount;++Lane)
|
||||
{
|
||||
const int32 GX=GX0+Lane;
|
||||
const int32 LocalIndex=((gz+1)*MDim+(gy+1))*MDim+(GX+1);
|
||||
DensityGrid[LocalIndex]=Densities[Lane];
|
||||
}
|
||||
const int32 LastGX=GX0+BatchCount-1;
|
||||
const int32 ReachBlockX=FMath::Clamp(LastGX,0,CellsPerAxis-1)
|
||||
/VoxelGenLOD::TileReachBlockCells;
|
||||
const int32 ReachBlockY=FMath::Clamp(gy,0,CellsPerAxis-1)
|
||||
/VoxelGenLOD::TileReachBlockCells;
|
||||
const int32 ReachBlockZ=FMath::Clamp(gz,0,CellsPerAxis-1)
|
||||
/VoxelGenLOD::TileReachBlockCells;
|
||||
VoxelGenLOD::TileReachBlockIndex=
|
||||
((ReachBlockZ*ReachBlocksPerAxis)+ReachBlockY)*ReachBlocksPerAxis+ReachBlockX;
|
||||
if(OutDensitySampleCount)*OutDensitySampleCount+=BatchCount;
|
||||
};
|
||||
if(bUseShapeWorldZBatch&&!bUseVerticalCacheTraversal)
|
||||
{
|
||||
for(const std::vector<int32>& YGroup:YGroups)
|
||||
@@ -1277,6 +1336,26 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
{
|
||||
for(size_t Offset=0;Offset<XGroup.size();)
|
||||
{
|
||||
if(bSkipProofBlockGrid)
|
||||
{
|
||||
const int32 GX0=XGroup[Offset];
|
||||
const int32 LocalIndex=((gz+1)*MDim+(gy+1))*MDim+(GX0+1);
|
||||
if(RequiredGridSamples[LocalIndex]==0)
|
||||
{++Offset;continue;}
|
||||
int32 BatchCount=1;
|
||||
while(BatchCount<8&&Offset+static_cast<size_t>(BatchCount)<XGroup.size()
|
||||
&&XGroup[Offset+static_cast<size_t>(BatchCount)]==GX0+BatchCount)
|
||||
{
|
||||
const int32 NextGX=XGroup[Offset+static_cast<size_t>(BatchCount)];
|
||||
const int32 NextIndex=((gz+1)*MDim+(gy+1))*MDim+(NextGX+1);
|
||||
if(RequiredGridSamples[NextIndex]==0)break;
|
||||
++BatchCount;
|
||||
}
|
||||
if(BatchCount<2)SampleGridPoint(GX0,gy,gz);
|
||||
else SampleShapeWorldXBatch(GX0,BatchCount,gy,gz);
|
||||
Offset+=static_cast<size_t>(BatchCount);
|
||||
continue;
|
||||
}
|
||||
const int32 BatchCount=static_cast<int32>(std::min<size_t>(
|
||||
8,XGroup.size()-Offset));
|
||||
const int32 GX0=XGroup[Offset];
|
||||
@@ -1291,30 +1370,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
continue;
|
||||
}
|
||||
|
||||
float Densities[8]{};
|
||||
ActiveShapeWorld->DensityAtLatticeX8(
|
||||
OriginVoxels.X+GX0*Step,Step,
|
||||
OriginVoxels.Y+gy*Step,OriginVoxels.Z+gz*Step,
|
||||
BatchCount,Densities);
|
||||
for(int32 Lane=0;Lane<BatchCount;++Lane)
|
||||
{
|
||||
const int32 GX=GX0+Lane;
|
||||
const int32 LocalIndex=
|
||||
((gz+1)*MDim+(gy+1))*MDim+(GX+1);
|
||||
DensityGrid[LocalIndex]=Densities[Lane];
|
||||
}
|
||||
const int32 LastGX=GX0+BatchCount-1;
|
||||
const int32 ReachBlockX=FMath::Clamp(LastGX,0,CellsPerAxis-1)
|
||||
/VoxelGenLOD::TileReachBlockCells;
|
||||
const int32 ReachBlockY=FMath::Clamp(gy,0,CellsPerAxis-1)
|
||||
/VoxelGenLOD::TileReachBlockCells;
|
||||
const int32 ReachBlockZ=FMath::Clamp(gz,0,CellsPerAxis-1)
|
||||
/VoxelGenLOD::TileReachBlockCells;
|
||||
VoxelGenLOD::TileReachBlockIndex=
|
||||
((ReachBlockZ*ReachBlocksPerAxis)+ReachBlockY)
|
||||
*ReachBlocksPerAxis+ReachBlockX;
|
||||
if(OutDensitySampleCount)
|
||||
*OutDensitySampleCount+=BatchCount;
|
||||
SampleShapeWorldXBatch(GX0,BatchCount,gy,gz);
|
||||
Offset+=static_cast<size_t>(BatchCount);
|
||||
}
|
||||
}
|
||||
@@ -1398,7 +1454,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
// cubes loop will consume. This is intentionally redundant with ClassifyTile: it is a
|
||||
// cheap soundness fence around the proof/cache hand-off. Unknown, zero, or opposite-sign
|
||||
// vertices clear the candidate, so an unproven block pays the ordinary cell path.
|
||||
constexpr int32 BlockCells = 8;
|
||||
const int32 BlockCells = ProofBlockCells;
|
||||
for (int32 BlockZ = 0; BlockZ < BlocksPerAxis; ++BlockZ)
|
||||
{
|
||||
const int32 BlockMinZ = BlockZ * BlockCells;
|
||||
@@ -1454,6 +1510,31 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
}
|
||||
}
|
||||
if(OutStageTimings&&bUseProofBlocks)
|
||||
{
|
||||
for(int32 BlockZ=0;BlockZ<BlocksPerAxis;++BlockZ)
|
||||
{
|
||||
const int32 CellMinZ=BlockZ*ProofBlockCells;
|
||||
const int32 CellMaxZ=FMath::Min(CellsPerAxis-1,CellMinZ+ProofBlockCells-1);
|
||||
const int32 ZCount=FMath::Max(0,FMath::Min(CzHi,CellMaxZ)
|
||||
-FMath::Max(CzLo,CellMinZ)+1);
|
||||
if(ZCount==0)continue;
|
||||
for(int32 BlockY=0;BlockY<BlocksPerAxis;++BlockY)
|
||||
for(int32 BlockX=0;BlockX<BlocksPerAxis;++BlockX)
|
||||
{
|
||||
const int32 Index=(BlockZ*BlocksPerAxis+BlockY)*BlocksPerAxis+BlockX;
|
||||
const uint8 Reason=BlockSkip[Index];
|
||||
if(Reason==0)continue;
|
||||
const int32 XCount=FMath::Min(CellsPerAxis-1,
|
||||
(BlockX+1)*ProofBlockCells-1)-BlockX*ProofBlockCells+1;
|
||||
const int32 YCount=FMath::Min(CellsPerAxis-1,
|
||||
(BlockY+1)*ProofBlockCells-1)-BlockY*ProofBlockCells+1;
|
||||
const uint64 Skipped=static_cast<uint64>(XCount)*YCount*ZCount;
|
||||
if(Reason==1)OutStageTimings->ProofSkippedSolidCells+=Skipped;
|
||||
else if(Reason==2)OutStageTimings->ProofSkippedAirCells+=Skipped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── CAPTURE-DURING-MESHING ──
|
||||
// Si demandé et que la tuile est pleine résolution (CellsPerAxis==CHUNK_SIZE ⇒ Step==1<<Level,
|
||||
@@ -1518,6 +1599,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
};
|
||||
auto GradAt = [&](int32 gx, int32 gy, int32 gz) -> FVector
|
||||
{
|
||||
if(OutStageTimings)++OutStageTimings->GradientCalls;
|
||||
const int32 CacheIndex=((gz+1)*MDim+(gy+1))*MDim+(gx+1);
|
||||
if(bUseGradientMemo&&GradientMemoValid[CacheIndex]!=0)
|
||||
{
|
||||
@@ -1577,6 +1659,25 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
uint64 AuditCellClassificationCycles=0,AuditGradientCycles=0;
|
||||
uint64 AuditInterpolationCycles=0,AuditDedupStreamCycles=0;
|
||||
uint64 AuditGradientSamples=0,AuditInterpolationSamples=0,AuditDedupStreamSamples=0;
|
||||
struct FMidpointDensityKey
|
||||
{
|
||||
uint64 X=0,Y=0,Z=0;
|
||||
bool operator==(const FMidpointDensityKey&)const=default;
|
||||
};
|
||||
struct FMidpointDensityKeyHash
|
||||
{
|
||||
size_t operator()(const FMidpointDensityKey& Key)const noexcept
|
||||
{
|
||||
uint64 Hash=Key.X^std::rotl(Key.Y,21)^std::rotl(Key.Z,42);
|
||||
Hash^=Hash>>30;Hash*=0xbf58476d1ce4e5b9ull;
|
||||
Hash^=Hash>>27;Hash*=0x94d049bb133111ebull;
|
||||
Hash^=Hash>>31;
|
||||
return static_cast<size_t>(Hash);
|
||||
}
|
||||
};
|
||||
std::unordered_map<FMidpointDensityKey,float,FMidpointDensityKeyHash> MidpointDensityMemo;
|
||||
if(bStandaloneEnableExactMidpointDensityMemo&&ShapeWorldContext&&Step==1)
|
||||
MidpointDensityMemo.reserve(1024);
|
||||
uint8 CaseIndexRow[CHUNK_SIZE]{};
|
||||
uint8 SurfaceRow[CHUNK_SIZE]{};
|
||||
uint8 SampleClassificationRow[CHUNK_SIZE]{};
|
||||
@@ -1593,14 +1694,21 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
for (int32 GroupX = 0; GroupX < CellsPerAxis; GroupX += 8)
|
||||
{
|
||||
const int32 GroupCount = FMath::Min(8, CellsPerAxis - GroupX);
|
||||
if (bUseProofBlocks
|
||||
&& BlockSkip[((cz / 8) * BlocksPerAxis + (cy / 8)) * BlocksPerAxis
|
||||
+ (GroupX / 8)] != 0)
|
||||
bool bAnySkipped=false,bAllSkipped=bUseProofBlocks;
|
||||
if(bUseProofBlocks)
|
||||
{
|
||||
continue;
|
||||
for(int32 Lane=0;Lane<GroupCount;++Lane)
|
||||
{
|
||||
const int32 CellX=GroupX+Lane;
|
||||
const int32 BlockIndex=((cz/ProofBlockCells)*BlocksPerAxis
|
||||
+(cy/ProofBlockCells))*BlocksPerAxis+(CellX/ProofBlockCells);
|
||||
const bool bSkipped=BlockSkip[BlockIndex]!=0;
|
||||
bAnySkipped|=bSkipped;bAllSkipped&=bSkipped;
|
||||
}
|
||||
}
|
||||
if(bAllSkipped)continue;
|
||||
|
||||
if (GroupCount == 8)
|
||||
if (GroupCount == 8&&!bAnySkipped)
|
||||
{
|
||||
const float* Row00 = DensityGrid.GetData()
|
||||
+ ((cz + 1) * MDim + (cy + 1)) * MDim + GroupX + 1;
|
||||
@@ -1641,6 +1749,10 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
for (int32 Lane = 0; Lane < GroupCount; ++Lane)
|
||||
{
|
||||
const int32 CellX = GroupX + Lane;
|
||||
if(bUseProofBlocks
|
||||
&&BlockSkip[((cz/ProofBlockCells)*BlocksPerAxis
|
||||
+(cy/ProofBlockCells))*BlocksPerAxis
|
||||
+(CellX/ProofBlockCells)]!=0)continue;
|
||||
int32 CaseIndex = 0;
|
||||
for (int32 Corner = 0; Corner < 8; ++Corner)
|
||||
{
|
||||
@@ -1683,7 +1795,9 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
for (int32 cx = 0; cx < CellsPerAxis; cx++)
|
||||
{
|
||||
if (bUseProofBlocks
|
||||
&& BlockSkip[((cz / 8) * BlocksPerAxis + (cy / 8)) * BlocksPerAxis + (cx / 8)] != 0)
|
||||
&& BlockSkip[((cz / ProofBlockCells) * BlocksPerAxis
|
||||
+ (cy / ProofBlockCells)) * BlocksPerAxis
|
||||
+ (cx / ProofBlockCells)] != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1746,9 +1860,28 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
{
|
||||
const FVector Midpoint=(Positions[A]+Positions[B])*0.5f;
|
||||
if(OutStageTimings)++OutStageTimings->EdgeMidpointDensityCalls;
|
||||
const float MidDensity=ShapeWorldContext->DensityAtUnclamped(
|
||||
Midpoint.X/GeometryVoxelSize,Midpoint.Y/GeometryVoxelSize,
|
||||
Midpoint.Z/GeometryVoxelSize);
|
||||
const double MidX=Midpoint.X/GeometryVoxelSize;
|
||||
const double MidY=Midpoint.Y/GeometryVoxelSize;
|
||||
const double MidZ=Midpoint.Z/GeometryVoxelSize;
|
||||
float MidDensity=0.0f;
|
||||
if(bStandaloneEnableExactMidpointDensityMemo)
|
||||
{
|
||||
const FMidpointDensityKey Key{std::bit_cast<uint64>(MidX),
|
||||
std::bit_cast<uint64>(MidY),std::bit_cast<uint64>(MidZ)};
|
||||
const auto Found=MidpointDensityMemo.find(Key);
|
||||
if(Found!=MidpointDensityMemo.end())
|
||||
{
|
||||
MidDensity=Found->second;
|
||||
if(OutStageTimings)
|
||||
++OutStageTimings->EdgeMidpointDensityMemoHits;
|
||||
}
|
||||
else
|
||||
{
|
||||
MidDensity=ShapeWorldContext->DensityAtUnclamped(MidX,MidY,MidZ);
|
||||
MidpointDensityMemo.emplace(Key,MidDensity);
|
||||
}
|
||||
}
|
||||
else MidDensity=ShapeWorldContext->DensityAtUnclamped(MidX,MidY,MidZ);
|
||||
if(FMath::Abs(MidDensity-IsoLevel)<1.0e-6f)
|
||||
T=0.5f;
|
||||
else if((MidDensity<IsoLevel)==(D1<IsoLevel))
|
||||
@@ -1938,6 +2071,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
};
|
||||
|
||||
std::vector<float> SurfaceVertexDensitySamples;
|
||||
const auto CountSkirtDensityProbe=[&]()
|
||||
{if(OutStageTimings)++OutStageTimings->SkirtDensityProbes;};
|
||||
if(ShapeWorldContext)
|
||||
SurfaceVertexDensitySamples.assign(static_cast<size_t>(MeshData.Vertices.Num()),
|
||||
std::numeric_limits<float>::quiet_NaN());
|
||||
@@ -1945,12 +2080,18 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
{
|
||||
if(!ShapeWorldContext)return 0.0f;
|
||||
if(OriginalIndex<0||static_cast<size_t>(OriginalIndex)>=SurfaceVertexDensitySamples.size())
|
||||
{
|
||||
CountSkirtDensityProbe();
|
||||
return ShapeWorldContext->DensityAt(Position.X/GeometryVoxelSize,
|
||||
Position.Y/GeometryVoxelSize,Position.Z/GeometryVoxelSize);
|
||||
}
|
||||
float& Density=SurfaceVertexDensitySamples[static_cast<size_t>(OriginalIndex)];
|
||||
if(std::isnan(Density))
|
||||
{
|
||||
CountSkirtDensityProbe();
|
||||
Density=ShapeWorldContext->DensityAt(Position.X/GeometryVoxelSize,
|
||||
Position.Y/GeometryVoxelSize,Position.Z/GeometryVoxelSize);
|
||||
}
|
||||
return Density;
|
||||
};
|
||||
|
||||
@@ -1974,8 +2115,11 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
{
|
||||
FSkirtVertex Vertex{Position,Normal,Color,0.0f,OriginalIndex};
|
||||
if(!bOnSurface&&ShapeWorldContext)
|
||||
{
|
||||
CountSkirtDensityProbe();
|
||||
Vertex.Density=ShapeWorldContext->DensityAt(Position.X/GeometryVoxelSize,
|
||||
Position.Y/GeometryVoxelSize,Position.Z/GeometryVoxelSize);
|
||||
}
|
||||
return Vertex;
|
||||
};
|
||||
const auto InterpolateVertex=[&](const FSkirtVertex& A,const FSkirtVertex& B,float T)
|
||||
@@ -1990,10 +2134,13 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
static_cast<uint8>(FMath::Clamp(FMath::RoundToInt(FMath::Lerp(static_cast<float>(A.Color.G),static_cast<float>(B.Color.G),T)),0,255)),
|
||||
static_cast<uint8>(FMath::Clamp(FMath::RoundToInt(FMath::Lerp(static_cast<float>(A.Color.B),static_cast<float>(B.Color.B),T)),0,255)),
|
||||
static_cast<uint8>(FMath::Clamp(FMath::RoundToInt(FMath::Lerp(static_cast<float>(A.Color.A),static_cast<float>(B.Color.A),T)),0,255)));
|
||||
Out.Density=ShapeWorldContext
|
||||
?ShapeWorldContext->DensityAt(Out.Position.X/GeometryVoxelSize,
|
||||
Out.Position.Y/GeometryVoxelSize,Out.Position.Z/GeometryVoxelSize)
|
||||
:FMath::Lerp(A.Density,B.Density,T);
|
||||
if(ShapeWorldContext)
|
||||
{
|
||||
CountSkirtDensityProbe();
|
||||
Out.Density=ShapeWorldContext->DensityAt(Out.Position.X/GeometryVoxelSize,
|
||||
Out.Position.Y/GeometryVoxelSize,Out.Position.Z/GeometryVoxelSize);
|
||||
}
|
||||
else Out.Density=FMath::Lerp(A.Density,B.Density,T);
|
||||
Out.OriginalIndex=INDEX_NONE;
|
||||
return Out;
|
||||
};
|
||||
@@ -2052,6 +2199,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
const FVector Position=P0.Position*static_cast<float>(WA)
|
||||
+P1.Position*static_cast<float>(WB)
|
||||
+P2.Position*static_cast<float>(WC);
|
||||
CountSkirtDensityProbe();
|
||||
return ShapeWorldContext->DensityAt(Position.X/GeometryVoxelSize,
|
||||
Position.Y/GeometryVoxelSize,Position.Z/GeometryVoxelSize);
|
||||
};
|
||||
@@ -2101,6 +2249,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
{
|
||||
const FVector Center=(P0.Position+P1.Position+P2.Position)
|
||||
/(3.0f*GeometryVoxelSize);
|
||||
CountSkirtDensityProbe();
|
||||
if(ShapeWorldContext->DensityAt(Center.X,Center.Y,Center.Z)>0.0f)continue;
|
||||
}
|
||||
Tris.Add(PolygonIndices[0]);Tris.Add(PolygonIndices[Fan]);Tris.Add(PolygonIndices[Fan+1]);
|
||||
@@ -2113,12 +2262,14 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
const int32 Tri[3] = { Tris[t], Tris[t + 1], Tris[t + 2] };
|
||||
for (int32 e = 0; e < 3; ++e)
|
||||
{
|
||||
if(OutStageTimings)++OutStageTimings->SkirtEdgeCandidates;
|
||||
const int32 iA = Tri[e], iB = Tri[(e + 1) % 3];
|
||||
// COPIES par valeur — AddSkirtVert réalloue Vertices/Normals (invaliderait des refs).
|
||||
const FVector PA = MeshData.Vertices[iA];
|
||||
const FVector PB = MeshData.Vertices[iB];
|
||||
const uint8 FaceMask=BoundaryFaceMask(PA,PB);
|
||||
if (!FaceMask) continue;
|
||||
if(OutStageTimings)++OutStageTimings->SkirtBoundaryCandidates;
|
||||
|
||||
const FVector NA = MeshData.Normals[iA];
|
||||
const FVector NB = MeshData.Normals[iB];
|
||||
@@ -2183,6 +2334,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
|
||||
if(OutStageTimings)
|
||||
{
|
||||
OutStageTimings->CellClassificationCalls=CellClassificationCalls;
|
||||
OutStageTimings->SurfaceCellCalls=SurfaceCellCalls;
|
||||
const double CycleToMs=FPlatformTime::GetSecondsPerCycle64()*1000.0;
|
||||
const auto ScaleSampledCycles=[CycleToMs](uint64 Cycles,uint64 Samples,uint64 Calls)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user