feat(render): Phase A8 — indoor visibility + streaming fixes batch

Lands the working A8 indoor-rendering and streaming fixes accumulated this
session. User has verified these visually to some degree (e.g. lifestone /
translucent meshes confirmed fine under the FrontFace flip; bridge / wall /
collision regressions confirmed fixed after travel); not every path has been
exhaustively gated. The cellar-flap defect remains OPEN and will be solved
the retail-faithful way via a dedicated brainstorm (see handoff docs).

Rendering core (reviewed, high confidence):
- EnvCellRenderer SSBO stride fix: upload packed Matrix4x4[] (64B) instead of
  the 80B CPU InstanceData struct the shader never expected — fixes the
  transform/texture "explosion" for any draw with >1 instance (cells that
  dedupe to a shared cellGeomId). Real root cause.
- WB-style global FrontFace(CW) + per-batch CullMode carried through the MDI
  layout (GroupKey + BuildIndirectArrays + DrawIndirectRange split into
  same-cull runs with absolute uDrawIDOffset per run).
- EntitySet partitioning (IndoorPass / OutdoorScenery / LiveDynamic) +
  WorldEntity.BuildingShellAnchorCellId so building shells scope to their
  dat-derived building cell instead of rendering everywhere.
- RenderOutsideInAcdream (look into buildings from outside) +
  CollectVisiblePortalBuildings frustum cull of portal bounds.
- Sky-when-inside-building + per-cell audit probe + GL-state probe.

Streaming / perf (test-covered; not independently code-reviewed this session):
- Near/far priority queues so near work wins over far; PromoteToNear carries
  full landblock + mesh data; LandblockEntriesWithoutAnimatedIndex avoids
  rebuilding the animated-lookup dict in the hot draw path. Fixes the
  bridge-not-appearing / missing-walls / broken-collision-after-travel
  regressions and improves post-transition FPS.

Tooling + docs:
- tools/A8CellAudit: offline dat cell/portal/building dumper (portals +
  buildings modes) — reproduces the cellar-flap investigation with no launch.
- docs/research cellar-flap root-cause + option-2 handoff (the didInsideStencil
  double-duty finding + the WB-recursive design decision + brainstorm prompt),
  entity-taxonomy, replan, issue-78 visibility investigation.

Diagnostics retained on purpose: ACDREAM_A8_DIAG_* gates, portal_stencil.vert
provisional pos.w clamp, and the probe families are kept (env-var gated, zero
cost when off) because the pending option-2 cellar-flap brainstorm needs them.
Strip in the option-2 ship commit.

Indoor branch stays behind ACDREAM_A8_INDOOR_BRANCH=1 (default off = pre-A8
visual). Build green; App tests + Core (streaming/dispatcher/loader) tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-29 10:14:50 +02:00
parent e415bb3863
commit 5dc4140c11
38 changed files with 3965 additions and 277 deletions

View file

@ -1192,7 +1192,7 @@ or +small fix if different. Not blocking M1.
## #71 — WorldPicker Stage B — polygon refine for retail-accurate clicks
**Status:** OPEN
**Severity:** LOW (Stage A — screen-rect picker — is sufficient for M1)
**Severity:** MEDIUM (Stage A now causes real play mis-picks through open doors/windows)
**Filed:** 2026-05-16
**Component:** selection / picker
@ -1212,6 +1212,15 @@ to the visible mesh — under-pick what looks like empty space inside
the rect, catch visible mesh that pokes past the sphere boundary
(creature outstretched arm, sign edge).
**New evidence (2026-05-28 / Phase A8 visual gate):** User stood outside
a Holtburg building, saw a vendor through an open doorway/window, clicked
the visible vendor, and acdream selected the door instead:
`[B.4b] pick guid=0x7A9B4015 name=Door`. This is exactly the Stage A
failure mode: the open door's projected `Setup.SelectionSphere` rect is
closer than the vendor's rect, even though the visible door polygon is not
under the cursor. The fix is polygon refinement against visible GfxObj
triangles plus current animated part transforms; do not special-case doors.
**Acceptance:** Pipe per-part GfxObj visual polygons through a
`PickPolygonProvider` interface (don't duplicate mesh decoding —
hook the existing `ObjectMeshManager` cached data). Two-tier in
@ -1222,8 +1231,8 @@ frame edges.
**Estimated scope:** Medium (~4-6 hours). Defer until visual
verification surfaces a Stage A miss in real play. The user
confirmed 2026-05-16 that "I can click on longer ranges now so
good" — Stage A is enough for M1's "click an NPC" demo.
confirmed 2026-05-28 that the door/vendor case is now observable in real
play, so this should be scheduled soon after A8 rather than left as polish.
---