From 3ffe1e44f698f368a7eb18fb15076e36a2bc5039 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 19 May 2026 18:54:10 +0200 Subject: [PATCH] =?UTF-8?q?fix(physics):=20Phase=202=20=E2=80=94=20pass=20?= =?UTF-8?q?foot-sphere=20center=20to=20ResolveCellId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Visual test of Phase 2 portal traversal showed walls still didn't block from inside buildings. Diagnosis: ResolveCellId was being called with sp.CheckPos (entity reference, at the feet — world Z=terrain) instead of sp.GlobalSphere[0].Origin (foot sphere center, ~0.5m above terrain). Combined with the +0.02f Z-bump on cached cell origins (for render z-fight prevention), the test position landed at cell-local Z=-0.02 — just below the cell floor — and PointInsideCellBsp correctly reported "outside" for every cell. CheckBuildingTransit never added candidates; player CellId stayed outdoor; indoor cell-BSP collision branch never fired; walls didn't block. Retail's check_building_transit uses sphere.Center (the sphere CENTER, not the entity reference) per the pseudocode at docs/research/acclient_indoor_transitions_pseudocode.md:222-238. Three call sites updated (PhysicsEngine x2 inside ResolveWithTransition; TransitionTypes inside Transition.FindEnvCollisions). Also adds a [check-bldg] diagnostic line to CheckBuildingTransit (gated on the existing ACDREAM_PROBE_INDOOR_BSP flag) so future verification captures show per-portal inside/outside results without needing another diagnostic flag. Spec: docs/superpowers/specs/2026-05-19-indoor-portal-cell-tracking-design.md Co-Authored-By: Claude Opus 4.7 (1M context) --- src/AcDream.Core/Physics/CellTransit.cs | 21 +++++++++++++++++++-- src/AcDream.Core/Physics/PhysicsEngine.cs | 4 ++-- src/AcDream.Core/Physics/TransitionTypes.cs | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/AcDream.Core/Physics/CellTransit.cs b/src/AcDream.Core/Physics/CellTransit.cs index 12bfa0d..564a46f 100644 --- a/src/AcDream.Core/Physics/CellTransit.cs +++ b/src/AcDream.Core/Physics/CellTransit.cs @@ -186,11 +186,28 @@ public static class CellTransit foreach (var portal in building.Portals) { var otherCell = cache.GetCellStruct(portal.OtherCellId); - if (otherCell?.CellBSP?.Root is null) continue; + if (otherCell?.CellBSP?.Root is null) + { + if (PhysicsDiagnostics.ProbeIndoorBspEnabled) + { + string reason = otherCell is null ? "cell not cached" : "CellBSP null"; + Console.WriteLine(System.FormattableString.Invariant( + $"[check-bldg] portal->0x{portal.OtherCellId:X8} skipped: {reason}")); + } + continue; + } // Sphere center in the OTHER cell's local space. var localCenter = Vector3.Transform(worldSphereCenter, otherCell.InverseWorldTransform); - if (BSPQuery.PointInsideCellBsp(otherCell.CellBSP.Root, localCenter)) + bool inside = BSPQuery.PointInsideCellBsp(otherCell.CellBSP.Root, localCenter); + + if (PhysicsDiagnostics.ProbeIndoorBspEnabled) + { + Console.WriteLine(System.FormattableString.Invariant( + $"[check-bldg] portal->0x{portal.OtherCellId:X8} wpos=({worldSphereCenter.X:F3},{worldSphereCenter.Y:F3},{worldSphereCenter.Z:F3}) lpos=({localCenter.X:F3},{localCenter.Y:F3},{localCenter.Z:F3}) inside={inside}")); + } + + if (inside) { candidates.Add(portal.OtherCellId); } diff --git a/src/AcDream.Core/Physics/PhysicsEngine.cs b/src/AcDream.Core/Physics/PhysicsEngine.cs index d9e3633..5061f34 100644 --- a/src/AcDream.Core/Physics/PhysicsEngine.cs +++ b/src/AcDream.Core/Physics/PhysicsEngine.cs @@ -769,7 +769,7 @@ public sealed class PhysicsEngine return new ResolveResult( sp.CheckPos, - ResolveCellId(sp.CheckPos, sphereRadius, sp.CheckCellId), + ResolveCellId(sp.GlobalSphere[0].Origin, sphereRadius, sp.CheckCellId), onGround, collisionNormalValid, collisionNormal); @@ -787,7 +787,7 @@ public sealed class PhysicsEngine uint partialCellId = sp.CheckCellId != 0 ? sp.CheckCellId : cellId; return new ResolveResult( sp.CheckPos, - ResolveCellId(sp.CheckPos, sphereRadius, partialCellId), + ResolveCellId(sp.GlobalSphere[0].Origin, sphereRadius, partialCellId), partialOnGround, collisionNormalValid, collisionNormal); diff --git a/src/AcDream.Core/Physics/TransitionTypes.cs b/src/AcDream.Core/Physics/TransitionTypes.cs index 4a6d696..1deed49 100644 --- a/src/AcDream.Core/Physics/TransitionTypes.cs +++ b/src/AcDream.Core/Physics/TransitionTypes.cs @@ -1181,7 +1181,7 @@ public sealed class Transition Vector3 footCenter = sp.GlobalSphere[0].Origin; float sphereRadius = sp.GlobalSphere[0].Radius; - uint resolvedOutdoorCellId = engine.ResolveCellId(sp.CheckPos, sphereRadius, sp.CheckCellId); + uint resolvedOutdoorCellId = engine.ResolveCellId(sp.GlobalSphere[0].Origin, sphereRadius, sp.CheckCellId); if (resolvedOutdoorCellId != sp.CheckCellId) sp.SetCheckPos(sp.CheckPos, resolvedOutdoorCellId);