@ifc-lite/renderer 1.47.0 → 1.48.1
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/dist/clash-solid-pipeline.d.ts +34 -0
- package/dist/clash-solid-pipeline.d.ts.map +1 -0
- package/dist/clash-solid-pipeline.js +193 -0
- package/dist/clash-solid-pipeline.js.map +1 -0
- package/dist/deviation/deviation-computer.d.ts +87 -0
- package/dist/deviation/deviation-computer.d.ts.map +1 -0
- package/dist/deviation/deviation-computer.js +201 -0
- package/dist/deviation/deviation-computer.js.map +1 -0
- package/dist/index.d.ts +22 -46
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -141
- package/dist/index.js.map +1 -1
- package/dist/renderer-overlays.d.ts +4 -0
- package/dist/renderer-overlays.d.ts.map +1 -1
- package/dist/renderer-overlays.js +19 -0
- package/dist/renderer-overlays.js.map +1 -1
- package/dist/shaders/sky.wgsl.d.ts +1 -1
- package/dist/shaders/sky.wgsl.d.ts.map +1 -1
- package/dist/shaders/sky.wgsl.js +7 -1
- package/dist/shaders/sky.wgsl.js.map +1 -1
- package/dist/snap-corner.d.ts +44 -0
- package/dist/snap-corner.d.ts.map +1 -0
- package/dist/snap-corner.js +111 -0
- package/dist/snap-corner.js.map +1 -0
- package/dist/snap-detector.d.ts +8 -4
- package/dist/snap-detector.d.ts.map +1 -1
- package/dist/snap-detector.js +60 -58
- package/dist/snap-detector.js.map +1 -1
- package/dist/snap-edge-runs.d.ts +75 -0
- package/dist/snap-edge-runs.d.ts.map +1 -0
- package/dist/snap-edge-runs.js +233 -0
- package/dist/snap-edge-runs.js.map +1 -0
- package/dist/snap-geometry-cache.d.ts +17 -11
- package/dist/snap-geometry-cache.d.ts.map +1 -1
- package/dist/snap-geometry-cache.js +115 -141
- package/dist/snap-geometry-cache.js.map +1 -1
- package/dist/snap-weld.d.ts +66 -0
- package/dist/snap-weld.d.ts.map +1 -0
- package/dist/snap-weld.js +150 -0
- package/dist/snap-weld.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** One flat opaque colour for the whole solid — BIMcollab-style, not per-triangle. */
|
|
2
|
+
export interface ClashSolidInput {
|
|
3
|
+
/** World-space vertex positions, flat `[x, y, z, …]`. */
|
|
4
|
+
positions: Float32Array | Float64Array;
|
|
5
|
+
/** Triangle indices into `positions / 3`. */
|
|
6
|
+
indices: Uint32Array;
|
|
7
|
+
/** Straight-alpha RGBA in [0..1]. Alpha 1 renders fully opaque. */
|
|
8
|
+
color: [number, number, number, number];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Expand an indexed triangle mesh + single colour into the flat
|
|
12
|
+
* pos+color-per-vertex stream `SYMBOLIC_FILL_WGSL` expects, matching
|
|
13
|
+
* `triangulateFillTo`'s non-indexed triangle-list convention.
|
|
14
|
+
*/
|
|
15
|
+
export declare function expandTriangles(input: ClashSolidInput): Float32Array;
|
|
16
|
+
export declare class ClashSolidPipeline {
|
|
17
|
+
private readonly device;
|
|
18
|
+
private readonly format;
|
|
19
|
+
private readonly sampleCount;
|
|
20
|
+
private pipeline;
|
|
21
|
+
private bindGroupLayout;
|
|
22
|
+
private uniformBuffer;
|
|
23
|
+
private bindGroup;
|
|
24
|
+
private vertexBuffer;
|
|
25
|
+
private vertexCount;
|
|
26
|
+
constructor(device: GPUDevice, presentationFormat: GPUTextureFormat, sampleCount?: number);
|
|
27
|
+
private init;
|
|
28
|
+
/** Upload the solid mesh. Pass `null` to clear. */
|
|
29
|
+
upload(input: ClashSolidInput | null): void;
|
|
30
|
+
hasGeometry(): boolean;
|
|
31
|
+
render(pass: GPURenderPassEncoder, viewProj: Float32Array): void;
|
|
32
|
+
destroy(): void;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=clash-solid-pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clash-solid-pipeline.d.ts","sourceRoot":"","sources":["../src/clash-solid-pipeline.ts"],"names":[],"mappings":"AA+BA,sFAAsF;AACtF,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,SAAS,EAAE,YAAY,GAAG,YAAY,CAAC;IACvC,6CAA6C;IAC7C,OAAO,EAAE,WAAW,CAAC;IACrB,mEAAmE;IACnE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,YAAY,CAkBpE;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,WAAW,CAAK;gBAEZ,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,WAAW,GAAE,MAAU;IAM5F,OAAO,CAAC,IAAI;IAmFZ,mDAAmD;IACnD,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI;IAuB3C,WAAW,IAAI,OAAO;IAItB,MAAM,CAAC,IAAI,EAAE,oBAAoB,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI;IAUhE,OAAO,IAAI,IAAI;CAUhB"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* The focused clash's INTERSECTION SOLID — the true overlap volume of a
|
|
6
|
+
* clashing pair, rendered opaque so it reads as a distinct shape rather than
|
|
7
|
+
* an abstract wireframe box (the BIMcollab Zoom / Solibri presentation the
|
|
8
|
+
* viewer's own overlap-box overlay was compared against and found "not
|
|
9
|
+
* helpful, hard to see").
|
|
10
|
+
*
|
|
11
|
+
* Deliberately its own tiny pipeline rather than a reuse of
|
|
12
|
+
* `SymbolicFillPipeline`: the vertex layout and shader (`SYMBOLIC_FILL_WGSL`,
|
|
13
|
+
* pos.xyz + color.rgba, straight `viewProj * pos`) are identical and shared,
|
|
14
|
+
* but the depth/blend state cannot be — the fill pipeline is a coplanar DECAL
|
|
15
|
+
* (`depthWriteEnabled: false`, negative bias, alpha-blended) for flat
|
|
16
|
+
* annotation regions that sit ON a real surface. The intersection solid is a
|
|
17
|
+
* real enclosed 3D volume: it must depth-test AND depth-write like ordinary
|
|
18
|
+
* opaque geometry so its own front/back triangles resolve correctly and it is
|
|
19
|
+
* properly occluded by unrelated opaque geometry in front of it, while still
|
|
20
|
+
* drawing on top of (and being visible through) the two ghosted parent
|
|
21
|
+
* elements — ghosted geometry renders with `depthWriteEnabled: false`
|
|
22
|
+
* (`pipeline.ts`), so it never occludes anything drawn after it in the same
|
|
23
|
+
* pass, including this.
|
|
24
|
+
*/
|
|
25
|
+
import { SYMBOLIC_FILL_WGSL } from './shaders/symbolic-overlay.wgsl.js';
|
|
26
|
+
import { PIPELINE_CONSTANTS } from './constants.js';
|
|
27
|
+
const VERTEX_STRIDE_BYTES = (3 + 4) * 4; // pos.xyz + color.rgba, 4 bytes each
|
|
28
|
+
/**
|
|
29
|
+
* Expand an indexed triangle mesh + single colour into the flat
|
|
30
|
+
* pos+color-per-vertex stream `SYMBOLIC_FILL_WGSL` expects, matching
|
|
31
|
+
* `triangulateFillTo`'s non-indexed triangle-list convention.
|
|
32
|
+
*/
|
|
33
|
+
export function expandTriangles(input) {
|
|
34
|
+
const { positions, indices, color } = input;
|
|
35
|
+
const triCount = Math.floor(indices.length / 3);
|
|
36
|
+
const out = new Float32Array(triCount * 3 * 7);
|
|
37
|
+
let w = 0;
|
|
38
|
+
for (let t = 0; t < triCount; t += 1) {
|
|
39
|
+
for (let k = 0; k < 3; k += 1) {
|
|
40
|
+
const vi = indices[t * 3 + k] * 3;
|
|
41
|
+
out[w++] = positions[vi];
|
|
42
|
+
out[w++] = positions[vi + 1];
|
|
43
|
+
out[w++] = positions[vi + 2];
|
|
44
|
+
out[w++] = color[0];
|
|
45
|
+
out[w++] = color[1];
|
|
46
|
+
out[w++] = color[2];
|
|
47
|
+
out[w++] = color[3];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
export class ClashSolidPipeline {
|
|
53
|
+
device;
|
|
54
|
+
format;
|
|
55
|
+
sampleCount;
|
|
56
|
+
pipeline = null;
|
|
57
|
+
bindGroupLayout = null;
|
|
58
|
+
uniformBuffer = null;
|
|
59
|
+
bindGroup = null;
|
|
60
|
+
vertexBuffer = null;
|
|
61
|
+
vertexCount = 0;
|
|
62
|
+
constructor(device, presentationFormat, sampleCount = 1) {
|
|
63
|
+
this.device = device;
|
|
64
|
+
this.format = presentationFormat;
|
|
65
|
+
this.sampleCount = sampleCount;
|
|
66
|
+
}
|
|
67
|
+
init() {
|
|
68
|
+
if (this.pipeline)
|
|
69
|
+
return;
|
|
70
|
+
this.bindGroupLayout = this.device.createBindGroupLayout({
|
|
71
|
+
label: 'clash-solid-bgl',
|
|
72
|
+
entries: [
|
|
73
|
+
{ binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: 'uniform' } },
|
|
74
|
+
],
|
|
75
|
+
});
|
|
76
|
+
const module = this.device.createShaderModule({
|
|
77
|
+
label: 'clash-solid-shader',
|
|
78
|
+
code: SYMBOLIC_FILL_WGSL,
|
|
79
|
+
});
|
|
80
|
+
this.pipeline = this.device.createRenderPipeline({
|
|
81
|
+
label: 'clash-solid-pipeline',
|
|
82
|
+
layout: this.device.createPipelineLayout({ bindGroupLayouts: [this.bindGroupLayout] }),
|
|
83
|
+
vertex: {
|
|
84
|
+
module,
|
|
85
|
+
entryPoint: 'vs_main',
|
|
86
|
+
buffers: [
|
|
87
|
+
{
|
|
88
|
+
arrayStride: VERTEX_STRIDE_BYTES,
|
|
89
|
+
attributes: [
|
|
90
|
+
{ shaderLocation: 0, offset: 0, format: 'float32x3' },
|
|
91
|
+
{ shaderLocation: 1, offset: 3 * 4, format: 'float32x4' },
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
fragment: {
|
|
97
|
+
module,
|
|
98
|
+
entryPoint: 'fs_main',
|
|
99
|
+
// Same two-target layout as every other overlay pipeline sharing this
|
|
100
|
+
// pass: presentation colour + the picker objectId, write-masked off so
|
|
101
|
+
// the opaque pass's picker IDs underneath survive.
|
|
102
|
+
targets: [
|
|
103
|
+
{
|
|
104
|
+
format: this.format,
|
|
105
|
+
blend: {
|
|
106
|
+
color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
107
|
+
alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
108
|
+
},
|
|
109
|
+
writeMask: GPUColorWrite.ALL,
|
|
110
|
+
},
|
|
111
|
+
{ format: 'rgba8unorm', writeMask: 0 },
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
// `cullMode: 'none'`: the intersection kernel's output winding isn't a
|
|
115
|
+
// contract the renderer depends on (mirrors the main opaque pipeline's
|
|
116
|
+
// own "IFC winding order varies" choice), and a double-sided draw still
|
|
117
|
+
// looks correct for a convex-ish overlap volume viewed from outside.
|
|
118
|
+
primitive: { topology: 'triangle-list', cullMode: 'none' },
|
|
119
|
+
depthStencil: {
|
|
120
|
+
format: PIPELINE_CONSTANTS.DEPTH_FORMAT,
|
|
121
|
+
// Unlike the fill pipeline's decal, this is a real volume: it must
|
|
122
|
+
// write depth so its own triangles sort against each other and so it
|
|
123
|
+
// is correctly occluded by opaque geometry drawn earlier in the pass.
|
|
124
|
+
depthWriteEnabled: true,
|
|
125
|
+
depthCompare: 'greater', // Reverse-Z, matches the main opaque pipeline.
|
|
126
|
+
// Small decal bias so a face that lands exactly on a parent's own
|
|
127
|
+
// surface (a flush overlap boundary) doesn't z-fight with it.
|
|
128
|
+
depthBias: -2,
|
|
129
|
+
depthBiasSlopeScale: -0.25,
|
|
130
|
+
depthBiasClamp: 0,
|
|
131
|
+
},
|
|
132
|
+
multisample: { count: this.sampleCount },
|
|
133
|
+
});
|
|
134
|
+
this.uniformBuffer = this.device.createBuffer({
|
|
135
|
+
label: 'clash-solid-camera',
|
|
136
|
+
size: 64,
|
|
137
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
138
|
+
});
|
|
139
|
+
this.bindGroup = this.device.createBindGroup({
|
|
140
|
+
label: 'clash-solid-bg',
|
|
141
|
+
layout: this.bindGroupLayout,
|
|
142
|
+
entries: [{ binding: 0, resource: { buffer: this.uniformBuffer } }],
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/** Upload the solid mesh. Pass `null` to clear. */
|
|
146
|
+
upload(input) {
|
|
147
|
+
this.init();
|
|
148
|
+
if (this.vertexBuffer) {
|
|
149
|
+
this.vertexBuffer.destroy();
|
|
150
|
+
this.vertexBuffer = null;
|
|
151
|
+
}
|
|
152
|
+
this.vertexCount = 0;
|
|
153
|
+
if (!input || input.indices.length === 0)
|
|
154
|
+
return;
|
|
155
|
+
const data = expandTriangles(input);
|
|
156
|
+
if (data.length === 0)
|
|
157
|
+
return;
|
|
158
|
+
this.vertexBuffer = this.device.createBuffer({
|
|
159
|
+
label: 'clash-solid-vbuf',
|
|
160
|
+
size: data.byteLength,
|
|
161
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
162
|
+
});
|
|
163
|
+
this.device.queue.writeBuffer(this.vertexBuffer, 0, data);
|
|
164
|
+
this.vertexCount = data.length / (VERTEX_STRIDE_BYTES / 4);
|
|
165
|
+
}
|
|
166
|
+
hasGeometry() {
|
|
167
|
+
return this.vertexCount > 0;
|
|
168
|
+
}
|
|
169
|
+
render(pass, viewProj) {
|
|
170
|
+
if (!this.pipeline || !this.uniformBuffer || !this.bindGroup || !this.vertexBuffer)
|
|
171
|
+
return;
|
|
172
|
+
if (this.vertexCount === 0)
|
|
173
|
+
return;
|
|
174
|
+
this.device.queue.writeBuffer(this.uniformBuffer, 0, viewProj);
|
|
175
|
+
pass.setPipeline(this.pipeline);
|
|
176
|
+
pass.setBindGroup(0, this.bindGroup);
|
|
177
|
+
pass.setVertexBuffer(0, this.vertexBuffer);
|
|
178
|
+
pass.draw(this.vertexCount);
|
|
179
|
+
}
|
|
180
|
+
destroy() {
|
|
181
|
+
if (this.vertexBuffer)
|
|
182
|
+
this.vertexBuffer.destroy();
|
|
183
|
+
if (this.uniformBuffer)
|
|
184
|
+
this.uniformBuffer.destroy();
|
|
185
|
+
this.vertexBuffer = null;
|
|
186
|
+
this.uniformBuffer = null;
|
|
187
|
+
this.bindGroup = null;
|
|
188
|
+
this.bindGroupLayout = null;
|
|
189
|
+
this.pipeline = null;
|
|
190
|
+
this.vertexCount = 0;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=clash-solid-pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clash-solid-pipeline.js","sourceRoot":"","sources":["../src/clash-solid-pipeline.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,mBAAmB,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,qCAAqC;AAY9E;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAsB;IACpD,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,IAAI,YAAY,CAAC,QAAQ,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAClC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;YACzB,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAC7B,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAC7B,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACpB,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACpB,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACpB,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,OAAO,kBAAkB;IACZ,MAAM,CAAY;IAClB,MAAM,CAAmB;IACzB,WAAW,CAAS;IAC7B,QAAQ,GAA6B,IAAI,CAAC;IAC1C,eAAe,GAA8B,IAAI,CAAC;IAClD,aAAa,GAAqB,IAAI,CAAC;IACvC,SAAS,GAAwB,IAAI,CAAC;IACtC,YAAY,GAAqB,IAAI,CAAC;IACtC,WAAW,GAAG,CAAC,CAAC;IAExB,YAAY,MAAiB,EAAE,kBAAoC,EAAE,cAAsB,CAAC;QAC1F,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,kBAAkB,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAEO,IAAI;QACV,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAE1B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC;YACvD,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE;gBACP,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;aAC/E;SACF,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC;YAC5C,KAAK,EAAE,oBAAoB;YAC3B,IAAI,EAAE,kBAAkB;SACzB,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;YAC/C,KAAK,EAAE,sBAAsB;YAC7B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,EAAE,gBAAgB,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;YACtF,MAAM,EAAE;gBACN,MAAM;gBACN,UAAU,EAAE,SAAS;gBACrB,OAAO,EAAE;oBACP;wBACE,WAAW,EAAE,mBAAmB;wBAChC,UAAU,EAAE;4BACV,EAAE,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE;4BACrD,EAAE,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE;yBAC1D;qBACF;iBACF;aACF;YACD,QAAQ,EAAE;gBACR,MAAM;gBACN,UAAU,EAAE,SAAS;gBACrB,sEAAsE;gBACtE,uEAAuE;gBACvE,mDAAmD;gBACnD,OAAO,EAAE;oBACP;wBACE,MAAM,EAAE,IAAI,CAAC,MAAM;wBACnB,KAAK,EAAE;4BACL,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,SAAS,EAAE,KAAK,EAAE;4BAC/E,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,SAAS,EAAE,KAAK,EAAE;yBAChF;wBACD,SAAS,EAAE,aAAa,CAAC,GAAG;qBAC7B;oBACD,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,EAAE;iBACvC;aACF;YACD,uEAAuE;YACvE,uEAAuE;YACvE,wEAAwE;YACxE,qEAAqE;YACrE,SAAS,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,EAAE;YAC1D,YAAY,EAAE;gBACZ,MAAM,EAAE,kBAAkB,CAAC,YAAY;gBACvC,mEAAmE;gBACnE,qEAAqE;gBACrE,sEAAsE;gBACtE,iBAAiB,EAAE,IAAI;gBACvB,YAAY,EAAE,SAAS,EAAE,+CAA+C;gBACxE,kEAAkE;gBAClE,8DAA8D;gBAC9D,SAAS,EAAE,CAAC,CAAC;gBACb,mBAAmB,EAAE,CAAC,IAAI;gBAC1B,cAAc,EAAE,CAAC;aAClB;YACD,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE;SACzC,CAAC,CAAC;QAEH,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;YAC5C,KAAK,EAAE,oBAAoB;YAC3B,IAAI,EAAE,EAAE;YACR,KAAK,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,QAAQ;SACxD,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;YAC3C,KAAK,EAAE,gBAAgB;YACvB,MAAM,EAAE,IAAI,CAAC,eAAe;YAC5B,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;SACpE,CAAC,CAAC;IACL,CAAC;IAED,mDAAmD;IACnD,MAAM,CAAC,KAA6B;QAClC,IAAI,CAAC,IAAI,EAAE,CAAC;QAEZ,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QAErB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEjD,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE9B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;YAC3C,KAAK,EAAE,kBAAkB;YACzB,IAAI,EAAE,IAAI,CAAC,UAAU;YACrB,KAAK,EAAE,cAAc,CAAC,MAAM,GAAG,cAAc,CAAC,QAAQ;SACvD,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1D,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,mBAAmB,GAAG,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,IAA0B,EAAE,QAAsB;QACvD,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QAC3F,IAAI,IAAI,CAAC,WAAW,KAAK,CAAC;YAAE,OAAO;QACnC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC/D,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACrC,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,YAAY;YAAE,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;QACnD,IAAI,IAAI,CAAC,aAAa;YAAE,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;QACrD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;IACvB,CAAC;CACF"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { WebGPUDevice } from '../device.js';
|
|
2
|
+
import type { Scene } from '../scene.js';
|
|
3
|
+
import type { PointCloudRenderer } from '../pointcloud/point-cloud-renderer.js';
|
|
4
|
+
/** Tuning knobs for a single `DeviationComputer.compute` run. */
|
|
5
|
+
export interface DeviationComputeOptions {
|
|
6
|
+
/** Clip range applied during compute. 0 → no clip. Default 1m. */
|
|
7
|
+
maxRange?: number;
|
|
8
|
+
forceRebuild?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Everything the compute pass borrows from the `Renderer` for the
|
|
12
|
+
* duration of one call. None of it is retained.
|
|
13
|
+
*/
|
|
14
|
+
export interface DeviationComputeContext {
|
|
15
|
+
device: WebGPUDevice;
|
|
16
|
+
scene: Scene;
|
|
17
|
+
pointCloudRenderer: PointCloudRenderer | null;
|
|
18
|
+
requestRender: () => void;
|
|
19
|
+
}
|
|
20
|
+
/** Metadata a completed deviation pass reports back to the UI. */
|
|
21
|
+
export interface DeviationComputeResult {
|
|
22
|
+
bvhTriangles: number;
|
|
23
|
+
bvhNodes: number;
|
|
24
|
+
chunksProcessed: number;
|
|
25
|
+
pointsProcessed: number;
|
|
26
|
+
bounds: {
|
|
27
|
+
min: [number, number, number];
|
|
28
|
+
max: [number, number, number];
|
|
29
|
+
} | null;
|
|
30
|
+
suggestedHalfRange: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Owns the deviation compute pipeline and its BVH cache.
|
|
34
|
+
*
|
|
35
|
+
* Lifecycle mirrors the `Renderer`'s: `init()` on device creation,
|
|
36
|
+
* `destroy()` before the device goes away.
|
|
37
|
+
*/
|
|
38
|
+
export declare class DeviationComputer {
|
|
39
|
+
private pipeline;
|
|
40
|
+
/**
|
|
41
|
+
* Cache of which mesh-set the BVH was built from. We rebuild on
|
|
42
|
+
* `computeDeviations` only when the cached "fingerprint" misses,
|
|
43
|
+
* so re-running deviation against the same model is a fast
|
|
44
|
+
* dispatch — the BVH is multi-second on big BIMs and we don't
|
|
45
|
+
* want to pay that on every slider drag.
|
|
46
|
+
*/
|
|
47
|
+
private bvhFingerprint;
|
|
48
|
+
/**
|
|
49
|
+
* Create the compute pipeline for the BIM↔scan deviation heatmap.
|
|
50
|
+
* Lazily owns the per-triangle BVH GPU buffers; idle until the
|
|
51
|
+
* first `compute` call.
|
|
52
|
+
*
|
|
53
|
+
* Idempotent: a second `init()` without an intervening `destroy()`
|
|
54
|
+
* tears down the previous pipeline (releasing its GPU buffers
|
|
55
|
+
* instead of orphaning them) and clears `bvhFingerprint`. Without
|
|
56
|
+
* this, the stale fingerprint would match the next `compute()`
|
|
57
|
+
* call's input, skip re-uploading the BVH into the NEW pipeline,
|
|
58
|
+
* and `dispatch()` would silently no-op every chunk (no BVH
|
|
59
|
+
* uploaded) — reporting a plausible-looking deviation of zero
|
|
60
|
+
* instead of failing loudly.
|
|
61
|
+
*/
|
|
62
|
+
init(device: GPUDevice): void;
|
|
63
|
+
/**
|
|
64
|
+
* Compute BIM ↔ scan deviation for every loaded point cloud asset.
|
|
65
|
+
*
|
|
66
|
+
* Walks every triangle in the scene (individual + batched meshes,
|
|
67
|
+
* regardless of which IFC ingest path produced them — STEP, IFCx,
|
|
68
|
+
* GLB, or federated combinations), builds a per-triangle BVH on
|
|
69
|
+
* the GPU, then runs a closest-point compute pass per chunk that
|
|
70
|
+
* writes signed distance into each chunk's deviation buffer.
|
|
71
|
+
*
|
|
72
|
+
* Returns metadata so the UI can populate a histogram + auto-range:
|
|
73
|
+
* the per-asset point count, the suggested ±range from the 95th
|
|
74
|
+
* percentile, and the bbox the BVH was built from.
|
|
75
|
+
*
|
|
76
|
+
* Idempotent: re-running with the same mesh set reuses the GPU
|
|
77
|
+
* BVH (the BVH build dominates wall time on big BIMs). Pass
|
|
78
|
+
* `forceRebuild: true` to invalidate.
|
|
79
|
+
*/
|
|
80
|
+
compute(opts: DeviationComputeOptions, ctx: DeviationComputeContext): Promise<DeviationComputeResult>;
|
|
81
|
+
/**
|
|
82
|
+
* Release the compute pipeline + cached BVH GPU buffers and forget
|
|
83
|
+
* the fingerprint, so a re-`init()` on a fresh device rebuilds.
|
|
84
|
+
*/
|
|
85
|
+
destroy(): void;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=deviation-computer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deviation-computer.d.ts","sourceRoot":"","sources":["../../src/deviation/deviation-computer.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AA2ChF,iEAAiE;AACjE,MAAM,WAAW,uBAAuB;IACpC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACpC,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;IACb,kBAAkB,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC9C,aAAa,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE;QAAE,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;IAChF,kBAAkB,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,qBAAa,iBAAiB;IAC1B,OAAO,CAAC,QAAQ,CAAkC;IAClD;;;;;;OAMG;IACH,OAAO,CAAC,cAAc,CAAuB;IAE7C;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAM7B;;;;;;;;;;;;;;;;OAgBG;IACG,OAAO,CACT,IAAI,EAAE,uBAAuB,EAC7B,GAAG,EAAE,uBAAuB,GAC7B,OAAO,CAAC,sBAAsB,CAAC;IAuFlC;;;OAGG;IACH,OAAO,IAAI,IAAI;CAKlB"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* BIM ↔ scan deviation as a composed collaborator (issue #2425).
|
|
6
|
+
*
|
|
7
|
+
* Owns the whole deviation concern's state — the `DeviationPipeline`
|
|
8
|
+
* and the BVH-reuse fingerprint — so the `Renderer` holds one field
|
|
9
|
+
* instead of two and `computeDeviations` becomes a delegation. Nothing
|
|
10
|
+
* outside this file reads either piece of state: the render loop, the
|
|
11
|
+
* diagnostics counters and the bounds tracker are all untouched by it,
|
|
12
|
+
* which is what makes this seam separable at all.
|
|
13
|
+
*
|
|
14
|
+
* The collaborator borrows (never owns) the device, the point-cloud
|
|
15
|
+
* renderer and the scene — they arrive per call in `DeviationComputeContext`.
|
|
16
|
+
*/
|
|
17
|
+
import { DeviationPipeline } from './deviation-pipeline.js';
|
|
18
|
+
import { buildTriangleBVH } from './triangle-bvh.js';
|
|
19
|
+
/**
|
|
20
|
+
* Build a deterministic fingerprint of the BVH input mesh set so
|
|
21
|
+
* `Renderer.computeDeviations` can skip the rebuild when the source
|
|
22
|
+
* geometry hasn't changed. Folds in expressId / modelIndex / position
|
|
23
|
+
* + index lengths per mesh so two distinct mesh sets that happen to
|
|
24
|
+
* share the same aggregate position-length total can't collide on the
|
|
25
|
+
* same fingerprint and reuse a stale BVH.
|
|
26
|
+
*/
|
|
27
|
+
function computeBvhFingerprint(meshes) {
|
|
28
|
+
const parts = [String(meshes.length)];
|
|
29
|
+
for (const m of meshes) {
|
|
30
|
+
const id = m.expressId ?? -1;
|
|
31
|
+
const mi = m.modelIndex ?? -1;
|
|
32
|
+
const posLen = m.positions?.length ?? 0;
|
|
33
|
+
const idxLen = m.indices?.length ?? 0;
|
|
34
|
+
parts.push(`${id}:${mi}:${posLen}:${idxLen}`);
|
|
35
|
+
}
|
|
36
|
+
return parts.join('|');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Aggregate every triangle source the scene exposes — individual
|
|
40
|
+
* meshes (created on demand by picking / highlights) AND batched
|
|
41
|
+
* meshes (the streaming geometry path's compact GPU buffers).
|
|
42
|
+
* Both formats arrive as `MeshData`; the BVH builder doesn't care
|
|
43
|
+
* which source they came from.
|
|
44
|
+
*/
|
|
45
|
+
function collectAllSceneMeshes(scene) {
|
|
46
|
+
// The Scene keeps every CPU-side MeshData regardless of which
|
|
47
|
+
// ingest path produced it (STEP / IFCx / GLB). One iteration
|
|
48
|
+
// covers individual + batched + multi-piece + multi-model.
|
|
49
|
+
// `forEachMeshData` deduplicates by identity so a colour-merged
|
|
50
|
+
// batch is only added once even if it's indexed under multiple
|
|
51
|
+
// contributor expressIds.
|
|
52
|
+
const out = [];
|
|
53
|
+
scene.forEachMeshData((md) => {
|
|
54
|
+
if (md.positions && md.positions.length > 0)
|
|
55
|
+
out.push(md);
|
|
56
|
+
});
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Owns the deviation compute pipeline and its BVH cache.
|
|
61
|
+
*
|
|
62
|
+
* Lifecycle mirrors the `Renderer`'s: `init()` on device creation,
|
|
63
|
+
* `destroy()` before the device goes away.
|
|
64
|
+
*/
|
|
65
|
+
export class DeviationComputer {
|
|
66
|
+
pipeline = null;
|
|
67
|
+
/**
|
|
68
|
+
* Cache of which mesh-set the BVH was built from. We rebuild on
|
|
69
|
+
* `computeDeviations` only when the cached "fingerprint" misses,
|
|
70
|
+
* so re-running deviation against the same model is a fast
|
|
71
|
+
* dispatch — the BVH is multi-second on big BIMs and we don't
|
|
72
|
+
* want to pay that on every slider drag.
|
|
73
|
+
*/
|
|
74
|
+
bvhFingerprint = null;
|
|
75
|
+
/**
|
|
76
|
+
* Create the compute pipeline for the BIM↔scan deviation heatmap.
|
|
77
|
+
* Lazily owns the per-triangle BVH GPU buffers; idle until the
|
|
78
|
+
* first `compute` call.
|
|
79
|
+
*
|
|
80
|
+
* Idempotent: a second `init()` without an intervening `destroy()`
|
|
81
|
+
* tears down the previous pipeline (releasing its GPU buffers
|
|
82
|
+
* instead of orphaning them) and clears `bvhFingerprint`. Without
|
|
83
|
+
* this, the stale fingerprint would match the next `compute()`
|
|
84
|
+
* call's input, skip re-uploading the BVH into the NEW pipeline,
|
|
85
|
+
* and `dispatch()` would silently no-op every chunk (no BVH
|
|
86
|
+
* uploaded) — reporting a plausible-looking deviation of zero
|
|
87
|
+
* instead of failing loudly.
|
|
88
|
+
*/
|
|
89
|
+
init(device) {
|
|
90
|
+
this.pipeline?.destroy();
|
|
91
|
+
this.pipeline = new DeviationPipeline(device);
|
|
92
|
+
this.bvhFingerprint = null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Compute BIM ↔ scan deviation for every loaded point cloud asset.
|
|
96
|
+
*
|
|
97
|
+
* Walks every triangle in the scene (individual + batched meshes,
|
|
98
|
+
* regardless of which IFC ingest path produced them — STEP, IFCx,
|
|
99
|
+
* GLB, or federated combinations), builds a per-triangle BVH on
|
|
100
|
+
* the GPU, then runs a closest-point compute pass per chunk that
|
|
101
|
+
* writes signed distance into each chunk's deviation buffer.
|
|
102
|
+
*
|
|
103
|
+
* Returns metadata so the UI can populate a histogram + auto-range:
|
|
104
|
+
* the per-asset point count, the suggested ±range from the 95th
|
|
105
|
+
* percentile, and the bbox the BVH was built from.
|
|
106
|
+
*
|
|
107
|
+
* Idempotent: re-running with the same mesh set reuses the GPU
|
|
108
|
+
* BVH (the BVH build dominates wall time on big BIMs). Pass
|
|
109
|
+
* `forceRebuild: true` to invalidate.
|
|
110
|
+
*/
|
|
111
|
+
async compute(opts, ctx) {
|
|
112
|
+
if (!this.pipeline || !ctx.pointCloudRenderer) {
|
|
113
|
+
throw new Error('Renderer not initialised — call init() first.');
|
|
114
|
+
}
|
|
115
|
+
const meshes = collectAllSceneMeshes(ctx.scene);
|
|
116
|
+
// Fingerprint folds in per-mesh expressId / modelIndex /
|
|
117
|
+
// positions length / triangle count, so two distinct meshes
|
|
118
|
+
// that happen to share an aggregate position-length total
|
|
119
|
+
// can't alias each other. A federation reload that swaps one
|
|
120
|
+
// model for another with the same total triangle count would
|
|
121
|
+
// otherwise reuse the previous BVH and report wrong distances.
|
|
122
|
+
const fingerprint = computeBvhFingerprint(meshes);
|
|
123
|
+
if (opts.forceRebuild || fingerprint !== this.bvhFingerprint) {
|
|
124
|
+
const bvh = buildTriangleBVH(meshes);
|
|
125
|
+
this.pipeline.uploadBvh(bvh);
|
|
126
|
+
this.bvhFingerprint = fingerprint;
|
|
127
|
+
}
|
|
128
|
+
const stats = this.pipeline.getBvhStats();
|
|
129
|
+
const maxRange = opts.maxRange ?? 1.0;
|
|
130
|
+
// Encode every chunk into a single command submit so the GPU
|
|
131
|
+
// can pipeline the dispatches without a CPU round-trip per
|
|
132
|
+
// chunk. Histogram readback is a follow-up — for v1 we emit
|
|
133
|
+
// the deviation buffers and let the splat shader visualise.
|
|
134
|
+
const encoder = ctx.device.getDevice().createCommandEncoder({ label: 'pointcloud-deviation' });
|
|
135
|
+
let chunksProcessed = 0;
|
|
136
|
+
let pointsProcessed = 0;
|
|
137
|
+
const nodes = ctx.pointCloudRenderer.getInternalNodes();
|
|
138
|
+
for (const node of nodes) {
|
|
139
|
+
for (const chunk of node.chunks) {
|
|
140
|
+
const ok = this.pipeline.dispatch(encoder, {
|
|
141
|
+
positionsBuffer: chunk.vertexBuffer,
|
|
142
|
+
deviationsBuffer: chunk.deviationBuffer,
|
|
143
|
+
pointCount: chunk.pointCount,
|
|
144
|
+
maxRange,
|
|
145
|
+
// #1804: chunk positions are stored in the asset's
|
|
146
|
+
// decode-shifted local frame when IfcMapConversion
|
|
147
|
+
// alignment is active; the BVH triangles are world
|
|
148
|
+
// space, so the compute pass must apply the same
|
|
149
|
+
// per-asset matrix the splat shader renders with.
|
|
150
|
+
model: node.model,
|
|
151
|
+
});
|
|
152
|
+
if (ok) {
|
|
153
|
+
chunksProcessed++;
|
|
154
|
+
pointsProcessed += chunk.pointCount;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
ctx.device.getDevice().queue.submit([encoder.finish()]);
|
|
159
|
+
try {
|
|
160
|
+
// Wait until the GPU finishes the dispatches before resolving.
|
|
161
|
+
// Otherwise the caller's "compute done" callback fires before
|
|
162
|
+
// the deviation buffers are actually populated.
|
|
163
|
+
await ctx.device.getDevice().queue.onSubmittedWorkDone();
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
// The GPU is done reading each chunk's params uniform — free
|
|
167
|
+
// them. Must run even when the await above rejects (e.g. the
|
|
168
|
+
// device is lost mid-submit): the transient params buffers
|
|
169
|
+
// were already created and pushed onto `transientParamsBuffers`
|
|
170
|
+
// above, so skipping this on the rejection path would leak
|
|
171
|
+
// them.
|
|
172
|
+
this.pipeline.releaseTransientParams();
|
|
173
|
+
}
|
|
174
|
+
ctx.requestRender();
|
|
175
|
+
// Suggest a default half-range = max(0.01m, max-extent / 1000).
|
|
176
|
+
// Tighter than the maxRange clip; gives the user a reasonable
|
|
177
|
+
// starting slider position without a histogram readback.
|
|
178
|
+
const bb = stats.bounds;
|
|
179
|
+
const suggestedHalfRange = bb
|
|
180
|
+
? Math.max(0.01, Math.max(bb.max[0] - bb.min[0], bb.max[1] - bb.min[1], bb.max[2] - bb.min[2]) / 1000)
|
|
181
|
+
: 0.05;
|
|
182
|
+
return {
|
|
183
|
+
bvhTriangles: stats.triangleCount,
|
|
184
|
+
bvhNodes: stats.nodeCount,
|
|
185
|
+
chunksProcessed,
|
|
186
|
+
pointsProcessed,
|
|
187
|
+
bounds: stats.bounds,
|
|
188
|
+
suggestedHalfRange,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Release the compute pipeline + cached BVH GPU buffers and forget
|
|
193
|
+
* the fingerprint, so a re-`init()` on a fresh device rebuilds.
|
|
194
|
+
*/
|
|
195
|
+
destroy() {
|
|
196
|
+
this.pipeline?.destroy();
|
|
197
|
+
this.pipeline = null;
|
|
198
|
+
this.bvhFingerprint = null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=deviation-computer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deviation-computer.js","sourceRoot":"","sources":["../../src/deviation/deviation-computer.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -69,6 +69,7 @@ import { SnapDetector, type SnapTarget, type SnapOptions, type EdgeLockInput, ty
|
|
|
69
69
|
import { type RenderDegradationInfo } from './render-degradation.js';
|
|
70
70
|
import type { FrameStats } from './render-stats.js';
|
|
71
71
|
import type { PointCloudAsset } from '@ifc-lite/geometry';
|
|
72
|
+
import { type DeviationComputeOptions, type DeviationComputeResult } from './deviation/deviation-computer.js';
|
|
72
73
|
/**
|
|
73
74
|
* Main renderer class
|
|
74
75
|
*/
|
|
@@ -185,15 +186,8 @@ export declare class Renderer {
|
|
|
185
186
|
/** Retained so a listener registered AFTER the loss still learns of it. */
|
|
186
187
|
private deviceLostInfo;
|
|
187
188
|
private deviceLostListeners;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
* Cache of which mesh-set the BVH was built from. We rebuild on
|
|
191
|
-
* `computeDeviations` only when the cached "fingerprint" misses,
|
|
192
|
-
* so re-running deviation against the same model is a fast
|
|
193
|
-
* dispatch — the BVH is multi-second on big BIMs and we don't
|
|
194
|
-
* want to pay that on every slider drag.
|
|
195
|
-
*/
|
|
196
|
-
private deviationBvhFingerprint;
|
|
189
|
+
/** BIM ↔ scan deviation: owns the compute pipeline + its BVH cache. */
|
|
190
|
+
private readonly deviationComputer;
|
|
197
191
|
private readonly visualEnhancementResolver;
|
|
198
192
|
private readonly modelBoundsTracker;
|
|
199
193
|
/** Read-only view of the tracked scene AABB (live reference, not a copy). */
|
|
@@ -444,43 +438,11 @@ export declare class Renderer {
|
|
|
444
438
|
/**
|
|
445
439
|
* Compute BIM ↔ scan deviation for every loaded point cloud asset.
|
|
446
440
|
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
*
|
|
453
|
-
* Returns metadata so the UI can populate a histogram + auto-range:
|
|
454
|
-
* the per-asset point count, the suggested ±range from the 95th
|
|
455
|
-
* percentile, and the bbox the BVH was built from.
|
|
456
|
-
*
|
|
457
|
-
* Idempotent: re-running with the same mesh set reuses the GPU
|
|
458
|
-
* BVH (the BVH build dominates wall time on big BIMs). Pass
|
|
459
|
-
* `forceRebuild: true` to invalidate.
|
|
460
|
-
*/
|
|
461
|
-
computeDeviations(opts?: {
|
|
462
|
-
/** Clip range applied during compute. 0 → no clip. Default 1m. */
|
|
463
|
-
maxRange?: number;
|
|
464
|
-
forceRebuild?: boolean;
|
|
465
|
-
}): Promise<{
|
|
466
|
-
bvhTriangles: number;
|
|
467
|
-
bvhNodes: number;
|
|
468
|
-
chunksProcessed: number;
|
|
469
|
-
pointsProcessed: number;
|
|
470
|
-
bounds: {
|
|
471
|
-
min: [number, number, number];
|
|
472
|
-
max: [number, number, number];
|
|
473
|
-
} | null;
|
|
474
|
-
suggestedHalfRange: number;
|
|
475
|
-
}>;
|
|
476
|
-
/**
|
|
477
|
-
* Aggregate every triangle source the scene exposes — individual
|
|
478
|
-
* meshes (created on demand by picking / highlights) AND batched
|
|
479
|
-
* meshes (the streaming geometry path's compact GPU buffers).
|
|
480
|
-
* Both formats arrive as `MeshData`; the BVH builder doesn't care
|
|
481
|
-
* which source they came from.
|
|
482
|
-
*/
|
|
483
|
-
private collectAllSceneMeshes;
|
|
441
|
+
* Delegates to the `DeviationComputer` collaborator, which owns the
|
|
442
|
+
* compute pipeline and the BVH-reuse fingerprint; see
|
|
443
|
+
* `deviation/deviation-computer.ts` for the full contract.
|
|
444
|
+
*/
|
|
445
|
+
computeDeviations(opts?: DeviationComputeOptions): Promise<DeviationComputeResult>;
|
|
484
446
|
/**
|
|
485
447
|
* Toggle Eye-Dome Lighting and tune its strength.
|
|
486
448
|
*
|
|
@@ -866,6 +828,20 @@ export declare class Renderer {
|
|
|
866
828
|
vertices: Float32Array;
|
|
867
829
|
color: [number, number, number, number];
|
|
868
830
|
} | null): void;
|
|
831
|
+
/**
|
|
832
|
+
* Draw the focused clash's TRUE INTERSECTION VOLUME — the actual overlap
|
|
833
|
+
* mesh from `clashIntersectionSolid` — as an opaque solid, so the clash
|
|
834
|
+
* reads as a shape rather than a wireframe box or contact line (the
|
|
835
|
+
* BIMcollab Zoom / Solibri presentation). Pass `null` to clear. Independent
|
|
836
|
+
* of `setClashOverlapBox` / `setClashContactLines`: the caller decides
|
|
837
|
+
* which one is current for a given clash (solid when the kernel resolved
|
|
838
|
+
* one, box/lines as the fallback when it didn't).
|
|
839
|
+
*/
|
|
840
|
+
setClashIntersectionSolid(solid: {
|
|
841
|
+
positions: Float32Array | Float64Array;
|
|
842
|
+
indices: Uint32Array;
|
|
843
|
+
color: [number, number, number, number];
|
|
844
|
+
} | null): void;
|
|
869
845
|
/**
|
|
870
846
|
* Upload filled IfcAnnotation regions for the symbolic overlay
|
|
871
847
|
* (issue #653). Pass an empty array to clear.
|