From a36369d8ca316dabe42fcfdc8cbef7f0c1fdd9c2 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 5 May 2026 18:16:29 +0200 Subject: [PATCH] diag(physics): #42 add ACDREAM_AIRBORNE_DIAG [SWEEP] trace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1 of #42 root-cause investigation per the handoff doc. We A/B confirmed (commit b37b713) that the ~1m XY drift on retail- observed stationary jumps comes from inside ResolveWithTransition when the per-tick airborne sweep runs (CellId fix at GameWindow.cs 3467). What we don't yet know: whether the drift originates in H1 (initial-overlap depenetration along a tilted-terrain normal), H2 (step-down probe firing despite isOnGround=false), or H3 (EdgeSlide on near-vertical motion grazing a wall). This diagnostic gates a one-line Console trace on ACDREAM_AIRBORNE_DIAG=1 AND !isOnGround so it doesn't pollute grounded movement, and prints: [SWEEP] airborne pre=(...) target=(...) post=(...) cell=PRE->POST ok=BOOL deltaXY=(dx,dy) cp=valid|none cpN=(nx,ny,nz) deltaXY = post - target — for a clean stationary +Z jump we expect (0,0). Non-zero with cp=valid and a tilted cpN confirms H1; non-zero direction tracking actor facing instead of terrain orientation points to H2/H3. Code-walk findings recorded for the next investigation pass: - K-fix7 already prevents seeding ContactPlane on entry for airborne (PhysicsEngine.cs:493-519), so step 0's AdjustOffset cannot consume a stale plane. - BUT ValidateWalkable can still SET ContactPlane during step 0's collision pass via the "below plane" branch (TransitionTypes.cs 1320-1352) when sphere lowPoint dips below the tilted terrain triangle. Step 1's AdjustOffset would then consume that fresh plane and the "moving away from contact plane" branch (TransitionTypes.cs:1749-1754) projects the +Z offset along the slope normal, redirecting Z motion into XY. - Step-down branch is correctly gated on oi.Contact (matches retail CTransition::transitional_insert at named-retail acclient_2013_pseudo_c.txt:273249, "(state & 1) == 0" returns OK without firing step-down). - Retail's IS_VIEWER_OI=0x4 branch in OBJECTINFO::validate_walkable (acclient.h:6185) is never set anywhere in the named decomp, so the airborne path runs the same code in retail as in acdream. User repros at flat plaza / east hillside / north hillside; the direction-correlation of deltaXY with local terrain orientation identifies which hypothesis is firing. Build green; 13 PhysicsEngine tests green. No behavior change when ACDREAM_AIRBORNE_DIAG is unset. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/AcDream.Core/Physics/PhysicsEngine.cs | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/AcDream.Core/Physics/PhysicsEngine.cs b/src/AcDream.Core/Physics/PhysicsEngine.cs index da6711e..5868fe0 100644 --- a/src/AcDream.Core/Physics/PhysicsEngine.cs +++ b/src/AcDream.Core/Physics/PhysicsEngine.cs @@ -625,6 +625,33 @@ public sealed class PhysicsEngine bool collisionNormalValid = ci.CollisionNormalValid; Vector3 collisionNormal = ci.CollisionNormal; + // #42 diagnostic (2026-05-05): trace airborne sweeps to identify the + // source of the ~1m XY drift on retail-observed stationary jumps. + // Gated on ACDREAM_AIRBORNE_DIAG=1 and !isOnGround. One line per + // resolve call. deltaXY = post - target tells us how much the sweep + // diverged from the requested target; for a clean stationary +Z + // jump we expect (0,0). cp=valid with a tilted normal would confirm + // H1 (initial-overlap depenetration → next-step AdjustOffset projects + // the +Z offset along a non-+Z normal). User repros at flat plaza / + // east hillside / north hillside; if drift direction tracks terrain + // orientation, H1 is the cause; if it tracks actor facing, H2 / H3. + if (!isOnGround + && Environment.GetEnvironmentVariable("ACDREAM_AIRBORNE_DIAG") == "1") + { + var post = sp.CheckPos; + float dx = post.X - targetPos.X; + float dy = post.Y - targetPos.Y; + string cpInfo = ci.ContactPlaneValid + ? $"valid cpN=({ci.ContactPlane.Normal.X:F3},{ci.ContactPlane.Normal.Y:F3},{ci.ContactPlane.Normal.Z:F3})" + : "none"; + Console.WriteLine( + $"[SWEEP] airborne pre=({currentPos.X:F3},{currentPos.Y:F3},{currentPos.Z:F3}) " + + $"target=({targetPos.X:F3},{targetPos.Y:F3},{targetPos.Z:F3}) " + + $"post=({post.X:F3},{post.Y:F3},{post.Z:F3}) " + + $"cell={cellId:X8}->{sp.CheckCellId:X8} ok={ok} " + + $"deltaXY=({dx:F3},{dy:F3}) cp={cpInfo}"); + } + if (ok) { bool onGround = ci.ContactPlaneValid