fix(measure): the fragmentation was the instrument -- 309 pockets -> 96

Jahni walked the maze end to end while the metric called it 309
disconnected pockets whose largest held 176 cells, with the arrival mouth
in a THREE-CELL pocket. Direct observation beat the instrument, again.

Cause, found exactly: the stencil demanded a solid cell at precisely
(X,Y,Z-1). No supporting-surface search, no slope tolerance, no step
height. At step 1 a floor that slopes even slightly loses its solid cell
under most of its air, so a walkable corridor dissolved into specks.

Now: a bounded downward search for a COHERENT SUPPORTING SURFACE within a
step-height tolerance (MaxStepHeight 45 cm = 1.8 voxels), a walkable-slope
normal test (WalkableFloorAngle 44 deg), then the full capsule re-tested at
the resolved support height. Deliberately NOT "any solid within N cells",
which would accept ledges and isolated pixels.

⭐ ON THE WALKED MAZE (seed 0, index 50, slot 4):
  components   309 -> 96          largest       176 -> 1401
  fit cells   2801 -> 6668        mouth comps  43/81 -> 147/1401
The 300-pocket world was a measurement artifact. Fifth time this session
the instrument was the suspect; fourth time it was the culprit.

⚠️ AND IT IS NOT SELF-FLATTERING -- a metric change that only ever makes
numbers rise is worthless, so it ships with controls that must REJECT:
  vertical shaft wall      0 fit cells
  isolated 1-voxel ledge   0 fit cells
  overhang underside       0 fit cells
  ramp steeper than 44 deg 0 fit cells
  coarse step 4            refused, not guessed
Open flat-floor control: 6.13% of air cells fit, ONE component of 12,168 --
sensible, since only near-floor poses qualify in a tall room.

⚠️ BUT THE ROUTE STILL DOES NOT EXIST, and that part of H3 survives. The law
is 1/8 before and after -- what changed is its honesty: the one connection
used to be FloatingIslands through FOUR cells, and is now FlatPlain through
2,221 with a zero gap. Seven archetypes still have both mouths in separate
player-fit components 56-169 voxels (14-42 m) apart, and those components
are now large and real (663-2902 cells), not specks.

⇒ So the world HAS substantial connected player-sized space. The passage
MOUTHS are not landing in it. That is the Tier 1 landing problem again,
one level down: VF_SuggestLandingPoint aims at open AIR, and air is not
where a body fits. Next task.

Codex found three errors in my baseline quoting, all mine: I wrote "0/8
connected" while also saying one connects through four cells (it was 1/8),
misquoted SurfaceWorld's gap as 1101 when the saved value is 110.648, and
mixed provenance on the VerticalShafts row.

Build clean, 31 succeeded / 0 failed. The 2 warnings are both deliberate:
the lateral-regions gate, and a test reporting that its own sample "barely
asked its question".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8V9my1PL68pmQb2gutZZV
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-06 04:46:01 +02:00
1 parent cf5f83e9f0
commit 110ae9d0ed
4 files changed
+790 -53

No files matched your search

