Skip to content

Pick resolved galaxy disks from the procedural-disk pipeline#287

Merged
rulkens merged 11 commits into
mainfrom
worktree-pick-disk-from-procedural
Jun 7, 2026
Merged

Pick resolved galaxy disks from the procedural-disk pipeline#287
rulkens merged 11 commits into
mainfrom
worktree-pick-disk-from-procedural

Conversation

@rulkens

@rulkens rulkens commented Jun 7, 2026

Copy link
Copy Markdown
Owner

What & why

Picking for a resolved galaxy used to be reconstructed on the point sprite: the point pick fragment rebuilt the disk ellipse from two flat varyings + an isDiskHandoff flag set in the hot point vertex stage. It was buggy (drew an oversized square that didn't foreshorten) and a perf cost on a stage that runs for ~2.5M sprites per frame.

The realization: the procedural-disk renderer already draws the exact disk the debug ring traces — same paddedRadiusMpc + diskAxes math. So this picks from the procedural disk instead, and strips the ellipse machinery off the point stage.

The de-complecting in one line: the point pass picks dots; the procedural pass picks disks. No shader branches on a galaxy's LOD regime in the pick path anymore.

How

  • Carry (sourceCode, localIdx) identity on each ProceduralDiskInstance.
  • Pack packSelection(source, localIdx) into the free instance slot 6 as raw u32 bits (via a Uint32Array view — localIdx can exceed 2²⁴, so storing it as a float would corrupt large ids). The renderer owns a grow-on-demand pick instance buffer, re-uploaded byte-identically each frame.
  • New proceduralDisks/pickFragment.wesl discards outside the unit-circle disk — exactly mirroring the visual fragment's edge — and writes pickId + PICK_SENTINEL_OFFSET into the r32uint pick target.
  • New procedural r32uint pick pipeline + pickDisks(pass), wired into pickRenderer.recordPickPass alongside the structure-ring pick. Shared depth means the front-most of a galaxy's point dot and its disk wins; both carry the same packed id, so overlap is harmless.
  • Point pick simplified back to a plain dot(uv,uv) > 1.0 dot clamped to the size floor — deleting isDiskHandoff / pickMajorBillboard / pickMinorBillboard (two worldToClip calls + three flat varyings gone from the hot vertex stage) and the now-unused diskAxes import.

The pick surface now matches the debug disk-radius ring in size and foreshortening, for all galaxies (not just curated-famous ones). Curated-famous galaxies that render via the textured disk with calibrated tilt are an explicit fast-follow (noted in the plan's "Out of scope").

Verification

  • npm run build links (the new fragment compiles; the trimmed point VSOut is coherent across vertex output + both fragments).
  • npm test — 2449 pass (384 files). New tests: instance identity, slot-6 u32-bits packing, pickDisks(6, N) + empty-frame no-op, createPickRenderer append-not-reorder signature.
  • npm run typecheck clean.
  • Entanglement-radar clean: pick buffer is fresh-not-mirror; cached camera is legitimate same-frame replay; no regime branches remain.
  • Visual: user-confirmed in the dev server — with showDiskRadiusRing + showPickBuffer on, the pick region for a resolved galaxy hugs the ring in size and tilt.

Built via the subagent-driven-development workflow; plan + TDD task list in docs/superpowers/plans/2026-06-08-pick-disk-from-procedural.md.

🤖 Generated with Claude Code

rulkens and others added 10 commits June 8, 2026 00:23
Plan to move resolved-galaxy disk picking off the point sprite and onto
the procedural-disk pipeline (which already draws the exact disk the
debug ring traces), and simplify the point pick back to a plain dot —
removing the pick-ellipse varyings + extra worldToClip from the hot
point vertex stage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nstances

Task 1 of the disk-pick plan. The procedural disk renderer already draws
the disk the debug ring traces, so it will become the pick surface. First
step: each emitted instance must know which galaxy it is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…buffer

Task 2 of the disk-pick plan. draw() now writes packSelection(source,
localIdx) into the free instance slot 6 as raw u32 bits (via a Uint32Array
view over the same buffer — localIdx can exceed 2^24, so storing it as a
float would corrupt large ids). The renderer also owns a grow-on-demand
pick instance buffer filled byte-identically each frame; Task 4's pickDisks
pass draws from it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 3 of the disk-pick plan. The vertex stage bitcasts the packed id from
slot 6 (orientation.z) into a flat pickId varying; the new pickFragment.wesl
discards outside the unit-circle disk — exactly mirroring the visual
fragment's edge, so the pick surface is the disk the debug ring traces — and
writes pickId + PICK_SENTINEL_OFFSET into the r32uint pick target.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 4 of the disk-pick plan. The procedural-disk renderer gains its own
r32uint pick pipeline (own @group(0) camera BGL + uniform, reusing the shared
focus BGL so it's layout-compatible) and a pickDisks(pass) method that replays
the camera state draw() last saw and draws the owned pick instance buffer.
pickRenderer.recordPickPass calls it alongside the ring pick; shared depth
means the front-most of a galaxy's point dot and disk wins, and both carry the
same packed id so overlap is harmless. An empty frame zeroes the replayed
count so a vanished disk can't be re-picked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 5 of the disk-pick plan. With the procedural-disk pass now owning
resolved-disk picking, the point pick no longer reconstructs the disk ellipse.
Strips the isDiskHandoff / pickMajorBillboard / pickMinorBillboard varyings
and the diskAxes import off the hot, shared point vertex stage (two worldToClip
calls and three flat varyings gone from a ~2.5M-invocation stage), clamps the
pick-pass billboard to the pointSizePx dot floor, and reduces the point pick
fragment to a plain dot(uv,uv) > 1.0 discard. The point pass picks dots; the
procedural pass picks disks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The procDisks hash serializes every ProceduralDiskInstance field, so the
sourceCode/localIdx identity added for disk picking now appears in it. Pure
snapshot refresh — no behaviour change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entanglement-radar found the slot-6 pack site documented bits-vs-float but not
what slot 6 IS. Add a one-line pointer to proceduralDisks/io.wesl orientation.z
so a future edit can't treat the slot as free and silently corrupt pick ids.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 7, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
skymap bf997d9 Commit Preview URL

Branch Preview URL
Jun 07 2026, 11:57 PM

DoD audit READY: 2449 tests pass, typecheck clean, 31/31 checkboxes ticked,
no new unowned TODOs, test parity up (new pickRenderer.diskPick suite), visual
smoke test user-confirmed (pick region hugs the disk-radius ring). Deferred:
textured-disk picking for curated-famous galaxies (documented in Out of scope).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rulkens rulkens merged commit ce3f523 into main Jun 7, 2026
1 of 2 checks passed
@rulkens rulkens deleted the worktree-pick-disk-from-procedural branch June 7, 2026 23:56
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