Skip to content

Architecture hardening: enforce renderer parity, decompose god-files, fix shadow seams - #85

Open
apresmoi wants to merge 28 commits into
mainfrom
shadow-seam-bleed
Open

Architecture hardening: enforce renderer parity, decompose god-files, fix shadow seams#85
apresmoi wants to merge 28 commits into
mainfrom
shadow-seam-bleed

Conversation

@apresmoi

Copy link
Copy Markdown
Collaborator

Architecture hardening pass over the whole monorepo, plus a shadow-quality
investigation that started from a visible artifact and ended in core.

All 23 commits are gated: 3,121 tests green, 0 typecheck errors, package +
website builds clean, mirror/boundary checks passing at every commit boundary.

Why

An audit of module boundaries, duplication, defaults, types and performance
turned up one root cause behind most of it: the three-renderer copy discipline
(vanilla / React / Vue) was enforced by prose in AGENTS.md and a PR checklist,
with nothing mechanical behind it. It had already failed — several fixes existed
in one renderer only, producing live rendering bugs.

What landed

Enforcement (the reason the rest stays fixed)

  • pnpm check:boundaries — package dependency rules, deep-import bans, node-builtin
    containment.
  • pnpm check:mirrors — byte-identity for react↔vue clones; hash-pinned lock for the
    structurally-divergent vanilla↔framework mirrors, so editing one fails CI until
    you re-pin and confirm all three renderers got the change.
  • pnpm typecheck per package (now including test files), wired into CI.

Live bugs fixed (each was a one-renderer fix that never got mirrored)

  • Voxel paint order under mesh rotation (React/Vue).
  • seamBleed silently ignored for textured triangles.
  • Projective quads lit as if facing +Z in dynamic mode.
  • Triangle-primitive detection ignoring strategies.disable and the owning document.
  • Point-light shading missing from React/Vue solid triangles.
  • Vue camera pan mirrored under yaw.

Performance

  • React/Vue no longer recompute the entire receiver-shadow pipeline on every camera
    frame — they now match vanilla, which never did. Caster-items, parametric-override
    and overlap-dedup caches are shared across all three renderers.
  • Shadow cache invalidation fixed for in-place updatePolygon edits.

Consistency

  • seamBleed unified: one core-owned resolution (raw px, "auto" = 1.5), replacing
    two divergent semantics.
  • Defaults reconciled: maxDistanceInfinity everywhere, an 80 ms animated-shadow
    throttle in all three renderers, ground-shadow gated on a real directional light.

Decomposition (coverage-baselined first, behavior-preserving)

  • createPolyScene.ts 2,481 → 1,286 lines + 7 concept modules; region coverage
    80.4% → 84.4%.
  • React PolyMesh 1,554 → 498, Vue 1,455 → 419, into 8 mirrored modules each;
    coverage up on every axis. All pre-existing tests pass unmodified.
  • ~2,300 duplicated lines hoisted into core (render stats, orbit/FPV/gizmo math).

Shadow quality (traced from a visible artifact to root cause)

  • Interior member seams, coplanar T-junction group splits, and — the dominant class —
    crease boundaries between face groups now bleed correctly. The last one was only
    safe once solid single-light shadows switched to painting a pre-blended colour
    opaquely
    instead of the shadow colour at partial alpha, making overlap idempotent.
    Proven pixel-identical where nothing overlaps (0 of 607,200 px at opacity 1).
  • Root cause of the residual cracks: crease matching ran on plane-snapped outlines, so
    non-planar imported quads pushed genuinely-shared edges past the matching epsilon and
    272 real creases were misclassified as silhouette. Castle missing-shadow pixels
    117 → 70, now within 1 px of the brute-force bound.
  • Textured and multi-light receivers are excluded by design and verified byte-identical.

Website

  • Gallery ground was repainted in CSS over the engine's bake, so its shadow blended
    from a different base: shadow strength 0 still left a dark patch and the ground
    shadow read ~68% deeper than the geometry justified. Ground now bakes honestly —
    shadow ratio 0.370 → 0.657 vs the model's 0.662, strength 0 is genuinely clean, and
    light-dragging got ~14% faster.
  • Added Shadow strength; gated the parametric-only controls; removed the no-op
    Shadow color.
  • New benches: shadow timing, .vox import; per-phase shadow timers wired; render
    stats now report shadow subpath and path-data counts (the real cost drivers).

Breaking changes

  • seamBleed is raw px everywhere; "auto" now yields 1.5 px overscan in React/Vue
    (was none), and values > 1.5 are no longer clamped in vanilla.
  • Orbit/Map maxDistance default 5000 → Infinity in React/Vue (Three parity).
  • Vue pan direction under camera yaw corrected (was mirrored — a genuine bug).
  • Removed: 8 dead PolyScene props, debugShowBackfaces, the resolveColor export,
    and usePolySceneContext's ignored options parameter.
  • Renamed: React ShadowOptionsPolyShadowOptions (now exported by all three);
    Poly*CameraOptionsFromThreePoly*CameraStateFromThree.
  • Solid single-light shadow paths now carry a pre-blended fill at opacity 1 instead of
    the shadow colour at fractional alpha.

Review notes

The commits are ordered as coherent units and can be reviewed in sequence — the
enforcement commits come first, then bug fixes, then perf, then the refactors, then
the shadow work. Happy to split this into stacked PRs if you'd prefer that shape.

Deliberately not done, needing your call: curating polycss's
export * from core, the three incompatible PolyMeshHandle shapes, polycss-fonts
generic export names, hoisting the shared styles.ts CSS, and an optional
shadow.mode: "flat" for artistically uniform shadows.

…d+exported, PolyThreeMesh parity, morph core dep)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant