@babylonjs/lite 1.11.0 → 1.12.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.
- package/CHANGELOG.md +22 -0
- package/dist/index.js +444 -368
- package/dist/index.js.map +1 -1
- package/index.d.ts +191 -4
- package/lib/audio/index.js +1 -0
- package/lib/audio/index.js.map +1 -1
- package/lib/audio/media-stream-output.js +44 -0
- package/lib/audio/media-stream-output.js.map +1 -0
- package/lib/engine/engine.js +1 -1
- package/lib/frame-graph/render-task.js +1 -1
- package/lib/frame-graph/render-task.js.map +1 -1
- package/lib/frame-graph/shadow-inputs.js +10 -1
- package/lib/frame-graph/shadow-inputs.js.map +1 -1
- package/lib/frame-graph/transmission.js +1 -16
- package/lib/frame-graph/transmission.js.map +1 -1
- package/lib/index.js +5 -3
- package/lib/index.js.map +1 -1
- package/lib/loader-gltf/gltf-json-asset.js +1 -1
- package/lib/loader-gltf/gltf-json-asset.js.map +1 -1
- package/lib/loader-gltf/gltf-multi-buffer.js +2 -10
- package/lib/loader-gltf/gltf-multi-buffer.js.map +1 -1
- package/lib/loader-gltf/gltf-pbr-builder-ext.js +4 -18
- package/lib/loader-gltf/gltf-pbr-builder-ext.js.map +1 -1
- package/lib/loader-gltf/gltf-pbr-builder.js +17 -14
- package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
- package/lib/loader-gltf/gltf-sampler-desc.js +3 -16
- package/lib/loader-gltf/gltf-sampler-desc.js.map +1 -1
- package/lib/material/node/blocks/ambient-occlusion-block.js +1 -4
- package/lib/material/node/blocks/ambient-occlusion-block.js.map +1 -1
- package/lib/material/node/blocks/biplanar-block.js +3 -6
- package/lib/material/node/blocks/biplanar-block.js.map +1 -1
- package/lib/material/node/blocks/image-source.js +2 -5
- package/lib/material/node/blocks/image-source.js.map +1 -1
- package/lib/material/node/blocks/input-block.js +5 -26
- package/lib/material/node/blocks/input-block.js.map +1 -1
- package/lib/material/node/blocks/pbr-mr-helper-full.js +5 -8
- package/lib/material/node/blocks/pbr-mr-helper-full.js.map +1 -1
- package/lib/material/node/blocks/reflection-texture-block.js +1 -4
- package/lib/material/node/blocks/reflection-texture-block.js.map +1 -1
- package/lib/material/node/blocks/texture-block.js +2 -5
- package/lib/material/node/blocks/texture-block.js.map +1 -1
- package/lib/material/node/blocks/triplanar-block.js +3 -6
- package/lib/material/node/blocks/triplanar-block.js.map +1 -1
- package/lib/material/node/node-emitter.js +24 -1
- package/lib/material/node/node-emitter.js.map +1 -1
- package/lib/material/node/node-geometry-renderable.js +2 -2
- package/lib/material/node/node-material.js +1 -22
- package/lib/material/node/node-material.js.map +1 -1
- package/lib/material/node/node-types.js.map +1 -1
- package/lib/material/pbr/fragments/shadow-only-fragment.js +68 -0
- package/lib/material/pbr/fragments/shadow-only-fragment.js.map +1 -0
- package/lib/material/pbr/pbr-flag-bits.js.map +1 -1
- package/lib/material/pbr/pbr-material.js.map +1 -1
- package/lib/material/pbr/pbr-renderable.js +3 -0
- package/lib/material/pbr/pbr-renderable.js.map +1 -1
- package/lib/material/pbr/pbr-template.js +1 -0
- package/lib/material/pbr/pbr-template.js.map +1 -1
- package/lib/mesh/GaussianSplatting/gaussian-splatting-mesh.js +2 -8
- package/lib/mesh/GaussianSplatting/gaussian-splatting-mesh.js.map +1 -1
- package/lib/mesh/mesh-dispose.js +2 -0
- package/lib/mesh/mesh-dispose.js.map +1 -1
- package/lib/mesh/mesh.js +2 -8
- package/lib/mesh/mesh.js.map +1 -1
- package/lib/mesh/thin-instance-cull-binding.js +54 -3
- package/lib/mesh/thin-instance-cull-binding.js.map +1 -1
- package/lib/mesh/thin-instance-gpu-culling.js +241 -24
- package/lib/mesh/thin-instance-gpu-culling.js.map +1 -1
- package/lib/mesh/thin-instance.js +112 -1
- package/lib/mesh/thin-instance.js.map +1 -1
- package/lib/picking/gpu-picker.js +4 -2
- package/lib/picking/gpu-picker.js.map +1 -1
- package/lib/picking/picking-pipeline.js +1 -1
- package/lib/picking/picking-pipeline.js.map +1 -1
- package/lib/picking/picking-shader.js +38 -16
- package/lib/picking/picking-shader.js.map +1 -1
- package/lib/scene/scene-core.js +3 -0
- package/lib/scene/scene-core.js.map +1 -1
- package/lib/scene/scene-node.js +2 -6
- package/lib/scene/scene-node.js.map +1 -1
- package/lib/scene/transform-node.js +3 -0
- package/lib/scene/transform-node.js.map +1 -1
- package/lib/scene/world-matrix-state.js +7 -1
- package/lib/scene/world-matrix-state.js.map +1 -1
- package/lib/shadow/csm-directional-shadow-generator.js +22 -1
- package/lib/shadow/csm-directional-shadow-generator.js.map +1 -1
- package/lib/shadow/csm-shadow-task-hooks.js +19 -11
- package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
- package/lib/shadow/esm-directional-shadow-generator.js +2 -51
- package/lib/shadow/esm-directional-shadow-generator.js.map +1 -1
- package/lib/shadow/pcf-directional-shadow-generator.js +2 -48
- package/lib/shadow/pcf-directional-shadow-generator.js.map +1 -1
- package/lib/shadow/shadow-base.js +48 -1
- package/lib/shadow/shadow-base.js.map +1 -1
- package/lib/text/text-renderable.js +2 -10
- package/lib/text/text-renderable.js.map +1 -1
- package/lib/texture/texture-array.js +96 -0
- package/lib/texture/texture-array.js.map +1 -0
- package/package.json +3 -3
|
@@ -170,6 +170,117 @@ function setThinInstanceCullBoundsPad(mesh, pad) {
|
|
|
170
170
|
}
|
|
171
171
|
ti._cullBoundsPad = pad;
|
|
172
172
|
}
|
|
173
|
+
function setThinInstanceLodPartner(fullMesh, lodMesh, options) {
|
|
174
|
+
const ti = fullMesh.thinInstances;
|
|
175
|
+
const lodTi = lodMesh.thinInstances;
|
|
176
|
+
if (!ti || !lodTi) {
|
|
177
|
+
throw new Error("setThinInstanceLodPartner requires thinInstances on both meshes");
|
|
178
|
+
}
|
|
179
|
+
if (fullMesh === lodMesh) {
|
|
180
|
+
throw new Error("setThinInstanceLodPartner requires two distinct meshes");
|
|
181
|
+
}
|
|
182
|
+
if ((ti._refCount ?? 1) > 1 || (lodTi._refCount ?? 1) > 1) {
|
|
183
|
+
throw new Error("setThinInstanceLodPartner does not support thin-instance data shared by mesh clones");
|
|
184
|
+
}
|
|
185
|
+
if (!Number.isFinite(options.distance) || options.distance < 0) {
|
|
186
|
+
throw new RangeError("setThinInstanceLodPartner distance must be a finite non-negative number");
|
|
187
|
+
}
|
|
188
|
+
const band = options.band ?? 0;
|
|
189
|
+
if (!Number.isFinite(band) || band < 0) {
|
|
190
|
+
throw new RangeError("setThinInstanceLodPartner band must be a finite non-negative number");
|
|
191
|
+
}
|
|
192
|
+
if (ti._lodSource) {
|
|
193
|
+
throw new Error("setThinInstanceLodPartner fullMesh cannot already be an LOD partner");
|
|
194
|
+
}
|
|
195
|
+
if (lodTi._lodPartner) {
|
|
196
|
+
throw new Error("setThinInstanceLodPartner lodMesh cannot also own an LOD partner");
|
|
197
|
+
}
|
|
198
|
+
if (lodTi._lodSource && lodTi._lodSource !== fullMesh) {
|
|
199
|
+
throw new Error("setThinInstanceLodPartner lodMesh is already paired with another source mesh");
|
|
200
|
+
}
|
|
201
|
+
if (lodTi.colors && !ti.colors) {
|
|
202
|
+
throw new Error("setThinInstanceLodPartner requires source instance colors when the LOD partner uses instance colors");
|
|
203
|
+
}
|
|
204
|
+
const previous = ti._lodPartner;
|
|
205
|
+
if (previous && previous !== lodMesh) {
|
|
206
|
+
const prevTi = previous.thinInstances;
|
|
207
|
+
if (prevTi && prevTi._lodSource === fullMesh) {
|
|
208
|
+
releaseLodConsumer(prevTi);
|
|
209
|
+
}
|
|
210
|
+
previous._clone = void 0;
|
|
211
|
+
ti._lodBuckets = null;
|
|
212
|
+
}
|
|
213
|
+
ti._lodPartner = lodMesh;
|
|
214
|
+
ti._lodDistance = options.distance;
|
|
215
|
+
ti._lodBand = band;
|
|
216
|
+
lodTi._lodSource = fullMesh;
|
|
217
|
+
fullMesh._clone = lodMesh._clone = "Cannot clone LOD-paired mesh";
|
|
218
|
+
const buckets = ti._lodBuckets ?? /* @__PURE__ */ new WeakMap();
|
|
219
|
+
ti._lodBuckets = buckets;
|
|
220
|
+
lodTi._lodBuckets = buckets;
|
|
221
|
+
if (!lodTi._gpuCullingEnabled) {
|
|
222
|
+
lodTi._lodAutoCull = true;
|
|
223
|
+
lodTi._gpuCullingEnabled = true;
|
|
224
|
+
lodTi._gpuVersion = -1;
|
|
225
|
+
lodTi._colorGpuVersion = -1;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function clearThinInstanceLodPartner(fullMesh) {
|
|
229
|
+
const ti = fullMesh.thinInstances;
|
|
230
|
+
if (!ti?._lodPartner) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const lodMesh = ti._lodPartner;
|
|
234
|
+
const lodTi = lodMesh.thinInstances;
|
|
235
|
+
ti._lodPartner = null;
|
|
236
|
+
ti._lodDistance = void 0;
|
|
237
|
+
ti._lodBand = void 0;
|
|
238
|
+
ti._lodBuckets = null;
|
|
239
|
+
fullMesh._clone = void 0;
|
|
240
|
+
lodMesh._clone = void 0;
|
|
241
|
+
if (lodTi && lodTi._lodSource === fullMesh) {
|
|
242
|
+
releaseLodConsumer(lodTi);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function _detachThinInstanceLodMesh(mesh) {
|
|
246
|
+
const ti = mesh.thinInstances;
|
|
247
|
+
if (!ti) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const source = ti._lodSource;
|
|
251
|
+
const sourceTi = source?.thinInstances;
|
|
252
|
+
if (source && sourceTi?._lodPartner === mesh) {
|
|
253
|
+
sourceTi._lodPartner = null;
|
|
254
|
+
sourceTi._lodDistance = void 0;
|
|
255
|
+
sourceTi._lodBand = void 0;
|
|
256
|
+
sourceTi._lodBuckets = null;
|
|
257
|
+
source._clone = void 0;
|
|
258
|
+
}
|
|
259
|
+
if (ti._lodSource) {
|
|
260
|
+
releaseLodConsumer(ti);
|
|
261
|
+
}
|
|
262
|
+
const partner = ti._lodPartner;
|
|
263
|
+
const partnerTi = partner?.thinInstances;
|
|
264
|
+
ti._lodPartner = null;
|
|
265
|
+
ti._lodDistance = void 0;
|
|
266
|
+
ti._lodBand = void 0;
|
|
267
|
+
ti._lodBuckets = null;
|
|
268
|
+
mesh._clone = void 0;
|
|
269
|
+
if (partner && partnerTi?._lodSource === mesh) {
|
|
270
|
+
partner._clone = void 0;
|
|
271
|
+
releaseLodConsumer(partnerTi);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function releaseLodConsumer(lodTi) {
|
|
275
|
+
lodTi._lodSource = null;
|
|
276
|
+
lodTi._lodBuckets = null;
|
|
277
|
+
if (lodTi._lodAutoCull) {
|
|
278
|
+
lodTi._lodAutoCull = false;
|
|
279
|
+
lodTi._gpuCullingEnabled = false;
|
|
280
|
+
lodTi._gpuVersion = -1;
|
|
281
|
+
lodTi._colorGpuVersion = -1;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
173
284
|
|
|
174
|
-
export { addThinInstance, enableThinInstanceDynamicDrawCount, enableThinInstanceGpuCulling, flushThinInstances, removeThinInstance, setThinInstanceColor, setThinInstanceColors, setThinInstanceCount, setThinInstanceCullBoundsPad, setThinInstanceDrawCount, setThinInstanceMatrix, setThinInstances };
|
|
285
|
+
export { _detachThinInstanceLodMesh, addThinInstance, clearThinInstanceLodPartner, enableThinInstanceDynamicDrawCount, enableThinInstanceGpuCulling, flushThinInstances, removeThinInstance, setThinInstanceColor, setThinInstanceColors, setThinInstanceCount, setThinInstanceCullBoundsPad, setThinInstanceDrawCount, setThinInstanceLodPartner, setThinInstanceMatrix, setThinInstances };
|
|
175
286
|
//# sourceMappingURL=thin-instance.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thin-instance.js","sources":["../../../src/mesh/thin-instance.ts"],"sourcesContent":["/** Thin instances — CPU-side data for hardware-instanced rendering.\n * Each instance carries a world matrix (16 floats) and optionally a\n * per-instance color (4 floats). The render system creates and syncs\n * GPU buffers automatically via version tracking. */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { Mat4 } from \"../math/types.js\";\nimport type { Mesh } from \"./mesh.js\";\n\n/** CPU-side data backing a thin-instanced mesh: world matrices, optional colors, and GPU sync state. */\nexport interface ThinInstanceData {\n /** CPU-side instance world matrices (16 floats per instance). Storage may\n * be Float32Array (default) or Float64Array (after an HPM engine is\n * constructed; the caller built the slab via `allocateMat4()`). The GPU\n * upload path in thin-instance-gpu.ts handles both (REQ-API-3, D5). */\n matrices: Float32Array | Float64Array;\n /** Active instance count. */\n count: number;\n /** @internal Allocated capacity (in instances). */\n _capacity: number;\n /** @internal Version counter — bumped by helpers, checked by render system. */\n _version: number;\n /** @internal GPU buffer — created and managed by render system, not user. */\n _gpuBuffer: GPUBuffer | null;\n /** @internal Whether the current matrix GPU buffer was created with STORAGE usage. */\n _gpuBufferStorage: boolean;\n /** @internal Last version uploaded to GPU. */\n _gpuVersion: number;\n\n /** @internal Min dirty instance index (inclusive). */\n _dirtyMin: number;\n /** @internal Max dirty instance index (exclusive). */\n _dirtyMax: number;\n\n /** Optional per-instance RGBA colors (4 floats per instance). */\n colors?: Float32Array | null;\n /** @internal Color version counter — independent of matrix version. */\n _colorVersion: number;\n /** @internal Min dirty color instance index (inclusive) — mirrors the matrix dirty range so\n * per-instance color updates (setThinInstanceColor) upload only the touched span. */\n _colorDirtyMin: number;\n /** @internal Max dirty color instance index (exclusive). */\n _colorDirtyMax: number;\n /** @internal GPU buffer for per-instance colors. */\n _colorGpuBuffer: GPUBuffer | null;\n /** @internal Whether the current color GPU buffer was created with STORAGE usage. */\n _colorGpuBufferStorage: boolean;\n /** @internal Last color version uploaded to GPU. */\n _colorGpuVersion: number;\n /** @internal Stable indirect args buffer used by cached thin-instance render bundles. */\n _drawArgsBuffer?: GPUBuffer | null;\n /** @internal CPU mirror for the indirect args buffer. */\n _drawArgsData?: Uint32Array;\n /** @internal Last index count written to `_drawArgsBuffer`. */\n _drawArgsIndexCount?: number;\n /** @internal Last instance count observed by a cached direct draw or written to `_drawArgsBuffer`. */\n _drawArgsInstanceCount?: number;\n\n /** @internal Lazy per-mesh F32 upload scratch. Allocated by thin-instance-gpu.ts only\n * when `matrices` is F64-backed (HPM-on); F32-backed input takes a direct\n * writeBuffer fast-path. Sized in floats = `_capacity * 16`. */\n _uploadF32?: Float32Array;\n\n /** @internal Opt-in flag for GPU frustum culling + indirect drawing. */\n _gpuCullingEnabled: boolean;\n\n /** @internal Extra world-space radius added to every instance's culling sphere\n * (see `setThinInstanceCullBoundsPad`). Undefined reads as 0. */\n _cullBoundsPad?: number;\n /** @internal Extra-owner count when shared with a clone via `cloneTransformNode` — see\n * resource/ref-count.ts. Absent/undefined means exactly one (implicit) owner. */\n _refCount?: number;\n}\n\n/** Set all instances from a pre-built matrix array. */\nexport function setThinInstances(mesh: Mesh, matrices: Float32Array | Float64Array, count: number): void {\n if (!mesh.thinInstances) {\n mesh.thinInstances = {\n matrices,\n count,\n _capacity: count,\n _version: 1,\n _gpuBuffer: null,\n _gpuBufferStorage: false,\n _gpuVersion: 0,\n _dirtyMin: 0,\n _dirtyMax: count,\n _colorVersion: 0,\n _colorDirtyMin: 0,\n _colorDirtyMax: 0,\n _colorGpuBuffer: null,\n _colorGpuBufferStorage: false,\n _colorGpuVersion: 0,\n _gpuCullingEnabled: false,\n };\n } else {\n mesh.thinInstances.matrices = matrices;\n mesh.thinInstances.count = count;\n mesh.thinInstances._capacity = count;\n mesh.thinInstances._version++;\n mesh.thinInstances._dirtyMin = 0;\n mesh.thinInstances._dirtyMax = count;\n }\n}\n\n/** Update ONLY the active instance count (and re-upload the [0,count) matrix range), leaving `_capacity`\n * — and therefore the already-allocated GPU buffer — untouched. This is the way to vary how many instances\n * draw FRAME-TO-FRAME on an established thin-instanced mesh WITHOUT recreating the GPU buffer (which would\n * invalidate any cached render/shadow bundle that captured the old buffer handle). Pre-size the buffer once\n * with `setThinInstances(mesh, matrices, capacity)`, then call this each update with `count <= capacity`.\n * Count changes promote cached direct draws to stable indirect arguments, so the bundle stays valid.\n * Caller must keep writing into the SAME `matrices` array the mesh already references. No-op if the mesh\n * isn't thin-instanced yet. */\nexport function setThinInstanceCount(mesh: Mesh, count: number): void {\n const ti = mesh.thinInstances;\n if (!ti) {\n return;\n }\n ti.count = count;\n ti._version++;\n ti._dirtyMin = 0;\n ti._dirtyMax = count;\n}\n\n/** Change only the active instance draw count for an established fixed-capacity pool.\n *\n * Unlike `setThinInstanceCount`, this does not mark matrix or color data dirty. Callers that expose\n * newly written slots must separately mark their exact dirty ranges before the next draw. The pool\n * must have completed an initial full-capacity GPU synchronization. */\nexport function setThinInstanceDrawCount(mesh: Mesh, count: number): void {\n const ti = mesh.thinInstances;\n if (!ti) {\n return;\n }\n if (!Number.isInteger(count) || count < 0 || count > ti._capacity) {\n throw new RangeError(`Thin instance draw count ${count} must be an integer between 0 and capacity ${ti._capacity}`);\n }\n if (ti.count === count) {\n return;\n }\n if (!ti._gpuBuffer || ti._gpuVersion !== ti._version || (ti.colors && (!ti._colorGpuBuffer || ti._colorGpuVersion !== ti._colorVersion))) {\n throw new Error(\"setThinInstanceDrawCount requires a fully synchronized fixed-capacity pool\");\n }\n ti.count = count;\n ti._gpuVersion = ++ti._version;\n}\n\n/** Opt a fixed-capacity thin-instance pool into stable indirect draw arguments before its count changes.\n * The buffer is created on the next normal GPU sync, allowing scene warm-up to absorb its visibility epoch\n * instead of lazily invalidating cached render bundles on the first interactive count transition. */\nexport function enableThinInstanceDynamicDrawCount(mesh: Mesh): void {\n const ti = mesh.thinInstances;\n if (!ti) {\n throw new Error(\"enableThinInstanceDynamicDrawCount requires mesh.thinInstances\");\n }\n if (!ti._drawArgsBuffer) {\n ti._drawArgsInstanceCount = -1;\n }\n}\n\n/** Add one instance. Returns its index. Grows capacity as needed. */\nexport function addThinInstance(mesh: Mesh, matrix: Mat4): number {\n const ti = mesh.thinInstances;\n if (!ti) {\n const capacity = 16;\n const matrices = new F32(capacity * 16);\n matrices.set(matrix, 0);\n mesh.thinInstances = {\n matrices,\n count: 1,\n _capacity: capacity,\n _version: 1,\n _gpuBuffer: null,\n _gpuBufferStorage: false,\n _gpuVersion: 0,\n _dirtyMin: 0,\n _dirtyMax: 1,\n _colorVersion: 0,\n _colorDirtyMin: 0,\n _colorDirtyMax: 0,\n _colorGpuBuffer: null,\n _colorGpuBufferStorage: false,\n _colorGpuVersion: 0,\n _gpuCullingEnabled: false,\n };\n return 0;\n }\n\n const index = ti.count;\n if (index >= ti._capacity) {\n const newCap = ti._capacity * 2;\n const newData = new F32(newCap * 16);\n newData.set(ti.matrices);\n ti.matrices = newData;\n ti._capacity = newCap;\n }\n\n ti.matrices.set(matrix, index * 16);\n ti.count++;\n ti._version++;\n ti._dirtyMin = 0;\n ti._dirtyMax = ti.count;\n return index;\n}\n\n/** Update one instance's matrix. */\nexport function setThinInstanceMatrix(mesh: Mesh, index: number, matrix: Mat4): void {\n const ti = mesh.thinInstances!;\n ti.matrices.set(matrix, index * 16);\n ti._version++;\n ti._dirtyMin = Math.min(ti._dirtyMin, index);\n ti._dirtyMax = Math.max(ti._dirtyMax, index + 1);\n}\n\n/** Remove instance by index. Swap-removes: last instance fills the gap. */\nexport function removeThinInstance(mesh: Mesh, index: number): void {\n const ti = mesh.thinInstances!;\n const last = ti.count - 1;\n if (index !== last) {\n ti.matrices.copyWithin(index * 16, last * 16, last * 16 + 16);\n }\n ti.count--;\n ti._version++;\n ti._dirtyMin = 0;\n ti._dirtyMax = ti.count;\n}\n\n/** Mark thin instance data dirty after direct array manipulation. */\nexport function flushThinInstances(mesh: Mesh): void {\n const ti = mesh.thinInstances!;\n ti._version++;\n ti._dirtyMin = 0;\n ti._dirtyMax = ti.count;\n}\n\n/** Set per-instance RGBA colors for a thin-instanced mesh. */\nexport function setThinInstanceColors(mesh: Mesh, colors: Float32Array): void {\n const ti = mesh.thinInstances!;\n ti.colors = colors;\n ti._version++;\n ti._colorVersion++;\n ti._colorDirtyMin = 0;\n ti._colorDirtyMax = ti.count;\n}\n\n/** Update ONE instance's RGBA color in place — the color twin of `setThinInstanceMatrix`. Only the\n * touched span re-uploads (dirty-range), so per-instance color churn (e.g. streamed instances carrying\n * per-slot animation timestamps) stays cheap on large pools. Requires colors to have been set via\n * `setThinInstanceColors` first. */\nexport function setThinInstanceColor(mesh: Mesh, index: number, r: number, g: number, b: number, a: number): void {\n const ti = mesh.thinInstances!;\n const c = ti.colors!;\n const o = index * 4;\n c[o] = r;\n c[o + 1] = g;\n c[o + 2] = b;\n c[o + 3] = a;\n ti._version++;\n ti._colorVersion++;\n ti._colorDirtyMin = Math.min(ti._colorDirtyMin, index);\n ti._colorDirtyMax = Math.max(ti._colorDirtyMax, index + 1);\n}\n\n/** Enable or disable GPU frustum culling for an existing thin-instanced mesh.\n *\n * Call this after `setThinInstances()`/`addThinInstance()` and before `registerScene()`.\n * The render system keeps the feature opt-in so non-culled thin-instance scenes do not\n * fetch the compute-culling module or allocate compacted visible-instance buffers.\n */\nexport function enableThinInstanceGpuCulling(mesh: Mesh, enabled = true): void {\n const ti = mesh.thinInstances;\n if (!ti) {\n throw new Error(\"enableThinInstanceGpuCulling requires mesh.thinInstances\");\n }\n if (ti._gpuCullingEnabled === enabled) {\n return;\n }\n ti._gpuCullingEnabled = enabled;\n ti._gpuVersion = -1;\n ti._colorGpuVersion = -1;\n}\n\n/** Set an extra WORLD-SPACE radius added to every instance's GPU-culling sphere.\n *\n * The culler derives each instance's sphere from the prototype's authored vertex bounds, so a\n * vertex shader that DISPLACES geometry beyond them — terrain-following height offsets, tall wind\n * sway, growth animations — can move a visible instance outside its sphere and pop it at the\n * screen edge. The usual workaround is disabling culling for the whole mesh; padding the sphere\n * by the maximum shader displacement instead keeps culling both correct and enabled.\n *\n * The pad is in world units, applied after per-instance scaling. 0 restores the exact authored\n * bounds. Takes effect on the next culled pass (the culler reads it when writing its per-pass\n * params), so it can be changed live.\n */\nexport function setThinInstanceCullBoundsPad(mesh: Mesh, pad: number): void {\n const ti = mesh.thinInstances;\n if (!ti) {\n throw new Error(\"setThinInstanceCullBoundsPad requires mesh.thinInstances\");\n }\n ti._cullBoundsPad = pad;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"thin-instance.js","sources":["../../../src/mesh/thin-instance.ts"],"sourcesContent":["/** Thin instances — CPU-side data for hardware-instanced rendering.\n * Each instance carries a world matrix (16 floats) and optionally a\n * per-instance color (4 floats). The render system creates and syncs\n * GPU buffers automatically via version tracking. */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { Mat4 } from \"../math/types.js\";\nimport type { Mesh } from \"./mesh.js\";\nimport type { RenderTargetSignature } from \"../engine/render-target.js\";\n\n/** @internal One render pass's far-bucket output published by the GPU culler for the LOD partner's draw. */\nexport interface ThinInstanceLodBucket {\n /** Compacted far-bucket instance matrices (vertex + storage usage). */\n matrixBuffer: GPUBuffer;\n /** Compacted far-bucket instance colors, when the culled mesh compacts colors. */\n colorBuffer: GPUBuffer | null;\n /** Indirect draw args for the far bucket (partner indexCount, visible far count). */\n argsBuffer: GPUBuffer;\n /** False while the source mesh's culling is not running — the partner must draw nothing. */\n active: boolean;\n}\n\n/** CPU-side data backing a thin-instanced mesh: world matrices, optional colors, and GPU sync state. */\nexport interface ThinInstanceData {\n /** CPU-side instance world matrices (16 floats per instance). Storage may\n * be Float32Array (default) or Float64Array (after an HPM engine is\n * constructed; the caller built the slab via `allocateMat4()`). The GPU\n * upload path in thin-instance-gpu.ts handles both (REQ-API-3, D5). */\n matrices: Float32Array | Float64Array;\n /** Active instance count. */\n count: number;\n /** @internal Allocated capacity (in instances). */\n _capacity: number;\n /** @internal Version counter — bumped by helpers, checked by render system. */\n _version: number;\n /** @internal GPU buffer — created and managed by render system, not user. */\n _gpuBuffer: GPUBuffer | null;\n /** @internal Whether the current matrix GPU buffer was created with STORAGE usage. */\n _gpuBufferStorage: boolean;\n /** @internal Last version uploaded to GPU. */\n _gpuVersion: number;\n\n /** @internal Min dirty instance index (inclusive). */\n _dirtyMin: number;\n /** @internal Max dirty instance index (exclusive). */\n _dirtyMax: number;\n\n /** Optional per-instance RGBA colors (4 floats per instance). */\n colors?: Float32Array | null;\n /** @internal Color version counter — independent of matrix version. */\n _colorVersion: number;\n /** @internal Min dirty color instance index (inclusive) — mirrors the matrix dirty range so\n * per-instance color updates (setThinInstanceColor) upload only the touched span. */\n _colorDirtyMin: number;\n /** @internal Max dirty color instance index (exclusive). */\n _colorDirtyMax: number;\n /** @internal GPU buffer for per-instance colors. */\n _colorGpuBuffer: GPUBuffer | null;\n /** @internal Whether the current color GPU buffer was created with STORAGE usage. */\n _colorGpuBufferStorage: boolean;\n /** @internal Last color version uploaded to GPU. */\n _colorGpuVersion: number;\n /** @internal Stable indirect args buffer used by cached thin-instance render bundles. */\n _drawArgsBuffer?: GPUBuffer | null;\n /** @internal CPU mirror for the indirect args buffer. */\n _drawArgsData?: Uint32Array;\n /** @internal Last index count written to `_drawArgsBuffer`. */\n _drawArgsIndexCount?: number;\n /** @internal Last instance count observed by a cached direct draw or written to `_drawArgsBuffer`. */\n _drawArgsInstanceCount?: number;\n\n /** @internal Lazy per-mesh F32 upload scratch. Allocated by thin-instance-gpu.ts only\n * when `matrices` is F64-backed (HPM-on); F32-backed input takes a direct\n * writeBuffer fast-path. Sized in floats = `_capacity * 16`. */\n _uploadF32?: Float32Array;\n\n /** @internal Opt-in flag for GPU frustum culling + indirect drawing. */\n _gpuCullingEnabled: boolean;\n\n /** @internal Extra world-space radius added to every instance's culling sphere\n * (see `setThinInstanceCullBoundsPad`). Undefined reads as 0. */\n _cullBoundsPad?: number;\n /** @internal LOD partner mesh receiving this mesh's far bucket (set on the full-detail side). */\n _lodPartner?: Mesh | null;\n /** @internal Camera distance (world units) at which an in-frustum instance moves to the LOD partner. */\n _lodDistance?: number;\n /** @internal Width (world units) of the per-instance threshold dither window (0 = hard cut). */\n _lodBand?: number;\n /** @internal Set on the LOD side: mesh whose culling produces this mesh's drawn instances. */\n _lodSource?: Mesh | null;\n /** @internal Shared per-pass far-bucket outputs for an LOD pair. Its presence also marks both meshes as paired. */\n _lodBuckets?: WeakMap<RenderTargetSignature, ThinInstanceLodBucket> | null;\n /** @internal True when pairing auto-enabled GPU culling on the LOD side (clearing restores it). */\n _lodAutoCull?: boolean;\n /** @internal Extra-owner count when shared with a clone via `cloneTransformNode` — see\n * resource/ref-count.ts. Absent/undefined means exactly one (implicit) owner. */\n _refCount?: number;\n}\n\n/** Set all instances from a pre-built matrix array. */\nexport function setThinInstances(mesh: Mesh, matrices: Float32Array | Float64Array, count: number): void {\n if (!mesh.thinInstances) {\n mesh.thinInstances = {\n matrices,\n count,\n _capacity: count,\n _version: 1,\n _gpuBuffer: null,\n _gpuBufferStorage: false,\n _gpuVersion: 0,\n _dirtyMin: 0,\n _dirtyMax: count,\n _colorVersion: 0,\n _colorDirtyMin: 0,\n _colorDirtyMax: 0,\n _colorGpuBuffer: null,\n _colorGpuBufferStorage: false,\n _colorGpuVersion: 0,\n _gpuCullingEnabled: false,\n };\n } else {\n mesh.thinInstances.matrices = matrices;\n mesh.thinInstances.count = count;\n mesh.thinInstances._capacity = count;\n mesh.thinInstances._version++;\n mesh.thinInstances._dirtyMin = 0;\n mesh.thinInstances._dirtyMax = count;\n }\n}\n\n/** Update ONLY the active instance count (and re-upload the [0,count) matrix range), leaving `_capacity`\n * — and therefore the already-allocated GPU buffer — untouched. This is the way to vary how many instances\n * draw FRAME-TO-FRAME on an established thin-instanced mesh WITHOUT recreating the GPU buffer (which would\n * invalidate any cached render/shadow bundle that captured the old buffer handle). Pre-size the buffer once\n * with `setThinInstances(mesh, matrices, capacity)`, then call this each update with `count <= capacity`.\n * Count changes promote cached direct draws to stable indirect arguments, so the bundle stays valid.\n * Caller must keep writing into the SAME `matrices` array the mesh already references. No-op if the mesh\n * isn't thin-instanced yet. */\nexport function setThinInstanceCount(mesh: Mesh, count: number): void {\n const ti = mesh.thinInstances;\n if (!ti) {\n return;\n }\n ti.count = count;\n ti._version++;\n ti._dirtyMin = 0;\n ti._dirtyMax = count;\n}\n\n/** Change only the active instance draw count for an established fixed-capacity pool.\n *\n * Unlike `setThinInstanceCount`, this does not mark matrix or color data dirty. Callers that expose\n * newly written slots must separately mark their exact dirty ranges before the next draw. The pool\n * must have completed an initial full-capacity GPU synchronization. */\nexport function setThinInstanceDrawCount(mesh: Mesh, count: number): void {\n const ti = mesh.thinInstances;\n if (!ti) {\n return;\n }\n if (!Number.isInteger(count) || count < 0 || count > ti._capacity) {\n throw new RangeError(`Thin instance draw count ${count} must be an integer between 0 and capacity ${ti._capacity}`);\n }\n if (ti.count === count) {\n return;\n }\n if (!ti._gpuBuffer || ti._gpuVersion !== ti._version || (ti.colors && (!ti._colorGpuBuffer || ti._colorGpuVersion !== ti._colorVersion))) {\n throw new Error(\"setThinInstanceDrawCount requires a fully synchronized fixed-capacity pool\");\n }\n ti.count = count;\n ti._gpuVersion = ++ti._version;\n}\n\n/** Opt a fixed-capacity thin-instance pool into stable indirect draw arguments before its count changes.\n * The buffer is created on the next normal GPU sync, allowing scene warm-up to absorb its visibility epoch\n * instead of lazily invalidating cached render bundles on the first interactive count transition. */\nexport function enableThinInstanceDynamicDrawCount(mesh: Mesh): void {\n const ti = mesh.thinInstances;\n if (!ti) {\n throw new Error(\"enableThinInstanceDynamicDrawCount requires mesh.thinInstances\");\n }\n if (!ti._drawArgsBuffer) {\n ti._drawArgsInstanceCount = -1;\n }\n}\n\n/** Add one instance. Returns its index. Grows capacity as needed. */\nexport function addThinInstance(mesh: Mesh, matrix: Mat4): number {\n const ti = mesh.thinInstances;\n if (!ti) {\n const capacity = 16;\n const matrices = new F32(capacity * 16);\n matrices.set(matrix, 0);\n mesh.thinInstances = {\n matrices,\n count: 1,\n _capacity: capacity,\n _version: 1,\n _gpuBuffer: null,\n _gpuBufferStorage: false,\n _gpuVersion: 0,\n _dirtyMin: 0,\n _dirtyMax: 1,\n _colorVersion: 0,\n _colorDirtyMin: 0,\n _colorDirtyMax: 0,\n _colorGpuBuffer: null,\n _colorGpuBufferStorage: false,\n _colorGpuVersion: 0,\n _gpuCullingEnabled: false,\n };\n return 0;\n }\n\n const index = ti.count;\n if (index >= ti._capacity) {\n const newCap = ti._capacity * 2;\n const newData = new F32(newCap * 16);\n newData.set(ti.matrices);\n ti.matrices = newData;\n ti._capacity = newCap;\n }\n\n ti.matrices.set(matrix, index * 16);\n ti.count++;\n ti._version++;\n ti._dirtyMin = 0;\n ti._dirtyMax = ti.count;\n return index;\n}\n\n/** Update one instance's matrix. */\nexport function setThinInstanceMatrix(mesh: Mesh, index: number, matrix: Mat4): void {\n const ti = mesh.thinInstances!;\n ti.matrices.set(matrix, index * 16);\n ti._version++;\n ti._dirtyMin = Math.min(ti._dirtyMin, index);\n ti._dirtyMax = Math.max(ti._dirtyMax, index + 1);\n}\n\n/** Remove instance by index. Swap-removes: last instance fills the gap. */\nexport function removeThinInstance(mesh: Mesh, index: number): void {\n const ti = mesh.thinInstances!;\n const last = ti.count - 1;\n if (index !== last) {\n ti.matrices.copyWithin(index * 16, last * 16, last * 16 + 16);\n }\n ti.count--;\n ti._version++;\n ti._dirtyMin = 0;\n ti._dirtyMax = ti.count;\n}\n\n/** Mark thin instance data dirty after direct array manipulation. */\nexport function flushThinInstances(mesh: Mesh): void {\n const ti = mesh.thinInstances!;\n ti._version++;\n ti._dirtyMin = 0;\n ti._dirtyMax = ti.count;\n}\n\n/** Set per-instance RGBA colors for a thin-instanced mesh. */\nexport function setThinInstanceColors(mesh: Mesh, colors: Float32Array): void {\n const ti = mesh.thinInstances!;\n ti.colors = colors;\n ti._version++;\n ti._colorVersion++;\n ti._colorDirtyMin = 0;\n ti._colorDirtyMax = ti.count;\n}\n\n/** Update ONE instance's RGBA color in place — the color twin of `setThinInstanceMatrix`. Only the\n * touched span re-uploads (dirty-range), so per-instance color churn (e.g. streamed instances carrying\n * per-slot animation timestamps) stays cheap on large pools. Requires colors to have been set via\n * `setThinInstanceColors` first. */\nexport function setThinInstanceColor(mesh: Mesh, index: number, r: number, g: number, b: number, a: number): void {\n const ti = mesh.thinInstances!;\n const c = ti.colors!;\n const o = index * 4;\n c[o] = r;\n c[o + 1] = g;\n c[o + 2] = b;\n c[o + 3] = a;\n ti._version++;\n ti._colorVersion++;\n ti._colorDirtyMin = Math.min(ti._colorDirtyMin, index);\n ti._colorDirtyMax = Math.max(ti._colorDirtyMax, index + 1);\n}\n\n/** Enable or disable GPU frustum culling for an existing thin-instanced mesh.\n *\n * Call this after `setThinInstances()`/`addThinInstance()` and before `registerScene()`.\n * The render system keeps the feature opt-in so non-culled thin-instance scenes do not\n * fetch the compute-culling module or allocate compacted visible-instance buffers.\n */\nexport function enableThinInstanceGpuCulling(mesh: Mesh, enabled = true): void {\n const ti = mesh.thinInstances;\n if (!ti) {\n throw new Error(\"enableThinInstanceGpuCulling requires mesh.thinInstances\");\n }\n if (ti._gpuCullingEnabled === enabled) {\n return;\n }\n ti._gpuCullingEnabled = enabled;\n ti._gpuVersion = -1;\n ti._colorGpuVersion = -1;\n}\n\n/** Set an extra WORLD-SPACE radius added to every instance's GPU-culling sphere.\n *\n * The culler derives each instance's sphere from the prototype's authored vertex bounds, so a\n * vertex shader that DISPLACES geometry beyond them — terrain-following height offsets, tall wind\n * sway, growth animations — can move a visible instance outside its sphere and pop it at the\n * screen edge. The usual workaround is disabling culling for the whole mesh; padding the sphere\n * by the maximum shader displacement instead keeps culling both correct and enabled.\n *\n * The pad is in world units, applied after per-instance scaling. 0 restores the exact authored\n * bounds. Takes effect on the next culled pass (the culler reads it when writing its per-pass\n * params), so it can be changed live.\n */\nexport function setThinInstanceCullBoundsPad(mesh: Mesh, pad: number): void {\n const ti = mesh.thinInstances;\n if (!ti) {\n throw new Error(\"setThinInstanceCullBoundsPad requires mesh.thinInstances\");\n }\n ti._cullBoundsPad = pad;\n}\n\n/** Options for `setThinInstanceLodPartner`. */\nexport interface ThinInstanceLodPartnerOptions {\n /** Camera distance (world units) at which an in-frustum instance switches from `fullMesh` to the LOD partner. */\n distance: number;\n /** Width (world units) of a per-instance threshold dither window centered on `distance` (default 0 — hard cut). */\n band?: number;\n}\n\n/** Pair a GPU-culled thin-instanced mesh with a lower-detail partner: when `fullMesh`'s compute cull runs,\n * in-frustum instances closer than `options.distance` keep drawing through `fullMesh` while farther ones are\n * compacted into a second bucket drawn by `lodMesh` instead. `lodMesh` must be a normal scene mesh sharing the\n * same thin-instance matrix layout (it may reference the same matrices array); while paired it draws ONLY the\n * far bucket — never its own instance count — so when culling is disabled, unavailable, or the instance count\n * is 0, `fullMesh` falls back to drawing ALL instances and `lodMesh` draws nothing. `options.band` dithers the\n * threshold per instance by ±band/2 via a deterministic hash of the instance index. Like\n * `enableThinInstanceGpuCulling`, call after `setThinInstances()` and before `registerScene()`;\n * `distance`/`band` may be re-set live by calling again with the same pair. */\nexport function setThinInstanceLodPartner(fullMesh: Mesh, lodMesh: Mesh, options: ThinInstanceLodPartnerOptions): void {\n const ti = fullMesh.thinInstances;\n const lodTi = lodMesh.thinInstances;\n if (!ti || !lodTi) {\n throw new Error(\"setThinInstanceLodPartner requires thinInstances on both meshes\");\n }\n if (fullMesh === lodMesh) {\n throw new Error(\"setThinInstanceLodPartner requires two distinct meshes\");\n }\n if ((ti._refCount ?? 1) > 1 || (lodTi._refCount ?? 1) > 1) {\n throw new Error(\"setThinInstanceLodPartner does not support thin-instance data shared by mesh clones\");\n }\n if (!Number.isFinite(options.distance) || options.distance < 0) {\n throw new RangeError(\"setThinInstanceLodPartner distance must be a finite non-negative number\");\n }\n const band = options.band ?? 0;\n if (!Number.isFinite(band) || band < 0) {\n throw new RangeError(\"setThinInstanceLodPartner band must be a finite non-negative number\");\n }\n if (ti._lodSource) {\n throw new Error(\"setThinInstanceLodPartner fullMesh cannot already be an LOD partner\");\n }\n if (lodTi._lodPartner) {\n throw new Error(\"setThinInstanceLodPartner lodMesh cannot also own an LOD partner\");\n }\n if (lodTi._lodSource && lodTi._lodSource !== fullMesh) {\n throw new Error(\"setThinInstanceLodPartner lodMesh is already paired with another source mesh\");\n }\n if (lodTi.colors && !ti.colors) {\n throw new Error(\"setThinInstanceLodPartner requires source instance colors when the LOD partner uses instance colors\");\n }\n const previous = ti._lodPartner;\n if (previous && previous !== lodMesh) {\n const prevTi = previous.thinInstances;\n if (prevTi && prevTi._lodSource === fullMesh) {\n releaseLodConsumer(prevTi);\n }\n previous._clone = undefined;\n ti._lodBuckets = null;\n }\n ti._lodPartner = lodMesh;\n ti._lodDistance = options.distance;\n ti._lodBand = band;\n lodTi._lodSource = fullMesh;\n fullMesh._clone = lodMesh._clone = \"Cannot clone LOD-paired mesh\";\n const buckets = ti._lodBuckets ?? new WeakMap<RenderTargetSignature, ThinInstanceLodBucket>();\n ti._lodBuckets = buckets;\n lodTi._lodBuckets = buckets;\n if (!lodTi._gpuCullingEnabled) {\n // Route the partner through the culling draw path (so it can consume the far bucket and skip\n // its own count) and make sure the cull module is fetched for its material group.\n lodTi._lodAutoCull = true;\n lodTi._gpuCullingEnabled = true;\n lodTi._gpuVersion = -1;\n lodTi._colorGpuVersion = -1;\n }\n}\n\n/** Dissolve a `setThinInstanceLodPartner` pairing: `fullMesh` culling reverts to its single-bucket output and\n * the former partner returns to independent rendering (drawing its own instance count again after the next\n * renderable rebuild; GPU culling auto-enabled by the pairing is switched back off). */\nexport function clearThinInstanceLodPartner(fullMesh: Mesh): void {\n const ti = fullMesh.thinInstances;\n if (!ti?._lodPartner) {\n return;\n }\n const lodMesh = ti._lodPartner;\n const lodTi = lodMesh.thinInstances;\n ti._lodPartner = null;\n ti._lodDistance = undefined;\n ti._lodBand = undefined;\n ti._lodBuckets = null;\n fullMesh._clone = undefined;\n lodMesh._clone = undefined;\n if (lodTi && lodTi._lodSource === fullMesh) {\n releaseLodConsumer(lodTi);\n }\n}\n\n/** @internal Break any incoming or outgoing LOD pairing before a mesh's GPU resources are disposed. */\nexport function _detachThinInstanceLodMesh(mesh: Mesh): void {\n const ti = mesh.thinInstances;\n if (!ti) {\n return;\n }\n const source = ti._lodSource;\n const sourceTi = source?.thinInstances;\n if (source && sourceTi?._lodPartner === mesh) {\n sourceTi._lodPartner = null;\n sourceTi._lodDistance = undefined;\n sourceTi._lodBand = undefined;\n sourceTi._lodBuckets = null;\n source._clone = undefined;\n }\n if (ti._lodSource) {\n releaseLodConsumer(ti);\n }\n const partner = ti._lodPartner;\n const partnerTi = partner?.thinInstances;\n ti._lodPartner = null;\n ti._lodDistance = undefined;\n ti._lodBand = undefined;\n ti._lodBuckets = null;\n mesh._clone = undefined;\n if (partner && partnerTi?._lodSource === mesh) {\n partner._clone = undefined;\n releaseLodConsumer(partnerTi);\n }\n}\n\nfunction releaseLodConsumer(lodTi: ThinInstanceData): void {\n lodTi._lodSource = null;\n lodTi._lodBuckets = null;\n if (lodTi._lodAutoCull) {\n lodTi._lodAutoCull = false;\n lodTi._gpuCullingEnabled = false;\n lodTi._gpuVersion = -1;\n lodTi._colorGpuVersion = -1;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ import { getViewProjectionMatrix, getCameraPosition } from '../camera/camera.js'
|
|
|
8
8
|
import { resolveCameraViewport } from '../camera/viewport.js';
|
|
9
9
|
import { createUniformBuffer, createMappedBuffer, createEmptyUniformBuffer } from '../resource/gpu-buffers.js';
|
|
10
10
|
|
|
11
|
-
const _pickVP = new F32(
|
|
11
|
+
const _pickVP = new F32(20);
|
|
12
12
|
const PICK_MESH_UBO_BYTES = 80;
|
|
13
13
|
const PICK_TI_UBO_BYTES = 16;
|
|
14
14
|
const _uboScratch = new ArrayBuffer(PICK_MESH_UBO_BYTES);
|
|
@@ -57,7 +57,7 @@ function ensureTargets(engine, picker) {
|
|
|
57
57
|
function ensureSceneUbo(engine, picker) {
|
|
58
58
|
const device = engine._device;
|
|
59
59
|
if (!picker._sceneUbo) {
|
|
60
|
-
picker._sceneUbo = createEmptyUniformBuffer(engine,
|
|
60
|
+
picker._sceneUbo = createEmptyUniformBuffer(engine, 80, "pick-scene-ubo");
|
|
61
61
|
const sceneBGL = getPickingSceneBGL(engine);
|
|
62
62
|
picker._sceneBG = device.createBindGroup({ label: "pick-scene-bg", layout: sceneBGL, entries: [{ binding: 0, resource: { buffer: picker._sceneUbo } }] });
|
|
63
63
|
}
|
|
@@ -158,6 +158,8 @@ async function pickAsyncImpl(picker, x, y, options) {
|
|
|
158
158
|
const vp = getViewProjectionMatrix(camera, aspect);
|
|
159
159
|
const debugRay = debugLabel ? createPickingRay(px, py, vp, w, h) : null;
|
|
160
160
|
computePickVP(_pickVP, vp, px, py, w, h);
|
|
161
|
+
_pickVP[16] = viewport.x + px + 0.5;
|
|
162
|
+
_pickVP[17] = viewport.y + py + 0.5;
|
|
161
163
|
const rt = ensureTargets(engine, picker);
|
|
162
164
|
const sceneUbo = ensureSceneUbo(engine, picker);
|
|
163
165
|
device.queue.writeBuffer(sceneUbo, 0, _pickVP);
|
|
@@ -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 { 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, \"pick-discard-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 if (!scene.camera) {\n return createEmptyPickingInfo();\n }\n\n // Resolve every lazy dependency before opening a command encoder. Awaiting while a render pass is\n // still unsubmitted lets the main frame resize and retire mesh buffers that the pick pass already\n // captured; the eventual pick submission then references destroyed geometry.\n const preparedContributors: { source: PickSource; contributor: PickContributor }[] = [];\n if (scene._pickSources.length > 0) {\n const sources = scene._pickSources.slice();\n for (const source of sources) {\n let contributor = picker._contributors?.get(source);\n if (!contributor) {\n const pipeline = await source.load();\n if (!scene._pickSources.includes(source)) {\n continue;\n }\n contributor = pipeline.createPickContributor(source.entity);\n (picker._contributors ??= new Map()).set(source, contributor);\n }\n preparedContributors.push({ source, contributor });\n }\n }\n\n let deformedGeometry: typeof DeformedGeometry | null = null;\n for (const mesh of scene.meshes) {\n if ((mesh.morphTargets || mesh.skeleton) && mesh._cpuPositions) {\n deformedGeometry = await import(\"./deformed-geometry.js\");\n break;\n }\n }\n\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\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, \"pick-thin-instance-ubo\");\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, \"pick-mesh-ubo\");\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, \"pick-deformed-position\");\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 (preparedContributors.length > 0) {\n const pickCtx: PickPassContext = { picker, pass, engine, scene, camera, sceneBG: picker._sceneBG!, px, py, w, h };\n for (const { source, contributor } of preparedContributors) {\n if (!scene._pickSources.includes(source)) {\n continue;\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 let pickId: number;\n let depth: number;\n try {\n await Promise.all([rt.colorStaging.mapAsync(GPUMapMode.READ), rt.depthStaging.mapAsync(GPUMapMode.READ)]);\n\n const colorData = new U8(rt.colorStaging.getMappedRange());\n pickId = (colorData[0]! << 16) | (colorData[1]! << 8) | colorData[2]!;\n depth = new F32(rt.depthStaging.getMappedRange())[0]!;\n rt.colorStaging.unmap();\n rt.depthStaging.unmap();\n } finally {\n for (let i = 0; i < tempBuffers.length; i++) {\n tempBuffers[i]!.destroy();\n }\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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"}
|
|
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(20);\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 (80 bytes: pick VP + original framebuffer fragment coordinate). */\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, 80, \"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`, `fragmentCoord` (the selected pixel\n * centre in the original backing framebuffer), `pickId`, `thinInstanceIndex`,\n * `hasThinInstance`, and `instanceExtras` (the\n * original thin-instance matrix w lanes, zero for non-instanced meshes). Storage entries are\n * 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, \"pick-discard-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 if (!scene.camera) {\n return createEmptyPickingInfo();\n }\n\n // Resolve every lazy dependency before opening a command encoder. Awaiting while a render pass is\n // still unsubmitted lets the main frame resize and retire mesh buffers that the pick pass already\n // captured; the eventual pick submission then references destroyed geometry.\n const preparedContributors: { source: PickSource; contributor: PickContributor }[] = [];\n if (scene._pickSources.length > 0) {\n const sources = scene._pickSources.slice();\n for (const source of sources) {\n let contributor = picker._contributors?.get(source);\n if (!contributor) {\n const pipeline = await source.load();\n if (!scene._pickSources.includes(source)) {\n continue;\n }\n contributor = pipeline.createPickContributor(source.entity);\n (picker._contributors ??= new Map()).set(source, contributor);\n }\n preparedContributors.push({ source, contributor });\n }\n }\n\n let deformedGeometry: typeof DeformedGeometry | null = null;\n for (const mesh of scene.meshes) {\n if ((mesh.morphTargets || mesh.skeleton) && mesh._cpuPositions) {\n deformedGeometry = await import(\"./deformed-geometry.js\");\n break;\n }\n }\n\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 // The pick renders into a 1x1 target, whose fragment position is always (0.5, 0.5). Preserve the\n // selected pixel's coordinate in the original backing framebuffer so consumer discard rules can\n // reproduce screen-space dithering exactly. Include the camera viewport origin because visible\n // material fragment coordinates are surface-wide, while px/py above are viewport-local.\n _pickVP[16] = viewport.x + px + 0.5;\n _pickVP[17] = viewport.y + py + 0.5;\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\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, \"pick-thin-instance-ubo\");\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, \"pick-mesh-ubo\");\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, \"pick-deformed-position\");\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 (preparedContributors.length > 0) {\n const pickCtx: PickPassContext = { picker, pass, engine, scene, camera, sceneBG: picker._sceneBG!, px, py, w, h };\n for (const { source, contributor } of preparedContributors) {\n if (!scene._pickSources.includes(source)) {\n continue;\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 let pickId: number;\n let depth: number;\n try {\n await Promise.all([rt.colorStaging.mapAsync(GPUMapMode.READ), rt.depthStaging.mapAsync(GPUMapMode.READ)]);\n\n const colorData = new U8(rt.colorStaging.getMappedRange());\n pickId = (colorData[0]! << 16) | (colorData[1]! << 8) | colorData[2]!;\n depth = new F32(rt.depthStaging.getMappedRange())[0]!;\n rt.colorStaging.unmap();\n rt.depthStaging.unmap();\n } finally {\n for (let i = 0; i < tempBuffers.length; i++) {\n tempBuffers[i]!.destroy();\n }\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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"}
|
|
@@ -20,7 +20,7 @@ function invalidateIfNeeded(engine) {
|
|
|
20
20
|
function getPickingSceneBGL(engine) {
|
|
21
21
|
invalidateIfNeeded(engine);
|
|
22
22
|
if (!_sceneBGL) {
|
|
23
|
-
_sceneBGL = createSingleUniformBGL(engine, "picking-scene-bgl", SS.VERTEX);
|
|
23
|
+
_sceneBGL = createSingleUniformBGL(engine, "picking-scene-bgl", SS.VERTEX | SS.FRAGMENT);
|
|
24
24
|
}
|
|
25
25
|
return _sceneBGL;
|
|
26
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"picking-pipeline.js","sources":["../../../src/picking/picking-pipeline.ts"],"sourcesContent":["import { SS } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport { pickingShaderSource, pickingThinInstanceShaderSource } from \"./picking-shader.js\";\nimport { createSingleUniformBGL } from \"../shader/bgl-helpers.js\";\n\n// ─── Cache state (auto-invalidate on device change) ─────────────────\n\nlet _cachedDevice: GPUDevice | null = null;\nlet _sceneBGL: GPUBindGroupLayout | null = null;\nlet _meshBGL: GPUBindGroupLayout | null = null;\nlet _tiMeshBGL: GPUBindGroupLayout | null = null;\nlet _pipelineSets: Map<string, PickingPipelineSet> | null = null;\n\nexport interface PickingDiscardPipelineOptions {\n readonly key: string;\n readonly wgsl: string;\n readonly storage?: readonly { readonly name: string; readonly type: string }[];\n}\n\nexport interface PickingPipelineSet {\n readonly regularPipeline: GPURenderPipeline;\n readonly thinInstancePipeline: GPURenderPipeline;\n readonly discardBGL: GPUBindGroupLayout | null;\n}\n\nfunction invalidateIfNeeded(engine: EngineContext): void {\n const device = engine._device;\n if (device !== _cachedDevice) {\n _sceneBGL = null;\n _meshBGL = null;\n _tiMeshBGL = null;\n _pipelineSets = null;\n _cachedDevice = device;\n }\n}\n\n// ─── Bind group layouts ─────────────────────────────────────────────\n\n/** Group 0: scene-level viewProjection uniform. */\nexport function getPickingSceneBGL(engine: EngineContext): GPUBindGroupLayout {\n invalidateIfNeeded(engine);\n if (!_sceneBGL) {\n _sceneBGL = createSingleUniformBGL(engine, \"picking-scene-bgl\", SS.VERTEX);\n }\n return _sceneBGL;\n}\n\n/** Group 1: per-mesh world matrix + pickId uniform (regular meshes). */\nfunction getPickingMeshBGL(engine: EngineContext): GPUBindGroupLayout {\n invalidateIfNeeded(engine);\n if (!_meshBGL) {\n _meshBGL = createSingleUniformBGL(engine, \"picking-mesh-bgl\", SS.VERTEX | SS.FRAGMENT);\n }\n return _meshBGL;\n}\n\n/** Group 1: per-mesh baseMeshPickId uniform + instance storage buffer (thin instances). */\nfunction getPickingTIMeshBGL(engine: EngineContext): GPUBindGroupLayout {\n const device = engine._device;\n invalidateIfNeeded(engine);\n if (!_tiMeshBGL) {\n _tiMeshBGL = device.createBindGroupLayout({\n label: \"picking-ti-mesh-bgl\",\n entries: [\n {\n binding: 0,\n visibility: SS.VERTEX | SS.FRAGMENT,\n buffer: { type: \"uniform\" },\n },\n {\n binding: 1,\n visibility: SS.VERTEX,\n buffer: { type: \"read-only-storage\" },\n },\n ],\n });\n }\n return _tiMeshBGL;\n}\n\nfunction createDiscardBGL(engine: EngineContext, discard: PickingDiscardPipelineOptions): GPUBindGroupLayout {\n return engine._device.createBindGroupLayout({\n label: `picking-discard-${discard.key}-bgl`,\n entries: (discard.storage ?? []).map((_, binding) => ({\n binding,\n visibility: SS.FRAGMENT,\n buffer: { type: \"read-only-storage\" },\n })),\n });\n}\n\n// ─── Position-only vertex layout ────────────────────────────────────\n\nconst POSITION_VERTEX_LAYOUT: GPUVertexBufferLayout = {\n arrayStride: 12,\n attributes: [{ shaderLocation: 0, offset: 0, format: \"float32x3\" }],\n};\n\n// ─── Pipeline creation ──────────────────────────────────────────────\n\ninterface PickingPipelineOptions {\n shader: string;\n meshBGL: GPUBindGroupLayout;\n discardBGL: GPUBindGroupLayout | null;\n label: string;\n}\n\nfunction createPickingPipelineInternal(engine: EngineContext, opts: PickingPipelineOptions): GPURenderPipeline {\n const device = engine._device;\n const module = device.createShaderModule({ label: `${opts.label}-shader`, code: opts.shader });\n const bindGroupLayouts = opts.discardBGL ? [getPickingSceneBGL(engine), opts.meshBGL, opts.discardBGL] : [getPickingSceneBGL(engine), opts.meshBGL];\n const layout = device.createPipelineLayout({\n label: `${opts.label}-pipeline-layout`,\n bindGroupLayouts,\n });\n return device.createRenderPipeline({\n label: `${opts.label}-pipeline`,\n layout,\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [POSITION_VERTEX_LAYOUT],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format: \"rgba8unorm\" }, { format: \"r32float\" }],\n },\n depthStencil: {\n format: \"depth24plus\",\n depthCompare: \"greater\",\n depthWriteEnabled: true,\n },\n primitive: {\n topology: \"triangle-list\",\n // Pick the NEAREST surface regardless of facing (matches Babylon.js Scene.pick, which intersects\n // both triangle sides). Culling back faces here would make any DOUBLE-SIDED mesh\n // unpickable wherever the renderer shows its back face.\n cullMode: \"none\",\n },\n multisample: { count: 1 },\n });\n}\n\n/** Get (or create) the picking pipeline set for the default path or a caller-provided discard rule. */\nexport function getPickingPipelineSet(engine: EngineContext, discard?: PickingDiscardPipelineOptions | null): PickingPipelineSet {\n invalidateIfNeeded(engine);\n const key = discard ? `discard:${discard.key}` : \"default\";\n const pipelineSets = _pipelineSets ?? (_pipelineSets = new Map());\n const cached = pipelineSets.get(key);\n if (cached) {\n return cached;\n }\n\n const discardBGL = discard?.storage?.length ? createDiscardBGL(engine, discard) : null;\n const shaderOptions = discard ? { discardWgsl: discard.wgsl, storage: discard.storage } : undefined;\n const regularPipeline = createPickingPipelineInternal(engine, {\n shader: pickingShaderSource(shaderOptions),\n meshBGL: getPickingMeshBGL(engine),\n discardBGL,\n label: discard ? `picking-${discard.key}` : \"picking\",\n });\n const thinInstancePipeline = createPickingPipelineInternal(engine, {\n shader: pickingThinInstanceShaderSource(shaderOptions),\n meshBGL: getPickingTIMeshBGL(engine),\n discardBGL,\n label: discard ? `picking-ti-${discard.key}` : \"picking-ti\",\n });\n const set = { regularPipeline, thinInstancePipeline, discardBGL };\n pipelineSets.set(key, set);\n return set;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"picking-pipeline.js","sources":["../../../src/picking/picking-pipeline.ts"],"sourcesContent":["import { SS } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport { pickingShaderSource, pickingThinInstanceShaderSource } from \"./picking-shader.js\";\nimport { createSingleUniformBGL } from \"../shader/bgl-helpers.js\";\n\n// ─── Cache state (auto-invalidate on device change) ─────────────────\n\nlet _cachedDevice: GPUDevice | null = null;\nlet _sceneBGL: GPUBindGroupLayout | null = null;\nlet _meshBGL: GPUBindGroupLayout | null = null;\nlet _tiMeshBGL: GPUBindGroupLayout | null = null;\nlet _pipelineSets: Map<string, PickingPipelineSet> | null = null;\n\nexport interface PickingDiscardPipelineOptions {\n readonly key: string;\n readonly wgsl: string;\n readonly storage?: readonly { readonly name: string; readonly type: string }[];\n}\n\nexport interface PickingPipelineSet {\n readonly regularPipeline: GPURenderPipeline;\n readonly thinInstancePipeline: GPURenderPipeline;\n readonly discardBGL: GPUBindGroupLayout | null;\n}\n\nfunction invalidateIfNeeded(engine: EngineContext): void {\n const device = engine._device;\n if (device !== _cachedDevice) {\n _sceneBGL = null;\n _meshBGL = null;\n _tiMeshBGL = null;\n _pipelineSets = null;\n _cachedDevice = device;\n }\n}\n\n// ─── Bind group layouts ─────────────────────────────────────────────\n\n/** Group 0: scene-level viewProjection uniform. */\nexport function getPickingSceneBGL(engine: EngineContext): GPUBindGroupLayout {\n invalidateIfNeeded(engine);\n if (!_sceneBGL) {\n _sceneBGL = createSingleUniformBGL(engine, \"picking-scene-bgl\", SS.VERTEX | SS.FRAGMENT);\n }\n return _sceneBGL;\n}\n\n/** Group 1: per-mesh world matrix + pickId uniform (regular meshes). */\nfunction getPickingMeshBGL(engine: EngineContext): GPUBindGroupLayout {\n invalidateIfNeeded(engine);\n if (!_meshBGL) {\n _meshBGL = createSingleUniformBGL(engine, \"picking-mesh-bgl\", SS.VERTEX | SS.FRAGMENT);\n }\n return _meshBGL;\n}\n\n/** Group 1: per-mesh baseMeshPickId uniform + instance storage buffer (thin instances). */\nfunction getPickingTIMeshBGL(engine: EngineContext): GPUBindGroupLayout {\n const device = engine._device;\n invalidateIfNeeded(engine);\n if (!_tiMeshBGL) {\n _tiMeshBGL = device.createBindGroupLayout({\n label: \"picking-ti-mesh-bgl\",\n entries: [\n {\n binding: 0,\n visibility: SS.VERTEX | SS.FRAGMENT,\n buffer: { type: \"uniform\" },\n },\n {\n binding: 1,\n visibility: SS.VERTEX,\n buffer: { type: \"read-only-storage\" },\n },\n ],\n });\n }\n return _tiMeshBGL;\n}\n\nfunction createDiscardBGL(engine: EngineContext, discard: PickingDiscardPipelineOptions): GPUBindGroupLayout {\n return engine._device.createBindGroupLayout({\n label: `picking-discard-${discard.key}-bgl`,\n entries: (discard.storage ?? []).map((_, binding) => ({\n binding,\n visibility: SS.FRAGMENT,\n buffer: { type: \"read-only-storage\" },\n })),\n });\n}\n\n// ─── Position-only vertex layout ────────────────────────────────────\n\nconst POSITION_VERTEX_LAYOUT: GPUVertexBufferLayout = {\n arrayStride: 12,\n attributes: [{ shaderLocation: 0, offset: 0, format: \"float32x3\" }],\n};\n\n// ─── Pipeline creation ──────────────────────────────────────────────\n\ninterface PickingPipelineOptions {\n shader: string;\n meshBGL: GPUBindGroupLayout;\n discardBGL: GPUBindGroupLayout | null;\n label: string;\n}\n\nfunction createPickingPipelineInternal(engine: EngineContext, opts: PickingPipelineOptions): GPURenderPipeline {\n const device = engine._device;\n const module = device.createShaderModule({ label: `${opts.label}-shader`, code: opts.shader });\n const bindGroupLayouts = opts.discardBGL ? [getPickingSceneBGL(engine), opts.meshBGL, opts.discardBGL] : [getPickingSceneBGL(engine), opts.meshBGL];\n const layout = device.createPipelineLayout({\n label: `${opts.label}-pipeline-layout`,\n bindGroupLayouts,\n });\n return device.createRenderPipeline({\n label: `${opts.label}-pipeline`,\n layout,\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [POSITION_VERTEX_LAYOUT],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format: \"rgba8unorm\" }, { format: \"r32float\" }],\n },\n depthStencil: {\n format: \"depth24plus\",\n depthCompare: \"greater\",\n depthWriteEnabled: true,\n },\n primitive: {\n topology: \"triangle-list\",\n // Pick the NEAREST surface regardless of facing (matches Babylon.js Scene.pick, which intersects\n // both triangle sides). Culling back faces here would make any DOUBLE-SIDED mesh\n // unpickable wherever the renderer shows its back face.\n cullMode: \"none\",\n },\n multisample: { count: 1 },\n });\n}\n\n/** Get (or create) the picking pipeline set for the default path or a caller-provided discard rule. */\nexport function getPickingPipelineSet(engine: EngineContext, discard?: PickingDiscardPipelineOptions | null): PickingPipelineSet {\n invalidateIfNeeded(engine);\n const key = discard ? `discard:${discard.key}` : \"default\";\n const pipelineSets = _pipelineSets ?? (_pipelineSets = new Map());\n const cached = pipelineSets.get(key);\n if (cached) {\n return cached;\n }\n\n const discardBGL = discard?.storage?.length ? createDiscardBGL(engine, discard) : null;\n const shaderOptions = discard ? { discardWgsl: discard.wgsl, storage: discard.storage } : undefined;\n const regularPipeline = createPickingPipelineInternal(engine, {\n shader: pickingShaderSource(shaderOptions),\n meshBGL: getPickingMeshBGL(engine),\n discardBGL,\n label: discard ? `picking-${discard.key}` : \"picking\",\n });\n const thinInstancePipeline = createPickingPipelineInternal(engine, {\n shader: pickingThinInstanceShaderSource(shaderOptions),\n meshBGL: getPickingTIMeshBGL(engine),\n discardBGL,\n label: discard ? `picking-ti-${discard.key}` : \"picking-ti\",\n });\n const set = { regularPipeline, thinInstancePipeline, discardBGL };\n pipelineSets.set(key, set);\n return set;\n}\n"],"names":[],"mappings":";;;;AAOA,IAAI,aAAA,GAAkC,IAAA;AACtC,IAAI,SAAA,GAAuC,IAAA;AAC3C,IAAI,QAAA,GAAsC,IAAA;AAC1C,IAAI,UAAA,GAAwC,IAAA;AAC5C,IAAI,aAAA,GAAwD,IAAA;AAc5D,SAAS,mBAAmB,MAAA,EAA6B;AACrD,EAAA,MAAM,SAAS,MAAA,CAAO,OAAA;AACtB,EAAA,IAAI,WAAW,aAAA,EAAe;AAC1B,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,UAAA,GAAa,IAAA;AACb,IAAA,aAAA,GAAgB,IAAA;AAChB,IAAA,aAAA,GAAgB,MAAA;AAAA,EACpB;AACJ;AAKO,SAAS,mBAAmB,MAAA,EAA2C;AAC1E,EAAA,kBAAA,CAAmB,MAAM,CAAA;AACzB,EAAA,IAAI,CAAC,SAAA,EAAW;AACZ,IAAA,SAAA,GAAY,uBAAuB,MAAA,EAAQ,mBAAA,EAAqB,EAAA,CAAG,MAAA,GAAS,GAAG,QAAQ,CAAA;AAAA,EAC3F;AACA,EAAA,OAAO,SAAA;AACX;AAGA,SAAS,kBAAkB,MAAA,EAA2C;AAClE,EAAA,kBAAA,CAAmB,MAAM,CAAA;AACzB,EAAA,IAAI,CAAC,QAAA,EAAU;AACX,IAAA,QAAA,GAAW,uBAAuB,MAAA,EAAQ,kBAAA,EAAoB,EAAA,CAAG,MAAA,GAAS,GAAG,QAAQ,CAAA;AAAA,EACzF;AACA,EAAA,OAAO,QAAA;AACX;AAGA,SAAS,oBAAoB,MAAA,EAA2C;AACpE,EAAA,MAAM,SAAS,MAAA,CAAO,OAAA;AACtB,EAAA,kBAAA,CAAmB,MAAM,CAAA;AACzB,EAAA,IAAI,CAAC,UAAA,EAAY;AACb,IAAA,UAAA,GAAa,OAAO,qBAAA,CAAsB;AAAA,MACtC,KAAA,EAAO,qBAAA;AAAA,MACP,OAAA,EAAS;AAAA,QACL;AAAA,UACI,OAAA,EAAS,CAAA;AAAA,UACT,UAAA,EAAY,EAAA,CAAG,MAAA,GAAS,EAAA,CAAG,QAAA;AAAA,UAC3B,MAAA,EAAQ,EAAE,IAAA,EAAM,SAAA;AAAU,SAC9B;AAAA,QACA;AAAA,UACI,OAAA,EAAS,CAAA;AAAA,UACT,YAAY,EAAA,CAAG,MAAA;AAAA,UACf,MAAA,EAAQ,EAAE,IAAA,EAAM,mBAAA;AAAoB;AACxC;AACJ,KACH,CAAA;AAAA,EACL;AACA,EAAA,OAAO,UAAA;AACX;AAEA,SAAS,gBAAA,CAAiB,QAAuB,OAAA,EAA4D;AACzG,EAAA,OAAO,MAAA,CAAO,QAAQ,qBAAA,CAAsB;AAAA,IACxC,KAAA,EAAO,CAAA,gBAAA,EAAmB,OAAA,CAAQ,GAAG,CAAA,IAAA,CAAA;AAAA,IACrC,OAAA,EAAA,CAAU,QAAQ,OAAA,IAAW,IAAI,GAAA,CAAI,CAAC,GAAG,OAAA,MAAa;AAAA,MAClD,OAAA;AAAA,MACA,YAAY,EAAA,CAAG,QAAA;AAAA,MACf,MAAA,EAAQ,EAAE,IAAA,EAAM,mBAAA;AAAoB,KACxC,CAAE;AAAA,GACL,CAAA;AACL;AAIA,MAAM,sBAAA,GAAgD;AAAA,EAClD,WAAA,EAAa,EAAA;AAAA,EACb,UAAA,EAAY,CAAC,EAAE,cAAA,EAAgB,GAAG,MAAA,EAAQ,CAAA,EAAG,MAAA,EAAQ,WAAA,EAAa;AACtE,CAAA;AAWA,SAAS,6BAAA,CAA8B,QAAuB,IAAA,EAAiD;AAC3G,EAAA,MAAM,SAAS,MAAA,CAAO,OAAA;AACtB,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,kBAAA,CAAmB,EAAE,KAAA,EAAO,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,OAAA,CAAA,EAAW,IAAA,EAAM,IAAA,CAAK,MAAA,EAAQ,CAAA;AAC7F,EAAA,MAAM,mBAAmB,IAAA,CAAK,UAAA,GAAa,CAAC,kBAAA,CAAmB,MAAM,CAAA,EAAG,IAAA,CAAK,OAAA,EAAS,IAAA,CAAK,UAAU,CAAA,GAAI,CAAC,mBAAmB,MAAM,CAAA,EAAG,KAAK,OAAO,CAAA;AAClJ,EAAA,MAAM,MAAA,GAAS,OAAO,oBAAA,CAAqB;AAAA,IACvC,KAAA,EAAO,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,gBAAA,CAAA;AAAA,IACpB;AAAA,GACH,CAAA;AACD,EAAA,OAAO,OAAO,oBAAA,CAAqB;AAAA,IAC/B,KAAA,EAAO,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,SAAA,CAAA;AAAA,IACpB,MAAA;AAAA,IACA,MAAA,EAAQ;AAAA,MACJ,MAAA;AAAA,MACA,UAAA,EAAY,IAAA;AAAA,MACZ,OAAA,EAAS,CAAC,sBAAsB;AAAA,KACpC;AAAA,IACA,QAAA,EAAU;AAAA,MACN,MAAA;AAAA,MACA,UAAA,EAAY,IAAA;AAAA,MACZ,OAAA,EAAS,CAAC,EAAE,MAAA,EAAQ,cAAa,EAAG,EAAE,MAAA,EAAQ,UAAA,EAAY;AAAA,KAC9D;AAAA,IACA,YAAA,EAAc;AAAA,MACV,MAAA,EAAQ,aAAA;AAAA,MACR,YAAA,EAAc,SAAA;AAAA,MACd,iBAAA,EAAmB;AAAA,KACvB;AAAA,IACA,SAAA,EAAW;AAAA,MACP,QAAA,EAAU,eAAA;AAAA;AAAA;AAAA;AAAA,MAIV,QAAA,EAAU;AAAA,KACd;AAAA,IACA,WAAA,EAAa,EAAE,KAAA,EAAO,CAAA;AAAE,GAC3B,CAAA;AACL;AAGO,SAAS,qBAAA,CAAsB,QAAuB,OAAA,EAAoE;AAC7H,EAAA,kBAAA,CAAmB,MAAM,CAAA;AACzB,EAAA,MAAM,GAAA,GAAM,OAAA,GAAU,CAAA,QAAA,EAAW,OAAA,CAAQ,GAAG,CAAA,CAAA,GAAK,SAAA;AACjD,EAAA,MAAM,YAAA,GAAe,aAAA,KAAkB,aAAA,mBAAgB,IAAI,GAAA,EAAI,CAAA;AAC/D,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,GAAA,CAAI,GAAG,CAAA;AACnC,EAAA,IAAI,MAAA,EAAQ;AACR,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,aAAa,OAAA,EAAS,OAAA,EAAS,SAAS,gBAAA,CAAiB,MAAA,EAAQ,OAAO,CAAA,GAAI,IAAA;AAClF,EAAA,MAAM,aAAA,GAAgB,UAAU,EAAE,WAAA,EAAa,QAAQ,IAAA,EAAM,OAAA,EAAS,OAAA,CAAQ,OAAA,EAAQ,GAAI,MAAA;AAC1F,EAAA,MAAM,eAAA,GAAkB,8BAA8B,MAAA,EAAQ;AAAA,IAC1D,MAAA,EAAQ,oBAAoB,aAAa,CAAA;AAAA,IACzC,OAAA,EAAS,kBAAkB,MAAM,CAAA;AAAA,IACjC,UAAA;AAAA,IACA,KAAA,EAAO,OAAA,GAAU,CAAA,QAAA,EAAW,OAAA,CAAQ,GAAG,CAAA,CAAA,GAAK;AAAA,GAC/C,CAAA;AACD,EAAA,MAAM,oBAAA,GAAuB,8BAA8B,MAAA,EAAQ;AAAA,IAC/D,MAAA,EAAQ,gCAAgC,aAAa,CAAA;AAAA,IACrD,OAAA,EAAS,oBAAoB,MAAM,CAAA;AAAA,IACnC,UAAA;AAAA,IACA,KAAA,EAAO,OAAA,GAAU,CAAA,WAAA,EAAc,OAAA,CAAQ,GAAG,CAAA,CAAA,GAAK;AAAA,GAClD,CAAA;AACD,EAAA,MAAM,GAAA,GAAM,EAAE,eAAA,EAAiB,oBAAA,EAAsB,UAAA,EAAW;AAChE,EAAA,YAAA,CAAa,GAAA,CAAI,KAAK,GAAG,CAAA;AACzB,EAAA,OAAO,GAAA;AACX;;;;"}
|