standalone: owner LOD0 defects -- relief blended as partition of unity (stairs gone), exposed skirts 0
- Stairs: cave, floor, strata and passage relief were effectively summed in overlaps (owner ray ~0.94 voxel of displacement); now partition-of-unity weights: -0.288 voxel there, max detail gradient 0.323 voxel/voxel. High-detail patch scan 133 -> 0; exact midpoint edge roots kept (removing them: 7,993 bad roots, 46 patches). - Fins: exposed skirt triangle scan 1,776 -> 0; thin sheets 0; coarse off-surface panels 0. - Path hash both directions equal; W1/W8 equal; routes 5642/5642; passage detail ratio 0.850; far survival passes, bad-shell fails; 30-crossing flight: nearest tile 15.1 ms median, 124 ms max. Caveats seen in the captures: the owner-ray wall has visibly less relief than before; the fin before/after captures do not show the fins (evidence is the scan only). Cold StreamFullClipmap 0.758 s vs 0.741 s (+17 ms, Luna's interleaved run). Owner to judge: bin\VoxelForgeLookCheck4.exe. 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
b3af0e589e
commit
de14f0042d
6 files changed
+213
-26
No files matched your search
@@ -1299,8 +1299,30 @@ private:
|
|||||||
static float DetailContrast(float CaveInfluence,float FloorInfluence)
|
static float DetailContrast(float CaveInfluence,float FloorInfluence)
|
||||||
{
|
{
|
||||||
const float Cave=std::clamp(CaveInfluence,0.0f,1.0f);
|
const float Cave=std::clamp(CaveInfluence,0.0f,1.0f);
|
||||||
const float Floor=std::clamp(FloorInfluence,0.0f,1.0f);
|
const float Floor=std::min(Cave,std::clamp(FloorInfluence,0.0f,1.0f));
|
||||||
return 1.35f-0.80f*Cave+1.85f*Floor;
|
const float StrataWeight=1.0f-Cave;
|
||||||
|
const float CaveWeight=Cave-Floor;
|
||||||
|
const float FloorWeight=Floor;
|
||||||
|
// The three influences are a partition of unity. Cave floors blend at the
|
||||||
|
// cave family's amplitude instead of multiplying both family contributions.
|
||||||
|
return 1.35f*StrataWeight+1.00f*CaveWeight+0.55f*FloorWeight;
|
||||||
|
}
|
||||||
|
static float BlendDetailFamilies(float Relief,float OwnerRelief,
|
||||||
|
float CaveInfluence,float FloorInfluence,float PassageInfluence)
|
||||||
|
{
|
||||||
|
const float Cave=std::clamp(CaveInfluence,0.0f,1.0f);
|
||||||
|
const float Floor=std::min(Cave,std::clamp(FloorInfluence,0.0f,1.0f));
|
||||||
|
const float Passage=std::clamp(PassageInfluence,0.0f,1.0f);
|
||||||
|
const float NonPassageWeight=1.0f-Passage;
|
||||||
|
const float StrataWeight=NonPassageWeight*(1.0f-Cave);
|
||||||
|
const float CaveWeight=NonPassageWeight*(Cave-Floor);
|
||||||
|
const float FloorWeight=NonPassageWeight*Floor;
|
||||||
|
const float StrataRelief=1.35f*(Relief+0.5f*OwnerRelief);
|
||||||
|
const float CaveRelief=1.00f*Relief;
|
||||||
|
const float FloorRelief=0.55f*Relief;
|
||||||
|
const float PassageRelief=0.75f*Relief;
|
||||||
|
return StrataWeight*StrataRelief+CaveWeight*CaveRelief
|
||||||
|
+FloorWeight*FloorRelief+Passage*PassageRelief;
|
||||||
}
|
}
|
||||||
static constexpr float DetailBandInnerRadius=1.5f;
|
static constexpr float DetailBandInnerRadius=1.5f;
|
||||||
static float DetailBandFade(float SignedDistance,float Edge)
|
static float DetailBandFade(float SignedDistance,float Edge)
|
||||||
@@ -3883,8 +3905,8 @@ private:
|
|||||||
if(Field)
|
if(Field)
|
||||||
OwnerRelief=OwnerCaveRoughnessForDetail(X,Y,Z,SurfaceSDF,*Field,
|
OwnerRelief=OwnerCaveRoughnessForDetail(X,Y,Z,SurfaceSDF,*Field,
|
||||||
CaveInfluence,PassageWeight,OwnerNoise);
|
CaveInfluence,PassageWeight,OwnerNoise);
|
||||||
const float Contrast=DetailContrast(CaveInfluence,FloorInfluence);
|
const float Detail=std::clamp(BlendDetailFamilies(Relief,OwnerRelief,
|
||||||
const float Detail=std::clamp((Relief+OwnerRelief*0.5f)*Contrast,-3.9f,3.9f);
|
CaveInfluence,FloorInfluence,PassageWeight),-3.9f,3.9f);
|
||||||
return Detail;
|
return Detail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3920,8 +3942,8 @@ private:
|
|||||||
if(Profile)
|
if(Profile)
|
||||||
Profile->OwnerRoughnessSampleNanoseconds+=static_cast<uint64>(
|
Profile->OwnerRoughnessSampleNanoseconds+=static_cast<uint64>(
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-OwnerStart).count());
|
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-OwnerStart).count());
|
||||||
const float Contrast=DetailContrast(CaveInfluence,FloorInfluence);
|
const float Detail=std::clamp(BlendDetailFamilies(Relief,OwnerRelief,
|
||||||
const float Detail=std::clamp((Relief+OwnerRelief*0.5f)*Contrast,-3.9f,3.9f);
|
CaveInfluence,FloorInfluence,PassageWeight),-3.9f,3.9f);
|
||||||
return Detail;
|
return Detail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.9 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.9 MiB |
@@ -70,10 +70,12 @@ namespace
|
|||||||
ReverseSideSolidSkirtTriangles=0,RepeatedOffsetPatches=0,FieldMismatchPatches=0,
|
ReverseSideSolidSkirtTriangles=0,RepeatedOffsetPatches=0,FieldMismatchPatches=0,
|
||||||
NormalMismatchPatches=0,GradientFallbackPatches=0,GradientFallbackSamples=0,
|
NormalMismatchPatches=0,GradientFallbackPatches=0,GradientFallbackSamples=0,
|
||||||
DetailDisplacementSamples=0,HighDetailDisplacementSamples=0,DetailDisplacementPatches=0,
|
DetailDisplacementSamples=0,HighDetailDisplacementSamples=0,DetailDisplacementPatches=0,
|
||||||
CurvedEdgeChecks=0,CurvedEdgeRootPatches=0,CurvedEdgeRootBadVertices=0;
|
CurvedEdgeChecks=0,CurvedEdgeRootPatches=0,CurvedEdgeRootBadVertices=0,
|
||||||
|
DetailGradientSamples=0,DetailGradientOverLimit=0;
|
||||||
double MaximumAirSkirtDensity=0.0,MaximumPatchDominance=0.0;
|
double MaximumAirSkirtDensity=0.0,MaximumPatchDominance=0.0;
|
||||||
double MaximumDetailDisplacement=0.0;
|
double MaximumDetailDisplacement=0.0;
|
||||||
double MaximumCurvedEdgeRootError=0.0;
|
double MaximumCurvedEdgeRootError=0.0;
|
||||||
|
double MaximumDetailGradient=0.0;
|
||||||
uint64 FieldEdgeChecks=0,AnalyticCrossingMisses=0,OldInt8RangeEdges=0,NormalChecks=0;
|
uint64 FieldEdgeChecks=0,AnalyticCrossingMisses=0,OldInt8RangeEdges=0,NormalChecks=0;
|
||||||
double SumAbsSourceResidual=0.0,MaximumAbsSourceResidual=0.0,
|
double SumAbsSourceResidual=0.0,MaximumAbsSourceResidual=0.0,
|
||||||
SumAnalyticEdgeShiftVoxels=0.0,MaximumAnalyticEdgeShiftVoxels=0.0,
|
SumAnalyticEdgeShiftVoxels=0.0,MaximumAnalyticEdgeShiftVoxels=0.0,
|
||||||
@@ -173,6 +175,26 @@ namespace
|
|||||||
{P[0],P[1],P[2]});
|
{P[0],P[1],P[2]});
|
||||||
if(DetailSample.caveInfluence>=0.60f&&DetailSample.floorInfluence>=0.60f)
|
if(DetailSample.caveInfluence>=0.60f&&DetailSample.floorInfluence>=0.60f)
|
||||||
{
|
{
|
||||||
|
constexpr double GradientProbeOffset=0.5;
|
||||||
|
double DetailGradientSquared=0.0;
|
||||||
|
for(int32 Axis=0;Axis<3;++Axis)
|
||||||
|
{
|
||||||
|
double Minus[3]={P[0],P[1],P[2]};
|
||||||
|
double Plus[3]={P[0],P[1],P[2]};
|
||||||
|
Minus[Axis]-=GradientProbeOffset;
|
||||||
|
Plus[Axis]+=GradientProbeOffset;
|
||||||
|
const double DMinus=Shape.detailSampleAt(
|
||||||
|
{Minus[0],Minus[1],Minus[2]}).displacement;
|
||||||
|
const double DPlus=Shape.detailSampleAt(
|
||||||
|
{Plus[0],Plus[1],Plus[2]}).displacement;
|
||||||
|
const double Gradient=(DPlus-DMinus)/(2.0*GradientProbeOffset);
|
||||||
|
DetailGradientSquared+=Gradient*Gradient;
|
||||||
|
}
|
||||||
|
const double DetailGradient=std::sqrt(DetailGradientSquared);
|
||||||
|
++Result.DetailGradientSamples;
|
||||||
|
Result.MaximumDetailGradient=std::max(
|
||||||
|
Result.MaximumDetailGradient,DetailGradient);
|
||||||
|
if(DetailGradient>0.75)++Result.DetailGradientOverLimit;
|
||||||
const double DetailDisplacement=std::abs(DetailSample.displacement);
|
const double DetailDisplacement=std::abs(DetailSample.displacement);
|
||||||
++Result.DetailDisplacementSamples;
|
++Result.DetailDisplacementSamples;
|
||||||
Result.MaximumDetailDisplacement=std::max(
|
Result.MaximumDetailDisplacement=std::max(
|
||||||
@@ -818,15 +840,32 @@ namespace
|
|||||||
if(NormalLength>1.0e-8)for(double& Value:Normal)Value/=NormalLength;
|
if(NormalLength>1.0e-8)for(double& Value:Normal)Value/=NormalLength;
|
||||||
const double Inward1=Shape.densityAt({Center.x-Normal[0],Center.y-Normal[1],Center.z-Normal[2]});
|
const double Inward1=Shape.densityAt({Center.x-Normal[0],Center.y-Normal[1],Center.z-Normal[2]});
|
||||||
const double Inward2=Shape.densityAt({Center.x-Normal[0]*2.0,Center.y-Normal[1]*2.0,Center.z-Normal[2]*2.0});
|
const double Inward2=Shape.densityAt({Center.x-Normal[0]*2.0,Center.y-Normal[1]*2.0,Center.z-Normal[2]*2.0});
|
||||||
if(Density>0.25)
|
double MaximumProbeDensity=Density;
|
||||||
|
const double ProbeWeights[10][3]={
|
||||||
|
{1.0,0.0,0.0},{0.0,1.0,0.0},{0.0,0.0,1.0},
|
||||||
|
{1.0/3.0,1.0/3.0,1.0/3.0},
|
||||||
|
{0.5,0.5,0.0},{0.0,0.5,0.5},{0.5,0.0,0.5},
|
||||||
|
{0.75,0.125,0.125},{0.125,0.75,0.125},{0.125,0.125,0.75}};
|
||||||
|
for(const auto& Weights:ProbeWeights)
|
||||||
|
{
|
||||||
|
const VoxelForgeCore::Vec3d Probe{
|
||||||
|
PA[0]*Weights[0]+PB[0]*Weights[1]+PC[0]*Weights[2],
|
||||||
|
PA[1]*Weights[0]+PB[1]*Weights[1]+PC[1]*Weights[2],
|
||||||
|
PA[2]*Weights[0]+PB[2]*Weights[1]+PC[2]*Weights[2]};
|
||||||
|
MaximumProbeDensity=std::max(MaximumProbeDensity,static_cast<double>(
|
||||||
|
Shape.densityAt({Probe.x*4.0,Probe.y*4.0,Probe.z*4.0})));
|
||||||
|
}
|
||||||
|
if(MaximumProbeDensity>0.25)
|
||||||
{
|
{
|
||||||
++Result.ExposedSkirtTriangles;
|
++Result.ExposedSkirtTriangles;
|
||||||
Result.MaximumAirSkirtDensity=std::max(Result.MaximumAirSkirtDensity,Density);
|
Result.MaximumAirSkirtDensity=std::max(
|
||||||
|
Result.MaximumAirSkirtDensity,MaximumProbeDensity);
|
||||||
if(AlternativeDensity<-0.25)++Result.ReverseSideSolidSkirtTriangles;
|
if(AlternativeDensity<-0.25)++Result.ReverseSideSolidSkirtTriangles;
|
||||||
if(bPrintSkirts&&Result.ExposedSkirtTriangles<=8)
|
if(bPrintSkirts&&Result.ExposedSkirtTriangles<=8)
|
||||||
std::printf("[ExposedTileSkirt] level=%d tile=(%d,%d,%d) center=(%.2f,%.2f,%.2f) density=%.3f reverse_density=%.3f inward=(%.3f,%.3f) normal=(%.3f,%.3f,%.3f)\n",
|
std::printf("[ExposedTileSkirt] level=%d tile=(%d,%d,%d) center=(%.2f,%.2f,%.2f) center_density=%.3f max_probe_density=%.3f reverse_density=%.3f inward=(%.3f,%.3f) normal=(%.3f,%.3f,%.3f)\n",
|
||||||
Tile.Key.Level,Tile.Key.Coord.X,Tile.Key.Coord.Y,Tile.Key.Coord.Z,
|
Tile.Key.Level,Tile.Key.Coord.X,Tile.Key.Coord.Y,Tile.Key.Coord.Z,
|
||||||
Center.x,Center.y,Center.z,Density,AlternativeDensity,Inward1,Inward2,
|
Center.x,Center.y,Center.z,Density,MaximumProbeDensity,
|
||||||
|
AlternativeDensity,Inward1,Inward2,
|
||||||
Normal[0],Normal[1],Normal[2]);
|
Normal[0],Normal[1],Normal[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2138,16 +2177,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
{
|
{
|
||||||
for(const FVoxelTileKey& Key:Leavers)RemoveResidentTile(Key);
|
for(const FVoxelTileKey& Key:Leavers)RemoveResidentTile(Key);
|
||||||
}
|
}
|
||||||
DesiredTiles=std::move(NewDesired.Tiles);
|
DesiredTiles=NewDesired.Tiles;
|
||||||
DesiredTileSet=std::move(NewSet);
|
DesiredTileSet=std::move(NewSet);
|
||||||
RefreshResidentSkirtMasks();
|
RefreshResidentSkirtMasks();
|
||||||
for(int32 I=0;I<=MaxLevels;++I)DesiredByLevel[I]=0;
|
for(int32 I=0;I<=MaxLevels;++I)DesiredByLevel[I]=0;
|
||||||
for(const FVoxelTileKey& Tile:DesiredTiles)++DesiredByLevel[Tile.Level];
|
for(const FVoxelTileKey& Tile:DesiredTiles)++DesiredByLevel[Tile.Level];
|
||||||
std::vector<FVoxelTileKey> ResidentKeys;ResidentKeys.reserve(ResidentTiles.size());
|
std::vector<FVoxelTileKey> ResidentKeys;ResidentKeys.reserve(ResidentTiles.size());
|
||||||
for(const auto& Pair:ResidentTiles)
|
for(const auto& Pair:ResidentTiles)
|
||||||
if(NewSet.contains(Pair.first))ResidentKeys.push_back(Pair.first);
|
if(DesiredTileSet.contains(Pair.first))ResidentKeys.push_back(Pair.first);
|
||||||
++CrossingIndex;
|
++CrossingIndex;
|
||||||
const FStandaloneStreamingUpdate Update=Streamer->UpdateDesired(CameraVoxels,ResidentKeys,CrossingIndex);
|
const FStandaloneStreamingUpdate Update=Streamer->UpdateDesiredWithSet(
|
||||||
|
std::move(NewDesired),ResidentKeys,CrossingIndex);
|
||||||
for(const auto& Pair:Update.CancelledByCrossing)
|
for(const auto& Pair:Update.CancelledByCrossing)
|
||||||
{
|
{
|
||||||
FCrossingWork& Work=CrossingWork[Pair.first];
|
FCrossingWork& Work=CrossingWork[Pair.first];
|
||||||
@@ -3525,6 +3565,10 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
MeshArtifactStats.DetailDisplacementPatches+=TileAudit.DetailDisplacementPatches;
|
MeshArtifactStats.DetailDisplacementPatches+=TileAudit.DetailDisplacementPatches;
|
||||||
MeshArtifactStats.MaximumDetailDisplacement=std::max(
|
MeshArtifactStats.MaximumDetailDisplacement=std::max(
|
||||||
MeshArtifactStats.MaximumDetailDisplacement,TileAudit.MaximumDetailDisplacement);
|
MeshArtifactStats.MaximumDetailDisplacement,TileAudit.MaximumDetailDisplacement);
|
||||||
|
MeshArtifactStats.DetailGradientSamples+=TileAudit.DetailGradientSamples;
|
||||||
|
MeshArtifactStats.DetailGradientOverLimit+=TileAudit.DetailGradientOverLimit;
|
||||||
|
MeshArtifactStats.MaximumDetailGradient=std::max(
|
||||||
|
MeshArtifactStats.MaximumDetailGradient,TileAudit.MaximumDetailGradient);
|
||||||
MeshArtifactStats.CurvedEdgeChecks+=TileAudit.CurvedEdgeChecks;
|
MeshArtifactStats.CurvedEdgeChecks+=TileAudit.CurvedEdgeChecks;
|
||||||
MeshArtifactStats.CurvedEdgeRootPatches+=TileAudit.CurvedEdgeRootPatches;
|
MeshArtifactStats.CurvedEdgeRootPatches+=TileAudit.CurvedEdgeRootPatches;
|
||||||
MeshArtifactStats.CurvedEdgeRootBadVertices+=TileAudit.CurvedEdgeRootBadVertices;
|
MeshArtifactStats.CurvedEdgeRootBadVertices+=TileAudit.CurvedEdgeRootBadVertices;
|
||||||
@@ -3690,12 +3734,25 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
MeshArtifactStats.WorstShiftExactD1,MeshArtifactStats.MaximumAbsSourceResidual,
|
MeshArtifactStats.WorstShiftExactD1,MeshArtifactStats.MaximumAbsSourceResidual,
|
||||||
MeshArtifactStats.WorstResidualPosition[0],MeshArtifactStats.WorstResidualPosition[1],
|
MeshArtifactStats.WorstResidualPosition[0],MeshArtifactStats.WorstResidualPosition[1],
|
||||||
MeshArtifactStats.WorstResidualPosition[2]);
|
MeshArtifactStats.WorstResidualPosition[2]);
|
||||||
std::printf("[MeshCurvedEdgeRootAudit] midpoint_edges=%llu bad_vertices=%llu patches=%llu max_midpoint_root_error=%.4f vox error_threshold=0.080 status=%s\n",
|
const uint64 RootBadLimit=std::max<uint64>(64,
|
||||||
|
MeshArtifactStats.CurvedEdgeChecks*35u/1000u);
|
||||||
|
const double RootBadShare=MeshArtifactStats.CurvedEdgeChecks
|
||||||
|
?static_cast<double>(MeshArtifactStats.CurvedEdgeRootBadVertices)
|
||||||
|
/MeshArtifactStats.CurvedEdgeChecks:0.0;
|
||||||
|
const bool bRootAuditPass=MeshArtifactStats.CurvedEdgeRootPatches==0
|
||||||
|
&&MeshArtifactStats.CurvedEdgeRootBadVertices<=RootBadLimit;
|
||||||
|
std::printf("[MeshCurvedEdgeRootAudit] midpoint_edges=%llu bad_vertices=%llu bad_share=%.3f%% allowed=%llu patches=%llu max_midpoint_root_error=%.4f vox error_threshold=0.080 status=%s\n",
|
||||||
static_cast<unsigned long long>(MeshArtifactStats.CurvedEdgeChecks),
|
static_cast<unsigned long long>(MeshArtifactStats.CurvedEdgeChecks),
|
||||||
static_cast<unsigned long long>(MeshArtifactStats.CurvedEdgeRootBadVertices),
|
static_cast<unsigned long long>(MeshArtifactStats.CurvedEdgeRootBadVertices),
|
||||||
|
RootBadShare*100.0,static_cast<unsigned long long>(RootBadLimit),
|
||||||
static_cast<unsigned long long>(MeshArtifactStats.CurvedEdgeRootPatches),
|
static_cast<unsigned long long>(MeshArtifactStats.CurvedEdgeRootPatches),
|
||||||
MeshArtifactStats.MaximumCurvedEdgeRootError,
|
MeshArtifactStats.MaximumCurvedEdgeRootError,
|
||||||
MeshArtifactStats.CurvedEdgeRootPatches==0?"PASS":"FAIL");
|
bRootAuditPass?"PASS":"FAIL");
|
||||||
|
std::printf("[MeshDetailGradientAudit] overlap_samples=%llu over_limit=%llu max_gradient=%.4f vox_per_vox limit=0.750 status=%s\n",
|
||||||
|
static_cast<unsigned long long>(MeshArtifactStats.DetailGradientSamples),
|
||||||
|
static_cast<unsigned long long>(MeshArtifactStats.DetailGradientOverLimit),
|
||||||
|
MeshArtifactStats.MaximumDetailGradient,
|
||||||
|
MeshArtifactStats.DetailGradientOverLimit==0?"PASS":"FAIL");
|
||||||
std::printf("[MeshSurfaceArtifactAudit] levels=0,1 tiles=%llu surface_vertices=%llu edge_vertices=%llu midpoint_band_vertices=%llu midpoint_share=%.2f%% visible_skirt_triangles=%llu hidden_same_lod_skirt_triangles=%llu exposed_skirt_triangles=%llu reverse_side_solid=%llu max_air_density=%.3f vox repeated_offset_patches=%llu field_residual_patches=%llu normal_mismatch_patches=%llu gradient_fallback_patches=%llu fallback_gradient_samples=%llu high_detail_vertices=%llu/%llu high_detail_patches=%llu max_detail_displacement=%.3f vox max_patch_dominance=%.3f status=%s\n",
|
std::printf("[MeshSurfaceArtifactAudit] levels=0,1 tiles=%llu surface_vertices=%llu edge_vertices=%llu midpoint_band_vertices=%llu midpoint_share=%.2f%% visible_skirt_triangles=%llu hidden_same_lod_skirt_triangles=%llu exposed_skirt_triangles=%llu reverse_side_solid=%llu max_air_density=%.3f vox repeated_offset_patches=%llu field_residual_patches=%llu normal_mismatch_patches=%llu gradient_fallback_patches=%llu fallback_gradient_samples=%llu high_detail_vertices=%llu/%llu high_detail_patches=%llu max_detail_displacement=%.3f vox max_patch_dominance=%.3f status=%s\n",
|
||||||
static_cast<unsigned long long>(MeshArtifactStats.Tiles),
|
static_cast<unsigned long long>(MeshArtifactStats.Tiles),
|
||||||
static_cast<unsigned long long>(MeshArtifactStats.SurfaceVertices),
|
static_cast<unsigned long long>(MeshArtifactStats.SurfaceVertices),
|
||||||
@@ -3721,7 +3778,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
&&MeshArtifactStats.NormalMismatchPatches==0
|
&&MeshArtifactStats.NormalMismatchPatches==0
|
||||||
&&MeshArtifactStats.GradientFallbackPatches==0
|
&&MeshArtifactStats.GradientFallbackPatches==0
|
||||||
&&MeshArtifactStats.DetailDisplacementPatches==0
|
&&MeshArtifactStats.DetailDisplacementPatches==0
|
||||||
&&MeshArtifactStats.CurvedEdgeRootPatches==0?"PASS":"FAIL");
|
&&bRootAuditPass
|
||||||
|
&&MeshArtifactStats.DetailGradientOverLimit==0?"PASS":"FAIL");
|
||||||
std::printf("[CoarsePanelAudit] tiles=%llu planar_open_components=%llu off_surface_panels=%llu panel_triangles=%llu max_source_error=%.3f vox status=%s\n",
|
std::printf("[CoarsePanelAudit] tiles=%llu planar_open_components=%llu off_surface_panels=%llu panel_triangles=%llu max_source_error=%.3f vox status=%s\n",
|
||||||
static_cast<unsigned long long>(CoarsePanelStats.Tiles),
|
static_cast<unsigned long long>(CoarsePanelStats.Tiles),
|
||||||
static_cast<unsigned long long>(CoarsePanelStats.PlanarOpenComponents),
|
static_cast<unsigned long long>(CoarsePanelStats.PlanarOpenComponents),
|
||||||
@@ -3775,7 +3833,10 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
if(bShardAudit&&CoreWorld&&(MeshArtifactStats.ExposedSkirtTriangles!=0
|
if(bShardAudit&&CoreWorld&&(MeshArtifactStats.ExposedSkirtTriangles!=0
|
||||||
||MeshArtifactStats.RepeatedOffsetPatches!=0||MeshArtifactStats.FieldMismatchPatches!=0
|
||MeshArtifactStats.RepeatedOffsetPatches!=0||MeshArtifactStats.FieldMismatchPatches!=0
|
||||||
||MeshArtifactStats.NormalMismatchPatches!=0||MeshArtifactStats.GradientFallbackPatches!=0
|
||MeshArtifactStats.NormalMismatchPatches!=0||MeshArtifactStats.GradientFallbackPatches!=0
|
||||||
||MeshArtifactStats.CurvedEdgeRootPatches!=0))return 13;
|
||MeshArtifactStats.CurvedEdgeRootPatches!=0
|
||||||
|
||MeshArtifactStats.CurvedEdgeRootBadVertices>std::max<uint64>(
|
||||||
|
64,MeshArtifactStats.CurvedEdgeChecks*35u/1000u)
|
||||||
|
||MeshArtifactStats.DetailGradientOverLimit!=0))return 13;
|
||||||
if(bShardAudit&&CoreWorld&&CoarsePanelStats.OffSurfacePanels!=0)return 13;
|
if(bShardAudit&&CoreWorld&&CoarsePanelStats.OffSurfacePanels!=0)return 13;
|
||||||
if(FarAudit)
|
if(FarAudit)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -483,6 +483,13 @@ public:
|
|||||||
return UpdateDesiredSet(std::move(NewDesired),ResidentKeys,CrossingIndex);
|
return UpdateDesiredSet(std::move(NewDesired),ResidentKeys,CrossingIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FStandaloneStreamingUpdate UpdateDesiredWithSet(
|
||||||
|
FStandaloneDesiredTileSet NewDesired,const std::vector<FVoxelTileKey>& ResidentKeys,
|
||||||
|
uint64 CrossingIndex=0)
|
||||||
|
{
|
||||||
|
return UpdateDesiredSet(std::move(NewDesired),ResidentKeys,CrossingIndex);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<FStandaloneCpuTile> TakeReady()
|
std::vector<FStandaloneCpuTile> TakeReady()
|
||||||
{
|
{
|
||||||
std::vector<FStandaloneCpuTile> Out;
|
std::vector<FStandaloneCpuTile> Out;
|
||||||
|
|||||||
@@ -25,6 +25,18 @@ namespace
|
|||||||
GVoxelForgeUseOperatorBlock,
|
GVoxelForgeUseOperatorBlock,
|
||||||
TEXT("Use the operator-major density block path for non-shared mesher grids."));
|
TEXT("Use the operator-major density block path for non-shared mesher grids."));
|
||||||
bool GVoxelForgeBlockSwitchParsed = false;
|
bool GVoxelForgeBlockSwitchParsed = false;
|
||||||
|
int32 GVoxelForgeRawSkirtsForAudit = 0;
|
||||||
|
bool GVoxelForgeRawSkirtsSwitchParsed = false;
|
||||||
|
|
||||||
|
void VF_ParseRawSkirtsAuditSwitch()
|
||||||
|
{
|
||||||
|
if(GVoxelForgeRawSkirtsSwitchParsed)return;
|
||||||
|
GVoxelForgeRawSkirtsSwitchParsed=true;
|
||||||
|
GVoxelForgeRawSkirtsForAudit=FString(FCommandLine::Get()).Contains(
|
||||||
|
TEXT("--audit-raw-skirts"))?1:0;
|
||||||
|
if(GVoxelForgeRawSkirtsForAudit)
|
||||||
|
std::printf("[SkirtAuditControl] raw skirt curtains enabled for negative-control scan\n");
|
||||||
|
}
|
||||||
|
|
||||||
int32 GVoxelForgeTilePostReachDebug = 0;
|
int32 GVoxelForgeTilePostReachDebug = 0;
|
||||||
FAutoConsoleVariableRef CVarVoxelForgeTilePostReachDebug(
|
FAutoConsoleVariableRef CVarVoxelForgeTilePostReachDebug(
|
||||||
@@ -93,6 +105,7 @@ void UVoxelMarchingCubesMesher::InitializeConsoleSwitches()
|
|||||||
{
|
{
|
||||||
VF_ParseOperatorBlockSwitch();
|
VF_ParseOperatorBlockSwitch();
|
||||||
VF_ParseTilePostReachSwitches();
|
VF_ParseTilePostReachSwitches();
|
||||||
|
VF_ParseRawSkirtsAuditSwitch();
|
||||||
}
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
@@ -562,7 +575,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
|||||||
VoxelDensityProfile::RecordClassifierCall(bSampleClassifier);
|
VoxelDensityProfile::RecordClassifierCall(bSampleClassifier);
|
||||||
}
|
}
|
||||||
EVoxelTileClass Verdict;
|
EVoxelTileClass Verdict;
|
||||||
if(ShapeWorldContext)
|
if(ShapeWorldContext&&Step<=2)
|
||||||
{
|
{
|
||||||
const auto ShapeVerdict=ShapeWorldContext->ClassifyBox(
|
const auto ShapeVerdict=ShapeWorldContext->ClassifyBox(
|
||||||
BlockOrigin,Step,BlockCells);
|
BlockOrigin,Step,BlockCells);
|
||||||
@@ -1631,6 +1644,23 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
|||||||
return Idx;
|
return Idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<float> SurfaceVertexDensitySamples;
|
||||||
|
if(ShapeWorldContext)
|
||||||
|
SurfaceVertexDensitySamples.assign(static_cast<size_t>(MeshData.Vertices.Num()),
|
||||||
|
std::numeric_limits<float>::quiet_NaN());
|
||||||
|
const auto SampleSurfaceVertexDensity=[&](int32 OriginalIndex,const FVector& Position)
|
||||||
|
{
|
||||||
|
if(!ShapeWorldContext)return 0.0f;
|
||||||
|
if(OriginalIndex<0||static_cast<size_t>(OriginalIndex)>=SurfaceVertexDensitySamples.size())
|
||||||
|
return ShapeWorldContext->DensityAt(Position.X/VOXEL_SIZE,
|
||||||
|
Position.Y/VOXEL_SIZE,Position.Z/VOXEL_SIZE);
|
||||||
|
float& Density=SurfaceVertexDensitySamples[static_cast<size_t>(OriginalIndex)];
|
||||||
|
if(std::isnan(Density))
|
||||||
|
Density=ShapeWorldContext->DensityAt(Position.X/VOXEL_SIZE,
|
||||||
|
Position.Y/VOXEL_SIZE,Position.Z/VOXEL_SIZE);
|
||||||
|
return Density;
|
||||||
|
};
|
||||||
|
|
||||||
// F17 — jupes émises PAR SEAU : chaque jupe hérite la classe (donc le polygroup /
|
// F17 — jupes émises PAR SEAU : chaque jupe hérite la classe (donc le polygroup /
|
||||||
// matériau) de son triangle source ; ajouter les jupes après coup casserait les runs
|
// matériau) de son triangle source ; ajouter les jupes après coup casserait les runs
|
||||||
// d'indices contigus par groupe qu'exige RMC.
|
// d'indices contigus par groupe qu'exige RMC.
|
||||||
@@ -1655,7 +1685,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
|||||||
Position.Y/VOXEL_SIZE,Position.Z/VOXEL_SIZE);
|
Position.Y/VOXEL_SIZE,Position.Z/VOXEL_SIZE);
|
||||||
return Vertex;
|
return Vertex;
|
||||||
};
|
};
|
||||||
const auto InterpolateVertex=[](const FSkirtVertex& A,const FSkirtVertex& B,float T)
|
const auto InterpolateVertex=[&](const FSkirtVertex& A,const FSkirtVertex& B,float T)
|
||||||
{
|
{
|
||||||
if(T<=1.0e-6f)return A;
|
if(T<=1.0e-6f)return A;
|
||||||
if(T>=1.0f-1.0e-6f)return B;
|
if(T>=1.0f-1.0e-6f)return B;
|
||||||
@@ -1667,13 +1697,31 @@ 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.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.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)));
|
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=FMath::Lerp(A.Density,B.Density,T);
|
Out.Density=ShapeWorldContext
|
||||||
|
?ShapeWorldContext->DensityAt(Out.Position.X/VOXEL_SIZE,
|
||||||
|
Out.Position.Y/VOXEL_SIZE,Out.Position.Z/VOXEL_SIZE)
|
||||||
|
:FMath::Lerp(A.Density,B.Density,T);
|
||||||
Out.OriginalIndex=INDEX_NONE;
|
Out.OriginalIndex=INDEX_NONE;
|
||||||
return Out;
|
return Out;
|
||||||
};
|
};
|
||||||
const auto EmitClippedTriangle=[&](const FSkirtVertex& A,const FSkirtVertex& B,
|
const auto EmitClippedTriangle=[&](auto&& Self,const FSkirtVertex& A,
|
||||||
const FSkirtVertex& C,uint8 FaceMask)
|
const FSkirtVertex& B,const FSkirtVertex& C,uint8 FaceMask,
|
||||||
|
int32 RefinementDepth)->void
|
||||||
{
|
{
|
||||||
|
if(GVoxelForgeRawSkirtsForAudit)
|
||||||
|
{
|
||||||
|
const FSkirtVertex Input[3]={A,B,C};
|
||||||
|
int32 Indices[3];
|
||||||
|
for(int32 Vertex=0;Vertex<3;++Vertex)
|
||||||
|
Indices[Vertex]=Input[Vertex].OriginalIndex>=0
|
||||||
|
?Input[Vertex].OriginalIndex
|
||||||
|
:AddSkirtVert(Input[Vertex].Position,Input[Vertex].Normal,
|
||||||
|
Input[Vertex].Color);
|
||||||
|
Tris.Add(Indices[0]);Tris.Add(Indices[1]);Tris.Add(Indices[2]);
|
||||||
|
Tris.Add(Indices[0]);Tris.Add(Indices[2]);Tris.Add(Indices[1]);
|
||||||
|
FaceMasks.Add(FaceMask);FaceMasks.Add(FaceMask);
|
||||||
|
return;
|
||||||
|
}
|
||||||
FSkirtVertex Input[3]={A,B,C};
|
FSkirtVertex Input[3]={A,B,C};
|
||||||
FSkirtVertex Polygon[5];
|
FSkirtVertex Polygon[5];
|
||||||
int32 PolygonCount=0;
|
int32 PolygonCount=0;
|
||||||
@@ -1704,7 +1752,56 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
|||||||
const FSkirtVertex& P0=Polygon[0];
|
const FSkirtVertex& P0=Polygon[0];
|
||||||
const FSkirtVertex& P1=Polygon[Fan];
|
const FSkirtVertex& P1=Polygon[Fan];
|
||||||
const FSkirtVertex& P2=Polygon[Fan+1];
|
const FSkirtVertex& P2=Polygon[Fan+1];
|
||||||
if(ShapeWorldContext)
|
if(ShapeWorldContext&&Step<=2)
|
||||||
|
{
|
||||||
|
const auto ProbeDensity=[&](double WA,double WB,double WC)
|
||||||
|
{
|
||||||
|
const FVector Position=P0.Position*static_cast<float>(WA)
|
||||||
|
+P1.Position*static_cast<float>(WB)
|
||||||
|
+P2.Position*static_cast<float>(WC);
|
||||||
|
return ShapeWorldContext->DensityAt(Position.X/VOXEL_SIZE,
|
||||||
|
Position.Y/VOXEL_SIZE,Position.Z/VOXEL_SIZE);
|
||||||
|
};
|
||||||
|
const auto VertexProbeDensity=[&](const FSkirtVertex& Vertex)
|
||||||
|
{
|
||||||
|
return Vertex.OriginalIndex>=0
|
||||||
|
?SampleSurfaceVertexDensity(Vertex.OriginalIndex,Vertex.Position)
|
||||||
|
:Vertex.Density;
|
||||||
|
};
|
||||||
|
float MaximumProbeDensity=FMath::Max(VertexProbeDensity(P0),
|
||||||
|
FMath::Max(VertexProbeDensity(P1),VertexProbeDensity(P2)));
|
||||||
|
const double ProbeWeights[4][3]={
|
||||||
|
{1.0/3.0,1.0/3.0,1.0/3.0},
|
||||||
|
{0.5,0.5,0.0},{0.0,0.5,0.5},{0.5,0.0,0.5}};
|
||||||
|
const int32 ProbeCount=RefinementDepth==0?4:1;
|
||||||
|
for(int32 Probe=0;Probe<ProbeCount;++Probe)
|
||||||
|
{
|
||||||
|
MaximumProbeDensity=FMath::Max(MaximumProbeDensity,
|
||||||
|
ProbeDensity(ProbeWeights[Probe][0],
|
||||||
|
ProbeWeights[Probe][1],ProbeWeights[Probe][2]));
|
||||||
|
if(MaximumProbeDensity>0.25f)break;
|
||||||
|
}
|
||||||
|
if(MaximumProbeDensity>0.25f)
|
||||||
|
{
|
||||||
|
if(RefinementDepth>=1)continue;
|
||||||
|
const auto MakeMidpoint=[&](const FSkirtVertex& X,
|
||||||
|
const FSkirtVertex& Y)
|
||||||
|
{
|
||||||
|
FSkirtVertex Mid=InterpolateVertex(X,Y,0.5f);
|
||||||
|
Mid.OriginalIndex=INDEX_NONE;
|
||||||
|
return Mid;
|
||||||
|
};
|
||||||
|
const FSkirtVertex M01=MakeMidpoint(P0,P1);
|
||||||
|
const FSkirtVertex M12=MakeMidpoint(P1,P2);
|
||||||
|
const FSkirtVertex M20=MakeMidpoint(P2,P0);
|
||||||
|
Self(Self,P0,M01,M20,FaceMask,RefinementDepth+1);
|
||||||
|
Self(Self,M01,P1,M12,FaceMask,RefinementDepth+1);
|
||||||
|
Self(Self,M20,M12,P2,FaceMask,RefinementDepth+1);
|
||||||
|
Self(Self,M01,M12,M20,FaceMask,RefinementDepth+1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(ShapeWorldContext)
|
||||||
{
|
{
|
||||||
const FVector Center=(P0.Position+P1.Position+P2.Position)
|
const FVector Center=(P0.Position+P1.Position+P2.Position)
|
||||||
/(3.0f*VOXEL_SIZE);
|
/(3.0f*VOXEL_SIZE);
|
||||||
@@ -1739,8 +1836,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
|||||||
// Clip both source triangles against the solid half-space before emitting
|
// Clip both source triangles against the solid half-space before emitting
|
||||||
// each orientation. A curtain that points into tunnel air must not become
|
// each orientation. A curtain that points into tunnel air must not become
|
||||||
// visible geometry beyond the sampled isosurface.
|
// visible geometry beyond the sampled isosurface.
|
||||||
EmitClippedTriangle(A,B,B2,FaceMask);
|
EmitClippedTriangle(EmitClippedTriangle,A,B,B2,FaceMask,0);
|
||||||
EmitClippedTriangle(A,B2,A2,FaceMask);
|
EmitClippedTriangle(EmitClippedTriangle,A,B2,A2,FaceMask,0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MeshData.NumSkirtTriangles += (Tris.Num() - BaseTriNum) / 3;
|
MeshData.NumSkirtTriangles += (Tris.Num() - BaseTriNum) / 3;
|
||||||
|
|||||||
Reference in new issue
Block a user