@babylonjs/lite 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/index.js +596 -526
  3. package/dist/index.js.map +1 -1
  4. package/index.d.ts +107 -4
  5. package/lib/engine/engine.js +1 -1
  6. package/lib/engine/engine.js.map +1 -1
  7. package/lib/frame-graph/render-task.js +6 -4
  8. package/lib/frame-graph/render-task.js.map +1 -1
  9. package/lib/index.js +4 -1
  10. package/lib/index.js.map +1 -1
  11. package/lib/loader-gltf/gltf-ext-quantization.js +4 -1
  12. package/lib/loader-gltf/gltf-ext-quantization.js.map +1 -1
  13. package/lib/loader-gltf/gltf-feature-registry.js +11 -1
  14. package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
  15. package/lib/loader-gltf/gltf-feature-variants.js +11 -1
  16. package/lib/loader-gltf/gltf-feature-variants.js.map +1 -1
  17. package/lib/loader-gltf/gltf-json-asset.js +8 -1
  18. package/lib/loader-gltf/gltf-json-asset.js.map +1 -1
  19. package/lib/loader-gltf/gltf-material.js +4 -29
  20. package/lib/loader-gltf/gltf-material.js.map +1 -1
  21. package/lib/loader-gltf/gltf-parser.js +1 -8
  22. package/lib/loader-gltf/gltf-parser.js.map +1 -1
  23. package/lib/loader-gltf/gltf-pbr-builder.js +1 -15
  24. package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
  25. package/lib/loader-gltf/gltf-variants.js +5 -10
  26. package/lib/loader-gltf/gltf-variants.js.map +1 -1
  27. package/lib/loader-gltf/load-gltf.js +19 -29
  28. package/lib/loader-gltf/load-gltf.js.map +1 -1
  29. package/lib/material/pbr/background-dds-skybox.js +35 -93
  30. package/lib/material/pbr/background-dds-skybox.js.map +1 -1
  31. package/lib/material/pbr/background-ground.js +13 -18
  32. package/lib/material/pbr/background-ground.js.map +1 -1
  33. package/lib/material/pbr/background-hdr-skybox.js +35 -93
  34. package/lib/material/pbr/background-hdr-skybox.js.map +1 -1
  35. package/lib/material/pbr/background-solid-skybox.js +35 -93
  36. package/lib/material/pbr/background-solid-skybox.js.map +1 -1
  37. package/lib/material/pbr/fragments/clearcoat-fragment.js +5 -1
  38. package/lib/material/pbr/fragments/clearcoat-fragment.js.map +1 -1
  39. package/lib/material/pbr/fragments/iridescence-fragment.js +4 -1
  40. package/lib/material/pbr/fragments/iridescence-fragment.js.map +1 -1
  41. package/lib/material/pbr/fragments/refraction-rtt-fragment.js +7 -2
  42. package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
  43. package/lib/material/pbr/fragments/sheen-fragment.js +2 -1
  44. package/lib/material/pbr/fragments/sheen-fragment.js.map +1 -1
  45. package/lib/material/pbr/fragments/subsurface-fragment.js +2 -1
  46. package/lib/material/pbr/fragments/subsurface-fragment.js.map +1 -1
  47. package/lib/material/pbr/fragments/unlit-fragment.js +1 -2
  48. package/lib/material/pbr/fragments/unlit-fragment.js.map +1 -1
  49. package/lib/material/pbr/pbr-compose.js +2 -0
  50. package/lib/material/pbr/pbr-compose.js.map +1 -1
  51. package/lib/material/pbr/pbr-flag-bits.js +1 -16
  52. package/lib/material/pbr/pbr-flag-bits.js.map +1 -1
  53. package/lib/material/pbr/pbr-flags.js +1 -1
  54. package/lib/material/pbr/pbr-fog-wgsl.js +1 -1
  55. package/lib/material/pbr/pbr-fog-wgsl.js.map +1 -1
  56. package/lib/material/pbr/pbr-geometry-output-shader.js +2 -1
  57. package/lib/material/pbr/pbr-geometry-output-shader.js.map +1 -1
  58. package/lib/material/pbr/pbr-geometry-view.js +2 -1
  59. package/lib/material/pbr/pbr-geometry-view.js.map +1 -1
  60. package/lib/material/pbr/pbr-renderable.js +4 -0
  61. package/lib/material/pbr/pbr-renderable.js.map +1 -1
  62. package/lib/material/pbr/pbr-template-gamma.js +7 -0
  63. package/lib/material/pbr/pbr-template-gamma.js.map +1 -0
  64. package/lib/material/pbr/pbr-template.js +2 -2
  65. package/lib/material/pbr/pbr-template.js.map +1 -1
  66. package/lib/material/standard/skybox-cubemap.js +1 -1
  67. package/lib/material/standard/skybox-cubemap.js.map +1 -1
  68. package/lib/material/standard/standard-template.js +1 -1
  69. package/lib/material/standard/standard-template.js.map +1 -1
  70. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline-sh.js +3 -5
  71. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline-sh.js.map +1 -1
  72. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline.js +3 -5
  73. package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline.js.map +1 -1
  74. package/lib/particle/node/blocks/particle-condition-block.js +55 -0
  75. package/lib/particle/node/blocks/particle-condition-block.js.map +1 -0
  76. package/lib/particle/node/blocks/particle-float-to-int-block.js +28 -0
  77. package/lib/particle/node/blocks/particle-float-to-int-block.js.map +1 -0
  78. package/lib/particle/node/blocks/particle-gradient-block.js +32 -0
  79. package/lib/particle/node/blocks/particle-gradient-block.js.map +1 -0
  80. package/lib/particle/node/blocks/particle-gradient-value-block.js +11 -0
  81. package/lib/particle/node/blocks/particle-gradient-value-block.js.map +1 -0
  82. package/lib/particle/node/blocks/particle-lerp-block.js +2 -24
  83. package/lib/particle/node/blocks/particle-lerp-block.js.map +1 -1
  84. package/lib/particle/node/blocks/particle-lerp.js +27 -0
  85. package/lib/particle/node/blocks/particle-lerp.js.map +1 -0
  86. package/lib/particle/node/blocks/particle-vector-length-block.js +30 -0
  87. package/lib/particle/node/blocks/particle-vector-length-block.js.map +1 -0
  88. package/lib/particle/node/blocks/system-block.js +13 -3
  89. package/lib/particle/node/blocks/system-block.js.map +1 -1
  90. package/lib/particle/node/blocks/texture-source-block.js +2 -1
  91. package/lib/particle/node/blocks/texture-source-block.js.map +1 -1
  92. package/lib/particle/node/blocks/update-size-block.js +19 -0
  93. package/lib/particle/node/blocks/update-size-block.js.map +1 -0
  94. package/lib/particle/node/npe-build-state.js +10 -0
  95. package/lib/particle/node/npe-build-state.js.map +1 -1
  96. package/lib/particle/node/npe-registry.js +12 -0
  97. package/lib/particle/node/npe-registry.js.map +1 -1
  98. package/lib/particle/particle-system.js +7 -2
  99. package/lib/particle/particle-system.js.map +1 -1
  100. package/lib/picking/billboard-pick-pipeline.js +269 -0
  101. package/lib/picking/billboard-pick-pipeline.js.map +1 -0
  102. package/lib/picking/gpu-picker.js +37 -37
  103. package/lib/picking/gpu-picker.js.map +1 -1
  104. package/lib/picking/gs-picking-pipeline.js +26 -1
  105. package/lib/picking/gs-picking-pipeline.js.map +1 -1
  106. package/lib/picking/pick-contributor.js +13 -0
  107. package/lib/picking/pick-contributor.js.map +1 -0
  108. package/lib/picking/picking-info.js.map +1 -1
  109. package/lib/scene/scene-core.js +2 -2
  110. package/lib/scene/scene-core.js.map +1 -1
  111. package/lib/shader/wgsl-fog.js +18 -0
  112. package/lib/shader/wgsl-fog.js.map +1 -0
  113. package/lib/shader/wgsl-helpers.js +1 -16
  114. package/lib/shader/wgsl-helpers.js.map +1 -1
  115. package/lib/sprite/billboard-scene.js +2 -0
  116. package/lib/sprite/billboard-scene.js.map +1 -1
  117. package/lib/sprite/picking/pick-billboard.js +16 -0
  118. package/lib/sprite/picking/pick-billboard.js.map +1 -0
  119. package/lib/sprite/picking/pick-sprite-2d.js +41 -0
  120. package/lib/sprite/picking/pick-sprite-2d.js.map +1 -0
  121. package/lib/sprite/shared/sprite-atlas.js +4 -1
  122. package/lib/sprite/shared/sprite-atlas.js.map +1 -1
  123. package/lib/sprite/sprite-2d.js.map +1 -1
  124. package/lib/sprite/sprite-pipeline.js +18 -1
  125. package/lib/sprite/sprite-pipeline.js.map +1 -1
  126. package/lib/sprite/sprite-renderer.js +12 -6
  127. package/lib/sprite/sprite-renderer.js.map +1 -1
  128. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"gpu-picker.js","sources":["../../../src/picking/gpu-picker.ts"],"sourcesContent":["import { F32, U32, U8 } from \"../engine/typed-arrays.js\";\nimport { TU, BU } from \"../engine/gpu-flags.js\";\nimport type { SceneContext } from \"../scene/scene.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { PickingInfo } from \"./picking-info.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type * as DeformedGeometry from \"./deformed-geometry.js\";\nimport type * as GsPickingPipeline from \"./gs-picking-pipeline.js\";\nimport type { GaussianSplattingMesh } from \"../mesh/GaussianSplatting/gaussian-splatting-mesh.js\";\nimport { createEmptyPickingInfo } from \"./picking-info.js\";\nimport { createPickingRay } from \"./ray.js\";\nimport { mat4Invert } from \"../math/mat4-invert.js\";\nimport { getPickingPipelineSet, getPickingSceneBGL } from \"./picking-pipeline.js\";\nimport { getViewProjectionMatrix, getCameraPosition } from \"../camera/camera.js\";\nimport { resolveCameraViewport } from \"../camera/viewport.js\";\nimport { createEmptyUniformBuffer, createMappedBuffer, createUniformBuffer } from \"../resource/gpu-buffers.js\";\n\n// ─── Scratch arrays — allocated once, reused across all picks ──────\nconst _pickVP = new F32(16);\nconst _gsPickMatrix = new F32(16);\nconst PICK_MESH_UBO_BYTES = 80;\nconst PICK_TI_UBO_BYTES = 16;\nconst _uboScratch = new ArrayBuffer(PICK_MESH_UBO_BYTES);\nconst _uboF32 = new F32(_uboScratch);\nconst _uboU32 = new U32(_uboScratch);\nconst _uboView = new U8(_uboScratch);\nconst _tiUboScratch = new ArrayBuffer(PICK_TI_UBO_BYTES);\nconst _tiUboU32 = new U32(_tiUboScratch);\nconst _tiUboView = new U8(_tiUboScratch);\n\n/** GPU-based picker — pure state. Use pickAsync() and disposePicker() standalone functions. */\nexport interface GpuPicker {\n /** @internal Optional hook for detailed picking (Phase 2). */\n _detailedPick: ((info: PickingInfo, ray: { origin: [number, number, number]; direction: [number, number, number]; length: number }) => void | Promise<void>) | null;\n /** @internal */\n _scene: SceneContext;\n /** @internal 1×1 render targets (lazily created). */\n _rt: PickTargets1x1 | null;\n /** @internal Reusable scene UBO (64 bytes). */\n _sceneUbo: GPUBuffer | null;\n /** @internal Reusable scene bind group. */\n _sceneBG: GPUBindGroup | null;\n /** @internal Per-GS-mesh picking resources (created on demand). */\n _gsMeshResources: Map<GaussianSplattingMesh, GsPickingPipeline.GsPickMeshResources> | null;\n /** @internal Tail of the serialized pick queue for this picker — see pickAsync(). */\n _pending: Promise<void> | null;\n}\n\ninterface PickTargets1x1 {\n colorTex: GPUTexture;\n colorView: GPUTextureView;\n depthColorTex: GPUTexture;\n depthColorView: GPUTextureView;\n depthTex: GPUTexture;\n depthView: GPUTextureView;\n colorStaging: GPUBuffer;\n depthStaging: GPUBuffer;\n}\n\n/** Create a GPU picker bound to the given scene. */\nexport function createGpuPicker(scene: SceneContext): GpuPicker {\n return {\n _detailedPick: null,\n _scene: scene,\n _rt: null,\n _sceneUbo: null,\n _sceneBG: null,\n _gsMeshResources: null,\n _pending: null,\n };\n}\n\nfunction ensureTargets(engine: EngineContext, picker: GpuPicker): PickTargets1x1 {\n const device = engine._device;\n if (picker._rt) {\n return picker._rt;\n }\n const colorTex = device.createTexture({ label: \"pick-color\", size: [1, 1], format: \"rgba8unorm\", usage: TU.RENDER_ATTACHMENT | TU.COPY_SRC });\n const depthColorTex = device.createTexture({\n label: \"pick-depth-color\",\n size: [1, 1],\n format: \"r32float\",\n usage: TU.RENDER_ATTACHMENT | TU.COPY_SRC,\n });\n const depthTex = device.createTexture({ label: \"pick-depth\", size: [1, 1], format: \"depth24plus\", usage: TU.RENDER_ATTACHMENT });\n picker._rt = {\n colorTex,\n colorView: colorTex.createView(),\n depthColorTex,\n depthColorView: depthColorTex.createView(),\n depthTex,\n depthView: depthTex.createView(),\n colorStaging: device.createBuffer({ label: \"pick-color-staging\", size: 256, usage: BU.COPY_DST | BU.MAP_READ }),\n depthStaging: device.createBuffer({ label: \"pick-depth-staging\", size: 256, usage: BU.COPY_DST | BU.MAP_READ }),\n };\n return picker._rt;\n}\n\nfunction ensureSceneUbo(engine: EngineContext, picker: GpuPicker): GPUBuffer {\n const device = engine._device;\n if (!picker._sceneUbo) {\n picker._sceneUbo = createEmptyUniformBuffer(engine, 64, \"pick-scene-ubo\");\n const sceneBGL = getPickingSceneBGL(engine);\n picker._sceneBG = device.createBindGroup({ label: \"pick-scene-bg\", layout: sceneBGL, entries: [{ binding: 0, resource: { buffer: picker._sceneUbo } }] });\n }\n return picker._sceneUbo;\n}\n\n/** Compute a VP matrix zoomed to a single pixel at (px, py) on a W×H canvas.\n * Renders to a 1×1 target — only fragments at the picked pixel survive. */\nfunction computePickVP(out: Float32Array, vp: Float32Array, px: number, py: number, w: number, h: number): void {\n const ndcX = (2 * (px + 0.5)) / w - 1;\n const ndcY = 1 - (2 * (py + 0.5)) / h;\n // pickVP = pickMatrix * VP (sparse multiply, see derivation in comments)\n for (let c = 0; c < 4; c++) {\n const base = c * 4;\n const w3 = vp[base + 3]!;\n out[base] = w * (vp[base]! - ndcX * w3);\n out[base + 1] = h * (vp[base + 1]! - ndcY * w3);\n out[base + 2] = vp[base + 2]!;\n out[base + 3] = w3;\n }\n}\n\n/** Options for {@link pickAsync}. */\nexport interface PickOptions {\n /** Restrict the pick to a subset of the scene's meshes — return `true` for a mesh that may be picked,\n * `false` to ignore it entirely (it neither occludes nor is returned). Lets a caller provide its\n * \"list of pickables\" so decorative meshes (grass, foliage, particles, …) can't swallow a pick of a\n * structure behind/around them. When omitted, every mesh is pickable (previous behaviour). Applied\n * identically to the id-assignment and id-resolve passes so ids stay consistent. */\n filter?: (mesh: Mesh) => boolean;\n /** Optional GPU fragment-discard extension for app-specific pick removal.\n *\n * `wgsl` is injected into the regular and thin-instance picking shaders and must define:\n *\n * `fn shouldDiscardPick(input: PickDiscardInput) -> bool`\n *\n * The input exposes only generic picker data: `worldPos`, `pickId`, `thinInstanceIndex`,\n * `hasThinInstance`, and `instanceExtras` (the original thin-instance matrix w lanes, zero for\n * non-instanced meshes). Storage entries are uploaded and bound by Lite for the current pick only. */\n discard?: PickDiscardRule;\n /** Dev-only diagnostics: logs the pick ray, pixel, pick id/depth and resolved mesh. */\n debugLabel?: string;\n}\n\n/**\n * Optional GPU-side discard rule for {@link pickAsync}.\n *\n * This lets apps remove pick hits with custom WGSL while keeping the main scene\n * render untouched. The WGSL must define\n * `fn shouldDiscardPick(input: PickDiscardInput) -> bool`.\n */\nexport interface PickDiscardRule {\n /** Stable cache key for the generated picking pipeline set. Change it when the WGSL or layout changes. */\n readonly key: string;\n /** WGSL source that defines `shouldDiscardPick(input: PickDiscardInput) -> bool`. */\n readonly wgsl: string;\n /** Optional typed-array storage inputs exposed to the discard WGSL at group 2. */\n readonly storage?: readonly PickDiscardStorage[];\n}\n\n/** Storage data for a pick discard rule. Lite injects the WGSL declaration and owns the GPU buffer upload. */\nexport interface PickDiscardStorage {\n /** WGSL variable name declared at `@group(2) @binding(index)`; must be unique within the rule. */\n readonly name: string;\n /** WGSL storage type, for example `array<vec4<f32>>`. */\n readonly type: string;\n /** Per-mesh data for the current pick. Return `null` to draw that mesh with the default picker. */\n readonly data: (mesh: Mesh) => ArrayBufferView | null | undefined;\n}\n\nfunction createPickDiscardBindGroup(engine: EngineContext, layout: GPUBindGroupLayout, discard: PickDiscardRule, mesh: Mesh, tempBuffers: GPUBuffer[]): GPUBindGroup | null {\n const storage = discard.storage;\n if (!storage || storage.length === 0) {\n return null;\n }\n const entries: GPUBindGroupEntry[] = [];\n for (let i = 0; i < storage.length; i++) {\n const data = storage[i]!.data(mesh);\n if (!data) {\n return null;\n }\n const buffer = createMappedBuffer(engine, data, BU.STORAGE);\n tempBuffers.push(buffer);\n entries.push({ binding: i, resource: { buffer } });\n }\n const device = engine._device;\n return device.createBindGroup({\n label: `pick-discard-${discard.key}-bg`,\n layout,\n entries,\n });\n}\n\n/** Pick the mesh at CSS-space canvas coordinates, matching Babylon.js Scene.pick. Returns a PickingInfo.\n * Does the actual GPU render + readback for one pick — call `pickAsync` (below) instead; it serializes\n * concurrent calls on the same picker so their shared 1×1 staging buffers never race. */\nasync function pickAsyncImpl(picker: GpuPicker, x: number, y: number, options?: PickOptions): Promise<PickingInfo> {\n const scene = picker._scene;\n const pickFilter = options?.filter ?? null;\n const pickDiscard = options?.discard ?? null;\n const debugLabel = options?.debugLabel;\n const engine = scene.surface.engine;\n const device = engine._device;\n // Pick coordinates are relative to the scene's own surface canvas, not the engine's\n // primary canvas — they differ when the scene renders into an auxiliary surface.\n const canvas = scene.surface.canvas;\n const camera = scene.camera;\n if (!camera) {\n return createEmptyPickingInfo();\n }\n\n const backingWidth = canvas.width;\n const backingHeight = canvas.height;\n const clientWidth = (\"clientWidth\" in canvas ? canvas.clientWidth : 0) || backingWidth;\n const clientHeight = (\"clientHeight\" in canvas ? canvas.clientHeight : 0) || backingHeight;\n const scaleX = backingWidth / clientWidth;\n const scaleY = backingHeight / clientHeight;\n const pickX = x * scaleX;\n const pickY = y * scaleY;\n const viewport = resolveCameraViewport(camera, backingWidth, backingHeight);\n const w = viewport.width;\n const h = viewport.height;\n if (w === 0 || h === 0) {\n return createEmptyPickingInfo();\n }\n\n if (pickX < viewport.x || pickY < viewport.y || pickX >= viewport.x + viewport.width || pickY >= viewport.y + viewport.height) {\n return createEmptyPickingInfo();\n }\n\n const px = Math.max(0, Math.min(Math.floor(pickX - viewport.x), w - 1));\n const py = Math.max(0, Math.min(Math.floor(pickY - viewport.y), h - 1));\n const aspect = w / h;\n const vp = getViewProjectionMatrix(camera, aspect);\n const debugRay = debugLabel ? createPickingRay(px, py, vp, w, h) : null;\n\n // ── Compute pick-zoomed VP (renders single pixel to 1×1 target) ──\n computePickVP(_pickVP, vp as unknown as Float32Array, px, py, w, h);\n\n const rt = ensureTargets(engine, picker);\n const sceneUbo = ensureSceneUbo(engine, picker);\n device.queue.writeBuffer(sceneUbo, 0, _pickVP);\n\n // ── Assign pick IDs (array-based, no Map for miss case) ──────────\n const meshes = scene.meshes;\n const meshCount = meshes.length;\n let nextId = 1;\n let deformedGeometry: typeof DeformedGeometry | null = null;\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n if ((mesh.morphTargets || mesh.skeleton) && mesh._cpuPositions) {\n deformedGeometry = await import(\"./deformed-geometry.js\");\n break;\n }\n }\n\n // ── Render pass (1×1 target) ─────────────────────────────────────\n const encoder = device.createCommandEncoder({ label: \"pick\" });\n const pass = encoder.beginRenderPass({\n colorAttachments: [\n { view: rt.colorView, clearValue: { r: 0, g: 0, b: 0, a: 0 }, loadOp: \"clear\", storeOp: \"store\" },\n { view: rt.depthColorView, clearValue: { r: 1, g: 0, b: 0, a: 0 }, loadOp: \"clear\", storeOp: \"store\" },\n ],\n depthStencilAttachment: { view: rt.depthView, depthClearValue: 0, depthLoadOp: \"clear\", depthStoreOp: \"discard\" },\n });\n\n const defaultPipelines = getPickingPipelineSet(engine);\n const discardPipelines = pickDiscard ? getPickingPipelineSet(engine, pickDiscard) : null;\n\n const tempBuffers: GPUBuffer[] = [];\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n // Skip meshes explicitly marked non-pickable. Mirrors BJS\n // `isPickable = false` — lets gizmos toggle display-only helpers\n // (e.g. the rotation-sector display plane) out of the picker without\n // affecting their rendering or hiding them from picks via `visible`.\n if (mesh.pickable === false) {\n continue; // not drawn AND not given an id (skipped identically below)\n }\n if (pickFilter && !pickFilter(mesh)) {\n continue; // excluded from picking → not drawn AND not given an id (skipped identically below)\n }\n const gpu = mesh._gpu;\n const ti = mesh.thinInstances;\n const discardBG = pickDiscard && discardPipelines?.discardBGL ? createPickDiscardBindGroup(engine, discardPipelines.discardBGL, pickDiscard, mesh, tempBuffers) : null;\n const pipelines = discardPipelines && (!discardPipelines.discardBGL || discardBG) ? discardPipelines : defaultPipelines;\n\n if (ti) {\n if (ti.count <= 0 || !ti._gpuBuffer) {\n continue;\n }\n _tiUboU32[0] = nextId;\n const tiUbo = createUniformBuffer(engine, _tiUboView);\n tempBuffers.push(tiUbo);\n\n pass.setPipeline(pipelines.thinInstancePipeline);\n pass.setBindGroup(0, picker._sceneBG!);\n pass.setBindGroup(\n 1,\n device.createBindGroup({\n layout: pipelines.thinInstancePipeline.getBindGroupLayout(1),\n entries: [\n { binding: 0, resource: { buffer: tiUbo } },\n { binding: 1, resource: { buffer: ti._gpuBuffer } },\n ],\n })\n );\n if (discardBG) {\n pass.setBindGroup(2, discardBG);\n }\n pass.setVertexBuffer(0, gpu.positionBuffer);\n pass.setIndexBuffer(gpu.indexBuffer, gpu.indexFormat);\n pass.drawIndexed(gpu.indexCount, ti.count);\n nextId += ti.count;\n } else {\n _uboF32.set(mesh.worldMatrix, 0);\n _uboU32[16] = nextId;\n const meshUbo = createUniformBuffer(engine, _uboView);\n tempBuffers.push(meshUbo);\n let positionBuffer = gpu.positionBuffer;\n if (deformedGeometry && (mesh.morphTargets || mesh.skeleton) && mesh._cpuPositions) {\n const deformedPositions = deformedGeometry.computeDeformedPositions(mesh);\n if (deformedPositions) {\n positionBuffer = createMappedBuffer(engine, deformedPositions, BU.VERTEX);\n tempBuffers.push(positionBuffer);\n }\n }\n\n pass.setPipeline(pipelines.regularPipeline);\n pass.setBindGroup(0, picker._sceneBG!);\n pass.setBindGroup(\n 1,\n device.createBindGroup({\n layout: pipelines.regularPipeline.getBindGroupLayout(1),\n entries: [{ binding: 0, resource: { buffer: meshUbo } }],\n })\n );\n if (discardBG) {\n pass.setBindGroup(2, discardBG);\n }\n pass.setVertexBuffer(0, positionBuffer);\n pass.setIndexBuffer(gpu.indexBuffer, gpu.indexFormat);\n pass.drawIndexed(gpu.indexCount);\n nextId++;\n }\n }\n\n // ── Gaussian-splatting meshes ────────────────────────────────────\n // Drawn from the same pass against the same depth target. Each GS mesh\n // gets one pick id (no thin-instance support — BJS GS picking is per-mesh\n // too). The GS picking pipeline applies an independent pickMatrix to the\n // GS clip-space output so the EWA Jacobian / `u.focal` math stays intact.\n const gsMeshes = (scene as unknown as { _gsMeshes: GaussianSplattingMesh[] })._gsMeshes;\n const gsMeshCount = gsMeshes.length;\n const gsNextIdStart = nextId;\n if (gsMeshCount > 0) {\n const gsModule = await import(\"./gs-picking-pipeline.js\");\n gsModule.computeGsPickMatrix(_gsPickMatrix, px, py, w, h);\n gsModule.gsPickWritePickMatrixAndBind(pass, engine, _gsPickMatrix);\n const resMap = picker._gsMeshResources ?? (picker._gsMeshResources = new Map());\n for (let gi = 0; gi < gsMeshCount; gi++) {\n const gsMesh = gsMeshes[gi]!;\n let res = resMap.get(gsMesh);\n if (!res) {\n res = gsModule.createGsPickMeshResources(engine, gsMesh);\n resMap.set(gsMesh, res);\n }\n gsModule.drawGsForPicking(pass, engine, scene, gsMesh, res, nextId, w, h);\n nextId++;\n }\n }\n pass.end();\n\n // ── Readback (both 1×1 — trivially small) ────────────────────────\n encoder.copyTextureToBuffer({ texture: rt.colorTex }, { buffer: rt.colorStaging, bytesPerRow: 256 }, { width: 1, height: 1 });\n encoder.copyTextureToBuffer({ texture: rt.depthColorTex }, { buffer: rt.depthStaging, bytesPerRow: 256 }, { width: 1, height: 1 });\n device.queue.submit([encoder.finish()]);\n\n await Promise.all([rt.colorStaging.mapAsync(GPUMapMode.READ), rt.depthStaging.mapAsync(GPUMapMode.READ)]);\n\n const colorData = new U8(rt.colorStaging.getMappedRange());\n const pickId = (colorData[0]! << 16) | (colorData[1]! << 8) | colorData[2]!;\n const depth = new F32(rt.depthStaging.getMappedRange())[0]!;\n rt.colorStaging.unmap();\n rt.depthStaging.unmap();\n\n // Destroy temp per-mesh UBOs\n for (let i = 0; i < tempBuffers.length; i++) {\n tempBuffers[i]!.destroy();\n }\n\n // ── Resolve pick ID to mesh ──────────────────────────────────────\n if (pickId === 0) {\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: debugRay,\n pickId,\n depth,\n hit: false,\n });\n }\n return createEmptyPickingInfo();\n }\n let hitMesh: Mesh | GaussianSplattingMesh | null = null;\n let hitThinIdx = -1;\n let hitIsGs = false;\n let scanId = 1;\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n if (mesh.pickable === false) {\n continue; // skipped identically to the draw pass above so scanId stays aligned with the ids\n }\n if (pickFilter && !pickFilter(mesh)) {\n continue; // skipped identically to the draw pass above so scanId stays aligned with the ids\n }\n const ti = mesh.thinInstances;\n if (ti) {\n if (ti.count <= 0 || !ti._gpuBuffer) {\n continue;\n }\n if (pickId >= scanId && pickId < scanId + ti.count) {\n hitMesh = mesh;\n hitThinIdx = pickId - scanId;\n break;\n }\n scanId += ti.count;\n } else {\n if (pickId === scanId) {\n hitMesh = mesh;\n break;\n }\n scanId++;\n }\n }\n if (!hitMesh && gsMeshCount > 0 && pickId >= gsNextIdStart) {\n const gsIdx = pickId - gsNextIdStart;\n if (gsIdx < gsMeshCount) {\n hitMesh = gsMeshes[gsIdx]!;\n hitIsGs = true;\n }\n }\n if (!hitMesh) {\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: debugRay,\n pickId,\n depth,\n hit: false,\n unresolved: true,\n });\n }\n return createEmptyPickingInfo();\n }\n\n const info = createEmptyPickingInfo();\n info.hit = true;\n info.pickedMesh = hitMesh;\n info.thinInstanceIndex = hitThinIdx;\n\n // Reconstruct world position from depth (using original full-res VP)\n const invVP = mat4Invert(vp);\n if (invVP) {\n const ndcX = (2 * (pickX - viewport.x)) / w - 1;\n const ndcY = 1 - (2 * (pickY - viewport.y)) / h;\n const wx = invVP[0]! * ndcX + invVP[4]! * ndcY + invVP[8]! * depth + invVP[12]!;\n const wy = invVP[1]! * ndcX + invVP[5]! * ndcY + invVP[9]! * depth + invVP[13]!;\n const wz = invVP[2]! * ndcX + invVP[6]! * ndcY + invVP[10]! * depth + invVP[14]!;\n const ww = invVP[3]! * ndcX + invVP[7]! * ndcY + invVP[11]! * depth + invVP[15]!;\n const invW = 1 / ww;\n info.pickedPoint = [wx * invW, wy * invW, wz * invW];\n\n const camPos = getCameraPosition(camera);\n const dx = info.pickedPoint[0] - camPos.x;\n const dy = info.pickedPoint[1] - camPos.y;\n const dz = info.pickedPoint[2] - camPos.z;\n info.distance = Math.sqrt(dx * dx + dy * dy + dz * dz);\n }\n\n if (picker._detailedPick && !hitIsGs) {\n const ray = createPickingRay(pickX - viewport.x, pickY - viewport.y, vp, w, h);\n if (ray) {\n info.ray = ray;\n await picker._detailedPick(info, ray);\n }\n }\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: info.ray ?? debugRay,\n pickId,\n depth,\n hit: true,\n mesh: (hitMesh as Mesh).name ?? \"(unnamed)\",\n thinInstanceIndex: hitThinIdx,\n pickedPoint: info.pickedPoint,\n distance: info.distance,\n });\n }\n\n return info;\n}\n\n/**\n * Pick the mesh at CSS-space canvas coordinates, matching Babylon.js Scene.pick. Returns a PickingInfo.\n *\n * A picker's 1×1 readback targets (`PickTargets1x1.colorStaging`/`depthStaging`) are lazily created ONCE\n * and reused for every pick — cheap, but it means two overlapping `pickAsync` calls on the SAME picker\n * race `mapAsync` on those shared buffers (\"Buffer already has an outstanding map pending\", since a\n * GPUBuffer allows only one pending map at a time). This is easy to trigger from a consumer: e.g. a\n * cursor-following hover preview that GPU-picks on every pointermove, racing a pick fired by a click that\n * lands before the hover's pick has unmapped. Queue concurrent calls per-picker instead of rejecting: each\n * pick's full map/unmap cycle completes before the next one starts.\n */\nexport function pickAsync(picker: GpuPicker, x: number, y: number, options?: PickOptions): Promise<PickingInfo> {\n const prior = picker._pending ?? Promise.resolve();\n const run = prior.then(\n () => pickAsyncImpl(picker, x, y, options),\n () => pickAsyncImpl(picker, x, y, options) // a prior pick's rejection must not wedge the queue for this caller\n );\n // Swallow so a rejection here doesn't propagate into the NEXT caller's chain (each caller gets its own\n // `run` promise and observes the real rejection via the returned promise, not via `_pending`).\n picker._pending = run.then(\n () => undefined,\n () => undefined\n );\n return run;\n}\n\n/** Dispose GPU resources owned by this picker. */\nexport function disposePicker(picker: GpuPicker): void {\n if (picker._rt) {\n picker._rt.colorTex.destroy();\n picker._rt.depthColorTex.destroy();\n picker._rt.depthTex.destroy();\n picker._rt.colorStaging.destroy();\n picker._rt.depthStaging.destroy();\n picker._rt = null;\n }\n if (picker._sceneUbo) {\n picker._sceneUbo.destroy();\n picker._sceneUbo = null;\n picker._sceneBG = null;\n }\n if (picker._gsMeshResources) {\n // Async dispose — destroy() is synchronous so we can run inline once\n // the module is loaded. If the module was never imported (no GS pick\n // ever happened) the map is empty and there's nothing to do.\n void import(\"./gs-picking-pipeline.js\").then((m) => {\n if (!picker._gsMeshResources) {\n return;\n }\n for (const res of picker._gsMeshResources.values()) {\n m.disposeGsPickMeshResources(res);\n }\n picker._gsMeshResources = null;\n });\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gpu-picker.js","sources":["../../../src/picking/gpu-picker.ts"],"sourcesContent":["import { F32, U32, U8 } from \"../engine/typed-arrays.js\";\nimport { TU, BU } from \"../engine/gpu-flags.js\";\nimport type { SceneContext } from \"../scene/scene.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { PickingInfo } from \"./picking-info.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type * as DeformedGeometry from \"./deformed-geometry.js\";\nimport type { PickContributor, PickSource, PickPassContext } from \"./pick-contributor.js\";\nimport { createEmptyPickingInfo } from \"./picking-info.js\";\nimport { createPickingRay } from \"./ray.js\";\nimport { mat4Invert } from \"../math/mat4-invert.js\";\nimport { getPickingPipelineSet, getPickingSceneBGL } from \"./picking-pipeline.js\";\nimport { getViewProjectionMatrix, getCameraPosition } from \"../camera/camera.js\";\nimport { resolveCameraViewport } from \"../camera/viewport.js\";\nimport { createEmptyUniformBuffer, createMappedBuffer, createUniformBuffer } from \"../resource/gpu-buffers.js\";\n\n// ─── Scratch arrays — allocated once, reused across all picks ──────\nconst _pickVP = new F32(16);\nconst PICK_MESH_UBO_BYTES = 80;\nconst PICK_TI_UBO_BYTES = 16;\nconst _uboScratch = new ArrayBuffer(PICK_MESH_UBO_BYTES);\nconst _uboF32 = new F32(_uboScratch);\nconst _uboU32 = new U32(_uboScratch);\nconst _uboView = new U8(_uboScratch);\nconst _tiUboScratch = new ArrayBuffer(PICK_TI_UBO_BYTES);\nconst _tiUboU32 = new U32(_tiUboScratch);\nconst _tiUboView = new U8(_tiUboScratch);\n\n/** GPU-based picker — pure state. Use pickAsync() and disposePicker() standalone functions. */\nexport interface GpuPicker {\n /** @internal Optional hook for detailed picking (Phase 2). */\n _detailedPick: ((info: PickingInfo, ray: { origin: [number, number, number]; direction: [number, number, number]; length: number }) => void | Promise<void>) | null;\n /** @internal */\n _scene: SceneContext;\n /** @internal 1×1 render targets (lazily created). */\n _rt: PickTargets1x1 | null;\n /** @internal Reusable scene UBO (64 bytes). */\n _sceneUbo: GPUBuffer | null;\n /** @internal Reusable scene bind group. */\n _sceneBG: GPUBindGroup | null;\n /** @internal Contributor built per pick source (once per picker) and cached, so each contributor's\n * GPU pick resources live in its closure and dispose generically — the picker never names an\n * entity type. */\n _contributors: Map<PickSource, PickContributor> | null;\n /** @internal Tail of the serialized pick queue for this picker — see pickAsync(). */\n _pending: Promise<void> | null;\n}\n\ninterface PickTargets1x1 {\n colorTex: GPUTexture;\n colorView: GPUTextureView;\n depthColorTex: GPUTexture;\n depthColorView: GPUTextureView;\n depthTex: GPUTexture;\n depthView: GPUTextureView;\n colorStaging: GPUBuffer;\n depthStaging: GPUBuffer;\n}\n\n/** Create a GPU picker bound to the given scene. */\nexport function createGpuPicker(scene: SceneContext): GpuPicker {\n return {\n _detailedPick: null,\n _scene: scene,\n _rt: null,\n _sceneUbo: null,\n _sceneBG: null,\n _contributors: null,\n _pending: null,\n };\n}\n\nfunction ensureTargets(engine: EngineContext, picker: GpuPicker): PickTargets1x1 {\n const device = engine._device;\n if (picker._rt) {\n return picker._rt;\n }\n const colorTex = device.createTexture({ label: \"pick-color\", size: [1, 1], format: \"rgba8unorm\", usage: TU.RENDER_ATTACHMENT | TU.COPY_SRC });\n const depthColorTex = device.createTexture({\n label: \"pick-depth-color\",\n size: [1, 1],\n format: \"r32float\",\n usage: TU.RENDER_ATTACHMENT | TU.COPY_SRC,\n });\n const depthTex = device.createTexture({ label: \"pick-depth\", size: [1, 1], format: \"depth24plus\", usage: TU.RENDER_ATTACHMENT });\n picker._rt = {\n colorTex,\n colorView: colorTex.createView(),\n depthColorTex,\n depthColorView: depthColorTex.createView(),\n depthTex,\n depthView: depthTex.createView(),\n colorStaging: device.createBuffer({ label: \"pick-color-staging\", size: 256, usage: BU.COPY_DST | BU.MAP_READ }),\n depthStaging: device.createBuffer({ label: \"pick-depth-staging\", size: 256, usage: BU.COPY_DST | BU.MAP_READ }),\n };\n return picker._rt;\n}\n\nfunction ensureSceneUbo(engine: EngineContext, picker: GpuPicker): GPUBuffer {\n const device = engine._device;\n if (!picker._sceneUbo) {\n picker._sceneUbo = createEmptyUniformBuffer(engine, 64, \"pick-scene-ubo\");\n const sceneBGL = getPickingSceneBGL(engine);\n picker._sceneBG = device.createBindGroup({ label: \"pick-scene-bg\", layout: sceneBGL, entries: [{ binding: 0, resource: { buffer: picker._sceneUbo } }] });\n }\n return picker._sceneUbo;\n}\n\n/** Compute a VP matrix zoomed to a single pixel at (px, py) on a W×H canvas.\n * Renders to a 1×1 target — only fragments at the picked pixel survive. */\nfunction computePickVP(out: Float32Array, vp: Float32Array, px: number, py: number, w: number, h: number): void {\n const ndcX = (2 * (px + 0.5)) / w - 1;\n const ndcY = 1 - (2 * (py + 0.5)) / h;\n // pickVP = pickMatrix * VP (sparse multiply, see derivation in comments)\n for (let c = 0; c < 4; c++) {\n const base = c * 4;\n const w3 = vp[base + 3]!;\n out[base] = w * (vp[base]! - ndcX * w3);\n out[base + 1] = h * (vp[base + 1]! - ndcY * w3);\n out[base + 2] = vp[base + 2]!;\n out[base + 3] = w3;\n }\n}\n\n/** Options for {@link pickAsync}. */\nexport interface PickOptions {\n /** Restrict the pick to a subset of the scene's meshes — return `true` for a mesh that may be picked,\n * `false` to ignore it entirely (it neither occludes nor is returned). Lets a caller provide its\n * \"list of pickables\" so decorative meshes (grass, foliage, particles, …) can't swallow a pick of a\n * structure behind/around them. When omitted, every mesh is pickable (previous behaviour). Applied\n * identically to the id-assignment and id-resolve passes so ids stay consistent. */\n filter?: (mesh: Mesh) => boolean;\n /** Optional GPU fragment-discard extension for app-specific pick removal.\n *\n * `wgsl` is injected into the regular and thin-instance picking shaders and must define:\n *\n * `fn shouldDiscardPick(input: PickDiscardInput) -> bool`\n *\n * The input exposes only generic picker data: `worldPos`, `pickId`, `thinInstanceIndex`,\n * `hasThinInstance`, and `instanceExtras` (the original thin-instance matrix w lanes, zero for\n * non-instanced meshes). Storage entries are uploaded and bound by Lite for the current pick only. */\n discard?: PickDiscardRule;\n /** Dev-only diagnostics: logs the pick ray, pixel, pick id/depth and resolved mesh. */\n debugLabel?: string;\n}\n\n/**\n * Optional GPU-side discard rule for {@link pickAsync}.\n *\n * This lets apps remove pick hits with custom WGSL while keeping the main scene\n * render untouched. The WGSL must define\n * `fn shouldDiscardPick(input: PickDiscardInput) -> bool`.\n */\nexport interface PickDiscardRule {\n /** Stable cache key for the generated picking pipeline set. Change it when the WGSL or layout changes. */\n readonly key: string;\n /** WGSL source that defines `shouldDiscardPick(input: PickDiscardInput) -> bool`. */\n readonly wgsl: string;\n /** Optional typed-array storage inputs exposed to the discard WGSL at group 2. */\n readonly storage?: readonly PickDiscardStorage[];\n}\n\n/** Storage data for a pick discard rule. Lite injects the WGSL declaration and owns the GPU buffer upload. */\nexport interface PickDiscardStorage {\n /** WGSL variable name declared at `@group(2) @binding(index)`; must be unique within the rule. */\n readonly name: string;\n /** WGSL storage type, for example `array<vec4<f32>>`. */\n readonly type: string;\n /** Per-mesh data for the current pick. Return `null` to draw that mesh with the default picker. */\n readonly data: (mesh: Mesh) => ArrayBufferView | null | undefined;\n}\n\nfunction createPickDiscardBindGroup(engine: EngineContext, layout: GPUBindGroupLayout, discard: PickDiscardRule, mesh: Mesh, tempBuffers: GPUBuffer[]): GPUBindGroup | null {\n const storage = discard.storage;\n if (!storage || storage.length === 0) {\n return null;\n }\n const entries: GPUBindGroupEntry[] = [];\n for (let i = 0; i < storage.length; i++) {\n const data = storage[i]!.data(mesh);\n if (!data) {\n return null;\n }\n const buffer = createMappedBuffer(engine, data, BU.STORAGE);\n tempBuffers.push(buffer);\n entries.push({ binding: i, resource: { buffer } });\n }\n const device = engine._device;\n return device.createBindGroup({\n label: `pick-discard-${discard.key}-bg`,\n layout,\n entries,\n });\n}\n\n/** Pick the mesh at CSS-space canvas coordinates, matching Babylon.js Scene.pick. Returns a PickingInfo.\n * Does the actual GPU render + readback for one pick — call `pickAsync` (below) instead; it serializes\n * concurrent calls on the same picker so their shared 1×1 staging buffers never race. */\nasync function pickAsyncImpl(picker: GpuPicker, x: number, y: number, options?: PickOptions): Promise<PickingInfo> {\n const scene = picker._scene;\n const pickFilter = options?.filter ?? null;\n const pickDiscard = options?.discard ?? null;\n const debugLabel = options?.debugLabel;\n const engine = scene.surface.engine;\n const device = engine._device;\n // Pick coordinates are relative to the scene's own surface canvas, not the engine's\n // primary canvas — they differ when the scene renders into an auxiliary surface.\n const canvas = scene.surface.canvas;\n const camera = scene.camera;\n if (!camera) {\n return createEmptyPickingInfo();\n }\n\n const backingWidth = canvas.width;\n const backingHeight = canvas.height;\n const clientWidth = (\"clientWidth\" in canvas ? canvas.clientWidth : 0) || backingWidth;\n const clientHeight = (\"clientHeight\" in canvas ? canvas.clientHeight : 0) || backingHeight;\n const scaleX = backingWidth / clientWidth;\n const scaleY = backingHeight / clientHeight;\n const pickX = x * scaleX;\n const pickY = y * scaleY;\n const viewport = resolveCameraViewport(camera, backingWidth, backingHeight);\n const w = viewport.width;\n const h = viewport.height;\n if (w === 0 || h === 0) {\n return createEmptyPickingInfo();\n }\n\n if (pickX < viewport.x || pickY < viewport.y || pickX >= viewport.x + viewport.width || pickY >= viewport.y + viewport.height) {\n return createEmptyPickingInfo();\n }\n\n const px = Math.max(0, Math.min(Math.floor(pickX - viewport.x), w - 1));\n const py = Math.max(0, Math.min(Math.floor(pickY - viewport.y), h - 1));\n const aspect = w / h;\n const vp = getViewProjectionMatrix(camera, aspect);\n const debugRay = debugLabel ? createPickingRay(px, py, vp, w, h) : null;\n\n // ── Compute pick-zoomed VP (renders single pixel to 1×1 target) ──\n computePickVP(_pickVP, vp as unknown as Float32Array, px, py, w, h);\n\n const rt = ensureTargets(engine, picker);\n const sceneUbo = ensureSceneUbo(engine, picker);\n device.queue.writeBuffer(sceneUbo, 0, _pickVP);\n\n // ── Assign pick IDs (array-based, no Map for miss case) ──────────\n const meshes = scene.meshes;\n const meshCount = meshes.length;\n let nextId = 1;\n let deformedGeometry: typeof DeformedGeometry | null = null;\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n if ((mesh.morphTargets || mesh.skeleton) && mesh._cpuPositions) {\n deformedGeometry = await import(\"./deformed-geometry.js\");\n break;\n }\n }\n\n // ── Render pass (1×1 target) ─────────────────────────────────────\n const encoder = device.createCommandEncoder({ label: \"pick\" });\n const pass = encoder.beginRenderPass({\n colorAttachments: [\n { view: rt.colorView, clearValue: { r: 0, g: 0, b: 0, a: 0 }, loadOp: \"clear\", storeOp: \"store\" },\n { view: rt.depthColorView, clearValue: { r: 1, g: 0, b: 0, a: 0 }, loadOp: \"clear\", storeOp: \"store\" },\n ],\n depthStencilAttachment: { view: rt.depthView, depthClearValue: 0, depthLoadOp: \"clear\", depthStoreOp: \"discard\" },\n });\n\n const defaultPipelines = getPickingPipelineSet(engine);\n const discardPipelines = pickDiscard ? getPickingPipelineSet(engine, pickDiscard) : null;\n\n const tempBuffers: GPUBuffer[] = [];\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n // Skip meshes explicitly marked non-pickable. Mirrors BJS\n // `isPickable = false` — lets gizmos toggle display-only helpers\n // (e.g. the rotation-sector display plane) out of the picker without\n // affecting their rendering or hiding them from picks via `visible`.\n if (mesh.pickable === false) {\n continue; // not drawn AND not given an id (skipped identically below)\n }\n if (pickFilter && !pickFilter(mesh)) {\n continue; // excluded from picking → not drawn AND not given an id (skipped identically below)\n }\n const gpu = mesh._gpu;\n const ti = mesh.thinInstances;\n const discardBG = pickDiscard && discardPipelines?.discardBGL ? createPickDiscardBindGroup(engine, discardPipelines.discardBGL, pickDiscard, mesh, tempBuffers) : null;\n const pipelines = discardPipelines && (!discardPipelines.discardBGL || discardBG) ? discardPipelines : defaultPipelines;\n\n if (ti) {\n if (ti.count <= 0 || !ti._gpuBuffer) {\n continue;\n }\n _tiUboU32[0] = nextId;\n const tiUbo = createUniformBuffer(engine, _tiUboView);\n tempBuffers.push(tiUbo);\n\n pass.setPipeline(pipelines.thinInstancePipeline);\n pass.setBindGroup(0, picker._sceneBG!);\n pass.setBindGroup(\n 1,\n device.createBindGroup({\n layout: pipelines.thinInstancePipeline.getBindGroupLayout(1),\n entries: [\n { binding: 0, resource: { buffer: tiUbo } },\n { binding: 1, resource: { buffer: ti._gpuBuffer } },\n ],\n })\n );\n if (discardBG) {\n pass.setBindGroup(2, discardBG);\n }\n pass.setVertexBuffer(0, gpu.positionBuffer);\n pass.setIndexBuffer(gpu.indexBuffer, gpu.indexFormat);\n pass.drawIndexed(gpu.indexCount, ti.count);\n nextId += ti.count;\n } else {\n _uboF32.set(mesh.worldMatrix, 0);\n _uboU32[16] = nextId;\n const meshUbo = createUniformBuffer(engine, _uboView);\n tempBuffers.push(meshUbo);\n let positionBuffer = gpu.positionBuffer;\n if (deformedGeometry && (mesh.morphTargets || mesh.skeleton) && mesh._cpuPositions) {\n const deformedPositions = deformedGeometry.computeDeformedPositions(mesh);\n if (deformedPositions) {\n positionBuffer = createMappedBuffer(engine, deformedPositions, BU.VERTEX);\n tempBuffers.push(positionBuffer);\n }\n }\n\n pass.setPipeline(pipelines.regularPipeline);\n pass.setBindGroup(0, picker._sceneBG!);\n pass.setBindGroup(\n 1,\n device.createBindGroup({\n layout: pipelines.regularPipeline.getBindGroupLayout(1),\n entries: [{ binding: 0, resource: { buffer: meshUbo } }],\n })\n );\n if (discardBG) {\n pass.setBindGroup(2, discardBG);\n }\n pass.setVertexBuffer(0, positionBuffer);\n pass.setIndexBuffer(gpu.indexBuffer, gpu.indexFormat);\n pass.drawIndexed(gpu.indexCount);\n nextId++;\n }\n }\n\n // ── Pick contributors (optional entity types) ───────────────────\n // Meshes above own ids 1..M. Each registered contributor (a GS mesh, a billboard system, …)\n // then draws into the SAME pass against the SAME depth target — so contributor entities\n // depth-sort against meshes and each other (an occluded one loses the pick) — and owns a\n // contiguous id range [base, next). The picker names no entity type: the per-type draw,\n // resolve, resources, and view math all live in the contributor's own (lazily imported)\n // module, so a scene with no contributors fetches zero contributor pick bytes.\n const contribRanges: { base: number; count: number; contributor: PickContributor }[] = [];\n if (scene._pickSources.length > 0) {\n const pickCtx: PickPassContext = { picker, pass, engine, scene, camera, sceneBG: picker._sceneBG!, px, py, w, h };\n for (let ci = 0; ci < scene._pickSources.length; ci++) {\n const src = scene._pickSources[ci]!;\n // Build each contributor once (per picker) and cache it — the first pick lazy-imports the\n // source's pipeline, so a scene that never picks fetches zero contributor pick bytes.\n let contributor = picker._contributors?.get(src);\n if (!contributor) {\n const pipeline = await src.load();\n contributor = pipeline.createPickContributor(src.entity);\n (picker._contributors ??= new Map()).set(src, contributor);\n }\n const base = nextId;\n nextId = contributor.draw(pickCtx, base);\n if (nextId > base) {\n contribRanges.push({ base, count: nextId - base, contributor });\n }\n }\n }\n pass.end();\n\n // ── Readback (both 1×1 — trivially small) ────────────────────────\n encoder.copyTextureToBuffer({ texture: rt.colorTex }, { buffer: rt.colorStaging, bytesPerRow: 256 }, { width: 1, height: 1 });\n encoder.copyTextureToBuffer({ texture: rt.depthColorTex }, { buffer: rt.depthStaging, bytesPerRow: 256 }, { width: 1, height: 1 });\n device.queue.submit([encoder.finish()]);\n\n await Promise.all([rt.colorStaging.mapAsync(GPUMapMode.READ), rt.depthStaging.mapAsync(GPUMapMode.READ)]);\n\n const colorData = new U8(rt.colorStaging.getMappedRange());\n const pickId = (colorData[0]! << 16) | (colorData[1]! << 8) | colorData[2]!;\n const depth = new F32(rt.depthStaging.getMappedRange())[0]!;\n rt.colorStaging.unmap();\n rt.depthStaging.unmap();\n\n // Destroy temp per-mesh UBOs\n for (let i = 0; i < tempBuffers.length; i++) {\n tempBuffers[i]!.destroy();\n }\n\n // ── Resolve pick ID to mesh ──────────────────────────────────────\n if (pickId === 0) {\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: debugRay,\n pickId,\n depth,\n hit: false,\n });\n }\n return createEmptyPickingInfo();\n }\n let hitMesh: Mesh | null = null;\n let hitThinIdx = -1;\n let scanId = 1;\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n if (mesh.pickable === false) {\n continue; // skipped identically to the draw pass above so scanId stays aligned with the ids\n }\n if (pickFilter && !pickFilter(mesh)) {\n continue; // skipped identically to the draw pass above so scanId stays aligned with the ids\n }\n const ti = mesh.thinInstances;\n if (ti) {\n if (ti.count <= 0 || !ti._gpuBuffer) {\n continue;\n }\n if (pickId >= scanId && pickId < scanId + ti.count) {\n hitMesh = mesh;\n hitThinIdx = pickId - scanId;\n break;\n }\n scanId += ti.count;\n } else {\n if (pickId === scanId) {\n hitMesh = mesh;\n break;\n }\n scanId++;\n }\n }\n // Contributor resolve: meshes own ids 1..M, so any unresolved id belongs to a contributor.\n // Find the owning contributor by its id range (a numeric compare, no entity-type knowledge).\n let hitContributor: PickContributor | null = null;\n let contribLocalId = -1;\n if (!hitMesh) {\n for (let ri = 0; ri < contribRanges.length; ri++) {\n const r = contribRanges[ri]!;\n if (pickId >= r.base && pickId < r.base + r.count) {\n hitContributor = r.contributor;\n contribLocalId = pickId - r.base;\n break;\n }\n }\n }\n if (!hitMesh && !hitContributor) {\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: debugRay,\n pickId,\n depth,\n hit: false,\n unresolved: true,\n });\n }\n return createEmptyPickingInfo();\n }\n\n const info = createEmptyPickingInfo();\n info.hit = true;\n info.pickedMesh = hitMesh;\n info.thinInstanceIndex = hitThinIdx;\n\n // Reconstruct world position from depth (using original full-res VP)\n const invVP = mat4Invert(vp);\n if (invVP) {\n const ndcX = (2 * (pickX - viewport.x)) / w - 1;\n const ndcY = 1 - (2 * (pickY - viewport.y)) / h;\n const wx = invVP[0]! * ndcX + invVP[4]! * ndcY + invVP[8]! * depth + invVP[12]!;\n const wy = invVP[1]! * ndcX + invVP[5]! * ndcY + invVP[9]! * depth + invVP[13]!;\n const wz = invVP[2]! * ndcX + invVP[6]! * ndcY + invVP[10]! * depth + invVP[14]!;\n const ww = invVP[3]! * ndcX + invVP[7]! * ndcY + invVP[11]! * depth + invVP[15]!;\n const invW = 1 / ww;\n info.pickedPoint = [wx * invW, wy * invW, wz * invW];\n\n const camPos = getCameraPosition(camera);\n const dx = info.pickedPoint[0] - camPos.x;\n const dy = info.pickedPoint[1] - camPos.y;\n const dz = info.pickedPoint[2] - camPos.z;\n info.distance = Math.sqrt(dx * dx + dy * dy + dz * dz);\n }\n\n if (hitContributor) {\n // The contributor attaches its own payload (GS sets pickedMesh; a billboard sets _spritePick).\n // pickedPoint/distance were reconstructed above from the shared pick depth.\n hitContributor.resolve(info, contribLocalId);\n }\n\n if (picker._detailedPick && hitMesh) {\n const ray = createPickingRay(pickX - viewport.x, pickY - viewport.y, vp, w, h);\n if (ray) {\n info.ray = ray;\n await picker._detailedPick(info, ray);\n }\n }\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: info.ray ?? debugRay,\n pickId,\n depth,\n hit: true,\n mesh: hitMesh ? (hitMesh.name ?? \"(unnamed)\") : \"(contributor)\",\n thinInstanceIndex: hitThinIdx,\n pickedPoint: info.pickedPoint,\n distance: info.distance,\n });\n }\n\n return info;\n}\n\n/**\n * Pick the mesh at CSS-space canvas coordinates, matching Babylon.js Scene.pick. Returns a PickingInfo.\n *\n * A picker's 1×1 readback targets (`PickTargets1x1.colorStaging`/`depthStaging`) are lazily created ONCE\n * and reused for every pick — cheap, but it means two overlapping `pickAsync` calls on the SAME picker\n * race `mapAsync` on those shared buffers (\"Buffer already has an outstanding map pending\", since a\n * GPUBuffer allows only one pending map at a time). This is easy to trigger from a consumer: e.g. a\n * cursor-following hover preview that GPU-picks on every pointermove, racing a pick fired by a click that\n * lands before the hover's pick has unmapped. Queue concurrent calls per-picker instead of rejecting: each\n * pick's full map/unmap cycle completes before the next one starts.\n */\nexport function pickAsync(picker: GpuPicker, x: number, y: number, options?: PickOptions): Promise<PickingInfo> {\n const prior = picker._pending ?? Promise.resolve();\n const run = prior.then(\n () => pickAsyncImpl(picker, x, y, options),\n () => pickAsyncImpl(picker, x, y, options) // a prior pick's rejection must not wedge the queue for this caller\n );\n // Swallow so a rejection here doesn't propagate into the NEXT caller's chain (each caller gets its own\n // `run` promise and observes the real rejection via the returned promise, not via `_pending`).\n picker._pending = run.then(\n () => undefined,\n () => undefined\n );\n return run;\n}\n\n/** Dispose GPU resources owned by this picker. */\nexport function disposePicker(picker: GpuPicker): void {\n if (picker._rt) {\n picker._rt.colorTex.destroy();\n picker._rt.depthColorTex.destroy();\n picker._rt.depthTex.destroy();\n picker._rt.colorStaging.destroy();\n picker._rt.depthStaging.destroy();\n picker._rt = null;\n }\n if (picker._sceneUbo) {\n picker._sceneUbo.destroy();\n picker._sceneUbo = null;\n picker._sceneBG = null;\n }\n if (picker._contributors) {\n // Each contributor was built once and cached here; it owns its GPU pick resources in its\n // closure and frees them in dispose() — so cleanup is generic (the picker names no entity type).\n for (const contributor of picker._contributors.values()) {\n contributor.dispose?.();\n }\n picker._contributors = null;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@ import { getRenderTargetSize } from '../engine/engine.js';
7
7
  import { getViewMatrix, getProjectionMatrix } from '../camera/camera.js';
8
8
 
9
9
  let _cache = null;
10
+ const _gsPickMx = new F32(16);
10
11
  function buildPickingWgsl() {
11
12
  const wgsl = (
12
13
  /* wgsl */
@@ -257,6 +258,11 @@ function drawGsForPicking(pass, engine, scene, mesh, res, pickId, targetWidth, t
257
258
  pass.setIndexBuffer(mesh._gs._indexBuffer, "uint16");
258
259
  pass.drawIndexed(6, mesh.vertexCount);
259
260
  }
261
+ function drawGsMeshForPicking(ctx, mesh, res, baseId) {
262
+ computeGsPickMatrix(_gsPickMx, ctx.px, ctx.py, ctx.w, ctx.h);
263
+ gsPickWritePickMatrixAndBind(ctx.pass, ctx.engine, _gsPickMx);
264
+ drawGsForPicking(ctx.pass, ctx.engine, ctx.scene, mesh, res, baseId, ctx.w, ctx.h);
265
+ }
260
266
  function computeGsPickMatrix(out, px, py, w, h) {
261
267
  const ndcX = 2 * (px + 0.5) / w - 1;
262
268
  const ndcY = 1 - 2 * (py + 0.5) / h;
@@ -277,6 +283,25 @@ function computeGsPickMatrix(out, px, py, w, h) {
277
283
  out[14] = 0;
278
284
  out[15] = 1;
279
285
  }
286
+ function createPickContributor(mesh) {
287
+ let res = null;
288
+ return {
289
+ draw(ctx, baseId) {
290
+ res ??= createGsPickMeshResources(ctx.engine, mesh);
291
+ drawGsMeshForPicking(ctx, mesh, res, baseId);
292
+ return baseId + 1;
293
+ },
294
+ resolve(info) {
295
+ info.pickedMesh = mesh;
296
+ },
297
+ dispose() {
298
+ if (res) {
299
+ disposeGsPickMeshResources(res);
300
+ res = null;
301
+ }
302
+ }
303
+ };
304
+ }
280
305
 
281
- export { computeGsPickMatrix, createGsPickMeshResources, disposeGsPickMeshResources, drawGsForPicking, gsPickWritePickMatrixAndBind };
306
+ export { computeGsPickMatrix, createGsPickMeshResources, createPickContributor, disposeGsPickMeshResources, drawGsForPicking, drawGsMeshForPicking, gsPickWritePickMatrixAndBind };
282
307
  //# sourceMappingURL=gs-picking-pipeline.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gs-picking-pipeline.js","sources":["../../../src/picking/gs-picking-pipeline.ts"],"sourcesContent":["/** GS GPU-picking pipeline — dynamic-imported by `gpu-picker.ts` when a scene\n * contains a Gaussian-Splatting mesh.\n *\n * The pipeline is a variant of the base Gaussian-splatting render pipeline\n * (`gaussian-splatting.wgsl`) with three modifications, applied via string\n * substitution + the `applyGsFragments` plugin system so the base shader stays\n * untouched:\n *\n * 1. A scene UBO at `@group(0) @binding(0)` carries a `pickMatrix`\n * (column-major 4x4) that zooms NDC onto the pick pixel — same maths as\n * `computePickVP` in `gpu-picker.ts`, but applied *after* the GS-specific\n * projection so the EWA Jacobian / `u.focal` math is untouched.\n * 2. The fragment returns `FsOut { @location(0) color, @location(1) depth }`\n * so the picker can read a pick id + NDC z from the 1×1 render target.\n * 3. `gsGpuPickingFragment` is applied (via `applyGsFragments`) to inject the\n * per-mesh `pickingColor` UBO at `@group(2) @binding(0)` and override the\n * fragment color with the encoded pick id. This mirrors BJS\n * `GaussianSplattingGpuPickingMaterialPlugin`'s `getCustomCode`/`bindForSubMesh`\n * split.\n *\n * The pipeline has no blending and `depthWriteEnabled = true`, so the\n * closest-splat wins at each pick pixel (matching BJS GPU picker behaviour). */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport { BU, SS } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { GaussianSplattingMesh } from \"../mesh/GaussianSplatting/gaussian-splatting-mesh.js\";\nimport { applyGsFragments } from \"../mesh/GaussianSplatting/gaussian-splatting-pipeline.js\";\nimport { gsGpuPickingFragment, encodeIdToColor } from \"../mesh/GaussianSplatting/gs-gpu-picking-fragment.js\";\nimport { getPickingSceneBGL } from \"./picking-pipeline.js\";\nimport { getRenderTargetSize } from \"../engine/engine.js\";\nimport { getViewMatrix, getProjectionMatrix } from \"../camera/camera.js\";\nimport type { SceneContext } from \"../scene/scene-core.js\";\n\ninterface GsPickingCache {\n device: GPUDevice;\n pipeline: GPURenderPipeline;\n meshBGL: GPUBindGroupLayout;\n pickingBGL: GPUBindGroupLayout;\n /** Shared \"pick scene\" UBO holding the 4x4 pickMatrix (set per pick). */\n pickMatrixUbo: GPUBuffer;\n /** Bind group exposing `pickMatrixUbo` at group(0) binding(0). */\n sceneBG: GPUBindGroup;\n}\n\nlet _cache: GsPickingCache | null = null;\n\n/** Build the GS picking WGSL as a self-contained template literal.\n *\n * This is a variant of the base GS shader (`gaussian-splatting.wgsl`) with two\n * modifications baked in directly (rather than string-patched at runtime), so\n * it survives identifier-mangling minification in production bundles:\n *\n * 1. The final `out.pos` is pre-multiplied by `gsPickScene.pickMatrix` (a 4×4\n * UBO at `@group(0) @binding(0)`) — applied *after* the GS-specific\n * projection so the EWA Jacobian / `u.focal` math is untouched.\n * 2. The fragment returns `FsOut { @location(0) color, @location(1) depth }`\n * so the picker can read pick id + NDC z from the 1×1 render target.\n *\n * The fragment keeps the `/* GS_FRAGMENT_* *\\/` slot markers so\n * `gsGpuPickingFragment` is still spliced in via `applyGsFragments` to\n * override `finalColor` with the encoded pick id. Template-literal WGSL\n * goes through `minifyTemplateWgsl` (preserves block comments) in the\n * bundle pipeline, so the slot markers survive minification.\n */\nfunction buildPickingWgsl(): string {\n const wgsl = /* wgsl */ `\nstruct GsPickScene { pickMatrix: mat4x4<f32> };\n@group(0) @binding(0) var<uniform> gsPickScene: GsPickScene;\n\nstruct U {\n world: mat4x4<f32>,\n view: mat4x4<f32>,\n projection: mat4x4<f32>,\n viewport: vec2<f32>,\n focal: vec2<f32>,\n dataSize: vec2<f32>,\n alpha: f32,\n _pad: f32,\n};\n@group(1) @binding(0) var<uniform> u: U;\n@group(1) @binding(1) var samp: sampler;\n@group(1) @binding(2) var centersTex: texture_2d<f32>;\n@group(1) @binding(3) var covATex: texture_2d<f32>;\n@group(1) @binding(4) var covBTex: texture_2d<f32>;\n@group(1) @binding(5) var colorsTex: texture_2d<f32>;\n\nstruct VOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) vColor: vec4<f32>,\n @location(1) vPos: vec2<f32>,\n};\n\nfn dataUv(idx: f32) -> vec2<f32> {\n let y = floor(idx / u.dataSize.x);\n let x = idx - y * u.dataSize.x;\n return vec2<f32>((x + 0.5) / u.dataSize.x, (y + 0.5) / u.dataSize.y);\n}\n\n@vertex\nfn vs(@location(0) corner: vec2<f32>, @location(1) splatIndex: f32) -> VOut {\n var out: VOut;\n let uv = dataUv(splatIndex);\n let center = textureSampleLevel(centersTex, samp, uv, 0.0).xyz;\n let color = textureSampleLevel(colorsTex, samp, uv, 0.0);\n let covA = textureSampleLevel(covATex, samp, uv, 0.0).xyz;\n let covB = textureSampleLevel(covBTex, samp, uv, 0.0).xyz;\n\n let worldPos = u.world * vec4<f32>(center, 1.0);\n let modelView = u.view * u.world;\n let camspace = u.view * worldPos;\n let pos2d = u.projection * camspace;\n\n let bounds = 1.2 * pos2d.w;\n if (pos2d.z < 0.0\n || pos2d.x < -bounds || pos2d.x > bounds\n || pos2d.y < -bounds || pos2d.y > bounds) {\n out.pos = vec4<f32>(0.0, 0.0, 2.0, 1.0);\n out.vColor = vec4<f32>(0.0);\n out.vPos = vec2<f32>(0.0);\n return out;\n }\n\n let Vrk = mat3x3<f32>(\n vec3<f32>(covA.x, covA.y, covA.z),\n vec3<f32>(covA.y, covB.x, covB.y),\n vec3<f32>(covA.z, covB.y, covB.z));\n\n let invZ = 1.0 / camspace.z;\n let invZ2 = invZ * invZ;\n let J = mat3x3<f32>(\n vec3<f32>(u.focal.x * invZ, 0.0, -u.focal.x * camspace.x * invZ2),\n vec3<f32>(0.0, u.focal.y * invZ, -u.focal.y * camspace.y * invZ2),\n vec3<f32>(0.0, 0.0, 0.0));\n\n let mv3 = mat3x3<f32>(modelView[0].xyz, modelView[1].xyz, modelView[2].xyz);\n let T = transpose(mv3) * J;\n var cov2d = transpose(T) * Vrk * T;\n\n let kernelSize: f32 = 0.3;\n cov2d[0][0] += kernelSize;\n cov2d[1][1] += kernelSize;\n\n let mid = (cov2d[0][0] + cov2d[1][1]) * 0.5;\n let dxy = (cov2d[0][0] - cov2d[1][1]) * 0.5;\n let radius = length(vec2<f32>(dxy, cov2d[0][1]));\n let epsilon: f32 = 0.0001;\n let lambda1 = mid + radius + epsilon;\n let lambda2 = mid - radius + epsilon;\n if (lambda2 < 0.0) {\n out.pos = vec4<f32>(0.0, 0.0, 2.0, 1.0);\n out.vColor = vec4<f32>(0.0);\n out.vPos = vec2<f32>(0.0);\n return out;\n }\n\n let diag = normalize(vec2<f32>(cov2d[0][1], lambda1 - cov2d[0][0]));\n let majorAxis = min(sqrt(2.0 * lambda1), 1024.0) * diag;\n let minorAxis = min(sqrt(2.0 * lambda2), 1024.0) * vec2<f32>(diag.y, -diag.x);\n\n let vCenter = pos2d.xy;\n out.pos = gsPickScene.pickMatrix * vec4<f32>(\n vCenter + (corner.x * majorAxis + corner.y * minorAxis) * pos2d.w / u.viewport,\n pos2d.z, pos2d.w);\n out.vColor = vec4<f32>(color.rgb, color.a * u.alpha);\n out.vPos = corner;\n return out;\n}\n\n/*GS_FRAGMENT_DEFINITIONS*/\nstruct FsOut { @location(0) color: vec4<f32>, @location(1) depth: vec4<f32> };\n@fragment\nfn fs(in: VOut) -> FsOut {\n /*GS_FRAGMENT_MAIN_BEGIN*/\n let A = -dot(in.vPos, in.vPos);\n var finalColor: vec4<f32>;\n if (A > -4.0) {\n let B = exp(A) * in.vColor.a;\n finalColor = vec4<f32>(in.vColor.rgb, B);\n } else {\n finalColor = vec4<f32>(0.0);\n }\n /*GS_FRAGMENT_BEFORE_FRAGCOLOR*/\n /*GS_FRAGMENT_MAIN_END*/\n return FsOut(finalColor, vec4<f32>(in.pos.z, 0.0, 0.0, 0.0));\n}\n`;\n return applyGsFragments(wgsl, [gsGpuPickingFragment]);\n}\n\nfunction getCache(engine: EngineContext): GsPickingCache {\n const device = engine._device;\n if (_cache && _cache.device === device) {\n return _cache;\n }\n const meshBGL = device.createBindGroupLayout({\n label: \"gs-picking-mesh-bgl\",\n entries: [\n { binding: 0, visibility: SS.VERTEX | SS.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: SS.VERTEX, sampler: { type: \"non-filtering\" } },\n { binding: 2, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 3, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 4, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 5, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n ],\n });\n const pickingBGL = device.createBindGroupLayout({\n label: \"gs-picking-pick-bgl\",\n entries: [{ binding: 0, visibility: SS.FRAGMENT, buffer: { type: \"uniform\" } }],\n });\n const module = device.createShaderModule({ label: \"gs-picking-shader\", code: buildPickingWgsl() });\n const pipeline = device.createRenderPipeline({\n label: \"gs-picking-pipeline\",\n layout: device.createPipelineLayout({ bindGroupLayouts: [getPickingSceneBGL(engine), meshBGL, pickingBGL] }),\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [\n { arrayStride: 8, stepMode: \"vertex\", attributes: [{ shaderLocation: 0, offset: 0, format: \"float32x2\" }] },\n { arrayStride: 4, stepMode: \"instance\", attributes: [{ shaderLocation: 1, offset: 0, format: \"float32\" }] },\n ],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format: \"rgba8unorm\" }, { format: \"r32float\" }],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\n depthStencil: { format: \"depth24plus\", depthCompare: \"less\", depthWriteEnabled: true },\n multisample: { count: 1 },\n });\n const pickMatrixUbo = device.createBuffer({ size: 64, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-scene-ubo\" });\n const sceneBG = device.createBindGroup({\n label: \"gs-picking-scene-bg\",\n layout: getPickingSceneBGL(engine),\n entries: [{ binding: 0, resource: { buffer: pickMatrixUbo } }],\n });\n _cache = { device, pipeline, meshBGL, pickingBGL, pickMatrixUbo, sceneBG };\n return _cache;\n}\n\n/** Write a 4x4 pickMatrix into the shared scene UBO and bind group 0 on `pass`. */\nexport function gsPickWritePickMatrixAndBind(pass: GPURenderPassEncoder, engine: EngineContext, pickMatrix: Float32Array): void {\n const cache = getCache(engine);\n engine._device.queue.writeBuffer(cache.pickMatrixUbo, 0, pickMatrix.buffer, pickMatrix.byteOffset, pickMatrix.byteLength);\n pass.setBindGroup(0, cache.sceneBG);\n}\n\n/** Per-GS-mesh state allocated on first pick of that mesh. */\nexport interface GsPickMeshResources {\n /** Per-mesh UBO (same 224-byte layout as the regular GS pipeline UBO). */\n meshUbo: GPUBuffer;\n /** Group 1 bind group: per-mesh UBO + sampler + 4 textures. */\n meshBG: GPUBindGroup;\n /** Per-pick picking-color UBO (16 bytes: vec3<f32> + pad). */\n pickingUbo: GPUBuffer;\n /** Group 2 bind group with the picking-color UBO. */\n pickingBG: GPUBindGroup;\n /** Scratch buffer for the per-mesh UBO. */\n meshCpu: Float32Array;\n /** Scratch buffer for the picking-color UBO. */\n pickingCpu: Float32Array;\n}\n\nexport function createGsPickMeshResources(engine: EngineContext, mesh: GaussianSplattingMesh): GsPickMeshResources {\n const device = engine._device;\n const cache = getCache(engine);\n\n const UBO_BYTES = 16 * 4 * 3 + 8 * 4;\n const meshUbo = device.createBuffer({ size: UBO_BYTES, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-mesh-ubo\" });\n const meshCpu = new F32(UBO_BYTES / 4);\n meshCpu[48 + 4] = mesh.textureWidth;\n meshCpu[48 + 5] = mesh.textureHeight;\n meshCpu[48 + 6] = 1;\n\n const meshBG = device.createBindGroup({\n label: \"gs-picking-mesh-bg\",\n layout: cache.meshBGL,\n entries: [\n { binding: 0, resource: { buffer: meshUbo } },\n { binding: 1, resource: mesh._gs._sampler },\n { binding: 2, resource: mesh._gs._centersView },\n { binding: 3, resource: mesh._gs._covAView },\n { binding: 4, resource: mesh._gs._covBView },\n { binding: 5, resource: mesh._gs._colorsView },\n ],\n });\n\n const pickingUbo = device.createBuffer({ size: 16, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-color-ubo\" });\n const pickingCpu = new F32(4);\n\n const pickingBG = device.createBindGroup({\n label: \"gs-picking-color-bg\",\n layout: cache.pickingBGL,\n entries: [{ binding: 0, resource: { buffer: pickingUbo } }],\n });\n\n return { meshUbo, meshBG, pickingUbo, pickingBG, meshCpu, pickingCpu };\n}\n\nexport function disposeGsPickMeshResources(res: GsPickMeshResources): void {\n res.meshUbo.destroy();\n res.pickingUbo.destroy();\n}\n\n/** Issue a pick draw for a single GS mesh. The caller must have already bound\n * the scene bind group (`pickMatrix` at group 0) on the pass; `pickId` is the\n * 24-bit pick id assigned by the picker. */\nexport function drawGsForPicking(\n pass: GPURenderPassEncoder,\n engine: EngineContext,\n scene: SceneContext,\n mesh: GaussianSplattingMesh,\n res: GsPickMeshResources,\n pickId: number,\n targetWidth: number,\n targetHeight: number\n): void {\n const cache = getCache(engine);\n\n // ── Per-mesh UBO ────────────────────────────────────────────────\n const cam = scene.camera;\n if (!cam) {\n return;\n }\n const size = getRenderTargetSize(engine);\n const aspect = (targetWidth || size.width) / (targetHeight || size.height);\n const view = getViewMatrix(cam) as unknown as Float32Array;\n const proj = getProjectionMatrix(cam, aspect) as unknown as Float32Array;\n const world = mesh.worldMatrix as unknown as Float32Array;\n const cpu = res.meshCpu;\n cpu.set(world, 0);\n cpu.set(view, 16);\n cpu.set(proj, 32);\n // Note: the GS picking pipeline still runs over the full-canvas viewport;\n // the picker output is a 1×1 target reached via the scene pickMatrix.\n cpu[48] = size.width;\n cpu[48 + 1] = size.height;\n cpu[48 + 2] = size.width * 0.5 * proj[0]!;\n cpu[48 + 3] = size.height * 0.5 * proj[5]!;\n // dataSize/alpha already written at construction.\n engine._device.queue.writeBuffer(res.meshUbo, 0, cpu.buffer, 0, cpu.byteLength);\n\n // ── Picking-color UBO ───────────────────────────────────────────\n const [r, g, b] = encodeIdToColor(pickId);\n res.pickingCpu[0] = r;\n res.pickingCpu[1] = g;\n res.pickingCpu[2] = b;\n res.pickingCpu[3] = 0;\n engine._device.queue.writeBuffer(res.pickingUbo, 0, res.pickingCpu.buffer, 0, 16);\n\n pass.setPipeline(cache.pipeline);\n pass.setBindGroup(1, res.meshBG);\n pass.setBindGroup(2, res.pickingBG);\n pass.setVertexBuffer(0, mesh._gs._quadBuffer);\n pass.setVertexBuffer(1, mesh._gs._splatIndexBuffer);\n pass.setIndexBuffer(mesh._gs._indexBuffer, \"uint16\");\n pass.drawIndexed(6, mesh.vertexCount);\n}\n\n/** Compute the pickMatrix for GS picking — same matrix `computePickVP` builds,\n * but applied to the clip-space output of the GS shader rather than the world.\n *\n * Maps canvas-NDC (px, py) ↦ (0, 0), and scales by (w, h) so a single canvas\n * pixel covers the full 1×1 pick render target. */\nexport function computeGsPickMatrix(out: Float32Array, px: number, py: number, w: number, h: number): void {\n const ndcX = (2 * (px + 0.5)) / w - 1;\n const ndcY = 1 - (2 * (py + 0.5)) / h;\n // column-major mat4\n out[0] = w;\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 0;\n out[5] = h;\n out[6] = 0;\n out[7] = 0;\n out[8] = 0;\n out[9] = 0;\n out[10] = 1;\n out[11] = 0;\n out[12] = -ndcX * w;\n out[13] = -ndcY * h;\n out[14] = 0;\n out[15] = 1;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gs-picking-pipeline.js","sources":["../../../src/picking/gs-picking-pipeline.ts"],"sourcesContent":["/** GS GPU-picking pipeline — dynamic-imported by `gpu-picker.ts` when a scene\n * contains a Gaussian-Splatting mesh.\n *\n * The pipeline is a variant of the base Gaussian-splatting render pipeline\n * (`gaussian-splatting.wgsl`) with three modifications, applied via string\n * substitution + the `applyGsFragments` plugin system so the base shader stays\n * untouched:\n *\n * 1. A scene UBO at `@group(0) @binding(0)` carries a `pickMatrix`\n * (column-major 4x4) that zooms NDC onto the pick pixel — same maths as\n * `computePickVP` in `gpu-picker.ts`, but applied *after* the GS-specific\n * projection so the EWA Jacobian / `u.focal` math is untouched.\n * 2. The fragment returns `FsOut { @location(0) color, @location(1) depth }`\n * so the picker can read a pick id + NDC z from the 1×1 render target.\n * 3. `gsGpuPickingFragment` is applied (via `applyGsFragments`) to inject the\n * per-mesh `pickingColor` UBO at `@group(2) @binding(0)` and override the\n * fragment color with the encoded pick id. This mirrors BJS\n * `GaussianSplattingGpuPickingMaterialPlugin`'s `getCustomCode`/`bindForSubMesh`\n * split.\n *\n * The pipeline has no blending and `depthWriteEnabled = true`, so the\n * closest-splat wins at each pick pixel (matching BJS GPU picker behaviour). */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport { BU, SS } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { GaussianSplattingMesh } from \"../mesh/GaussianSplatting/gaussian-splatting-mesh.js\";\nimport { applyGsFragments } from \"../mesh/GaussianSplatting/gaussian-splatting-pipeline.js\";\nimport { gsGpuPickingFragment, encodeIdToColor } from \"../mesh/GaussianSplatting/gs-gpu-picking-fragment.js\";\nimport { getPickingSceneBGL } from \"./picking-pipeline.js\";\nimport { getRenderTargetSize } from \"../engine/engine.js\";\nimport { getViewMatrix, getProjectionMatrix } from \"../camera/camera.js\";\nimport type { SceneContext } from \"../scene/scene-core.js\";\nimport type { PickContributor, PickPassContext } from \"./pick-contributor.js\";\n\ninterface GsPickingCache {\n device: GPUDevice;\n pipeline: GPURenderPipeline;\n meshBGL: GPUBindGroupLayout;\n pickingBGL: GPUBindGroupLayout;\n /** Shared \"pick scene\" UBO holding the 4x4 pickMatrix (set per pick). */\n pickMatrixUbo: GPUBuffer;\n /** Bind group exposing `pickMatrixUbo` at group(0) binding(0). */\n sceneBG: GPUBindGroup;\n}\n\nlet _cache: GsPickingCache | null = null;\n\n/** Scratch pick matrix, reused across GS pick draws (recomputed per draw from the pick pixel). */\nconst _gsPickMx = new F32(16);\n\n/** Build the GS picking WGSL as a self-contained template literal.\n *\n * This is a variant of the base GS shader (`gaussian-splatting.wgsl`) with two\n * modifications baked in directly (rather than string-patched at runtime), so\n * it survives identifier-mangling minification in production bundles:\n *\n * 1. The final `out.pos` is pre-multiplied by `gsPickScene.pickMatrix` (a 4×4\n * UBO at `@group(0) @binding(0)`) — applied *after* the GS-specific\n * projection so the EWA Jacobian / `u.focal` math is untouched.\n * 2. The fragment returns `FsOut { @location(0) color, @location(1) depth }`\n * so the picker can read pick id + NDC z from the 1×1 render target.\n *\n * The fragment keeps the `/* GS_FRAGMENT_* *\\/` slot markers so\n * `gsGpuPickingFragment` is still spliced in via `applyGsFragments` to\n * override `finalColor` with the encoded pick id. Template-literal WGSL\n * goes through `minifyTemplateWgsl` (preserves block comments) in the\n * bundle pipeline, so the slot markers survive minification.\n */\nfunction buildPickingWgsl(): string {\n const wgsl = /* wgsl */ `\nstruct GsPickScene { pickMatrix: mat4x4<f32> };\n@group(0) @binding(0) var<uniform> gsPickScene: GsPickScene;\n\nstruct U {\n world: mat4x4<f32>,\n view: mat4x4<f32>,\n projection: mat4x4<f32>,\n viewport: vec2<f32>,\n focal: vec2<f32>,\n dataSize: vec2<f32>,\n alpha: f32,\n _pad: f32,\n};\n@group(1) @binding(0) var<uniform> u: U;\n@group(1) @binding(1) var samp: sampler;\n@group(1) @binding(2) var centersTex: texture_2d<f32>;\n@group(1) @binding(3) var covATex: texture_2d<f32>;\n@group(1) @binding(4) var covBTex: texture_2d<f32>;\n@group(1) @binding(5) var colorsTex: texture_2d<f32>;\n\nstruct VOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) vColor: vec4<f32>,\n @location(1) vPos: vec2<f32>,\n};\n\nfn dataUv(idx: f32) -> vec2<f32> {\n let y = floor(idx / u.dataSize.x);\n let x = idx - y * u.dataSize.x;\n return vec2<f32>((x + 0.5) / u.dataSize.x, (y + 0.5) / u.dataSize.y);\n}\n\n@vertex\nfn vs(@location(0) corner: vec2<f32>, @location(1) splatIndex: f32) -> VOut {\n var out: VOut;\n let uv = dataUv(splatIndex);\n let center = textureSampleLevel(centersTex, samp, uv, 0.0).xyz;\n let color = textureSampleLevel(colorsTex, samp, uv, 0.0);\n let covA = textureSampleLevel(covATex, samp, uv, 0.0).xyz;\n let covB = textureSampleLevel(covBTex, samp, uv, 0.0).xyz;\n\n let worldPos = u.world * vec4<f32>(center, 1.0);\n let modelView = u.view * u.world;\n let camspace = u.view * worldPos;\n let pos2d = u.projection * camspace;\n\n let bounds = 1.2 * pos2d.w;\n if (pos2d.z < 0.0\n || pos2d.x < -bounds || pos2d.x > bounds\n || pos2d.y < -bounds || pos2d.y > bounds) {\n out.pos = vec4<f32>(0.0, 0.0, 2.0, 1.0);\n out.vColor = vec4<f32>(0.0);\n out.vPos = vec2<f32>(0.0);\n return out;\n }\n\n let Vrk = mat3x3<f32>(\n vec3<f32>(covA.x, covA.y, covA.z),\n vec3<f32>(covA.y, covB.x, covB.y),\n vec3<f32>(covA.z, covB.y, covB.z));\n\n let invZ = 1.0 / camspace.z;\n let invZ2 = invZ * invZ;\n let J = mat3x3<f32>(\n vec3<f32>(u.focal.x * invZ, 0.0, -u.focal.x * camspace.x * invZ2),\n vec3<f32>(0.0, u.focal.y * invZ, -u.focal.y * camspace.y * invZ2),\n vec3<f32>(0.0, 0.0, 0.0));\n\n let mv3 = mat3x3<f32>(modelView[0].xyz, modelView[1].xyz, modelView[2].xyz);\n let T = transpose(mv3) * J;\n var cov2d = transpose(T) * Vrk * T;\n\n let kernelSize: f32 = 0.3;\n cov2d[0][0] += kernelSize;\n cov2d[1][1] += kernelSize;\n\n let mid = (cov2d[0][0] + cov2d[1][1]) * 0.5;\n let dxy = (cov2d[0][0] - cov2d[1][1]) * 0.5;\n let radius = length(vec2<f32>(dxy, cov2d[0][1]));\n let epsilon: f32 = 0.0001;\n let lambda1 = mid + radius + epsilon;\n let lambda2 = mid - radius + epsilon;\n if (lambda2 < 0.0) {\n out.pos = vec4<f32>(0.0, 0.0, 2.0, 1.0);\n out.vColor = vec4<f32>(0.0);\n out.vPos = vec2<f32>(0.0);\n return out;\n }\n\n let diag = normalize(vec2<f32>(cov2d[0][1], lambda1 - cov2d[0][0]));\n let majorAxis = min(sqrt(2.0 * lambda1), 1024.0) * diag;\n let minorAxis = min(sqrt(2.0 * lambda2), 1024.0) * vec2<f32>(diag.y, -diag.x);\n\n let vCenter = pos2d.xy;\n out.pos = gsPickScene.pickMatrix * vec4<f32>(\n vCenter + (corner.x * majorAxis + corner.y * minorAxis) * pos2d.w / u.viewport,\n pos2d.z, pos2d.w);\n out.vColor = vec4<f32>(color.rgb, color.a * u.alpha);\n out.vPos = corner;\n return out;\n}\n\n/*GS_FRAGMENT_DEFINITIONS*/\nstruct FsOut { @location(0) color: vec4<f32>, @location(1) depth: vec4<f32> };\n@fragment\nfn fs(in: VOut) -> FsOut {\n /*GS_FRAGMENT_MAIN_BEGIN*/\n let A = -dot(in.vPos, in.vPos);\n var finalColor: vec4<f32>;\n if (A > -4.0) {\n let B = exp(A) * in.vColor.a;\n finalColor = vec4<f32>(in.vColor.rgb, B);\n } else {\n finalColor = vec4<f32>(0.0);\n }\n /*GS_FRAGMENT_BEFORE_FRAGCOLOR*/\n /*GS_FRAGMENT_MAIN_END*/\n return FsOut(finalColor, vec4<f32>(in.pos.z, 0.0, 0.0, 0.0));\n}\n`;\n return applyGsFragments(wgsl, [gsGpuPickingFragment]);\n}\n\nfunction getCache(engine: EngineContext): GsPickingCache {\n const device = engine._device;\n if (_cache && _cache.device === device) {\n return _cache;\n }\n const meshBGL = device.createBindGroupLayout({\n label: \"gs-picking-mesh-bgl\",\n entries: [\n { binding: 0, visibility: SS.VERTEX | SS.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: SS.VERTEX, sampler: { type: \"non-filtering\" } },\n { binding: 2, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 3, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 4, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 5, visibility: SS.VERTEX, texture: { sampleType: \"unfilterable-float\" } },\n ],\n });\n const pickingBGL = device.createBindGroupLayout({\n label: \"gs-picking-pick-bgl\",\n entries: [{ binding: 0, visibility: SS.FRAGMENT, buffer: { type: \"uniform\" } }],\n });\n const module = device.createShaderModule({ label: \"gs-picking-shader\", code: buildPickingWgsl() });\n const pipeline = device.createRenderPipeline({\n label: \"gs-picking-pipeline\",\n layout: device.createPipelineLayout({ bindGroupLayouts: [getPickingSceneBGL(engine), meshBGL, pickingBGL] }),\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [\n { arrayStride: 8, stepMode: \"vertex\", attributes: [{ shaderLocation: 0, offset: 0, format: \"float32x2\" }] },\n { arrayStride: 4, stepMode: \"instance\", attributes: [{ shaderLocation: 1, offset: 0, format: \"float32\" }] },\n ],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format: \"rgba8unorm\" }, { format: \"r32float\" }],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\n depthStencil: { format: \"depth24plus\", depthCompare: \"less\", depthWriteEnabled: true },\n multisample: { count: 1 },\n });\n const pickMatrixUbo = device.createBuffer({ size: 64, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-scene-ubo\" });\n const sceneBG = device.createBindGroup({\n label: \"gs-picking-scene-bg\",\n layout: getPickingSceneBGL(engine),\n entries: [{ binding: 0, resource: { buffer: pickMatrixUbo } }],\n });\n _cache = { device, pipeline, meshBGL, pickingBGL, pickMatrixUbo, sceneBG };\n return _cache;\n}\n\n/** Write a 4x4 pickMatrix into the shared scene UBO and bind group 0 on `pass`. */\nexport function gsPickWritePickMatrixAndBind(pass: GPURenderPassEncoder, engine: EngineContext, pickMatrix: Float32Array): void {\n const cache = getCache(engine);\n engine._device.queue.writeBuffer(cache.pickMatrixUbo, 0, pickMatrix.buffer, pickMatrix.byteOffset, pickMatrix.byteLength);\n pass.setBindGroup(0, cache.sceneBG);\n}\n\n/** Per-GS-mesh state allocated on first pick of that mesh. */\nexport interface GsPickMeshResources {\n /** Per-mesh UBO (same 224-byte layout as the regular GS pipeline UBO). */\n meshUbo: GPUBuffer;\n /** Group 1 bind group: per-mesh UBO + sampler + 4 textures. */\n meshBG: GPUBindGroup;\n /** Per-pick picking-color UBO (16 bytes: vec3<f32> + pad). */\n pickingUbo: GPUBuffer;\n /** Group 2 bind group with the picking-color UBO. */\n pickingBG: GPUBindGroup;\n /** Scratch buffer for the per-mesh UBO. */\n meshCpu: Float32Array;\n /** Scratch buffer for the picking-color UBO. */\n pickingCpu: Float32Array;\n}\n\nexport function createGsPickMeshResources(engine: EngineContext, mesh: GaussianSplattingMesh): GsPickMeshResources {\n const device = engine._device;\n const cache = getCache(engine);\n\n const UBO_BYTES = 16 * 4 * 3 + 8 * 4;\n const meshUbo = device.createBuffer({ size: UBO_BYTES, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-mesh-ubo\" });\n const meshCpu = new F32(UBO_BYTES / 4);\n meshCpu[48 + 4] = mesh.textureWidth;\n meshCpu[48 + 5] = mesh.textureHeight;\n meshCpu[48 + 6] = 1;\n\n const meshBG = device.createBindGroup({\n label: \"gs-picking-mesh-bg\",\n layout: cache.meshBGL,\n entries: [\n { binding: 0, resource: { buffer: meshUbo } },\n { binding: 1, resource: mesh._gs._sampler },\n { binding: 2, resource: mesh._gs._centersView },\n { binding: 3, resource: mesh._gs._covAView },\n { binding: 4, resource: mesh._gs._covBView },\n { binding: 5, resource: mesh._gs._colorsView },\n ],\n });\n\n const pickingUbo = device.createBuffer({ size: 16, usage: BU.UNIFORM | BU.COPY_DST, label: \"gs-picking-color-ubo\" });\n const pickingCpu = new F32(4);\n\n const pickingBG = device.createBindGroup({\n label: \"gs-picking-color-bg\",\n layout: cache.pickingBGL,\n entries: [{ binding: 0, resource: { buffer: pickingUbo } }],\n });\n\n return { meshUbo, meshBG, pickingUbo, pickingBG, meshCpu, pickingCpu };\n}\n\nexport function disposeGsPickMeshResources(res: GsPickMeshResources): void {\n res.meshUbo.destroy();\n res.pickingUbo.destroy();\n}\n\n/** Issue a pick draw for a single GS mesh. The caller must have already bound\n * the scene bind group (`pickMatrix` at group 0) on the pass; `pickId` is the\n * 24-bit pick id assigned by the picker. */\nexport function drawGsForPicking(\n pass: GPURenderPassEncoder,\n engine: EngineContext,\n scene: SceneContext,\n mesh: GaussianSplattingMesh,\n res: GsPickMeshResources,\n pickId: number,\n targetWidth: number,\n targetHeight: number\n): void {\n const cache = getCache(engine);\n\n // ── Per-mesh UBO ────────────────────────────────────────────────\n const cam = scene.camera;\n if (!cam) {\n return;\n }\n const size = getRenderTargetSize(engine);\n const aspect = (targetWidth || size.width) / (targetHeight || size.height);\n const view = getViewMatrix(cam) as unknown as Float32Array;\n const proj = getProjectionMatrix(cam, aspect) as unknown as Float32Array;\n const world = mesh.worldMatrix as unknown as Float32Array;\n const cpu = res.meshCpu;\n cpu.set(world, 0);\n cpu.set(view, 16);\n cpu.set(proj, 32);\n // Note: the GS picking pipeline still runs over the full-canvas viewport;\n // the picker output is a 1×1 target reached via the scene pickMatrix.\n cpu[48] = size.width;\n cpu[48 + 1] = size.height;\n cpu[48 + 2] = size.width * 0.5 * proj[0]!;\n cpu[48 + 3] = size.height * 0.5 * proj[5]!;\n // dataSize/alpha already written at construction.\n engine._device.queue.writeBuffer(res.meshUbo, 0, cpu.buffer, 0, cpu.byteLength);\n\n // ── Picking-color UBO ───────────────────────────────────────────\n const [r, g, b] = encodeIdToColor(pickId);\n res.pickingCpu[0] = r;\n res.pickingCpu[1] = g;\n res.pickingCpu[2] = b;\n res.pickingCpu[3] = 0;\n engine._device.queue.writeBuffer(res.pickingUbo, 0, res.pickingCpu.buffer, 0, 16);\n\n pass.setPipeline(cache.pipeline);\n pass.setBindGroup(1, res.meshBG);\n pass.setBindGroup(2, res.pickingBG);\n pass.setVertexBuffer(0, mesh._gs._quadBuffer);\n pass.setVertexBuffer(1, mesh._gs._splatIndexBuffer);\n pass.setIndexBuffer(mesh._gs._indexBuffer, \"uint16\");\n pass.drawIndexed(6, mesh.vertexCount);\n}\n\n/** Draw one GS mesh into the shared pick pass as a pick contributor: (re)binds the GS pick\n * matrix at group 0, then issues the pick draw with id `baseId`. The GS pipeline zooms its own\n * clip-space output onto the pick pixel, so it rebinds group 0 — the next contributor must\n * rebind what it needs (billboards rebind the mesh pick VP). */\nexport function drawGsMeshForPicking(ctx: PickPassContext, mesh: GaussianSplattingMesh, res: GsPickMeshResources, baseId: number): void {\n computeGsPickMatrix(_gsPickMx, ctx.px, ctx.py, ctx.w, ctx.h);\n gsPickWritePickMatrixAndBind(ctx.pass, ctx.engine, _gsPickMx);\n drawGsForPicking(ctx.pass, ctx.engine, ctx.scene, mesh, res, baseId, ctx.w, ctx.h);\n}\n\n/** Compute the pickMatrix for GS picking — same matrix `computePickVP` builds,\n * but applied to the clip-space output of the GS shader rather than the world.\n *\n * Maps canvas-NDC (px, py) ↦ (0, 0), and scales by (w, h) so a single canvas\n * pixel covers the full 1×1 pick render target. */\nexport function computeGsPickMatrix(out: Float32Array, px: number, py: number, w: number, h: number): void {\n const ndcX = (2 * (px + 0.5)) / w - 1;\n const ndcY = 1 - (2 * (py + 0.5)) / h;\n // column-major mat4\n out[0] = w;\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 0;\n out[5] = h;\n out[6] = 0;\n out[7] = 0;\n out[8] = 0;\n out[9] = 0;\n out[10] = 1;\n out[11] = 0;\n out[12] = -ndcX * w;\n out[13] = -ndcY * h;\n out[14] = 0;\n out[15] = 1;\n}\n\n/** Build the pick contributor for one GS mesh (one pick id). The picker calls this once (via the pick\n * source registered by the GS pipeline) and reuses the result, so the pick GPU resources live in\n * this closure and free in `dispose`. */\nexport function createPickContributor(mesh: GaussianSplattingMesh): PickContributor {\n let res: GsPickMeshResources | null = null;\n return {\n draw(ctx, baseId) {\n res ??= createGsPickMeshResources(ctx.engine, mesh);\n drawGsMeshForPicking(ctx, mesh, res, baseId);\n return baseId + 1;\n },\n resolve(info) {\n info.pickedMesh = mesh;\n },\n dispose() {\n if (res) {\n disposeGsPickMeshResources(res);\n res = null;\n }\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ function registerPickSource(scene, entity, load) {
2
+ const source = { entity, load };
3
+ scene._pickSources.push(source);
4
+ return () => {
5
+ const i = scene._pickSources.indexOf(source);
6
+ if (i >= 0) {
7
+ scene._pickSources.splice(i, 1);
8
+ }
9
+ };
10
+ }
11
+
12
+ export { registerPickSource };
13
+ //# sourceMappingURL=pick-contributor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pick-contributor.js","sources":["../../../src/picking/pick-contributor.ts"],"sourcesContent":["/** Pick contributor — the pluggable pick contract, analogous to `Renderable` for drawing.\n *\n * The GPU picker draws meshes itself (the always-present base case, kept intrinsic so a\n * scene that never picks pays zero pick bytes), then iterates `scene._pickSources` with no\n * knowledge of any specific entity type. Each *optional* pickable entity (a Gaussian-splatting\n * mesh, a billboard system, …) registers one `PickSource` when it is added to the scene — the\n * raw entity plus a thunk that lazy-imports the entity's pick pipeline. Adding a new pickable\n * type is a new module that calls `registerPickSource` — no edits to `gpu-picker.ts` or\n * `scene-core.ts`.\n *\n * A `PickSource` is pure data + a dynamic-`import()` thunk, so *rendering* a pickable entity\n * pulls no pick-pipeline bytes (the leanest pay-for-use — a render scene ships only the entity\n * reference + the import thunk). On the first pick the picker loads each source's pipeline, calls\n * its `createPickContributor(entity)` to build the handler, and caches it — so the pipeline,\n * GPU-resource, resolve, and view math all stay in a split chunk fetched only when a pick runs,\n * and a contributor's GPU state lives in its closure and disposes generically.\n *\n * Contributors draw into the SAME 1×1 pick pass as meshes (shared pick-id colour target,\n * `r32float` depth, and reverse-Z depth test), so occlusion is respected across entity\n * types. Each contributor owns a contiguous pick-id range assigned by the picker. */\n\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { SceneContext } from \"../scene/scene-core.js\";\nimport type { Camera } from \"../camera/camera.js\";\nimport type { PickingInfo } from \"./picking-info.js\";\nimport type { GpuPicker } from \"./gpu-picker.js\";\n\n/** Shared state for the single 1×1 pick pass, handed to each contributor's `draw`. */\nexport interface PickPassContext {\n /** The picker running this pass. */\n readonly picker: GpuPicker;\n /** The open pick render pass (meshes already drawn into ids `1..M`). */\n readonly pass: GPURenderPassEncoder;\n readonly engine: EngineContext;\n readonly scene: SceneContext;\n readonly camera: Camera;\n /** Group-0 bind group holding the pick-zoomed view-projection (the mesh pick scene UBO).\n * A contributor that rebinds group 0 (e.g. GS) must expect the next contributor to rebind\n * what it needs; contributors that reuse the mesh VP rebind this at the start of `draw`. */\n readonly sceneBG: GPUBindGroup;\n /** Pick pixel + pick-viewport size (canvas space), for contributors that build their own pick matrix. */\n readonly px: number;\n readonly py: number;\n readonly w: number;\n readonly h: number;\n}\n\n/** A pluggable pick handler for one optional pickable entity. Built lazily (once per picker) by a\n * factory the entity registered when it was added; the picker iterates contributors with no\n * entity-type knowledge. Reused across picks, so `draw` may cache GPU resources in its closure\n * and free them in `dispose`. */\nexport interface PickContributor {\n /** Draw this entity's pickables into the shared pass, assigning ids from `baseId`.\n * Returns the next free id, so `nextId - baseId` is the id count this contributor owns\n * (a hidden/empty entity still consumes its ids so id↔entity mapping stays positional). */\n draw(ctx: PickPassContext, baseId: number): number;\n /** Resolve a read-back id owned by this contributor (`localId = pickId - baseId`) by\n * attaching its payload to `info`. `info.pickedPoint` / `info.distance` are already set\n * from the shared pick-depth readback. */\n resolve(info: PickingInfo, localId: number): void;\n /** Free any GPU resources this contributor created (called by `disposePicker`). */\n dispose?(): void;\n}\n\n/** The uniform export every pick-pipeline module provides: it builds the {@link PickContributor}\n * for one entity. Lazy-imported and called by the picker on the first pick. */\nexport interface PickPipelineModule<E = unknown> {\n createPickContributor(entity: E): PickContributor;\n}\n\n/** A pickable entity registered on the scene: the entity plus a thunk that lazy-imports its pick\n * pipeline. Pure data + a dynamic-`import()` thunk, so pushing one pulls no pick-pipeline bytes into\n * the render bundle. The picker (at pick time) loads the pipeline and calls `createPickContributor`. */\nexport interface PickSource {\n /** The entity to pick (opaque to the picker; handed back to its own pipeline's factory). */\n readonly entity: unknown;\n /** Lazy-import this entity's pick pipeline module. */\n load(): Promise<PickPipelineModule>;\n}\n\n/** Register a pickable entity on the scene (mirrors pushing a `Renderable`). Fully typed: `load` must\n * resolve to the entity's pick pipeline, whose `createPickContributor` accepts this entity's type.\n * The scene stores a heterogeneous list, so the single entity-type erasure is contained here.\n *\n * Returns an unregister function. The entity's disposer should call it so a disposed entity is not\n * left behind in `scene._pickSources` (which the scene otherwise only clears wholesale on teardown). */\nexport function registerPickSource<E>(scene: SceneContext, entity: E, load: () => Promise<PickPipelineModule<E>>): () => void {\n const source: PickSource = { entity, load: load as () => Promise<PickPipelineModule> };\n scene._pickSources.push(source);\n return () => {\n const i = scene._pickSources.indexOf(source);\n if (i >= 0) {\n scene._pickSources.splice(i, 1);\n }\n };\n}\n"],"names":[],"mappings":"AAsFO,SAAS,kBAAA,CAAsB,KAAA,EAAqB,MAAA,EAAW,IAAA,EAAwD;AAC1H,EAAA,MAAM,MAAA,GAAqB,EAAE,MAAA,EAAQ,IAAA,EAAgD;AACrF,EAAA,KAAA,CAAM,YAAA,CAAa,KAAK,MAAM,CAAA;AAC9B,EAAA,OAAO,MAAM;AACT,IAAA,MAAM,CAAA,GAAI,KAAA,CAAM,YAAA,CAAa,OAAA,CAAQ,MAAM,CAAA;AAC3C,IAAA,IAAI,KAAK,CAAA,EAAG;AACR,MAAA,KAAA,CAAM,YAAA,CAAa,MAAA,CAAO,CAAA,EAAG,CAAC,CAAA;AAAA,IAClC;AAAA,EACJ,CAAA;AACJ;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"picking-info.js","sources":["../../../src/picking/picking-info.ts"],"sourcesContent":["import type { Mesh } from \"../mesh/mesh.js\";\nimport type { GaussianSplattingMesh } from \"../mesh/GaussianSplatting/gaussian-splatting-mesh.js\";\nimport type { Ray } from \"./ray.js\";\n\n/** Result of a GPU pick operation. */\nexport interface PickingInfo {\n hit: boolean;\n distance: number;\n pickedPoint: [number, number, number] | null;\n pickedNormal: [number, number, number] | null;\n pickedNormalWorld: [number, number, number] | null;\n pickedFaceNormal: [number, number, number] | null;\n pickedFaceNormalWorld: [number, number, number] | null;\n /** The picked mesh. May be a regular Lite `Mesh` or a `GaussianSplattingMesh`\n * (when GS picking via the gs-picking-pipeline ports the BJS\n * `GaussianSplattingGpuPickingMaterialPlugin`). */\n pickedMesh: Mesh | GaussianSplattingMesh | null;\n faceId: number;\n bu: number;\n bv: number;\n subMeshId: number;\n thinInstanceIndex: number;\n ray: Ray | null;\n}\n\n/** Create an empty (miss) picking result. */\nexport function createEmptyPickingInfo(): PickingInfo {\n return {\n hit: false,\n distance: 0,\n pickedPoint: null,\n pickedNormal: null,\n pickedNormalWorld: null,\n pickedFaceNormal: null,\n pickedFaceNormalWorld: null,\n pickedMesh: null,\n faceId: -1,\n bu: 0,\n bv: 0,\n subMeshId: 0,\n thinInstanceIndex: -1,\n ray: null,\n };\n}\n"],"names":[],"mappings":"AA0BO,SAAS,sBAAA,GAAsC;AAClD,EAAA,OAAO;AAAA,IACH,GAAA,EAAK,KAAA;AAAA,IACL,QAAA,EAAU,CAAA;AAAA,IACV,WAAA,EAAa,IAAA;AAAA,IACb,YAAA,EAAc,IAAA;AAAA,IACd,iBAAA,EAAmB,IAAA;AAAA,IACnB,gBAAA,EAAkB,IAAA;AAAA,IAClB,qBAAA,EAAuB,IAAA;AAAA,IACvB,UAAA,EAAY,IAAA;AAAA,IACZ,MAAA,EAAQ,EAAA;AAAA,IACR,EAAA,EAAI,CAAA;AAAA,IACJ,EAAA,EAAI,CAAA;AAAA,IACJ,SAAA,EAAW,CAAA;AAAA,IACX,iBAAA,EAAmB,EAAA;AAAA,IACnB,GAAA,EAAK;AAAA,GACT;AACJ;;;;"}
1
+ {"version":3,"file":"picking-info.js","sources":["../../../src/picking/picking-info.ts"],"sourcesContent":["import type { Mesh } from \"../mesh/mesh.js\";\nimport type { GaussianSplattingMesh } from \"../mesh/GaussianSplatting/gaussian-splatting-mesh.js\";\nimport type { Ray } from \"./ray.js\";\nimport type { BillboardPickInfo } from \"./billboard-pick-pipeline.js\";\n\n/** Result of a GPU pick operation. */\nexport interface PickingInfo {\n hit: boolean;\n distance: number;\n pickedPoint: [number, number, number] | null;\n pickedNormal: [number, number, number] | null;\n pickedNormalWorld: [number, number, number] | null;\n pickedFaceNormal: [number, number, number] | null;\n pickedFaceNormalWorld: [number, number, number] | null;\n /** The picked mesh. May be a regular Lite `Mesh` or a `GaussianSplattingMesh`\n * (when GS picking via the gs-picking-pipeline ports the BJS\n * `GaussianSplattingGpuPickingMaterialPlugin`). */\n pickedMesh: Mesh | GaussianSplattingMesh | null;\n faceId: number;\n bu: number;\n bv: number;\n subMeshId: number;\n thinInstanceIndex: number;\n ray: Ray | null;\n /** @internal Billboard sprite hit payload, set by the billboard pick contributor when a\n * `BillboardSpriteSystem` sprite was the closest hit. Extracted by `pickBillboardSprite`. */\n _spritePick?: BillboardPickInfo;\n}\n\n/** Create an empty (miss) picking result. */\nexport function createEmptyPickingInfo(): PickingInfo {\n return {\n hit: false,\n distance: 0,\n pickedPoint: null,\n pickedNormal: null,\n pickedNormalWorld: null,\n pickedFaceNormal: null,\n pickedFaceNormalWorld: null,\n pickedMesh: null,\n faceId: -1,\n bu: 0,\n bv: 0,\n subMeshId: 0,\n thinInstanceIndex: -1,\n ray: null,\n };\n}\n"],"names":[],"mappings":"AA8BO,SAAS,sBAAA,GAAsC;AAClD,EAAA,OAAO;AAAA,IACH,GAAA,EAAK,KAAA;AAAA,IACL,QAAA,EAAU,CAAA;AAAA,IACV,WAAA,EAAa,IAAA;AAAA,IACb,YAAA,EAAc,IAAA;AAAA,IACd,iBAAA,EAAmB,IAAA;AAAA,IACnB,gBAAA,EAAkB,IAAA;AAAA,IAClB,qBAAA,EAAuB,IAAA;AAAA,IACvB,UAAA,EAAY,IAAA;AAAA,IACZ,MAAA,EAAQ,EAAA;AAAA,IACR,EAAA,EAAI,CAAA;AAAA,IACJ,EAAA,EAAI,CAAA;AAAA,IACJ,SAAA,EAAW,CAAA;AAAA,IACX,iBAAA,EAAmB,EAAA;AAAA,IACnB,GAAA,EAAK;AAAA,GACT;AACJ;;;;"}
@@ -22,7 +22,7 @@ function createSceneContext(surface, options) {
22
22
  imageProcessing: { exposure: 1, contrast: 1, toneMappingEnabled: false },
23
23
  _renderables: [],
24
24
  _prePasses: [],
25
- _gsMeshes: [],
25
+ _pickSources: [],
26
26
  _uniformUpdaters: [],
27
27
  fixedDeltaMs: 0,
28
28
  _beforeRender: [],
@@ -184,7 +184,7 @@ function disposeScene(scene) {
184
184
  ctx.meshes.length = 0;
185
185
  ctx._renderables.length = 0;
186
186
  ctx._prePasses.length = 0;
187
- ctx._gsMeshes.length = 0;
187
+ ctx._pickSources.length = 0;
188
188
  ctx._uniformUpdaters.length = 0;
189
189
  ctx._beforeRender.length = 0;
190
190
  ctx._deferredBuilders.length = 0;