fix(picker): Cluster A #86 — cell-BSP ray occlusion in WorldPicker

WorldPicker.Pick previously had no occlusion test — any entity along
the click ray within maxDistance was a candidate, including ones
behind walls. Adds the CellBspRayOccluder static helper that
Möller-Trumbore-tests the click ray against every polygon in every
currently-cached EnvCell BSP, returning the nearest wall-hit `t`.
Both Pick overloads gate candidate selection by that wall-t (legacy
ray-sphere via world-space `t`, screen-rect via camera-space clip.W
depth — matching ScreenProjection.TryProjectSphereToScreenRect's
convention).

PhysicsDataCache exposes a new CellStructIds snapshot accessor so the
caller can iterate without needing the private cache dictionary.
CellPhysics.BSP/PhysicsPolygons/Vertices relaxed from required to
nullable so test fixtures can construct a CellPhysics from Resolved
alone without a real DAT BSP object. GameWindow snapshots the loaded
cell physics on each Pick call and passes the occluder callback.

Closes #86.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-19 14:41:56 +02:00
parent 27d7de11d8
commit 3764867566
6 changed files with 355 additions and 6 deletions

View file

@ -210,6 +210,15 @@ public sealed class PhysicsDataCache
public int SetupCount => _setup.Count;
public int CellStructCount => _cellStruct.Count;
/// <summary>
/// Indoor walking Phase 1 (2026-05-19). Snapshot of currently-cached
/// EnvCell ids — used by <see cref="AcDream.Core.Selection.WorldPicker"/>
/// to enumerate occluder candidates without exposing the underlying
/// dictionary. Returns the live key-set; callers should snapshot the
/// collection if they need stability across frames.
/// </summary>
public IReadOnlyCollection<uint> CellStructIds => (IReadOnlyCollection<uint>)_cellStruct.Keys;
/// <summary>
/// Register a pre-built <see cref="GfxObjPhysics"/> directly.
/// Intended for unit-test fixtures that construct synthetic BSP trees
@ -285,9 +294,15 @@ public sealed class SetupPhysics
/// </summary>
public sealed class CellPhysics
{
public required PhysicsBSPTree BSP { get; init; }
public required Dictionary<ushort, Polygon> PhysicsPolygons { get; init; }
public required VertexArray Vertices { get; init; }
/// <summary>
/// The physics BSP tree for this cell. Nullable so that test fixtures
/// can construct a <see cref="CellPhysics"/> from <see cref="Resolved"/>
/// alone without needing a real DAT BSP object. Production code must
/// null-check before traversal: <c>cell.BSP?.Root is not null</c>.
/// </summary>
public PhysicsBSPTree? BSP { get; init; }
public Dictionary<ushort, Polygon>? PhysicsPolygons { get; init; }
public VertexArray? Vertices { get; init; }
public Matrix4x4 WorldTransform { get; init; }
public Matrix4x4 InverseWorldTransform { get; init; }