Pick resolved galaxy disks from the procedural-disk pipeline#287
Merged
Conversation
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>
Deploying with
|
| 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
isDiskHandoffflag 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+diskAxesmath. 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
(sourceCode, localIdx)identity on eachProceduralDiskInstance.packSelection(source, localIdx)into the free instance slot 6 as raw u32 bits (via aUint32Arrayview —localIdxcan 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.proceduralDisks/pickFragment.wesldiscards outside the unit-circle disk — exactly mirroring the visual fragment's edge — and writespickId + PICK_SENTINEL_OFFSETinto the r32uint pick target.pickDisks(pass), wired intopickRenderer.recordPickPassalongside 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.dot(uv,uv) > 1.0dot clamped to the size floor — deletingisDiskHandoff/pickMajorBillboard/pickMinorBillboard(twoworldToClipcalls + three flat varyings gone from the hot vertex stage) and the now-unuseddiskAxesimport.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 buildlinks (the new fragment compiles; the trimmed pointVSOutis 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,createPickRendererappend-not-reorder signature.npm run typecheckclean.showDiskRadiusRing+showPickBufferon, 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