probe(physics): ACDREAM_PROBE_SUPPORT + ACDREAM_WIRE_MESH — separate #337's three candidates

The user is wedged at the top of Neftet rock plateaus, jumps sink into the
mesh, and a corpse falls straight through. ACDREAM_PROBE_REACH already ruled
out its own domain: blocked=0, every candidate tested-ok. Three candidates
remain — terrain support, a collision mesh not where its visual is, or the
transition wedging on an unobstructed path.

ACDREAM_PROBE_RESOLVE alone cannot separate them. It prints a three-value
contact-plane token, no plane normal, no plane height, no terrain sample and
no plane provenance, so all three produce the same line. Two additions:

[support] — one line per resolve for EVERY body, not just the player. A corpse
is a plain physics body with no player-specific logic, so its fall-through is
the cheapest available control on "movement code vs geometry data", and it is
invisible to any player-filtered probe. The line samples the outdoor terrain
INDEPENDENTLY at the body's own out-XY and prints the contact plane's own
height at that same XY. Two heights at one point make support=terrain /
object / none a measurement rather than an inference, and cpSrc= names the
site that asserted the plane so provenance and classification cross-check.

[geom] — once per GfxObj that comes near a mover: the object's physics-BSP
vertex cloud against its visual mesh AABB in the same local frame, through the
same prepared accessors the resolver queries. verdict=coincident REFUTES the
working hypothesis for that object outright; no-physics-bsp / empty-physics-bsp
/ displaced / extent-mismatch each name a specific data defect. Built to
refute, not to confirm — two diagnoses on this defect's lineage have already
been refuted by measurement.

ACDREAM_WIRE_MESH upgrades the existing F2 overlay, which drew a broadphase
proxy cylinder for BSP objects and so could not answer the question at all, to
the real physics-BSP polygon edges (cyan) beside the visual mesh box (magenta)
and the terrain surface (yellow). Own class per code-structure rule 1.

The provenance latch lives on PhysicsDiagnostics, not on CollisionInfo. Two
fields there first — the obvious home — broke the flat/graph differential
referee and the scratch-reset poison test, both of which compare CollisionInfo
member-for-member. Teaching either to skip a member is a one-line green fix
that puts a permanent hole in a referee whose whole job is comparing
everything. Captured as feedback_probe_state_off_compared_types.

Seven tests cover the support classifier's boundaries: a wrong classifier does
not fail to answer, it answers confidently wrong.

Gates: Release build 0 errors; complete suite 11,225 passed / 4 skipped / 0
failed from a cleaned tree — baseline 11,218/4/0 plus exactly the seven new
tests, skips unchanged.

Issue #337 filed with the symptom set, what is ruled out, and a table of what
each possible output means. All of this is TEMPORARY and recorded for
stripping with the physics-probe family.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-06 19:49:59 +02:00
parent 13fcf38138
commit 49a7e90652
9 changed files with 1376 additions and 1 deletions

View file

@ -783,4 +783,52 @@ public static class RenderingDiagnostics
/// </summary>
public static string? FrameHistoryPath { get; } =
Environment.GetEnvironmentVariable("ACDREAM_FRAME_HISTORY");
// ── #337 collision-mesh wireframe (2026-08-06 — TEMPORARY) ──────────────
//
// The F2 collision overlay already existed, but for a BSP object it drew a
// proxy cylinder sized from the REGISTERED BROADPHASE RADIUS. That shows
// where the collision system thinks the object roughly is; it cannot show
// where the collision SURFACES are, which is the only thing that answers
// "is the collision geometry where the visual geometry is". The knobs
// below turn F2 into the real answer: the actual physics-BSP polygon
// edges, in world space, next to the same object's visual mesh box.
//
// Off by default, so F2 keeps its old cheap behaviour for anyone who wants
// it and this costs nothing until asked for.
/// <summary>
/// When true, the F2 collision overlay draws each nearby object's REAL
/// physics-BSP polygon edges (cyan) and, beside them, the same object's
/// visual mesh bounding box (magenta), plus the terrain triangle under the
/// player (yellow). Any separation between the cyan surfaces and the
/// object you can see is the "collision is not where the visual is"
/// defect, read directly off the screen instead of inferred from a log.
/// Initial state from <c>ACDREAM_WIRE_MESH=1</c>.
/// TEMPORARY — strip with the #337 probe family.
/// </summary>
public static bool CollisionMeshWireframeEnabled { get; set; } =
Environment.GetEnvironmentVariable("ACDREAM_WIRE_MESH") == "1";
/// <summary>
/// Radius in metres around the player within which
/// <see cref="CollisionMeshWireframeEnabled"/> resolves polygon geometry.
/// A whole landblock of rock is far more geometry than a line list wants;
/// 30 m covers everything you can wedge against. Override with
/// <c>ACDREAM_WIRE_RADIUS=&lt;metres&gt;</c>.
/// </summary>
public static float CollisionMeshWireframeRadius { get; set; } =
ParsePositiveFloat(
Environment.GetEnvironmentVariable("ACDREAM_WIRE_RADIUS"),
fallback: 30f);
private static float ParsePositiveFloat(string? raw, float fallback)
=> float.TryParse(
raw,
System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture,
out float value)
&& value > 0f
? value
: fallback;
}