fix(passages): skip proofs cover the new descent geometry -- soundness PASS; passage tiles +28% cost
- Passage proof bounds now enclose everything the passage code can write: separated switchback
legs, outward door approaches, native floor slabs with a derived relief supremum, and the final
body-envelope carve (segment XY extent + half-width, floor range + player height + step).
- ClassifyTile: the cave-only AllSolid shortcut could restore a solid verdict after a passage
carve candidate was found; it now keeps the tile mixed.
- Descent corridor test asserts no corridor stencil tile gets a uniform verdict.
Evidence: SealedSolidProofSoundness and ClassifyTileSoundness pass; 64 owner seeds, 0 body
blockers, 0 uniform stencil tiles, CRCs match. Negative control (passage reach shrunk to 4 voxels):
7 sealed-solid proofs among 620 corridor tiles, i.e. the check bites.
KNOWN COST, not fixed: tile profile, owner world, seed 0, 15 s window: passage-candidate tiles
83.6 ms (de05e29) -> 107.2 ms (+28%), all tiles +5.4%. Two segment-rejection filters did not help
and were removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
fb052716c6
commit
b4ac9969f5
3 files changed
+444
-43
No files matched your search
@@ -99,6 +99,11 @@ namespace
|
|||||||
TArray<int32> SampleStations;
|
TArray<int32> SampleStations;
|
||||||
TArray<int32> SampleLaterals;
|
TArray<int32> SampleLaterals;
|
||||||
TArray<int32> SampleHeights;
|
TArray<int32> SampleHeights;
|
||||||
|
TSet<FIntVector> ProofStencilTileOrigins;
|
||||||
|
int32 ProofStencilUniformTiles = 0;
|
||||||
|
int32 ProofStencilSealedTiles = 0;
|
||||||
|
int32 ProofStencilOutOfLayoutAirTiles = 0;
|
||||||
|
int32 ProofStencilClassifyTiles = 0;
|
||||||
int32 SolidSamples = 0;
|
int32 SolidSamples = 0;
|
||||||
int32 BlockingSolidSamples = 0;
|
int32 BlockingSolidSamples = 0;
|
||||||
int32 SolidStations = 0;
|
int32 SolidStations = 0;
|
||||||
@@ -214,6 +219,41 @@ namespace
|
|||||||
(2.0f * FloorProbeHalfRange) / static_cast<float>(FloorProbeIntervals);
|
(2.0f * FloorProbeHalfRange) / static_cast<float>(FloorProbeIntervals);
|
||||||
constexpr float FirstBodySampleZ = 0.25f;
|
constexpr float FirstBodySampleZ = 0.25f;
|
||||||
constexpr float BodySampleSpacing = 0.5f;
|
constexpr float BodySampleSpacing = 0.5f;
|
||||||
|
OutSamples.ProofStencilTileOrigins.Reset();
|
||||||
|
OutSamples.ProofStencilUniformTiles = 0;
|
||||||
|
OutSamples.ProofStencilSealedTiles = 0;
|
||||||
|
OutSamples.ProofStencilOutOfLayoutAirTiles = 0;
|
||||||
|
OutSamples.ProofStencilClassifyTiles = 0;
|
||||||
|
const auto AddStencilTilesAtPosition = [&OutSamples](const FVector& Position)
|
||||||
|
{
|
||||||
|
int32 AxisStarts[3][2] = {};
|
||||||
|
int32 AxisCounts[3] = {};
|
||||||
|
const double Coordinates[3] = { Position.X, Position.Y, Position.Z };
|
||||||
|
for (int32 Axis = 0; Axis < 3; ++Axis)
|
||||||
|
{
|
||||||
|
const int32 TileIndex = FMath::FloorToInt(
|
||||||
|
Coordinates[Axis] / static_cast<double>(CHUNK_SIZE));
|
||||||
|
AxisStarts[Axis][0] = TileIndex * CHUNK_SIZE;
|
||||||
|
AxisCounts[Axis] = 1;
|
||||||
|
// MC tiles share their high-face samples. Include both tiles when a stencil
|
||||||
|
// position lies exactly on a 32-voxel boundary.
|
||||||
|
if (Coordinates[Axis]
|
||||||
|
== static_cast<double>(AxisStarts[Axis][0]))
|
||||||
|
{
|
||||||
|
AxisStarts[Axis][1] = AxisStarts[Axis][0] - CHUNK_SIZE;
|
||||||
|
AxisCounts[Axis] = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int32 XIndex = 0; XIndex < AxisCounts[0]; ++XIndex)
|
||||||
|
for (int32 YIndex = 0; YIndex < AxisCounts[1]; ++YIndex)
|
||||||
|
for (int32 ZIndex = 0; ZIndex < AxisCounts[2]; ++ZIndex)
|
||||||
|
{
|
||||||
|
OutSamples.ProofStencilTileOrigins.Add(FIntVector(
|
||||||
|
AxisStarts[0][XIndex],
|
||||||
|
AxisStarts[1][YIndex],
|
||||||
|
AxisStarts[2][ZIndex]));
|
||||||
|
}
|
||||||
|
};
|
||||||
TArray<FVector> FeetStations;
|
TArray<FVector> FeetStations;
|
||||||
TArray<FVector> LateralAxes;
|
TArray<FVector> LateralAxes;
|
||||||
TArray<float> ExpectedFloorZ;
|
TArray<float> ExpectedFloorZ;
|
||||||
@@ -344,6 +384,13 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
MeasuredFloorZ[FloorIndex] = BestFloor;
|
MeasuredFloorZ[FloorIndex] = BestFloor;
|
||||||
|
FVector FloorStencilPosition = FeetStations[StationIndex]
|
||||||
|
+ LateralAxes[StationIndex]
|
||||||
|
* (-PlayerHalfWidthVoxels
|
||||||
|
+ static_cast<float>(LateralIndex)
|
||||||
|
* LateralSampleSpacing);
|
||||||
|
FloorStencilPosition.Z = BestFloor;
|
||||||
|
AddStencilTilesAtPosition(FloorStencilPosition);
|
||||||
if (BestDistance != FLT_MAX)
|
if (BestDistance != FLT_MAX)
|
||||||
{
|
{
|
||||||
bHasMeasuredFloor[FloorIndex] = 1;
|
bHasMeasuredFloor[FloorIndex] = 1;
|
||||||
@@ -515,8 +562,10 @@ namespace
|
|||||||
0.0f));
|
0.0f));
|
||||||
const float BodySide = SliceRadius
|
const float BodySide = SliceRadius
|
||||||
* (static_cast<float>(LateralIndex) / 2.0f - 1.0f);
|
* (static_cast<float>(LateralIndex) / 2.0f - 1.0f);
|
||||||
OutSamples.Positions.Add(
|
const FVector BodyPosition =
|
||||||
Feet + Lateral * BodySide + FVector(0.0f, 0.0f, Height));
|
Feet + Lateral * BodySide + FVector(0.0f, 0.0f, Height);
|
||||||
|
OutSamples.Positions.Add(BodyPosition);
|
||||||
|
AddStencilTilesAtPosition(BodyPosition);
|
||||||
OutSamples.SampleStations.Add(StationIndex);
|
OutSamples.SampleStations.Add(StationIndex);
|
||||||
OutSamples.SampleLaterals.Add(LateralIndex);
|
OutSamples.SampleLaterals.Add(LateralIndex);
|
||||||
OutSamples.SampleHeights.Add(VerticalIndex);
|
OutSamples.SampleHeights.Add(VerticalIndex);
|
||||||
@@ -631,7 +680,8 @@ namespace
|
|||||||
bool SampleOwnerSeed(
|
bool SampleOwnerSeed(
|
||||||
int32 Seed,
|
int32 Seed,
|
||||||
FWalkCorridorSamples& OutSamples,
|
FWalkCorridorSamples& OutSamples,
|
||||||
FString& OutError)
|
FString& OutError,
|
||||||
|
bool bCheckProofStencil = false)
|
||||||
{
|
{
|
||||||
FOwnerPassageWorld World;
|
FOwnerPassageWorld World;
|
||||||
if (!World.Build(Seed, OutError))
|
if (!World.Build(Seed, OutError))
|
||||||
@@ -642,6 +692,24 @@ namespace
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (bCheckProofStencil)
|
||||||
|
{
|
||||||
|
for (const FIntVector& Origin : OutSamples.ProofStencilTileOrigins)
|
||||||
|
{
|
||||||
|
const bool bSealedSolid = World.Generator->TryProveSealedSolidTile(
|
||||||
|
Origin, 1, CHUNK_SIZE);
|
||||||
|
const bool bOutOfLayoutAir = World.Generator->TryProveOutOfLayoutAirTile(
|
||||||
|
Origin, 1, CHUNK_SIZE);
|
||||||
|
const EVoxelTileClass Verdict = World.Generator->ClassifyTile(
|
||||||
|
Origin, 1, CHUNK_SIZE);
|
||||||
|
const bool bUniformClassify = Verdict != EVoxelTileClass::Mixed;
|
||||||
|
OutSamples.ProofStencilSealedTiles += bSealedSolid ? 1 : 0;
|
||||||
|
OutSamples.ProofStencilOutOfLayoutAirTiles += bOutOfLayoutAir ? 1 : 0;
|
||||||
|
OutSamples.ProofStencilClassifyTiles += bUniformClassify ? 1 : 0;
|
||||||
|
OutSamples.ProofStencilUniformTiles +=
|
||||||
|
(bSealedSolid || bOutOfLayoutAir || bUniformClassify) ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (Seed == 0 && OutSamples.SolidSamples > 0)
|
if (Seed == 0 && OutSamples.SolidSamples > 0)
|
||||||
{
|
{
|
||||||
const FIntVector ChunkCoord(
|
const FIntVector ChunkCoord(
|
||||||
@@ -1093,6 +1161,11 @@ bool FVoxelForgeDescentCorridorSeedSweepTest::RunTest(const FString& Parameters)
|
|||||||
int64 TotalBlockingSolidSamples = 0;
|
int64 TotalBlockingSolidSamples = 0;
|
||||||
int64 TotalWidthFloorGaps = 0;
|
int64 TotalWidthFloorGaps = 0;
|
||||||
int64 TotalSamples = 0;
|
int64 TotalSamples = 0;
|
||||||
|
int64 TotalProofStencilTiles = 0;
|
||||||
|
int64 TotalProofStencilUniformTiles = 0;
|
||||||
|
int64 TotalProofStencilSealedTiles = 0;
|
||||||
|
int64 TotalProofStencilOutOfLayoutAirTiles = 0;
|
||||||
|
int64 TotalProofStencilClassifyTiles = 0;
|
||||||
int32 FirstFailingSeed = INDEX_NONE;
|
int32 FirstFailingSeed = INDEX_NONE;
|
||||||
FWalkCorridorSamples Worst;
|
FWalkCorridorSamples Worst;
|
||||||
FString WorstError;
|
FString WorstError;
|
||||||
@@ -1101,13 +1174,18 @@ bool FVoxelForgeDescentCorridorSeedSweepTest::RunTest(const FString& Parameters)
|
|||||||
{
|
{
|
||||||
FWalkCorridorSamples Samples;
|
FWalkCorridorSamples Samples;
|
||||||
FString Error;
|
FString Error;
|
||||||
if (!SampleOwnerSeed(Seed, Samples, Error))
|
if (!SampleOwnerSeed(Seed, Samples, Error, /*bCheckProofStencil=*/true))
|
||||||
{
|
{
|
||||||
AddError(Error);
|
AddError(Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LogCorridorResult(*this, Seed, Samples);
|
LogCorridorResult(*this, Seed, Samples);
|
||||||
TotalSamples += Samples.Densities.Num();
|
TotalSamples += Samples.Densities.Num();
|
||||||
|
TotalProofStencilTiles += Samples.ProofStencilTileOrigins.Num();
|
||||||
|
TotalProofStencilUniformTiles += Samples.ProofStencilUniformTiles;
|
||||||
|
TotalProofStencilSealedTiles += Samples.ProofStencilSealedTiles;
|
||||||
|
TotalProofStencilOutOfLayoutAirTiles += Samples.ProofStencilOutOfLayoutAirTiles;
|
||||||
|
TotalProofStencilClassifyTiles += Samples.ProofStencilClassifyTiles;
|
||||||
TotalSolidSamples += Samples.SolidSamples;
|
TotalSolidSamples += Samples.SolidSamples;
|
||||||
TotalBlockingSolidSamples += Samples.BlockingSolidSamples;
|
TotalBlockingSolidSamples += Samples.BlockingSolidSamples;
|
||||||
TotalWidthFloorGaps += Samples.WidthFloorGaps;
|
TotalWidthFloorGaps += Samples.WidthFloorGaps;
|
||||||
@@ -1169,9 +1247,19 @@ bool FVoxelForgeDescentCorridorSeedSweepTest::RunTest(const FString& Parameters)
|
|||||||
static_cast<long long>(TotalWidthFloorGaps), static_cast<long long>(TotalSamples),
|
static_cast<long long>(TotalWidthFloorGaps), static_cast<long long>(TotalSamples),
|
||||||
FirstFailingSeed, WorstError.IsEmpty() ? TEXT("none") : *WorstError,
|
FirstFailingSeed, WorstError.IsEmpty() ? TEXT("none") : *WorstError,
|
||||||
Worst.BlockingSolidSamples);
|
Worst.BlockingSolidSamples);
|
||||||
|
UE_LOG(LogTemp, Display,
|
||||||
|
TEXT("[VoxelForgeDescentProofStencilSweep] seeds=%d tiles_checked=%lld "
|
||||||
|
"uniform_tiles=%lld sealed_solid=%lld out_of_layout_air=%lld classify_uniform=%lld"),
|
||||||
|
SeedCount, static_cast<long long>(TotalProofStencilTiles),
|
||||||
|
static_cast<long long>(TotalProofStencilUniformTiles),
|
||||||
|
static_cast<long long>(TotalProofStencilSealedTiles),
|
||||||
|
static_cast<long long>(TotalProofStencilOutOfLayoutAirTiles),
|
||||||
|
static_cast<long long>(TotalProofStencilClassifyTiles));
|
||||||
TestEqual(TEXT("All 64 owner seeds have a clear, walkable A-to-B body corridor"),
|
TestEqual(TEXT("All 64 owner seeds have a clear, walkable A-to-B body corridor"),
|
||||||
SeedsWithWalkFailures, 0);
|
SeedsWithWalkFailures, 0);
|
||||||
return SeedsWithWalkFailures == 0;
|
TestEqual(TEXT("No corridor stencil tile is proven uniform by either proof or ClassifyTile"),
|
||||||
|
TotalProofStencilUniformTiles, static_cast<int64>(0));
|
||||||
|
return SeedsWithWalkFailures == 0 && TotalProofStencilUniformTiles == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WITH_DEV_AUTOMATION_TESTS
|
#endif // WITH_DEV_AUTOMATION_TESTS
|
||||||
@@ -7107,8 +7107,10 @@ EVoxelTileClass UVoxelGenerator::ClassifyTileUncached(
|
|||||||
|
|
||||||
// ── Structural passage/origin guards for non-open-air categories. ──
|
// ── Structural passage/origin guards for non-open-air categories. ──
|
||||||
// Carvers kill AllSolid but never add rock; landing and origin support floors kill AllAir.
|
// Carvers kill AllSolid but never add rock; landing and origin support floors kill AllAir.
|
||||||
if (StrateManager->AnyPassageNearLattice(
|
const bool bHasPassageCarvingCandidate =
|
||||||
TileVoxelBox, OriginVoxels, Step))
|
StrateManager->AnyPassageNearLattice(
|
||||||
|
TileVoxelBox, OriginVoxels, Step);
|
||||||
|
if (bHasPassageCarvingCandidate)
|
||||||
{
|
{
|
||||||
bCanSolid = false;
|
bCanSolid = false;
|
||||||
}
|
}
|
||||||
@@ -7355,10 +7357,10 @@ EVoxelTileClass UVoxelGenerator::ClassifyTileUncached(
|
|||||||
&& CaveMinZ == MinZ && CaveMaxZ == MaxZ;
|
&& CaveMinZ == MinZ && CaveMaxZ == MaxZ;
|
||||||
if (bCaveOnlyTile && StackVerdict == EVoxelTileClass::AllSolid)
|
if (bCaveOnlyTile && StackVerdict == EVoxelTileClass::AllSolid)
|
||||||
{
|
{
|
||||||
// The stack verdict covers the complete cave-only MC lattice. Its exact structural
|
// The operator stack does not own the final passage carve. Keep
|
||||||
// post path was supplied above, so a broad conservative passage guard must not force
|
// its AllSolid shortcut only when the exact-lattice passage guard
|
||||||
// this already-certified solid tile through GenerateMesh.
|
// found no modifier or body-envelope write in this tile.
|
||||||
bCanSolid = true;
|
bCanSolid = !bHasPassageCarvingCandidate;
|
||||||
bCanAir = false;
|
bCanAir = false;
|
||||||
}
|
}
|
||||||
else if (bCaveOnlyTile && StackVerdict == EVoxelTileClass::AllAir)
|
else if (bCaveOnlyTile && StackVerdict == EVoxelTileClass::AllAir)
|
||||||
|
|||||||
@@ -687,6 +687,74 @@ namespace
|
|||||||
&& OutSupportRadius > 0.0f;
|
&& OutSupportRadius > 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FMath::PerlinNoise2D is in (-1, 1); the two positive octave weights sum to one,
|
||||||
|
// SmoothStep01 keeps the apron envelope in [0, 1], and each segment stores the scale
|
||||||
|
// actually consumed by VF_ProjectNativePassageFloorSegment. This is the write supremum,
|
||||||
|
// including a small float-rounding allowance at the authored floor's world Z.
|
||||||
|
static bool VF_GetNativePassageFloorReliefBound(
|
||||||
|
const FVoxelPassage& Passage, float& OutReliefBound)
|
||||||
|
{
|
||||||
|
OutReliefBound = 0.0f;
|
||||||
|
if (!Passage.bNativeFloorEnabled)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Passage.ControlPoints.Num() < 2
|
||||||
|
|| Passage.ControlRadii.Num() != Passage.ControlPoints.Num()
|
||||||
|
|| Passage.NativeFloorProfileZ.Num() != Passage.ControlPoints.Num()
|
||||||
|
|| Passage.ControlPoints.Num() > 4096)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float MaxAbsFloorZ = 0.0f;
|
||||||
|
for (int32 Index = 0; Index < Passage.ControlPoints.Num(); ++Index)
|
||||||
|
{
|
||||||
|
if (!VoxelMath::IsFinite(Passage.NativeFloorProfileZ[Index]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MaxAbsFloorZ = FMath::Max(
|
||||||
|
MaxAbsFloorZ, FMath::Abs(Passage.NativeFloorProfileZ[Index]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The relief helper returns exactly zero for these inputs.
|
||||||
|
if (!(Passage.NativeFloorReliefStrength > 0.0f)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.NativeFloorReliefStrength)
|
||||||
|
|| !VoxelMath::IsFinite(Passage.NativeFloorReliefFrequency)
|
||||||
|
|| !(FMath::Abs(Passage.NativeFloorReliefFrequency) > KINDA_SMALL_NUMBER))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float MaxAmplitude = 0.0f;
|
||||||
|
for (int32 SegmentIndex = 0;
|
||||||
|
SegmentIndex + 1 < Passage.ControlPoints.Num(); ++SegmentIndex)
|
||||||
|
{
|
||||||
|
const float ReliefScale =
|
||||||
|
Passage.NativeFloorReliefScales.IsValidIndex(SegmentIndex)
|
||||||
|
? Passage.NativeFloorReliefScales[SegmentIndex] : 0.0f;
|
||||||
|
if (!VoxelMath::IsFinite(ReliefScale))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const float Amplitude =
|
||||||
|
FMath::Abs(Passage.NativeFloorReliefStrength)
|
||||||
|
* VOXEL_NOISE_SCALE * FMath::Abs(ReliefScale);
|
||||||
|
if (!VoxelMath::IsFinite(Amplitude))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MaxAmplitude = FMath::Max(MaxAmplitude, Amplitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float RoundingAllowance =
|
||||||
|
8.0f * FLT_EPSILON * FMath::Max(1.0f, MaxAbsFloorZ + MaxAmplitude)
|
||||||
|
+ KINDA_SMALL_NUMBER;
|
||||||
|
OutReliefBound = MaxAmplitude + RoundingAllowance;
|
||||||
|
return VoxelMath::IsFinite(OutReliefBound);
|
||||||
|
}
|
||||||
|
|
||||||
static bool VF_ProjectNativePassageFloorUncached(
|
static bool VF_ProjectNativePassageFloorUncached(
|
||||||
const FVoxelPassage& Passage, const FVector& Position,
|
const FVoxelPassage& Passage, const FVector& Position,
|
||||||
float& OutFloorZ, float& OutSupportRadius,
|
float& OutFloorZ, float& OutSupportRadius,
|
||||||
@@ -2410,6 +2478,16 @@ void UVoxelStrateManager::GeneratePassages()
|
|||||||
BoundsMax.Y = FMath::Max(BoundsMax.Y, Point.Y + Pad);
|
BoundsMax.Y = FMath::Max(BoundsMax.Y, Point.Y + Pad);
|
||||||
BoundsMax.Z = FMath::Max(BoundsMax.Z, Point.Z + Pad);
|
BoundsMax.Z = FMath::Max(BoundsMax.Z, Point.Z + Pad);
|
||||||
};
|
};
|
||||||
|
auto IncludeBounds = [&BoundsMin, &BoundsMax](
|
||||||
|
const FVector& Min, const FVector& Max)
|
||||||
|
{
|
||||||
|
BoundsMin.X = FMath::Min(BoundsMin.X, Min.X);
|
||||||
|
BoundsMin.Y = FMath::Min(BoundsMin.Y, Min.Y);
|
||||||
|
BoundsMin.Z = FMath::Min(BoundsMin.Z, Min.Z);
|
||||||
|
BoundsMax.X = FMath::Max(BoundsMax.X, Max.X);
|
||||||
|
BoundsMax.Y = FMath::Max(BoundsMax.Y, Max.Y);
|
||||||
|
BoundsMax.Z = FMath::Max(BoundsMax.Z, Max.Z);
|
||||||
|
};
|
||||||
for (const FVector& CP : Passage.ControlPoints)
|
for (const FVector& CP : Passage.ControlPoints)
|
||||||
{
|
{
|
||||||
IncludePoint(CP, Passage.Radius + 4.0f);
|
IncludePoint(CP, Passage.Radius + 4.0f);
|
||||||
@@ -2430,6 +2508,109 @@ void UVoxelStrateManager::GeneratePassages()
|
|||||||
IncludeLanding(Passage.UpperLanding);
|
IncludeLanding(Passage.UpperLanding);
|
||||||
IncludeLanding(Passage.LowerLanding);
|
IncludeLanding(Passage.LowerLanding);
|
||||||
|
|
||||||
|
// These two MC writers run after the tunnel SDF: the native support slab uses
|
||||||
|
// the relieved floor profile, and the final corridor air envelope uses each
|
||||||
|
// segment's own relief branch plus the door-to-standing approaches. Include the
|
||||||
|
// actual write suprema so the chunk shortlist and all sphere-based proofs remain
|
||||||
|
// conservative even when the authored relief exceeds the old one-voxel reserve.
|
||||||
|
if (Passage.bWalkableTunnelContract && Passage.bNativeFloorEnabled)
|
||||||
|
{
|
||||||
|
constexpr float CorridorHalfWidth =
|
||||||
|
0.5f * VoxelPassageGeometry::WalkCorridorPlayerWidthVoxels
|
||||||
|
+ VoxelPassageGeometry::WalkCorridorLatticeMarginVoxels;
|
||||||
|
constexpr float CorridorBottom =
|
||||||
|
VoxelPassageGeometry::MaxStepHeightVoxels;
|
||||||
|
constexpr float CorridorTop = CorridorBottom
|
||||||
|
+ VoxelPassageGeometry::WalkCorridorPlayerHeightVoxels;
|
||||||
|
float ReliefBound = 0.0f;
|
||||||
|
if (VF_GetNativePassageFloorReliefBound(Passage, ReliefBound)
|
||||||
|
&& Passage.ControlPoints.Num() >= 2
|
||||||
|
&& Passage.ControlRadii.Num() == Passage.ControlPoints.Num())
|
||||||
|
{
|
||||||
|
for (int32 SegmentIndex = 0;
|
||||||
|
SegmentIndex + 1 < Passage.ControlPoints.Num();
|
||||||
|
++SegmentIndex)
|
||||||
|
{
|
||||||
|
const FVector& A = Passage.ControlPoints[SegmentIndex];
|
||||||
|
const FVector& B = Passage.ControlPoints[SegmentIndex + 1];
|
||||||
|
const float RadiusA =
|
||||||
|
FMath::Abs(Passage.ControlRadii[SegmentIndex]);
|
||||||
|
const float RadiusB =
|
||||||
|
FMath::Abs(Passage.ControlRadii[SegmentIndex + 1]);
|
||||||
|
const float FloorA =
|
||||||
|
Passage.NativeFloorProfileZ[SegmentIndex];
|
||||||
|
const float FloorB =
|
||||||
|
Passage.NativeFloorProfileZ[SegmentIndex + 1];
|
||||||
|
if (!VoxelMath::IsFinite(A.X) || !VoxelMath::IsFinite(A.Y)
|
||||||
|
|| !VoxelMath::IsFinite(B.X) || !VoxelMath::IsFinite(B.Y)
|
||||||
|
|| !VoxelMath::IsFinite(FloorA)
|
||||||
|
|| !VoxelMath::IsFinite(FloorB))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const float SupportRadius =
|
||||||
|
VoxelPassageGeometry::NativePassageFloorSupportRadius(
|
||||||
|
RadiusA, RadiusB);
|
||||||
|
IncludeBounds(
|
||||||
|
FVector(
|
||||||
|
FMath::Min(A.X, B.X) - SupportRadius,
|
||||||
|
FMath::Min(A.Y, B.Y) - SupportRadius,
|
||||||
|
FMath::Min(FloorA, FloorB) - ReliefBound
|
||||||
|
- VoxelPassageGeometry::LandingFloorThicknessVoxels),
|
||||||
|
FVector(
|
||||||
|
FMath::Max(A.X, B.X) + SupportRadius,
|
||||||
|
FMath::Max(A.Y, B.Y) + SupportRadius,
|
||||||
|
FMath::Max(FloorA, FloorB) + ReliefBound
|
||||||
|
+ KINDA_SMALL_NUMBER));
|
||||||
|
IncludeBounds(
|
||||||
|
FVector(
|
||||||
|
FMath::Min(A.X, B.X) - CorridorHalfWidth,
|
||||||
|
FMath::Min(A.Y, B.Y) - CorridorHalfWidth,
|
||||||
|
FMath::Min(FloorA, FloorB) + CorridorBottom
|
||||||
|
- ReliefBound),
|
||||||
|
FVector(
|
||||||
|
FMath::Max(A.X, B.X) + CorridorHalfWidth,
|
||||||
|
FMath::Max(A.Y, B.Y) + CorridorHalfWidth,
|
||||||
|
FMath::Max(FloorA, FloorB) + CorridorTop
|
||||||
|
+ ReliefBound));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const FVoxelPassageLanding* Landing : {
|
||||||
|
&Passage.UpperLanding, &Passage.LowerLanding })
|
||||||
|
{
|
||||||
|
if (!VoxelMath::IsFinite(Landing->DoorPoint.X)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->DoorPoint.Y)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->StandingPoint.X)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->StandingPoint.Y)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->FloorZ))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const float LengthSquared = FVector2D::DistSquared(
|
||||||
|
FVector2D(Landing->DoorPoint.X, Landing->DoorPoint.Y),
|
||||||
|
FVector2D(Landing->StandingPoint.X, Landing->StandingPoint.Y));
|
||||||
|
if (!(LengthSquared > KINDA_SMALL_NUMBER)
|
||||||
|
|| !VoxelMath::IsFinite(LengthSquared))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
IncludeBounds(
|
||||||
|
FVector(
|
||||||
|
FMath::Min(Landing->DoorPoint.X, Landing->StandingPoint.X)
|
||||||
|
- CorridorHalfWidth,
|
||||||
|
FMath::Min(Landing->DoorPoint.Y, Landing->StandingPoint.Y)
|
||||||
|
- CorridorHalfWidth,
|
||||||
|
Landing->FloorZ + CorridorBottom),
|
||||||
|
FVector(
|
||||||
|
FMath::Max(Landing->DoorPoint.X, Landing->StandingPoint.X)
|
||||||
|
+ CorridorHalfWidth,
|
||||||
|
FMath::Max(Landing->DoorPoint.Y, Landing->StandingPoint.Y)
|
||||||
|
+ CorridorHalfWidth,
|
||||||
|
Landing->FloorZ + CorridorTop));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const FVector Center = (BoundsMin + BoundsMax) * 0.5f;
|
const FVector Center = (BoundsMin + BoundsMax) * 0.5f;
|
||||||
float MaxDistSq = 0.0f;
|
float MaxDistSq = 0.0f;
|
||||||
MaxDistSq = FMath::Max(MaxDistSq, (float)FVector::DistSquared(Center, BoundsMin));
|
MaxDistSq = FMath::Max(MaxDistSq, (float)FVector::DistSquared(Center, BoundsMin));
|
||||||
@@ -3399,6 +3580,11 @@ bool UVoxelStrateManager::AnyPassageLandingFloorNearBox(
|
|||||||
// a tile; a false negative could classify an air tile uniformly and remove the player's floor.
|
// a tile; a false negative could classify an air tile uniformly and remove the player's floor.
|
||||||
for (const FVoxelPassage& Passage : Passages)
|
for (const FVoxelPassage& Passage : Passages)
|
||||||
{
|
{
|
||||||
|
float ReliefBound = 0.0f;
|
||||||
|
if (!VF_GetNativePassageFloorReliefBound(Passage, ReliefBound))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const FVoxelPassageLanding* Landings[] = {
|
const FVoxelPassageLanding* Landings[] = {
|
||||||
&Passage.UpperLanding, &Passage.LowerLanding };
|
&Passage.UpperLanding, &Passage.LowerLanding };
|
||||||
for (const FVoxelPassageLanding* Landing : Landings)
|
for (const FVoxelPassageLanding* Landing : Landings)
|
||||||
@@ -3426,10 +3612,13 @@ bool UVoxelStrateManager::AnyPassageLandingFloorNearBox(
|
|||||||
// The default tube also owns a three-voxel support band. Its conservative segment AABB
|
// The default tube also owns a three-voxel support band. Its conservative segment AABB
|
||||||
// keeps a classifier from proving AllAir over the floor and then dropping that support
|
// keeps a classifier from proving AllAir over the floor and then dropping that support
|
||||||
// during meshing. False positives are intentional; false negatives would be a hole.
|
// during meshing. False positives are intentional; false negatives would be a hole.
|
||||||
if (Passage.bWalkableTunnelContract
|
if (Passage.bWalkableTunnelContract)
|
||||||
&& Passage.ControlPoints.Num() >= 2
|
|
||||||
&& Passage.ControlRadii.Num() == Passage.ControlPoints.Num())
|
|
||||||
{
|
{
|
||||||
|
if (Passage.ControlPoints.Num() < 2
|
||||||
|
|| Passage.ControlRadii.Num() != Passage.ControlPoints.Num())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
constexpr float Pad = 1.0f;
|
constexpr float Pad = 1.0f;
|
||||||
for (int32 SegmentIndex = 0;
|
for (int32 SegmentIndex = 0;
|
||||||
SegmentIndex + 1 < Passage.ControlPoints.Num();
|
SegmentIndex + 1 < Passage.ControlPoints.Num();
|
||||||
@@ -3437,22 +3626,29 @@ bool UVoxelStrateManager::AnyPassageLandingFloorNearBox(
|
|||||||
{
|
{
|
||||||
const FVector& A = Passage.ControlPoints[SegmentIndex];
|
const FVector& A = Passage.ControlPoints[SegmentIndex];
|
||||||
const FVector& B = Passage.ControlPoints[SegmentIndex + 1];
|
const FVector& B = Passage.ControlPoints[SegmentIndex + 1];
|
||||||
const float SupportRadius = FMath::Max(
|
if (!VoxelMath::IsFinite(A.X) || !VoxelMath::IsFinite(A.Y)
|
||||||
FMath::Min(
|
|| !VoxelMath::IsFinite(A.Z) || !VoxelMath::IsFinite(B.X)
|
||||||
FMath::Abs(Passage.ControlRadii[SegmentIndex]),
|
|| !VoxelMath::IsFinite(B.Y) || !VoxelMath::IsFinite(B.Z)
|
||||||
FMath::Abs(Passage.ControlRadii[SegmentIndex + 1])) - 0.5f,
|
|| !VoxelMath::IsFinite(Passage.ControlRadii[SegmentIndex])
|
||||||
VoxelPassageGeometry::PlayerRadiusVoxels);
|
|| !VoxelMath::IsFinite(Passage.ControlRadii[SegmentIndex + 1]))
|
||||||
const float FloorMinZ = FMath::Min(
|
{
|
||||||
VoxelPassageGeometry::TunnelFloorZ(
|
return true;
|
||||||
A, Passage.ControlRadii[SegmentIndex]),
|
}
|
||||||
VoxelPassageGeometry::TunnelFloorZ(
|
const float RadiusA = FMath::Abs(Passage.ControlRadii[SegmentIndex]);
|
||||||
B, Passage.ControlRadii[SegmentIndex + 1]))
|
const float RadiusB = FMath::Abs(Passage.ControlRadii[SegmentIndex + 1]);
|
||||||
- VoxelPassageGeometry::LandingFloorThicknessVoxels - Pad;
|
const float SupportRadius =
|
||||||
const float FloorMaxZ = FMath::Max(
|
VoxelPassageGeometry::NativePassageFloorSupportRadius(RadiusA, RadiusB);
|
||||||
VoxelPassageGeometry::TunnelFloorZ(
|
const float FloorA = Passage.bNativeFloorEnabled
|
||||||
A, Passage.ControlRadii[SegmentIndex]),
|
? Passage.NativeFloorProfileZ[SegmentIndex]
|
||||||
VoxelPassageGeometry::TunnelFloorZ(
|
: VoxelPassageGeometry::TunnelFloorZ(A, RadiusA);
|
||||||
B, Passage.ControlRadii[SegmentIndex + 1])) + Pad;
|
const float FloorB = Passage.bNativeFloorEnabled
|
||||||
|
? Passage.NativeFloorProfileZ[SegmentIndex + 1]
|
||||||
|
: VoxelPassageGeometry::TunnelFloorZ(B, RadiusB);
|
||||||
|
const float FloorMinZ = FMath::Min(FloorA, FloorB)
|
||||||
|
- VoxelPassageGeometry::LandingFloorThicknessVoxels
|
||||||
|
- Pad - ReliefBound;
|
||||||
|
const float FloorMaxZ = FMath::Max(FloorA, FloorB)
|
||||||
|
+ Pad + ReliefBound;
|
||||||
const float FloorMinX = FMath::Min(A.X, B.X) - SupportRadius - Pad;
|
const float FloorMinX = FMath::Min(A.X, B.X) - SupportRadius - Pad;
|
||||||
const float FloorMaxX = FMath::Max(A.X, B.X) + SupportRadius + Pad;
|
const float FloorMaxX = FMath::Max(A.X, B.X) + SupportRadius + Pad;
|
||||||
const float FloorMinY = FMath::Min(A.Y, B.Y) - SupportRadius - Pad;
|
const float FloorMinY = FMath::Min(A.Y, B.Y) - SupportRadius - Pad;
|
||||||
@@ -4255,6 +4451,106 @@ namespace
|
|||||||
FMath::Clamp(Center.Z, (float)Box.Min.Z, (float)Box.Max.Z));
|
FMath::Clamp(Center.Z, (float)Box.Min.Z, (float)Box.Max.Z));
|
||||||
return FVector::DistSquared(Closest, Center) <= Radius * Radius;
|
return FVector::DistSquared(Closest, Center) <= Radius * Radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool VF_WalkCorridorBodyCarveTouchesLattice(
|
||||||
|
const TArray<FVoxelPassage>& Passages,
|
||||||
|
const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step)
|
||||||
|
{
|
||||||
|
if (Step <= 0 || !VoxelBox.IsValid)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
constexpr float CorridorHalfWidth =
|
||||||
|
0.5f * VoxelPassageGeometry::WalkCorridorPlayerWidthVoxels
|
||||||
|
+ VoxelPassageGeometry::WalkCorridorLatticeMarginVoxels;
|
||||||
|
constexpr float CorridorBottom = VoxelPassageGeometry::MaxStepHeightVoxels;
|
||||||
|
constexpr float CorridorTop = CorridorBottom
|
||||||
|
+ VoxelPassageGeometry::WalkCorridorPlayerHeightVoxels;
|
||||||
|
|
||||||
|
for (const FVoxelPassage& Passage : Passages)
|
||||||
|
{
|
||||||
|
if (!Passage.bWalkableTunnelContract || !Passage.bNativeFloorEnabled)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
float ReliefBound = 0.0f;
|
||||||
|
if (!VF_GetNativePassageFloorReliefBound(Passage, ReliefBound))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Passage.ControlPoints.Num() < 2
|
||||||
|
|| Passage.ControlRadii.Num() != Passage.ControlPoints.Num())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32 SegmentIndex = 0;
|
||||||
|
SegmentIndex + 1 < Passage.ControlPoints.Num(); ++SegmentIndex)
|
||||||
|
{
|
||||||
|
const FVector& A = Passage.ControlPoints[SegmentIndex];
|
||||||
|
const FVector& B = Passage.ControlPoints[SegmentIndex + 1];
|
||||||
|
const float FloorA = Passage.NativeFloorProfileZ[SegmentIndex];
|
||||||
|
const float FloorB = Passage.NativeFloorProfileZ[SegmentIndex + 1];
|
||||||
|
if (!VoxelMath::IsFinite(A.X) || !VoxelMath::IsFinite(A.Y)
|
||||||
|
|| !VoxelMath::IsFinite(B.X) || !VoxelMath::IsFinite(B.Y)
|
||||||
|
|| !VoxelMath::IsFinite(FloorA) || !VoxelMath::IsFinite(FloorB))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (VF_LatticeBoxTouchesAABB(
|
||||||
|
VoxelBox, LatticeOrigin, Step,
|
||||||
|
FMath::Min(A.X, B.X) - CorridorHalfWidth,
|
||||||
|
FMath::Max(A.X, B.X) + CorridorHalfWidth,
|
||||||
|
FMath::Min(A.Y, B.Y) - CorridorHalfWidth,
|
||||||
|
FMath::Max(A.Y, B.Y) + CorridorHalfWidth,
|
||||||
|
FMath::Min(FloorA, FloorB) + CorridorBottom - ReliefBound,
|
||||||
|
FMath::Max(FloorA, FloorB) + CorridorTop + ReliefBound))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const FVoxelPassageLanding* Landing : {
|
||||||
|
&Passage.UpperLanding, &Passage.LowerLanding })
|
||||||
|
{
|
||||||
|
if (!VoxelMath::IsFinite(Landing->DoorPoint.X)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->DoorPoint.Y)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->StandingPoint.X)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->StandingPoint.Y)
|
||||||
|
|| !VoxelMath::IsFinite(Landing->FloorZ))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const float LengthSquared = FVector2D::DistSquared(
|
||||||
|
FVector2D(Landing->DoorPoint.X, Landing->DoorPoint.Y),
|
||||||
|
FVector2D(Landing->StandingPoint.X, Landing->StandingPoint.Y));
|
||||||
|
if (!VoxelMath::IsFinite(LengthSquared))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (LengthSquared <= KINDA_SMALL_NUMBER)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (VF_LatticeBoxTouchesAABB(
|
||||||
|
VoxelBox, LatticeOrigin, Step,
|
||||||
|
FMath::Min(Landing->DoorPoint.X, Landing->StandingPoint.X)
|
||||||
|
- CorridorHalfWidth,
|
||||||
|
FMath::Max(Landing->DoorPoint.X, Landing->StandingPoint.X)
|
||||||
|
+ CorridorHalfWidth,
|
||||||
|
FMath::Min(Landing->DoorPoint.Y, Landing->StandingPoint.Y)
|
||||||
|
- CorridorHalfWidth,
|
||||||
|
FMath::Max(Landing->DoorPoint.Y, Landing->StandingPoint.Y)
|
||||||
|
+ CorridorHalfWidth,
|
||||||
|
Landing->FloorZ + CorridorBottom,
|
||||||
|
Landing->FloorZ + CorridorTop))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UVoxelStrateManager::AnyPassageNearLattice(
|
bool UVoxelStrateManager::AnyPassageNearLattice(
|
||||||
@@ -4265,17 +4561,20 @@ bool UVoxelStrateManager::AnyPassageNearLattice(
|
|||||||
// zero carve factor. That was the LOD0 false-Mixed path. Keep the cheap domain test as the
|
// zero carve factor. That was the LOD0 false-Mixed path. Keep the cheap domain test as the
|
||||||
// common reject, then use the same exact lattice factor that MaxCarveOverBox consumes. The
|
// common reject, then use the same exact lattice factor that MaxCarveOverBox consumes. The
|
||||||
// manager cache makes the second call in the fold free for the same box.
|
// manager cache makes the second call in the fold free for the same box.
|
||||||
|
const bool bBodyCarveCandidate = VF_WalkCorridorBodyCarveTouchesLattice(
|
||||||
|
Passages, VoxelBox, LatticeOrigin, Step);
|
||||||
if (!VF_AnyPassageModifierDomainTouchesLattice(
|
if (!VF_AnyPassageModifierDomainTouchesLattice(
|
||||||
Passages, VoxelBox, LatticeOrigin, Step))
|
Passages, VoxelBox, LatticeOrigin, Step))
|
||||||
{
|
{
|
||||||
return false;
|
return bBodyCarveCandidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float MaxFactor = MaxPassageCarveFactorNearLattice(
|
const float MaxFactor = MaxPassageCarveFactorNearLattice(
|
||||||
VoxelBox, LatticeOrigin, Step);
|
VoxelBox, LatticeOrigin, Step);
|
||||||
// Invalid input deliberately returns 1.0 from the exact helper. A non-finite result is also
|
// 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.
|
// retained as a candidate rather than becoming an identity proof.
|
||||||
return !VoxelMath::IsFinite(MaxFactor) || MaxFactor > 0.0f;
|
return !VoxelMath::IsFinite(MaxFactor) || MaxFactor > 0.0f
|
||||||
|
|| bBodyCarveCandidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UVoxelStrateManager::AnyPassageAirPostNearLattice(
|
bool UVoxelStrateManager::AnyPassageAirPostNearLattice(
|
||||||
@@ -4296,6 +4595,11 @@ bool UVoxelStrateManager::AnyPassageAirPostNearLattice(
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (VF_WalkCorridorBodyCarveTouchesLattice(
|
||||||
|
Passages, VoxelBox, LatticeOrigin, Step))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int32 FirstX = 0, LastX = -1;
|
int32 FirstX = 0, LastX = -1;
|
||||||
int32 FirstY = 0, LastY = -1;
|
int32 FirstY = 0, LastY = -1;
|
||||||
@@ -5033,6 +5337,11 @@ bool UVoxelStrateManager::AnyPassageLandingFloorNearLattice(
|
|||||||
}
|
}
|
||||||
for (const FVoxelPassage& Passage : Passages)
|
for (const FVoxelPassage& Passage : Passages)
|
||||||
{
|
{
|
||||||
|
float ReliefBound = 0.0f;
|
||||||
|
if (!VF_GetNativePassageFloorReliefBound(Passage, ReliefBound))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const FVoxelPassageLanding* Landings[] = {
|
const FVoxelPassageLanding* Landings[] = {
|
||||||
&Passage.UpperLanding, &Passage.LowerLanding };
|
&Passage.UpperLanding, &Passage.LowerLanding };
|
||||||
for (const FVoxelPassageLanding* Landing : Landings)
|
for (const FVoxelPassageLanding* Landing : Landings)
|
||||||
@@ -5084,15 +5393,16 @@ bool UVoxelStrateManager::AnyPassageLandingFloorNearLattice(
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const float SupportRadius = FMath::Max(
|
const float RadiusA = FMath::Abs(Passage.ControlRadii[SegmentIndex]);
|
||||||
FMath::Min(
|
const float RadiusB = FMath::Abs(Passage.ControlRadii[SegmentIndex + 1]);
|
||||||
FMath::Abs(Passage.ControlRadii[SegmentIndex]),
|
const float SupportRadius =
|
||||||
FMath::Abs(Passage.ControlRadii[SegmentIndex + 1])) - 0.5f,
|
VoxelPassageGeometry::NativePassageFloorSupportRadius(RadiusA, RadiusB);
|
||||||
VoxelPassageGeometry::PlayerRadiusVoxels);
|
const float FloorA = Passage.bNativeFloorEnabled
|
||||||
const float FloorA = VoxelPassageGeometry::TunnelFloorZ(
|
? Passage.NativeFloorProfileZ[SegmentIndex]
|
||||||
A, Passage.ControlRadii[SegmentIndex]);
|
: VoxelPassageGeometry::TunnelFloorZ(A, RadiusA);
|
||||||
const float FloorB = VoxelPassageGeometry::TunnelFloorZ(
|
const float FloorB = Passage.bNativeFloorEnabled
|
||||||
B, Passage.ControlRadii[SegmentIndex + 1]);
|
? Passage.NativeFloorProfileZ[SegmentIndex + 1]
|
||||||
|
: VoxelPassageGeometry::TunnelFloorZ(B, RadiusB);
|
||||||
if (VF_LatticeBoxTouchesAABB(
|
if (VF_LatticeBoxTouchesAABB(
|
||||||
VoxelBox, LatticeOrigin, Step,
|
VoxelBox, LatticeOrigin, Step,
|
||||||
FMath::Min(A.X, B.X) - SupportRadius - Pad,
|
FMath::Min(A.X, B.X) - SupportRadius - Pad,
|
||||||
@@ -5100,8 +5410,9 @@ bool UVoxelStrateManager::AnyPassageLandingFloorNearLattice(
|
|||||||
FMath::Min(A.Y, B.Y) - SupportRadius - Pad,
|
FMath::Min(A.Y, B.Y) - SupportRadius - Pad,
|
||||||
FMath::Max(A.Y, B.Y) + SupportRadius + Pad,
|
FMath::Max(A.Y, B.Y) + SupportRadius + Pad,
|
||||||
FMath::Min(FloorA, FloorB)
|
FMath::Min(FloorA, FloorB)
|
||||||
- VoxelPassageGeometry::LandingFloorThicknessVoxels - Pad,
|
- VoxelPassageGeometry::LandingFloorThicknessVoxels
|
||||||
FMath::Max(FloorA, FloorB) + Pad))
|
- Pad - ReliefBound,
|
||||||
|
FMath::Max(FloorA, FloorB) + Pad + ReliefBound))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user