Picker Enhancements, add scissor and normals#8762
Conversation
…added support for getting normals from pick point; updated examples; added pick point to editor example
There was a problem hiding this comment.
Pull request overview
This PR enhances the engine’s picking pipeline by adding optional scissoring to reduce picker render cost, and introduces a new async API to reconstruct a picked world-space normal from the depth buffer. It also updates multiple examples to use the new scissor-based prepare flow and visualizes the pick hit/normal in the editor example.
Changes:
- Add optional scissor rect support to the picker render pass to narrow rasterization during
prepare(). - Add
picker.getWorldPointAndNormalAsync(x, y)to return the picked world point plus a derived per-pixel normal from a 2×2 depth neighborhood. - Update examples to scissor picker renders and add editor visualization + UI toggle for pick axes.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/framework/graphics/render-pass-picker.js | Adds scissor rect plumbing into the picker render pass and applies it during execution. |
| src/framework/graphics/picker.js | Adds normal reconstruction API, refactors unprojection helpers, and adds scissor option handling in prepare(). |
| examples/src/examples/misc/editor.selector.mjs | Enables depth picking and uses scissored prepare(); queries selection + normal in parallel. |
| examples/src/examples/misc/editor.example.mjs | Adds pick-hit axes visualization and integrates events from the selector. |
| examples/src/examples/misc/editor.controls.mjs | Adds UI toggle to show/hide pick point axes. |
| examples/src/examples/graphics/area-picker.example.mjs | Computes union scissor rect for all queries to reduce rasterization cost. |
| examples/src/examples/gaussian-splatting/picking.example.mjs | Uses scissored picker prepare around the click point. |
| examples/src/examples/gaussian-splatting/paint.example.mjs | Re-prepares picker per brush position with a 1×1 scissor for painting. |
| examples/src/examples/gaussian-splatting-legacy/picking.example.mjs | Uses scissored picker prepare around the click point. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // one 2x2 readback covering (x,y), (x+1,y), (x,y+1), (x+1,y+1) | ||
| const pixels = await this._readTexture(this.depthBuffer, x, y, 2, 2, /** @type {RenderTarget} */ (this.renderTargetDepth)); | ||
|
|
||
| // decode 4 RGBA8 pixels → 4 linear depths (null if cleared) | ||
| /** @type {(number|null)[]} */ | ||
| const depths = [null, null, null, null]; | ||
| for (let i = 0; i < 4; i++) { | ||
| const o = i * 4; | ||
| const intBits = ((pixels[o] << 24) | (pixels[o + 1] << 16) | (pixels[o + 2] << 8) | pixels[o + 3]) >>> 0; | ||
| if (intBits !== 0xFFFFFFFF) { | ||
| _int32View[0] = intBits; | ||
| depths[i] = _floatView[0]; | ||
| } | ||
| } |
| const w = options.width ?? 0; | ||
| const h = options.height ?? 0; | ||
| if (w > 0 && h > 0) { | ||
| const r = this.sanitizeRect(options.x ?? 0, options.y ?? 0, w + 1, h + 1); | ||
| const flippedY = this.renderTarget.height - (r.y + r.w); |
|
Fantastic PR! Keep to get this in. Please check the copilot comments. |
Sorry, I've been pretty lazy, either making changes to our fork or our own wrapper library so I'm trying to push more stuff upstream from now on |
|
any updates on those copilot comments? |
|
Been on other tickets, back now and it looks like its only working in webgpu, not webgl. Taking a look now.... |
Description
picker.getWorldPointAndNormalAsync(x, y);picking-feat.mp4
Fixes #
Checklist