acdream/docs/research/2026-08-07-ad55-sledding-constant-byte-decode.md
Erik 52aea775b9
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
test(physics): AP-157 measured — CylHeight half retired, sorting-sphere half proven collision-unreachable; AD-55 byte-decoded
Campaign S S1A, both outcomes the measure-first rule exists for.

AP-157's CylHeight half is RETIRED as a non-divergence: retail's own
cylsphere overload (CObjCell::find_cell_list @0x0052b9f0) copies
localtoglobal(low_pt) + radius per cylsphere, capped at 10, and never
reads height — retail collapses a cylsphere to a base-point sphere
exactly as acdream does.

The sorting-sphere half measured REAL against retail's registration set
— 1,812 of 3,343 evaluated Setups (54%) fail containment at 1 mm, worst
shortfall 18.135 m — and then PROVEN collision-unreachable: for this
branch the flood spheres and the collision-test geometry are the same
per-part Sphere list, so every omitted cell is one the entity's test
geometry cannot reach, and retail's wider sorting-sphere registrations
are narrow-phase rejects on retail too. Fix deferred to the next
bake-schema revision rather than performing Slice I3 surgery for zero
behavioural delta. The measurement test stays in the tree as the
permanent record (population cross-checked against the dispatch test's
independently-committed constants: 3,506 = 3,605 - 99).

AD-55 is byte-decoded and RESOLVED against our constant: the binary
loads qword [0x007c6b28] = pi/18 exactly and executes FCOS — retail's
Sledding flatness threshold is cos(10 deg) = 0.984808. Our 0.99999536f
is cos(0.17453 DEGREES): the radian literal misread as degrees, which
makes the object-friction arm unreachable on real terrain (nothing is
flatter than 0.175 deg). Evidence note carries the full instruction
listing and the polarity of the test ah,0x41 / jp idiom; the one-line
fix + conformance test is S5, queued behind the running implementation
slice for build-slot reasons.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 00:57:36 +02:00

3.4 KiB

AD-55 resolved by byte-decode — the Sledding flatness threshold is cos(10°), and our constant is a unit slip

Date: 2026-08-07 (overnight). Method: reference_pe_byte_decode — raw bytes from the PDB-paired v11.4186 binary (check_exe_pdb.py → MATCH, CodeView GUID 9e847e2f-777c-4bd9-886c-22256bb87f32), not the decomp text.

The question AD-55 filed

PhysicsBody.calc_friction's Sledding near-flat branch compares GroundNormal.Z > 0.99999536f (≈0.175° from flat). The raw decomp of CPhysicsObj::calc_friction @0x0050ee70 instead shows __fcos(0.17453292519943295) — cos(10°) ≈ 0.984808 — compared against contact_plane.N.z. One of the two had to be a decode artifact.

The bytes @0x0050ef53 (verbatim from the binary)

d9 86 38 01 00 00    fld   dword [esi+0x138]      ; contact_plane.N.z
dd 05 28 6b 7c 00    fld   qword [0x007c6b28]     ; the constant
d9 ff                fcos
de d9                fcompp                        ; cos(const) vs N.z
df e0                fnstsw ax
f6 c4 41             test  ah, 0x41
7a 0a                jp    +0x0a                   ; skip the friction load
8b 86 bc 00 00 00    mov   eax, [esi+0xbc]        ; this->friction
89 44 24 04          mov   [esp+4], eax
d9 44 24 04          fld   dword [esp+4]

qword [0x007c6b28] = 0.17453292519943295 = π/18 exactly (verified by direct read at the mapped file offset). The binary genuinely executes FCOS at runtime — the compiler did not fold it — so the threshold retail compares against N.z is cos(π/18 rad) = cos(10°) = 0.984807753....

The verdict

  • The decomp was RIGHT. Our port is wrong. 0.99999536f is cos(0.17453292519943295°) — the radian literal read as degrees and run through a degree→radian cosine. A one-character-class unit slip that survived because nothing gates slope feel numerically.
  • Felt consequence: the branch means "on ground flatter than the threshold, use the object's own friction; on steeper ground (while slow — the arg3 < 6.25 speed² gate at 0x0050ef46 guards this), keep the 0.2 sliding friction." With our constant, "flat" requires < 0.175° — real terrain triangles essentially never qualify, so the object-friction arm of Sledding is unreachable in practice and slow movers keep sliding friction on gentle slopes retail treats as flat. Ice-feel in exactly the S4/S5 slope-feel family.
  • Fix shape (S5): replace the constant with retail's semantics. Either the folded 0.98480775f with a comment carrying this evidence, or the exact MathF.Cos(MathF.PI / 18f) computed once — prefer the folded constant + comment, matching how AP-7's 0.25f landed. Polarity must be ported from the test ah,0x41; jp idiom above, not assumed: the friction load is SKIPPED when the jump is taken (cos(10°) > N.z, i.e. steeper than 10°, or unordered), and taken when N.z ≥ cos(10°). Verify our branch's existing polarity against this before changing only the constant.
  • The speed² gates in the same function — 1.5625 (= 1.25²) at 0x0050ef24 and 6.25 (= 2.5²) at 0x0050ef46 — matched our port already at AP-7 and are untouched.

Bookkeeping owed at the fix

Retire AD-55 (the row's open question is now answered against our constant); conformance test pinning 0.98480775f + the ported polarity; S4/S5's slope-feel session covers the felt change. Until the fix lands, the register row stands corrected by this note.