@babylonjs/lite 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/dist/index.js +596 -526
- package/dist/index.js.map +1 -1
- package/index.d.ts +107 -4
- package/lib/engine/engine.js +1 -1
- package/lib/engine/engine.js.map +1 -1
- package/lib/frame-graph/render-task.js +6 -4
- package/lib/frame-graph/render-task.js.map +1 -1
- package/lib/index.js +4 -1
- package/lib/index.js.map +1 -1
- package/lib/loader-gltf/gltf-ext-quantization.js +4 -1
- package/lib/loader-gltf/gltf-ext-quantization.js.map +1 -1
- package/lib/loader-gltf/gltf-feature-registry.js +11 -1
- package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
- package/lib/loader-gltf/gltf-feature-variants.js +11 -1
- package/lib/loader-gltf/gltf-feature-variants.js.map +1 -1
- package/lib/loader-gltf/gltf-json-asset.js +8 -1
- package/lib/loader-gltf/gltf-json-asset.js.map +1 -1
- package/lib/loader-gltf/gltf-material.js +4 -29
- package/lib/loader-gltf/gltf-material.js.map +1 -1
- package/lib/loader-gltf/gltf-parser.js +1 -8
- package/lib/loader-gltf/gltf-parser.js.map +1 -1
- package/lib/loader-gltf/gltf-pbr-builder.js +1 -15
- package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
- package/lib/loader-gltf/gltf-variants.js +5 -10
- package/lib/loader-gltf/gltf-variants.js.map +1 -1
- package/lib/loader-gltf/load-gltf.js +19 -29
- package/lib/loader-gltf/load-gltf.js.map +1 -1
- package/lib/material/pbr/background-dds-skybox.js +35 -93
- package/lib/material/pbr/background-dds-skybox.js.map +1 -1
- package/lib/material/pbr/background-ground.js +13 -18
- package/lib/material/pbr/background-ground.js.map +1 -1
- package/lib/material/pbr/background-hdr-skybox.js +35 -93
- package/lib/material/pbr/background-hdr-skybox.js.map +1 -1
- package/lib/material/pbr/background-solid-skybox.js +35 -93
- package/lib/material/pbr/background-solid-skybox.js.map +1 -1
- package/lib/material/pbr/fragments/clearcoat-fragment.js +5 -1
- package/lib/material/pbr/fragments/clearcoat-fragment.js.map +1 -1
- package/lib/material/pbr/fragments/iridescence-fragment.js +4 -1
- package/lib/material/pbr/fragments/iridescence-fragment.js.map +1 -1
- package/lib/material/pbr/fragments/refraction-rtt-fragment.js +7 -2
- package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
- package/lib/material/pbr/fragments/sheen-fragment.js +2 -1
- package/lib/material/pbr/fragments/sheen-fragment.js.map +1 -1
- package/lib/material/pbr/fragments/subsurface-fragment.js +2 -1
- package/lib/material/pbr/fragments/subsurface-fragment.js.map +1 -1
- package/lib/material/pbr/fragments/unlit-fragment.js +1 -2
- package/lib/material/pbr/fragments/unlit-fragment.js.map +1 -1
- package/lib/material/pbr/pbr-compose.js +2 -0
- package/lib/material/pbr/pbr-compose.js.map +1 -1
- package/lib/material/pbr/pbr-flag-bits.js +1 -16
- package/lib/material/pbr/pbr-flag-bits.js.map +1 -1
- package/lib/material/pbr/pbr-flags.js +1 -1
- package/lib/material/pbr/pbr-fog-wgsl.js +1 -1
- package/lib/material/pbr/pbr-fog-wgsl.js.map +1 -1
- package/lib/material/pbr/pbr-geometry-output-shader.js +2 -1
- package/lib/material/pbr/pbr-geometry-output-shader.js.map +1 -1
- package/lib/material/pbr/pbr-geometry-view.js +2 -1
- package/lib/material/pbr/pbr-geometry-view.js.map +1 -1
- package/lib/material/pbr/pbr-renderable.js +4 -0
- package/lib/material/pbr/pbr-renderable.js.map +1 -1
- package/lib/material/pbr/pbr-template-gamma.js +7 -0
- package/lib/material/pbr/pbr-template-gamma.js.map +1 -0
- package/lib/material/pbr/pbr-template.js +2 -2
- package/lib/material/pbr/pbr-template.js.map +1 -1
- package/lib/material/standard/skybox-cubemap.js +1 -1
- package/lib/material/standard/skybox-cubemap.js.map +1 -1
- package/lib/material/standard/standard-template.js +1 -1
- package/lib/material/standard/standard-template.js.map +1 -1
- package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline-sh.js +3 -5
- package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline-sh.js.map +1 -1
- package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline.js +3 -5
- package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline.js.map +1 -1
- package/lib/particle/node/blocks/particle-condition-block.js +55 -0
- package/lib/particle/node/blocks/particle-condition-block.js.map +1 -0
- package/lib/particle/node/blocks/particle-float-to-int-block.js +28 -0
- package/lib/particle/node/blocks/particle-float-to-int-block.js.map +1 -0
- package/lib/particle/node/blocks/particle-gradient-block.js +32 -0
- package/lib/particle/node/blocks/particle-gradient-block.js.map +1 -0
- package/lib/particle/node/blocks/particle-gradient-value-block.js +11 -0
- package/lib/particle/node/blocks/particle-gradient-value-block.js.map +1 -0
- package/lib/particle/node/blocks/particle-lerp-block.js +2 -24
- package/lib/particle/node/blocks/particle-lerp-block.js.map +1 -1
- package/lib/particle/node/blocks/particle-lerp.js +27 -0
- package/lib/particle/node/blocks/particle-lerp.js.map +1 -0
- package/lib/particle/node/blocks/particle-vector-length-block.js +30 -0
- package/lib/particle/node/blocks/particle-vector-length-block.js.map +1 -0
- package/lib/particle/node/blocks/system-block.js +13 -3
- package/lib/particle/node/blocks/system-block.js.map +1 -1
- package/lib/particle/node/blocks/texture-source-block.js +2 -1
- package/lib/particle/node/blocks/texture-source-block.js.map +1 -1
- package/lib/particle/node/blocks/update-size-block.js +19 -0
- package/lib/particle/node/blocks/update-size-block.js.map +1 -0
- package/lib/particle/node/npe-build-state.js +10 -0
- package/lib/particle/node/npe-build-state.js.map +1 -1
- package/lib/particle/node/npe-registry.js +12 -0
- package/lib/particle/node/npe-registry.js.map +1 -1
- package/lib/particle/particle-system.js +7 -2
- package/lib/particle/particle-system.js.map +1 -1
- package/lib/picking/billboard-pick-pipeline.js +269 -0
- package/lib/picking/billboard-pick-pipeline.js.map +1 -0
- package/lib/picking/gpu-picker.js +37 -37
- package/lib/picking/gpu-picker.js.map +1 -1
- package/lib/picking/gs-picking-pipeline.js +26 -1
- package/lib/picking/gs-picking-pipeline.js.map +1 -1
- package/lib/picking/pick-contributor.js +13 -0
- package/lib/picking/pick-contributor.js.map +1 -0
- package/lib/picking/picking-info.js.map +1 -1
- package/lib/scene/scene-core.js +2 -2
- package/lib/scene/scene-core.js.map +1 -1
- package/lib/shader/wgsl-fog.js +18 -0
- package/lib/shader/wgsl-fog.js.map +1 -0
- package/lib/shader/wgsl-helpers.js +1 -16
- package/lib/shader/wgsl-helpers.js.map +1 -1
- package/lib/sprite/billboard-scene.js +2 -0
- package/lib/sprite/billboard-scene.js.map +1 -1
- package/lib/sprite/picking/pick-billboard.js +16 -0
- package/lib/sprite/picking/pick-billboard.js.map +1 -0
- package/lib/sprite/picking/pick-sprite-2d.js +41 -0
- package/lib/sprite/picking/pick-sprite-2d.js.map +1 -0
- package/lib/sprite/shared/sprite-atlas.js +4 -1
- package/lib/sprite/shared/sprite-atlas.js.map +1 -1
- package/lib/sprite/sprite-2d.js.map +1 -1
- package/lib/sprite/sprite-pipeline.js +18 -1
- package/lib/sprite/sprite-pipeline.js.map +1 -1
- package/lib/sprite/sprite-renderer.js +12 -6
- package/lib/sprite/sprite-renderer.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { U32, F32 } from '../engine/typed-arrays.js';
|
|
2
|
+
import { BU, SS, CW } from '../engine/gpu-flags.js';
|
|
3
|
+
import { BILLBOARD_INSTANCE_STRIDE_BYTES } from '../sprite/billboard-sprite.js';
|
|
4
|
+
import { getPickingSceneBGL } from './picking-pipeline.js';
|
|
5
|
+
import { getViewMatrix } from '../camera/camera.js';
|
|
6
|
+
|
|
7
|
+
const BILLBOARD_PICK_UBO_BYTES = 48;
|
|
8
|
+
const POSITION_OFFSET_BYTES = 0;
|
|
9
|
+
const SIZE_OFFSET_BYTES = 12;
|
|
10
|
+
const UV_MIN_OFFSET_BYTES = 20;
|
|
11
|
+
const UV_MAX_OFFSET_BYTES = 28;
|
|
12
|
+
const ROTATION_OFFSET_BYTES = 36;
|
|
13
|
+
const PIVOT_OFFSET_BYTES = 40;
|
|
14
|
+
const SHARED_QUAD_INDICES = new Uint16Array([0, 1, 2, 0, 2, 3]);
|
|
15
|
+
function packBillboardPickUbo(view, baseId, cutoff, axis, f32, u32) {
|
|
16
|
+
f32[0] = view[0];
|
|
17
|
+
f32[1] = view[4];
|
|
18
|
+
f32[2] = view[8];
|
|
19
|
+
u32[3] = baseId;
|
|
20
|
+
f32[4] = view[1];
|
|
21
|
+
f32[5] = view[5];
|
|
22
|
+
f32[6] = view[9];
|
|
23
|
+
f32[7] = cutoff;
|
|
24
|
+
f32[8] = axis[0];
|
|
25
|
+
f32[9] = axis[1];
|
|
26
|
+
f32[10] = axis[2];
|
|
27
|
+
}
|
|
28
|
+
function makePickBasisWgsl(orientation) {
|
|
29
|
+
switch (orientation) {
|
|
30
|
+
case "facing":
|
|
31
|
+
return `fn basis() -> B {
|
|
32
|
+
let r = normalize(bb.camRight);
|
|
33
|
+
let u = normalize(bb.camUp);
|
|
34
|
+
return B(r, -u);
|
|
35
|
+
}`;
|
|
36
|
+
case "axis-locked":
|
|
37
|
+
return `fn basis() -> B {
|
|
38
|
+
let a = normalize(bb.axis);
|
|
39
|
+
let cr = normalize(bb.camRight);
|
|
40
|
+
let pr = cr - a * dot(cr, a);
|
|
41
|
+
let pl = length(pr);
|
|
42
|
+
let f = select(vec3f(0, 0, 1), vec3f(1, 0, 0), abs(a.z) > 0.999);
|
|
43
|
+
let fr = cross(a, f);
|
|
44
|
+
let r = select(fr / max(length(fr), 1e-4), pr / max(pl, 1e-4), pl > 1e-4);
|
|
45
|
+
return B(r, -a);
|
|
46
|
+
}`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function makeBillboardPickWgsl(orientation, isCutout) {
|
|
50
|
+
const cutoutBindings = isCutout ? `@group(1) @binding(1) var atlasTex: texture_2d<f32>;
|
|
51
|
+
@group(1) @binding(2) var atlasSamp: sampler;` : "";
|
|
52
|
+
const uvVarying = isCutout ? `,
|
|
53
|
+
@location(1) uv: vec2f` : "";
|
|
54
|
+
const uvAssign = isCutout ? `out.uv = mix(in.a, in.b, q);` : "";
|
|
55
|
+
const cutoutDiscard = isCutout ? `let s = textureSample(atlasTex, atlasSamp, in.uv);
|
|
56
|
+
if (s.a < bb.cutoff) {
|
|
57
|
+
discard;
|
|
58
|
+
}` : "";
|
|
59
|
+
return `struct PickScene { viewProjection: mat4x4f };
|
|
60
|
+
@group(0) @binding(0) var<uniform> scene: PickScene;
|
|
61
|
+
struct BB {
|
|
62
|
+
camRight: vec3f,
|
|
63
|
+
baseId: u32,
|
|
64
|
+
camUp: vec3f,
|
|
65
|
+
cutoff: f32,
|
|
66
|
+
axis: vec3f,
|
|
67
|
+
_pad: f32,
|
|
68
|
+
};
|
|
69
|
+
@group(1) @binding(0) var<uniform> bb: BB;
|
|
70
|
+
${cutoutBindings}
|
|
71
|
+
struct B { r: vec3f, u: vec3f };
|
|
72
|
+
${makePickBasisWgsl(orientation)}
|
|
73
|
+
struct I {
|
|
74
|
+
@builtin(vertex_index) vid: u32,
|
|
75
|
+
@builtin(instance_index) iid: u32,
|
|
76
|
+
@location(0) p: vec3f,
|
|
77
|
+
@location(1) s: vec2f,
|
|
78
|
+
@location(2) a: vec2f,
|
|
79
|
+
@location(3) b: vec2f,
|
|
80
|
+
@location(4) r: f32,
|
|
81
|
+
@location(5) o: vec2f,
|
|
82
|
+
};
|
|
83
|
+
struct O {
|
|
84
|
+
@builtin(position) p: vec4f,
|
|
85
|
+
@location(0) @interpolate(flat) pickId: u32${uvVarying}
|
|
86
|
+
};
|
|
87
|
+
@vertex
|
|
88
|
+
fn vs(in: I) -> O {
|
|
89
|
+
let q = vec2f(select(0.0, 1.0, in.vid == 1u || in.vid == 2u), select(0.0, 1.0, in.vid >= 2u));
|
|
90
|
+
let l = (q - in.o) * in.s;
|
|
91
|
+
let cr = cos(in.r);
|
|
92
|
+
let sr = sin(in.r);
|
|
93
|
+
let rot = vec2f(l.x * cr - l.y * sr, l.x * sr + l.y * cr);
|
|
94
|
+
let bs = basis();
|
|
95
|
+
let wp = in.p + bs.r * rot.x + bs.u * rot.y;
|
|
96
|
+
var out: O;
|
|
97
|
+
out.p = scene.viewProjection * vec4f(wp, 1);
|
|
98
|
+
out.pickId = bb.baseId + in.iid;
|
|
99
|
+
${uvAssign}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
struct FsOut { @location(0) color: vec4f, @location(1) depth: vec4f };
|
|
103
|
+
@fragment
|
|
104
|
+
fn fs(in: O) -> FsOut {
|
|
105
|
+
${cutoutDiscard}
|
|
106
|
+
let id = in.pickId;
|
|
107
|
+
let r = f32((id >> 16u) & 0xFFu) / 255.0;
|
|
108
|
+
let g = f32((id >> 8u) & 0xFFu) / 255.0;
|
|
109
|
+
let b = f32(id & 0xFFu) / 255.0;
|
|
110
|
+
return FsOut(vec4f(r, g, b, 1.0), vec4f(in.p.z, 0.0, 0.0, 0.0));
|
|
111
|
+
}`;
|
|
112
|
+
}
|
|
113
|
+
let _cache = null;
|
|
114
|
+
function getCache(engine) {
|
|
115
|
+
if (_cache && _cache.device === engine._device) {
|
|
116
|
+
return _cache;
|
|
117
|
+
}
|
|
118
|
+
_cache = { device: engine._device, pipelines: /* @__PURE__ */ new Map(), bgls: /* @__PURE__ */ new Map() };
|
|
119
|
+
return _cache;
|
|
120
|
+
}
|
|
121
|
+
function getGroup1Bgl(engine, cache, isCutout) {
|
|
122
|
+
const key = isCutout ? "cutout" : "plain";
|
|
123
|
+
let bgl = cache.bgls.get(key);
|
|
124
|
+
if (!bgl) {
|
|
125
|
+
const entries = [{ binding: 0, visibility: SS.VERTEX | SS.FRAGMENT, buffer: { type: "uniform" } }];
|
|
126
|
+
if (isCutout) {
|
|
127
|
+
entries.push({ binding: 1, visibility: SS.FRAGMENT, texture: { sampleType: "float" } });
|
|
128
|
+
entries.push({ binding: 2, visibility: SS.FRAGMENT, sampler: { type: "filtering" } });
|
|
129
|
+
}
|
|
130
|
+
bgl = engine._device.createBindGroupLayout({ label: `billboard-pick-bgl-${key}`, entries });
|
|
131
|
+
cache.bgls.set(key, bgl);
|
|
132
|
+
}
|
|
133
|
+
return bgl;
|
|
134
|
+
}
|
|
135
|
+
function getPickPipeline(engine, system) {
|
|
136
|
+
const cache = getCache(engine);
|
|
137
|
+
const orientation = system._orientation;
|
|
138
|
+
const isCutout = system._depthMode === "cutout";
|
|
139
|
+
const key = `${orientation}|${isCutout ? 1 : 0}`;
|
|
140
|
+
const cached = cache.pipelines.get(key);
|
|
141
|
+
if (cached) {
|
|
142
|
+
return cached;
|
|
143
|
+
}
|
|
144
|
+
const device = engine._device;
|
|
145
|
+
const module = device.createShaderModule({ label: `billboard-pick-${key}`, code: makeBillboardPickWgsl(orientation, isCutout) });
|
|
146
|
+
const group1Bgl = getGroup1Bgl(engine, cache, isCutout);
|
|
147
|
+
const pipeline = device.createRenderPipeline({
|
|
148
|
+
label: `billboard-pick-pipeline-${key}`,
|
|
149
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [getPickingSceneBGL(engine), group1Bgl] }),
|
|
150
|
+
vertex: {
|
|
151
|
+
module,
|
|
152
|
+
entryPoint: "vs",
|
|
153
|
+
buffers: [
|
|
154
|
+
{
|
|
155
|
+
arrayStride: BILLBOARD_INSTANCE_STRIDE_BYTES,
|
|
156
|
+
stepMode: "instance",
|
|
157
|
+
attributes: [
|
|
158
|
+
{ shaderLocation: 0, offset: POSITION_OFFSET_BYTES, format: "float32x3" },
|
|
159
|
+
{ shaderLocation: 1, offset: SIZE_OFFSET_BYTES, format: "float32x2" },
|
|
160
|
+
{ shaderLocation: 2, offset: UV_MIN_OFFSET_BYTES, format: "float32x2" },
|
|
161
|
+
{ shaderLocation: 3, offset: UV_MAX_OFFSET_BYTES, format: "float32x2" },
|
|
162
|
+
{ shaderLocation: 4, offset: ROTATION_OFFSET_BYTES, format: "float32" },
|
|
163
|
+
{ shaderLocation: 5, offset: PIVOT_OFFSET_BYTES, format: "float32x2" }
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
fragment: { module, entryPoint: "fs", targets: [{ format: "rgba8unorm", writeMask: CW.ALL }, { format: "r32float" }] },
|
|
169
|
+
// Match the mesh picker: reverse-Z depth (clear 0, "greater") so a billboard occluded by a
|
|
170
|
+
// mesh — or by a nearer billboard — loses the pick. Write depth so billboards occlude too.
|
|
171
|
+
primitive: { topology: "triangle-list", cullMode: "none" },
|
|
172
|
+
depthStencil: { format: "depth24plus", depthCompare: "greater", depthWriteEnabled: true },
|
|
173
|
+
multisample: { count: 1 }
|
|
174
|
+
});
|
|
175
|
+
cache.pipelines.set(key, pipeline);
|
|
176
|
+
return pipeline;
|
|
177
|
+
}
|
|
178
|
+
function createBillboardPickResources(engine, system) {
|
|
179
|
+
const device = engine._device;
|
|
180
|
+
const cache = getCache(engine);
|
|
181
|
+
const isCutout = system._depthMode === "cutout";
|
|
182
|
+
const ubo = device.createBuffer({ label: "billboard-pick-ubo", size: BILLBOARD_PICK_UBO_BYTES, usage: BU.UNIFORM | BU.COPY_DST });
|
|
183
|
+
const uboScratch = new ArrayBuffer(BILLBOARD_PICK_UBO_BYTES);
|
|
184
|
+
const instanceCapacity = Math.max(1, system._capacity);
|
|
185
|
+
const instanceBuffer = device.createBuffer({
|
|
186
|
+
label: "billboard-pick-instances",
|
|
187
|
+
size: instanceCapacity * BILLBOARD_INSTANCE_STRIDE_BYTES,
|
|
188
|
+
usage: BU.VERTEX | BU.COPY_DST
|
|
189
|
+
});
|
|
190
|
+
const indexBuffer = device.createBuffer({ label: "billboard-pick-indices", size: SHARED_QUAD_INDICES.byteLength, usage: BU.INDEX | BU.COPY_DST });
|
|
191
|
+
device.queue.writeBuffer(indexBuffer, 0, SHARED_QUAD_INDICES);
|
|
192
|
+
const entries = [{ binding: 0, resource: { buffer: ubo } }];
|
|
193
|
+
if (isCutout) {
|
|
194
|
+
entries.push({ binding: 1, resource: system.atlas.texture.view });
|
|
195
|
+
entries.push({ binding: 2, resource: system.atlas.texture.sampler });
|
|
196
|
+
}
|
|
197
|
+
const bindGroup = device.createBindGroup({ label: "billboard-pick-bg", layout: getGroup1Bgl(engine, cache, isCutout), entries });
|
|
198
|
+
return {
|
|
199
|
+
ubo,
|
|
200
|
+
uboScratch,
|
|
201
|
+
uboF32: new F32(uboScratch),
|
|
202
|
+
uboU32: new U32(uboScratch),
|
|
203
|
+
instanceBuffer,
|
|
204
|
+
instanceCapacity,
|
|
205
|
+
indexBuffer,
|
|
206
|
+
bindGroup
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function disposeBillboardPickResources(res) {
|
|
210
|
+
res.ubo.destroy();
|
|
211
|
+
res.instanceBuffer.destroy();
|
|
212
|
+
res.indexBuffer.destroy();
|
|
213
|
+
}
|
|
214
|
+
function drawBillboardForPicking(pass, engine, system, res, baseId, view) {
|
|
215
|
+
const device = engine._device;
|
|
216
|
+
const count = system.count;
|
|
217
|
+
if (count === 0) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (system._capacity > res.instanceCapacity) {
|
|
221
|
+
res.instanceBuffer.destroy();
|
|
222
|
+
res.instanceCapacity = system._capacity;
|
|
223
|
+
res.instanceBuffer = device.createBuffer({
|
|
224
|
+
label: "billboard-pick-instances",
|
|
225
|
+
size: res.instanceCapacity * BILLBOARD_INSTANCE_STRIDE_BYTES,
|
|
226
|
+
usage: BU.VERTEX | BU.COPY_DST
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
const data = system._instanceData;
|
|
230
|
+
device.queue.writeBuffer(res.instanceBuffer, 0, data.buffer, data.byteOffset, count * BILLBOARD_INSTANCE_STRIDE_BYTES);
|
|
231
|
+
const cutoff = system._depthMode === "cutout" ? system.alphaCutoff : 0;
|
|
232
|
+
packBillboardPickUbo(view, baseId, cutoff, system._axis, res.uboF32, res.uboU32);
|
|
233
|
+
device.queue.writeBuffer(res.ubo, 0, res.uboScratch, 0, BILLBOARD_PICK_UBO_BYTES);
|
|
234
|
+
pass.setPipeline(getPickPipeline(engine, system));
|
|
235
|
+
pass.setBindGroup(1, res.bindGroup);
|
|
236
|
+
pass.setIndexBuffer(res.indexBuffer, "uint16");
|
|
237
|
+
pass.setVertexBuffer(0, res.instanceBuffer);
|
|
238
|
+
pass.drawIndexed(6, count);
|
|
239
|
+
}
|
|
240
|
+
function drawBillboardSystemForPicking(ctx, system, res, baseId) {
|
|
241
|
+
ctx.pass.setBindGroup(0, ctx.sceneBG);
|
|
242
|
+
drawBillboardForPicking(ctx.pass, ctx.engine, system, res, baseId, getViewMatrix(ctx.camera));
|
|
243
|
+
}
|
|
244
|
+
function createPickContributor(system) {
|
|
245
|
+
let res = null;
|
|
246
|
+
return {
|
|
247
|
+
draw(ctx, baseId) {
|
|
248
|
+
const count = system.count;
|
|
249
|
+
if (!system.visible || count === 0) {
|
|
250
|
+
return baseId + count;
|
|
251
|
+
}
|
|
252
|
+
res ??= createBillboardPickResources(ctx.engine, system);
|
|
253
|
+
drawBillboardSystemForPicking(ctx, system, res, baseId);
|
|
254
|
+
return baseId + count;
|
|
255
|
+
},
|
|
256
|
+
resolve(info, localId) {
|
|
257
|
+
info._spritePick = { system, spriteIndex: localId, pickedPoint: info.pickedPoint, distance: info.distance };
|
|
258
|
+
},
|
|
259
|
+
dispose() {
|
|
260
|
+
if (res) {
|
|
261
|
+
disposeBillboardPickResources(res);
|
|
262
|
+
res = null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export { createBillboardPickResources, createPickContributor, disposeBillboardPickResources, drawBillboardForPicking, drawBillboardSystemForPicking, packBillboardPickUbo };
|
|
269
|
+
//# sourceMappingURL=billboard-pick-pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billboard-pick-pipeline.js","sources":["../../../src/picking/billboard-pick-pipeline.ts"],"sourcesContent":["/** Billboard GPU-picking pipeline — dynamic-imported by `gpu-picker.ts` when a\n * scene contains a `BillboardSpriteSystem`.\n *\n * Billboards are world-space, camera-oriented quads drawn inside the scene's 3D\n * pass, so they must be picked in the SAME depth-sorted 1×1 pass the mesh picker\n * uses (a billboard occluded by a wall must not win the pick). This mirrors the\n * Gaussian-splatting picking integration (`gs-picking-pipeline.ts`): the picker\n * assigns each system a contiguous pick-id range after the meshes + GS meshes,\n * draws every system into the shared pass with the pick-zoomed view-projection,\n * and resolves the read-back id back to `{ system, spriteIndex }`.\n *\n * The vertex stage reproduces the render shader's quad math (`billboard-pipeline.ts`)\n * exactly — same corner derivation, pivot, rotation, and camera-basis orientation —\n * so the picked pixel matches what the renderer drew. The camera basis cannot be\n * read from the pick scene UBO (it carries only the pick-zoomed view-projection),\n * so the camera right/up world axes are extracted on the CPU and passed in the\n * per-system pick UBO. The fragment encodes the pick id as RGB and the NDC depth\n * as `r32float`, matching the mesh picker's two-attachment contract.\n */\nimport { F32, U32 } from \"../engine/typed-arrays.js\";\nimport { BU, SS, CW } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Mat4 } from \"../math/types.js\";\nimport type { BillboardOrientation, BillboardSpriteSystem } from \"../sprite/billboard-sprite.js\";\nimport { BILLBOARD_INSTANCE_STRIDE_BYTES } from \"../sprite/billboard-sprite.js\";\nimport { getPickingSceneBGL } from \"./picking-pipeline.js\";\nimport type { PickContributor, PickPassContext } from \"./pick-contributor.js\";\nimport { getViewMatrix } from \"../camera/camera.js\";\n\n/** Result of a successful {@link pickBillboardSprite} hit (re-exported from the public picker). */\nexport interface BillboardPickInfo {\n /** The billboard system that owns the hit sprite. */\n system: BillboardSpriteSystem;\n /** Index-API slot of the hit sprite within `system` (the value returned by `addBillboardSpriteIndex`). */\n spriteIndex: number;\n /** World-space hit point reconstructed from the pick depth, or `null` if it could not be reconstructed. */\n pickedPoint: [number, number, number] | null;\n /** Distance from the camera to `pickedPoint`, or `0` when unavailable. */\n distance: number;\n}\n\n/** Per-pick UBO: 48 bytes. Layout matches the WGSL `BB` struct below. */\nconst BILLBOARD_PICK_UBO_BYTES = 48;\n\n// Instance vertex attribute byte offsets — must match the render layout in `billboard-pipeline.ts`.\nconst POSITION_OFFSET_BYTES = 0;\nconst SIZE_OFFSET_BYTES = 12;\nconst UV_MIN_OFFSET_BYTES = 20;\nconst UV_MAX_OFFSET_BYTES = 28;\nconst ROTATION_OFFSET_BYTES = 36;\nconst PIVOT_OFFSET_BYTES = 40;\n\nconst SHARED_QUAD_INDICES = new Uint16Array([0, 1, 2, 0, 2, 3]);\n\n/**\n * Pack the per-pick billboard UBO into `f32`/`u32` (aliased views of one 48-byte buffer).\n * Layout (vec3 + scalar interleave): `[0..2]` camRight, `[3]` baseId (u32), `[4..6]` camUp,\n * `[7]` cutoff, `[8..10]` axis, `[11]` pad. camRight/camUp are rows 0/1 of the column-major\n * view matrix — reproducing the render shader's `normalize(view rowN)` camera basis.\n */\nexport function packBillboardPickUbo(view: Mat4, baseId: number, cutoff: number, axis: readonly [number, number, number], f32: Float32Array, u32: Uint32Array): void {\n f32[0] = view[0]!;\n f32[1] = view[4]!;\n f32[2] = view[8]!;\n u32[3] = baseId;\n f32[4] = view[1]!;\n f32[5] = view[5]!;\n f32[6] = view[9]!;\n f32[7] = cutoff;\n f32[8] = axis[0];\n f32[9] = axis[1];\n f32[10] = axis[2];\n}\n\nfunction makePickBasisWgsl(orientation: BillboardOrientation): string {\n switch (orientation) {\n case \"facing\":\n return `fn basis() -> B {\nlet r = normalize(bb.camRight);\nlet u = normalize(bb.camUp);\nreturn B(r, -u);\n}`;\n case \"axis-locked\":\n return `fn basis() -> B {\nlet a = normalize(bb.axis);\nlet cr = normalize(bb.camRight);\nlet pr = cr - a * dot(cr, a);\nlet pl = length(pr);\nlet f = select(vec3f(0, 0, 1), vec3f(1, 0, 0), abs(a.z) > 0.999);\nlet fr = cross(a, f);\nlet r = select(fr / max(length(fr), 1e-4), pr / max(pl, 1e-4), pl > 1e-4);\nreturn B(r, -a);\n}`;\n }\n}\n\nfunction makeBillboardPickWgsl(orientation: BillboardOrientation, isCutout: boolean): string {\n const cutoutBindings = isCutout\n ? `@group(1) @binding(1) var atlasTex: texture_2d<f32>;\n@group(1) @binding(2) var atlasSamp: sampler;`\n : \"\";\n const uvVarying = isCutout ? `,\\n@location(1) uv: vec2f` : \"\";\n const uvAssign = isCutout ? `out.uv = mix(in.a, in.b, q);` : \"\";\n const cutoutDiscard = isCutout\n ? `let s = textureSample(atlasTex, atlasSamp, in.uv);\nif (s.a < bb.cutoff) {\ndiscard;\n}`\n : \"\";\n return `struct PickScene { viewProjection: mat4x4f };\n@group(0) @binding(0) var<uniform> scene: PickScene;\nstruct BB {\ncamRight: vec3f,\nbaseId: u32,\ncamUp: vec3f,\ncutoff: f32,\naxis: vec3f,\n_pad: f32,\n};\n@group(1) @binding(0) var<uniform> bb: BB;\n${cutoutBindings}\nstruct B { r: vec3f, u: vec3f };\n${makePickBasisWgsl(orientation)}\nstruct I {\n@builtin(vertex_index) vid: u32,\n@builtin(instance_index) iid: u32,\n@location(0) p: vec3f,\n@location(1) s: vec2f,\n@location(2) a: vec2f,\n@location(3) b: vec2f,\n@location(4) r: f32,\n@location(5) o: vec2f,\n};\nstruct O {\n@builtin(position) p: vec4f,\n@location(0) @interpolate(flat) pickId: u32${uvVarying}\n};\n@vertex\nfn vs(in: I) -> O {\nlet q = vec2f(select(0.0, 1.0, in.vid == 1u || in.vid == 2u), select(0.0, 1.0, in.vid >= 2u));\nlet l = (q - in.o) * in.s;\nlet cr = cos(in.r);\nlet sr = sin(in.r);\nlet rot = vec2f(l.x * cr - l.y * sr, l.x * sr + l.y * cr);\nlet bs = basis();\nlet wp = in.p + bs.r * rot.x + bs.u * rot.y;\nvar out: O;\nout.p = scene.viewProjection * vec4f(wp, 1);\nout.pickId = bb.baseId + in.iid;\n${uvAssign}\nreturn out;\n}\nstruct FsOut { @location(0) color: vec4f, @location(1) depth: vec4f };\n@fragment\nfn fs(in: O) -> FsOut {\n${cutoutDiscard}\nlet id = in.pickId;\nlet r = f32((id >> 16u) & 0xFFu) / 255.0;\nlet g = f32((id >> 8u) & 0xFFu) / 255.0;\nlet b = f32(id & 0xFFu) / 255.0;\nreturn FsOut(vec4f(r, g, b, 1.0), vec4f(in.p.z, 0.0, 0.0, 0.0));\n}`;\n}\n\ninterface BillboardPickCache {\n device: GPUDevice;\n /** Pipelines keyed by `${orientation}|${isCutout ? 1 : 0}`. */\n pipelines: Map<string, GPURenderPipeline>;\n /** Group-1 bind-group layouts keyed by `isCutout`. */\n bgls: Map<string, GPUBindGroupLayout>;\n}\n\nlet _cache: BillboardPickCache | null = null;\n\nfunction getCache(engine: EngineContext): BillboardPickCache {\n if (_cache && _cache.device === engine._device) {\n return _cache;\n }\n _cache = { device: engine._device, pipelines: new Map(), bgls: new Map() };\n return _cache;\n}\n\nfunction getGroup1Bgl(engine: EngineContext, cache: BillboardPickCache, isCutout: boolean): GPUBindGroupLayout {\n const key = isCutout ? \"cutout\" : \"plain\";\n let bgl = cache.bgls.get(key);\n if (!bgl) {\n const entries: GPUBindGroupLayoutEntry[] = [{ binding: 0, visibility: SS.VERTEX | SS.FRAGMENT, buffer: { type: \"uniform\" } }];\n if (isCutout) {\n entries.push({ binding: 1, visibility: SS.FRAGMENT, texture: { sampleType: \"float\" } });\n entries.push({ binding: 2, visibility: SS.FRAGMENT, sampler: { type: \"filtering\" } });\n }\n bgl = engine._device.createBindGroupLayout({ label: `billboard-pick-bgl-${key}`, entries });\n cache.bgls.set(key, bgl);\n }\n return bgl;\n}\n\nfunction getPickPipeline(engine: EngineContext, system: BillboardSpriteSystem): GPURenderPipeline {\n const cache = getCache(engine);\n const orientation = system._orientation;\n const isCutout = system._depthMode === \"cutout\";\n const key = `${orientation}|${isCutout ? 1 : 0}`;\n const cached = cache.pipelines.get(key);\n if (cached) {\n return cached;\n }\n const device = engine._device;\n const module = device.createShaderModule({ label: `billboard-pick-${key}`, code: makeBillboardPickWgsl(orientation, isCutout) });\n const group1Bgl = getGroup1Bgl(engine, cache, isCutout);\n const pipeline = device.createRenderPipeline({\n label: `billboard-pick-pipeline-${key}`,\n layout: device.createPipelineLayout({ bindGroupLayouts: [getPickingSceneBGL(engine), group1Bgl] }),\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [\n {\n arrayStride: BILLBOARD_INSTANCE_STRIDE_BYTES,\n stepMode: \"instance\",\n attributes: [\n { shaderLocation: 0, offset: POSITION_OFFSET_BYTES, format: \"float32x3\" },\n { shaderLocation: 1, offset: SIZE_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 2, offset: UV_MIN_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 3, offset: UV_MAX_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 4, offset: ROTATION_OFFSET_BYTES, format: \"float32\" },\n { shaderLocation: 5, offset: PIVOT_OFFSET_BYTES, format: \"float32x2\" },\n ],\n },\n ],\n },\n fragment: { module, entryPoint: \"fs\", targets: [{ format: \"rgba8unorm\", writeMask: CW.ALL }, { format: \"r32float\" }] },\n // Match the mesh picker: reverse-Z depth (clear 0, \"greater\") so a billboard occluded by a\n // mesh — or by a nearer billboard — loses the pick. Write depth so billboards occlude too.\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\n depthStencil: { format: \"depth24plus\", depthCompare: \"greater\", depthWriteEnabled: true },\n multisample: { count: 1 },\n });\n cache.pipelines.set(key, pipeline);\n return pipeline;\n}\n\n/** Per-system GPU resources allocated on first pick of that system. */\nexport interface BillboardPickResources {\n ubo: GPUBuffer;\n uboScratch: ArrayBuffer;\n uboF32: Float32Array;\n uboU32: Uint32Array;\n instanceBuffer: GPUBuffer;\n instanceCapacity: number;\n indexBuffer: GPUBuffer;\n bindGroup: GPUBindGroup;\n}\n\nexport function createBillboardPickResources(engine: EngineContext, system: BillboardSpriteSystem): BillboardPickResources {\n const device = engine._device;\n const cache = getCache(engine);\n const isCutout = system._depthMode === \"cutout\";\n\n const ubo = device.createBuffer({ label: \"billboard-pick-ubo\", size: BILLBOARD_PICK_UBO_BYTES, usage: BU.UNIFORM | BU.COPY_DST });\n const uboScratch = new ArrayBuffer(BILLBOARD_PICK_UBO_BYTES);\n\n const instanceCapacity = Math.max(1, system._capacity);\n const instanceBuffer = device.createBuffer({\n label: \"billboard-pick-instances\",\n size: instanceCapacity * BILLBOARD_INSTANCE_STRIDE_BYTES,\n usage: BU.VERTEX | BU.COPY_DST,\n });\n\n const indexBuffer = device.createBuffer({ label: \"billboard-pick-indices\", size: SHARED_QUAD_INDICES.byteLength, usage: BU.INDEX | BU.COPY_DST });\n device.queue.writeBuffer(indexBuffer, 0, SHARED_QUAD_INDICES);\n\n const entries: GPUBindGroupEntry[] = [{ binding: 0, resource: { buffer: ubo } }];\n if (isCutout) {\n entries.push({ binding: 1, resource: system.atlas.texture.view });\n entries.push({ binding: 2, resource: system.atlas.texture.sampler });\n }\n const bindGroup = device.createBindGroup({ label: \"billboard-pick-bg\", layout: getGroup1Bgl(engine, cache, isCutout), entries });\n\n return {\n ubo,\n uboScratch,\n uboF32: new F32(uboScratch),\n uboU32: new U32(uboScratch),\n instanceBuffer,\n instanceCapacity,\n indexBuffer,\n bindGroup,\n };\n}\n\nexport function disposeBillboardPickResources(res: BillboardPickResources): void {\n res.ubo.destroy();\n res.instanceBuffer.destroy();\n res.indexBuffer.destroy();\n}\n\n/**\n * Draw one billboard system into the shared pick pass with consecutive pick ids `[baseId, baseId + count)`.\n * The caller must have already bound the pick scene UBO (pick-zoomed view-projection) at group 0.\n * `view` is the camera's column-major view matrix (for the billboard basis).\n */\nexport function drawBillboardForPicking(\n pass: GPURenderPassEncoder,\n engine: EngineContext,\n system: BillboardSpriteSystem,\n res: BillboardPickResources,\n baseId: number,\n view: Mat4\n): void {\n const device = engine._device;\n const count = system.count;\n if (count === 0) {\n return;\n }\n\n // Grow the pick instance buffer if the system outgrew it since the last pick.\n if (system._capacity > res.instanceCapacity) {\n res.instanceBuffer.destroy();\n res.instanceCapacity = system._capacity;\n res.instanceBuffer = device.createBuffer({\n label: \"billboard-pick-instances\",\n size: res.instanceCapacity * BILLBOARD_INSTANCE_STRIDE_BYTES,\n usage: BU.VERTEX | BU.COPY_DST,\n });\n }\n // Upload the system's CPU instance data in logical order, so pick id − baseId == sprite index.\n const data = system._instanceData;\n device.queue.writeBuffer(res.instanceBuffer, 0, data.buffer, data.byteOffset, count * BILLBOARD_INSTANCE_STRIDE_BYTES);\n\n // Per-pick UBO: camera basis + axis + baseId + alpha cutoff (cutout only).\n const cutoff = system._depthMode === \"cutout\" ? system.alphaCutoff : 0;\n packBillboardPickUbo(view, baseId, cutoff, system._axis, res.uboF32, res.uboU32);\n device.queue.writeBuffer(res.ubo, 0, res.uboScratch, 0, BILLBOARD_PICK_UBO_BYTES);\n\n pass.setPipeline(getPickPipeline(engine, system));\n pass.setBindGroup(1, res.bindGroup);\n pass.setIndexBuffer(res.indexBuffer, \"uint16\");\n pass.setVertexBuffer(0, res.instanceBuffer);\n pass.drawIndexed(6, count);\n}\n\n// ─── Contributor orchestration ──────────────────────────────────────────────\n// The per-system pick draw runs on behalf of the GPU picker's generic contributor loop. It lives\n// here (the dynamic-imported pick module) rather than in `gpu-picker.ts` so a picker scene with no\n// billboards never fetches it; the picker iterates `scene._pickSources` and this module's\n// `createPickContributor` (reached via the pick source registered in `billboard-scene.ts`) is\n// lazy-imported on the first pick.\n\n/**\n * Draw one billboard system into the shared pick pass, assigning pick ids `[baseId, baseId + system.count)`.\n * Rebinds group 0 to the mesh pick view-projection (a prior contributor, e.g. GS, may have rebound\n * it) and derives the camera basis from the view matrix. Only called when the system is visible and\n * non-empty; the contributor consumes the id range for hidden/empty systems without drawing.\n */\nexport function drawBillboardSystemForPicking(ctx: PickPassContext, system: BillboardSpriteSystem, res: BillboardPickResources, baseId: number): void {\n ctx.pass.setBindGroup(0, ctx.sceneBG);\n drawBillboardForPicking(ctx.pass, ctx.engine, system, res, baseId, getViewMatrix(ctx.camera));\n}\n\n/** Build the pick contributor for one billboard system. The picker calls this once (via the pick\n * source registered in `billboard-scene.ts`) and reuses the result, so the GPU pick resources live\n * in this closure and free in `dispose`. A hidden/empty system still consumes its id range so\n * id↔sprite mapping stays positional. */\nexport function createPickContributor(system: BillboardSpriteSystem): PickContributor {\n let res: BillboardPickResources | null = null;\n return {\n draw(ctx, baseId) {\n const count = system.count;\n if (!system.visible || count === 0) {\n return baseId + count; // consume the id range, but nothing to draw\n }\n res ??= createBillboardPickResources(ctx.engine, system);\n drawBillboardSystemForPicking(ctx, system, res, baseId);\n return baseId + count;\n },\n resolve(info, localId) {\n info._spritePick = { system, spriteIndex: localId, pickedPoint: info.pickedPoint, distance: info.distance };\n },\n dispose() {\n if (res) {\n disposeBillboardPickResources(res);\n res = null;\n }\n },\n };\n}\n"],"names":[],"mappings":";;;;;;AA0CA,MAAM,wBAAA,GAA2B,EAAA;AAGjC,MAAM,qBAAA,GAAwB,CAAA;AAC9B,MAAM,iBAAA,GAAoB,EAAA;AAC1B,MAAM,mBAAA,GAAsB,EAAA;AAC5B,MAAM,mBAAA,GAAsB,EAAA;AAC5B,MAAM,qBAAA,GAAwB,EAAA;AAC9B,MAAM,kBAAA,GAAqB,EAAA;AAE3B,MAAM,mBAAA,GAAsB,IAAI,WAAA,CAAY,CAAC,CAAA,EAAG,GAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAC,CAAA;AAQvD,SAAS,qBAAqB,IAAA,EAAY,MAAA,EAAgB,MAAA,EAAgB,IAAA,EAAyC,KAAmB,GAAA,EAAwB;AACjK,EAAA,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA;AACf,EAAA,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA;AACf,EAAA,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA;AACf,EAAA,GAAA,CAAI,CAAC,CAAA,GAAI,MAAA;AACT,EAAA,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA;AACf,EAAA,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA;AACf,EAAA,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA;AACf,EAAA,GAAA,CAAI,CAAC,CAAA,GAAI,MAAA;AACT,EAAA,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA;AACf,EAAA,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA;AACf,EAAA,GAAA,CAAI,EAAE,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA;AACpB;AAEA,SAAS,kBAAkB,WAAA,EAA2C;AAClE,EAAA,QAAQ,WAAA;AAAa,IACjB,KAAK,QAAA;AACD,MAAA,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA;AAAA,IAKX,KAAK,aAAA;AACD,MAAA,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA;AAAA;AAWnB;AAEA,SAAS,qBAAA,CAAsB,aAAmC,QAAA,EAA2B;AACzF,EAAA,MAAM,iBAAiB,QAAA,GACjB,CAAA;AAAA,6CAAA,CAAA,GAEA,EAAA;AACN,EAAA,MAAM,YAAY,QAAA,GAAW,CAAA;AAAA,sBAAA,CAAA,GAA8B,EAAA;AAC3D,EAAA,MAAM,QAAA,GAAW,WAAW,CAAA,4BAAA,CAAA,GAAiC,EAAA;AAC7D,EAAA,MAAM,gBAAgB,QAAA,GAChB,CAAA;AAAA;AAAA;AAAA,CAAA,CAAA,GAIA,EAAA;AACN,EAAA,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWT,cAAc;AAAA;AAAA,EAEd,iBAAA,CAAkB,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2CAAA,EAaa,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcpD,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMR,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA;AAOf;AAUA,IAAI,MAAA,GAAoC,IAAA;AAExC,SAAS,SAAS,MAAA,EAA2C;AACzD,EAAA,IAAI,MAAA,IAAU,MAAA,CAAO,MAAA,KAAW,MAAA,CAAO,OAAA,EAAS;AAC5C,IAAA,OAAO,MAAA;AAAA,EACX;AACA,EAAA,MAAA,GAAS,EAAE,MAAA,EAAQ,MAAA,CAAO,OAAA,EAAS,SAAA,kBAAW,IAAI,GAAA,EAAI,EAAG,IAAA,kBAAM,IAAI,GAAA,EAAI,EAAE;AACzE,EAAA,OAAO,MAAA;AACX;AAEA,SAAS,YAAA,CAAa,MAAA,EAAuB,KAAA,EAA2B,QAAA,EAAuC;AAC3G,EAAA,MAAM,GAAA,GAAM,WAAW,QAAA,GAAW,OAAA;AAClC,EAAA,IAAI,GAAA,GAAM,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA;AAC5B,EAAA,IAAI,CAAC,GAAA,EAAK;AACN,IAAA,MAAM,OAAA,GAAqC,CAAC,EAAE,OAAA,EAAS,GAAG,UAAA,EAAY,EAAA,CAAG,MAAA,GAAS,EAAA,CAAG,UAAU,MAAA,EAAQ,EAAE,IAAA,EAAM,SAAA,IAAa,CAAA;AAC5H,IAAA,IAAI,QAAA,EAAU;AACV,MAAA,OAAA,CAAQ,IAAA,CAAK,EAAE,OAAA,EAAS,CAAA,EAAG,UAAA,EAAY,EAAA,CAAG,QAAA,EAAU,OAAA,EAAS,EAAE,UAAA,EAAY,OAAA,EAAQ,EAAG,CAAA;AACtF,MAAA,OAAA,CAAQ,IAAA,CAAK,EAAE,OAAA,EAAS,CAAA,EAAG,UAAA,EAAY,EAAA,CAAG,QAAA,EAAU,OAAA,EAAS,EAAE,IAAA,EAAM,WAAA,EAAY,EAAG,CAAA;AAAA,IACxF;AACA,IAAA,GAAA,GAAM,MAAA,CAAO,QAAQ,qBAAA,CAAsB,EAAE,OAAO,CAAA,mBAAA,EAAsB,GAAG,CAAA,CAAA,EAAI,OAAA,EAAS,CAAA;AAC1F,IAAA,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,GAAG,CAAA;AAAA,EAC3B;AACA,EAAA,OAAO,GAAA;AACX;AAEA,SAAS,eAAA,CAAgB,QAAuB,MAAA,EAAkD;AAC9F,EAAA,MAAM,KAAA,GAAQ,SAAS,MAAM,CAAA;AAC7B,EAAA,MAAM,cAAc,MAAA,CAAO,YAAA;AAC3B,EAAA,MAAM,QAAA,GAAW,OAAO,UAAA,KAAe,QAAA;AACvC,EAAA,MAAM,MAAM,CAAA,EAAG,WAAW,CAAA,CAAA,EAAI,QAAA,GAAW,IAAI,CAAC,CAAA,CAAA;AAC9C,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,SAAA,CAAU,GAAA,CAAI,GAAG,CAAA;AACtC,EAAA,IAAI,MAAA,EAAQ;AACR,IAAA,OAAO,MAAA;AAAA,EACX;AACA,EAAA,MAAM,SAAS,MAAA,CAAO,OAAA;AACtB,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,kBAAA,CAAmB,EAAE,KAAA,EAAO,CAAA,eAAA,EAAkB,GAAG,CAAA,CAAA,EAAI,IAAA,EAAM,qBAAA,CAAsB,WAAA,EAAa,QAAQ,GAAG,CAAA;AAC/H,EAAA,MAAM,SAAA,GAAY,YAAA,CAAa,MAAA,EAAQ,KAAA,EAAO,QAAQ,CAAA;AACtD,EAAA,MAAM,QAAA,GAAW,OAAO,oBAAA,CAAqB;AAAA,IACzC,KAAA,EAAO,2BAA2B,GAAG,CAAA,CAAA;AAAA,IACrC,MAAA,EAAQ,MAAA,CAAO,oBAAA,CAAqB,EAAE,gBAAA,EAAkB,CAAC,kBAAA,CAAmB,MAAM,CAAA,EAAG,SAAS,CAAA,EAAG,CAAA;AAAA,IACjG,MAAA,EAAQ;AAAA,MACJ,MAAA;AAAA,MACA,UAAA,EAAY,IAAA;AAAA,MACZ,OAAA,EAAS;AAAA,QACL;AAAA,UACI,WAAA,EAAa,+BAAA;AAAA,UACb,QAAA,EAAU,UAAA;AAAA,UACV,UAAA,EAAY;AAAA,YACR,EAAE,cAAA,EAAgB,CAAA,EAAG,MAAA,EAAQ,qBAAA,EAAuB,QAAQ,WAAA,EAAY;AAAA,YACxE,EAAE,cAAA,EAAgB,CAAA,EAAG,MAAA,EAAQ,iBAAA,EAAmB,QAAQ,WAAA,EAAY;AAAA,YACpE,EAAE,cAAA,EAAgB,CAAA,EAAG,MAAA,EAAQ,mBAAA,EAAqB,QAAQ,WAAA,EAAY;AAAA,YACtE,EAAE,cAAA,EAAgB,CAAA,EAAG,MAAA,EAAQ,mBAAA,EAAqB,QAAQ,WAAA,EAAY;AAAA,YACtE,EAAE,cAAA,EAAgB,CAAA,EAAG,MAAA,EAAQ,qBAAA,EAAuB,QAAQ,SAAA,EAAU;AAAA,YACtE,EAAE,cAAA,EAAgB,CAAA,EAAG,MAAA,EAAQ,kBAAA,EAAoB,QAAQ,WAAA;AAAY;AACzE;AACJ;AACJ,KACJ;AAAA,IACA,UAAU,EAAE,MAAA,EAAQ,YAAY,IAAA,EAAM,OAAA,EAAS,CAAC,EAAE,MAAA,EAAQ,YAAA,EAAc,SAAA,EAAW,GAAG,GAAA,EAAI,EAAG,EAAE,MAAA,EAAQ,UAAA,EAAY,CAAA,EAAE;AAAA;AAAA;AAAA,IAGrH,SAAA,EAAW,EAAE,QAAA,EAAU,eAAA,EAAiB,UAAU,MAAA,EAAO;AAAA,IACzD,cAAc,EAAE,MAAA,EAAQ,eAAe,YAAA,EAAc,SAAA,EAAW,mBAAmB,IAAA,EAAK;AAAA,IACxF,WAAA,EAAa,EAAE,KAAA,EAAO,CAAA;AAAE,GAC3B,CAAA;AACD,EAAA,KAAA,CAAM,SAAA,CAAU,GAAA,CAAI,GAAA,EAAK,QAAQ,CAAA;AACjC,EAAA,OAAO,QAAA;AACX;AAcO,SAAS,4BAAA,CAA6B,QAAuB,MAAA,EAAuD;AACvH,EAAA,MAAM,SAAS,MAAA,CAAO,OAAA;AACtB,EAAA,MAAM,KAAA,GAAQ,SAAS,MAAM,CAAA;AAC7B,EAAA,MAAM,QAAA,GAAW,OAAO,UAAA,KAAe,QAAA;AAEvC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,YAAA,CAAa,EAAE,KAAA,EAAO,oBAAA,EAAsB,IAAA,EAAM,wBAAA,EAA0B,KAAA,EAAO,EAAA,CAAG,OAAA,GAAU,EAAA,CAAG,UAAU,CAAA;AAChI,EAAA,MAAM,UAAA,GAAa,IAAI,WAAA,CAAY,wBAAwB,CAAA;AAE3D,EAAA,MAAM,gBAAA,GAAmB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAO,SAAS,CAAA;AACrD,EAAA,MAAM,cAAA,GAAiB,OAAO,YAAA,CAAa;AAAA,IACvC,KAAA,EAAO,0BAAA;AAAA,IACP,MAAM,gBAAA,GAAmB,+BAAA;AAAA,IACzB,KAAA,EAAO,EAAA,CAAG,MAAA,GAAS,EAAA,CAAG;AAAA,GACzB,CAAA;AAED,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,YAAA,CAAa,EAAE,OAAO,wBAAA,EAA0B,IAAA,EAAM,mBAAA,CAAoB,UAAA,EAAY,KAAA,EAAO,EAAA,CAAG,KAAA,GAAQ,EAAA,CAAG,UAAU,CAAA;AAChJ,EAAA,MAAA,CAAO,KAAA,CAAM,WAAA,CAAY,WAAA,EAAa,CAAA,EAAG,mBAAmB,CAAA;AAE5D,EAAA,MAAM,OAAA,GAA+B,CAAC,EAAE,OAAA,EAAS,CAAA,EAAG,UAAU,EAAE,MAAA,EAAQ,GAAA,EAAI,EAAG,CAAA;AAC/E,EAAA,IAAI,QAAA,EAAU;AACV,IAAA,OAAA,CAAQ,IAAA,CAAK,EAAE,OAAA,EAAS,CAAA,EAAG,UAAU,MAAA,CAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,EAAM,CAAA;AAChE,IAAA,OAAA,CAAQ,IAAA,CAAK,EAAE,OAAA,EAAS,CAAA,EAAG,UAAU,MAAA,CAAO,KAAA,CAAM,OAAA,CAAQ,OAAA,EAAS,CAAA;AAAA,EACvE;AACA,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,eAAA,CAAgB,EAAE,KAAA,EAAO,mBAAA,EAAqB,MAAA,EAAQ,YAAA,CAAa,MAAA,EAAQ,KAAA,EAAO,QAAQ,CAAA,EAAG,SAAS,CAAA;AAE/H,EAAA,OAAO;AAAA,IACH,GAAA;AAAA,IACA,UAAA;AAAA,IACA,MAAA,EAAQ,IAAI,GAAA,CAAI,UAAU,CAAA;AAAA,IAC1B,MAAA,EAAQ,IAAI,GAAA,CAAI,UAAU,CAAA;AAAA,IAC1B,cAAA;AAAA,IACA,gBAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACJ;AACJ;AAEO,SAAS,8BAA8B,GAAA,EAAmC;AAC7E,EAAA,GAAA,CAAI,IAAI,OAAA,EAAQ;AAChB,EAAA,GAAA,CAAI,eAAe,OAAA,EAAQ;AAC3B,EAAA,GAAA,CAAI,YAAY,OAAA,EAAQ;AAC5B;AAOO,SAAS,wBACZ,IAAA,EACA,MAAA,EACA,MAAA,EACA,GAAA,EACA,QACA,IAAA,EACI;AACJ,EAAA,MAAM,SAAS,MAAA,CAAO,OAAA;AACtB,EAAA,MAAM,QAAQ,MAAA,CAAO,KAAA;AACrB,EAAA,IAAI,UAAU,CAAA,EAAG;AACb,IAAA;AAAA,EACJ;AAGA,EAAA,IAAI,MAAA,CAAO,SAAA,GAAY,GAAA,CAAI,gBAAA,EAAkB;AACzC,IAAA,GAAA,CAAI,eAAe,OAAA,EAAQ;AAC3B,IAAA,GAAA,CAAI,mBAAmB,MAAA,CAAO,SAAA;AAC9B,IAAA,GAAA,CAAI,cAAA,GAAiB,OAAO,YAAA,CAAa;AAAA,MACrC,KAAA,EAAO,0BAAA;AAAA,MACP,IAAA,EAAM,IAAI,gBAAA,GAAmB,+BAAA;AAAA,MAC7B,KAAA,EAAO,EAAA,CAAG,MAAA,GAAS,EAAA,CAAG;AAAA,KACzB,CAAA;AAAA,EACL;AAEA,EAAA,MAAM,OAAO,MAAA,CAAO,aAAA;AACpB,EAAA,MAAA,CAAO,KAAA,CAAM,WAAA,CAAY,GAAA,CAAI,cAAA,EAAgB,CAAA,EAAG,KAAK,MAAA,EAAQ,IAAA,CAAK,UAAA,EAAY,KAAA,GAAQ,+BAA+B,CAAA;AAGrH,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,KAAe,QAAA,GAAW,OAAO,WAAA,GAAc,CAAA;AACrE,EAAA,oBAAA,CAAqB,IAAA,EAAM,QAAQ,MAAA,EAAQ,MAAA,CAAO,OAAO,GAAA,CAAI,MAAA,EAAQ,IAAI,MAAM,CAAA;AAC/E,EAAA,MAAA,CAAO,KAAA,CAAM,YAAY,GAAA,CAAI,GAAA,EAAK,GAAG,GAAA,CAAI,UAAA,EAAY,GAAG,wBAAwB,CAAA;AAEhF,EAAA,IAAA,CAAK,WAAA,CAAY,eAAA,CAAgB,MAAA,EAAQ,MAAM,CAAC,CAAA;AAChD,EAAA,IAAA,CAAK,YAAA,CAAa,CAAA,EAAG,GAAA,CAAI,SAAS,CAAA;AAClC,EAAA,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,WAAA,EAAa,QAAQ,CAAA;AAC7C,EAAA,IAAA,CAAK,eAAA,CAAgB,CAAA,EAAG,GAAA,CAAI,cAAc,CAAA;AAC1C,EAAA,IAAA,CAAK,WAAA,CAAY,GAAG,KAAK,CAAA;AAC7B;AAeO,SAAS,6BAAA,CAA8B,GAAA,EAAsB,MAAA,EAA+B,GAAA,EAA6B,MAAA,EAAsB;AAClJ,EAAA,GAAA,CAAI,IAAA,CAAK,YAAA,CAAa,CAAA,EAAG,GAAA,CAAI,OAAO,CAAA;AACpC,EAAA,uBAAA,CAAwB,GAAA,CAAI,IAAA,EAAM,GAAA,CAAI,MAAA,EAAQ,MAAA,EAAQ,KAAK,MAAA,EAAQ,aAAA,CAAc,GAAA,CAAI,MAAM,CAAC,CAAA;AAChG;AAMO,SAAS,sBAAsB,MAAA,EAAgD;AAClF,EAAA,IAAI,GAAA,GAAqC,IAAA;AACzC,EAAA,OAAO;AAAA,IACH,IAAA,CAAK,KAAK,MAAA,EAAQ;AACd,MAAA,MAAM,QAAQ,MAAA,CAAO,KAAA;AACrB,MAAA,IAAI,CAAC,MAAA,CAAO,OAAA,IAAW,KAAA,KAAU,CAAA,EAAG;AAChC,QAAA,OAAO,MAAA,GAAS,KAAA;AAAA,MACpB;AACA,MAAA,GAAA,KAAQ,4BAAA,CAA6B,GAAA,CAAI,MAAA,EAAQ,MAAM,CAAA;AACvD,MAAA,6BAAA,CAA8B,GAAA,EAAK,MAAA,EAAQ,GAAA,EAAK,MAAM,CAAA;AACtD,MAAA,OAAO,MAAA,GAAS,KAAA;AAAA,IACpB,CAAA;AAAA,IACA,OAAA,CAAQ,MAAM,OAAA,EAAS;AACnB,MAAA,IAAA,CAAK,WAAA,GAAc,EAAE,MAAA,EAAQ,WAAA,EAAa,OAAA,EAAS,aAAa,IAAA,CAAK,WAAA,EAAa,QAAA,EAAU,IAAA,CAAK,QAAA,EAAS;AAAA,IAC9G,CAAA;AAAA,IACA,OAAA,GAAU;AACN,MAAA,IAAI,GAAA,EAAK;AACL,QAAA,6BAAA,CAA8B,GAAG,CAAA;AACjC,QAAA,GAAA,GAAM,IAAA;AAAA,MACV;AAAA,IACJ;AAAA,GACJ;AACJ;;;;"}
|
|
@@ -9,7 +9,6 @@ import { resolveCameraViewport } from '../camera/viewport.js';
|
|
|
9
9
|
import { createUniformBuffer, createMappedBuffer, createEmptyUniformBuffer } from '../resource/gpu-buffers.js';
|
|
10
10
|
|
|
11
11
|
const _pickVP = new F32(16);
|
|
12
|
-
const _gsPickMatrix = new F32(16);
|
|
13
12
|
const PICK_MESH_UBO_BYTES = 80;
|
|
14
13
|
const PICK_TI_UBO_BYTES = 16;
|
|
15
14
|
const _uboScratch = new ArrayBuffer(PICK_MESH_UBO_BYTES);
|
|
@@ -26,7 +25,7 @@ function createGpuPicker(scene) {
|
|
|
26
25
|
_rt: null,
|
|
27
26
|
_sceneUbo: null,
|
|
28
27
|
_sceneBG: null,
|
|
29
|
-
|
|
28
|
+
_contributors: null,
|
|
30
29
|
_pending: null
|
|
31
30
|
};
|
|
32
31
|
}
|
|
@@ -227,23 +226,22 @@ async function pickAsyncImpl(picker, x, y, options) {
|
|
|
227
226
|
nextId++;
|
|
228
227
|
}
|
|
229
228
|
}
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
229
|
+
const contribRanges = [];
|
|
230
|
+
if (scene._pickSources.length > 0) {
|
|
231
|
+
const pickCtx = { picker, pass, engine, scene, camera, sceneBG: picker._sceneBG, px, py, w, h };
|
|
232
|
+
for (let ci = 0; ci < scene._pickSources.length; ci++) {
|
|
233
|
+
const src = scene._pickSources[ci];
|
|
234
|
+
let contributor = picker._contributors?.get(src);
|
|
235
|
+
if (!contributor) {
|
|
236
|
+
const pipeline = await src.load();
|
|
237
|
+
contributor = pipeline.createPickContributor(src.entity);
|
|
238
|
+
(picker._contributors ??= /* @__PURE__ */ new Map()).set(src, contributor);
|
|
239
|
+
}
|
|
240
|
+
const base = nextId;
|
|
241
|
+
nextId = contributor.draw(pickCtx, base);
|
|
242
|
+
if (nextId > base) {
|
|
243
|
+
contribRanges.push({ base, count: nextId - base, contributor });
|
|
244
244
|
}
|
|
245
|
-
gsModule.drawGsForPicking(pass, engine, scene, gsMesh, res, nextId, w, h);
|
|
246
|
-
nextId++;
|
|
247
245
|
}
|
|
248
246
|
}
|
|
249
247
|
pass.end();
|
|
@@ -274,7 +272,6 @@ async function pickAsyncImpl(picker, x, y, options) {
|
|
|
274
272
|
}
|
|
275
273
|
let hitMesh = null;
|
|
276
274
|
let hitThinIdx = -1;
|
|
277
|
-
let hitIsGs = false;
|
|
278
275
|
let scanId = 1;
|
|
279
276
|
for (let mi = 0; mi < meshCount; mi++) {
|
|
280
277
|
const mesh = meshes[mi];
|
|
@@ -303,14 +300,19 @@ async function pickAsyncImpl(picker, x, y, options) {
|
|
|
303
300
|
scanId++;
|
|
304
301
|
}
|
|
305
302
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
303
|
+
let hitContributor = null;
|
|
304
|
+
let contribLocalId = -1;
|
|
305
|
+
if (!hitMesh) {
|
|
306
|
+
for (let ri = 0; ri < contribRanges.length; ri++) {
|
|
307
|
+
const r = contribRanges[ri];
|
|
308
|
+
if (pickId >= r.base && pickId < r.base + r.count) {
|
|
309
|
+
hitContributor = r.contributor;
|
|
310
|
+
contribLocalId = pickId - r.base;
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
311
313
|
}
|
|
312
314
|
}
|
|
313
|
-
if (!hitMesh) {
|
|
315
|
+
if (!hitMesh && !hitContributor) {
|
|
314
316
|
if (debugLabel) {
|
|
315
317
|
console.trace("pick-debug", {
|
|
316
318
|
label: debugLabel,
|
|
@@ -344,7 +346,10 @@ async function pickAsyncImpl(picker, x, y, options) {
|
|
|
344
346
|
const dz = info.pickedPoint[2] - camPos.z;
|
|
345
347
|
info.distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
346
348
|
}
|
|
347
|
-
if (
|
|
349
|
+
if (hitContributor) {
|
|
350
|
+
hitContributor.resolve(info, contribLocalId);
|
|
351
|
+
}
|
|
352
|
+
if (picker._detailedPick && hitMesh) {
|
|
348
353
|
const ray = createPickingRay(pickX - viewport.x, pickY - viewport.y, vp, w, h);
|
|
349
354
|
if (ray) {
|
|
350
355
|
info.ray = ray;
|
|
@@ -359,7 +364,7 @@ async function pickAsyncImpl(picker, x, y, options) {
|
|
|
359
364
|
pickId,
|
|
360
365
|
depth,
|
|
361
366
|
hit: true,
|
|
362
|
-
mesh: hitMesh.name ?? "(unnamed)",
|
|
367
|
+
mesh: hitMesh ? hitMesh.name ?? "(unnamed)" : "(contributor)",
|
|
363
368
|
thinInstanceIndex: hitThinIdx,
|
|
364
369
|
pickedPoint: info.pickedPoint,
|
|
365
370
|
distance: info.distance
|
|
@@ -394,16 +399,11 @@ function disposePicker(picker) {
|
|
|
394
399
|
picker._sceneUbo = null;
|
|
395
400
|
picker._sceneBG = null;
|
|
396
401
|
}
|
|
397
|
-
if (picker.
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
for (const res of picker._gsMeshResources.values()) {
|
|
403
|
-
m.disposeGsPickMeshResources(res);
|
|
404
|
-
}
|
|
405
|
-
picker._gsMeshResources = null;
|
|
406
|
-
});
|
|
402
|
+
if (picker._contributors) {
|
|
403
|
+
for (const contributor of picker._contributors.values()) {
|
|
404
|
+
contributor.dispose?.();
|
|
405
|
+
}
|
|
406
|
+
picker._contributors = null;
|
|
407
407
|
}
|
|
408
408
|
}
|
|
409
409
|
|