feat(e3d): GI for mesh blocks, frustum far-plane and subpixel cull fixes
Global illumination now traces lean TriangleMeshBlock geometry:
each block triangle is a TriangleBvh.Entry with no backing polygon
(the entry carries the block and triangle index), sampled
per-triangle like plain polygons. Phase A writes direct light with
shadow bits and phase B adds the bounce estimate straight into the
block's new per-triangle giLight array (allocated at 255 = full
texture, so enabling GI shows no dark age); MeshTriangle.paint
copies the triplet before the span loop and the span writers
multiply sampled texels by it — per-triangle baked shading at a
branch per pixel. Albedo for bounce rays comes from each texture's
cached average color. Blocks skip the texel/lightmap phase
entirely.
Because streaming worlds bump the render-list version per loaded
cell, snapshot rebuilds are single-flighted (one worker builds, the
rest keep sampling the old snapshot) and rate-limited
(-De3d.gi.minRebuildMs=, default 1000 ms) so bursts coalesce
instead of rebuilding a multi-million-triangle BVH back-to-back.
Raise the frustum far plane from 10000 to 1e9 units
(-De3d.cull.far=): the short plane wrongly culled distant cell/LOD
composites whose whole bounds lay beyond it, punching holes into
mid-distance terrain while streaming (bugreport-
20260928-005318;
FO4 LOD blocks span 100k+ units). Add -De3d.cull.frustum=false to
disable composite culling and -De3d.cull.frustumTrace=true to log
culled composites' bounds.
Include paint margins in the subpixel cull span: single-vertex
shapes (Billboard/GlowingPoint) have a zero raw span and their
entire visible area IS the margin, so the raw-span test culled them
regardless of glow size — billboards rendered invisible.
Document the mesh-block GI path, the far-plane rationale, and the
new tuning properties in the Global illumination and Frustum
culling pages and AGENTS.org.