feat(A): port find_visible_child_cell + AdjustPosition (Render Residual A primitives)

The two Core physics primitives retail's SmartBox::update_viewer calls down into,
ported verbatim (TDD, 7 new tests):

- CellTransit.FindVisibleChildCell (CEnvCell::find_visible_child_cell, pc:311397):
  return the cell whose cell-BSP point_in_cell contains a world point — start cell
  first, then (stab-list mode) the start's VisibleCellIds or (portal mode) its
  direct portals. Sibling of FindCellList. Mirrors FindCellList's null-CellBSP skip
  (CellTransit.cs:518) so a cell lacking hydrated CellBSP doesn't spuriously claim
  every point via PointInsideCellBsp's null-node "inside" default.

- PhysicsEngine.AdjustPosition (CPhysicsObj::AdjustPosition, pc:280009): resolve a
  point's cell from a seed. Indoor (>=0x100) → FindVisibleChildCell(stab-list);
  outdoor → landcell snap (same grid lookup as ResolveCellId). The seen_outside
  sub-fallback is deferred (off the cottage/cellar path; spec §6).

Both are unwired into any production path — they land the machinery update_viewer's
start-cell + fallback 1 need (and that residual C also needs). The App SweepEye
orchestration that calls them lands next.

Decomp-faithful per the live-capture finding: A's V1 sweep already contains the eye
(eyeInRoot=Y 99.75%, never void); this completes A as a verbatim port. Spec:
docs/superpowers/specs/2026-06-05-residual-a-camera-collision-design.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-05 10:56:16 +02:00
parent 0ffc3f5be9
commit 5177b54bbe
4 changed files with 336 additions and 0 deletions

View file

@ -343,6 +343,69 @@ public static class CellTransit
}
}
/// <summary>
/// Verbatim port of <c>CEnvCell::find_visible_child_cell</c>
/// (<c>acclient_2013_pseudo_c.txt:311397</c>). Returns the cell whose cell-BSP
/// <c>point_in_cell</c> contains <paramref name="worldPoint"/>, checking the
/// start cell first (:311402), then — when <paramref name="useStabList"/> is
/// true (retail <c>arg3 != 0</c>, :311444) — the start's <c>stab_list</c>
/// (<see cref="CellPhysics.VisibleCellIds"/>), else (<c>arg3 == 0</c>, :311411)
/// its direct portal neighbours. Returns 0 when no cell contains the point
/// (retail <c>return 0</c> at :311469).
///
/// <para>
/// Sibling of <see cref="FindCellList"/> (retail <c>find_cell_list</c>) — both
/// resolve membership from the cell graph via <see cref="BSPQuery.PointInsideCellBsp"/>.
/// Used by <c>CPhysicsObj::AdjustPosition</c> (pc:280028, <c>arg5 = 1</c> →
/// stab-list mode) to seat the camera sweep's start cell at the head-pivot.
/// </para>
///
/// <para>
/// acdream adaptation (matches <see cref="FindCellList"/> at line 518): a cell
/// with no hydrated <see cref="CellPhysics.CellBSP"/> cannot run
/// <c>point_in_cell</c>, so it is treated as NOT containing the point (skipped),
/// rather than letting <see cref="BSPQuery.PointInsideCellBsp"/>'s null-node
/// "inside" default make it spuriously claim every point.
/// </para>
/// </summary>
public static uint FindVisibleChildCell(
PhysicsDataCache cache, uint startCellId, Vector3 worldPoint, bool useStabList)
{
var start = cache.GetCellStruct(startCellId);
if (start is null) return 0u;
// this->point_in_cell(point) → return this (:311402-311405)
if (PointInCell(start, worldPoint)) return startCellId;
if (useStabList)
{
// arg3 != 0 → iterate stab_list, GetVisible + point_in_cell (:311444-311465)
foreach (uint id in start.VisibleCellIds)
if (PointInCell(cache.GetCellStruct(id), worldPoint)) return id;
}
else
{
// arg3 == 0 → iterate direct portals, GetOtherCell + point_in_cell (:311411-311434)
foreach (var portal in start.Portals)
if (PointInCell(cache.GetCellStruct(portal.OtherCellId), worldPoint)) return portal.OtherCellId;
}
return 0u;
}
/// <summary>
/// <c>CEnvCell::point_in_cell</c> (cell-BSP vtable[0x84]) against a world point:
/// transform to the cell's local frame, then <see cref="BSPQuery.PointInsideCellBsp"/>.
/// A cell with no hydrated <see cref="CellPhysics.CellBSP"/> returns false (see
/// <see cref="FindVisibleChildCell"/>'s adaptation note).
/// </summary>
private static bool PointInCell(CellPhysics? cell, Vector3 worldPoint)
{
if (cell?.CellBSP?.Root is null) return false;
var local = Vector3.Transform(worldPoint, cell.InverseWorldTransform);
return BSPQuery.PointInsideCellBsp(cell.CellBSP.Root, local);
}
/// <summary>
/// Top-level cell-tracking driver, ported from retail's
/// <c>CObjCell::find_cell_list</c> (sphere variant).