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)
|
||||
{
|
||||
const float Cave=std::clamp(CaveInfluence,0.0f,1.0f);
|
||||
const float Floor=std::clamp(FloorInfluence,0.0f,1.0f);
|
||||
return 1.35f-0.80f*Cave+1.85f*Floor;
|
||||
const float Floor=std::min(Cave,std::clamp(FloorInfluence,0.0f,1.0f));
|
||||
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 float DetailBandFade(float SignedDistance,float Edge)
|
||||
@@ -3883,8 +3905,8 @@ private:
|
||||
if(Field)
|
||||
OwnerRelief=OwnerCaveRoughnessForDetail(X,Y,Z,SurfaceSDF,*Field,
|
||||
CaveInfluence,PassageWeight,OwnerNoise);
|
||||
const float Contrast=DetailContrast(CaveInfluence,FloorInfluence);
|
||||
const float Detail=std::clamp((Relief+OwnerRelief*0.5f)*Contrast,-3.9f,3.9f);
|
||||
const float Detail=std::clamp(BlendDetailFamilies(Relief,OwnerRelief,
|
||||
CaveInfluence,FloorInfluence,PassageWeight),-3.9f,3.9f);
|
||||
return Detail;
|
||||
}
|
||||
|
||||
@@ -3920,8 +3942,8 @@ private:
|
||||
if(Profile)
|
||||
Profile->OwnerRoughnessSampleNanoseconds+=static_cast<uint64>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-OwnerStart).count());
|
||||
const float Contrast=DetailContrast(CaveInfluence,FloorInfluence);
|
||||
const float Detail=std::clamp((Relief+OwnerRelief*0.5f)*Contrast,-3.9f,3.9f);
|
||||
const float Detail=std::clamp(BlendDetailFamilies(Relief,OwnerRelief,
|
||||
CaveInfluence,FloorInfluence,PassageWeight),-3.9f,3.9f);
|
||||
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,
|
||||
NormalMismatchPatches=0,GradientFallbackPatches=0,GradientFallbackSamples=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 MaximumDetailDisplacement=0.0;
|
||||
double MaximumCurvedEdgeRootError=0.0;
|
||||
double MaximumDetailGradient=0.0;
|
||||
uint64 FieldEdgeChecks=0,AnalyticCrossingMisses=0,OldInt8RangeEdges=0,NormalChecks=0;
|
||||
double SumAbsSourceResidual=0.0,MaximumAbsSourceResidual=0.0,
|
||||
SumAnalyticEdgeShiftVoxels=0.0,MaximumAnalyticEdgeShiftVoxels=0.0,
|
||||
@@ -173,6 +175,26 @@ namespace
|
||||
{P[0],P[1],P[2]});
|
||||
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);
|
||||
++Result.DetailDisplacementSamples;
|
||||
Result.MaximumDetailDisplacement=std::max(
|
||||
@@ -818,15 +840,32 @@ namespace
|
||||
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 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.MaximumAirSkirtDensity=std::max(Result.MaximumAirSkirtDensity,Density);
|
||||
Result.MaximumAirSkirtDensity=std::max(
|
||||
Result.MaximumAirSkirtDensity,MaximumProbeDensity);
|
||||
if(AlternativeDensity<-0.25)++Result.ReverseSideSolidSkirtTriangles;
|
||||
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,
|
||||
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]);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
DesiredTiles=std::move(NewDesired.Tiles);
|
||||
DesiredTiles=NewDesired.Tiles;
|
||||
DesiredTileSet=std::move(NewSet);
|
||||
RefreshResidentSkirtMasks();
|
||||
for(int32 I=0;I<=MaxLevels;++I)DesiredByLevel[I]=0;
|
||||
for(const FVoxelTileKey& Tile:DesiredTiles)++DesiredByLevel[Tile.Level];
|
||||
std::vector<FVoxelTileKey> ResidentKeys;ResidentKeys.reserve(ResidentTiles.size());
|
||||
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;
|
||||
const FStandaloneStreamingUpdate Update=Streamer->UpdateDesired(CameraVoxels,ResidentKeys,CrossingIndex);
|
||||
const FStandaloneStreamingUpdate Update=Streamer->UpdateDesiredWithSet(
|
||||
std::move(NewDesired),ResidentKeys,CrossingIndex);
|
||||
for(const auto& Pair:Update.CancelledByCrossing)
|
||||
{
|
||||
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.MaximumDetailDisplacement=std::max(
|
||||
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.CurvedEdgeRootPatches+=TileAudit.CurvedEdgeRootPatches;
|
||||
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.WorstResidualPosition[0],MeshArtifactStats.WorstResidualPosition[1],
|
||||
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.CurvedEdgeRootBadVertices),
|
||||
RootBadShare*100.0,static_cast<unsigned long long>(RootBadLimit),
|
||||
static_cast<unsigned long long>(MeshArtifactStats.CurvedEdgeRootPatches),
|
||||
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",
|
||||
static_cast<unsigned long long>(MeshArtifactStats.Tiles),
|
||||
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.GradientFallbackPatches==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",
|
||||
static_cast<unsigned long long>(CoarsePanelStats.Tiles),
|
||||
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
|
||||
||MeshArtifactStats.RepeatedOffsetPatches!=0||MeshArtifactStats.FieldMismatchPatches!=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(FarAudit)
|
||||
{
|
||||
|
||||
@@ -483,6 +483,13 @@ public:
|
||||
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> Out;
|
||||
|
||||
@@ -25,6 +25,18 @@ namespace
|
||||
GVoxelForgeUseOperatorBlock,
|
||||
TEXT("Use the operator-major density block path for non-shared mesher grids."));
|
||||
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;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeTilePostReachDebug(
|
||||
@@ -93,6 +105,7 @@ void UVoxelMarchingCubesMesher::InitializeConsoleSwitches()
|
||||
{
|
||||
VF_ParseOperatorBlockSwitch();
|
||||
VF_ParseTilePostReachSwitches();
|
||||
VF_ParseRawSkirtsAuditSwitch();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
@@ -562,7 +575,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
VoxelDensityProfile::RecordClassifierCall(bSampleClassifier);
|
||||
}
|
||||
EVoxelTileClass Verdict;
|
||||
if(ShapeWorldContext)
|
||||
if(ShapeWorldContext&&Step<=2)
|
||||
{
|
||||
const auto ShapeVerdict=ShapeWorldContext->ClassifyBox(
|
||||
BlockOrigin,Step,BlockCells);
|
||||
@@ -1631,6 +1644,23 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
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 /
|
||||
// matériau) de son triangle source ; ajouter les jupes après coup casserait les runs
|
||||
// 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);
|
||||
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.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.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=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;
|
||||
return Out;
|
||||
};
|
||||
const auto EmitClippedTriangle=[&](const FSkirtVertex& A,const FSkirtVertex& B,
|
||||
const FSkirtVertex& C,uint8 FaceMask)
|
||||
const auto EmitClippedTriangle=[&](auto&& Self,const FSkirtVertex& A,
|
||||
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 Polygon[5];
|
||||
int32 PolygonCount=0;
|
||||
@@ -1704,7 +1752,56 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
const FSkirtVertex& P0=Polygon[0];
|
||||
const FSkirtVertex& P1=Polygon[Fan];
|
||||
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)
|
||||
/(3.0f*VOXEL_SIZE);
|
||||
@@ -1739,8 +1836,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
// Clip both source triangles against the solid half-space before emitting
|
||||
// each orientation. A curtain that points into tunnel air must not become
|
||||
// visible geometry beyond the sampled isosurface.
|
||||
EmitClippedTriangle(A,B,B2,FaceMask);
|
||||
EmitClippedTriangle(A,B2,A2,FaceMask);
|
||||
EmitClippedTriangle(EmitClippedTriangle,A,B,B2,FaceMask,0);
|
||||
EmitClippedTriangle(EmitClippedTriangle,A,B2,A2,FaceMask,0);
|
||||
}
|
||||
}
|
||||
MeshData.NumSkirtTriangles += (Tris.Num() - BaseTriNum) / 3;
|
||||
|
||||
Reference in new issue
Block a user