Revert "fix(G.3a): hydrate EnvCell physics + visibility independent of render mesh (#133)"

This reverts commit ab050a015f.
This commit is contained in:
Erik 2026-06-13 18:05:36 +02:00
parent 3238f1fde4
commit e7058caa79

View file

@ -5630,15 +5630,15 @@ public sealed class GameWindow : IDisposable
// as WorldEntity records below — they have real GfxObj MeshRefs that work
// fine; EnvCellRenderer.RegisterCell receives an empty staticObjects list.
var cellSubMeshes = AcDream.Core.Meshing.CellMesh.Build(envCell, cellStruct, _dats);
if (cellSubMeshes.Count > 0)
{
_pendingCellMeshes[envCellId] = cellSubMeshes;
// The cell transforms and the physics + visibility hydration are
// INDEPENDENT of whether the cell has drawable geometry. Retail
// couples neither collision nor portal visibility to a render mesh.
// Keep the small render lift out of physics; retail BSP contact
// planes use the EnvCell origin verbatim. The lift constant is
// shared with every draw-space consumer of portal polygons
// (OutsideView gate, seal/punch fans) — see
// PortalVisibilityBuilder.ShellDrawLiftZ (#130).
// Keep the small render lift out of physics; retail BSP
// contact planes use the EnvCell origin verbatim. The lift
// constant is shared with every draw-space consumer of
// portal polygons (OutsideView gate, seal/punch fans) —
// see PortalVisibilityBuilder.ShellDrawLiftZ (#130).
var physicsCellOrigin = envCell.Position.Origin + lbOffset;
var cellOrigin = physicsCellOrigin + new System.Numerics.Vector3(
0f, 0f, AcDream.App.Rendering.PortalVisibilityBuilder.ShellDrawLiftZ);
@ -5649,32 +5649,6 @@ public sealed class GameWindow : IDisposable
System.Numerics.Matrix4x4.CreateFromQuaternion(envCell.Position.Orientation) *
System.Numerics.Matrix4x4.CreateTranslation(physicsCellOrigin);
// G.3a (#133) hydration decouple: BuildLoadedCell + CacheCellStruct
// were previously gated behind `cellSubMeshes.Count > 0`, which
// silently dropped collision (CellTransit.GetCellStruct -> null ->
// fall through floor) and the visibility node for any geometry-less
// collision cell. CacheCellStruct self-gates on a null PhysicsBSP
// (PhysicsDataCache), so this is safe for cells with no physics.
// Note CacheCellStruct DOES register every resolved cell into the UCG
// CellGraph unconditionally (before that BSP gate), so a geometry-less
// cell now enters the graph — but it never enters the _cellStruct BSP
// dictionary that membership/placement resolve through, so the player
// can never be rooted in such a cell. The added graph entry is inert.
//
// BuildLoadedCell uses the PHYSICS (unlifted) transform. The +0.02 m
// render lift above is a DRAW concern (shell z-fighting vs terrain);
// feeding it into the visibility graph shifted every HORIZONTAL portal
// plane 2 cm up, side-culling decks/landings (#119-residual,
// TowerAscentReplayTests.CapturedTopOfStairs_*). Vertical doorways were
// immune (the lift slides their planes along themselves).
BuildLoadedCell(envCellId, envCell, cellStruct, physicsCellOrigin, physicsCellTransform);
_physicsDataCache.CacheCellStruct(envCellId, envCell, cellStruct, physicsCellTransform);
// Render registration only when the cell actually has drawable submeshes.
if (cellSubMeshes.Count > 0)
{
_pendingCellMeshes[envCellId] = cellSubMeshes;
// Phase A8: register the cell with EnvCellRenderer for rendering.
// staticObjects is empty — cell stabs continue as separate WorldEntity
// records via the dispatcher (see lines below for the unchanged stab path).
@ -5687,6 +5661,25 @@ public sealed class GameWindow : IDisposable
cellWorldPosition: cellOrigin,
cellRotation: envCell.Position.Orientation,
staticObjects: System.Array.Empty<(uint, System.Numerics.Vector3, System.Numerics.Quaternion, bool, System.Numerics.Matrix4x4)>());
// Step 4: build LoadedCell for portal visibility — with the
// PHYSICS (unlifted) transform. The +0.02 m render lift above
// is a DRAW concern (shell z-fighting vs terrain); feeding it
// into the visibility graph shifted every HORIZONTAL portal
// plane 2 cm up, putting an eye standing on a deck/landing
// 1020 mm BELOW the lifted plane — outside the side test's
// ±10 mm in-plane window — so the cell behind the portal was
// side-culled: the tower-top staircase vanish + roof flap
// (#119-residual; captured live at eye z=126.803 vs the
// 010A→0107 plane at 126.80, reproduced ONLY with the lift in
// TowerAscentReplayTests.CapturedTopOfStairs_*). Vertical
// doorways were immune (the lift slides their planes along
// themselves), which is why this hit exactly stairs, decks,
// and cellar mouths.
BuildLoadedCell(envCellId, envCell, cellStruct, physicsCellOrigin, physicsCellTransform);
// Cache CellStruct physics BSP for indoor collision (UNCHANGED).
_physicsDataCache.CacheCellStruct(envCellId, envCell, cellStruct, physicsCellTransform);
}
}
}