+449 -38
View File
@@ -34,7 +34,8 @@ namespace VoxelStrateMeasurePrivate
// 1 = air, 0 = solid. The polarity is deliberately explicit at the sampling site below.
TArray<uint8> Air;
// Optional exact scalar samples retained only for an explicit preview capture.
// Exact scalar samples retained when the player-fit stencil needs a bounded local
// surface/clearance interpolation, or when an explicit preview capture requests them.
TArray<float> Density;
FORCEINLINE int32 Index(int32 X, int32 Y, int32 Z) const
@@ -587,6 +588,302 @@ namespace VoxelStrateMeasurePrivate
TArray<FIntPoint> HorizontalOffsets;
};
bool HasScalarDensity(const FSampleGrid& Grid)
{
return Grid.Density.Num() == Grid.CellCount;
}
float CellDensity(const FSampleGrid& Grid, int32 Cell)
{
return HasScalarDensity(Grid)
? Grid.Density[Cell]
: (Grid.Air[Cell] != 0u ? 1.0f : -1.0f);
}
bool SampleGridDensity(
const FSampleGrid& Grid,
const FVector& Point,
float& OutDensity)
{
if (!Grid.ContainsPoint(Point))
{
return false;
}
const float Step = static_cast<float>(Grid.SampleStep);
const float LocalX = (Point.X - Grid.MinX) / Step - 0.5f;
const float LocalY = (Point.Y - Grid.MinY) / Step - 0.5f;
const float LocalZ = (Point.Z - Grid.MinZ) / Step - 0.5f;
if (!HasScalarDensity(Grid))
{
const int32 X = FMath::Clamp(
FMath::FloorToInt((Point.X - Grid.MinX) / Step), 0, Grid.NumX - 1);
const int32 Y = FMath::Clamp(
FMath::FloorToInt((Point.Y - Grid.MinY) / Step), 0, Grid.NumY - 1);
const int32 Z = FMath::Clamp(
FMath::FloorToInt((Point.Z - Grid.MinZ) / Step), 0, Grid.NumZ - 1);
OutDensity = CellDensity(Grid, Grid.Index(X, Y, Z));
return FMath::IsFinite(OutDensity);
}
const int32 X0 = FMath::Clamp(FMath::FloorToInt(LocalX), 0, Grid.NumX - 1);
const int32 Y0 = FMath::Clamp(FMath::FloorToInt(LocalY), 0, Grid.NumY - 1);
const int32 Z0 = FMath::Clamp(FMath::FloorToInt(LocalZ), 0, Grid.NumZ - 1);
const int32 X1 = FMath::Min(X0 + 1, Grid.NumX - 1);
const int32 Y1 = FMath::Min(Y0 + 1, Grid.NumY - 1);
const int32 Z1 = FMath::Min(Z0 + 1, Grid.NumZ - 1);
const float AlphaX = FMath::Clamp(LocalX - static_cast<float>(X0), 0.0f, 1.0f);
const float AlphaY = FMath::Clamp(LocalY - static_cast<float>(Y0), 0.0f, 1.0f);
const float AlphaZ = FMath::Clamp(LocalZ - static_cast<float>(Z0), 0.0f, 1.0f);
const float D000 = CellDensity(Grid, Grid.Index(X0, Y0, Z0));
const float D100 = CellDensity(Grid, Grid.Index(X1, Y0, Z0));
const float D010 = CellDensity(Grid, Grid.Index(X0, Y1, Z0));
const float D110 = CellDensity(Grid, Grid.Index(X1, Y1, Z0));
const float D001 = CellDensity(Grid, Grid.Index(X0, Y0, Z1));
const float D101 = CellDensity(Grid, Grid.Index(X1, Y0, Z1));
const float D011 = CellDensity(Grid, Grid.Index(X0, Y1, Z1));
const float D111 = CellDensity(Grid, Grid.Index(X1, Y1, Z1));
const float D00 = FMath::Lerp(D000, D100, AlphaX);
const float D10 = FMath::Lerp(D010, D110, AlphaX);
const float D01 = FMath::Lerp(D001, D101, AlphaX);
const float D11 = FMath::Lerp(D011, D111, AlphaX);
const float D0 = FMath::Lerp(D00, D10, AlphaY);
const float D1 = FMath::Lerp(D01, D11, AlphaY);
OutDensity = FMath::Lerp(D0, D1, AlphaZ);
return FMath::IsFinite(OutDensity);
}
bool FindSupportSurfaceBelow(
const FSampleGrid& Grid,
int32 X,
int32 Y,
int32 CandidateAnchorZ,
int32 MaxDownwardSearchCells,
float MaxStepHeightVoxels,
float& OutSurfaceHeight,
int32& OutSurfaceAnchorZ)
{
const float Step = static_cast<float>(Grid.SampleStep);
const float CandidateBottom = Grid.MinZ
+ static_cast<float>(CandidateAnchorZ) * Step;
const float LowestAllowedSurface = CandidateBottom - MaxStepHeightVoxels;
const int32 LowestAnchorZ = FMath::Max(
1, CandidateAnchorZ - MaxDownwardSearchCells);
// Search from the free pose downwards. The first transition in a column is the nearest
// supporting surface; lower transitions are relevant only after the candidate has been
// lifted by the bounded step-height tolerance.
for (int32 SurfaceAnchorZ = CandidateAnchorZ;
SurfaceAnchorZ >= LowestAnchorZ;
--SurfaceAnchorZ)
{
const int32 SolidZ = SurfaceAnchorZ - 1;
if (SolidZ < 0 || SurfaceAnchorZ >= Grid.NumZ)
{
continue;
}
const int32 SolidCell = Grid.Index(X, Y, SolidZ);
const int32 AirCell = Grid.Index(X, Y, SurfaceAnchorZ);
if (Grid.Air[SolidCell] != 0u || Grid.Air[AirCell] == 0u)
{
continue;
}
float SurfaceHeight = Grid.MinZ + static_cast<float>(SurfaceAnchorZ) * Step;
if (HasScalarDensity(Grid))
{
const float LowerDensity = CellDensity(Grid, SolidCell);
const float UpperDensity = CellDensity(Grid, AirCell);
const float Denominator = UpperDensity - LowerDensity;
if (!FMath::IsFinite(LowerDensity) || !FMath::IsFinite(UpperDensity)
|| !FMath::IsFinite(Denominator) || Denominator <= 0.0f)
{
continue;
}
const float Fraction = FMath::Clamp(
-LowerDensity / Denominator, 0.0f, 1.0f);
SurfaceHeight = Grid.CellCenter(X, Y, SolidZ).Z + Fraction * Step;
}
if (FMath::IsFinite(SurfaceHeight)
&& SurfaceHeight <= CandidateBottom + KINDA_SMALL_NUMBER
&& SurfaceHeight >= LowestAllowedSurface - KINDA_SMALL_NUMBER)
{
OutSurfaceHeight = SurfaceHeight;
OutSurfaceAnchorZ = SurfaceAnchorZ;
return true;
}
}
return false;
}
bool BuildPlayerSupportPatchOffsets(
const FVoxelStrateMeasureSettings& Settings,
TArray<FIntPoint>& OutOffsets,
int32& OutCentreOffsetIndex,
FString& OutReason)
{
OutOffsets.Reset();
OutCentreOffsetIndex = INDEX_NONE;
const double Radius = static_cast<double>(Settings.PlayerCapsuleRadiusVoxels);
if (!FMath::IsFinite(Radius) || Radius <= 0.0)
{
return Refuse(OutReason,
TEXT("Player capsule radius must be finite and greater than zero."));
}
const double MaxOffsetReal = FMath::CeilToDouble(Radius);
constexpr double MaxSupportPatchOffsets = static_cast<double>(1ll << 20);
if (!FMath::IsFinite(MaxOffsetReal) || MaxOffsetReal > 4096.0)
{
return Refuse(OutReason,
TEXT("The player support patch radius exceeds the bounded fit stencil."));
}
const int32 MaxOffset = static_cast<int32>(MaxOffsetReal);
int64 NumOffsets = 0;
for (int32 OffsetY = -MaxOffset; OffsetY <= MaxOffset; ++OffsetY)
{
for (int32 OffsetX = -MaxOffset; OffsetX <= MaxOffset; ++OffsetX)
{
if (static_cast<double>(OffsetX * OffsetX + OffsetY * OffsetY)
> Radius * Radius + KINDA_SMALL_NUMBER)
{
continue;
}
if (NumOffsets >= static_cast<int64>(MaxSupportPatchOffsets))
{
return Refuse(
OutReason,
TEXT("The player support patch exceeds its bounded work limit."));
}
if (OffsetX == 0 && OffsetY == 0)
{
OutCentreOffsetIndex = OutOffsets.Num();
}
OutOffsets.Add(FIntPoint(OffsetX, OffsetY));
++NumOffsets;
}
}
if (OutCentreOffsetIndex == INDEX_NONE || OutOffsets.IsEmpty())
{
return Refuse(OutReason, TEXT("The player support patch has no footprint samples."));
}
return true;
}
bool IsWalkableSupportPatch(
const TArray<FIntPoint>& SupportOffsets,
const TArray<float>& SupportHeights,
const TArray<uint8>& bSupported,
int32 CentreOffsetIndex,
int32 RequiredSupportCount,
float MinimumWalkableNormalZ,
int32& OutSupportCount,
float& OutSupportHeight,
int32& OutSupportAnchorZ,
const TArray<int32>& SupportAnchorZs)
{
OutSupportCount = 0;
OutSupportHeight = -FLT_MAX;
OutSupportAnchorZ = INDEX_NONE;
if (!bSupported.IsValidIndex(CentreOffsetIndex)
|| bSupported[CentreOffsetIndex] == 0u)
{
return false;
}
for (int32 Index = 0; Index < SupportOffsets.Num(); ++Index)
{
if (bSupported[Index] == 0u)
{
continue;
}
++OutSupportCount;
if (SupportHeights[Index] > OutSupportHeight
|| (SupportHeights[Index] == OutSupportHeight
&& (OutSupportAnchorZ == INDEX_NONE
|| SupportAnchorZs[Index] > OutSupportAnchorZ)))
{
OutSupportHeight = SupportHeights[Index];
OutSupportAnchorZ = SupportAnchorZs[Index];
}
}
if (OutSupportCount < RequiredSupportCount || OutSupportAnchorZ == INDEX_NONE)
{
return false;
}
// The worst pairwise secant is intentionally conservative: an isolated height jump in
// the footprint cannot hide behind an average plane normal. For the default 1.36-voxel
// capsule this checks the centre and the four cardinal support samples.
float MaximumGradient = 0.0f;
for (int32 First = 0; First < SupportOffsets.Num(); ++First)
{
if (bSupported[First] == 0u)
{
continue;
}
for (int32 Second = First + 1; Second < SupportOffsets.Num(); ++Second)
{
if (bSupported[Second] == 0u)
{
continue;
}
const float DX = static_cast<float>(
SupportOffsets[Second].X - SupportOffsets[First].X);
const float DY = static_cast<float>(
SupportOffsets[Second].Y - SupportOffsets[First].Y);
const float HorizontalDistance = FMath::Sqrt(DX * DX + DY * DY);
if (HorizontalDistance <= KINDA_SMALL_NUMBER)
{
continue;
}
MaximumGradient = FMath::Max(
MaximumGradient,
FMath::Abs(SupportHeights[Second] - SupportHeights[First])
/ HorizontalDistance);
}
}
const float SupportNormalZ = 1.0f / FMath::Sqrt(
1.0f + MaximumGradient * MaximumGradient);
return FMath::IsFinite(SupportNormalZ)
&& SupportNormalZ + KINDA_SMALL_NUMBER >= MinimumWalkableNormalZ;
}
bool CapsuleClearAtSupportHeight(
const FSampleGrid& Grid,
const TArray<FPlayerCapsuleStencilRow>& StencilRows,
int32 X,
int32 Y,
float SupportHeight)
{
const float Step = static_cast<float>(Grid.SampleStep);
const float BaseX = Grid.MinX + (static_cast<float>(X) + 0.5f) * Step;
const float BaseY = Grid.MinY + (static_cast<float>(Y) + 0.5f) * Step;
for (const FPlayerCapsuleStencilRow& Row : StencilRows)
{
const float SampleZ = SupportHeight + static_cast<float>(Row.RelativeZ) + 0.5f * Step;
for (const FIntPoint& Offset : Row.HorizontalOffsets)
{
float Density = 0.0f;
if (!SampleGridDensity(
Grid,
FVector(
BaseX + static_cast<float>(Offset.X) * Step,
BaseY + static_cast<float>(Offset.Y) * Step,
SampleZ),
Density)
|| !(Density > 0.0f))
{
return false;
}
}
}
return true;
}
bool IsPointInsideCapsule(
float RelativeZ,
int32 OffsetX,
@@ -594,9 +891,9 @@ namespace VoxelStrateMeasurePrivate
float Radius,
float HalfHeight)
{
// The capsule is upright and its centre is HalfHeight above the top face of the solid
// floor cell. This is a direct capsule test in the fine voxel lattice; it deliberately
// does not infer occupancy from Manhattan distance.
// The capsule is upright and this row is evaluated relative to the resolved support
// surface. This is a direct capsule test in the fine voxel lattice; it deliberately does
// not infer occupancy from Manhattan distance.
const float AxisHalfLength = FMath::Max(0.0f, HalfHeight - Radius);
const float DistanceToAxis = FMath::Max(
FMath::Abs(RelativeZ) - AxisHalfLength, 0.0f);
@@ -682,10 +979,9 @@ namespace VoxelStrateMeasurePrivate
}
}
// The full-height floor rule still requires one clear voxel in rows whose voxel
// centre lies outside the rounded cap (normally the top partial voxel of a 7.04
// voxel player). This is what makes 176 cm a full-height requirement rather than a
// 7-centre-point approximation.
// Keep a centre sample for every row. This matters for the top partial row of a
// capsule whose height is not an integer number of voxels: the full-height clearance
// requirement must not silently become a seven-centre-point approximation.
if (Row.HorizontalOffsets.IsEmpty())
{
Row.HorizontalOffsets.Add(FIntPoint::ZeroValue);
@@ -712,6 +1008,62 @@ namespace VoxelStrateMeasurePrivate
Grid.SampleStep));
}
if (!FMath::IsFinite(Settings.PlayerMaxStepHeightMeters)
|| Settings.PlayerMaxStepHeightMeters < 0.0f)
{
return Refuse(
OutReason,
TEXT("PlayerMaxStepHeightMeters must be finite and non-negative."));
}
if (!FMath::IsFinite(Settings.PlayerWalkableFloorAngleDegrees)
|| Settings.PlayerWalkableFloorAngleDegrees < 0.0f
|| Settings.PlayerWalkableFloorAngleDegrees > 90.0f)
{
return Refuse(
OutReason,
TEXT("PlayerWalkableFloorAngleDegrees must be finite and within [0,90]."));
}
if (!FMath::IsFinite(Settings.PlayerSupportPatchMinCoverageFraction)
|| Settings.PlayerSupportPatchMinCoverageFraction <= 0.0f
|| Settings.PlayerSupportPatchMinCoverageFraction > 1.0f)
{
return Refuse(
OutReason,
TEXT("PlayerSupportPatchMinCoverageFraction must be within (0,1]."));
}
const double MaxStepHeightVoxelsReal =
static_cast<double>(Settings.PlayerMaxStepHeightMeters)
/ static_cast<double>(FVoxelPlayerCapsuleConstants::VoxelSizeMeters);
constexpr double MaxSupportSearchHeightVoxels = 4096.0;
if (!FMath::IsFinite(MaxStepHeightVoxelsReal)
|| MaxStepHeightVoxelsReal < 0.0
|| MaxStepHeightVoxelsReal > MaxSupportSearchHeightVoxels)
{
return Refuse(
OutReason,
TEXT("PlayerMaxStepHeightMeters exceeds the bounded support search."));
}
const int32 MaxDownwardSearchCells = static_cast<int32>(
FMath::CeilToDouble(MaxStepHeightVoxelsReal));
const float MaxStepHeightVoxels = static_cast<float>(MaxStepHeightVoxelsReal);
const float MinimumWalkableNormalZ = FMath::Cos(FMath::DegreesToRadians(
Settings.PlayerWalkableFloorAngleDegrees));
TArray<FIntPoint> SupportOffsets;
int32 CentreOffsetIndex = INDEX_NONE;
if (!BuildPlayerSupportPatchOffsets(
Settings, SupportOffsets, CentreOffsetIndex, OutReason))
{
return false;
}
const int32 RequiredSupportCount = FMath::Clamp(
FMath::CeilToInt(
Settings.PlayerSupportPatchMinCoverageFraction
* static_cast<float>(SupportOffsets.Num())),
1,
SupportOffsets.Num());
TArray<FPlayerCapsuleStencilRow> StencilRows;
if (!BuildPlayerCapsuleStencil(Settings, StencilRows, OutReason))
{
@@ -719,6 +1071,12 @@ namespace VoxelStrateMeasurePrivate
}
OutPlayerFit.Init(0u, Grid.CellCount);
TArray<float> SupportHeights;
SupportHeights.SetNumUninitialized(SupportOffsets.Num());
TArray<int32> SupportAnchorZs;
SupportAnchorZs.SetNumUninitialized(SupportOffsets.Num());
TArray<uint8> bSupported;
bSupported.Init(0u, SupportOffsets.Num());
for (int32 Z = 1; Z < Grid.NumZ; ++Z)
{
for (int32 Y = 0; Y < Grid.NumY; ++Y)
@@ -726,44 +1084,95 @@ namespace VoxelStrateMeasurePrivate
for (int32 X = 0; X < Grid.NumX; ++X)
{
const int32 Current = Grid.Index(X, Y, Z);
if (Grid.Air[Current] == 0u
|| Grid.Air[Grid.Index(X, Y, Z - 1)] != 0u)
// This is the free-pose candidate. Its support is resolved independently
// below, so an air cell one or two voxels above a surface can be lowered to
// that surface when the character's named step height permits it.
if (Grid.Air[Current] == 0u)
{
continue;
}
bool bFits = true;
for (const FPlayerCapsuleStencilRow& Row : StencilRows)
int32 NumSupported = 0;
for (int32 SupportIndex = 0;
SupportIndex < SupportOffsets.Num();
++SupportIndex)
{
const int32 TargetZ = Z + Row.RelativeZ;
if (TargetZ < 0 || TargetZ >= Grid.NumZ)
const FIntPoint& Offset = SupportOffsets[SupportIndex];
const int32 SupportX = X + Offset.X;
const int32 SupportY = Y + Offset.Y;
bSupported[SupportIndex] = 0u;
if (SupportX < 0 || SupportX >= Grid.NumX
|| SupportY < 0 || SupportY >= Grid.NumY)
{
bFits = false;
break;
continue;
}
for (const FIntPoint& Offset : Row.HorizontalOffsets)
float SurfaceHeight = 0.0f;
int32 SurfaceAnchorZ = INDEX_NONE;
if (FindSupportSurfaceBelow(
Grid,
SupportX,
SupportY,
Z,
MaxDownwardSearchCells,
MaxStepHeightVoxels,
SurfaceHeight,
SurfaceAnchorZ))
{
const int32 TargetX = X + Offset.X;
const int32 TargetY = Y + Offset.Y;
if (TargetX < 0 || TargetX >= Grid.NumX
|| TargetY < 0 || TargetY >= Grid.NumY
|| Grid.Air[Grid.Index(TargetX, TargetY, TargetZ)] == 0u)
{
bFits = false;
break;
}
}
if (!bFits)
{
break;
bSupported[SupportIndex] = 1u;
SupportHeights[SupportIndex] = SurfaceHeight;
SupportAnchorZs[SupportIndex] = SurfaceAnchorZ;
++NumSupported;
}
}
if (bFits)
if (NumSupported < RequiredSupportCount
|| bSupported[CentreOffsetIndex] == 0u)
{
OutPlayerFit[Current] = 1u;
++OutNumPlayerFitCells;
continue;
}
int32 SupportCount = 0;
float SupportHeight = 0.0f;
int32 SupportAnchorZ = INDEX_NONE;
if (!IsWalkableSupportPatch(
SupportOffsets,
SupportHeights,
bSupported,
CentreOffsetIndex,
RequiredSupportCount,
MinimumWalkableNormalZ,
SupportCount,
SupportHeight,
SupportAnchorZ,
SupportAnchorZs))
{
continue;
}
// Resolve the pose to the highest coherent support in the patch and run the
// full capsule stencil again there. The support search alone is not allowed
// to turn a ledge or an overhang into a fit result. Keep the original free
// pose cell in the mask: every accepted pose is a valid point in the
// existing 6-neighbour traversal graph, and retaining the bounded vertical
// band is what lets adjacent gently sloped surfaces overlap instead of
// becoming diagonal, disconnected floor anchors.
if (!CapsuleClearAtSupportHeight(
Grid, StencilRows, X, Y, SupportHeight))
{
continue;
}
if (SupportAnchorZ < 1 || SupportAnchorZ >= Grid.NumZ)
{
continue;
}
if (OutPlayerFit[Current] != 0u)
{
continue;
}
OutPlayerFit[Current] = 1u;
++OutNumPlayerFitCells;
}
}
}
@@ -1341,8 +1750,8 @@ namespace VoxelStrateMeasurePrivate
if (EligibilityMask != nullptr)
{
// A player-fit route is deliberately a graph over floor anchors, not a second air
// route with a coarse-cell illusion. The mask was built at SampleStep=1 and every
// A player-fit route is deliberately a graph over supported free poses, not a second
// air route with a coarse-cell illusion. The mask was built at SampleStep=1 and every
// edge below is between adjacent player-fitting cells.
for (int32 PathIndex = 0; PathIndex < Path.Num(); ++PathIndex)
{
@@ -1797,7 +2206,7 @@ namespace VoxelStrateMeasurePrivate
if (!BuildSampleGrid(Generator, Manager, Sampler, StrateIndex,
ExplicitBottomWorldZ, ExplicitTopWorldZ,
ExplicitBoundarySealThickness, bUseExplicitBounds,
Settings, false, Grid, RefusalReason))
Settings, bPlayerFitRestricted, Grid, RefusalReason))
{
if (OutDiagnostics != nullptr)
{
@@ -1909,7 +2318,8 @@ FVoxelStrateMetrics VF_MeasureStrate(
VoxelStrateMeasurePrivate::FSampleGrid Grid;
if (!VoxelStrateMeasurePrivate::BuildSampleGrid(
&Generator, &Manager, nullptr, StrateIndex, 0, 0, 0.0f, false,
Settings, OutSampleGrid != nullptr, Grid, Result.RefusalReason))
Settings, OutSampleGrid != nullptr || Settings.SampleStep == 1,
Grid, Result.RefusalReason))
{
return Result;
}
@@ -1953,7 +2363,8 @@ FVoxelStrateMetrics VF_MeasureStrateWithSampler(
VoxelStrateMeasurePrivate::FSampleGrid Grid;
if (!VoxelStrateMeasurePrivate::BuildSampleGrid(
nullptr, nullptr, &Sampler, INDEX_NONE, StrateBottomWorldZ, StrateTopWorldZ,
BoundarySealThickness, true, Settings, OutSampleGrid != nullptr,
BoundarySealThickness, true, Settings,
OutSampleGrid != nullptr || Settings.SampleStep == 1,
Grid, Result.RefusalReason))
{
return Result;