perf(generation): per-tile reach test skips landing and structural posts -- ~12% less worker CPU
The remove-one ablation put landing posts at ~10% and passage structural posts at ~6% of worker time, though landings exist only near passage ends and the origin. The parked exact-cull round's per-SAMPLE post bounds had been a net loss. This round decides reach once per TILE (Codex; reviewed by me). - VoxelTilePostReach.h: one per-tile reach mask (thread-local byte), set when the tile is set up. The origin landing, passage landing SDF/floors, native floors, tunnel air and passage structural posts return their input unchanged on tiles no instance can reach. Per sample this costs an inline flag read. - Reach proofs come from each post's geometry (origin rooms, landing SDF and floors, native floor band, structural support spheres), not from comments. - voxel.TilePostReachDebug=1 evaluates the skipped posts anyway and compares the FINAL field; voxel.TilePostReachScale=0.5 deliberately shrinks the reach and must fail. Default build: 694 tiles, landing reachable on 4 (skipped on 690), structural posts skipped on 169. Soundness: 123,958,673 comparisons, 0 differences; shrunk reach: 13,929 differences, FAIL as required. Final DEFAULT build vs the committed-tip DLL (same command line, two interleaved runs each): static worker 34.80 -> 30.52 s (-12%) LOD0 generation p50/p95 67.0/107.6 -> 55.8/95.8 ms moving worker 75.96 -> 66.66 s (-12%) LOD0 generation p50/p95 66.3/106.0 -> 56.0/95.8 ms Field exact: 841/841 keyed tiles vs a fresh committed-tip trace (669,902 triangles, which includes 9260775's gap floors), export b3e5f4c3..9b377, capability 20,830 / 10,909; strate-crossing and sealed-proof tests pass. Ledger: all launches passed or failed where designed to; early raw-SDF debug attempts failed until the comparator was corrected to compare the final field. No crash folders. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TaFdXhTdYMkW1xrV6ApT6Y
This commit is contained in:
1 parent
92607751b1
commit
ce4d8783e9
7 files changed
+785
-15
No files matched your search
@@ -4,6 +4,8 @@
|
|||||||
#include "VoxelMarchingCubesMesher.h"
|
#include "VoxelMarchingCubesMesher.h"
|
||||||
#include "MarchingCubesTables.h"
|
#include "MarchingCubesTables.h"
|
||||||
#include "VoxelDensityProfile.h"
|
#include "VoxelDensityProfile.h"
|
||||||
|
#include "VoxelStrateManager.h"
|
||||||
|
#include "VoxelTilePostReach.h"
|
||||||
#include "ProfilingDebugging/CpuProfilerTrace.h"
|
#include "ProfilingDebugging/CpuProfilerTrace.h"
|
||||||
#include "HAL/IConsoleManager.h"
|
#include "HAL/IConsoleManager.h"
|
||||||
#include "Misc/CommandLine.h"
|
#include "Misc/CommandLine.h"
|
||||||
@@ -17,6 +19,45 @@ namespace
|
|||||||
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 GVoxelForgeTilePostReachDebug = 0;
|
||||||
|
FAutoConsoleVariableRef CVarVoxelForgeTilePostReachDebug(
|
||||||
|
TEXT("voxel.TilePostReachDebug"),
|
||||||
|
GVoxelForgeTilePostReachDebug,
|
||||||
|
TEXT("Run skipped landing/structural posts in a bypass and compare their exact result; "
|
||||||
|
"a nonzero difference fails the debug run."));
|
||||||
|
|
||||||
|
float GVoxelForgeTilePostReachScale = 1.0f;
|
||||||
|
FAutoConsoleVariableRef CVarVoxelForgeTilePostReachScale(
|
||||||
|
TEXT("voxel.TilePostReachScale"),
|
||||||
|
GVoxelForgeTilePostReachScale,
|
||||||
|
TEXT("Scale the per-tile post reach for deliberate soundness tests. Default 1.0 is exact; "
|
||||||
|
"values below 1 intentionally shrink the guard."));
|
||||||
|
|
||||||
|
bool GVoxelForgeTilePostReachSwitchesParsed = false;
|
||||||
|
|
||||||
|
void VF_ParseTilePostReachSwitches()
|
||||||
|
{
|
||||||
|
if (GVoxelForgeTilePostReachSwitchesParsed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GVoxelForgeTilePostReachSwitchesParsed = true;
|
||||||
|
int32 DebugValue = GVoxelForgeTilePostReachDebug;
|
||||||
|
float ScaleValue = GVoxelForgeTilePostReachScale;
|
||||||
|
FParse::Value(
|
||||||
|
FCommandLine::Get(), TEXT("voxel.TilePostReachDebug="), DebugValue);
|
||||||
|
FParse::Value(
|
||||||
|
FCommandLine::Get(), TEXT("voxel.TilePostReachScale="), ScaleValue);
|
||||||
|
GVoxelForgeTilePostReachDebug = DebugValue != 0 ? 1 : 0;
|
||||||
|
if (!VoxelMath::IsFinite(ScaleValue) || ScaleValue <= 0.0f)
|
||||||
|
{
|
||||||
|
ScaleValue = 1.0f;
|
||||||
|
}
|
||||||
|
GVoxelForgeTilePostReachScale = FMath::Min(ScaleValue, 1.0f);
|
||||||
|
VoxelGenLOD::GTilePostReachDebugEnabled.store(
|
||||||
|
GVoxelForgeTilePostReachDebug != 0, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
void VF_ParseOperatorBlockSwitch()
|
void VF_ParseOperatorBlockSwitch()
|
||||||
{
|
{
|
||||||
if (GVoxelForgeBlockSwitchParsed) { return; }
|
if (GVoxelForgeBlockSwitchParsed) { return; }
|
||||||
@@ -42,6 +83,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
|||||||
{
|
{
|
||||||
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_MesherGenerateMesh);
|
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_MesherGenerateMesh);
|
||||||
VF_ParseOperatorBlockSwitch();
|
VF_ParseOperatorBlockSwitch();
|
||||||
|
VF_ParseTilePostReachSwitches();
|
||||||
FVoxelMeshData MeshData;
|
FVoxelMeshData MeshData;
|
||||||
if (OutCaptureGrid) { OutCaptureGrid->Reset(); }
|
if (OutCaptureGrid) { OutCaptureGrid->Reset(); }
|
||||||
if (OutDensitySampleCount) { *OutDensitySampleCount = 0; }
|
if (OutDensitySampleCount) { *OutDensitySampleCount = 0; }
|
||||||
@@ -233,6 +275,49 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
|||||||
VoxelGenLOD::TileOriginVoxels, OriginVoxels);
|
VoxelGenLOD::TileOriginVoxels, OriginVoxels);
|
||||||
TGuardValue<int32> TileCellsGuard(
|
TGuardValue<int32> TileCellsGuard(
|
||||||
VoxelGenLOD::TileCellsPerAxis, CellsPerAxis);
|
VoxelGenLOD::TileCellsPerAxis, CellsPerAxis);
|
||||||
|
|
||||||
|
// Every density sample in this tile sees the same post-reach decision. The one-point halo
|
||||||
|
// matches the actual grid fill below (gx/gy/gz = -1..GridDim), so a false bit proves that no
|
||||||
|
// landing or passage structural writer can reach any sample used by meshing or gradients.
|
||||||
|
TGuardValue<bool> TilePostReachDebugGuard(
|
||||||
|
VoxelGenLOD::bTilePostReachDebug,
|
||||||
|
GVoxelForgeTilePostReachDebug != 0);
|
||||||
|
TGuardValue<bool> TilePostReachBypassGuard(
|
||||||
|
VoxelGenLOD::bTilePostReachBypass, false);
|
||||||
|
uint8 TilePostReachFlags = VoxelGenLOD::AllTilePostReach;
|
||||||
|
if (Generator->StrateManager != nullptr)
|
||||||
|
{
|
||||||
|
const int64 Extent = static_cast<int64>(CellsPerAxis + 1) * Step;
|
||||||
|
const FBox LatticeWithHalo(
|
||||||
|
FVector(
|
||||||
|
static_cast<float>(static_cast<int64>(OriginVoxels.X) - Step),
|
||||||
|
static_cast<float>(static_cast<int64>(OriginVoxels.Y) - Step),
|
||||||
|
static_cast<float>(static_cast<int64>(OriginVoxels.Z) - Step)),
|
||||||
|
FVector(
|
||||||
|
static_cast<float>(static_cast<int64>(OriginVoxels.X) + Extent),
|
||||||
|
static_cast<float>(static_cast<int64>(OriginVoxels.Y) + Extent),
|
||||||
|
static_cast<float>(static_cast<int64>(OriginVoxels.Z) + Extent)));
|
||||||
|
const float ReachScale = GVoxelForgeTilePostReachScale;
|
||||||
|
TilePostReachFlags = 0;
|
||||||
|
if (Generator->StrateManager->AnyOriginLandingNearLattice(
|
||||||
|
LatticeWithHalo, OriginVoxels, Step, ReachScale))
|
||||||
|
{
|
||||||
|
TilePostReachFlags |= VoxelGenLOD::OriginLandingReachable;
|
||||||
|
}
|
||||||
|
if (Generator->StrateManager->AnyPassageLandingNearLattice(
|
||||||
|
LatticeWithHalo, OriginVoxels, Step, ReachScale))
|
||||||
|
{
|
||||||
|
TilePostReachFlags |= VoxelGenLOD::PassageLandingReachable;
|
||||||
|
}
|
||||||
|
if (Generator->StrateManager->AnyPassageStructuralPostNearLattice(
|
||||||
|
LatticeWithHalo, OriginVoxels, Step, ReachScale))
|
||||||
|
{
|
||||||
|
TilePostReachFlags |= VoxelGenLOD::PassageStructuralReachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TGuardValue<uint8> TilePostReachFlagsGuard(
|
||||||
|
VoxelGenLOD::TilePostReachFlags, TilePostReachFlags);
|
||||||
|
VoxelGenLOD::RecordTileReachMask(TilePostReachFlags);
|
||||||
// COUPE DE CONTENU PAR STRATE — restreint le maillage (et l'échantillonnage) aux cellules
|
// COUPE DE CONTENU PAR STRATE — restreint le maillage (et l'échantillonnage) aux cellules
|
||||||
// dont l'intervalle Z chevauche la bande [BandZMinVox, BandZMaxVox] (voxels inclusifs).
|
// dont l'intervalle Z chevauche la bande [BandZMinVox, BandZMaxVox] (voxels inclusifs).
|
||||||
// Les tuiles à capture ne sont jamais bandées (niveau 0 — garde-fou ci-dessous).
|
// Les tuiles à capture ne sont jamais bandées (niveau 0 — garde-fou ci-dessous).
|
||||||
|
|||||||
@@ -2511,6 +2511,12 @@ float UVoxelStrateManager::EvaluateModifierSDF(float WorldX, float WorldY, float
|
|||||||
return FLT_MAX;
|
return FLT_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The landing terms are part of the passage modifier, so their reach proof is deliberately
|
||||||
|
// wider than the direct post's four-voxel blend: SmoothMin can lower a finite term by K/6 at
|
||||||
|
// each later combine. Outside that padded room domain, omitting the two landing evaluations
|
||||||
|
// is an identity for VF_ApplyPassageCarving.
|
||||||
|
const bool bEvaluateLandingSDF = VoxelGenLOD::IsPassageLandingReachable()
|
||||||
|
|| VoxelGenLOD::IsTilePostReachBypassActive();
|
||||||
const FIntVector ChunkCoord(
|
const FIntVector ChunkCoord(
|
||||||
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
|
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
|
||||||
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
|
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
|
||||||
@@ -2602,11 +2608,14 @@ float UVoxelStrateManager::EvaluateModifierSDF(float WorldX, float WorldY, float
|
|||||||
// A landing is a real room with a hard flat-floor half-space, not a sphere around the
|
// A landing is a real room with a hard flat-floor half-space, not a sphere around the
|
||||||
// tube endpoint. It is the same fixed SDF geometry selected during GeneratePassages; it
|
// tube endpoint. It is the same fixed SDF geometry selected during GeneratePassages; it
|
||||||
// never performs a source query here and does not synthesize a radial root connector.
|
// never performs a source query here and does not synthesize a radial root connector.
|
||||||
|
if (bEvaluateLandingSDF)
|
||||||
|
{
|
||||||
const float UpperLandingSDF = VF_EvaluatePassageLandingSDF(Pos, P.UpperLanding);
|
const float UpperLandingSDF = VF_EvaluatePassageLandingSDF(Pos, P.UpperLanding);
|
||||||
const float LowerLandingSDF = VF_EvaluatePassageLandingSDF(Pos, P.LowerLanding);
|
const float LowerLandingSDF = VF_EvaluatePassageLandingSDF(Pos, P.LowerLanding);
|
||||||
MinSDF = VoxelSDF::SmoothMin(MinSDF, UpperLandingSDF, BlendK);
|
MinSDF = VoxelSDF::SmoothMin(MinSDF, UpperLandingSDF, BlendK);
|
||||||
MinSDF = VoxelSDF::SmoothMin(MinSDF, LowerLandingSDF, BlendK);
|
MinSDF = VoxelSDF::SmoothMin(MinSDF, LowerLandingSDF, BlendK);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return MinSDF;
|
return MinSDF;
|
||||||
}
|
}
|
||||||
@@ -2617,13 +2626,41 @@ void UVoxelStrateManager::ApplyPassageModifier(
|
|||||||
{
|
{
|
||||||
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
||||||
VoxelDensityProfile::EBucket::PassageModifier);
|
VoxelDensityProfile::EBucket::PassageModifier);
|
||||||
|
const bool bBypass = VoxelGenLOD::IsTilePostReachBypassActive();
|
||||||
|
const bool bCompareLandingModifier = VoxelGenLOD::bTilePostReachDebug
|
||||||
|
&& !VoxelGenLOD::IsPassageLandingReachable() && !bBypass;
|
||||||
|
float CanonicalModifierDensity = Density;
|
||||||
|
if (bCompareLandingModifier)
|
||||||
|
{
|
||||||
|
const bool PreviousBypass = VoxelGenLOD::bTilePostReachBypass;
|
||||||
|
VoxelGenLOD::bTilePostReachBypass = true;
|
||||||
|
const float CanonicalSDF = EvaluateModifierSDF(WorldX, WorldY, WorldZ);
|
||||||
|
VF_ApplyPassageCarving(
|
||||||
|
CanonicalModifierDensity, CanonicalSDF, BaseDensity, SealThickness);
|
||||||
|
VoxelGenLOD::bTilePostReachBypass = PreviousBypass;
|
||||||
|
}
|
||||||
if (!VoxelDensityAblation::IsPassageCarvingOff())
|
if (!VoxelDensityAblation::IsPassageCarvingOff())
|
||||||
{
|
{
|
||||||
const float ModSDF = EvaluateModifierSDF(WorldX, WorldY, WorldZ);
|
const float ModSDF = EvaluateModifierSDF(WorldX, WorldY, WorldZ);
|
||||||
VF_ApplyPassageCarving(Density, ModSDF, BaseDensity, SealThickness);
|
VF_ApplyPassageCarving(Density, ModSDF, BaseDensity, SealThickness);
|
||||||
}
|
}
|
||||||
|
if (bCompareLandingModifier)
|
||||||
|
{
|
||||||
|
VoxelGenLOD::RecordSkippedPostComparison(
|
||||||
|
VoxelGenLOD::ETilePostComparisonKind::PassageLandingSDF,
|
||||||
|
!VoxelGenLOD::SameFloatBits(Density, CanonicalModifierDensity));
|
||||||
|
}
|
||||||
ApplyPassageLandingAir(Density, WorldX, WorldY, WorldZ, BaseDensity, SealThickness);
|
ApplyPassageLandingAir(Density, WorldX, WorldY, WorldZ, BaseDensity, SealThickness);
|
||||||
|
|
||||||
|
const bool bLandingReachable = VoxelGenLOD::IsPassageLandingReachable()
|
||||||
|
|| VoxelGenLOD::IsTilePostReachBypassActive();
|
||||||
|
const bool bStructuralReachable = VoxelGenLOD::IsPassageStructuralReachable()
|
||||||
|
|| VoxelGenLOD::IsTilePostReachBypassActive();
|
||||||
|
if (!bLandingReachable && !bStructuralReachable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const FIntVector ChunkCoord(
|
const FIntVector ChunkCoord(
|
||||||
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
|
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
|
||||||
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
|
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
|
||||||
@@ -2634,10 +2671,12 @@ void UVoxelStrateManager::ApplyPassageModifier(
|
|||||||
for (const int32 PassageIndex : Nearby)
|
for (const int32 PassageIndex : Nearby)
|
||||||
{
|
{
|
||||||
const FVoxelPassage& Passage = PassageData[PassageIndex];
|
const FVoxelPassage& Passage = PassageData[PassageIndex];
|
||||||
const bool bLandingFloor = !VoxelDensityAblation::IsLandingPostsOff()
|
const bool bLandingFloor = bLandingReachable
|
||||||
|
&& !VoxelDensityAblation::IsLandingPostsOff()
|
||||||
&& (VF_IsPassageLandingFloor(Position, Passage.UpperLanding)
|
&& (VF_IsPassageLandingFloor(Position, Passage.UpperLanding)
|
||||||
|| VF_IsPassageLandingFloor(Position, Passage.LowerLanding));
|
|| VF_IsPassageLandingFloor(Position, Passage.LowerLanding));
|
||||||
const bool bLegacySupportFloor = !VoxelDensityAblation::IsPassageStructuralPostsOff()
|
const bool bLegacySupportFloor = bStructuralReachable
|
||||||
|
&& !VoxelDensityAblation::IsPassageStructuralPostsOff()
|
||||||
&& bPassageSupportFloorWritesEnabled
|
&& bPassageSupportFloorWritesEnabled
|
||||||
&& !Passage.bNativeFloorEnabled
|
&& !Passage.bNativeFloorEnabled
|
||||||
&& VF_IsWalkableTunnelFloor(Passage, Position, this, PassageIndex);
|
&& VF_IsWalkableTunnelFloor(Passage, Position, this, PassageIndex);
|
||||||
@@ -2656,8 +2695,11 @@ void UVoxelStrateManager::ApplyPassageModifier(
|
|||||||
// the current walkable tunnel. Reassert tunnel air after all floor posts so overlap cannot
|
// the current walkable tunnel. Reassert tunnel air after all floor posts so overlap cannot
|
||||||
// turn a valid route into a solid plug. At the tunnel's own floor the air predicate is false,
|
// turn a valid route into a solid plug. At the tunnel's own floor the air predicate is false,
|
||||||
// so the support plane remains solid.
|
// so the support plane remains solid.
|
||||||
|
if (bStructuralReachable)
|
||||||
|
{
|
||||||
ApplyPassageTunnelAir(Density, WorldX, WorldY, WorldZ, BaseDensity, SealThickness);
|
ApplyPassageTunnelAir(Density, WorldX, WorldY, WorldZ, BaseDensity, SealThickness);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void UVoxelStrateManager::ApplyPassageCarvingOnly(
|
void UVoxelStrateManager::ApplyPassageCarvingOnly(
|
||||||
float& Density, float WorldX, float WorldY, float WorldZ,
|
float& Density, float WorldX, float WorldY, float WorldZ,
|
||||||
@@ -2669,8 +2711,27 @@ void UVoxelStrateManager::ApplyPassageCarvingOnly(
|
|||||||
}
|
}
|
||||||
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
||||||
VoxelDensityProfile::EBucket::PassageModifier);
|
VoxelDensityProfile::EBucket::PassageModifier);
|
||||||
|
const bool bBypass = VoxelGenLOD::IsTilePostReachBypassActive();
|
||||||
|
const bool bCompareLandingModifier = VoxelGenLOD::bTilePostReachDebug
|
||||||
|
&& !VoxelGenLOD::IsPassageLandingReachable() && !bBypass;
|
||||||
|
float CanonicalModifierDensity = Density;
|
||||||
|
if (bCompareLandingModifier)
|
||||||
|
{
|
||||||
|
const bool PreviousBypass = VoxelGenLOD::bTilePostReachBypass;
|
||||||
|
VoxelGenLOD::bTilePostReachBypass = true;
|
||||||
|
const float CanonicalSDF = EvaluateModifierSDF(WorldX, WorldY, WorldZ);
|
||||||
|
VF_ApplyPassageCarving(
|
||||||
|
CanonicalModifierDensity, CanonicalSDF, BaseDensity, SealThickness);
|
||||||
|
VoxelGenLOD::bTilePostReachBypass = PreviousBypass;
|
||||||
|
}
|
||||||
const float ModSDF = EvaluateModifierSDF(WorldX, WorldY, WorldZ);
|
const float ModSDF = EvaluateModifierSDF(WorldX, WorldY, WorldZ);
|
||||||
VF_ApplyPassageCarving(Density, ModSDF, BaseDensity, SealThickness);
|
VF_ApplyPassageCarving(Density, ModSDF, BaseDensity, SealThickness);
|
||||||
|
if (bCompareLandingModifier)
|
||||||
|
{
|
||||||
|
VoxelGenLOD::RecordSkippedPostComparison(
|
||||||
|
VoxelGenLOD::ETilePostComparisonKind::PassageLandingSDF,
|
||||||
|
!VoxelGenLOD::SameFloatBits(Density, CanonicalModifierDensity));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UVoxelStrateManager::ApplyPassageLandingAir(
|
void UVoxelStrateManager::ApplyPassageLandingAir(
|
||||||
@@ -2681,6 +2742,16 @@ void UVoxelStrateManager::ApplyPassageLandingAir(
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!VoxelGenLOD::IsPassageLandingReachable()
|
||||||
|
&& !VoxelGenLOD::IsTilePostReachBypassActive())
|
||||||
|
{
|
||||||
|
VoxelGenLOD::CompareSkippedPost(Density, [&]()
|
||||||
|
{
|
||||||
|
ApplyPassageLandingAir(
|
||||||
|
Density, WorldX, WorldY, WorldZ, BaseDensity, SealThickness);
|
||||||
|
}, VoxelGenLOD::ETilePostComparisonKind::PassageLandingAir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
||||||
VoxelDensityProfile::EBucket::PassageLandingAir);
|
VoxelDensityProfile::EBucket::PassageLandingAir);
|
||||||
const FIntVector ChunkCoord(
|
const FIntVector ChunkCoord(
|
||||||
@@ -2749,6 +2820,16 @@ void UVoxelStrateManager::ApplyPassageTunnelAir(
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!VoxelGenLOD::IsPassageStructuralReachable()
|
||||||
|
&& !VoxelGenLOD::IsTilePostReachBypassActive())
|
||||||
|
{
|
||||||
|
VoxelGenLOD::CompareSkippedPost(Density, [&]()
|
||||||
|
{
|
||||||
|
ApplyPassageTunnelAir(
|
||||||
|
Density, WorldX, WorldY, WorldZ, BaseDensity, SealThickness);
|
||||||
|
}, VoxelGenLOD::ETilePostComparisonKind::PassageTunnelAir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
||||||
VoxelDensityProfile::EBucket::PassageTunnelAir);
|
VoxelDensityProfile::EBucket::PassageTunnelAir);
|
||||||
const FVector Position(WorldX, WorldY, WorldZ);
|
const FVector Position(WorldX, WorldY, WorldZ);
|
||||||
@@ -2784,6 +2865,30 @@ void UVoxelStrateManager::ApplyPassageLandingFloorMC(
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const bool bBypass = VoxelGenLOD::IsTilePostReachBypassActive();
|
||||||
|
const bool bLandingReachable = VoxelGenLOD::IsPassageLandingReachable() || bBypass;
|
||||||
|
const bool bStructuralReachable = VoxelGenLOD::IsPassageStructuralReachable() || bBypass;
|
||||||
|
const bool bCompareLanding = VoxelGenLOD::bTilePostReachDebug
|
||||||
|
&& !bLandingReachable && !bBypass;
|
||||||
|
float CanonicalDensity = Density;
|
||||||
|
if (bCompareLanding)
|
||||||
|
{
|
||||||
|
const bool PreviousBypass = VoxelGenLOD::bTilePostReachBypass;
|
||||||
|
VoxelGenLOD::bTilePostReachBypass = true;
|
||||||
|
ApplyPassageLandingFloorMC(
|
||||||
|
CanonicalDensity, WorldX, WorldY, WorldZ, BaseDensity);
|
||||||
|
VoxelGenLOD::bTilePostReachBypass = PreviousBypass;
|
||||||
|
}
|
||||||
|
if (!bLandingReachable && !bStructuralReachable)
|
||||||
|
{
|
||||||
|
if (bCompareLanding)
|
||||||
|
{
|
||||||
|
VoxelGenLOD::RecordSkippedPostComparison(
|
||||||
|
VoxelGenLOD::ETilePostComparisonKind::PassageLandingFloor,
|
||||||
|
!VoxelGenLOD::SameFloatBits(Density, CanonicalDensity));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
||||||
VoxelDensityProfile::EBucket::PassageLandingFloor);
|
VoxelDensityProfile::EBucket::PassageLandingFloor);
|
||||||
const FIntVector ChunkCoord(
|
const FIntVector ChunkCoord(
|
||||||
@@ -2796,10 +2901,12 @@ void UVoxelStrateManager::ApplyPassageLandingFloorMC(
|
|||||||
for (const int32 PassageIndex : Nearby)
|
for (const int32 PassageIndex : Nearby)
|
||||||
{
|
{
|
||||||
const FVoxelPassage& Passage = PassageData[PassageIndex];
|
const FVoxelPassage& Passage = PassageData[PassageIndex];
|
||||||
const bool bLandingFloor = !VoxelDensityAblation::IsLandingPostsOff()
|
const bool bLandingFloor = bLandingReachable
|
||||||
|
&& !VoxelDensityAblation::IsLandingPostsOff()
|
||||||
&& (VF_IsPassageLandingFloor(Position, Passage.UpperLanding)
|
&& (VF_IsPassageLandingFloor(Position, Passage.UpperLanding)
|
||||||
|| VF_IsPassageLandingFloor(Position, Passage.LowerLanding));
|
|| VF_IsPassageLandingFloor(Position, Passage.LowerLanding));
|
||||||
const bool bLegacySupportFloor = !VoxelDensityAblation::IsPassageStructuralPostsOff()
|
const bool bLegacySupportFloor = bStructuralReachable
|
||||||
|
&& !VoxelDensityAblation::IsPassageStructuralPostsOff()
|
||||||
&& bPassageSupportFloorWritesEnabled
|
&& bPassageSupportFloorWritesEnabled
|
||||||
&& !Passage.bNativeFloorEnabled
|
&& !Passage.bNativeFloorEnabled
|
||||||
&& VF_IsWalkableTunnelFloor(Passage, Position, this, PassageIndex);
|
&& VF_IsWalkableTunnelFloor(Passage, Position, this, PassageIndex);
|
||||||
@@ -2812,6 +2919,12 @@ void UVoxelStrateManager::ApplyPassageLandingFloorMC(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (bCompareLanding)
|
||||||
|
{
|
||||||
|
VoxelGenLOD::RecordSkippedPostComparison(
|
||||||
|
VoxelGenLOD::ETilePostComparisonKind::PassageLandingFloor,
|
||||||
|
!VoxelGenLOD::SameFloatBits(Density, CanonicalDensity));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool VF_IsPassageRoomFloor(
|
static bool VF_IsPassageRoomFloor(
|
||||||
@@ -2844,6 +2957,32 @@ void UVoxelStrateManager::ApplyPassageStructuralPostsMC(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool bBypass = VoxelGenLOD::IsTilePostReachBypassActive();
|
||||||
|
const bool bLandingReachable = VoxelGenLOD::IsPassageLandingReachable() || bBypass;
|
||||||
|
const bool bStructuralReachable = VoxelGenLOD::IsPassageStructuralReachable() || bBypass;
|
||||||
|
const bool bCompareLanding = VoxelGenLOD::bTilePostReachDebug
|
||||||
|
&& !bLandingReachable && !bBypass;
|
||||||
|
float CanonicalDensity = Density;
|
||||||
|
if (bCompareLanding)
|
||||||
|
{
|
||||||
|
const bool PreviousBypass = VoxelGenLOD::bTilePostReachBypass;
|
||||||
|
VoxelGenLOD::bTilePostReachBypass = true;
|
||||||
|
ApplyPassageStructuralPostsMC(
|
||||||
|
CanonicalDensity, WorldX, WorldY, WorldZ,
|
||||||
|
BaseDensity, SealThickness, bProtectAuthoredTunnelFloor);
|
||||||
|
VoxelGenLOD::bTilePostReachBypass = PreviousBypass;
|
||||||
|
}
|
||||||
|
if (!bLandingReachable && !bStructuralReachable)
|
||||||
|
{
|
||||||
|
if (bCompareLanding)
|
||||||
|
{
|
||||||
|
VoxelGenLOD::RecordSkippedPostComparison(
|
||||||
|
VoxelGenLOD::ETilePostComparisonKind::PassageStructuralPosts,
|
||||||
|
!VoxelGenLOD::SameFloatBits(Density, CanonicalDensity));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
||||||
VoxelDensityProfile::EBucket::PassageStructuralPosts);
|
VoxelDensityProfile::EBucket::PassageStructuralPosts);
|
||||||
|
|
||||||
@@ -2873,36 +3012,37 @@ void UVoxelStrateManager::ApplyPassageStructuralPostsMC(
|
|||||||
const FVoxelPassage& Passage = PassageData[PassageIndex];
|
const FVoxelPassage& Passage = PassageData[PassageIndex];
|
||||||
|
|
||||||
const bool bTunnelSupportFloor =
|
const bool bTunnelSupportFloor =
|
||||||
!bDisableStructuralPosts
|
bStructuralReachable && !bDisableStructuralPosts
|
||||||
&& bPassageSupportFloorWritesEnabled
|
&& bPassageSupportFloorWritesEnabled
|
||||||
&& !Passage.bNativeFloorEnabled
|
&& !Passage.bNativeFloorEnabled
|
||||||
&& VF_IsWalkableTunnelFloor(Passage, Position, this, PassageIndex);
|
&& VF_IsWalkableTunnelFloor(Passage, Position, this, PassageIndex);
|
||||||
if (!bSuppressFloor && !bDisableLandingPosts
|
if (!bSuppressFloor && bLandingReachable && !bDisableLandingPosts
|
||||||
&& (VF_IsPassageLandingFloor(Position, Passage.UpperLanding)
|
&& (VF_IsPassageLandingFloor(Position, Passage.UpperLanding)
|
||||||
|| VF_IsPassageLandingFloor(Position, Passage.LowerLanding)))
|
|| VF_IsPassageLandingFloor(Position, Passage.LowerLanding)))
|
||||||
{
|
{
|
||||||
bAnyLandingFloor = true;
|
bAnyLandingFloor = true;
|
||||||
}
|
}
|
||||||
if (!bSuppressFloor && !bDisableStructuralPosts && bTunnelSupportFloor)
|
if (!bSuppressFloor && bStructuralReachable
|
||||||
|
&& !bDisableStructuralPosts && bTunnelSupportFloor)
|
||||||
{
|
{
|
||||||
bLegacyTunnelSupportFloor = true;
|
bLegacyTunnelSupportFloor = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bSuppressFloor && !bDisableLandingPosts
|
if (!bSuppressFloor && bLandingReachable && !bDisableLandingPosts
|
||||||
&& (VF_IsPassageRoomFloor(Position, Passage.UpperLanding)
|
&& (VF_IsPassageRoomFloor(Position, Passage.UpperLanding)
|
||||||
|| VF_IsPassageRoomFloor(Position, Passage.LowerLanding)))
|
|| VF_IsPassageRoomFloor(Position, Passage.LowerLanding)))
|
||||||
{
|
{
|
||||||
bAnyRoomFloor = true;
|
bAnyRoomFloor = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bDisableStructuralPosts && !bWalkableAir
|
if (bStructuralReachable && !bDisableStructuralPosts && !bWalkableAir
|
||||||
&& FVector::DistSquared(Position, Passage.BoundCenter) <= Passage.BoundRadiusSq
|
&& FVector::DistSquared(Position, Passage.BoundCenter) <= Passage.BoundRadiusSq
|
||||||
&& VF_IsWalkableTunnelAir(Passage, Position, this, PassageIndex))
|
&& VF_IsWalkableTunnelAir(Passage, Position, this, PassageIndex))
|
||||||
{
|
{
|
||||||
bWalkableAir = true;
|
bWalkableAir = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bDisableLandingPosts
|
if (bLandingReachable && !bDisableLandingPosts
|
||||||
&& FVector::DistSquared(Position, Passage.BoundCenter) <= Passage.BoundRadiusSq)
|
&& FVector::DistSquared(Position, Passage.BoundCenter) <= Passage.BoundRadiusSq)
|
||||||
{
|
{
|
||||||
const FVoxelPassageLanding* Landings[] = {
|
const FVoxelPassageLanding* Landings[] = {
|
||||||
@@ -2950,6 +3090,12 @@ void UVoxelStrateManager::ApplyPassageStructuralPostsMC(
|
|||||||
VoxelDensityProfile::AddCounter(
|
VoxelDensityProfile::AddCounter(
|
||||||
VoxelDensityProfile::ECounter::PassageSupportFloorBackstopFires);
|
VoxelDensityProfile::ECounter::PassageSupportFloorBackstopFires);
|
||||||
}
|
}
|
||||||
|
if (bCompareLanding)
|
||||||
|
{
|
||||||
|
VoxelGenLOD::RecordSkippedPostComparison(
|
||||||
|
VoxelGenLOD::ETilePostComparisonKind::PassageStructuralPosts,
|
||||||
|
!VoxelGenLOD::SameFloatBits(Density, CanonicalDensity));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UVoxelStrateManager::ApplyPassageNativeFloorMC(
|
void UVoxelStrateManager::ApplyPassageNativeFloorMC(
|
||||||
@@ -2960,6 +3106,15 @@ void UVoxelStrateManager::ApplyPassageNativeFloorMC(
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!VoxelGenLOD::IsPassageStructuralReachable()
|
||||||
|
&& !VoxelGenLOD::IsTilePostReachBypassActive())
|
||||||
|
{
|
||||||
|
VoxelGenLOD::CompareSkippedPost(Density, [&]()
|
||||||
|
{
|
||||||
|
ApplyPassageNativeFloorMC(Density, WorldX, WorldY, WorldZ, BaseDensity);
|
||||||
|
}, VoxelGenLOD::ETilePostComparisonKind::PassageNativeFloor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const FIntVector ChunkCoord(
|
const FIntVector ChunkCoord(
|
||||||
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
|
FMath::FloorToInt(WorldX / (float)CHUNK_SIZE),
|
||||||
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
|
FMath::FloorToInt(WorldY / (float)CHUNK_SIZE),
|
||||||
@@ -3018,6 +3173,16 @@ void UVoxelStrateManager::ApplyPassageLandingRoomFloorMC(
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!VoxelGenLOD::IsPassageLandingReachable()
|
||||||
|
&& !VoxelGenLOD::IsTilePostReachBypassActive())
|
||||||
|
{
|
||||||
|
VoxelGenLOD::CompareSkippedPost(Density, [&]()
|
||||||
|
{
|
||||||
|
ApplyPassageLandingRoomFloorMC(
|
||||||
|
Density, WorldX, WorldY, WorldZ, BaseDensity);
|
||||||
|
}, VoxelGenLOD::ETilePostComparisonKind::PassageLandingRoomFloor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
VoxelDensityProfile::FScopedTimer ProfileTimer(
|
||||||
VoxelDensityProfile::EBucket::PassageLandingRoomFloor);
|
VoxelDensityProfile::EBucket::PassageLandingRoomFloor);
|
||||||
const FIntVector ChunkCoord(
|
const FIntVector ChunkCoord(
|
||||||
@@ -3912,6 +4077,22 @@ namespace
|
|||||||
FMath::Min((float)Box.Max.Z, MaxZ),
|
FMath::Min((float)Box.Max.Z, MaxZ),
|
||||||
(float)Origin.Z, Step);
|
(float)Origin.Z, Step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool VF_LatticeBoxTouchesSphere(
|
||||||
|
const FBox& Box, const FVector& Center, float Radius)
|
||||||
|
{
|
||||||
|
if (!Box.IsValid || !VoxelMath::IsFinite(Center.X)
|
||||||
|
|| !VoxelMath::IsFinite(Center.Y) || !VoxelMath::IsFinite(Center.Z)
|
||||||
|
|| !VoxelMath::IsFinite(Radius) || Radius < 0.0f)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const FVector Closest(
|
||||||
|
FMath::Clamp(Center.X, (float)Box.Min.X, (float)Box.Max.X),
|
||||||
|
FMath::Clamp(Center.Y, (float)Box.Min.Y, (float)Box.Max.Y),
|
||||||
|
FMath::Clamp(Center.Z, (float)Box.Min.Z, (float)Box.Max.Z));
|
||||||
|
return FVector::DistSquared(Closest, Center) <= Radius * Radius;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UVoxelStrateManager::AnyPassageNearLattice(
|
bool UVoxelStrateManager::AnyPassageNearLattice(
|
||||||
@@ -4768,6 +4949,189 @@ bool UVoxelStrateManager::AnyPassageLandingFloorNearLattice(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UVoxelStrateManager::AnyPassageLandingNearLattice(
|
||||||
|
const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step,
|
||||||
|
float ReachScale) const
|
||||||
|
{
|
||||||
|
if (Step <= 0 || !VoxelBox.IsValid
|
||||||
|
|| !VoxelMath::IsFinite(ReachScale) || ReachScale <= 0.0f)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VF_ApplyPassageCarving has a hard no-op band at ModSDF >= 4. A landing term outside the
|
||||||
|
// rounded-room AABB by 4 + K (K=3) is either saturated out of SmoothMin when the tube is
|
||||||
|
// inside the carve band, or leaves the folded result >= 4 when the tube is outside it. That
|
||||||
|
// proves the landing term cannot change the field; the direct landing/floor posts use their
|
||||||
|
// smaller, operation-specific pads below.
|
||||||
|
for (const FVoxelPassage& Passage : Passages)
|
||||||
|
{
|
||||||
|
if (!VoxelMath::IsFinite(Passage.BoundCenter.X)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.BoundCenter.Y)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.BoundCenter.Z)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.BoundRadius)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.BoundRadiusSq)
|
||||||
|
|| Passage.BoundRadius < 0.0f || Passage.BoundRadiusSq < 0.0f)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Passage.ControlPoints.Num() >= 2)
|
||||||
|
{
|
||||||
|
if (Passage.ControlPoints.Num() > 4096)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const bool bTaper = Passage.ControlRadii.Num() == Passage.ControlPoints.Num();
|
||||||
|
if (!bTaper && !VoxelMath::IsFinite(Passage.Radius))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (int32 PointIndex = 0;
|
||||||
|
PointIndex < Passage.ControlPoints.Num(); ++PointIndex)
|
||||||
|
{
|
||||||
|
const FVector& Point = Passage.ControlPoints[PointIndex];
|
||||||
|
if (!VoxelMath::IsFinite(Point.X) || !VoxelMath::IsFinite(Point.Y)
|
||||||
|
|| !VoxelMath::IsFinite(Point.Z)
|
||||||
|
|| (bTaper && !VoxelMath::IsFinite(Passage.ControlRadii[PointIndex])))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!VoxelMath::IsFinite(Passage.UpperPoint.X)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.UpperPoint.Y)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.UpperPoint.Z)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.LowerPoint.X)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.LowerPoint.Y)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.LowerPoint.Z)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.Radius))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FVoxelPassageLanding* Landings[] = {
|
||||||
|
&Passage.UpperLanding, &Passage.LowerLanding };
|
||||||
|
for (const FVoxelPassageLanding* Landing : Landings)
|
||||||
|
{
|
||||||
|
if (!VoxelMath::IsFinite(Landing->StandingPoint.X)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->StandingPoint.Y)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->FloorZ)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->CeilingZ)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->HalfWidth)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->FloorThickness))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Landing->HalfWidth > 0.0f
|
||||||
|
&& Landing->FloorThickness > 0.0f
|
||||||
|
&& VF_LatticeBoxTouchesAABB(
|
||||||
|
VoxelBox, LatticeOrigin, Step,
|
||||||
|
Landing->StandingPoint.X - FMath::Max(Landing->HalfWidth - 1.0f, 0.0f)
|
||||||
|
- ReachScale,
|
||||||
|
Landing->StandingPoint.X + FMath::Max(Landing->HalfWidth - 1.0f, 0.0f)
|
||||||
|
+ ReachScale,
|
||||||
|
Landing->StandingPoint.Y - FMath::Max(Landing->HalfWidth - 1.0f, 0.0f)
|
||||||
|
- ReachScale,
|
||||||
|
Landing->StandingPoint.Y + FMath::Max(Landing->HalfWidth - 1.0f, 0.0f)
|
||||||
|
+ ReachScale,
|
||||||
|
Landing->FloorZ - Landing->FloorThickness - ReachScale,
|
||||||
|
Landing->FloorZ + ReachScale))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Landing->HalfWidth <= 0.0f || Landing->CeilingZ <= Landing->FloorZ)
|
||||||
|
{
|
||||||
|
// VF_EvaluatePassageLandingSDF returns FLT_MAX for this descriptor. The floor
|
||||||
|
// test above has already handled the only remaining possible writer.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr float SmoothMinK = 3.0f;
|
||||||
|
const float RoomPad = (VoxelPassageGeometry::LandingCarveBlendVoxels + SmoothMinK)
|
||||||
|
* ReachScale;
|
||||||
|
if (!VoxelMath::IsFinite(RoomPad) || RoomPad < 0.0f)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const FVoxelPassage& Passage : Passages)
|
||||||
|
{
|
||||||
|
const FVoxelPassageLanding* Landings[] = {
|
||||||
|
&Passage.UpperLanding, &Passage.LowerLanding };
|
||||||
|
for (const FVoxelPassageLanding* Landing : Landings)
|
||||||
|
{
|
||||||
|
if (Landing->HalfWidth <= 0.0f || Landing->CeilingZ <= Landing->FloorZ)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
constexpr float RoomRounding = 1.5f;
|
||||||
|
const float Height = Landing->CeilingZ - Landing->FloorZ;
|
||||||
|
const float HalfX = FMath::Max(Landing->HalfWidth - RoomRounding, 0.25f);
|
||||||
|
const float HalfZ = FMath::Max(Height * 0.5f - RoomRounding, 0.25f);
|
||||||
|
const float CenterZ = (Landing->FloorZ + Landing->CeilingZ) * 0.5f;
|
||||||
|
if (VF_LatticeBoxTouchesAABB(
|
||||||
|
VoxelBox, LatticeOrigin, Step,
|
||||||
|
Landing->StandingPoint.X - HalfX - RoomRounding - RoomPad,
|
||||||
|
Landing->StandingPoint.X + HalfX + RoomRounding + RoomPad,
|
||||||
|
Landing->StandingPoint.Y - HalfX - RoomRounding - RoomPad,
|
||||||
|
Landing->StandingPoint.Y + HalfX + RoomRounding + RoomPad,
|
||||||
|
FMath::Min(Landing->FloorZ,
|
||||||
|
CenterZ - HalfZ - RoomRounding) - RoomPad,
|
||||||
|
FMath::Max(Landing->CeilingZ,
|
||||||
|
CenterZ + HalfZ + RoomRounding) + RoomPad))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UVoxelStrateManager::AnyPassageStructuralPostNearLattice(
|
||||||
|
const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step,
|
||||||
|
float ReachScale) const
|
||||||
|
{
|
||||||
|
(void)LatticeOrigin;
|
||||||
|
(void)Step;
|
||||||
|
if (Step <= 0 || !VoxelBox.IsValid
|
||||||
|
|| !VoxelMath::IsFinite(ReachScale) || ReachScale <= 0.0f)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The construction-time BoundRadius encloses every tube, landing room, floor band, and its
|
||||||
|
// four-voxel carve margin. All structural writers below are subsets of that sphere, so a
|
||||||
|
// sphere/AABB miss proves identity without doing a per-sample chunk-shortlist lookup.
|
||||||
|
for (const FVoxelPassage& Passage : Passages)
|
||||||
|
{
|
||||||
|
if (!VoxelMath::IsFinite(Passage.BoundCenter.X)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.BoundCenter.Y)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.BoundCenter.Z)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.BoundRadius)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.BoundRadiusSq)
|
||||||
|
|| Passage.BoundRadius < 0.0f || Passage.BoundRadiusSq < 0.0f)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const float Reach = (Passage.BoundRadius + 1.0f) * ReachScale;
|
||||||
|
if (!VoxelMath::IsFinite(Reach)
|
||||||
|
|| VF_LatticeBoxTouchesSphere(VoxelBox, Passage.BoundCenter, Reach))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool UVoxelStrateManager::AnyLandingFloorAtLattice(
|
bool UVoxelStrateManager::AnyLandingFloorAtLattice(
|
||||||
const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step) const
|
const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step) const
|
||||||
{
|
{
|
||||||
@@ -5013,9 +5377,14 @@ bool UVoxelStrateManager::AnyOriginLandingFloorNearBox(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool UVoxelStrateManager::AnyOriginLandingNearLattice(
|
bool UVoxelStrateManager::AnyOriginLandingNearLattice(
|
||||||
const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step) const
|
const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step,
|
||||||
|
float ReachScale) const
|
||||||
{
|
{
|
||||||
if (OriginSpineRadius <= 0.0f) return false;
|
if (OriginSpineRadius <= 0.0f) return false;
|
||||||
|
if (!VoxelMath::IsFinite(ReachScale) || ReachScale <= 0.0f)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
for (const FStrateSlot& Slot : StrateLayout)
|
for (const FStrateSlot& Slot : StrateLayout)
|
||||||
{
|
{
|
||||||
if (Slot.Definition == nullptr) continue;
|
if (Slot.Definition == nullptr) continue;
|
||||||
@@ -5023,7 +5392,8 @@ bool UVoxelStrateManager::AnyOriginLandingNearLattice(
|
|||||||
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
|
const float BottomZ = static_cast<float>(Slot.BottomChunkZ) * CHUNK_SIZE;
|
||||||
const float Seal = VF_BoundarySealThicknessForDefinition(*Slot.Definition);
|
const float Seal = VF_BoundarySealThicknessForDefinition(*Slot.Definition);
|
||||||
if (VoxelPassageGeometry::OriginLandingRoomTouchesLattice(
|
if (VoxelPassageGeometry::OriginLandingRoomTouchesLattice(
|
||||||
VoxelBox, LatticeOrigin, Step, TopZ, BottomZ, Seal, OriginSpineRadius))
|
VoxelBox, LatticeOrigin, Step, TopZ, BottomZ, Seal, OriginSpineRadius,
|
||||||
|
ReachScale))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "VoxelStats.h"
|
#include "VoxelStats.h"
|
||||||
#include "VoxelCaveMorphology.h"
|
#include "VoxelCaveMorphology.h"
|
||||||
#include "VoxelPassageGeometry.h"
|
#include "VoxelPassageGeometry.h"
|
||||||
|
#include "VoxelTilePostReach.h"
|
||||||
// IWYU (FPSemantics = Precise ⇒ plus de PCH partagé) : GetPlayerPosition déréférence le pawn, donc
|
// IWYU (FPSemantics = Precise ⇒ plus de PCH partagé) : GetPlayerPosition déréférence le pawn, donc
|
||||||
// APawn doit être COMPLET — `Casts.h` n'en donne qu'une déclaration avant. APlayerController était
|
// APawn doit être COMPLET — `Casts.h` n'en donne qu'une déclaration avant. APlayerController était
|
||||||
// complet par transitivité seulement : on l'inclut explicitement, c'est exactement la fragilité
|
// complet par transitivité seulement : on l'inclut explicitement, c'est exactement la fragilité
|
||||||
@@ -1132,6 +1133,98 @@ void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|||||||
}
|
}
|
||||||
VoxelCaveMorphology::LogPlayerFitMemoStats();
|
VoxelCaveMorphology::LogPlayerFitMemoStats();
|
||||||
LogStreamingLatencySummary();
|
LogStreamingLatencySummary();
|
||||||
|
const uint64 TilePostReachDifferences =
|
||||||
|
VoxelGenLOD::GSkippedPostDifferences.load(std::memory_order_relaxed);
|
||||||
|
UE_LOG(LogTemp, Display,
|
||||||
|
TEXT("[VoxelForgeTilePostReach] tiles=%llu origin_reachable=%llu origin_skipped=%llu "
|
||||||
|
"passage_landing_reachable=%llu passage_landing_skipped=%llu "
|
||||||
|
"passage_structural_reachable=%llu passage_structural_skipped=%llu "
|
||||||
|
"debug=%d comparisons=%llu differences=%llu"),
|
||||||
|
static_cast<unsigned long long>(
|
||||||
|
VoxelGenLOD::GTilePostReachTileCount.load(std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(
|
||||||
|
VoxelGenLOD::GOriginLandingReachableTiles.load(std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(
|
||||||
|
VoxelGenLOD::GOriginLandingSkippedTiles.load(std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(
|
||||||
|
VoxelGenLOD::GPassageLandingReachableTiles.load(std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(
|
||||||
|
VoxelGenLOD::GPassageLandingSkippedTiles.load(std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(
|
||||||
|
VoxelGenLOD::GPassageStructuralReachableTiles.load(std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(
|
||||||
|
VoxelGenLOD::GPassageStructuralSkippedTiles.load(std::memory_order_relaxed)),
|
||||||
|
VoxelGenLOD::GTilePostReachDebugEnabled.load(std::memory_order_relaxed) ? 1 : 0,
|
||||||
|
static_cast<unsigned long long>(
|
||||||
|
VoxelGenLOD::GSkippedPostComparisons.load(std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(TilePostReachDifferences));
|
||||||
|
UE_LOG(LogTemp, Display,
|
||||||
|
TEXT("[VoxelForgeTilePostReachKinds] comparisons="
|
||||||
|
"origin_air=%llu/origin_floor=%llu/passage_sdf=%llu/landing_air=%llu/"
|
||||||
|
"tunnel_air=%llu/landing_floor=%llu/structural=%llu/native_floor=%llu/room_floor=%llu "
|
||||||
|
"differences=%llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu"),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::OriginAir)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::OriginFloor)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingSDF)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingAir)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageTunnelAir)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingFloor)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageStructuralPosts)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageNativeFloor)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingRoomFloor)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::OriginAir)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::OriginFloor)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingSDF)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingAir)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageTunnelAir)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingFloor)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageStructuralPosts)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageNativeFloor)].load(
|
||||||
|
std::memory_order_relaxed)),
|
||||||
|
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
|
||||||
|
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingRoomFloor)].load(
|
||||||
|
std::memory_order_relaxed)));
|
||||||
|
if (VoxelGenLOD::GTilePostReachDebugEnabled.load(std::memory_order_relaxed)
|
||||||
|
&& TilePostReachDifferences != 0)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Error,
|
||||||
|
TEXT("[VoxelForgeTilePostReach] soundness failure: skipped post changed the field."));
|
||||||
|
FPlatformMisc::RequestExitWithStatus(
|
||||||
|
false, 1, TEXT("VoxelForge tile post reach soundness failure"));
|
||||||
|
}
|
||||||
PendingTiles.Empty();
|
PendingTiles.Empty();
|
||||||
PendingTileCancellation.Empty();
|
PendingTileCancellation.Empty();
|
||||||
PendingUnload.Empty();
|
PendingUnload.Empty();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "VoxelTypes.h" // SmoothStep01
|
#include "VoxelTypes.h" // SmoothStep01
|
||||||
#include "VoxelPassageGeometry.h" // shared body-sized landing/tunnel geometry
|
#include "VoxelPassageGeometry.h" // shared body-sized landing/tunnel geometry
|
||||||
#include "VoxelDensityAblation.h" // development-only world-changing measurements
|
#include "VoxelDensityAblation.h" // development-only world-changing measurements
|
||||||
|
#include "VoxelTilePostReach.h" // worker-local landing/structural post reach bits
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// SEAL DE FRONTIÈRE / BOUNDARY SEAL
|
// SEAL DE FRONTIÈRE / BOUNDARY SEAL
|
||||||
@@ -193,6 +194,17 @@ FORCEINLINE void VF_ApplyOriginLandingAirMC(float& Density,
|
|||||||
float WorldX, float WorldY, float WorldZ,
|
float WorldX, float WorldY, float WorldZ,
|
||||||
float StrateTopZ, float StrateBottomZ, float SealThickness, float BaseDensity, float Radius)
|
float StrateTopZ, float StrateBottomZ, float SealThickness, float BaseDensity, float Radius)
|
||||||
{
|
{
|
||||||
|
if (!VoxelGenLOD::IsOriginLandingReachable()
|
||||||
|
&& !VoxelGenLOD::IsTilePostReachBypassActive())
|
||||||
|
{
|
||||||
|
VoxelGenLOD::CompareSkippedPost(Density, [&]()
|
||||||
|
{
|
||||||
|
VF_ApplyOriginLandingAirMC(
|
||||||
|
Density, WorldX, WorldY, WorldZ,
|
||||||
|
StrateTopZ, StrateBottomZ, SealThickness, BaseDensity, Radius);
|
||||||
|
}, VoxelGenLOD::ETilePostComparisonKind::OriginAir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
float InternalDensity = -Density;
|
float InternalDensity = -Density;
|
||||||
VF_ApplyOriginLandingAir(InternalDensity, WorldX, WorldY, WorldZ,
|
VF_ApplyOriginLandingAir(InternalDensity, WorldX, WorldY, WorldZ,
|
||||||
StrateTopZ, StrateBottomZ, SealThickness, BaseDensity, Radius);
|
StrateTopZ, StrateBottomZ, SealThickness, BaseDensity, Radius);
|
||||||
@@ -204,6 +216,17 @@ FORCEINLINE void VF_ApplyOriginLandingFloorMC(float& Density,
|
|||||||
float WorldX, float WorldY, float WorldZ,
|
float WorldX, float WorldY, float WorldZ,
|
||||||
float StrateTopZ, float StrateBottomZ, float SealThickness, float BaseDensity, float Radius)
|
float StrateTopZ, float StrateBottomZ, float SealThickness, float BaseDensity, float Radius)
|
||||||
{
|
{
|
||||||
|
if (!VoxelGenLOD::IsOriginLandingReachable()
|
||||||
|
&& !VoxelGenLOD::IsTilePostReachBypassActive())
|
||||||
|
{
|
||||||
|
VoxelGenLOD::CompareSkippedPost(Density, [&]()
|
||||||
|
{
|
||||||
|
VF_ApplyOriginLandingFloorMC(
|
||||||
|
Density, WorldX, WorldY, WorldZ,
|
||||||
|
StrateTopZ, StrateBottomZ, SealThickness, BaseDensity, Radius);
|
||||||
|
}, VoxelGenLOD::ETilePostComparisonKind::OriginFloor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
float InternalDensity = -Density;
|
float InternalDensity = -Density;
|
||||||
VF_ApplyOriginLandingFloor(InternalDensity, WorldX, WorldY, WorldZ,
|
VF_ApplyOriginLandingFloor(InternalDensity, WorldX, WorldY, WorldZ,
|
||||||
StrateTopZ, StrateBottomZ, SealThickness, BaseDensity, Radius);
|
StrateTopZ, StrateBottomZ, SealThickness, BaseDensity, Radius);
|
||||||
|
|||||||
@@ -471,13 +471,20 @@ namespace VoxelPassageGeometry
|
|||||||
|
|
||||||
FORCEINLINE bool OriginLandingRoomTouchesLattice(
|
FORCEINLINE bool OriginLandingRoomTouchesLattice(
|
||||||
const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step,
|
const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step,
|
||||||
float StrateTopZ, float StrateBottomZ, float SealThickness, float OriginRadius)
|
float StrateTopZ, float StrateBottomZ, float SealThickness, float OriginRadius,
|
||||||
|
float ReachScale = 1.0f)
|
||||||
{
|
{
|
||||||
if (!VoxelBox.IsValid) return false;
|
if (!VoxelBox.IsValid) return false;
|
||||||
|
if (!VoxelMath::IsFinite(ReachScale) || ReachScale <= 0.0f)
|
||||||
|
{
|
||||||
|
// This is an invalid proof request, not an empty room. The manager treats a
|
||||||
|
// malformed scale as conservative/unknown before calling this helper.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const FOriginLandingGeometry Geometry = BuildOriginLandingGeometry(
|
const FOriginLandingGeometry Geometry = BuildOriginLandingGeometry(
|
||||||
StrateTopZ, StrateBottomZ, SealThickness, OriginRadius);
|
StrateTopZ, StrateBottomZ, SealThickness, OriginRadius);
|
||||||
if (!Geometry.bValid) return false;
|
if (!Geometry.bValid) return false;
|
||||||
const float Pad = LandingCarveBlendVoxels;
|
const float Pad = LandingCarveBlendVoxels * ReachScale;
|
||||||
return LatticeAxisHasSampleInInterval(
|
return LatticeAxisHasSampleInInterval(
|
||||||
FMath::Max((float)VoxelBox.Min.X, -Geometry.HalfWidth - Pad),
|
FMath::Max((float)VoxelBox.Min.X, -Geometry.HalfWidth - Pad),
|
||||||
FMath::Min((float)VoxelBox.Max.X, Geometry.HalfWidth + Pad),
|
FMath::Min((float)VoxelBox.Max.X, Geometry.HalfWidth + Pad),
|
||||||
|
|||||||
@@ -472,6 +472,14 @@ public:
|
|||||||
float BaseDensity, float SealThickness) const;
|
float BaseDensity, float SealThickness) const;
|
||||||
bool AnyPassageLandingFloorNearLattice(const FBox& VoxelBox,
|
bool AnyPassageLandingFloorNearLattice(const FBox& VoxelBox,
|
||||||
const FIntVector& LatticeOrigin, int32 Step) const;
|
const FIntVector& LatticeOrigin, int32 Step) const;
|
||||||
|
/** Conservative landing-room plus floor reach for the exact MC lattice and its halo. */
|
||||||
|
bool AnyPassageLandingNearLattice(const FBox& VoxelBox,
|
||||||
|
const FIntVector& LatticeOrigin, int32 Step,
|
||||||
|
float ReachScale = 1.0f) const;
|
||||||
|
/** Conservative sphere guard for all passage-owned structural posts and native D-floors. */
|
||||||
|
bool AnyPassageStructuralPostNearLattice(const FBox& VoxelBox,
|
||||||
|
const FIntVector& LatticeOrigin, int32 Step,
|
||||||
|
float ReachScale = 1.0f) const;
|
||||||
/** Exact presence test for a structural floor write on the MC lattice. */
|
/** Exact presence test for a structural floor write on the MC lattice. */
|
||||||
bool AnyLandingFloorAtLattice(const FBox& VoxelBox,
|
bool AnyLandingFloorAtLattice(const FBox& VoxelBox,
|
||||||
const FIntVector& LatticeOrigin, int32 Step) const;
|
const FIntVector& LatticeOrigin, int32 Step) const;
|
||||||
@@ -481,7 +489,8 @@ public:
|
|||||||
bool AnyOriginLandingFloorNearBox(const FVector& MinVoxel, const FVector& MaxVoxel) const;
|
bool AnyOriginLandingFloorNearBox(const FVector& MinVoxel, const FVector& MaxVoxel) const;
|
||||||
|
|
||||||
bool AnyOriginLandingNearLattice(const FBox& VoxelBox,
|
bool AnyOriginLandingNearLattice(const FBox& VoxelBox,
|
||||||
const FIntVector& LatticeOrigin, int32 Step) const;
|
const FIntVector& LatticeOrigin, int32 Step,
|
||||||
|
float ReachScale = 1.0f) const;
|
||||||
/** Exact conservative candidate for the post-disturbance origin-room air reassertion. */
|
/** Exact conservative candidate for the post-disturbance origin-room air reassertion. */
|
||||||
bool AnyOriginLandingAirNearLattice(const FBox& VoxelBox,
|
bool AnyOriginLandingAirNearLattice(const FBox& VoxelBox,
|
||||||
const FIntVector& LatticeOrigin, int32 Step,
|
const FIntVector& LatticeOrigin, int32 Step,
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
// Per-tile reach state for density posts.
|
||||||
|
//
|
||||||
|
// The marching-cubes worker installs this state once for the tile's lattice plus its one-point
|
||||||
|
// halo. Density helpers only read the bit that owns them. The inline TLS is intentional: the
|
||||||
|
// runtime hot path must not cross a DLL boundary for a single boolean load.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace VoxelGenLOD
|
||||||
|
{
|
||||||
|
enum ETilePostReach : uint8
|
||||||
|
{
|
||||||
|
OriginLandingReachable = 1u << 0,
|
||||||
|
PassageLandingReachable = 1u << 1,
|
||||||
|
PassageStructuralReachable= 1u << 2,
|
||||||
|
AllTilePostReach = OriginLandingReachable
|
||||||
|
| PassageLandingReachable
|
||||||
|
| PassageStructuralReachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ETilePostComparisonKind : uint8
|
||||||
|
{
|
||||||
|
Other = 0,
|
||||||
|
OriginAir,
|
||||||
|
OriginFloor,
|
||||||
|
PassageLandingSDF,
|
||||||
|
PassageLandingAir,
|
||||||
|
PassageTunnelAir,
|
||||||
|
PassageLandingFloor,
|
||||||
|
PassageStructuralPosts,
|
||||||
|
PassageNativeFloor,
|
||||||
|
PassageLandingRoomFloor,
|
||||||
|
Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
// A tile that is not being meshed, or a point query outside the mesher, must retain the
|
||||||
|
// canonical behaviour. GenerateMesh scopes this to its computed mask.
|
||||||
|
inline thread_local uint8 TilePostReachFlags = AllTilePostReach;
|
||||||
|
inline thread_local bool bTilePostReachDebug = false;
|
||||||
|
inline thread_local bool bTilePostReachBypass = false;
|
||||||
|
|
||||||
|
// These are diagnostics only. They never participate in a field decision.
|
||||||
|
inline std::atomic<bool> GTilePostReachDebugEnabled { false };
|
||||||
|
inline std::atomic<uint64> GTilePostReachTileCount { 0 };
|
||||||
|
inline std::atomic<uint64> GOriginLandingReachableTiles { 0 };
|
||||||
|
inline std::atomic<uint64> GPassageLandingReachableTiles { 0 };
|
||||||
|
inline std::atomic<uint64> GPassageStructuralReachableTiles { 0 };
|
||||||
|
inline std::atomic<uint64> GOriginLandingSkippedTiles { 0 };
|
||||||
|
inline std::atomic<uint64> GPassageLandingSkippedTiles { 0 };
|
||||||
|
inline std::atomic<uint64> GPassageStructuralSkippedTiles { 0 };
|
||||||
|
inline std::atomic<uint64> GSkippedPostComparisons { 0 };
|
||||||
|
inline std::atomic<uint64> GSkippedPostDifferences { 0 };
|
||||||
|
inline std::atomic<uint64> GSkippedPostComparisonsByKind[
|
||||||
|
static_cast<uint8>(ETilePostComparisonKind::Count)]{};
|
||||||
|
inline std::atomic<uint64> GSkippedPostDifferencesByKind[
|
||||||
|
static_cast<uint8>(ETilePostComparisonKind::Count)]{};
|
||||||
|
|
||||||
|
FORCEINLINE bool IsTilePostReachable(uint8 Bit)
|
||||||
|
{
|
||||||
|
return (TilePostReachFlags & Bit) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE bool IsOriginLandingReachable()
|
||||||
|
{
|
||||||
|
return IsTilePostReachable(OriginLandingReachable);
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE bool IsPassageLandingReachable()
|
||||||
|
{
|
||||||
|
return IsTilePostReachable(PassageLandingReachable);
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE bool IsPassageStructuralReachable()
|
||||||
|
{
|
||||||
|
return IsTilePostReachable(PassageStructuralReachable);
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE bool IsTilePostReachBypassActive()
|
||||||
|
{
|
||||||
|
return bTilePostReachBypass;
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE void RecordTileReachMask(uint8 Flags)
|
||||||
|
{
|
||||||
|
GTilePostReachTileCount.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
if ((Flags & OriginLandingReachable) != 0)
|
||||||
|
{
|
||||||
|
GOriginLandingReachableTiles.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GOriginLandingSkippedTiles.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
if ((Flags & PassageLandingReachable) != 0)
|
||||||
|
{
|
||||||
|
GPassageLandingReachableTiles.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GPassageLandingSkippedTiles.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
if ((Flags & PassageStructuralReachable) != 0)
|
||||||
|
{
|
||||||
|
GPassageStructuralReachableTiles.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GPassageStructuralSkippedTiles.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE bool SameFloatBits(float A, float B)
|
||||||
|
{
|
||||||
|
uint32 ABits = 0;
|
||||||
|
uint32 BBits = 0;
|
||||||
|
FMemory::Memcpy(&ABits, &A, sizeof(float));
|
||||||
|
FMemory::Memcpy(&BBits, &B, sizeof(float));
|
||||||
|
return ABits == BBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE void RecordSkippedPostComparison(
|
||||||
|
ETilePostComparisonKind Kind, bool bDifferent)
|
||||||
|
{
|
||||||
|
GSkippedPostComparisons.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
const uint8 KindIndex = static_cast<uint8>(Kind);
|
||||||
|
if (KindIndex < static_cast<uint8>(ETilePostComparisonKind::Count))
|
||||||
|
{
|
||||||
|
GSkippedPostComparisonsByKind[KindIndex].fetch_add(
|
||||||
|
1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
if (bDifferent)
|
||||||
|
{
|
||||||
|
GSkippedPostDifferences.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
if (KindIndex < static_cast<uint8>(ETilePostComparisonKind::Count))
|
||||||
|
{
|
||||||
|
GSkippedPostDifferencesByKind[KindIndex].fetch_add(
|
||||||
|
1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE void RecordSkippedPostComparison(bool bDifferent)
|
||||||
|
{
|
||||||
|
RecordSkippedPostComparison(ETilePostComparisonKind::Other, bDifferent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Run one skipped float post through its canonical body, then restore the fast-path value. */
|
||||||
|
template <typename CallableType>
|
||||||
|
FORCEINLINE void CompareSkippedPost(
|
||||||
|
float& Density, CallableType Callable,
|
||||||
|
ETilePostComparisonKind Kind = ETilePostComparisonKind::Other)
|
||||||
|
{
|
||||||
|
if (!bTilePostReachDebug || bTilePostReachBypass)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float Before = Density;
|
||||||
|
{
|
||||||
|
const bool PreviousBypass = bTilePostReachBypass;
|
||||||
|
bTilePostReachBypass = true;
|
||||||
|
Callable();
|
||||||
|
bTilePostReachBypass = PreviousBypass;
|
||||||
|
}
|
||||||
|
RecordSkippedPostComparison(Kind, !SameFloatBits(Before, Density));
|
||||||
|
Density = Before;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Compare a scalar proof input such as EvaluateModifierSDF without changing the field. */
|
||||||
|
FORCEINLINE void CompareSkippedPostValue(
|
||||||
|
float Candidate, float Canonical,
|
||||||
|
ETilePostComparisonKind Kind = ETilePostComparisonKind::Other)
|
||||||
|
{
|
||||||
|
if (bTilePostReachDebug && !bTilePostReachBypass)
|
||||||
|
{
|
||||||
|
RecordSkippedPostComparison(Kind, !SameFloatBits(Candidate, Canonical));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user