From 5489b5e3115de743ed896485003167912cf00a38 Mon Sep 17 00:00:00 2001 From: Fr0zka Date: Sun, 13 Sep 2026 23:50:30 +0200 Subject: [PATCH] perf(generation): inline exact IsFinite -- LOD0 ~11-12% faster, field-exact The sampler's last "unknown" LOD0 bucket (~13.8%) was ucrtbase.dll!_finite: FMath::IsFinite is an out-of-line CRT call on the MSVC game path, hit from the innermost tunnel, passage, density, spatial-index and meshing loops. - VoxelMath::IsFiniteFast(float/double) in VoxelTypes.h: memcpy to uint32/uint64, (Bits & ExpMask) != ExpMask. It is the same predicate for every bit pattern, with no FP comparison for fast-math to fold. The disassembly shows it inlined (movd/movq, mask, compare). - VoxelMath::IsFinite routes through a plain global, voxel.FastIsFinite (default 1), for the A/B. The CRT call remains only on the switch-off branch. Hot generation-path uses are replaced; no _finite / std::isfinite remains in Source/VoxelForge. Tool/editor/test code keeps FMath. - A test that can fail: VoxelForge.Math.IsFiniteExact compares against FMath::IsFinite over 22 explicit float/double patterns (+-0, denormals, min/max normal, +-Inf, quiet and signalling NaN payloads) plus 2,000,000 hashed patterns each: 0 mismatches. The explicit NaNs make a NaN-wrong helper fail. Field exact: 841/841 keyed tiles identical to NextHeadStaticTrace_20260913 (verdict/empty/ triangles), export OBJ unchanged (b3e5f4c3..9b377), capability 20,830 / 10,909. Within-round A/B, game path, two interleaved runs each, sampler off (off -> on): static LOD0 request p50 95.5 -> 84.8 ms generation p50/p95 70.3/114.1 -> 62.3/100.6 ms worker 35.4 -> 31.6 s (-10.5%) moving LOD0 request p50/p95 70.8/144.2 -> 62.0/126.1 ms generation p50/p95 62.6/113.4 -> 55.1/101.0 ms worker 102.6 -> 91.3 s (-11.0%) Sampler-on moving: the _finite/CRT cluster is gone. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01TaFdXhTdYMkW1xrV6ApT6Y --- .../Private/Tests/VoxelForgeIsFiniteTest.cpp | 181 +++++++ .../Private/VoxelCaveMorphology.cpp | 452 +++++++++--------- .../Private/VoxelDensityOpStack.cpp | 188 ++++---- Source/VoxelForge/Private/VoxelGenerator.cpp | 79 ++- .../Private/VoxelMarchingCubesMesher.cpp | 4 +- .../VoxelForge/Private/VoxelStrateManager.cpp | 332 ++++++------- Source/VoxelForge/Private/VoxelWorld.cpp | 10 +- Source/VoxelForge/Public/VoxelDensityOp.h | 2 +- .../Public/VoxelDensityPrimitives.h | 16 +- .../VoxelForge/Public/VoxelPassageGeometry.h | 65 +-- Source/VoxelForge/Public/VoxelTypes.h | 45 ++ 11 files changed, 816 insertions(+), 558 deletions(-) create mode 100644 Source/VoxelForge/Private/Tests/VoxelForgeIsFiniteTest.cpp diff --git a/Source/VoxelForge/Private/Tests/VoxelForgeIsFiniteTest.cpp b/Source/VoxelForge/Private/Tests/VoxelForgeIsFiniteTest.cpp new file mode 100644 index 0000000..dc21639 --- /dev/null +++ b/Source/VoxelForge/Private/Tests/VoxelForgeIsFiniteTest.cpp @@ -0,0 +1,181 @@ +// Bit-exact equivalence coverage for the generation-path finite predicate. + +#if WITH_DEV_AUTOMATION_TESTS + +#include "Misc/AutomationTest.h" + +#include "VoxelTypes.h" + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FVoxelForgeIsFiniteTest, + "VoxelForge.Math.IsFiniteExact", + EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::EngineFilter) + +namespace +{ + template + TValue ValueFromBits(TBits Bits) + { + TValue Value; + FMemory::Memcpy(&Value, &Bits, sizeof(Value)); + return Value; + } + + FORCEINLINE uint64 NextHashedBits(uint64& State) + { + State += 0x9E3779B97F4A7C15ull; + uint64 Z = State; + Z = (Z ^ (Z >> 30)) * 0xBF58476D1CE4E5B9ull; + Z = (Z ^ (Z >> 27)) * 0x94D049BB133111EBull; + return Z ^ (Z >> 31); + } + + struct FFloatPattern + { + const TCHAR* Name; + uint32 Bits; + }; + + struct FDoublePattern + { + const TCHAR* Name; + uint64 Bits; + }; +} + +bool FVoxelForgeIsFiniteTest::RunTest(const FString& Parameters) +{ + int32 FloatMismatches = 0; + int32 DoubleMismatches = 0; + + auto CheckFloat = [&](const TCHAR* Name, uint32 Bits) + { + const float Value = ValueFromBits(Bits); + const bool Fast = VoxelMath::IsFiniteFast(Value); + const bool Reference = FMath::IsFinite(Value); + if (Fast != Reference) + { + ++FloatMismatches; + if (FloatMismatches <= 8) + { + AddError(FString::Printf( + TEXT("%s: float bits 0x%08X produced fast=%s, FMath=%s"), + Name, Bits, + Fast ? TEXT("true") : TEXT("false"), + Reference ? TEXT("true") : TEXT("false"))); + } + } + }; + + auto CheckDouble = [&](const TCHAR* Name, uint64 Bits) + { + const double Value = ValueFromBits(Bits); + const bool Fast = VoxelMath::IsFiniteFast(Value); + const bool Reference = FMath::IsFinite(Value); + if (Fast != Reference) + { + ++DoubleMismatches; + if (DoubleMismatches <= 8) + { + AddError(FString::Printf( + TEXT("%s: double bits 0x%08X%08X produced fast=%s, FMath=%s"), + Name, + static_cast(Bits >> 32), static_cast(Bits), + Fast ? TEXT("true") : TEXT("false"), + Reference ? TEXT("true") : TEXT("false"))); + } + } + }; + + // Explicit classes make a NaN regression fail even if the random stream happens not to hit a + // NaN. In particular, a helper that only checks the sign/exponent incorrectly must fail here. + const FFloatPattern FloatPatterns[] = + { + { TEXT("+0"), 0x00000000u }, + { TEXT("-0"), 0x80000000u }, + { TEXT("+min subnormal"), 0x00000001u }, + { TEXT("-min subnormal"), 0x80000001u }, + { TEXT("+max subnormal"), 0x007FFFFFu }, + { TEXT("-max subnormal"), 0x807FFFFFu }, + { TEXT("+min normal"), 0x00800000u }, + { TEXT("-min normal"), 0x80800000u }, + { TEXT("+max normal"), 0x7F7FFFFFu }, + { TEXT("-max normal"), 0xFF7FFFFFu }, + { TEXT("+infinity"), 0x7F800000u }, + { TEXT("-infinity"), 0xFF800000u }, + { TEXT("+quiet NaN payload 1"), 0x7FC00001u }, + { TEXT("-quiet NaN payload 1"), 0xFFC00001u }, + { TEXT("+quiet NaN payload mixed"), 0x7FC12345u }, + { TEXT("-quiet NaN payload mixed"), 0xFFC12345u }, + { TEXT("+signalling NaN payload 1"), 0x7F800001u }, + { TEXT("-signalling NaN payload 1"), 0xFF800001u }, + { TEXT("+signalling NaN payload mixed"), 0x7FA00001u }, + { TEXT("-signalling NaN payload mixed"), 0xFFA00001u }, + { TEXT("+signalling NaN max payload"), 0x7FBFFFFFu }, + { TEXT("-signalling NaN max payload"), 0xFFBFFFFFu }, + }; + for (const FFloatPattern& Pattern : FloatPatterns) + { + CheckFloat(Pattern.Name, Pattern.Bits); + } + + const FDoublePattern DoublePatterns[] = + { + { TEXT("+0"), 0x0000000000000000ull }, + { TEXT("-0"), 0x8000000000000000ull }, + { TEXT("+min subnormal"), 0x0000000000000001ull }, + { TEXT("-min subnormal"), 0x8000000000000001ull }, + { TEXT("+max subnormal"), 0x000FFFFFFFFFFFFFull }, + { TEXT("-max subnormal"), 0x800FFFFFFFFFFFFFull }, + { TEXT("+min normal"), 0x0010000000000000ull }, + { TEXT("-min normal"), 0x8010000000000000ull }, + { TEXT("+max normal"), 0x7FEFFFFFFFFFFFFFull }, + { TEXT("-max normal"), 0xFFEFFFFFFFFFFFFFull }, + { TEXT("+infinity"), 0x7FF0000000000000ull }, + { TEXT("-infinity"), 0xFFF0000000000000ull }, + { TEXT("+quiet NaN payload 1"), 0x7FF8000000000001ull }, + { TEXT("-quiet NaN payload 1"), 0xFFF8000000000001ull }, + { TEXT("+quiet NaN payload mixed"), 0x7FF8123456789ABCull }, + { TEXT("-quiet NaN payload mixed"), 0xFFF8123456789ABCull }, + { TEXT("+signalling NaN payload 1"), 0x7FF0000000000001ull }, + { TEXT("-signalling NaN payload 1"), 0xFFF0000000000001ull }, + { TEXT("+signalling NaN payload mixed"), 0x7FF4000000000001ull }, + { TEXT("-signalling NaN payload mixed"), 0xFFF4000000000001ull }, + { TEXT("+signalling NaN max payload"), 0x7FF7FFFFFFFFFFFFull }, + { TEXT("-signalling NaN max payload"), 0xFFF7FFFFFFFFFFFFull }, + }; + for (const FDoublePattern& Pattern : DoublePatterns) + { + CheckDouble(Pattern.Name, Pattern.Bits); + } + + // Deterministic hash-generated raw bit patterns exercise ordinary values and special values + // without converting through floating point. The loop counts are intentionally large enough + // to make this a broad bit-pattern check rather than a handful of representative numbers. + constexpr uint32 NumRandomFloatPatterns = 2000000u; + constexpr uint32 NumRandomDoublePatterns = 2000000u; + uint64 FloatState = 0x123456789ABCDEF0ull; + for (uint32 Index = 0; Index < NumRandomFloatPatterns; ++Index) + { + CheckFloat(TEXT("hashed random"), static_cast(NextHashedBits(FloatState))); + } + + uint64 DoubleState = 0x0FEDCBA987654321ull; + for (uint32 Index = 0; Index < NumRandomDoublePatterns; ++Index) + { + CheckDouble(TEXT("hashed random"), NextHashedBits(DoubleState)); + } + + AddInfo(FString::Printf( + TEXT("Compared %d explicit + %u hashed float patterns and %d explicit + %u hashed double " + "patterns; float mismatches=%d, double mismatches=%d."), + UE_ARRAY_COUNT(FloatPatterns), NumRandomFloatPatterns, + UE_ARRAY_COUNT(DoublePatterns), NumRandomDoublePatterns, + FloatMismatches, DoubleMismatches)); + + TestEqual(TEXT("float finite predicate has no mismatches"), FloatMismatches, 0); + TestEqual(TEXT("double finite predicate has no mismatches"), DoubleMismatches, 0); + return FloatMismatches == 0 && DoubleMismatches == 0; +} + +#endif // WITH_DEV_AUTOMATION_TESTS diff --git a/Source/VoxelForge/Private/VoxelCaveMorphology.cpp b/Source/VoxelForge/Private/VoxelCaveMorphology.cpp index f1ca390..d4c90e1 100644 --- a/Source/VoxelForge/Private/VoxelCaveMorphology.cpp +++ b/Source/VoxelForge/Private/VoxelCaveMorphology.cpp @@ -121,11 +121,11 @@ namespace double X, double Y) { FVFFloorReliefColumnKey Key; - const bool bIntegerX = FMath::IsFinite(X) + const bool bIntegerX = VoxelMath::IsFinite(X) && X >= static_cast(MIN_int32) && X <= static_cast(MAX_int32) && FMath::FloorToDouble(X) == X; - const bool bIntegerY = FMath::IsFinite(Y) + const bool bIntegerY = VoxelMath::IsFinite(Y) && Y >= static_cast(MIN_int32) && Y <= static_cast(MAX_int32) && FMath::FloorToDouble(Y) == Y; @@ -294,19 +294,19 @@ namespace { OutBound = 0.0f; if (!(Strength > 0.0f) - || !FMath::IsFinite(Strength) - || !FMath::IsFinite(Frequency) + || !VoxelMath::IsFinite(Strength) + || !VoxelMath::IsFinite(Frequency) || !(Envelope > 0.0f) - || !FMath::IsFinite(Envelope) + || !VoxelMath::IsFinite(Envelope) || !(ReliefScale > 0.0f) - || !FMath::IsFinite(ReliefScale)) + || !VoxelMath::IsFinite(ReliefScale)) { return false; } OutBound = FMath::Abs(Strength) * VOXEL_NOISE_SCALE * VF_FloorReliefNoiseAbsBound * Envelope * ReliefScale; - return FMath::IsFinite(OutBound) && OutBound >= 0.0f; + return VoxelMath::IsFinite(OutBound) && OutBound >= 0.0f; } // Keep corridor relief on the exact two-octave field already used by room floors. The @@ -319,8 +319,8 @@ namespace const FVFFloorReliefColumnKey& Column) { if (!(Tunnel.FloorReliefStrength > 0.0f) - || !FMath::IsFinite(Tunnel.FloorReliefStrength) - || !FMath::IsFinite(Tunnel.FloorReliefFrequency) + || !VoxelMath::IsFinite(Tunnel.FloorReliefStrength) + || !VoxelMath::IsFinite(Tunnel.FloorReliefFrequency) || !(Envelope > 0.0f) || !(ReliefScale > 0.0f)) { @@ -391,8 +391,8 @@ namespace const FCachedTunnel& Tunnel) { if (!(Tunnel.FloorReliefStrength > 0.0f) - || !FMath::IsFinite(Tunnel.FloorReliefStrength) - || !FMath::IsFinite(Tunnel.FloorReliefFrequency)) + || !VoxelMath::IsFinite(Tunnel.FloorReliefStrength) + || !VoxelMath::IsFinite(Tunnel.FloorReliefFrequency)) { return 0.0f; } @@ -442,7 +442,7 @@ namespace const float ReliefGradientBound = NoiseGradientBound + EnvelopeDerivativeBound; if (!(ReliefGradientBound > KINDA_SMALL_NUMBER) - || !FMath::IsFinite(ReliefGradientBound)) + || !VoxelMath::IsFinite(ReliefGradientBound)) { return 0.0f; } @@ -462,8 +462,8 @@ namespace { if (bTerraced || !(Tunnel.FloorReliefStrength > 0.0f) - || !FMath::IsFinite(Tunnel.FloorReliefStrength) - || !FMath::IsFinite(Tunnel.FloorReliefFrequency)) + || !VoxelMath::IsFinite(Tunnel.FloorReliefStrength) + || !VoxelMath::IsFinite(Tunnel.FloorReliefFrequency)) { return 0.0f; } @@ -471,7 +471,7 @@ namespace const float HorizontalRun = FVector2D( static_cast(B.X - A.X), static_cast(B.Y - A.Y)).Size(); if (!(HorizontalRun > KINDA_SMALL_NUMBER) - || !FMath::IsFinite(BaseGradient) + || !VoxelMath::IsFinite(BaseGradient) || BaseGradient < 0.0f) { return 0.0f; @@ -501,7 +501,7 @@ namespace const float ReliefGradientBound = NoiseGradientBound + EnvelopeDerivativeBound; if (!(ReliefGradientBound > KINDA_SMALL_NUMBER) - || !FMath::IsFinite(ReliefGradientBound)) + || !VoxelMath::IsFinite(ReliefGradientBound)) { return 0.0f; } @@ -536,11 +536,11 @@ namespace ControlPoints[Index], FMath::Abs(ControlRadii[Index])); } - const float GentleThreshold = FMath::IsFinite( + const float GentleThreshold = VoxelMath::IsFinite( Tunnel.TunnelFloorGentleSlopeThreshold) ? FMath::Max(Tunnel.TunnelFloorGentleSlopeThreshold, 0.0f) : VoxelPassageGeometry::PlayerWalkableFloorMaxGradient; - const float LegacyStepHeight = FMath::IsFinite( + const float LegacyStepHeight = VoxelMath::IsFinite( Tunnel.TunnelFloorTerraceStepHeight) ? FMath::Max(Tunnel.TunnelFloorTerraceStepHeight, 0.0f) : 0.0f; @@ -568,7 +568,7 @@ namespace && TotalFloorDelta > TotalHorizontalRun * GentleThreshold; if (bGlobalTerrace) { - const float MaxLedgeHeight = FMath::IsFinite( + const float MaxLedgeHeight = VoxelMath::IsFinite( Tunnel.TunnelFloorMaxLedgeHeight) ? FMath::Max(Tunnel.TunnelFloorMaxLedgeHeight, KINDA_SMALL_NUMBER) : KINDA_SMALL_NUMBER; @@ -759,10 +759,10 @@ namespace - FMath::Abs(ControlRadiusData[ControlIndex]); const float StoredFloorZ = Endpoint == 0 ? MouthFloorZA : MouthFloorZB; const float FloorZ = StoredFloorZ > -FLT_MAX - && FMath::IsFinite(StoredFloorZ) + && VoxelMath::IsFinite(StoredFloorZ) ? StoredFloorZ : DefaultFloorZ; - if (!FMath::IsFinite(FloorZ)) + if (!VoxelMath::IsFinite(FloorZ)) { continue; } @@ -1063,14 +1063,14 @@ namespace && Profile != nullptr && Tunnel.TunnelFloorLedgeCountPreference > 0; const bool bAnchorCapsule = bUseAuthoredProfile - && FMath::IsFinite(NaturalFloorZ); + && VoxelMath::IsFinite(NaturalFloorZ); const auto EvaluateAnchoredCapsule = [ &Position, &A, &B, RadiusA, RadiusB, NaturalFloorZ, bAnchorCapsule] (float FloorZ) -> float { const float Shift = FloorZ - NaturalFloorZ; if (!bAnchorCapsule - || !FMath::IsFinite(Shift) + || !VoxelMath::IsFinite(Shift) || FMath::Abs(Shift) <= KINDA_SMALL_NUMBER) { return VoxelSDF::TaperedCapsule( @@ -1120,7 +1120,7 @@ namespace // the translated arch only when the column relief actually changed its anchor; most // samples retain the cheap provisional result. if (bAnchorCapsule - && FMath::IsFinite(FloorZ) + && VoxelMath::IsFinite(FloorZ) && FMath::Abs(FloorZ - FloorTerms.BaseFloorZ) > KINDA_SMALL_NUMBER) { SegmentSDF = EvaluateAnchoredCapsule(FloorZ); @@ -1275,8 +1275,8 @@ namespace } return bFound - && FMath::IsFinite(OutFloorZ) - && FMath::IsFinite(OutSupportRadius) + && VoxelMath::IsFinite(OutFloorZ) + && VoxelMath::IsFinite(OutSupportRadius) && BestDistanceSquared <= FMath::Square(OutSupportRadius); } @@ -1316,7 +1316,7 @@ bool FChunkSDFSpatialIndex::GetRange( OutBegin = 0; OutEnd = 0; if (!bValid - || !FMath::IsFinite(WorldX) || !FMath::IsFinite(WorldY) + || !VoxelMath::IsFinite(WorldX) || !VoxelMath::IsFinite(WorldY) || NumCellsX <= 0 || NumCellsY <= 0) { return false; @@ -1369,8 +1369,8 @@ namespace static bool VF_SetSpatialBounds( float CenterX, float CenterY, float Radius, FVFSpatialBounds& OutBounds) { - if (!FMath::IsFinite(CenterX) || !FMath::IsFinite(CenterY) - || !FMath::IsFinite(Radius) || Radius < 0.0f) + if (!VoxelMath::IsFinite(CenterX) || !VoxelMath::IsFinite(CenterY) + || !VoxelMath::IsFinite(Radius) || Radius < 0.0f) { return false; } @@ -1378,8 +1378,8 @@ namespace OutBounds.MinY = CenterY - Radius; OutBounds.MaxX = CenterX + Radius; OutBounds.MaxY = CenterY + Radius; - return FMath::IsFinite(OutBounds.MinX) && FMath::IsFinite(OutBounds.MinY) - && FMath::IsFinite(OutBounds.MaxX) && FMath::IsFinite(OutBounds.MaxY) + return VoxelMath::IsFinite(OutBounds.MinX) && VoxelMath::IsFinite(OutBounds.MinY) + && VoxelMath::IsFinite(OutBounds.MaxX) && VoxelMath::IsFinite(OutBounds.MaxY) && OutBounds.MinX <= OutBounds.MaxX && OutBounds.MinY <= OutBounds.MaxY; } @@ -1387,9 +1387,9 @@ namespace float MinX, float MinY, float MaxX, float MaxY, float Radius, FVFSpatialBounds& OutBounds) { - if (!FMath::IsFinite(MinX) || !FMath::IsFinite(MinY) - || !FMath::IsFinite(MaxX) || !FMath::IsFinite(MaxY) - || !FMath::IsFinite(Radius) || Radius < 0.0f + if (!VoxelMath::IsFinite(MinX) || !VoxelMath::IsFinite(MinY) + || !VoxelMath::IsFinite(MaxX) || !VoxelMath::IsFinite(MaxY) + || !VoxelMath::IsFinite(Radius) || Radius < 0.0f || MinX > MaxX || MinY > MaxY) { return false; @@ -1398,8 +1398,8 @@ namespace OutBounds.MinY = MinY - Radius; OutBounds.MaxX = MaxX + Radius; OutBounds.MaxY = MaxY + Radius; - return FMath::IsFinite(OutBounds.MinX) && FMath::IsFinite(OutBounds.MinY) - && FMath::IsFinite(OutBounds.MaxX) && FMath::IsFinite(OutBounds.MaxY) + return VoxelMath::IsFinite(OutBounds.MinX) && VoxelMath::IsFinite(OutBounds.MinY) + && VoxelMath::IsFinite(OutBounds.MaxX) && VoxelMath::IsFinite(OutBounds.MaxY) && OutBounds.MinX <= OutBounds.MaxX && OutBounds.MinY <= OutBounds.MaxY; } @@ -1855,7 +1855,7 @@ static bool VF_ComputeTerrainOpEnvelope( auto RecordAuthoredExtent = [&](float Extent) -> bool { - if (!FMath::IsFinite(Extent) || Extent < 0.0f) + if (!VoxelMath::IsFinite(Extent) || Extent < 0.0f) { return false; } @@ -1865,12 +1865,12 @@ static bool VF_ComputeTerrainOpEnvelope( }; auto RecordReach = [&](float Reach) -> bool { - if (!FMath::IsFinite(Reach) || Reach < 0.0f) + if (!VoxelMath::IsFinite(Reach) || Reach < 0.0f) { return false; } MaxReach = FMath::Max(MaxReach, Reach); - return FMath::IsFinite(MaxReach); + return VoxelMath::IsFinite(MaxReach); }; auto RecordDetail = [&](float EffectRange) -> bool { @@ -1922,7 +1922,7 @@ static bool VF_ComputeTerrainOpEnvelope( OpParams.PinchLength}; for (const float Value : OperationValues) { - if (!FMath::IsFinite(Value)) + if (!VoxelMath::IsFinite(Value)) { return false; } @@ -2089,9 +2089,9 @@ static bool VF_ComputeTerrainOpEnvelope( { for (const FStrateTerrainOpEntry& Entry : *TerrainOps) { - if (!FMath::IsFinite(Entry.Probability) + if (!VoxelMath::IsFinite(Entry.Probability) || Entry.Probability < 0.0f || Entry.Probability > 1.0f - || !FMath::IsFinite(Entry.Weight) + || !VoxelMath::IsFinite(Entry.Weight) || Entry.Weight < 0.0f || Entry.Weight > 3.0f) { return false; @@ -2144,8 +2144,8 @@ static bool VF_ComputeTerrainOpEnvelope( // the legacy/no-op path store extra rooms and could change the canonical field. Active pools // publish the wider envelope so the tile proof and STORE cull see pits/chimneys/detail reach. OutReach = bHasActiveTerrainOperation ? MaxReach : 0.0f; - return FMath::IsFinite(OutReach) - && FMath::IsFinite(MaxAuthoredExtent) + return VoxelMath::IsFinite(OutReach) + && VoxelMath::IsFinite(MaxAuthoredExtent) && MaxAuthoredExtent <= VF_MaxProvenTerrainOpExtent; } @@ -2183,7 +2183,7 @@ static bool VF_ComputeRoomGraphReach( Params.StrateBottomWorldZ}; for (const float Value : RelevantParams) { - if (!FMath::IsFinite(Value)) + if (!VoxelMath::IsFinite(Value)) { return false; } @@ -2295,10 +2295,10 @@ static bool VF_ComputeRoomGraphReach( const float CollectMargin = 2.0f * MaxTunnelLength + FMath::Max(DirectRoomReach, PairAabbReach); - if (!FMath::IsFinite(DirectRoomReach) - || !FMath::IsFinite(PairAabbReach) - || !FMath::IsFinite(PairZReach) - || !FMath::IsFinite(CollectMargin) + if (!VoxelMath::IsFinite(DirectRoomReach) + || !VoxelMath::IsFinite(PairAabbReach) + || !VoxelMath::IsFinite(PairZReach) + || !VoxelMath::IsFinite(CollectMargin) || DirectRoomReach < 0.0f || PairAabbReach < 0.0f || PairZReach < 0.0f @@ -2343,11 +2343,11 @@ static bool VF_MayHaveRoomGraphFeature( for (const FBuildRoom& Room : Rooms) { - if (!FMath::IsFinite(Room.Center.X) - || !FMath::IsFinite(Room.Center.Y) - || !FMath::IsFinite(Room.Center.Z) - || !FMath::IsFinite(Room.RadiusXY) - || !FMath::IsFinite(Room.RadiusZ)) + if (!VoxelMath::IsFinite(Room.Center.X) + || !VoxelMath::IsFinite(Room.Center.Y) + || !VoxelMath::IsFinite(Room.Center.Z) + || !VoxelMath::IsFinite(Room.RadiusXY) + || !VoxelMath::IsFinite(Room.RadiusZ)) { return true; } @@ -2355,7 +2355,7 @@ static bool VF_MayHaveRoomGraphFeature( Room.RadiusXY, Room.RadiusZ) + Reach.FloorReliefEnvelope + Reach.BlendEnvelope * 3.0f; const float RoomReach = FMath::Max(BaseRoomReach, Reach.TerrainOpReach); - if (!FMath::IsFinite(RoomReach) || RoomReach < 0.0f) + if (!VoxelMath::IsFinite(RoomReach) || RoomReach < 0.0f) { return true; } @@ -2372,7 +2372,7 @@ static bool VF_MayHaveRoomGraphFeature( SearchMinY - RoomReach, SearchMaxX + RoomReach, SearchMaxY + RoomReach); - if (!FMath::IsFinite(DistanceSquared)) + if (!VoxelMath::IsFinite(DistanceSquared)) { return true; } @@ -2412,7 +2412,7 @@ static bool VF_MayHaveRoomGraphFeature( } const float EuclideanDistance = static_cast( FVector::Dist(FirstCenter, SecondCenter)); - if (!FMath::IsFinite(EuclideanDistance)) + if (!VoxelMath::IsFinite(EuclideanDistance)) { return true; } @@ -2436,8 +2436,8 @@ static bool VF_MayHaveRoomGraphFeature( static_cast(FirstCenter.Y), static_cast(SecondCenter.Y)) + Reach.PairAabbReach; - if (!FMath::IsFinite(PairMinX) || !FMath::IsFinite(PairMinY) - || !FMath::IsFinite(PairMaxX) || !FMath::IsFinite(PairMaxY)) + if (!VoxelMath::IsFinite(PairMinX) || !VoxelMath::IsFinite(PairMinY) + || !VoxelMath::IsFinite(PairMaxX) || !VoxelMath::IsFinite(PairMaxY)) { return true; } @@ -2453,7 +2453,7 @@ static bool VF_MayHaveRoomGraphFeature( #if 0 const float MaxTunnelLengthSquared = Reach.MaxTunnelLength * Reach.MaxTunnelLength; - if (!FMath::IsFinite(MaxTunnelLengthSquared)) + if (!VoxelMath::IsFinite(MaxTunnelLengthSquared)) { return true; } @@ -2493,7 +2493,7 @@ static bool VF_MayHaveRoomGraphFeature( const float DistanceSquared = static_cast( FVector::DistSquared( Rooms[Index].Center, Rooms[Other].Center)); - if (!FMath::IsFinite(DistanceSquared)) + if (!VoxelMath::IsFinite(DistanceSquared)) { return -2; } @@ -2502,7 +2502,7 @@ static bool VF_MayHaveRoomGraphFeature( continue; } const float Metric = LinkMetric(Index, Other); - if (!FMath::IsFinite(Metric)) + if (!VoxelMath::IsFinite(Metric)) { return -2; } @@ -2537,7 +2537,7 @@ static bool VF_MayHaveRoomGraphFeature( { const float EuclideanDistance = static_cast( FVector::Dist(Rooms[First].Center, Rooms[Second].Center)); - if (!FMath::IsFinite(EuclideanDistance)) + if (!VoxelMath::IsFinite(EuclideanDistance)) { return true; } @@ -2552,7 +2552,7 @@ static bool VF_MayHaveRoomGraphFeature( CheckDistance += VerticalSeparation * Params.TunnelHorizontalBias * 5.0f; } - if (!FMath::IsFinite(CheckDistance) + if (!VoxelMath::IsFinite(CheckDistance) || CheckDistance > Reach.MaxTunnelLength) { continue; @@ -2589,8 +2589,8 @@ static bool VF_MayHaveRoomGraphFeature( static_cast(Rooms[First].Center.Y), static_cast(Rooms[Second].Center.Y)) + Reach.PairAabbReach; - if (!FMath::IsFinite(PairMinX) || !FMath::IsFinite(PairMinY) - || !FMath::IsFinite(PairMaxX) || !FMath::IsFinite(PairMaxY)) + if (!VoxelMath::IsFinite(PairMinX) || !VoxelMath::IsFinite(PairMinY) + || !VoxelMath::IsFinite(PairMaxX) || !VoxelMath::IsFinite(PairMaxY)) { return true; } @@ -2813,16 +2813,16 @@ namespace OutCentreSupportIndex = INDEX_NONE; OutRequiredSupportCount = 0; - if (!FMath::IsFinite(Settings.PlayerCapsuleRadiusVoxels) + if (!VoxelMath::IsFinite(Settings.PlayerCapsuleRadiusVoxels) || Settings.PlayerCapsuleRadiusVoxels <= 0.0f - || !FMath::IsFinite(Settings.PlayerCapsuleHalfHeightVoxels) + || !VoxelMath::IsFinite(Settings.PlayerCapsuleHalfHeightVoxels) || Settings.PlayerCapsuleHalfHeightVoxels <= 0.0f - || !FMath::IsFinite(Settings.PlayerMaxStepHeightMeters) + || !VoxelMath::IsFinite(Settings.PlayerMaxStepHeightMeters) || Settings.PlayerMaxStepHeightMeters < 0.0f - || !FMath::IsFinite(Settings.PlayerWalkableFloorAngleDegrees) + || !VoxelMath::IsFinite(Settings.PlayerWalkableFloorAngleDegrees) || Settings.PlayerWalkableFloorAngleDegrees < 0.0f || Settings.PlayerWalkableFloorAngleDegrees > 90.0f - || !FMath::IsFinite(Settings.PlayerSupportPatchMinCoverageFraction) + || !VoxelMath::IsFinite(Settings.PlayerSupportPatchMinCoverageFraction) || Settings.PlayerSupportPatchMinCoverageFraction <= 0.0f || Settings.PlayerSupportPatchMinCoverageFraction > 1.0f) { @@ -2924,9 +2924,9 @@ namespace float& OutInnerTop, float& OutInnerBottom) { - if (!FMath::IsFinite(StrateTopZ) - || !FMath::IsFinite(StrateBottomZ) - || !FMath::IsFinite(BoundarySealThickness) + if (!VoxelMath::IsFinite(StrateTopZ) + || !VoxelMath::IsFinite(StrateBottomZ) + || !VoxelMath::IsFinite(BoundarySealThickness) || BoundarySealThickness < 0.0f || StrateTopZ <= StrateBottomZ) { @@ -2938,7 +2938,7 @@ namespace { const double DerivedMargin = 2.0 * static_cast(BoundarySealThickness); - if (!FMath::IsFinite(DerivedMargin) + if (!VoxelMath::IsFinite(DerivedMargin) || DerivedMargin > static_cast(INT32_MAX)) { return false; @@ -2952,8 +2952,8 @@ namespace OutInnerBottom = StrateBottomZ + static_cast(InteriorMarginVoxels); OutInnerTop = StrateTopZ - static_cast(InteriorMarginVoxels); - return FMath::IsFinite(OutInnerTop) - && FMath::IsFinite(OutInnerBottom) + return VoxelMath::IsFinite(OutInnerTop) + && VoxelMath::IsFinite(OutInnerBottom) && OutInnerTop > OutInnerBottom; } @@ -2967,12 +2967,12 @@ namespace FVector& OutPoint) { OutPoint = FVector::ZeroVector; - if (!FMath::IsFinite(CandidateFeetPoint.X) - || !FMath::IsFinite(CandidateFeetPoint.Y) - || !FMath::IsFinite(CandidateFeetPoint.Z) - || !FMath::IsFinite(StrateTopZ) - || !FMath::IsFinite(StrateBottomZ) - || !FMath::IsFinite(BoundarySealThickness) + if (!VoxelMath::IsFinite(CandidateFeetPoint.X) + || !VoxelMath::IsFinite(CandidateFeetPoint.Y) + || !VoxelMath::IsFinite(CandidateFeetPoint.Z) + || !VoxelMath::IsFinite(StrateTopZ) + || !VoxelMath::IsFinite(StrateBottomZ) + || !VoxelMath::IsFinite(BoundarySealThickness) || BoundarySealThickness < 0.0f || StrateTopZ <= StrateBottomZ) { @@ -2993,7 +2993,7 @@ namespace const double MaxStepHeightVoxelsReal = static_cast(Settings.PlayerMaxStepHeightMeters) / static_cast(FVoxelPlayerCapsuleConstants::VoxelSizeMeters); - if (!FMath::IsFinite(MaxStepHeightVoxelsReal) + if (!VoxelMath::IsFinite(MaxStepHeightVoxelsReal) || MaxStepHeightVoxelsReal < 0.0 || MaxStepHeightVoxelsReal > 4096.0) { @@ -3028,8 +3028,8 @@ namespace CandidateFeetPoint.X + static_cast(Offset.X), CandidateFeetPoint.Y + static_cast(Offset.Y), UpperZ); - if (!FMath::IsFinite(LowerDensity) - || !FMath::IsFinite(UpperDensity) + if (!VoxelMath::IsFinite(LowerDensity) + || !VoxelMath::IsFinite(UpperDensity) || !(LowerDensity <= 0.0f) || !(UpperDensity > 0.0f)) { @@ -3037,7 +3037,7 @@ namespace } const float Denominator = UpperDensity - LowerDensity; - if (!FMath::IsFinite(Denominator) || Denominator <= 0.0f) + if (!VoxelMath::IsFinite(Denominator) || Denominator <= 0.0f) { continue; } @@ -3045,7 +3045,7 @@ namespace -LowerDensity / Denominator, 0.0f, 1.0f); const float SurfaceHeight = LowerZ + Fraction; const float CandidateBottom = CandidateFeetPoint.Z - 0.5f; - if (!FMath::IsFinite(SurfaceHeight) + if (!VoxelMath::IsFinite(SurfaceHeight) || SurfaceHeight > CandidateBottom + KINDA_SMALL_NUMBER || SurfaceHeight < CandidateBottom - MaxStepHeightVoxels - KINDA_SMALL_NUMBER) @@ -3074,7 +3074,7 @@ namespace ++SupportCount; SupportHeight = FMath::Max(SupportHeight, SupportHeights[Index]); } - if (SupportCount < RequiredSupportCount || !FMath::IsFinite(SupportHeight)) + if (SupportCount < RequiredSupportCount || !VoxelMath::IsFinite(SupportHeight)) { return false; } @@ -3100,7 +3100,7 @@ namespace } const float SupportNormalZ = 1.0f / FMath::Sqrt( 1.0f + MaximumGradient * MaximumGradient); - if (!FMath::IsFinite(SupportNormalZ) + if (!VoxelMath::IsFinite(SupportNormalZ) || SupportNormalZ + KINDA_SMALL_NUMBER < MinimumWalkableNormalZ) { return false; @@ -3119,7 +3119,7 @@ namespace CandidateFeetPoint.X + static_cast(Offset.X), CandidateFeetPoint.Y + static_cast(Offset.Y), SampleZ); - if (!FMath::IsFinite(Density) || !(Density > 0.0f)) + if (!VoxelMath::IsFinite(Density) || !(Density > 0.0f)) { return false; } @@ -3137,8 +3137,8 @@ namespace const float OutputZ = SupportHeight + 0.5f; const float OccupiedTop = SupportHeight + 2.0f * Settings.PlayerCapsuleHalfHeightVoxels; - if (!FMath::IsFinite(InnerBottom) || !FMath::IsFinite(InnerTop) - || !FMath::IsFinite(OutputZ) || !FMath::IsFinite(OccupiedTop) + if (!VoxelMath::IsFinite(InnerBottom) || !VoxelMath::IsFinite(InnerTop) + || !VoxelMath::IsFinite(OutputZ) || !VoxelMath::IsFinite(OccupiedTop) || OutputZ <= InnerBottom || OccupiedTop >= InnerTop) { @@ -3147,9 +3147,9 @@ namespace OutPoint = FVector(CandidateFeetPoint.X, CandidateFeetPoint.Y, OutputZ); return !OutPoint.ContainsNaN() - && FMath::IsFinite(OutPoint.X) - && FMath::IsFinite(OutPoint.Y) - && FMath::IsFinite(OutPoint.Z); + && VoxelMath::IsFinite(OutPoint.X) + && VoxelMath::IsFinite(OutPoint.Y) + && VoxelMath::IsFinite(OutPoint.Z); } struct FVFRoomLandingSite @@ -3223,9 +3223,9 @@ namespace const FVector Mapped = VF_ApplyCaveWarp(WorldPoint, Params, Seed); const FVector Error = TargetSDFPoint - Mapped; if (!Error.ContainsNaN() - && FMath::IsFinite(Error.X) - && FMath::IsFinite(Error.Y) - && FMath::IsFinite(Error.Z)) + && VoxelMath::IsFinite(Error.X) + && VoxelMath::IsFinite(Error.Y) + && VoxelMath::IsFinite(Error.Z)) { WorldPoint.X += Error.X; WorldPoint.Y += Error.Y; @@ -3413,7 +3413,7 @@ namespace { OutPoint = FVector::ZeroVector; const float FloorZ = VF_RoomFloorZ(Site, WarpSeed, Params); - if (!FMath::IsFinite(FloorZ)) + if (!VoxelMath::IsFinite(FloorZ)) { return false; } @@ -4198,9 +4198,9 @@ namespace float MaxLateralSnap, FVector& OutPoint) { - if (!FMath::IsFinite(StrateTopZ) || !FMath::IsFinite(StrateBottomZ) - || !FMath::IsFinite(WorldX) || !FMath::IsFinite(WorldY) - || !FMath::IsFinite(MaxLateralSnap) || MaxLateralSnap < 0.0f + if (!VoxelMath::IsFinite(StrateTopZ) || !VoxelMath::IsFinite(StrateBottomZ) + || !VoxelMath::IsFinite(WorldX) || !VoxelMath::IsFinite(WorldY) + || !VoxelMath::IsFinite(MaxLateralSnap) || MaxLateralSnap < 0.0f || StrateTopZ <= StrateBottomZ) { return false; @@ -4208,11 +4208,11 @@ namespace const float CellSize = Params.RoomSpacing; const float RadiusEnvelope = FMath::Max(Params.MinRoomRadius, Params.MaxRoomRadius); - if (!FMath::IsFinite(CellSize) || !FMath::IsFinite(Params.RoomDensity) - || !FMath::IsFinite(Params.MinRoomRadius) || !FMath::IsFinite(Params.MaxRoomRadius) - || !FMath::IsFinite(Params.RoomHeightRatio) - || !FMath::IsFinite(Params.BoundarySealThickness) - || !FMath::IsFinite(Params.MaxTunnelLength) + if (!VoxelMath::IsFinite(CellSize) || !VoxelMath::IsFinite(Params.RoomDensity) + || !VoxelMath::IsFinite(Params.MinRoomRadius) || !VoxelMath::IsFinite(Params.MaxRoomRadius) + || !VoxelMath::IsFinite(Params.RoomHeightRatio) + || !VoxelMath::IsFinite(Params.BoundarySealThickness) + || !VoxelMath::IsFinite(Params.MaxTunnelLength) || CellSize <= 0.0f || Params.RoomDensity <= 0.0f || Params.MaxTunnelLength <= 0.0f || RadiusEnvelope <= 0.0f || Params.RoomHeightRatio <= 0.0f @@ -4231,7 +4231,7 @@ namespace const float StrateMinZ = StrateBottomZ + Params.BoundarySealThickness + RoomZBuffer; const float StrateMaxZ = StrateTopZ - Params.BoundarySealThickness - RoomZBuffer; const float StrateRangeZ = StrateMaxZ - StrateMinZ; - if (!FMath::IsFinite(StrateRangeZ) || StrateRangeZ <= 0.0f) + if (!VoxelMath::IsFinite(StrateRangeZ) || StrateRangeZ <= 0.0f) { return false; } @@ -4380,7 +4380,7 @@ namespace // malformed but finite parameters never turn a boundary value into an open-point claim. const float InnerTop = StrateTopZ - Params.BoundarySealThickness; const float InnerBottom = StrateBottomZ + Params.BoundarySealThickness; - if (!FMath::IsFinite(BestZ) || BestZ <= InnerBottom || BestZ >= InnerTop) + if (!VoxelMath::IsFinite(BestZ) || BestZ <= InnerBottom || BestZ >= InnerTop) { return false; } @@ -4550,26 +4550,26 @@ namespace float MaxLateralSnap, FVector& OutPoint) { - if (!FMath::IsFinite(StrateTopZ) || !FMath::IsFinite(StrateBottomZ) - || !FMath::IsFinite(WorldX) || !FMath::IsFinite(WorldY) - || !FMath::IsFinite(MaxLateralSnap) || MaxLateralSnap < 0.0f + if (!VoxelMath::IsFinite(StrateTopZ) || !VoxelMath::IsFinite(StrateBottomZ) + || !VoxelMath::IsFinite(WorldX) || !VoxelMath::IsFinite(WorldY) + || !VoxelMath::IsFinite(MaxLateralSnap) || MaxLateralSnap < 0.0f || StrateTopZ <= StrateBottomZ) { return false; } - if (!FMath::IsFinite(Params.FloorRelativeHeight) - || !FMath::IsFinite(Params.CeilingRelativeHeight) - || !FMath::IsFinite(Params.FloorRoughness) - || !FMath::IsFinite(Params.FloorRoughnessFrequency) - || !FMath::IsFinite(Params.CeilingRoughness) - || !FMath::IsFinite(Params.CeilingRoughnessFrequency) - || !FMath::IsFinite(Params.ColumnDensity) - || !FMath::IsFinite(Params.ColumnMinRadius) - || !FMath::IsFinite(Params.ColumnMaxRadius) - || !FMath::IsFinite(Params.ColumnSpacing) - || !FMath::IsFinite(Params.BoundarySealThickness) - || !FMath::IsFinite(Params.BaseDensity) + if (!VoxelMath::IsFinite(Params.FloorRelativeHeight) + || !VoxelMath::IsFinite(Params.CeilingRelativeHeight) + || !VoxelMath::IsFinite(Params.FloorRoughness) + || !VoxelMath::IsFinite(Params.FloorRoughnessFrequency) + || !VoxelMath::IsFinite(Params.CeilingRoughness) + || !VoxelMath::IsFinite(Params.CeilingRoughnessFrequency) + || !VoxelMath::IsFinite(Params.ColumnDensity) + || !VoxelMath::IsFinite(Params.ColumnMinRadius) + || !VoxelMath::IsFinite(Params.ColumnMaxRadius) + || !VoxelMath::IsFinite(Params.ColumnSpacing) + || !VoxelMath::IsFinite(Params.BoundarySealThickness) + || !VoxelMath::IsFinite(Params.BaseDensity) || Params.BoundarySealThickness < 0.0f) { return false; @@ -4611,7 +4611,7 @@ namespace const float InnerBottom = StrateBottomZ + Params.BoundarySealThickness; const float OpenBottom = FMath::Max(FloorSurface, InnerBottom); const float OpenTop = FMath::Min(CeilSurface, InnerTop); - if (!FMath::IsFinite(OpenBottom) || !FMath::IsFinite(OpenTop) || OpenTop <= OpenBottom) + if (!VoxelMath::IsFinite(OpenBottom) || !VoxelMath::IsFinite(OpenTop) || OpenTop <= OpenBottom) { return false; } @@ -4689,21 +4689,21 @@ namespace float MaxLateralSnap, FVector& OutPoint) { - if (!FMath::IsFinite(StrateTopZ) || !FMath::IsFinite(StrateBottomZ) - || !FMath::IsFinite(WorldX) || !FMath::IsFinite(WorldY) - || !FMath::IsFinite(MaxLateralSnap) || MaxLateralSnap < 0.0f + if (!VoxelMath::IsFinite(StrateTopZ) || !VoxelMath::IsFinite(StrateBottomZ) + || !VoxelMath::IsFinite(WorldX) || !VoxelMath::IsFinite(WorldY) + || !VoxelMath::IsFinite(MaxLateralSnap) || MaxLateralSnap < 0.0f || StrateTopZ <= StrateBottomZ) { return false; } - if (!FMath::IsFinite(Params.CellSize) - || !FMath::IsFinite(Params.CorridorRadius) - || !FMath::IsFinite(Params.BranchProbability) - || !FMath::IsFinite(Params.Verticality) - || !FMath::IsFinite(Params.SurfaceRoughness) - || !FMath::IsFinite(Params.BoundarySealThickness) - || !FMath::IsFinite(Params.BaseDensity) + if (!VoxelMath::IsFinite(Params.CellSize) + || !VoxelMath::IsFinite(Params.CorridorRadius) + || !VoxelMath::IsFinite(Params.BranchProbability) + || !VoxelMath::IsFinite(Params.Verticality) + || !VoxelMath::IsFinite(Params.SurfaceRoughness) + || !VoxelMath::IsFinite(Params.BoundarySealThickness) + || !VoxelMath::IsFinite(Params.BaseDensity) || Params.CellSize <= 0.0f || Params.CorridorRadius <= 0.0f || Params.BranchProbability < 0.0f || Params.BranchProbability > 1.0f @@ -4725,14 +4725,14 @@ namespace // A strict interior of the tube is needed. At the MC zero surface the source's smooth // carve is only half applied, so stopping at the nominal radius would be an air guess. const float SafeRadius = Radius - RoughnessBound - 0.25f; - if (!FMath::IsFinite(SafeRadius) || SafeRadius <= 0.0f) + if (!VoxelMath::IsFinite(SafeRadius) || SafeRadius <= 0.0f) { return false; } const float InnerBottom = StrateBottomZ + Params.BoundarySealThickness; const float InnerTop = StrateTopZ - Params.BoundarySealThickness; - if (!FMath::IsFinite(InnerBottom) || !FMath::IsFinite(InnerTop) + if (!VoxelMath::IsFinite(InnerBottom) || !VoxelMath::IsFinite(InnerTop) || InnerBottom >= InnerTop) { return false; @@ -4835,8 +4835,8 @@ namespace } } - if (!bFound || BestDistSq > MaxSnapSq || !FMath::IsFinite(BestX) - || !FMath::IsFinite(BestY) || !FMath::IsFinite(BestZ)) + if (!bFound || BestDistSq > MaxSnapSq || !VoxelMath::IsFinite(BestX) + || !VoxelMath::IsFinite(BestY) || !VoxelMath::IsFinite(BestZ)) { return false; } @@ -4912,25 +4912,25 @@ namespace float MaxLateralSnap, FVector& OutPoint) { - if (!FMath::IsFinite(StrateTopZ) || !FMath::IsFinite(StrateBottomZ) - || !FMath::IsFinite(WorldX) || !FMath::IsFinite(WorldY) - || !FMath::IsFinite(MaxLateralSnap) || MaxLateralSnap < 0.0f + if (!VoxelMath::IsFinite(StrateTopZ) || !VoxelMath::IsFinite(StrateBottomZ) + || !VoxelMath::IsFinite(WorldX) || !VoxelMath::IsFinite(WorldY) + || !VoxelMath::IsFinite(MaxLateralSnap) || MaxLateralSnap < 0.0f || StrateTopZ <= StrateBottomZ) { return false; } - if (!FMath::IsFinite(Params.ShaftSpacing) - || !FMath::IsFinite(Params.ShaftDensity) - || !FMath::IsFinite(Params.ShaftMinRadius) - || !FMath::IsFinite(Params.ShaftMaxRadius) - || !FMath::IsFinite(Params.CrossConnectChance) - || !FMath::IsFinite(Params.ConnectorRadius) - || !FMath::IsFinite(Params.LedgeSpacing) - || !FMath::IsFinite(Params.LedgeDepth) - || !FMath::IsFinite(Params.SurfaceRoughness) - || !FMath::IsFinite(Params.BoundarySealThickness) - || !FMath::IsFinite(Params.BaseDensity) + if (!VoxelMath::IsFinite(Params.ShaftSpacing) + || !VoxelMath::IsFinite(Params.ShaftDensity) + || !VoxelMath::IsFinite(Params.ShaftMinRadius) + || !VoxelMath::IsFinite(Params.ShaftMaxRadius) + || !VoxelMath::IsFinite(Params.CrossConnectChance) + || !VoxelMath::IsFinite(Params.ConnectorRadius) + || !VoxelMath::IsFinite(Params.LedgeSpacing) + || !VoxelMath::IsFinite(Params.LedgeDepth) + || !VoxelMath::IsFinite(Params.SurfaceRoughness) + || !VoxelMath::IsFinite(Params.BoundarySealThickness) + || !VoxelMath::IsFinite(Params.BaseDensity) || Params.ShaftSpacing <= 0.0f || Params.ShaftDensity <= 0.0f || Params.ShaftDensity > 1.0f || Params.ShaftMinRadius <= 0.0f @@ -4979,7 +4979,7 @@ namespace const float Radius = FMath::Lerp(Params.ShaftMinRadius, Params.ShaftMaxRadius, VoxelHash::ToFloat01(VoxelHash::Mix(H ^ 0xBEEFu))); const float SafeRadius = Radius - ShaftInteriorMargin; - if (!FMath::IsFinite(SafeRadius) || SafeRadius <= 0.0f) + if (!VoxelMath::IsFinite(SafeRadius) || SafeRadius <= 0.0f) { continue; } @@ -5013,8 +5013,8 @@ namespace } } - if (!bFound || BestDistSq > MaxSnapSq || !FMath::IsFinite(BestAxisX) - || !FMath::IsFinite(BestAxisY)) + if (!bFound || BestDistSq > MaxSnapSq || !VoxelMath::IsFinite(BestAxisX) + || !VoxelMath::IsFinite(BestAxisY)) { return false; } @@ -5044,7 +5044,7 @@ namespace const float FirstCandidateZ = InnerBottom + 0.5f; const float LastCandidateZ = InnerTop - 2.0f * FitSettings.PlayerCapsuleHalfHeightVoxels - 0.5f; - if (!FMath::IsFinite(FirstCandidateZ) || !FMath::IsFinite(LastCandidateZ) + if (!VoxelMath::IsFinite(FirstCandidateZ) || !VoxelMath::IsFinite(LastCandidateZ) || LastCandidateZ < FirstCandidateZ) { return false; @@ -5075,8 +5075,8 @@ namespace const float LateralDY = CandidateY - WorldY; const float LateralDistanceSq = FMath::Square(LateralDX) + FMath::Square(LateralDY); - if (!FMath::IsFinite(CandidateX) || !FMath::IsFinite(CandidateY) - || !FMath::IsFinite(LateralDistanceSq) + if (!VoxelMath::IsFinite(CandidateX) || !VoxelMath::IsFinite(CandidateY) + || !VoxelMath::IsFinite(LateralDistanceSq) || OffsetSq > FMath::Square(BestSafeRadius) || LateralDistanceSq > MaxSnapSq + KINDA_SMALL_NUMBER) { @@ -5220,25 +5220,25 @@ namespace float MaxLateralSnap, FVector& OutPoint) { - if (!FMath::IsFinite(StrateTopZ) || !FMath::IsFinite(StrateBottomZ) - || !FMath::IsFinite(WorldX) || !FMath::IsFinite(WorldY) - || !FMath::IsFinite(MaxLateralSnap) || MaxLateralSnap < 0.0f + if (!VoxelMath::IsFinite(StrateTopZ) || !VoxelMath::IsFinite(StrateBottomZ) + || !VoxelMath::IsFinite(WorldX) || !VoxelMath::IsFinite(WorldY) + || !VoxelMath::IsFinite(MaxLateralSnap) || MaxLateralSnap < 0.0f || StrateTopZ <= StrateBottomZ) { return false; } - if (!FMath::IsFinite(Params.IslandSpacing) - || !FMath::IsFinite(Params.IslandDensity) - || !FMath::IsFinite(Params.IslandMinRadius) - || !FMath::IsFinite(Params.IslandMaxRadius) - || !FMath::IsFinite(Params.ThicknessRatio) - || !FMath::IsFinite(Params.VerticalJitter) - || !FMath::IsFinite(Params.TopFlatten) - || !FMath::IsFinite(Params.SurfaceRoughness) - || !FMath::IsFinite(Params.SDFBlendRadius) - || !FMath::IsFinite(Params.BoundarySealThickness) - || !FMath::IsFinite(Params.BaseDensity) + if (!VoxelMath::IsFinite(Params.IslandSpacing) + || !VoxelMath::IsFinite(Params.IslandDensity) + || !VoxelMath::IsFinite(Params.IslandMinRadius) + || !VoxelMath::IsFinite(Params.IslandMaxRadius) + || !VoxelMath::IsFinite(Params.ThicknessRatio) + || !VoxelMath::IsFinite(Params.VerticalJitter) + || !VoxelMath::IsFinite(Params.TopFlatten) + || !VoxelMath::IsFinite(Params.SurfaceRoughness) + || !VoxelMath::IsFinite(Params.SDFBlendRadius) + || !VoxelMath::IsFinite(Params.BoundarySealThickness) + || !VoxelMath::IsFinite(Params.BaseDensity) || Params.IslandSpacing <= 0.0f || Params.IslandDensity <= 0.0f || Params.IslandDensity > 1.0f || Params.IslandMinRadius <= 0.0f @@ -5304,7 +5304,7 @@ namespace } } - if (Islands.Num() == 0 || !FMath::IsFinite(MinBotZ) || !FMath::IsFinite(MaxTopZ)) + if (Islands.Num() == 0 || !VoxelMath::IsFinite(MinBotZ) || !VoxelMath::IsFinite(MaxTopZ)) { return false; } @@ -5316,7 +5316,7 @@ namespace const float SearchBottom = MinBotZ - SearchPad; constexpr float ScanStep = 0.5f; constexpr int32 MaxVerticalSamples = 2048; - if (!FMath::IsFinite(SearchTop) || !FMath::IsFinite(SearchBottom) + if (!VoxelMath::IsFinite(SearchTop) || !VoxelMath::IsFinite(SearchBottom) || SearchTop <= SearchBottom || (SearchTop - SearchBottom) / ScanStep > (float)MaxVerticalSamples) { @@ -5334,7 +5334,7 @@ namespace float UpperZ = SearchTop; float UpperDensity = VF_EvaluateIslandInteriorDensity( Params, SeedU, Islands.GetData(), Islands.Num(), CandidateX, CandidateY, UpperZ); - if (!FMath::IsFinite(UpperDensity) || UpperDensity > 0.0f) + if (!VoxelMath::IsFinite(UpperDensity) || UpperDensity > 0.0f) { return false; } @@ -5344,7 +5344,7 @@ namespace const float LowerZ = FMath::Max(SearchBottom, UpperZ - ScanStep); const float LowerDensity = VF_EvaluateIslandInteriorDensity( Params, SeedU, Islands.GetData(), Islands.Num(), CandidateX, CandidateY, LowerZ); - if (!FMath::IsFinite(LowerDensity)) return false; + if (!VoxelMath::IsFinite(LowerDensity)) return false; // Descending from guaranteed air, the first solid bracket is the highest actual // blob top at this XY. That is the footing surface; no island-centre guess is @@ -5358,7 +5358,7 @@ namespace const float Mid = (SolidZ + AirZ) * 0.5f; const float MidDensity = VF_EvaluateIslandInteriorDensity( Params, SeedU, Islands.GetData(), Islands.Num(), CandidateX, CandidateY, Mid); - if (!FMath::IsFinite(MidDensity)) return false; + if (!VoxelMath::IsFinite(MidDensity)) return false; if (MidDensity > 0.0f) SolidZ = Mid; else AirZ = Mid; } @@ -5366,7 +5366,7 @@ namespace const float InnerBottom = StrateBottomZ + Params.BoundarySealThickness; const float InnerTop = StrateTopZ - Params.BoundarySealThickness; const float LandingZ = AirZ + 0.5f; - if (!FMath::IsFinite(LandingZ) + if (!VoxelMath::IsFinite(LandingZ) || LandingZ <= InnerBottom || LandingZ >= InnerTop || VF_EvaluateIslandInteriorDensity( Params, SeedU, Islands.GetData(), Islands.Num(), CandidateX, CandidateY, LandingZ) > 0.0f @@ -5379,7 +5379,7 @@ namespace } OutLandingZ = LandingZ; - return FMath::IsFinite(OutLandingZ); + return VoxelMath::IsFinite(OutLandingZ); } UpperZ = LowerZ; @@ -5418,7 +5418,7 @@ namespace // the one-voxel margin keeps a returned top away from the nominal radial edge. If no // such disk is within MaxLateralSnap, declining is preferable to an arbitrary jump. const float SafeRadius = Island.Rxy - WarpBound - 1.0f; - if (!FMath::IsFinite(SafeRadius) || SafeRadius <= 0.0f) continue; + if (!VoxelMath::IsFinite(SafeRadius) || SafeRadius <= 0.0f) continue; const float DX = WorldX - Island.X; const float DY = WorldY - Island.Y; @@ -5508,8 +5508,8 @@ namespace } OutPoint = BestPoint; - return FMath::IsFinite(OutPoint.X) && FMath::IsFinite(OutPoint.Y) - && FMath::IsFinite(OutPoint.Z); + return VoxelMath::IsFinite(OutPoint.X) && VoxelMath::IsFinite(OutPoint.Y) + && VoxelMath::IsFinite(OutPoint.Z); } } @@ -5622,7 +5622,7 @@ FVoxelPassageLanding VF_BuildPassageLanding( // a seal and never moves a normal interior pose. const float InnerBottom = StrateBottomZ + SafeSeal; const float InnerTop = StrateTopZ - SafeSeal; - if (FMath::IsFinite(InnerBottom) && FMath::IsFinite(InnerTop) + if (VoxelMath::IsFinite(InnerBottom) && VoxelMath::IsFinite(InnerTop) && InnerTop > InnerBottom) { // Passage carving is intentionally ordered after the vertical seal and fades over its @@ -5662,13 +5662,13 @@ float VF_EvaluatePassageLandingSDF( const FVector& Position, const FVoxelPassageLanding& Landing) { - if (!FMath::IsFinite(Position.X) || !FMath::IsFinite(Position.Y) - || !FMath::IsFinite(Position.Z) - || !FMath::IsFinite(Landing.StandingPoint.X) - || !FMath::IsFinite(Landing.StandingPoint.Y) - || !FMath::IsFinite(Landing.FloorZ) - || !FMath::IsFinite(Landing.CeilingZ) - || !FMath::IsFinite(Landing.HalfWidth) + if (!VoxelMath::IsFinite(Position.X) || !VoxelMath::IsFinite(Position.Y) + || !VoxelMath::IsFinite(Position.Z) + || !VoxelMath::IsFinite(Landing.StandingPoint.X) + || !VoxelMath::IsFinite(Landing.StandingPoint.Y) + || !VoxelMath::IsFinite(Landing.FloorZ) + || !VoxelMath::IsFinite(Landing.CeilingZ) + || !VoxelMath::IsFinite(Landing.HalfWidth) || Landing.HalfWidth <= 0.0f || Landing.CeilingZ <= Landing.FloorZ) { @@ -5698,9 +5698,9 @@ bool VF_IsPassageLandingFloor( const FVector& Position, const FVoxelPassageLanding& Landing) { - if (!FMath::IsFinite(Position.X) || !FMath::IsFinite(Position.Y) - || !FMath::IsFinite(Position.Z) || !FMath::IsFinite(Landing.FloorZ) - || !FMath::IsFinite(Landing.FloorThickness) + if (!VoxelMath::IsFinite(Position.X) || !VoxelMath::IsFinite(Position.Y) + || !VoxelMath::IsFinite(Position.Z) || !VoxelMath::IsFinite(Landing.FloorZ) + || !VoxelMath::IsFinite(Landing.FloorThickness) || Landing.FloorThickness <= 0.0f || Landing.HalfWidth <= 0.0f) { return false; @@ -5829,14 +5829,14 @@ bool VoxelCaveMorphology::MayHaveFeatureInSearchBox( bool bUseZ, float SearchMinZ, float SearchMaxZ, const TArray* TerrainOps) { - if (!FMath::IsFinite(SearchMinX) || !FMath::IsFinite(SearchMinY) - || !FMath::IsFinite(SearchMaxX) || !FMath::IsFinite(SearchMaxY) + if (!VoxelMath::IsFinite(SearchMinX) || !VoxelMath::IsFinite(SearchMinY) + || !VoxelMath::IsFinite(SearchMaxX) || !VoxelMath::IsFinite(SearchMaxY) || SearchMinX > SearchMaxX || SearchMinY > SearchMaxY) { return true; } if (bUseZ - && (!FMath::IsFinite(SearchMinZ) || !FMath::IsFinite(SearchMaxZ) + && (!VoxelMath::IsFinite(SearchMinZ) || !VoxelMath::IsFinite(SearchMaxZ) || SearchMinZ > SearchMaxZ)) { return true; @@ -5858,8 +5858,8 @@ bool VoxelCaveMorphology::MayHaveFeatureInSearchBox( const float CollectMinY = SearchMinY - Reach.CollectMargin; const float CollectMaxX = SearchMaxX + Reach.CollectMargin; const float CollectMaxY = SearchMaxY + Reach.CollectMargin; - if (!FMath::IsFinite(CollectMinX) || !FMath::IsFinite(CollectMinY) - || !FMath::IsFinite(CollectMaxX) || !FMath::IsFinite(CollectMaxY)) + if (!VoxelMath::IsFinite(CollectMinX) || !VoxelMath::IsFinite(CollectMinY) + || !VoxelMath::IsFinite(CollectMaxX) || !VoxelMath::IsFinite(CollectMaxY)) { return true; } @@ -5867,7 +5867,7 @@ bool VoxelCaveMorphology::MayHaveFeatureInSearchBox( auto FloorCell = [](double Value, double CellSize, int32& OutCell) -> bool { const double Cell = FMath::FloorToDouble(Value / CellSize); - if (!FMath::IsFinite(Cell) + if (!VoxelMath::IsFinite(Cell) || Cell < static_cast(MIN_int32) + 2.0 || Cell > static_cast(MAX_int32) - 2.0) { @@ -5919,9 +5919,9 @@ bool VoxelCaveMorphology::MayHaveFeatureInSearchBox( - Params.BoundarySealThickness - RoomZBuffer; const float StrateRangeZ = StrateMaxZ - StrateMinZ; const float StrateCenterZ = (StrateMinZ + StrateMaxZ) * 0.5f; - if (!FMath::IsFinite(StrateMinZ) || !FMath::IsFinite(StrateMaxZ) - || !FMath::IsFinite(StrateRangeZ) - || !FMath::IsFinite(StrateCenterZ)) + if (!VoxelMath::IsFinite(StrateMinZ) || !VoxelMath::IsFinite(StrateMaxZ) + || !VoxelMath::IsFinite(StrateRangeZ) + || !VoxelMath::IsFinite(StrateCenterZ)) { return true; } @@ -6198,13 +6198,13 @@ void VoxelCaveMorphology::BuildChunkCache( + FMath::Max( FMath::Max(Params.SDFBlendRadius, 0.0f) * 3.0f, VoxelPassageGeometry::LandingFloorThicknessVoxels); - if (!FMath::IsFinite(CaveWarpBound) - || !FMath::IsFinite(ChainReach) + if (!VoxelMath::IsFinite(CaveWarpBound) + || !VoxelMath::IsFinite(ChainReach) || ChainReach < 0.0f - || !FMath::IsFinite((float)RoomA.Center.X) - || !FMath::IsFinite((float)RoomA.Center.Y) - || !FMath::IsFinite((float)RoomB.Center.X) - || !FMath::IsFinite((float)RoomB.Center.Y)) + || !VoxelMath::IsFinite((float)RoomA.Center.X) + || !VoxelMath::IsFinite((float)RoomA.Center.Y) + || !VoxelMath::IsFinite((float)RoomB.Center.X) + || !VoxelMath::IsFinite((float)RoomB.Center.Y)) { return true; } @@ -6221,8 +6221,8 @@ void VoxelCaveMorphology::BuildChunkCache( const float MaxY = FMath::Max( static_cast(RoomA.Center.Y), static_cast(RoomB.Center.Y)) + ChainReach; - return FMath::IsFinite(MinX) && FMath::IsFinite(MinY) - && FMath::IsFinite(MaxX) && FMath::IsFinite(MaxY) + return VoxelMath::IsFinite(MinX) && VoxelMath::IsFinite(MinY) + && VoxelMath::IsFinite(MaxX) && VoxelMath::IsFinite(MaxY) && MaxX >= SearchMinX && MaxY >= SearchMinY && MinX <= SearchMaxX && MinY <= SearchMaxY; }; @@ -6573,9 +6573,9 @@ void VoxelCaveMorphology::BuildChunkCache( // turning the whole network into a level grid. const float RoomFloorA = RoomFloorZFor(RoomA); const float RoomFloorB = RoomFloorZFor(RoomB); - const float SafeFloorA = FMath::IsFinite(RoomFloorA) + const float SafeFloorA = VoxelMath::IsFinite(RoomFloorA) ? RoomFloorA : RoomA.Center.Z - RoomA.RadiusZ; - const float SafeFloorB = FMath::IsFinite(RoomFloorB) + const float SafeFloorB = VoxelMath::IsFinite(RoomFloorB) ? RoomFloorB : RoomB.Center.Z - RoomB.RadiusZ; FVector EndA = RoomA.Center + FVector(0.0f, 0.0f, SafeFloorA + RadA - RoomA.Center.Z); @@ -6787,11 +6787,11 @@ void VoxelCaveMorphology::BuildChunkCache( // the room's base floor is resolved; no per-sample tunnel search or correction is // involved. Strength zero is the exact legacy path. const float MouthRiseStrength = FMath::Clamp( - FMath::IsFinite(Params.RoomMouthRiseStrength) + VoxelMath::IsFinite(Params.RoomMouthRiseStrength) ? Params.RoomMouthRiseStrength : 0.0f, 0.0f, 1.0f); const float MouthRiseBlend = FMath::Clamp( - FMath::IsFinite(Params.RoomMouthRiseBlendVoxels) + VoxelMath::IsFinite(Params.RoomMouthRiseBlendVoxels) ? Params.RoomMouthRiseBlendVoxels : 8.0f, 8.0f, 128.0f); const float RequiredRoomClearance = @@ -6804,15 +6804,15 @@ void VoxelCaveMorphology::BuildChunkCache( const FVector& Mouth, float TargetFloorZ) { if (!(MouthRiseStrength > 0.0f) - || !FMath::IsFinite(TargetFloorZ) - || !FMath::IsFinite(Mouth.X) - || !FMath::IsFinite(Mouth.Y) - || !FMath::IsFinite(Mouth.Z)) + || !VoxelMath::IsFinite(TargetFloorZ) + || !VoxelMath::IsFinite(Mouth.X) + || !VoxelMath::IsFinite(Mouth.Y) + || !VoxelMath::IsFinite(Mouth.Z)) { return; } const float BaseFloorZ = RoomFloorZFor(Room); - if (!FMath::IsFinite(BaseFloorZ) + if (!VoxelMath::IsFinite(BaseFloorZ) || TargetFloorZ <= BaseFloorZ + KINDA_SMALL_NUMBER || Room.Center.Z + Room.RadiusZ - TargetFloorZ < RequiredRoomClearance) @@ -6951,8 +6951,8 @@ void VoxelCaveMorphology::BuildChunkCache( const float SharedCeiling = FMath::Min( RoomA.Center.Z + RoomA.RadiusZ, RoomB.Center.Z + RoomB.RadiusZ); - if (!FMath::IsFinite(CommonFloor) - || !FMath::IsFinite(SharedCeiling) + if (!VoxelMath::IsFinite(CommonFloor) + || !VoxelMath::IsFinite(SharedCeiling) || SharedCeiling - CommonFloor < JoinRequiredHeight) { continue; @@ -6984,7 +6984,7 @@ void VoxelCaveMorphology::BuildChunkCache( const float JoinRadius = FMath::Min( FMath::Min(RoomA.RadiusXY, RoomB.RadiusXY) * 0.35f, (EndDistance - StartDistance) * 0.5f); - if (!FMath::IsFinite(JoinRadius) || JoinRadius < JoinMinimumRadius) + if (!VoxelMath::IsFinite(JoinRadius) || JoinRadius < JoinMinimumRadius) { continue; } @@ -7266,7 +7266,7 @@ void VoxelCaveMorphology::BuildChunkCache( Rise.Strength = AuthoredRise.Strength; const float Reach = static_cast(FVector::Dist( CR.Center, Rise.Mouth)) + FMath::Max(Rise.BlendRadius, 0.0f); - if (FMath::IsFinite(Reach)) + if (VoxelMath::IsFinite(Reach)) { CR.CullRadiusSq = FMath::Max(CR.CullRadiusSq, Reach * Reach); } @@ -7423,7 +7423,7 @@ float VoxelCaveMorphology::EvaluateSDFCached( const float Distance = FMath::Sqrt(DX * DX + DY * DY); if (Distance >= Radius || !(Rise.Strength > 0.0f) - || !FMath::IsFinite(Rise.TargetFloorZ)) + || !VoxelMath::IsFinite(Rise.TargetFloorZ)) { continue; } diff --git a/Source/VoxelForge/Private/VoxelDensityOpStack.cpp b/Source/VoxelForge/Private/VoxelDensityOpStack.cpp index 983d33a..eef1a7c 100644 --- a/Source/VoxelForge/Private/VoxelDensityOpStack.cpp +++ b/Source/VoxelForge/Private/VoxelDensityOpStack.cpp @@ -190,9 +190,9 @@ namespace FORCEINLINE float VF_PerlinAbsBoundOverBox( const FVector3f& InMin, const FVector3f& InMax) { - if (!FMath::IsFinite(InMin.X) || !FMath::IsFinite(InMin.Y) - || !FMath::IsFinite(InMin.Z) || !FMath::IsFinite(InMax.X) - || !FMath::IsFinite(InMax.Y) || !FMath::IsFinite(InMax.Z) + if (!VoxelMath::IsFinite(InMin.X) || !VoxelMath::IsFinite(InMin.Y) + || !VoxelMath::IsFinite(InMin.Z) || !VoxelMath::IsFinite(InMax.X) + || !VoxelMath::IsFinite(InMax.Y) || !VoxelMath::IsFinite(InMax.Z) || InMin.X > InMax.X || InMin.Y > InMax.Y || InMin.Z > InMax.Z || FMath::Abs(InMin.X) > 1000000.0f || FMath::Abs(InMax.X) > 1000000.0f || FMath::Abs(InMin.Y) > 1000000.0f || FMath::Abs(InMax.Y) > 1000000.0f @@ -279,7 +279,7 @@ namespace ValueMax += Contribution.Max; } - if (!FMath::IsFinite(ValueMin) || !FMath::IsFinite(ValueMax)) + if (!VoxelMath::IsFinite(ValueMin) || !VoxelMath::IsFinite(ValueMax)) { return VF_PerlinAbsBound; } @@ -420,8 +420,8 @@ namespace FORCEINLINE float VF_LatticeAxisDistanceToPoint( float BoxMin, float BoxMax, float Origin, float Step, float Target, float Extra) { - if (!(Step > 0.0f) || !FMath::IsFinite(BoxMin) || !FMath::IsFinite(BoxMax) - || !FMath::IsFinite(Origin) || !FMath::IsFinite(Target) + if (!(Step > 0.0f) || !VoxelMath::IsFinite(BoxMin) || !VoxelMath::IsFinite(BoxMax) + || !VoxelMath::IsFinite(Origin) || !VoxelMath::IsFinite(Target) || BoxMin > BoxMax) { return FLT_MAX; @@ -451,7 +451,7 @@ namespace float BoxMin, float BoxMax, float Origin, float Step, float TargetMin, float TargetMax, float Extra) { - if (!(Step > 0.0f) || !FMath::IsFinite(TargetMin) || !FMath::IsFinite(TargetMax) + if (!(Step > 0.0f) || !VoxelMath::IsFinite(TargetMin) || !VoxelMath::IsFinite(TargetMax) || TargetMin > TargetMax) { return FLT_MAX; @@ -483,7 +483,7 @@ namespace FORCEINLINE float VF_SaturatingAdd(float A, float B) { - if (!FMath::IsFinite(A) || !FMath::IsFinite(B)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(A) || !VoxelMath::IsFinite(B)) { return FLT_MAX; } if (B > 0.0f && A > FLT_MAX - B) { return FLT_MAX; } if (B < 0.0f && A < -FLT_MAX - B) { return -FLT_MAX; } return A + B; @@ -603,7 +603,7 @@ namespace void PropagateSdfOverBox(FVoxelBoxSdfInterval& InOut, const FBox&, const FVoxelOpContext&) const override { - if (!FMath::IsFinite(CellSize) || !FMath::IsFinite(CorridorRadius) + if (!VoxelMath::IsFinite(CellSize) || !VoxelMath::IsFinite(CorridorRadius) || CellSize <= 0.0f || CorridorRadius < 0.0f) { InOut.SetUnknown(); @@ -1575,8 +1575,8 @@ namespace const FVoxelOpContext&) const override { if (!InOut.IsKnown()) { return; } - if (!FMath::IsFinite(Strength) || !FMath::IsFinite(Frequency) - || !FMath::IsFinite(ApplyWithin) || BaseOctaves <= 0) + if (!VoxelMath::IsFinite(Strength) || !VoxelMath::IsFinite(Frequency) + || !VoxelMath::IsFinite(ApplyWithin) || BaseOctaves <= 0) { InOut.SetUnknown(); return; @@ -1589,7 +1589,7 @@ namespace FMath::Max(FMath::Abs((float)VoxelBox.Min.X), FMath::Abs((float)VoxelBox.Max.X)), FMath::Max(FMath::Abs((float)VoxelBox.Min.Y), FMath::Abs((float)VoxelBox.Max.Y)), FMath::Max(FMath::Abs((float)VoxelBox.Min.Z), FMath::Abs((float)VoxelBox.Max.Z))); - if (!FMath::IsFinite(MaxAbsCoord) || !FMath::IsFinite(MaxAbsCoord * FMath::Abs(Frequency))) + if (!VoxelMath::IsFinite(MaxAbsCoord) || !VoxelMath::IsFinite(MaxAbsCoord * FMath::Abs(Frequency))) { InOut.SetUnknown(); return; @@ -1806,8 +1806,8 @@ namespace EVoxelOpEffect EffectOverBox(const FBox&, const FVoxelOpContext&) const override { const float Coefficient = Sign * BaseDensity * 2.0f; - if (!FMath::IsFinite(Coefficient) || !FMath::IsFinite(Blend) - || !FMath::IsFinite(MinDivisor) || Blend <= 0.0f) + if (!VoxelMath::IsFinite(Coefficient) || !VoxelMath::IsFinite(Blend) + || !VoxelMath::IsFinite(MinDivisor) || Blend <= 0.0f) { return EVoxelOpEffect::Both; } @@ -1831,8 +1831,8 @@ namespace float MaxCarveOverBox(const FBox&, const FVoxelOpContext&) const override { const float Coefficient = Sign * BaseDensity * 2.0f; - if (!FMath::IsFinite(Coefficient) || !FMath::IsFinite(Blend) - || !FMath::IsFinite(MinDivisor) || Blend <= 0.0f) + if (!VoxelMath::IsFinite(Coefficient) || !VoxelMath::IsFinite(Blend) + || !VoxelMath::IsFinite(MinDivisor) || Blend <= 0.0f) { return FLT_MAX; } @@ -1842,8 +1842,8 @@ namespace float MaxFillOverBox(const FBox&, const FVoxelOpContext&) const override { const float Coefficient = Sign * BaseDensity * 2.0f; - if (!FMath::IsFinite(Coefficient) || !FMath::IsFinite(Blend) - || !FMath::IsFinite(MinDivisor) || Blend <= 0.0f) + if (!VoxelMath::IsFinite(Coefficient) || !VoxelMath::IsFinite(Blend) + || !VoxelMath::IsFinite(MinDivisor) || Blend <= 0.0f) { return FLT_MAX; } @@ -1854,8 +1854,8 @@ namespace const FVoxelBoxHypotheses& H) const override { const float Coefficient = Sign * BaseDensity * 2.0f; - if (!FMath::IsFinite(Coefficient)) { return FLT_MAX; } - if (!FMath::IsFinite(Blend) || !FMath::IsFinite(MinDivisor) || Blend <= 0.0f) + if (!VoxelMath::IsFinite(Coefficient)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(Blend) || !VoxelMath::IsFinite(MinDivisor) || Blend <= 0.0f) { return FLT_MAX; } @@ -1867,8 +1867,8 @@ namespace const FVoxelBoxHypotheses& H) const override { const float Coefficient = Sign * BaseDensity * 2.0f; - if (!FMath::IsFinite(Coefficient)) { return FLT_MAX; } - if (!FMath::IsFinite(Blend) || !FMath::IsFinite(MinDivisor) || Blend <= 0.0f) + if (!VoxelMath::IsFinite(Coefficient)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(Blend) || !VoxelMath::IsFinite(MinDivisor) || Blend <= 0.0f) { return FLT_MAX; } @@ -1891,14 +1891,14 @@ namespace float MaxFactor(const FVoxelBoxHypotheses& H) const { - if (!H.Sdf.IsKnown() || !FMath::IsFinite(Blend) - || !FMath::IsFinite(MinDivisor) || Blend <= 0.0f) + if (!H.Sdf.IsKnown() || !VoxelMath::IsFinite(Blend) + || !VoxelMath::IsFinite(MinDivisor) || Blend <= 0.0f) { return 1.0f; } const float Denom = FMath::Max(Blend * 2.0f, MinDivisor); - if (!(Denom > 0.0f) || !FMath::IsFinite(Denom)) { return 1.0f; } + if (!(Denom > 0.0f) || !VoxelMath::IsFinite(Denom)) { return 1.0f; } const float T = FMath::Clamp((Blend - H.Sdf.Min) / Denom, 0.0f, 1.0f); return SmoothStep01(T); @@ -2223,18 +2223,18 @@ namespace const float MaxFactor = LiveManager->MaxPassageCarveFactorNearLattice( VoxelBox, Ctx.LatticeOriginVoxels, Ctx.Step); if (!(MaxFactor > 0.0f)) { return 0.0f; } - if (!(H.SolidMargin > 0.0f) || !FMath::IsFinite(H.SolidMargin) - || !FMath::IsFinite(Base) || !FMath::IsFinite(Seal)) + if (!(H.SolidMargin > 0.0f) || !VoxelMath::IsFinite(H.SolidMargin) + || !VoxelMath::IsFinite(Base) || !VoxelMath::IsFinite(Seal)) { return FLT_MAX; } const float AirTarget = -(Base * 2.0f + Seal + 4.0f); - if (!FMath::IsFinite(AirTarget)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(AirTarget)) { return FLT_MAX; } const float LowerAfterCarve = FMath::Min( H.SolidMargin, FMath::Lerp(H.SolidMargin, AirTarget, FMath::Clamp(MaxFactor, 0.0f, 1.0f))); - if (!FMath::IsFinite(LowerAfterCarve)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(LowerAfterCarve)) { return FLT_MAX; } return FMath::Max(0.0f, H.SolidMargin - LowerAfterCarve); } @@ -2355,13 +2355,13 @@ namespace void PropagateSdfOverBox(FVoxelBoxSdfInterval& InOut, const FBox& VoxelBox, const FVoxelOpContext&) const override { - if (!FMath::IsFinite((float)VoxelBox.Min.X) || !FMath::IsFinite((float)VoxelBox.Min.Y) - || !FMath::IsFinite((float)VoxelBox.Min.Z) - || !FMath::IsFinite((float)VoxelBox.Max.X) || !FMath::IsFinite((float)VoxelBox.Max.Y) - || !FMath::IsFinite((float)VoxelBox.Max.Z) + if (!VoxelMath::IsFinite((float)VoxelBox.Min.X) || !VoxelMath::IsFinite((float)VoxelBox.Min.Y) + || !VoxelMath::IsFinite((float)VoxelBox.Min.Z) + || !VoxelMath::IsFinite((float)VoxelBox.Max.X) || !VoxelMath::IsFinite((float)VoxelBox.Max.Y) + || !VoxelMath::IsFinite((float)VoxelBox.Max.Z) || VoxelBox.Min.X > VoxelBox.Max.X || VoxelBox.Min.Y > VoxelBox.Max.Y || VoxelBox.Min.Z > VoxelBox.Max.Z - || !FMath::IsFinite(P.ShaftSpacing) || P.ShaftSpacing <= 0.0f) + || !VoxelMath::IsFinite(P.ShaftSpacing) || P.ShaftSpacing <= 0.0f) { InOut.SetUnknown(); return; @@ -3021,16 +3021,16 @@ namespace void PropagateSdfOverBox(FVoxelBoxSdfInterval& InOut, const FBox& VoxelBox, const FVoxelOpContext&) const override { - if (!FMath::IsFinite((float)VoxelBox.Min.X) || !FMath::IsFinite((float)VoxelBox.Min.Y) - || !FMath::IsFinite((float)VoxelBox.Min.Z) - || !FMath::IsFinite((float)VoxelBox.Max.X) || !FMath::IsFinite((float)VoxelBox.Max.Y) - || !FMath::IsFinite((float)VoxelBox.Max.Z) + if (!VoxelMath::IsFinite((float)VoxelBox.Min.X) || !VoxelMath::IsFinite((float)VoxelBox.Min.Y) + || !VoxelMath::IsFinite((float)VoxelBox.Min.Z) + || !VoxelMath::IsFinite((float)VoxelBox.Max.X) || !VoxelMath::IsFinite((float)VoxelBox.Max.Y) + || !VoxelMath::IsFinite((float)VoxelBox.Max.Z) || VoxelBox.Min.X > VoxelBox.Max.X || VoxelBox.Min.Y > VoxelBox.Max.Y || VoxelBox.Min.Z > VoxelBox.Max.Z - || !FMath::IsFinite(P.IslandSpacing) || !FMath::IsFinite(P.IslandDensity) - || !FMath::IsFinite(P.IslandMinRadius) || !FMath::IsFinite(P.IslandMaxRadius) - || !FMath::IsFinite(P.ThicknessRatio) || !FMath::IsFinite(P.VerticalJitter) - || !FMath::IsFinite(P.TopFlatten) || !FMath::IsFinite(P.SDFBlendRadius) + || !VoxelMath::IsFinite(P.IslandSpacing) || !VoxelMath::IsFinite(P.IslandDensity) + || !VoxelMath::IsFinite(P.IslandMinRadius) || !VoxelMath::IsFinite(P.IslandMaxRadius) + || !VoxelMath::IsFinite(P.ThicknessRatio) || !VoxelMath::IsFinite(P.VerticalJitter) + || !VoxelMath::IsFinite(P.TopFlatten) || !VoxelMath::IsFinite(P.SDFBlendRadius) || P.IslandDensity < 0.0f) { InOut.SetUnknown(); @@ -4389,7 +4389,7 @@ namespace { const float StructuralFloorMinimumDensity = FMath::Max(P.BaseDensity * 2.0f, 1.0f); - if (FMath::IsFinite(StructuralFloorMinimumDensity) + if (VoxelMath::IsFinite(StructuralFloorMinimumDensity) && StructuralFloorMinimumDensity > 0.0f) { InOut.bProtectedStructuralFloor = true; @@ -4629,7 +4629,7 @@ namespace float Supremum = 0.0f; for (const FCachedColumn& Column : GetBoxCache().Columns) { - if (!FMath::IsFinite(Column.BaseDensity) || Column.BaseDensity < 0.0f) + if (!VoxelMath::IsFinite(Column.BaseDensity) || Column.BaseDensity < 0.0f) { // Eval multiplies by BaseDensity. A negative authored value reverses the // declared FillOnly direction, so the state-aware caller must fall back to @@ -4637,7 +4637,7 @@ namespace return FLT_MAX; } const float Contribution = Column.BaseDensity * 1.5f; - if (!FMath::IsFinite(Contribution) + if (!VoxelMath::IsFinite(Contribution) || Supremum > FLT_MAX - Contribution) { return FLT_MAX; @@ -4658,13 +4658,13 @@ namespace // room wins the nearest-SDF query, and is finite without assuming a global room height. float Supremum = 0.0f; const float BoxMinZ = static_cast(B.KeyBox.Min.Z); - if (!FMath::IsFinite(BoxMinZ)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(BoxMinZ)) { return FLT_MAX; } for (const FCachedRoom& Room : GetBoxCache().Rooms) { const float CenterZ = static_cast(Room.Center.Z); const float RadiusZ = Room.RadiusZ; - if (!FMath::IsFinite(CenterZ) || !FMath::IsFinite(RadiusZ) - || !FMath::IsFinite(P.FloorBias) || P.FloorBias < 0.0f) + if (!VoxelMath::IsFinite(CenterZ) || !VoxelMath::IsFinite(RadiusZ) + || !VoxelMath::IsFinite(P.FloorBias) || P.FloorBias < 0.0f) { return FLT_MAX; } @@ -4672,7 +4672,7 @@ namespace const float Below = FMath::Max(CenterZ - BoxMinZ, 0.0f); const float Normalized = Below / Denominator; const float Contribution = Normalized * Normalized * P.FloorBias; - if (!FMath::IsFinite(Contribution)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(Contribution)) { return FLT_MAX; } Supremum = FMath::Max(Supremum, Contribution); } return Supremum; @@ -4745,7 +4745,7 @@ namespace B.SdfInterval.SetUnknown(); InOut.SetUnknown(); }; - auto Finite = [](float V) { return FMath::IsFinite(V); }; + auto Finite = [](float V) { return VoxelMath::IsFinite(V); }; if (ManagerLifetimeId != 0 && LiveManager == nullptr) { @@ -6262,7 +6262,7 @@ namespace }; const float K = P.SDFBlendRadius; - const float WormThreshold = FMath::IsFinite(P.WormNetworkRange) + const float WormThreshold = VoxelMath::IsFinite(P.WormNetworkRange) ? FMath::Max(3.0f * K, P.WormNetworkRange) : FLT_MAX; const float ThresholdWithBlend = VF_SaturatingAdd(WormThreshold, K); float Lower = FLT_MAX; @@ -6803,7 +6803,7 @@ namespace static_cast(World.Z), GetBoxCache(), SupportColumn, VoxelGenLOD::ShouldUseSpatialIndex(false)); ++B.ExactTailEvaluated; - if (!FMath::IsFinite(Evaluation.SDF)) + if (!VoxelMath::IsFinite(Evaluation.SDF)) { bExactTailUnknown = true; if (CachedTailState != nullptr) { *CachedTailState = 0x84u; } @@ -6941,7 +6941,7 @@ namespace FORCEINLINE bool VF_CaveBoxIsFar(const FVoxelBoxHypotheses& H, float SDFBlendRadius) { const float Threshold = SDFBlendRadius * 3.0f; - return FMath::IsFinite(Threshold) && H.Sdf.IsKnown() && H.Sdf.Min >= Threshold; + return VoxelMath::IsFinite(Threshold) && H.Sdf.IsKnown() && H.Sdf.Min >= Threshold; } FORCEINLINE EVoxelOpEffect VF_CaveDetailEffect(const FRoomGraphSource* Rooms, @@ -7179,7 +7179,7 @@ namespace float MaxAmplitude() const { if (!(P.SurfaceRoughness > 0.0f)) { return 0.0f; } - if (!FMath::IsFinite(P.SurfaceRoughness)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(P.SurfaceRoughness)) { return FLT_MAX; } float NoiseBound = VF_PerlinAbsBound; switch (P.RoughnessNoiseType) @@ -7200,14 +7200,14 @@ namespace } const float Result = 1.4f * NoiseBound * P.SurfaceRoughness * VOXEL_NOISE_SCALE; - return FMath::IsFinite(Result) ? Result : FLT_MAX; + return VoxelMath::IsFinite(Result) ? Result : FLT_MAX; } private: bool IsInactiveFromSdf(const FVoxelBoxHypotheses& H) const { if (VF_CaveBoxIsFar(H, P.SDFBlendRadius)) { return true; } - if (!(P.SurfaceRoughness > 0.0f) || !FMath::IsFinite(P.SurfaceRoughness)) + if (!(P.SurfaceRoughness > 0.0f) || !VoxelMath::IsFinite(P.SurfaceRoughness)) { return P.SurfaceRoughness <= 0.0f; } @@ -7215,7 +7215,7 @@ namespace // Eval's second gate is abs(CaveSDF) < SurfaceRoughness*2. A known positive // lower endpoint at or beyond that depth therefore proves the op is an identity even // when the broader shared near-surface gate (SDFBlendRadius*3) remains open. - return FMath::IsFinite(RoughnessDepth) && H.Sdf.IsKnown() + return VoxelMath::IsFinite(RoughnessDepth) && H.Sdf.IsKnown() && H.Sdf.Min >= RoughnessDepth; } @@ -7372,8 +7372,8 @@ namespace float MaxAmplitude() const { return (P.TerraceStepHeight > 0.0f - && FMath::IsFinite(P.TerraceStepHeight) - && FMath::IsFinite(P.TerraceHardness) + && VoxelMath::IsFinite(P.TerraceStepHeight) + && VoxelMath::IsFinite(P.TerraceHardness) && P.TerraceHardness >= 0.0f && P.TerraceHardness <= 1.0f) ? P.TerraceStepHeight : (P.TerraceStepHeight > 0.0f ? FLT_MAX : 0.0f); } @@ -7473,7 +7473,7 @@ namespace float MaxAmplitude() const { if (!(P.LayerLineSpacing > 0.0f)) { return 0.0f; } - return FMath::IsFinite(P.LayerLineDepth) + return VoxelMath::IsFinite(P.LayerLineDepth) ? FMath::Max(P.LayerLineDepth, 0.0f) : FLT_MAX; } @@ -7571,7 +7571,7 @@ namespace float MaxAmplitude() const { if (!(P.RibbingSpacing > 0.0f)) { return 0.0f; } - return FMath::IsFinite(P.RibbingDepth) + return VoxelMath::IsFinite(P.RibbingDepth) ? FMath::Max(P.RibbingDepth, 0.0f) : FLT_MAX; } @@ -7675,14 +7675,14 @@ namespace float MaxAmplitude() const { if (!(P.OverhangStrength > 0.0f && P.OverhangDepth > 0.0f)) { return 0.0f; } - if (!FMath::IsFinite(P.OverhangDepth) - || !FMath::IsFinite(P.OverhangStrength)) + if (!VoxelMath::IsFinite(P.OverhangDepth) + || !VoxelMath::IsFinite(P.OverhangStrength)) { return FLT_MAX; } const float Result = VOXEL_NOISE_SCALE * VF_PerlinAbsBound * P.OverhangDepth * P.OverhangStrength; - return FMath::IsFinite(Result) ? Result : FLT_MAX; + return VoxelMath::IsFinite(Result) ? Result : FLT_MAX; } private: @@ -7801,10 +7801,10 @@ namespace float MaxAmplitude() const { if (!(P.CliffStrength > 0.0f)) { return 0.0f; } - if (!FMath::IsFinite(P.CliffStrength)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(P.CliffStrength)) { return FLT_MAX; } const float Result = 8.0f * 3.0f * VOXEL_NOISE_SCALE * VF_PerlinAbsBound * P.CliffStrength; - return FMath::IsFinite(Result) ? Result : FLT_MAX; + return VoxelMath::IsFinite(Result) ? Result : FLT_MAX; } private: @@ -7908,9 +7908,9 @@ namespace float MaxAmplitude() const { if (!(P.ScallopStrength > 0.0f)) { return 0.0f; } - if (!FMath::IsFinite(P.ScallopStrength)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(P.ScallopStrength)) { return FLT_MAX; } const float Result = VF_CellularAbsBound * P.ScallopStrength; - return FMath::IsFinite(Result) ? Result : FLT_MAX; + return VoxelMath::IsFinite(Result) ? Result : FLT_MAX; } private: @@ -8037,9 +8037,9 @@ namespace float MaxAmplitude() const { if (!(P.ArchDensity > 0.0f)) { return 0.0f; } - if (!FMath::IsFinite(P.BaseDensity)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(P.BaseDensity)) { return FLT_MAX; } const float Result = FMath::Abs(P.BaseDensity) * 3.0f * 1.5f; - return FMath::IsFinite(Result) ? Result : FLT_MAX; + return VoxelMath::IsFinite(Result) ? Result : FLT_MAX; } private: @@ -8130,7 +8130,7 @@ namespace } const float Bound = Rooms->RoomColumnFillSupremumForLastBox(); if (!(Bound > 0.0f)) { return EVoxelOpEffect::Identity; } - return FMath::IsFinite(Bound) ? EVoxelOpEffect::FillOnly : EVoxelOpEffect::Both; + return VoxelMath::IsFinite(Bound) ? EVoxelOpEffect::FillOnly : EVoxelOpEffect::Both; } float MaxCarveOverBox(const FBox&, const FVoxelOpContext&) const override { return 0.0f; } @@ -8145,7 +8145,7 @@ namespace { if (Rooms == nullptr || VF_CaveBoxIsFar(H, P.SDFBlendRadius)) { return 0.0f; } const float Bound = Rooms->RoomColumnFillSupremumForLastBox(); - return FMath::IsFinite(Bound) ? 0.0f : Bound; + return VoxelMath::IsFinite(Bound) ? 0.0f : Bound; } float MaxFillOverBox(const FBox&, const FVoxelOpContext&, @@ -8293,9 +8293,9 @@ namespace float MaxAmplitude() const { if (!(P.DomeDensity > 0.0f)) { return 0.0f; } - if (!FMath::IsFinite(P.BaseDensity)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(P.BaseDensity)) { return FLT_MAX; } const float Result = FMath::Abs(P.BaseDensity) * 2.0f * 1.5f; - return FMath::IsFinite(Result) ? Result : FLT_MAX; + return VoxelMath::IsFinite(Result) ? Result : FLT_MAX; } private: @@ -8415,9 +8415,9 @@ namespace float MaxAmplitude() const { if (!(P.PinchDensity > 0.0f)) { return 0.0f; } - if (!FMath::IsFinite(P.BaseDensity)) { return FLT_MAX; } + if (!VoxelMath::IsFinite(P.BaseDensity)) { return FLT_MAX; } const float Result = FMath::Abs(P.BaseDensity) * 3.0f * 1.5f; - return FMath::IsFinite(Result) ? Result : FLT_MAX; + return VoxelMath::IsFinite(Result) ? Result : FLT_MAX; } private: @@ -8504,7 +8504,7 @@ namespace float MaxFillOverBox(const FBox&, const FVoxelOpContext&) const override { - return FMath::IsFinite(P.FloorBias) && P.FloorBias >= 0.0f + return VoxelMath::IsFinite(P.FloorBias) && P.FloorBias >= 0.0f ? P.FloorBias : (P.FloorBias < 0.0f ? 0.0f : FLT_MAX); } @@ -8646,12 +8646,12 @@ namespace const FVoxelBoxHypotheses& H) const override { const float Strength = MaxCarveOverBox(VoxelBox, Ctx); - if (!(Strength > 0.0f) || !FMath::IsFinite(Strength)) + if (!(Strength > 0.0f) || !VoxelMath::IsFinite(Strength)) { return Strength; } - if (!(P.WormNetworkRange > 0.0f) || !FMath::IsFinite(P.WormNetworkRange) - || !H.Sdf.IsKnown() || !FMath::IsFinite(H.Sdf.Min)) + if (!(P.WormNetworkRange > 0.0f) || !VoxelMath::IsFinite(P.WormNetworkRange) + || !H.Sdf.IsKnown() || !VoxelMath::IsFinite(H.Sdf.Min)) { return Strength; } @@ -8664,11 +8664,11 @@ namespace // interval's worst case, so this never assumes a room value that the preceding source // did not prove. if (Ctx.bUseLatticeProof && Ctx.bTightenWarpProof && Ctx.Step >= 1 - && FMath::IsFinite(P.WormFrequency) - && FMath::IsFinite(P.WormHorizontalBias) - && FMath::IsFinite(P.WormThreshold) + && VoxelMath::IsFinite(P.WormFrequency) + && VoxelMath::IsFinite(P.WormHorizontalBias) + && VoxelMath::IsFinite(P.WormThreshold) && P.WormThreshold > 0.0f - && FMath::IsFinite(P.VerticalScale)) + && VoxelMath::IsFinite(P.VerticalScale)) { const int32 Step = FMath::Max(Ctx.Step, 1); const float OriginX = static_cast(Ctx.LatticeOriginVoxels.X); @@ -8774,7 +8774,7 @@ namespace float MaxCarveAmplitude() const { if (!(P.WormStrength > 0.0f && P.WormThreshold > 0.0f)) { return 0.0f; } - return FMath::IsFinite(P.WormStrength) ? P.WormStrength : FLT_MAX; + return VoxelMath::IsFinite(P.WormStrength) ? P.WormStrength : FLT_MAX; } const TCHAR* DebugName() const override { return TEXT("WormFieldSource"); } @@ -9200,7 +9200,7 @@ void FVoxelOpStack::DiagnoseBox(const FBox& VoxelBox, const FVoxelOpContext& Ctx Entry.Op->Eval(static_cast(X), static_cast(Y), static_cast(Z), Sample); const float DensityDelta = Sample.Density - DensityBefore; - if (FMath::IsFinite(DensityDelta)) + if (VoxelMath::IsFinite(DensityDelta)) { Diagnostic.bHasDensityDelta = true; Diagnostic.ActualDensityDeltaMin = @@ -9219,12 +9219,12 @@ void FVoxelOpStack::DiagnoseBox(const FBox& VoxelBox, const FVoxelOpContext& Ctx * FMath::Max(1.0f, FMath::Abs(Diagnostic.MaxCarve)); const float FillEpsilon = BoundEpsilon * FMath::Max(1.0f, FMath::Abs(Diagnostic.MaxFill)); - if (FMath::IsFinite(Diagnostic.MaxCarve) + if (VoxelMath::IsFinite(Diagnostic.MaxCarve) && DensityDelta < -Diagnostic.MaxCarve - CarveEpsilon) { Diagnostic.bCarveBoundViolated = true; } - if (FMath::IsFinite(Diagnostic.MaxFill) + if (VoxelMath::IsFinite(Diagnostic.MaxFill) && DensityDelta > Diagnostic.MaxFill + FillEpsilon) { Diagnostic.bFillBoundViolated = true; @@ -9232,7 +9232,7 @@ void FVoxelOpStack::DiagnoseBox(const FBox& VoxelBox, const FVoxelOpContext& Ctx } } - if (Diagnostic.bWritesSdf && FMath::IsFinite(Sample.Sdf)) + if (Diagnostic.bWritesSdf && VoxelMath::IsFinite(Sample.Sdf)) { Diagnostic.bHasSdfValue = true; Diagnostic.ActualSdfMin = FMath::Min( @@ -10338,8 +10338,8 @@ bool VF_BuildStackFromRecipe(const FVoxelOpStackRecipe& Recipe, const float Bottom = VoxelStrateRecipePrivate::Bottom(Params, Recipe.StructuralParamBlock); const float Seal = VoxelStrateRecipePrivate::BoundarySeal(Params, Recipe.StructuralParamBlock); const float Base = VoxelStrateRecipePrivate::BaseDensity(Params, Recipe.StructuralParamBlock); - if (!FMath::IsFinite(Top) || !FMath::IsFinite(Bottom) || Top <= Bottom - || !FMath::IsFinite(Seal) || !FMath::IsFinite(Base)) + if (!VoxelMath::IsFinite(Top) || !VoxelMath::IsFinite(Bottom) || Top <= Bottom + || !VoxelMath::IsFinite(Seal) || !VoxelMath::IsFinite(Base)) { return Fail(TEXT("Recipe structural parameters do not define a finite positive strate.")); } @@ -10574,9 +10574,9 @@ bool VF_BuildStrateRegionStack( ? Manifest.BoundarySealThickness : VF_RegionSeal(FirstParams, GlobalBlock); float Base = Manifest.bHasGlobalStructuralParams ? Manifest.BaseDensity : VF_RegionBase(FirstParams, GlobalBlock); - if (!FMath::IsFinite(Top) || !FMath::IsFinite(Bottom) || !(Top > Bottom) - || !FMath::IsFinite(Seal) || Seal < 0.0f - || !FMath::IsFinite(Base) || !(Base > 0.0f)) + if (!VoxelMath::IsFinite(Top) || !VoxelMath::IsFinite(Bottom) || !(Top > Bottom) + || !VoxelMath::IsFinite(Seal) || Seal < 0.0f + || !VoxelMath::IsFinite(Base) || !(Base > 0.0f)) { return Fail(TEXT("lateral region structural parameters are not a finite positive strate")); } diff --git a/Source/VoxelForge/Private/VoxelGenerator.cpp b/Source/VoxelForge/Private/VoxelGenerator.cpp index ce0d243..a7ff304 100644 --- a/Source/VoxelForge/Private/VoxelGenerator.cpp +++ b/Source/VoxelForge/Private/VoxelGenerator.cpp @@ -29,8 +29,37 @@ #include +namespace VoxelMath +{ + VOXELFORGE_API int32 GFastIsFinite = 1; +} + namespace { + // The default-on branch uses VoxelMath::IsFiniteFast, while 0 retains the original + // FMath::IsFinite call for a real within-round A/B and sampler comparison. + FAutoConsoleVariableRef CVarVoxelForgeFastIsFinite( + TEXT("voxel.FastIsFinite"), + VoxelMath::GFastIsFinite, + TEXT("Use the exact inline IEEE-754 bit test instead of the CRT-backed FMath::IsFinite.")); + bool GVoxelForgeFastIsFiniteSwitchParsed = false; + + void VF_ParseFastIsFiniteSwitch() + { + if (GVoxelForgeFastIsFiniteSwitchParsed) + { + return; + } + GVoxelForgeFastIsFiniteSwitchParsed = true; + int32 CommandLineValue = VoxelMath::GFastIsFinite; + if (FParse::Value( + FCommandLine::Get(), TEXT("voxel.FastIsFinite="), CommandLineValue)) + { + VoxelMath::GFastIsFinite = CommandLineValue; + } + VoxelMath::GFastIsFinite = VoxelMath::GFastIsFinite != 0 ? 1 : 0; + } + // The canonical native TunnelNetwork/Underwater graph has a hand-lowered evaluator. Keep a // command-line switch so every change can be A/B'd against the existing interpreted/block // path without changing the authored graph or rebuilding a different world definition. @@ -861,7 +890,7 @@ static FORCEINLINE bool VF_DisturbanceBandTouchesBox( { const float InnerTop = D.StrateTopWorldZ - D.BoundarySealThickness; const float InnerBot = D.StrateBottomWorldZ + D.BoundarySealThickness; - if (!FMath::IsFinite(InnerTop) || !FMath::IsFinite(InnerBot)) + if (!VoxelMath::IsFinite(InnerTop) || !VoxelMath::IsFinite(InnerBot)) { return true; // Unknown boundary placement must not discharge a proof. } @@ -929,16 +958,16 @@ static bool VF_AnyChasmCanTouchLattice( { return false; } - if (!FMath::IsFinite(D.ChasmDensity) || D.ChasmDensity < 0.0f - || D.ChasmDensity > 1.0f || !FMath::IsFinite(D.ChasmSpacing) - || !FMath::IsFinite(D.ChasmRadius) || D.ChasmSpacing <= 0.0f + if (!VoxelMath::IsFinite(D.ChasmDensity) || D.ChasmDensity < 0.0f + || D.ChasmDensity > 1.0f || !VoxelMath::IsFinite(D.ChasmSpacing) + || !VoxelMath::IsFinite(D.ChasmRadius) || D.ChasmSpacing <= 0.0f || D.ChasmRadius < 0.0f) { return true; } const float InnerTop = D.StrateTopWorldZ - D.BoundarySealThickness; const float InnerBot = D.StrateBottomWorldZ + D.BoundarySealThickness; - if (!FMath::IsFinite(InnerTop) || !FMath::IsFinite(InnerBot)) + if (!VoxelMath::IsFinite(InnerTop) || !VoxelMath::IsFinite(InnerBot)) { return true; } @@ -987,16 +1016,16 @@ static bool VF_AnyBridgeCanTouchLattice( { return false; } - if (!FMath::IsFinite(D.BridgeDensity) || D.BridgeDensity < 0.0f - || D.BridgeDensity > 1.0f || !FMath::IsFinite(D.BridgeSpacing) - || !FMath::IsFinite(D.BridgeRadius) || D.BridgeSpacing <= 0.0f + if (!VoxelMath::IsFinite(D.BridgeDensity) || D.BridgeDensity < 0.0f + || D.BridgeDensity > 1.0f || !VoxelMath::IsFinite(D.BridgeSpacing) + || !VoxelMath::IsFinite(D.BridgeRadius) || D.BridgeSpacing <= 0.0f || D.BridgeRadius < 0.0f) { return true; } const float InnerTop = D.StrateTopWorldZ - D.BoundarySealThickness; const float InnerBot = D.StrateBottomWorldZ + D.BoundarySealThickness; - if (!FMath::IsFinite(InnerTop) || !FMath::IsFinite(InnerBot)) + if (!VoxelMath::IsFinite(InnerTop) || !VoxelMath::IsFinite(InnerBot)) { return true; } @@ -1055,16 +1084,16 @@ static bool VF_AnyRidgeCanTouchLattice( { return false; } - if (!FMath::IsFinite(D.RidgeDensity) || D.RidgeDensity < 0.0f - || D.RidgeDensity > 1.0f || !FMath::IsFinite(D.RidgeSpacing) - || !FMath::IsFinite(D.RidgeHeight) || !FMath::IsFinite(D.RidgeThickness) + if (!VoxelMath::IsFinite(D.RidgeDensity) || D.RidgeDensity < 0.0f + || D.RidgeDensity > 1.0f || !VoxelMath::IsFinite(D.RidgeSpacing) + || !VoxelMath::IsFinite(D.RidgeHeight) || !VoxelMath::IsFinite(D.RidgeThickness) || D.RidgeSpacing <= 0.0f || D.RidgeHeight <= 0.0f || D.RidgeThickness < 0.0f) { return true; } const float InnerTop = D.StrateTopWorldZ - D.BoundarySealThickness; const float InnerBot = D.StrateBottomWorldZ + D.BoundarySealThickness; - if (!FMath::IsFinite(InnerTop) || !FMath::IsFinite(InnerBot)) + if (!VoxelMath::IsFinite(InnerTop) || !VoxelMath::IsFinite(InnerBot)) { return true; } @@ -1124,9 +1153,9 @@ static bool VF_AnyChasmCanTouchBox( { return false; } - if (!FMath::IsFinite(D.ChasmDensity) || D.ChasmDensity < 0.0f - || D.ChasmDensity > 1.0f || !FMath::IsFinite(D.ChasmSpacing) - || !FMath::IsFinite(D.ChasmRadius) || D.ChasmSpacing <= 0.0f + if (!VoxelMath::IsFinite(D.ChasmDensity) || D.ChasmDensity < 0.0f + || D.ChasmDensity > 1.0f || !VoxelMath::IsFinite(D.ChasmSpacing) + || !VoxelMath::IsFinite(D.ChasmRadius) || D.ChasmSpacing <= 0.0f || D.ChasmRadius < 0.0f) { return true; @@ -1171,9 +1200,9 @@ static bool VF_AnyBridgeCanTouchBox( { return false; } - if (!FMath::IsFinite(D.BridgeDensity) || D.BridgeDensity < 0.0f - || D.BridgeDensity > 1.0f || !FMath::IsFinite(D.BridgeSpacing) - || !FMath::IsFinite(D.BridgeRadius) || D.BridgeSpacing <= 0.0f + if (!VoxelMath::IsFinite(D.BridgeDensity) || D.BridgeDensity < 0.0f + || D.BridgeDensity > 1.0f || !VoxelMath::IsFinite(D.BridgeSpacing) + || !VoxelMath::IsFinite(D.BridgeRadius) || D.BridgeSpacing <= 0.0f || D.BridgeRadius < 0.0f) { return true; @@ -1224,9 +1253,9 @@ static bool VF_AnyRidgeCanTouchBox( { return false; } - if (!FMath::IsFinite(D.RidgeDensity) || D.RidgeDensity < 0.0f - || D.RidgeDensity > 1.0f || !FMath::IsFinite(D.RidgeSpacing) - || !FMath::IsFinite(D.RidgeHeight) || !FMath::IsFinite(D.RidgeThickness) + if (!VoxelMath::IsFinite(D.RidgeDensity) || D.RidgeDensity < 0.0f + || D.RidgeDensity > 1.0f || !VoxelMath::IsFinite(D.RidgeSpacing) + || !VoxelMath::IsFinite(D.RidgeHeight) || !VoxelMath::IsFinite(D.RidgeThickness) || D.RidgeSpacing <= 0.0f || D.RidgeHeight <= 0.0f || D.RidgeThickness < 0.0f) { return true; @@ -1758,6 +1787,7 @@ UVoxelGenerator::UVoxelGenerator() void UVoxelGenerator::InitializeSettings(const UVoxelSettings* Settings) { + VF_ParseFastIsFiniteSwitch(); // Les paramètres globaux sont copiés ici une seule fois : le chemin voxel ne doit pas // déréférencer l'asset de settings. Seed = Settings ? Settings->GetEffectiveWorldSeed() : 0; @@ -1780,6 +1810,7 @@ void UVoxelGenerator::EndDensityBlock() const float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) const { TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_GetDensityAt); + VF_ParseFastIsFiniteSwitch(); VF_ParseFusedEvaluatorSwitch(); VoxelDensityProfile::FScopedTimer DensityProfileTimer( VoxelDensityProfile::EBucket::GetDensityAt); @@ -5009,7 +5040,7 @@ static EVoxelTileClass VF_ClassifyBoxRefined(const FVoxelOpStack& Stack, for (const int32 IX : Xs) { const float Density = EvaluateExactLatticeValue(IX, IY, IZ, bUseFinalField); - if (!FMath::IsFinite(Density) || Density == 0.0f) + if (!VoxelMath::IsFinite(Density) || Density == 0.0f) { return false; } @@ -5053,7 +5084,7 @@ static EVoxelTileClass VF_ClassifyBoxRefined(const FVoxelOpStack& Stack, for (int32 IX = IX0; IX <= IX1; ++IX) { const float Density = EvaluateExactLatticeValue(IX, IY, IZ, bUseFinalField); - if (!FMath::IsFinite(Density) || Density == 0.0f) + if (!VoxelMath::IsFinite(Density) || Density == 0.0f) { ++ZeroOrInvalidCount; IZ = IZ1; diff --git a/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp b/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp index d956e07..9a4e5f7 100644 --- a/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp +++ b/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp @@ -459,7 +459,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels, BlockOrigin.X + X * Step, BlockOrigin.Y + Y * Step, BlockOrigin.Z + Z * Step); - if (!FMath::IsFinite(Density) || Density == 0.0f) + if (!VoxelMath::IsFinite(Density) || Density == 0.0f) { bAllSolid = false; bAllAir = false; @@ -646,7 +646,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels, { const float Density = DensityGrid[ ((Z + 1) * MDim + (Y + 1)) * MDim + (X + 1)]; - if (!FMath::IsFinite(Density) + if (!VoxelMath::IsFinite(Density) || Density == 0.0f || (Candidate == 1 ? Density >= 0.0f : Density <= 0.0f)) { diff --git a/Source/VoxelForge/Private/VoxelStrateManager.cpp b/Source/VoxelForge/Private/VoxelStrateManager.cpp index ca72f59..ae90865 100644 --- a/Source/VoxelForge/Private/VoxelStrateManager.cpp +++ b/Source/VoxelForge/Private/VoxelStrateManager.cpp @@ -56,7 +56,7 @@ namespace Result.bSurfaceRoughness = FParse::Value( *CommandLine, TEXT("voxel.surfaceroughness="), Result.SurfaceRoughness); if (Result.bSurfaceRoughness - && (!FMath::IsFinite(Result.SurfaceRoughness) || Result.SurfaceRoughness < 0.0f)) + && (!VoxelMath::IsFinite(Result.SurfaceRoughness) || Result.SurfaceRoughness < 0.0f)) { Result.bSurfaceRoughness = false; } @@ -64,7 +64,7 @@ namespace Result.bFrequency = FParse::Value( *CommandLine, TEXT("voxel.roughnessfrequency="), Result.Frequency); if (Result.bFrequency - && (!FMath::IsFinite(Result.Frequency) || Result.Frequency <= 0.0f)) + && (!VoxelMath::IsFinite(Result.Frequency) || Result.Frequency <= 0.0f)) { Result.bFrequency = false; } @@ -423,14 +423,14 @@ namespace const FVoxelPassage& Passage, double X, double Y) { if (!(Passage.NativeFloorReliefStrength > 0.0f) - || !FMath::IsFinite(Passage.NativeFloorReliefStrength) - || !FMath::IsFinite(Passage.NativeFloorReliefFrequency) + || !VoxelMath::IsFinite(Passage.NativeFloorReliefStrength) + || !VoxelMath::IsFinite(Passage.NativeFloorReliefFrequency) || !(FMath::Abs(Passage.NativeFloorReliefFrequency) > KINDA_SMALL_NUMBER)) { return 0.0f; } - const bool bInteger = FMath::IsFinite(X) && FMath::IsFinite(Y) + const bool bInteger = VoxelMath::IsFinite(X) && VoxelMath::IsFinite(Y) && FMath::FloorToDouble(X) == X && FMath::FloorToDouble(Y) == Y && X >= static_cast(MIN_int32) && X <= static_cast(MAX_int32) && Y >= static_cast(MIN_int32) && Y <= static_cast(MAX_int32); @@ -487,10 +487,10 @@ namespace float Strength, float Frequency) { if (!(Strength > 0.0f) - || !FMath::IsFinite(Strength) - || !FMath::IsFinite(Frequency) + || !VoxelMath::IsFinite(Strength) + || !VoxelMath::IsFinite(Frequency) || !(FMath::Abs(Frequency) > KINDA_SMALL_NUMBER) - || !FMath::IsFinite(BaseGradient) + || !VoxelMath::IsFinite(BaseGradient) || BaseGradient < 0.0f) { return 0.0f; @@ -521,7 +521,7 @@ namespace const float ReliefGradientBound = NoiseGradientBound + EnvelopeDerivativeBound; if (!(ReliefGradientBound > KINDA_SMALL_NUMBER) - || !FMath::IsFinite(ReliefGradientBound) + || !VoxelMath::IsFinite(ReliefGradientBound) || !(HorizontalRun > KINDA_SMALL_NUMBER)) { return 0.0f; @@ -648,8 +648,8 @@ namespace * VOXEL_NOISE_SCALE * Passage.NativeFloorReliefStrength * Envelope * ReliefScale; OutSupportRadius = BestSupportRadius; - return FMath::IsFinite(OutFloorZ) - && FMath::IsFinite(OutSupportRadius) + return VoxelMath::IsFinite(OutFloorZ) + && VoxelMath::IsFinite(OutSupportRadius) && OutSupportRadius > 0.0f; } @@ -784,7 +784,7 @@ static void VF_AuthorNativePassageFloor( const float Radius = FMath::Abs(Passage.ControlRadii[PointIndex]); const float FloorZ = VoxelPassageGeometry::TunnelFloorZ( Passage.ControlPoints[PointIndex], Radius); - if (!FMath::IsFinite(FloorZ)) + if (!VoxelMath::IsFinite(FloorZ)) { Passage.NativeFloorProfileZ.Reset(); return; @@ -1867,11 +1867,11 @@ void UVoxelStrateManager::GeneratePassages() const float UpperSealThickness = BoundarySealThicknessFor(*UpperDef); const float InnerBottomZ = UpperBottomZ + UpperSealThickness; const float InnerTopZ = UpperTopZ - UpperSealThickness; - if (FMath::IsFinite(SuggestedUpperPoint.X) - && FMath::IsFinite(SuggestedUpperPoint.Y) - && FMath::IsFinite(SuggestedUpperPoint.Z) - && FMath::IsFinite(InnerBottomZ) - && FMath::IsFinite(InnerTopZ) + if (VoxelMath::IsFinite(SuggestedUpperPoint.X) + && VoxelMath::IsFinite(SuggestedUpperPoint.Y) + && VoxelMath::IsFinite(SuggestedUpperPoint.Z) + && VoxelMath::IsFinite(InnerBottomZ) + && VoxelMath::IsFinite(InnerTopZ) && InnerBottomZ < InnerTopZ) { // VF_SuggestLandingPoint already guarantees a strict interior answer. Keep a @@ -1894,11 +1894,11 @@ void UVoxelStrateManager::GeneratePassages() const float LowerBottomZ = (float)(Lower.BottomChunkZ) * CHUNK_SIZE; const float InnerBottomZ = LowerBottomZ + LowerSealThickness; const float InnerTopZ = LowerTopZ - LowerSealThickness; - if (FMath::IsFinite(SuggestedLowerPoint.X) - && FMath::IsFinite(SuggestedLowerPoint.Y) - && FMath::IsFinite(SuggestedLowerPoint.Z) - && FMath::IsFinite(InnerBottomZ) - && FMath::IsFinite(InnerTopZ) + if (VoxelMath::IsFinite(SuggestedLowerPoint.X) + && VoxelMath::IsFinite(SuggestedLowerPoint.Y) + && VoxelMath::IsFinite(SuggestedLowerPoint.Z) + && VoxelMath::IsFinite(InnerBottomZ) + && VoxelMath::IsFinite(InnerTopZ) && InnerBottomZ < InnerTopZ) { // VF_SuggestLandingPoint already guarantees a strict interior answer. Keep a @@ -2724,9 +2724,9 @@ void UVoxelStrateManager::ApplyPassageLandingFloorMC( static bool VF_IsPassageRoomFloor( const FVector& Position, const FVoxelPassageLanding& Landing) { - if (!FMath::IsFinite(Position.X) || !FMath::IsFinite(Position.Y) - || !FMath::IsFinite(Position.Z) || !FMath::IsFinite(Landing.FloorZ) - || !FMath::IsFinite(Landing.FloorThickness) + if (!VoxelMath::IsFinite(Position.X) || !VoxelMath::IsFinite(Position.Y) + || !VoxelMath::IsFinite(Position.Z) || !VoxelMath::IsFinite(Landing.FloorZ) + || !VoxelMath::IsFinite(Landing.FloorThickness) || Landing.FloorThickness <= 0.0f || Landing.HalfWidth <= 0.0f) { return false; @@ -3035,8 +3035,8 @@ namespace float Min, float Max, float Origin, int32 Step, int32& OutFirst, int32& OutLast) { - if (Step <= 0 || !FMath::IsFinite(Min) || !FMath::IsFinite(Max) - || !FMath::IsFinite(Origin) || Min > Max) + if (Step <= 0 || !VoxelMath::IsFinite(Min) || !VoxelMath::IsFinite(Max) + || !VoxelMath::IsFinite(Origin) || Min > Max) { return false; } @@ -3065,9 +3065,9 @@ namespace auto AddDomain = [&](const FVector& Min, const FVector& Max) -> bool { - if (!FMath::IsFinite(Min.X) || !FMath::IsFinite(Min.Y) - || !FMath::IsFinite(Min.Z) || !FMath::IsFinite(Max.X) - || !FMath::IsFinite(Max.Y) || !FMath::IsFinite(Max.Z) + if (!VoxelMath::IsFinite(Min.X) || !VoxelMath::IsFinite(Min.Y) + || !VoxelMath::IsFinite(Min.Z) || !VoxelMath::IsFinite(Max.X) + || !VoxelMath::IsFinite(Max.Y) || !VoxelMath::IsFinite(Max.Z) || Min.X > Max.X || Min.Y > Max.Y || Min.Z > Max.Z) { return false; @@ -3078,11 +3078,11 @@ namespace for (const FVoxelPassage& Passage : Passages) { - if (!FMath::IsFinite(Passage.BoundCenter.X) - || !FMath::IsFinite(Passage.BoundCenter.Y) - || !FMath::IsFinite(Passage.BoundCenter.Z) - || !FMath::IsFinite(Passage.BoundRadius) - || !FMath::IsFinite(Passage.BoundRadiusSq) + 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) { // Invalid generated geometry is an unknown, not an empty passage set. @@ -3096,7 +3096,7 @@ namespace return false; } const bool bTaper = Passage.ControlRadii.Num() == Passage.ControlPoints.Num(); - if (!bTaper && !FMath::IsFinite(Passage.Radius)) + if (!bTaper && !VoxelMath::IsFinite(Passage.Radius)) { return false; } @@ -3104,12 +3104,12 @@ namespace PointIndex < Passage.ControlPoints.Num(); ++PointIndex) { const FVector& Point = Passage.ControlPoints[PointIndex]; - if (!FMath::IsFinite(Point.X) || !FMath::IsFinite(Point.Y) - || !FMath::IsFinite(Point.Z)) + if (!VoxelMath::IsFinite(Point.X) || !VoxelMath::IsFinite(Point.Y) + || !VoxelMath::IsFinite(Point.Z)) { return false; } - if (bTaper && !FMath::IsFinite(Passage.ControlRadii[PointIndex])) + if (bTaper && !VoxelMath::IsFinite(Passage.ControlRadii[PointIndex])) { return false; } @@ -3125,7 +3125,7 @@ namespace const float RadiusB = bTaper ? Passage.ControlRadii[SegmentIndex + 1] : Passage.Radius; const float Radius = FMath::Max(FMath::Abs(RadiusA), FMath::Abs(RadiusB)); - if (!FMath::IsFinite(Radius)) + if (!VoxelMath::IsFinite(Radius)) { return false; } @@ -3143,13 +3143,13 @@ namespace } else { - if (!FMath::IsFinite(Passage.UpperPoint.X) - || !FMath::IsFinite(Passage.UpperPoint.Y) - || !FMath::IsFinite(Passage.UpperPoint.Z) - || !FMath::IsFinite(Passage.LowerPoint.X) - || !FMath::IsFinite(Passage.LowerPoint.Y) - || !FMath::IsFinite(Passage.LowerPoint.Z) - || !FMath::IsFinite(Passage.Radius)) + 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 false; } @@ -3170,11 +3170,11 @@ namespace &Passage.UpperLanding, &Passage.LowerLanding }; for (const FVoxelPassageLanding* Landing : Landings) { - if (!FMath::IsFinite(Landing->StandingPoint.X) - || !FMath::IsFinite(Landing->StandingPoint.Y) - || !FMath::IsFinite(Landing->FloorZ) - || !FMath::IsFinite(Landing->CeilingZ) - || !FMath::IsFinite(Landing->HalfWidth)) + if (!VoxelMath::IsFinite(Landing->StandingPoint.X) + || !VoxelMath::IsFinite(Landing->StandingPoint.Y) + || !VoxelMath::IsFinite(Landing->FloorZ) + || !VoxelMath::IsFinite(Landing->CeilingZ) + || !VoxelMath::IsFinite(Landing->HalfWidth)) { // The canonical evaluator returns FLT_MAX for this landing. Treating it as // unknown is safer for future evaluator changes and only gives up a skip. @@ -3238,7 +3238,7 @@ namespace const float SmoothDip = static_cast(DomainCount - 1) * (SmoothMinK / 6.0f); const float Padding = CarveThreshold + SmoothDip; - if (!FMath::IsFinite(Padding) || Padding < 0.0f) + if (!VoxelMath::IsFinite(Padding) || Padding < 0.0f) { return true; } @@ -3273,8 +3273,8 @@ namespace const TArray& Passages, const FVector& Position) { - if (!FMath::IsFinite(Position.X) || !FMath::IsFinite(Position.Y) - || !FMath::IsFinite(Position.Z)) + if (!VoxelMath::IsFinite(Position.X) || !VoxelMath::IsFinite(Position.Y) + || !VoxelMath::IsFinite(Position.Z)) { return true; } @@ -3285,11 +3285,11 @@ namespace float ModifierLowerBound = FLT_MAX; for (const FVoxelPassage& Passage : Passages) { - if (!FMath::IsFinite(Passage.BoundCenter.X) - || !FMath::IsFinite(Passage.BoundCenter.Y) - || !FMath::IsFinite(Passage.BoundCenter.Z) - || !FMath::IsFinite(Passage.BoundRadius) - || !FMath::IsFinite(Passage.BoundRadiusSq) + 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; @@ -3307,7 +3307,7 @@ namespace return true; } const bool bTaper = Passage.ControlRadii.Num() == Passage.ControlPoints.Num(); - if (!bTaper && !FMath::IsFinite(Passage.Radius)) + if (!bTaper && !VoxelMath::IsFinite(Passage.Radius)) { return true; } @@ -3315,12 +3315,12 @@ namespace PointIndex < Passage.ControlPoints.Num(); ++PointIndex) { const FVector& Point = Passage.ControlPoints[PointIndex]; - if (!FMath::IsFinite(Point.X) || !FMath::IsFinite(Point.Y) - || !FMath::IsFinite(Point.Z)) + if (!VoxelMath::IsFinite(Point.X) || !VoxelMath::IsFinite(Point.Y) + || !VoxelMath::IsFinite(Point.Z)) { return true; } - if (bTaper && !FMath::IsFinite(Passage.ControlRadii[PointIndex])) + if (bTaper && !VoxelMath::IsFinite(Passage.ControlRadii[PointIndex])) { return true; } @@ -3337,7 +3337,7 @@ namespace const float Radius = FMath::Max(FMath::Abs(RadiusA), FMath::Abs(RadiusB)); const float SegmentLowerBound = VF_DistanceToSegmentLowerBound( Position, A, B, Radius); - if (!FMath::IsFinite(SegmentLowerBound)) + if (!VoxelMath::IsFinite(SegmentLowerBound)) { return true; } @@ -3347,20 +3347,20 @@ namespace } else { - if (!FMath::IsFinite(Passage.UpperPoint.X) - || !FMath::IsFinite(Passage.UpperPoint.Y) - || !FMath::IsFinite(Passage.UpperPoint.Z) - || !FMath::IsFinite(Passage.LowerPoint.X) - || !FMath::IsFinite(Passage.LowerPoint.Y) - || !FMath::IsFinite(Passage.LowerPoint.Z) - || !FMath::IsFinite(Passage.Radius)) + 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; } PassageLowerBound = VF_DistanceToSegmentLowerBound( Position, Passage.UpperPoint, Passage.LowerPoint, FMath::Abs(Passage.Radius)); - if (!FMath::IsFinite(PassageLowerBound)) + if (!VoxelMath::IsFinite(PassageLowerBound)) { return true; } @@ -3370,11 +3370,11 @@ namespace &Passage.UpperLanding, &Passage.LowerLanding }; for (const FVoxelPassageLanding* Landing : Landings) { - if (!FMath::IsFinite(Landing->StandingPoint.X) - || !FMath::IsFinite(Landing->StandingPoint.Y) - || !FMath::IsFinite(Landing->FloorZ) - || !FMath::IsFinite(Landing->CeilingZ) - || !FMath::IsFinite(Landing->HalfWidth)) + if (!VoxelMath::IsFinite(Landing->StandingPoint.X) + || !VoxelMath::IsFinite(Landing->StandingPoint.Y) + || !VoxelMath::IsFinite(Landing->FloorZ) + || !VoxelMath::IsFinite(Landing->CeilingZ) + || !VoxelMath::IsFinite(Landing->HalfWidth)) { return true; } @@ -3384,7 +3384,7 @@ namespace continue; } const float LandingSDF = VF_EvaluatePassageLandingSDF(Position, *Landing); - if (!FMath::IsFinite(LandingSDF)) + if (!VoxelMath::IsFinite(LandingSDF)) { return true; } @@ -3395,7 +3395,7 @@ namespace ModifierLowerBound = VoxelSDF::SmoothMin( ModifierLowerBound, PassageLowerBound, BlendK); } - return !FMath::IsFinite(ModifierLowerBound) + return !VoxelMath::IsFinite(ModifierLowerBound) || ModifierLowerBound < CarveThreshold + NumericalSafetyMargin; } @@ -3419,8 +3419,8 @@ namespace static bool VF_SegmentDistanceToBox( const FVector& A, const FVector& B, const FBox& Box, float& OutDistance) { - if (!FMath::IsFinite(A.X) || !FMath::IsFinite(A.Y) || !FMath::IsFinite(A.Z) - || !FMath::IsFinite(B.X) || !FMath::IsFinite(B.Y) || !FMath::IsFinite(B.Z) + if (!VoxelMath::IsFinite(A.X) || !VoxelMath::IsFinite(A.Y) || !VoxelMath::IsFinite(A.Z) + || !VoxelMath::IsFinite(B.X) || !VoxelMath::IsFinite(B.Y) || !VoxelMath::IsFinite(B.Z) || !Box.IsValid) { return false; @@ -3508,22 +3508,22 @@ namespace MinimumDistanceSquared = FMath::Min( MinimumDistanceSquared, DistanceSquared); } - if (!FMath::IsFinite(MinimumDistanceSquared)) + if (!VoxelMath::IsFinite(MinimumDistanceSquared)) { return false; } OutDistance = FMath::Sqrt(MinimumDistanceSquared); - return FMath::IsFinite(OutDistance); + return VoxelMath::IsFinite(OutDistance); } static bool VF_LandingLowerBoundOverBox( const FVoxelPassageLanding& Landing, const FBox& Box, float& OutLowerBound) { - if (!FMath::IsFinite(Landing.StandingPoint.X) - || !FMath::IsFinite(Landing.StandingPoint.Y) - || !FMath::IsFinite(Landing.FloorZ) - || !FMath::IsFinite(Landing.CeilingZ) - || !FMath::IsFinite(Landing.HalfWidth) + if (!VoxelMath::IsFinite(Landing.StandingPoint.X) + || !VoxelMath::IsFinite(Landing.StandingPoint.Y) + || !VoxelMath::IsFinite(Landing.FloorZ) + || !VoxelMath::IsFinite(Landing.CeilingZ) + || !VoxelMath::IsFinite(Landing.HalfWidth) || !Box.IsValid) { return false; @@ -3555,7 +3555,7 @@ namespace FMath::Max(HalfExtent.Y, HalfExtent.Z)) - RoomRounding; OutLowerBound = FMath::Max( RoundedBoxLower, Landing.FloorZ - (float)Box.Max.Z); - return FMath::IsFinite(OutLowerBound); + return VoxelMath::IsFinite(OutLowerBound); } static bool VF_PassageModifierLowerBoundOverBox( @@ -3573,11 +3573,11 @@ namespace const FVector BoxMax((float)Box.Max.X, (float)Box.Max.Y, (float)Box.Max.Z); for (const FVoxelPassage& Passage : Passages) { - if (!FMath::IsFinite(Passage.BoundCenter.X) - || !FMath::IsFinite(Passage.BoundCenter.Y) - || !FMath::IsFinite(Passage.BoundCenter.Z) - || !FMath::IsFinite(Passage.BoundRadius) - || !FMath::IsFinite(Passage.BoundRadiusSq) + 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 false; @@ -3600,7 +3600,7 @@ namespace return false; } const bool bTaper = Passage.ControlRadii.Num() == Passage.ControlPoints.Num(); - if (!bTaper && !FMath::IsFinite(Passage.Radius)) + if (!bTaper && !VoxelMath::IsFinite(Passage.Radius)) { return false; } @@ -3608,9 +3608,9 @@ namespace PointIndex < Passage.ControlPoints.Num(); ++PointIndex) { const FVector& Point = Passage.ControlPoints[PointIndex]; - if (!FMath::IsFinite(Point.X) || !FMath::IsFinite(Point.Y) - || !FMath::IsFinite(Point.Z) - || (bTaper && !FMath::IsFinite(Passage.ControlRadii[PointIndex]))) + if (!VoxelMath::IsFinite(Point.X) || !VoxelMath::IsFinite(Point.Y) + || !VoxelMath::IsFinite(Point.Z) + || (bTaper && !VoxelMath::IsFinite(Passage.ControlRadii[PointIndex]))) { return false; } @@ -3624,7 +3624,7 @@ namespace ? Passage.ControlRadii[SegmentIndex + 1] : Passage.Radius; const float Radius = FMath::Max(FMath::Abs(RadiusA), FMath::Abs(RadiusB)); float SegmentDistance = 0.0f; - if (!FMath::IsFinite(Radius) + if (!VoxelMath::IsFinite(Radius) || !VF_SegmentDistanceToBox( Passage.ControlPoints[SegmentIndex], Passage.ControlPoints[SegmentIndex + 1], Box, SegmentDistance)) @@ -3637,13 +3637,13 @@ namespace } else { - if (!FMath::IsFinite(Passage.UpperPoint.X) - || !FMath::IsFinite(Passage.UpperPoint.Y) - || !FMath::IsFinite(Passage.UpperPoint.Z) - || !FMath::IsFinite(Passage.LowerPoint.X) - || !FMath::IsFinite(Passage.LowerPoint.Y) - || !FMath::IsFinite(Passage.LowerPoint.Z) - || !FMath::IsFinite(Passage.Radius)) + 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 false; } @@ -3673,7 +3673,7 @@ namespace ModifierLowerBound, PassageLowerBound, BlendK); } OutLowerBound = ModifierLowerBound; - return FMath::IsFinite(OutLowerBound); + return VoxelMath::IsFinite(OutLowerBound); } // MaxPassageCarveFactorNearLattice is queried once for the root and once for every refined @@ -3819,7 +3819,7 @@ bool UVoxelStrateManager::AnyPassageNearLattice( VoxelBox, LatticeOrigin, Step); // Invalid input deliberately returns 1.0 from the exact helper. A non-finite result is also // retained as a candidate rather than becoming an identity proof. - return !FMath::IsFinite(MaxFactor) || MaxFactor > 0.0f; + return !VoxelMath::IsFinite(MaxFactor) || MaxFactor > 0.0f; } bool UVoxelStrateManager::AnyPassageAirPostNearLattice( @@ -3835,8 +3835,8 @@ bool UVoxelStrateManager::AnyPassageAirPostNearLattice( { return true; } - if (!FMath::IsFinite(BaseDensity) || !(BaseDensity > 0.0f) - || !FMath::IsFinite(SealThickness) || SealThickness < 0.0f) + if (!VoxelMath::IsFinite(BaseDensity) || !(BaseDensity > 0.0f) + || !VoxelMath::IsFinite(SealThickness) || SealThickness < 0.0f) { return true; } @@ -3887,9 +3887,9 @@ bool UVoxelStrateManager::AnyPassageAirPostNearLattice( PointIndex < Passage.ControlPoints.Num(); ++PointIndex) { const FVector& Point = Passage.ControlPoints[PointIndex]; - if (!FMath::IsFinite(Point.X) || !FMath::IsFinite(Point.Y) - || !FMath::IsFinite(Point.Z) - || !FMath::IsFinite(Passage.ControlRadii[PointIndex])) + if (!VoxelMath::IsFinite(Point.X) || !VoxelMath::IsFinite(Point.Y) + || !VoxelMath::IsFinite(Point.Z) + || !VoxelMath::IsFinite(Passage.ControlRadii[PointIndex])) { return true; } @@ -3952,9 +3952,9 @@ bool UVoxelStrateManager::AnyPassageAirPostNearLattice( float MinX, float MaxX, float MinY, float MaxY, float MinZ, float MaxZ, bool& bTouches) -> bool { - if (!FMath::IsFinite(MinX) || !FMath::IsFinite(MaxX) - || !FMath::IsFinite(MinY) || !FMath::IsFinite(MaxY) - || !FMath::IsFinite(MinZ) || !FMath::IsFinite(MaxZ) + if (!VoxelMath::IsFinite(MinX) || !VoxelMath::IsFinite(MaxX) + || !VoxelMath::IsFinite(MinY) || !VoxelMath::IsFinite(MaxY) + || !VoxelMath::IsFinite(MinZ) || !VoxelMath::IsFinite(MaxZ) || MinX > MaxX || MinY > MaxY || MinZ > MaxZ) { return false; @@ -3980,11 +3980,11 @@ bool UVoxelStrateManager::AnyPassageAirPostNearLattice( for (int32 PassageIndex = 0; PassageIndex < Passages.Num(); ++PassageIndex) { const FVoxelPassage& Passage = Passages[PassageIndex]; - if (!FMath::IsFinite(Passage.BoundCenter.X) - || !FMath::IsFinite(Passage.BoundCenter.Y) - || !FMath::IsFinite(Passage.BoundCenter.Z) - || !FMath::IsFinite(Passage.BoundRadius) - || !FMath::IsFinite(Passage.BoundRadiusSq) + 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; @@ -4002,11 +4002,11 @@ bool UVoxelStrateManager::AnyPassageAirPostNearLattice( // not part of this validity check: the landing-air writer reads the SDF fields even // when a separate floor descriptor is degenerate. const bool bLandingSdfValid = - FMath::IsFinite(Landing->StandingPoint.X) - && FMath::IsFinite(Landing->StandingPoint.Y) - && FMath::IsFinite(Landing->FloorZ) - && FMath::IsFinite(Landing->CeilingZ) - && FMath::IsFinite(Landing->HalfWidth) + VoxelMath::IsFinite(Landing->StandingPoint.X) + && VoxelMath::IsFinite(Landing->StandingPoint.Y) + && VoxelMath::IsFinite(Landing->FloorZ) + && VoxelMath::IsFinite(Landing->CeilingZ) + && VoxelMath::IsFinite(Landing->HalfWidth) && Landing->HalfWidth > 0.0f && Landing->CeilingZ > Landing->FloorZ; if (bLandingSdfValid) @@ -4065,7 +4065,7 @@ bool UVoxelStrateManager::AnyPassageAirPostNearLattice( } const float AirTarget = -(BaseDensity * 2.0f + SealThickness + 4.0f); - if (!FMath::IsFinite(AirTarget)) + if (!VoxelMath::IsFinite(AirTarget)) { return true; } @@ -4111,7 +4111,7 @@ bool UVoxelStrateManager::AnyPassageAirPostNearLattice( Position, Passage.UpperLanding); const float LowerSDF = VF_EvaluatePassageLandingSDF( Position, Passage.LowerLanding); - if (!FMath::IsFinite(UpperSDF) || !FMath::IsFinite(LowerSDF)) + if (!VoxelMath::IsFinite(UpperSDF) || !VoxelMath::IsFinite(LowerSDF)) { // A malformed landing is an unknown post, never an identity proof. return true; @@ -4134,7 +4134,7 @@ bool UVoxelStrateManager::AnyPassageAirPostNearLattice( CarveFactor = SmoothStep01(CarveFactor); const float LandingThreshold = FMath::Lerp( BaseDensity, AirTarget, CarveFactor); - if (!FMath::IsFinite(LandingThreshold) + if (!VoxelMath::IsFinite(LandingThreshold) || LandingThreshold <= 0.0f) { // Zero is not solid under the mesher's sign convention, so it is part of @@ -4219,7 +4219,7 @@ float UVoxelStrateManager::MaxPassageCarveFactorNearLattice( * (SmoothMinK / 6.0f); const float Padding = PassageCarveThreshold + SmoothDip; ModifierDomainPadding = Padding; - if (!FMath::IsFinite(Padding) || Padding < 0.0f) + if (!VoxelMath::IsFinite(Padding) || Padding < 0.0f) { bKnownModifierDomains = false; } @@ -4323,7 +4323,7 @@ float UVoxelStrateManager::MaxPassageCarveFactorNearLattice( return; } const float ModifierSDF = EvaluateModifierSDF(X, Y, Z); - const float CarveFactor = FMath::IsFinite(ModifierSDF) + const float CarveFactor = VoxelMath::IsFinite(ModifierSDF) ? CarveFactorFromSDF(ModifierSDF) : 1.0f; Cache.Factors[Cache.Index(IX, IY, IZ)] = CarveFactor; MaxCarveFactor = FMath::Max(MaxCarveFactor, CarveFactor); @@ -4421,9 +4421,9 @@ float UVoxelStrateManager::MaxPassageCarveFactorNearLattice( CandidateMaxZ = FMath::Max(CandidateMaxZ, Domain.Max.Z + ModifierDomainPadding); } - if (!FMath::IsFinite(CandidateMinX) || !FMath::IsFinite(CandidateMinY) - || !FMath::IsFinite(CandidateMinZ) || !FMath::IsFinite(CandidateMaxX) - || !FMath::IsFinite(CandidateMaxY) || !FMath::IsFinite(CandidateMaxZ)) + if (!VoxelMath::IsFinite(CandidateMinX) || !VoxelMath::IsFinite(CandidateMinY) + || !VoxelMath::IsFinite(CandidateMinZ) || !VoxelMath::IsFinite(CandidateMaxX) + || !VoxelMath::IsFinite(CandidateMaxY) || !VoxelMath::IsFinite(CandidateMaxZ)) { bKnownModifierDomains = false; } @@ -4486,7 +4486,7 @@ float UVoxelStrateManager::MaxPassageCarveFactorNearLattice( if (MayEvaluatePoint(Position)) { const float ModifierSDF = EvaluateModifierSDF(X, Y, Z); - CarveFactor = FMath::IsFinite(ModifierSDF) + CarveFactor = VoxelMath::IsFinite(ModifierSDF) ? CarveFactorFromSDF(ModifierSDF) : 1.0f; } Cache.Factors[Cache.Index(IX, IY, IZ)] = CarveFactor; @@ -4517,7 +4517,7 @@ float UVoxelStrateManager::MaxPassageCarveFactorNearLattice( { const float RemainingSDF = EvaluateModifierSDF( RemainingWorldX, RemainingWorldY, RemainingWorldZ); - RemainingFactor = FMath::IsFinite(RemainingSDF) + RemainingFactor = VoxelMath::IsFinite(RemainingSDF) ? CarveFactorFromSDF(RemainingSDF) : 1.0f; } Cache.Factors[Cache.Index(RemainingX, RemainingY, RemainingZ)] = @@ -4552,7 +4552,7 @@ float UVoxelStrateManager::MaxPassageCarveFactorNearLattice( continue; } const float ModifierSDF = EvaluateModifierSDF(X, Y, Z); - if (!FMath::IsFinite(ModifierSDF)) + if (!VoxelMath::IsFinite(ModifierSDF)) { return 1.0f; } @@ -4581,11 +4581,11 @@ bool UVoxelStrateManager::AnyPassageLandingFloorNearLattice( &Passage.UpperLanding, &Passage.LowerLanding }; for (const FVoxelPassageLanding* Landing : Landings) { - if (!FMath::IsFinite(Landing->StandingPoint.X) - || !FMath::IsFinite(Landing->StandingPoint.Y) - || !FMath::IsFinite(Landing->FloorZ) - || !FMath::IsFinite(Landing->HalfWidth) - || !FMath::IsFinite(Landing->FloorThickness)) + if (!VoxelMath::IsFinite(Landing->StandingPoint.X) + || !VoxelMath::IsFinite(Landing->StandingPoint.Y) + || !VoxelMath::IsFinite(Landing->FloorZ) + || !VoxelMath::IsFinite(Landing->HalfWidth) + || !VoxelMath::IsFinite(Landing->FloorThickness)) { return true; } @@ -4620,11 +4620,11 @@ bool UVoxelStrateManager::AnyPassageLandingFloorNearLattice( { const FVector& A = Passage.ControlPoints[SegmentIndex]; const FVector& B = Passage.ControlPoints[SegmentIndex + 1]; - if (!FMath::IsFinite(A.X) || !FMath::IsFinite(A.Y) - || !FMath::IsFinite(A.Z) || !FMath::IsFinite(B.X) - || !FMath::IsFinite(B.Y) || !FMath::IsFinite(B.Z) - || !FMath::IsFinite(Passage.ControlRadii[SegmentIndex]) - || !FMath::IsFinite(Passage.ControlRadii[SegmentIndex + 1])) + if (!VoxelMath::IsFinite(A.X) || !VoxelMath::IsFinite(A.Y) + || !VoxelMath::IsFinite(A.Z) || !VoxelMath::IsFinite(B.X) + || !VoxelMath::IsFinite(B.Y) || !VoxelMath::IsFinite(B.Z) + || !VoxelMath::IsFinite(Passage.ControlRadii[SegmentIndex]) + || !VoxelMath::IsFinite(Passage.ControlRadii[SegmentIndex + 1])) { return true; } @@ -4696,11 +4696,11 @@ bool UVoxelStrateManager::AnyLandingFloorAtLattice( auto HasAnyLandingSample = [&](const FVoxelPassageLanding& Landing) -> bool { - if (!FMath::IsFinite(Landing.StandingPoint.X) - || !FMath::IsFinite(Landing.StandingPoint.Y) - || !FMath::IsFinite(Landing.FloorZ) - || !FMath::IsFinite(Landing.HalfWidth) - || !FMath::IsFinite(Landing.FloorThickness) + if (!VoxelMath::IsFinite(Landing.StandingPoint.X) + || !VoxelMath::IsFinite(Landing.StandingPoint.Y) + || !VoxelMath::IsFinite(Landing.FloorZ) + || !VoxelMath::IsFinite(Landing.HalfWidth) + || !VoxelMath::IsFinite(Landing.FloorThickness) || Landing.HalfWidth <= 0.0f || Landing.FloorThickness <= 0.0f) { // This function gates an AllAir proof. Invalid authored floor data is an unknown @@ -4755,11 +4755,11 @@ bool UVoxelStrateManager::AnyLandingFloorAtLattice( { const FVector& A = Passage.ControlPoints[SegmentIndex]; const FVector& B = Passage.ControlPoints[SegmentIndex + 1]; - if (!FMath::IsFinite(A.X) || !FMath::IsFinite(A.Y) - || !FMath::IsFinite(A.Z) || !FMath::IsFinite(B.X) - || !FMath::IsFinite(B.Y) || !FMath::IsFinite(B.Z) - || !FMath::IsFinite(Passage.ControlRadii[SegmentIndex]) - || !FMath::IsFinite(Passage.ControlRadii[SegmentIndex + 1])) + if (!VoxelMath::IsFinite(A.X) || !VoxelMath::IsFinite(A.Y) + || !VoxelMath::IsFinite(A.Z) || !VoxelMath::IsFinite(B.X) + || !VoxelMath::IsFinite(B.Y) || !VoxelMath::IsFinite(B.Z) + || !VoxelMath::IsFinite(Passage.ControlRadii[SegmentIndex]) + || !VoxelMath::IsFinite(Passage.ControlRadii[SegmentIndex + 1])) { return true; } @@ -4779,9 +4779,9 @@ bool UVoxelStrateManager::AnyLandingFloorAtLattice( const float MinZ = FMath::Min(FloorA, FloorB) - VoxelPassageGeometry::LandingFloorThicknessVoxels - Pad; const float MaxZ = FMath::Max(FloorA, FloorB) + Pad; - if (!FMath::IsFinite(MinX) || !FMath::IsFinite(MaxX) - || !FMath::IsFinite(MinY) || !FMath::IsFinite(MaxY) - || !FMath::IsFinite(MinZ) || !FMath::IsFinite(MaxZ)) + if (!VoxelMath::IsFinite(MinX) || !VoxelMath::IsFinite(MaxX) + || !VoxelMath::IsFinite(MinY) || !VoxelMath::IsFinite(MaxY) + || !VoxelMath::IsFinite(MinZ) || !VoxelMath::IsFinite(MaxZ)) { return true; } @@ -4926,13 +4926,13 @@ bool UVoxelStrateManager::AnyOriginLandingAirNearLattice( { return true; } - if (!FMath::IsFinite(BaseDensity) || !(BaseDensity > 0.0f) - || !FMath::IsFinite(SealThickness) || SealThickness < 0.0f) + if (!VoxelMath::IsFinite(BaseDensity) || !(BaseDensity > 0.0f) + || !VoxelMath::IsFinite(SealThickness) || SealThickness < 0.0f) { return true; } const float AirTarget = -(BaseDensity * 2.0f + SealThickness + 4.0f); - if (!FMath::IsFinite(AirTarget)) + if (!VoxelMath::IsFinite(AirTarget)) { return true; } @@ -4989,7 +4989,7 @@ bool UVoxelStrateManager::AnyOriginLandingAirNearLattice( (float)LatticeOrigin.Z + (float)(IZ * Step)); const float RoomSDF = VoxelPassageGeometry::OriginLandingRoomSDF( Position, Geometry); - if (!FMath::IsFinite(RoomSDF)) + if (!VoxelMath::IsFinite(RoomSDF)) { return true; } @@ -5002,7 +5002,7 @@ bool UVoxelStrateManager::AnyOriginLandingAirNearLattice( CarveFactor = SmoothStep01(CarveFactor); const float LandingThreshold = FMath::Lerp( BaseDensity, AirTarget, CarveFactor); - if (!FMath::IsFinite(LandingThreshold) + if (!VoxelMath::IsFinite(LandingThreshold) || LandingThreshold <= 0.0f) { return true; diff --git a/Source/VoxelForge/Private/VoxelWorld.cpp b/Source/VoxelForge/Private/VoxelWorld.cpp index 01a870f..420bb9a 100644 --- a/Source/VoxelForge/Private/VoxelWorld.cpp +++ b/Source/VoxelForge/Private/VoxelWorld.cpp @@ -436,7 +436,7 @@ namespace ++OutMismatches; } - const float Delta = (FMath::IsFinite(Live) && FMath::IsFinite(Reference)) + const float Delta = (VoxelMath::IsFinite(Live) && VoxelMath::IsFinite(Reference)) ? FMath::Abs(Live - Reference) : FLT_MAX; OutMaxDelta = FMath::Max(OutMaxDelta, Delta); ++OutSamples; @@ -1720,8 +1720,8 @@ bool AVoxelWorld::TryGetPlayerPosition(FVector& OutPosition, APawn** OutPawn) co } const FVector Position = Pawn->GetActorLocation(); - if (!FMath::IsFinite(Position.X) || !FMath::IsFinite(Position.Y) - || !FMath::IsFinite(Position.Z)) + if (!VoxelMath::IsFinite(Position.X) || !VoxelMath::IsFinite(Position.Y) + || !VoxelMath::IsFinite(Position.Z)) { return false; } @@ -1766,7 +1766,7 @@ bool AVoxelWorld::GetPlayerSupportTile(APawn* Pawn, const FVector& PlayerPositio Pawn->GetActorBounds(/*bOnlyCollidingComponents*/ true, BoundsOrigin, BoundsExtent); HalfHeightCm = FMath::Max(0.0f, BoundsExtent.Z); } - if (!FMath::IsFinite(HalfHeightCm)) + if (!VoxelMath::IsFinite(HalfHeightCm)) { return false; } @@ -4060,7 +4060,7 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector OriginVoxels.Y + Y * ValidationStep, OriginVoxels.Z + Z * ValidationStep); ++Result.ValidationDensityCalls; - const bool bSampleAgrees = FMath::IsFinite(Density) && Density != 0.0f + const bool bSampleAgrees = VoxelMath::IsFinite(Density) && Density != 0.0f && (Verdict != EVoxelTileClass::AllSolid || Density < 0.0f) && (Verdict != EVoxelTileClass::AllAir || Density > 0.0f); if (!bSampleAgrees) diff --git a/Source/VoxelForge/Public/VoxelDensityOp.h b/Source/VoxelForge/Public/VoxelDensityOp.h index 72e9b85..02a5f18 100644 --- a/Source/VoxelForge/Public/VoxelDensityOp.h +++ b/Source/VoxelForge/Public/VoxelDensityOp.h @@ -415,7 +415,7 @@ struct FVoxelBoxSdfInterval void Set(float InMin, float InMax) { - if (!FMath::IsFinite(InMin) || !FMath::IsFinite(InMax) || InMin > InMax) + if (!VoxelMath::IsFinite(InMin) || !VoxelMath::IsFinite(InMax) || InMin > InMax) { SetUnknown(); return; diff --git a/Source/VoxelForge/Public/VoxelDensityPrimitives.h b/Source/VoxelForge/Public/VoxelDensityPrimitives.h index 9495a3c..b27c7f6 100644 --- a/Source/VoxelForge/Public/VoxelDensityPrimitives.h +++ b/Source/VoxelForge/Public/VoxelDensityPrimitives.h @@ -222,9 +222,9 @@ FORCEINLINE void VF_ApplyXYEdgeSeal(float& Density, float WorldX, float WorldY, float WorldRadiusVoxels, float Thickness, float BaseDensity) { if (!(WorldRadiusVoxels > 0.0f) || !(Thickness > 0.0f) || !(BaseDensity > 0.0f)) return; - if (!FMath::IsFinite(WorldX) || !FMath::IsFinite(WorldY) - || !FMath::IsFinite(WorldRadiusVoxels) || !FMath::IsFinite(Thickness) - || !FMath::IsFinite(BaseDensity)) return; + if (!VoxelMath::IsFinite(WorldX) || !VoxelMath::IsFinite(WorldY) + || !VoxelMath::IsFinite(WorldRadiusVoxels) || !VoxelMath::IsFinite(Thickness) + || !VoxelMath::IsFinite(BaseDensity)) return; const float InnerRadius = WorldRadiusVoxels - Thickness; const float InnerRadiusSq = InnerRadius * InnerRadius; @@ -282,10 +282,10 @@ FORCEINLINE bool VF_IsValidXYEdgeSealProofInput(const FBox& VoxelBox, float WorldRadiusVoxels, float Thickness, float BaseDensity) { return VoxelBox.IsValid - && FMath::IsFinite(VoxelBox.Min.X) && FMath::IsFinite(VoxelBox.Min.Y) - && FMath::IsFinite(VoxelBox.Max.X) && FMath::IsFinite(VoxelBox.Max.Y) - && FMath::IsFinite(WorldRadiusVoxels) && FMath::IsFinite(Thickness) - && FMath::IsFinite(BaseDensity) + && VoxelMath::IsFinite(VoxelBox.Min.X) && VoxelMath::IsFinite(VoxelBox.Min.Y) + && VoxelMath::IsFinite(VoxelBox.Max.X) && VoxelMath::IsFinite(VoxelBox.Max.Y) + && VoxelMath::IsFinite(WorldRadiusVoxels) && VoxelMath::IsFinite(Thickness) + && VoxelMath::IsFinite(BaseDensity) && WorldRadiusVoxels > 0.0f && Thickness > 0.0f && BaseDensity > 0.0f; } @@ -344,7 +344,7 @@ FORCEINLINE bool VF_XYEdgeSealBoxTouchesBand(const FBox& VoxelBox, float WorldRadiusVoxels, float Thickness) { if (!(WorldRadiusVoxels > 0.0f) || !(Thickness > 0.0f) - || !FMath::IsFinite(WorldRadiusVoxels) || !FMath::IsFinite(Thickness)) return false; + || !VoxelMath::IsFinite(WorldRadiusVoxels) || !VoxelMath::IsFinite(Thickness)) return false; const float InnerRadius = WorldRadiusVoxels - Thickness; return VF_XYEdgeFarthestRadiusSq(VoxelBox) > InnerRadius * InnerRadius; diff --git a/Source/VoxelForge/Public/VoxelPassageGeometry.h b/Source/VoxelForge/Public/VoxelPassageGeometry.h index b8460c1..2ca9f5d 100644 --- a/Source/VoxelForge/Public/VoxelPassageGeometry.h +++ b/Source/VoxelForge/Public/VoxelPassageGeometry.h @@ -7,6 +7,7 @@ #pragma once #include "CoreMinimal.h" +#include "VoxelTypes.h" namespace VoxelPassageGeometry { @@ -85,13 +86,13 @@ namespace VoxelPassageGeometry + FloorThickness + SealSafetyMarginVoxels; const float MaximumFloor = InteriorTopZ - PlayerHeightVoxels - SealSafetyMarginVoxels; - if (!FMath::IsFinite(StrateBottomZ) - || !FMath::IsFinite(InteriorBottomZ) - || !FMath::IsFinite(InteriorTopZ) - || !FMath::IsFinite(LedgeSpacing) - || !FMath::IsFinite(LedgeDepth) - || !FMath::IsFinite(MinimumFloor) - || !FMath::IsFinite(MaximumFloor) + if (!VoxelMath::IsFinite(StrateBottomZ) + || !VoxelMath::IsFinite(InteriorBottomZ) + || !VoxelMath::IsFinite(InteriorTopZ) + || !VoxelMath::IsFinite(LedgeSpacing) + || !VoxelMath::IsFinite(LedgeDepth) + || !VoxelMath::IsFinite(MinimumFloor) + || !VoxelMath::IsFinite(MaximumFloor) || !(MaximumFloor > MinimumFloor)) { return 0.5f * (InteriorBottomZ + InteriorTopZ); @@ -153,12 +154,12 @@ namespace VoxelPassageGeometry const float FloorRadius = FMath::Max(0.5f, SafeRadius - 0.5f); if (!(SafeRadius > 0.0f) || !(BaseDensity > 0.0f) - || !FMath::IsFinite(WorldX) - || !FMath::IsFinite(WorldY) - || !FMath::IsFinite(WorldZ) - || !FMath::IsFinite(FloorZ) - || !FMath::IsFinite(Start.X) || !FMath::IsFinite(Start.Y) - || !FMath::IsFinite(End.X) || !FMath::IsFinite(End.Y)) + || !VoxelMath::IsFinite(WorldX) + || !VoxelMath::IsFinite(WorldY) + || !VoxelMath::IsFinite(WorldZ) + || !VoxelMath::IsFinite(FloorZ) + || !VoxelMath::IsFinite(Start.X) || !VoxelMath::IsFinite(Start.Y) + || !VoxelMath::IsFinite(End.X) || !VoxelMath::IsFinite(End.Y)) { return; } @@ -218,14 +219,14 @@ namespace VoxelPassageGeometry const float CoreRadius = FMath::Max(0.5f, SafeRadius - 1.0f); if (!(SafeRadius > 0.0f) || !(BaseDensity > 0.0f) - || !FMath::IsFinite(WorldX) - || !FMath::IsFinite(WorldY) - || !FMath::IsFinite(WorldZ) - || !FMath::IsFinite(FloorZ) - || !FMath::IsFinite(Start.X) || !FMath::IsFinite(Start.Y) - || !FMath::IsFinite(Start.Z) - || !FMath::IsFinite(End.X) || !FMath::IsFinite(End.Y) - || !FMath::IsFinite(End.Z)) + || !VoxelMath::IsFinite(WorldX) + || !VoxelMath::IsFinite(WorldY) + || !VoxelMath::IsFinite(WorldZ) + || !VoxelMath::IsFinite(FloorZ) + || !VoxelMath::IsFinite(Start.X) || !VoxelMath::IsFinite(Start.Y) + || !VoxelMath::IsFinite(Start.Z) + || !VoxelMath::IsFinite(End.X) || !VoxelMath::IsFinite(End.Y) + || !VoxelMath::IsFinite(End.Z)) { return; } @@ -360,8 +361,8 @@ namespace VoxelPassageGeometry } return bFoundSegment - && FMath::IsFinite(OutFloorZ) - && FMath::IsFinite(OutSupportRadius) + && VoxelMath::IsFinite(OutFloorZ) + && VoxelMath::IsFinite(OutSupportRadius) && OutSupportRadius > 0.0f && BestDistanceSquared <= FMath::Square(OutSupportRadius); } @@ -405,10 +406,10 @@ namespace VoxelPassageGeometry { FOriginLandingGeometry Result; if (!(OriginRadius > 0.0f) - || !FMath::IsFinite(StrateTopZ) - || !FMath::IsFinite(StrateBottomZ) - || !FMath::IsFinite(SealThickness) - || !FMath::IsFinite(OriginRadius)) + || !VoxelMath::IsFinite(StrateTopZ) + || !VoxelMath::IsFinite(StrateBottomZ) + || !VoxelMath::IsFinite(SealThickness) + || !VoxelMath::IsFinite(OriginRadius)) { return Result; } @@ -429,10 +430,10 @@ namespace VoxelPassageGeometry + Result.FloorThickness + SealSafetyMarginVoxels; const float MaximumFloor = InnerTop - DesiredHeight - SealSafetyMarginVoxels; - if (!FMath::IsFinite(InnerBottom) || !FMath::IsFinite(InnerTop) + if (!VoxelMath::IsFinite(InnerBottom) || !VoxelMath::IsFinite(InnerTop) || !(InnerTop > InnerBottom) - || !FMath::IsFinite(MinimumFloor) - || !FMath::IsFinite(MaximumFloor) + || !VoxelMath::IsFinite(MinimumFloor) + || !VoxelMath::IsFinite(MaximumFloor) || MaximumFloor < MinimumFloor) { Result.HalfWidth = 0.0f; @@ -455,8 +456,8 @@ namespace VoxelPassageGeometry FORCEINLINE bool LatticeAxisHasSampleInInterval( float Min, float Max, float Origin, int32 Step) { - if (Step <= 0 || !FMath::IsFinite(Min) || !FMath::IsFinite(Max) - || !FMath::IsFinite(Origin) || Min > Max) + if (Step <= 0 || !VoxelMath::IsFinite(Min) || !VoxelMath::IsFinite(Max) + || !VoxelMath::IsFinite(Origin) || Min > Max) { return false; } diff --git a/Source/VoxelForge/Public/VoxelTypes.h b/Source/VoxelForge/Public/VoxelTypes.h index b910565..98f7ebe 100644 --- a/Source/VoxelForge/Public/VoxelTypes.h +++ b/Source/VoxelForge/Public/VoxelTypes.h @@ -8,6 +8,51 @@ #include "CoreMinimal.h" +//============================================================================= +// EXACT FINITE PREDICATE / PREDICAT FINI EXACT +//============================================================================= +// The CRT-backed FMath::IsFinite is an out-of-line call on the MSVC game path. A finite +// IEEE-754 value is identified entirely by its exponent bits, so this helper has the same +// boolean result for every bit pattern without doing a floating-point comparison. +namespace VoxelMath +{ + // Runtime policy switch. The fast helper below is deliberately independent of this value so + // the equivalence test can compare it directly with FMath::IsFinite. IsFinite() is the A/B + // entry point used by generation code. + VOXELFORGE_API extern int32 GFastIsFinite; + + FORCEINLINE bool IsFiniteFast(float Value) + { + static_assert(sizeof(float) == sizeof(uint32), "VoxelMath::IsFiniteFast requires IEEE float"); + uint32 Bits = 0; + FMemory::Memcpy(&Bits, &Value, sizeof(Bits)); + constexpr uint32 ExponentMask = 0x7F800000u; + return (Bits & ExponentMask) != ExponentMask; + } + + FORCEINLINE bool IsFiniteFast(double Value) + { + static_assert(sizeof(double) == sizeof(uint64), "VoxelMath::IsFiniteFast requires IEEE double"); + uint64 Bits = 0; + FMemory::Memcpy(&Bits, &Value, sizeof(Bits)); + constexpr uint64 ExponentMask = 0x7FF0000000000000ull; + return (Bits & ExponentMask) != ExponentMask; + } + + // Keep the switch at the call site while keeping the exact bit test separate and directly + // testable. In the default-on game path this inlines to the bit load/mask/branch; the 0 path + // is retained for within-round performance and sampler A/B runs. + FORCEINLINE bool IsFinite(float Value) + { + return GFastIsFinite != 0 ? IsFiniteFast(Value) : FMath::IsFinite(Value); + } + + FORCEINLINE bool IsFinite(double Value) + { + return GFastIsFinite != 0 ? IsFiniteFast(Value) : FMath::IsFinite(Value); + } +} + //============================================================================= // CHUNK CONSTANTS //=============================================================================