docs: cutover flip shipped — see-through + oscillation DIAGNOSED (evidence-based handoff)
The flip killed the branch-toggle flap (one path, zero OutdoorRoot frames). It exposed two residuals now PROVEN via a live [bshell] probe, not guessed: (1) oscillation = the outdoor-node flood membership swings 1<->~13 building cells frame-to-frame, so the walls (EnvCell shells) blink; (2) see-through = EnvCell wall polys are single-sided for SidesType==CounterClockwise, so from outside you see their culled back. The ModelId building shells DO render (6/6 with mesh) but are a partial frame, not the walls — the skip-all-interiors experiment proved the walls are the EnvCell shells. Fixes identified (stabilise flood + build back faces) but not implemented; full do-not-retry list + open pre-flip-reconciliation question in the doc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
774cb22713
commit
ef2186147d
1 changed files with 125 additions and 0 deletions
|
|
@ -0,0 +1,125 @@
|
||||||
|
# Handoff — Cutover FLIP shipped; see-through + oscillation DIAGNOSED (evidence-based) — 2026-06-07 (PM)
|
||||||
|
|
||||||
|
> **CANONICAL PICKUP for the render-unification residuals.** Worktree `thirsty-goldberg-51bb9b`,
|
||||||
|
> branch `claude/thirsty-goldberg-51bb9b`, HEAD `774cb22`. The cutover flip is SHIPPED (one render
|
||||||
|
> path, no branch-toggle flap). It exposed two residuals — **see-through building walls** and
|
||||||
|
> **oscillation** — whose root causes are now PROVEN with a live probe (not guessed). The fixes are
|
||||||
|
> identified but NOT yet implemented. Read §3 (diagnosis) and §5 (do-not-retry) before touching code.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. What shipped (committed, keep)
|
||||||
|
|
||||||
|
The CUTOVER FLIP from `2026-06-07-render-unification-cutover-flip-handoff.md` landed:
|
||||||
|
|
||||||
|
| Commit | What |
|
||||||
|
|---|---|
|
||||||
|
| `5379f6e` | Step A — `PortalVisibilityBuilder.Build` seeds a full-screen `OutsideView` when the root is the outdoor node (`LoadedCell.IsOutdoorNode`, set by `OutdoorCellNode.Build`). +2 UnifiedFloodTests, +2 flag assertions. |
|
||||||
|
| `445e861` | Step B — the flip: `GameWindow.cs:~7387` `clipRoot = viewerRoot ?? _outdoorNode`. Drops the `playerIndoorGate` gate. ONE path, no inside/outside branch. Preserves the `LiveDynamic` draw for the outdoor root. |
|
||||||
|
| `88caa0d` | depth-clear fix — `ClearDepthSlice = null` for the outdoor root (the full-screen depth clear was painting the cellar over the player; fixed). |
|
||||||
|
| `774cb22` | Revert of `0030dac` (the slot-0 skip — a FAILED fix, see §5). |
|
||||||
|
|
||||||
|
**The flip's PRIMARY goal succeeded:** `[render-sig]` shows `branch=RetailPViewInside` every frame,
|
||||||
|
**zero `OutdoorRoot` frames** across a whole session. The two-branch-toggle flap is gone by
|
||||||
|
construction. Baselines: build green, App.Tests 216/0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. The two residuals the flip exposed (user-observed)
|
||||||
|
|
||||||
|
1. **See-through building walls from outside** — standing outside a building you see *into* it through
|
||||||
|
the walls (doors closed).
|
||||||
|
2. **Oscillation** — the interior/walls flicker between "showing nothing", "see-through", and "full
|
||||||
|
interior" frame-to-frame while standing still.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. ROOT CAUSE — proven by a live `[bshell]` probe (NOT guessed)
|
||||||
|
|
||||||
|
A throttled probe in `RetailPViewRenderer.DrawInside` (now stripped; re-add from git history of this
|
||||||
|
doc's session if needed) logged, for the outdoor-node root on a loaded frame at the Holtburg cottage:
|
||||||
|
|
||||||
|
```
|
||||||
|
[bshell] total=6 withMesh=6 inOutdoorPartition=6 envCellsFlooded=1 outdoorEntities=637
|
||||||
|
```
|
||||||
|
|
||||||
|
Interpretation (each number is decisive):
|
||||||
|
|
||||||
|
- **`total=6 withMesh=6 inOutdoorPartition=6`** — there ARE 6 building `ModelId` "shell" entities
|
||||||
|
(`WorldEntity.IsBuildingShell`, created in `LandblockLoader.cs:75-91` from `LandBlockInfo.Buildings[].ModelId`),
|
||||||
|
ALL carry meshes, ALL land in `partition.Outdoor` (they have `ParentCellId==null`;
|
||||||
|
`InteriorEntityPartition` line 47 → Outdoor; `WbDrawDispatcher.EntityPassesVisibleCellGate` returns
|
||||||
|
`true` for null `visibleCellIds`). **So the `ModelId` exterior DOES render.**
|
||||||
|
- **BUT** the earlier "skip all interior shell draws for the outdoor root" experiment (uncommitted,
|
||||||
|
reverted) made the building **fully see-through** — i.e. drawing ONLY the `ModelId` shells is NOT a
|
||||||
|
solid building. **Therefore the `ModelId` Setup is a partial frame, and the building's actual WALLS
|
||||||
|
are the EnvCell shell geometry** (`ObjectMeshManager.PrepareCellStructMeshData`, drawn by
|
||||||
|
`DrawEnvCellShells`).
|
||||||
|
- **`envCellsFlooded=1`** — in this frame the outdoor-node flood reached **ZERO** building interior
|
||||||
|
cells (only the node itself). Earlier `[render-sig]` frames at the same spot showed `ids=[node + ~12
|
||||||
|
building cells]` (≈13). **So the flood membership swings between 1 and ~13 frame-to-frame.**
|
||||||
|
|
||||||
|
### The two residuals, explained
|
||||||
|
1. **Oscillation = flood instability gating the walls.** The flip made wall-drawing depend on the
|
||||||
|
portal flood reaching each building's interior cells. That flood is unstable (1 ↔ ~13), so the
|
||||||
|
EnvCell walls blink in and out. ("showing nothing" = flood=1, no interior; "full interior /
|
||||||
|
see-through" = flood reached the building.)
|
||||||
|
2. **See-through = single-sided EnvCell walls.** Even when the walls DO draw, the EnvCell wall polys
|
||||||
|
are single-sided for `SidesType==CounterClockwise` (interior-facing). `PrepareCellStructMeshData`
|
||||||
|
(ObjectMeshManager ~1299-1310) builds the back face only for `SidesType==None` (front twice
|
||||||
|
reversed) and `SidesType==Clockwise` (neg surface). A `CounterClockwise` wall = front face only →
|
||||||
|
from outside you see its culled back → see-through.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Fix path (identified, NOT implemented)
|
||||||
|
|
||||||
|
Two independent fixes, both needed:
|
||||||
|
|
||||||
|
- **F1 — Stabilise the flood membership** so a building's interior cells are CONSISTENTLY in/out of
|
||||||
|
the visible set (no 1↔13 swing). This is the same metastability family as the indoor flicker. Likely
|
||||||
|
levers: ground the outdoor-node flood's building membership in the cell `stab_list`/PVS (stable,
|
||||||
|
precomputed) instead of the per-frame portal-side test + projection; or hysteresis on which buildings
|
||||||
|
are flooded. Probe to re-add: `envCellsFlooded` per frame (RLE it; it should be constant when standing
|
||||||
|
still).
|
||||||
|
- **F2 — Make the EnvCell walls solid from outside.** Either build the missing back faces for
|
||||||
|
`SidesType==CounterClockwise` walls in `PrepareCellStructMeshData`, or render those shells
|
||||||
|
double-sided (`CullMode.None`) when the viewer is outside the cell. Verify against retail: dump a real
|
||||||
|
Holtburg cell's wall-poly `SidesType` distribution first.
|
||||||
|
|
||||||
|
**Open research question (reconcile before F2):** pre-flip the buildings looked SOLID from outside.
|
||||||
|
What drew the solid walls pre-flip — a global EnvCell-shell render, the `DrawPortal`/`BuildFromExterior`
|
||||||
|
look-in, or were the `ModelId` shells solid then? Find what the flip replaced. The old outdoor `else`
|
||||||
|
block (`GameWindow.cs:~7557-7663`, now dead-when-clipRoot-non-null but still present) is the place to
|
||||||
|
read. This answers whether F2 is "build back faces" or "restore a pre-flip draw".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. DO NOT RETRY (failed this session, with evidence)
|
||||||
|
|
||||||
|
- **Slot-0 skip** (`0030dac`, reverted `774cb22`): "for the outdoor root, skip flooded cells whose
|
||||||
|
clip degenerated to no-clip slot 0." Made the oscillation WORSE — slot-0-ness flickers per frame, so
|
||||||
|
cells blinked. Wrong: the see-through is not the slot-0 fallback.
|
||||||
|
- **Skip-all-interiors experiment** (uncommitted, reverted): "outdoor root draws terrain + ModelId
|
||||||
|
exteriors only, no EnvCell shells." Made buildings FULLY see-through + flashing — proved the `ModelId`
|
||||||
|
Setup is not the walls (the walls are the EnvCell shells). Do not ship this.
|
||||||
|
- **Backface-culling-of-shells hypothesis** (never coded): plausible but the cull mode is already
|
||||||
|
data-driven (`poly.SidesType`); the real gap is single-sided geometry (no back face built), not a
|
||||||
|
cull-state bug.
|
||||||
|
- The subagent hypothesis "ModelId exterior occludes; interior overdraws it; fix = gate DrawEnvCellShells
|
||||||
|
off for the outdoor root" is **disproven** — that gate IS the skip-all-interiors experiment, which
|
||||||
|
removed the walls entirely.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. State + how to resume
|
||||||
|
- HEAD `774cb22`, tree clean, build green, App.Tests 216/0. The flip + depth-clear are committed; the
|
||||||
|
branch renders with the two residuals (see-through + oscillation).
|
||||||
|
- The flip is on this BRANCH only (main is unaffected). To get a stable client meanwhile, revert the
|
||||||
|
flip commits (`445e861` Step B is the behaviour change; reverting it alone restores the pre-flip
|
||||||
|
outdoor path — verify Step A `5379f6e` is inert without it).
|
||||||
|
- Re-add the `[bshell]` / `envCellsFlooded` probe (see this session's git reflog for the exact code) to
|
||||||
|
watch flood stability while working F1.
|
||||||
|
- Memory: `project_indoor_flap_rootcause` (update with this corrected diagnosis),
|
||||||
|
`reference_render_pipeline_state`, `feedback_render_downstream_of_membership` (the oscillation IS a
|
||||||
|
membership/flood-stability bug, per that note).
|
||||||
Loading…
Add table
Add a link
Reference in a new issue