fix(physics): S1B — indoor cell membership admits on the part BOX, as retail does (#335, AP-159 narrowed)
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
CellTransit.FindTransitCellsBox ports CEnvCell::find_transit_cells'
part-array overload @0x0052cae0 line-for-line: per-portal x per-part
order, the sphere cheap-reject at F_EPSILON+radius, the box admit whose
"Straddle or crossing-side" rule is exactly retail's `eax != side` under
the PDB Sidedness enum, leads-outside placed AFTER the admit, the
unconditional unloaded-neighbour hint without the sphere overload's
re-test, the destination box_intersects_cell gate with its deliberate
no-break, and add_all_outside_cells after the loop. The box-vs-cell BSP
traversal lands in BOTH representations behind the flat-authoritative
dispatcher with a graph referee whose 20,000 installed comparisons are
pinned by assertion (review F5), zero mismatch.
Dual Opus review: PASS on both lenses. The mandatory D0 pseudocode pass
caught that the contract's own supplementary note misattributed the box
block to the sphere overload — it belongs to a SECOND
check_building_transit overload @0x0052c680, whose portal-side
convention is INVERTED and whose admit differs; the pseudocode doc now
records that trap plus two byte confirmations made at review:
which_side @0x00444720 is strictly > eps for POSITIVE, and
intersect_box's in-plane early exit returns CROSSING(3)
(jp @0x005aa1bc -> mov eax,3), settling review items b1/b2 for the
future bridge porter. The bridge itself stays unported as AP-159's
explicit remainder.
The review also retired #335's severity premise honestly: "over-
inclusive only, never a missed one" is wrong at production shape ratios,
where the box (whole-vertex AABB) legitimately exceeds the sphere
(physics-polygon root sphere). Measured, both populations: rigged
(box << sphere) — 1,520 placements, 978 cells removed, 0 added;
production-ratio (box >= sphere) — 950 placements, 20 removed, 1 ADDED
through the loaded-neighbour gate, which is retail's direction, not a
defect. The no-op guard (review F4) asserts removal is nonzero so an
unwired admit cannot pass silently.
Process note: the implementer authored against this session's worktree
at bec5c69d, 25 commits stale — the recorded worktree-base class. All
six files were byte-identical between bases, the diff transplanted
losslessly, and every verdict-bearing run (referee, direction sweeps,
this clean-room) was re-executed on current main. S2's uncommitted
phase-1 edits were stashed for this landing so the suite verdicts
exactly one changeset.
Also untracks 341-slope-capture.jsonl (an accidental add) and
gitignores it.
Clean-room suite: 11,248 passed / 6 skipped / 0 failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
1b2580be4c
commit
b3e43d22c9
14 changed files with 1600 additions and 4914 deletions
|
|
@ -476,6 +476,51 @@ internal static class FlatBspQuery
|
|||
: true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flat port of retail <c>BSPNODE::box_intersects_cell_bsp</c>
|
||||
/// (0x0053C880). AP-159 / #335 (2026-08-07). Shares
|
||||
/// <see cref="BSPQuery.ClassifyBox"/> for the box-vs-plane classification
|
||||
/// math, exactly like this file's polygon methods share
|
||||
/// <see cref="BSPQuery.PolygonHitsSpherePrecise"/>. See
|
||||
/// docs/research/2026-08-07-ap159-pseudocode.md §3.
|
||||
/// </summary>
|
||||
public static bool BoxIntersectsCellBsp(
|
||||
FlatCellContainmentBsp tree,
|
||||
Vector3 min,
|
||||
Vector3 max)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(tree);
|
||||
return BoxIntersectsCellBsp(tree, tree.RootIndex, min, max);
|
||||
}
|
||||
|
||||
private static bool BoxIntersectsCellBsp(
|
||||
FlatCellContainmentBsp tree,
|
||||
int nodeIndex,
|
||||
Vector3 min,
|
||||
Vector3 max)
|
||||
{
|
||||
if (nodeIndex < 0)
|
||||
return true;
|
||||
|
||||
FlatCellBspNode node = tree.Nodes[nodeIndex];
|
||||
if (node.Type == BSPNodeType.Leaf)
|
||||
return true;
|
||||
|
||||
if (BSPQuery.ClassifyBox(node.SplittingPlane, min, max) ==
|
||||
BSPQuery.PlaneSide.Negative)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return node.PositiveChildIndex >= 0
|
||||
? BoxIntersectsCellBsp(
|
||||
tree,
|
||||
node.PositiveChildIndex,
|
||||
min,
|
||||
max)
|
||||
: true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flat static sphere/polygon overlap shadow query.
|
||||
/// </summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue