Commit graph

5 commits

Author SHA1 Message Date
Erik
987313aa54 knife-edge port: polyClipFinish W=0 eye-plane clip + degenerate-view propagation; EyeInsidePortalOpening rescue DELETED
Ports retail ACRender::polyClipFinish (0x006b6d00, pc:702749) near-eye
semantics into PortalProjection.ProjectToClip - the fundamental fix for
the in-plane portal clip family (climb strobes, tower-top roof/floor
flap while turning; live-corroborated this session: [viewer-diff]
0xAAB30108 strobing 27x mid-climb, whole interior dropping at the top).
Pseudocode: docs/research/2026-06-11-polyclipfinish-w0-clip-pseudocode.md.

Three legs, all decomp-driven:

1. ProjectToClip clips at w >= 0 EXACTLY (was EyePlaneW=1e-4), with
   retail's any-negative-w gate. Boundary intersections land at w == 0
   (homogeneous directions), so a portal the eye is CROSSING yields the
   correct unbounded half-region that the bounded view-region clip cuts
   to the screen. A w=0 vertex cannot survive a bounded region clip
   into the divide (direction fails some edge of any bounded convex
   region); the measure-zero corner case is guarded non-finite->empty.

2. CellView.CanonicalKey keys ALL-COLLINEAR (zero-area) views as their
   snapped segment ("L:" + extremes) instead of rejecting them - retail
   PROPAGATES degenerate views (ClipPortals decomp:433651-433711
   forwards any count!=0 GetClip output, no area gate anywhere), keeping
   the cell behind an exactly-in-plane portal in the draw list (cells
   draw whole; onward floods die naturally). Rejection dropped the
   whole chain for the frame - the parked-eye knife-edge band. Finite
   key space unchanged -> dedup + strict-growth convergence intact.

3. The EyeInsidePortalOpening rescue is DELETED (the T2-documented
   compensation for the 1e-4 divergence) along with EyeStandingPerpDist
   + PointInPoly2D. Empty clip = no flood, period (retail's rule).
   CornerFloodReplay - the gate that REFUTED the previous deletion
   attempt - passes WITHOUT the rescue under the W=0 port.

Harness criterion corrected to retail's rules (it codified the rescue):
cells fully BEHIND the camera are not required (all-behind portals clip
empty in retail); monotone area holds per root regime; the two
manufactured exact-on-plane steps assert root-only (boundary root pick
is ambiguous; the in-plane portal there is ~perpendicular to the gaze =
genuinely off-screen). Build_CollapsedInteriorPortalNearEye test
inverted to pin the retail empty-clip rule (it pinned the rescue).

New pins: eye-crossing portal -> w==0 boundary verts + half-region (not
sliver); gaze-along-plane degenerate view accepted + segment-key dedup;
non-finite guard. Replay harnesses (CornerFloodReplay, Issue120,
TowerAscent, HouseExit, Issue127) all green.

Suites: App 246+1skip / Core 1430+2skip / UI 420 / Net 294.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 21:44:23 +02:00
Erik
dede7e491c #120: CellView containment rejection - the reciprocal ping-pong converges; tripwire firings reproduced + killed
The armed tripwire self-attributed on the re-gate launch
(regate-118-119-launch.log): a pure TWO-CELL reciprocal ping-pong, 64 laps
each - chain root=0xA9B4015C eye=(109.995,37.158,96.249) cells 0162x64
015Cx64, and root=0xA9B3010F eye=(175.771,-107.310,118.814) cells 0103x64
010Fx64 (A9B3 = the hill cottage the user reports going all-transparent on
entry - likely the same mechanism, verify at the next gate).

Mechanism: with the eye within PortalSideEpsilon (+-1 cm; the T2
refuted-to-tighten root-lag tolerance - retail's is 0.0002) of a portal
plane, the in-plane case counts as interior for BOTH cells, so views lap
A->B->A...; each lap re-clips through two near-edge-on apertures whose
intersection numerics wobble by more than CellView's 1e-3 dedup grid, so
every lap keys as NEW and the in-place growth recurses to the depth-128
failsafe. The prior convergence sweeps could not reproduce because they
only load the corner building 0x016F-0x0175 - both firing pairs are
outside that set. Issue120ReciprocalPingPongTests loads the full
landblock's interior cells and drives the +-epsilon window directly:
deterministic firings + 65-polygon CellView piles pre-fix.

Fix (the handoff's own predicted class - dedup admitting near-duplicates
per lap, NOT a limit tune): CellView.Add rejects a polygon CONTAINED in
one already stored (convex edge test, DedupGridNdc slack). A round-trip
re-emission is, in exact arithmetic, a SUBSET of the polygon that
originated it - containment rejection makes union growth strictly
area-increasing, so no new visible area means no propagation. Bonus:
back-emission into a full-screen view (the root cell) now always rejects.
The corner-flood completeness pins stay green - no real region is dropped.

Suites: App 236 (232+4), Core 1419+2skip, UI 420, Net 294.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 17:32:21 +02:00
Erik
dac8f6ad1f fix(render): §4 flood strobe — homogeneous reciprocal clip + collinear-aware region dedup
THE BUG (pinned deterministically by the new CornerFloodReplayTests harness — real
Holtburg cells, captured corner-press scenario): a smooth 2 cm/step monotonic eye
sweep across the 0172↔0173↔0171 doorway produced a NON-monotonic flood — on ~10 of
61 steps the player's room (0172) vanished from the flood entirely or collapsed to
a sub-pixel sliver, taking its downstream chain (016F, the outside view) with it.
Live, those isolated frames are the §4 background strobe: openings/passages flash
the clear color during transitions, and the corner press shows background at the
angles that park the eye near the doorway plane.

TWO root causes, both fixed:

1. ApplyReciprocalClip ran the reciprocal portal polygon through the legacy
   divide-first ProjectToNdc + 2D Intersect path, justified by "the reciprocal is
   never near the eye." That assumption is exactly false at doorways/corners: the
   reciprocal IS the same opening whose plane the eye presses against (2-60 cm).
   ProjectToNdc's MinW=0.05 eye-clip + side-plane clip + divide is knife-edge
   there — 2 cm eye moves flipped its output between a no-op and a duplicated-
   vertex hairline that ground the healthy region down to <3 distinct vertices.
   FIX: route the reciprocal through the SAME homogeneous pipeline as the forward
   clip (ProjectToClip + ClipToRegion) — which is what retail does:
   PView::OtherPortalClip (decomp:433524-433563) runs the reciprocal through the
   very same GetClip(finish=1) → ACRender::polyClipFinish homogeneous clipper.
   Also ported retail's skip: exact_match portals (CCellPortal.exact_match,
   acclient.h:32300; PView::ClipPortals :433689) bypass the reciprocal clip —
   both sides share the same polygon, so re-clipping is redundant.

2. CellView.CanonicalKey missed COLLINEAR re-emissions: the homogeneous region
   clipper legitimately inserts intersection vertices ON a subject edge when a
   region edge grazes it, so BFS re-clip rounds re-emit the SAME geometric region
   with 1-2 extra collinear edge vertices — keyed as distinct, defeating the
   dedup and accumulating duplicate polygons (this was the real mechanism behind
   the historical "float drift defeats the dedup" rationale that had parked the
   reciprocal on the unstable path). FIX: canonicalize away collinear snapped
   points (exact integer cross-products on the 1e-3 NDC grid) so the key is
   purely a function of the region's corners.

Conformance: CornerSweep_FloodIsCompleteAndMonotone pins the fixed behavior —
61-step monotonic eye sweep ⇒ full flood every step, outside view always reached,
player-room region monotone (was: clean shrink 4.000→2.879 with zero drops, vs
~10 glitch steps before). Diagnostic facts (trace diff, hop microscope, primitive
scratch) retained as the apparatus.

Suites: App 223 green (incl. Build_AppliesReciprocalOtherPortalClip, now passing
with proper tightening AND dedup), Core 1377 green + the 4 pre-existing #99-era
failures + 1 skip, UI 420, Net 294. Visual gate pending: corner press, room↔room,
cellar↔floor, indoor↔outdoor transitions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 10:26:01 +02:00
Erik
1405dd8e90 feat(render): indoor render WORKS — terminating portal flood + every-cell seal + look-in FPS
Checkpoint of the unified retail-faithful indoor render. The two-week HANG/grey is fixed and the
interior seals (live-verified by the user). Commits the session render-rewrite foundation together
with the fixes that made it functional.

- HANG fix: PortalVisibilityBuilder.Build portal flood did not terminate (the faithful ProjectToClip
  near-side clip drifts per round, defeating the CellView dedup; the BFS had no bound after U.2a removed
  MaxReprocessPerCell). Fix = drift-tolerant snapped/canonical CellView.Add dedup (PortalView.cs) plus
  restored MaxReprocessPerCell=16 bounded re-enqueue (PortalVisibilityBuilder.cs). Re-enqueue is kept
  (load-bearing for late-slice propagation, Build_ViewGrowthAfterDoneCell_PropagatesNewSlicesToExit);
  only its count is capped. CellViewDedupTests added.
- Seal (DrawCells Task 2): RetailPViewRenderer.DrawEnvCellShells draws EVERY visible cell via
  IndoorDrawPlan.ShellPass (was gated on the ClipFrameAssembler slot filter, leaving slot-less cells grey).
- Look-in FPS: GameWindow exterior look-in candidates limited to the player landblock +-1 (was all ~81
  loaded LBs iterated every outdoor frame). No behaviour change (far cells were >48m, already culled).

Remaining dominant issue = the FLAP at transitions: viewer-cell metastability (render roots at the
camera-eye cell, which oscillates outdoor-indoor as the 3rd-person boom drifts across the doorway,
confirmed in render-sig). SEPARATE fix, NOT the DrawCells port. Full handoff + flap fix plan + tracked
follow-ups (#78 terrain, look-in-from-inside, look-in FPS, L-spotlight):
docs/research/2026-06-07-indoor-render-session-handoff.md.

Baselines: build 0 err; App.Tests 210/210; Core.Tests 1331 pass / 4 fail (pre-existing) / 1 skip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 10:14:43 +02:00
Erik
406307e8ee feat(render): Phase A8.F — ViewPolygon + CellView clip-region data model
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 11:30:28 +02:00