@ifc-lite/renderer 1.17.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/deviation/deviation-pipeline.d.ts +48 -0
  2. package/dist/deviation/deviation-pipeline.d.ts.map +1 -0
  3. package/dist/deviation/deviation-pipeline.js +163 -0
  4. package/dist/deviation/deviation-pipeline.js.map +1 -0
  5. package/dist/deviation/deviation-shader.wgsl.d.ts +23 -0
  6. package/dist/deviation/deviation-shader.wgsl.d.ts.map +1 -0
  7. package/dist/deviation/deviation-shader.wgsl.js +237 -0
  8. package/dist/deviation/deviation-shader.wgsl.js.map +1 -0
  9. package/dist/deviation/triangle-bvh.d.ts +58 -0
  10. package/dist/deviation/triangle-bvh.d.ts.map +1 -0
  11. package/dist/deviation/triangle-bvh.js +255 -0
  12. package/dist/deviation/triangle-bvh.js.map +1 -0
  13. package/dist/device.d.ts +3 -0
  14. package/dist/device.d.ts.map +1 -1
  15. package/dist/device.js +10 -0
  16. package/dist/device.js.map +1 -1
  17. package/dist/edl-pass.d.ts +52 -0
  18. package/dist/edl-pass.d.ts.map +1 -0
  19. package/dist/edl-pass.js +204 -0
  20. package/dist/edl-pass.js.map +1 -0
  21. package/dist/index.d.ts +175 -4
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +676 -110
  24. package/dist/index.js.map +1 -1
  25. package/dist/picker.d.ts +40 -3
  26. package/dist/picker.d.ts.map +1 -1
  27. package/dist/picker.js +211 -59
  28. package/dist/picker.js.map +1 -1
  29. package/dist/picking-manager.d.ts +32 -1
  30. package/dist/picking-manager.d.ts.map +1 -1
  31. package/dist/picking-manager.js +53 -1
  32. package/dist/picking-manager.js.map +1 -1
  33. package/dist/point-picker.d.ts +61 -0
  34. package/dist/point-picker.d.ts.map +1 -0
  35. package/dist/point-picker.js +223 -0
  36. package/dist/point-picker.js.map +1 -0
  37. package/dist/pointcloud/point-cloud-node.d.ts +63 -0
  38. package/dist/pointcloud/point-cloud-node.d.ts.map +1 -0
  39. package/dist/pointcloud/point-cloud-node.js +149 -0
  40. package/dist/pointcloud/point-cloud-node.js.map +1 -0
  41. package/dist/pointcloud/point-cloud-renderer.d.ts +166 -0
  42. package/dist/pointcloud/point-cloud-renderer.d.ts.map +1 -0
  43. package/dist/pointcloud/point-cloud-renderer.js +293 -0
  44. package/dist/pointcloud/point-cloud-renderer.js.map +1 -0
  45. package/dist/pointcloud/point-cloud-uniforms.d.ts +36 -0
  46. package/dist/pointcloud/point-cloud-uniforms.d.ts.map +1 -0
  47. package/dist/pointcloud/point-cloud-uniforms.js +89 -0
  48. package/dist/pointcloud/point-cloud-uniforms.js.map +1 -0
  49. package/dist/pointcloud/point-pipeline.d.ts +27 -0
  50. package/dist/pointcloud/point-pipeline.d.ts.map +1 -0
  51. package/dist/pointcloud/point-pipeline.js +126 -0
  52. package/dist/pointcloud/point-pipeline.js.map +1 -0
  53. package/dist/pointcloud/point-shader.wgsl.d.ts +22 -0
  54. package/dist/pointcloud/point-shader.wgsl.d.ts.map +1 -0
  55. package/dist/pointcloud/point-shader.wgsl.js +288 -0
  56. package/dist/pointcloud/point-shader.wgsl.js.map +1 -0
  57. package/dist/scene.d.ts +11 -0
  58. package/dist/scene.d.ts.map +1 -1
  59. package/dist/scene.js +21 -0
  60. package/dist/scene.js.map +1 -1
  61. package/dist/section-2d-overlay.d.ts +24 -5
  62. package/dist/section-2d-overlay.d.ts.map +1 -1
  63. package/dist/section-2d-overlay.js +42 -13
  64. package/dist/section-2d-overlay.js.map +1 -1
  65. package/dist/section-plane-basis.d.ts +64 -0
  66. package/dist/section-plane-basis.d.ts.map +1 -0
  67. package/dist/section-plane-basis.js +86 -0
  68. package/dist/section-plane-basis.js.map +1 -0
  69. package/dist/section-plane.d.ts +18 -0
  70. package/dist/section-plane.d.ts.map +1 -1
  71. package/dist/section-plane.js +89 -6
  72. package/dist/section-plane.js.map +1 -1
  73. package/dist/types.d.ts +35 -4
  74. package/dist/types.d.ts.map +1 -1
  75. package/package.json +3 -3
@@ -0,0 +1,48 @@
1
+ import type { TriangleBVHResult } from './triangle-bvh.js';
2
+ export interface DeviationDispatchInput {
3
+ /** Storage-usage GPU buffer holding interleaved point vertices.
4
+ * Must be the same buffer used as the splat pipeline's vertex
5
+ * buffer for this chunk; the compute shader reads positions
6
+ * directly from it (no copy). */
7
+ positionsBuffer: GPUBuffer;
8
+ /** Output buffer — one f32 per point. Must allow STORAGE. */
9
+ deviationsBuffer: GPUBuffer;
10
+ /** Number of points to process. */
11
+ pointCount: number;
12
+ /** Optional clip range in metres. 0 / negative → no clip. */
13
+ maxRange: number;
14
+ }
15
+ export declare class DeviationPipeline {
16
+ private device;
17
+ private pipeline;
18
+ private bindGroupLayout;
19
+ private bvhNodesBuffer;
20
+ private trianglesBuffer;
21
+ private bvhTriangleCount;
22
+ private bvhNodeCount;
23
+ private bvhBounds;
24
+ constructor(device: GPUDevice);
25
+ /**
26
+ * Upload the per-triangle BVH to the GPU. Replaces any previous
27
+ * upload; safe to call repeatedly when the mesh set changes (load,
28
+ * federation update, isolation toggle).
29
+ */
30
+ uploadBvh(bvh: TriangleBVHResult): void;
31
+ hasBvh(): boolean;
32
+ getBvhStats(): {
33
+ nodeCount: number;
34
+ triangleCount: number;
35
+ bounds: TriangleBVHResult['bounds'] | null;
36
+ };
37
+ /**
38
+ * Run the compute pass for one point chunk. Encoder-based so the
39
+ * caller can dispatch many chunks back-to-back in one submit.
40
+ *
41
+ * Returns false when there's no BVH uploaded yet — caller should
42
+ * skip the chunk in that case.
43
+ */
44
+ dispatch(encoder: GPUCommandEncoder, input: DeviationDispatchInput): boolean;
45
+ private disposeBvh;
46
+ destroy(): void;
47
+ }
48
+ //# sourceMappingURL=deviation-pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deviation-pipeline.d.ts","sourceRoot":"","sources":["../../src/deviation/deviation-pipeline.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAc3D,MAAM,WAAW,sBAAsB;IACrC;;;sCAGkC;IAClC,eAAe,EAAE,SAAS,CAAC;IAC3B,6DAA6D;IAC7D,gBAAgB,EAAE,SAAS,CAAC;IAC5B,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,SAAS,CAA4C;gBAEjD,MAAM,EAAE,SAAS;IAqB7B;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,iBAAiB,GAAG,IAAI;IAuCvC,MAAM,IAAI,OAAO;IAIjB,WAAW,IAAI;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAA;KAAE;IAQvG;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO;IA4C5E,OAAO,CAAC,UAAU;IAOlB,OAAO,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,163 @@
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
+ * Compute pipeline for BIM ↔ scan deviation. Owns the GPU-resident
6
+ * triangle BVH, builds bind groups per chunk on demand, and dispatches
7
+ * the closest-point-on-triangle compute shader.
8
+ *
9
+ * The BVH lives in two storage buffers (nodes + triangles); both are
10
+ * uploaded once per mesh-set change and reused across every chunk in
11
+ * every point cloud asset. Per-chunk bind groups are created lazily
12
+ * because a chunk's vertex buffer is the `positions` storage source
13
+ * for the compute pass.
14
+ */
15
+ import { deviationShaderSource } from './deviation-shader.wgsl.js';
16
+ import { POINT_VERTEX_BYTES } from '../pointcloud/point-pipeline.js';
17
+ /** Bytes per BVH node — 8 floats / 8 u32s laid out per the shader. */
18
+ const BVH_NODE_BYTES = 32;
19
+ /** Bytes per triangle — 12 floats (3 verts + face normal). */
20
+ const TRIANGLE_BYTES = 48;
21
+ /**
22
+ * Uniform block size for `DeviationParams`. WGSL std140 packs the
23
+ * struct compactly; we add 4 padding u32s to round to 32 bytes which
24
+ * matches a single uniform alignment slot on every WebGPU impl.
25
+ */
26
+ const PARAMS_UNIFORM_BYTES = 32;
27
+ export class DeviationPipeline {
28
+ device;
29
+ pipeline;
30
+ bindGroupLayout;
31
+ bvhNodesBuffer = null;
32
+ trianglesBuffer = null;
33
+ bvhTriangleCount = 0;
34
+ bvhNodeCount = 0;
35
+ bvhBounds = null;
36
+ constructor(device) {
37
+ this.device = device;
38
+ this.bindGroupLayout = device.createBindGroupLayout({
39
+ entries: [
40
+ { binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } },
41
+ { binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } },
42
+ { binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } },
43
+ { binding: 3, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } },
44
+ { binding: 4, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'uniform' } },
45
+ ],
46
+ });
47
+ const layout = device.createPipelineLayout({ bindGroupLayouts: [this.bindGroupLayout] });
48
+ this.pipeline = device.createComputePipeline({
49
+ layout,
50
+ compute: {
51
+ module: device.createShaderModule({ code: deviationShaderSource }),
52
+ entryPoint: 'cs_main',
53
+ },
54
+ });
55
+ }
56
+ /**
57
+ * Upload the per-triangle BVH to the GPU. Replaces any previous
58
+ * upload; safe to call repeatedly when the mesh set changes (load,
59
+ * federation update, isolation toggle).
60
+ */
61
+ uploadBvh(bvh) {
62
+ this.disposeBvh();
63
+ if (bvh.triangleCount === 0) {
64
+ this.bvhNodeCount = 0;
65
+ this.bvhTriangleCount = 0;
66
+ this.bvhBounds = bvh.bounds;
67
+ return;
68
+ }
69
+ // Nodes: pack into a STORAGE buffer. The Float32Array view also
70
+ // contains u32 fields (childA, childB) but they were already
71
+ // written via Uint32Array aliasing during the build, so a single
72
+ // write of the underlying ArrayBuffer carries them too.
73
+ const nodeBuf = this.device.createBuffer({
74
+ size: bvh.nodeCount * BVH_NODE_BYTES,
75
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
76
+ });
77
+ this.device.queue.writeBuffer(nodeBuf, 0,
78
+ // Pass the typed-array view directly — TS widens `.buffer` to
79
+ // ArrayBufferLike on a function-parameter Float32Array which
80
+ // doesn't satisfy writeBuffer's signature. The view form has
81
+ // size in elements (so we slice down to the populated head).
82
+ bvh.nodes.subarray(0, bvh.nodeCount * BVH_NODE_BYTES / 4));
83
+ const triBuf = this.device.createBuffer({
84
+ size: bvh.triangleCount * TRIANGLE_BYTES,
85
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
86
+ });
87
+ this.device.queue.writeBuffer(triBuf, 0, bvh.triangles.subarray(0, bvh.triangleCount * TRIANGLE_BYTES / 4));
88
+ this.bvhNodesBuffer = nodeBuf;
89
+ this.trianglesBuffer = triBuf;
90
+ this.bvhNodeCount = bvh.nodeCount;
91
+ this.bvhTriangleCount = bvh.triangleCount;
92
+ this.bvhBounds = bvh.bounds;
93
+ }
94
+ hasBvh() {
95
+ return this.bvhNodesBuffer !== null && this.trianglesBuffer !== null && this.bvhTriangleCount > 0;
96
+ }
97
+ getBvhStats() {
98
+ return {
99
+ nodeCount: this.bvhNodeCount,
100
+ triangleCount: this.bvhTriangleCount,
101
+ bounds: this.bvhBounds,
102
+ };
103
+ }
104
+ /**
105
+ * Run the compute pass for one point chunk. Encoder-based so the
106
+ * caller can dispatch many chunks back-to-back in one submit.
107
+ *
108
+ * Returns false when there's no BVH uploaded yet — caller should
109
+ * skip the chunk in that case.
110
+ */
111
+ dispatch(encoder, input) {
112
+ if (!this.bvhNodesBuffer || !this.trianglesBuffer)
113
+ return false;
114
+ if (input.pointCount === 0)
115
+ return true;
116
+ // Per-chunk uniform buffer with the dispatch params. Created
117
+ // fresh each call — 32 bytes is too small to bother caching.
118
+ const paramsBuffer = this.device.createBuffer({
119
+ size: PARAMS_UNIFORM_BYTES,
120
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
121
+ });
122
+ const params = new Uint32Array(PARAMS_UNIFORM_BYTES / 4);
123
+ const paramsF = new Float32Array(params.buffer);
124
+ params[0] = input.pointCount;
125
+ // Each point in the splat vertex layout occupies POINT_VERTEX_BYTES
126
+ // (24 bytes). The shader walks `positions` as a flat f32 array; one
127
+ // point = 6 floats; the vec3 position is at offset 0.
128
+ params[1] = POINT_VERTEX_BYTES / 4; // pointStrideF32
129
+ params[2] = 0; // positionOffsetF32
130
+ paramsF[3] = Math.max(0, input.maxRange);
131
+ // params[4..7] reserved padding; left zero.
132
+ this.device.queue.writeBuffer(paramsBuffer, 0, params.buffer, 0, PARAMS_UNIFORM_BYTES);
133
+ const bindGroup = this.device.createBindGroup({
134
+ layout: this.bindGroupLayout,
135
+ entries: [
136
+ { binding: 0, resource: { buffer: this.bvhNodesBuffer } },
137
+ { binding: 1, resource: { buffer: this.trianglesBuffer } },
138
+ { binding: 2, resource: { buffer: input.positionsBuffer } },
139
+ { binding: 3, resource: { buffer: input.deviationsBuffer } },
140
+ { binding: 4, resource: { buffer: paramsBuffer } },
141
+ ],
142
+ });
143
+ const pass = encoder.beginComputePass();
144
+ pass.setPipeline(this.pipeline);
145
+ pass.setBindGroup(0, bindGroup);
146
+ // Workgroup size 64; ceil division. Most GPUs handle ~10⁵
147
+ // workgroups in one dispatch without trouble.
148
+ const groupCount = Math.ceil(input.pointCount / 64);
149
+ pass.dispatchWorkgroups(groupCount);
150
+ pass.end();
151
+ return true;
152
+ }
153
+ disposeBvh() {
154
+ this.bvhNodesBuffer?.destroy();
155
+ this.trianglesBuffer?.destroy();
156
+ this.bvhNodesBuffer = null;
157
+ this.trianglesBuffer = null;
158
+ }
159
+ destroy() {
160
+ this.disposeBvh();
161
+ }
162
+ }
163
+ //# sourceMappingURL=deviation-pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deviation-pipeline.js","sourceRoot":"","sources":["../../src/deviation/deviation-pipeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Compute shader for BIM ↔ scan deviation.
3
+ *
4
+ * For each scan point, walk a per-triangle BVH and compute the signed
5
+ * distance to the nearest mesh surface. Sign is positive on the side
6
+ * the triangle's outward normal points to.
7
+ *
8
+ * Bind group layout:
9
+ * @binding(0) bvhNodes: array<vec4<f32>, 2> — 32-byte nodes (read)
10
+ * packed: [aabbMin, leafOrLeft]
11
+ * [aabbMax, leafOrRight]
12
+ * @binding(1) triangles: array<f32> — 12 floats per triangle
13
+ * @binding(2) positions: array<f32> — 6 floats per point
14
+ * (matches POINT_VERTEX_BYTES
15
+ * layout: vec3 + colorPacked)
16
+ * @binding(3) deviations: array<f32> — output, one float per point
17
+ * @binding(4) params: DeviationParams
18
+ *
19
+ * Workgroup size 64 (one wavefront / warp on most desktop GPUs).
20
+ * Each invocation processes one point.
21
+ */
22
+ export declare const deviationShaderSource = "\nstruct BvhNode {\n aabbMinX: f32, aabbMinY: f32, aabbMinZ: f32,\n // High bit: leaf flag. Low 31 bits: leaf=triStart, internal=leftChildIdx.\n leafOrLeft: u32,\n aabbMaxX: f32, aabbMaxY: f32, aabbMaxZ: f32,\n // Leaf=triCount, internal=rightChildIdx.\n countOrRight: u32,\n}\n\nstruct DeviationParams {\n pointCount: u32,\n pointStrideF32: u32, // floats between successive points in positions buffer\n positionOffsetF32: u32, // float offset of vec3 position within a point\n // Optional clip range \u2014 when nonzero, signs above + below are kept\n // but values past \u00B1maxRange are clamped (saves shader work for\n // points far outside the model).\n maxRange: f32,\n // Reserved padding to keep the struct 16-byte aligned for std140.\n _pad0: u32, _pad1: u32, _pad2: u32, _pad3: u32,\n}\n\n@group(0) @binding(0) var<storage, read> bvhNodes: array<BvhNode>;\n@group(0) @binding(1) var<storage, read> triangles: array<f32>;\n@group(0) @binding(2) var<storage, read> positions: array<f32>;\n@group(0) @binding(3) var<storage, read_write> deviations: array<f32>;\n@group(0) @binding(4) var<uniform> params: DeviationParams;\n\nconst LEAF_FLAG: u32 = 0x80000000u;\nconst STACK_SIZE: u32 = 64u;\n\n// Squared distance from point p to AABB [aabbMin, aabbMax].\n// Returns 0 if p is inside the box.\nfn distSqPointAabb(\n px: f32, py: f32, pz: f32,\n ax: f32, ay: f32, az: f32,\n bx: f32, by: f32, bz: f32,\n) -> f32 {\n let dx = max(max(ax - px, 0.0), px - bx);\n let dy = max(max(ay - py, 0.0), py - by);\n let dz = max(max(az - pz, 0.0), pz - bz);\n return dx * dx + dy * dy + dz * dz;\n}\n\nstruct ClosestResult {\n point: vec3<f32>,\n distSq: f32,\n}\n\n// Ericson, Real-Time Collision Detection \u00A75.1.5: closest point on\n// a triangle to an arbitrary point in space. Branches over the\n// Voronoi regions of the triangle (3 verts, 3 edges, interior).\nfn closestPointOnTriangle(p: vec3<f32>, a: vec3<f32>, b: vec3<f32>, c: vec3<f32>) -> ClosestResult {\n let ab = b - a;\n let ac = c - a;\n let ap = p - a;\n let d1 = dot(ab, ap);\n let d2 = dot(ac, ap);\n if (d1 <= 0.0 && d2 <= 0.0) {\n let diff = p - a;\n return ClosestResult(a, dot(diff, diff));\n }\n let bp = p - b;\n let d3 = dot(ab, bp);\n let d4 = dot(ac, bp);\n if (d3 >= 0.0 && d4 <= d3) {\n let diff = p - b;\n return ClosestResult(b, dot(diff, diff));\n }\n let vc = d1 * d4 - d3 * d2;\n if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) {\n let v = d1 / (d1 - d3);\n let q = a + v * ab;\n let diff = p - q;\n return ClosestResult(q, dot(diff, diff));\n }\n let cp = p - c;\n let d5 = dot(ab, cp);\n let d6 = dot(ac, cp);\n if (d6 >= 0.0 && d5 <= d6) {\n let diff = p - c;\n return ClosestResult(c, dot(diff, diff));\n }\n let vb = d5 * d2 - d1 * d6;\n if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) {\n let w = d2 / (d2 - d6);\n let q = a + w * ac;\n let diff = p - q;\n return ClosestResult(q, dot(diff, diff));\n }\n let va = d3 * d6 - d5 * d4;\n if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0) {\n let w = (d4 - d3) / ((d4 - d3) + (d5 - d6));\n let q = b + w * (c - b);\n let diff = p - q;\n return ClosestResult(q, dot(diff, diff));\n }\n // Inside the face: barycentric (v, w).\n let denom = 1.0 / (va + vb + vc);\n let v = vb * denom;\n let w = vc * denom;\n let q = a + ab * v + ac * w;\n let diff = p - q;\n return ClosestResult(q, dot(diff, diff));\n}\n\n@compute @workgroup_size(64)\nfn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let pi = gid.x;\n if (pi >= params.pointCount) {\n return;\n }\n let posOff = pi * params.pointStrideF32 + params.positionOffsetF32;\n let p = vec3<f32>(positions[posOff], positions[posOff + 1u], positions[posOff + 2u]);\n\n // Best squared distance across all triangles. Stored squared so\n // we can prune AABBs without taking sqrt every step.\n var bestDistSq: f32 = 1.0e30;\n var bestPoint: vec3<f32> = vec3<f32>(0.0);\n var bestNormal: vec3<f32> = vec3<f32>(0.0, 1.0, 0.0);\n\n // Stack-based BVH descent. Workgroup-uniform stack would let\n // siblings cooperate; for v1 a per-thread stack in private memory\n // is simpler and fast enough.\n var stack: array<u32, STACK_SIZE>;\n var sp: u32 = 0u;\n stack[sp] = 0u;\n sp = sp + 1u;\n\n loop {\n if (sp == 0u) { break; }\n sp = sp - 1u;\n let nodeIdx = stack[sp];\n let node = bvhNodes[nodeIdx];\n\n let aabbDistSq = distSqPointAabb(\n p.x, p.y, p.z,\n node.aabbMinX, node.aabbMinY, node.aabbMinZ,\n node.aabbMaxX, node.aabbMaxY, node.aabbMaxZ,\n );\n if (aabbDistSq >= bestDistSq) {\n continue;\n }\n\n let leafFlag = node.leafOrLeft & LEAF_FLAG;\n if (leafFlag != 0u) {\n let triStart = node.leafOrLeft & (~LEAF_FLAG);\n let triCount = node.countOrRight;\n var i: u32 = 0u;\n loop {\n if (i >= triCount) { break; }\n let triOff = (triStart + i) * 12u;\n let v0 = vec3<f32>(triangles[triOff], triangles[triOff + 1u], triangles[triOff + 2u]);\n let v1 = vec3<f32>(triangles[triOff + 3u], triangles[triOff + 4u], triangles[triOff + 5u]);\n let v2 = vec3<f32>(triangles[triOff + 6u], triangles[triOff + 7u], triangles[triOff + 8u]);\n let n = vec3<f32>(triangles[triOff + 9u], triangles[triOff + 10u], triangles[triOff + 11u]);\n let res = closestPointOnTriangle(p, v0, v1, v2);\n if (res.distSq < bestDistSq) {\n bestDistSq = res.distSq;\n bestPoint = res.point;\n bestNormal = n;\n }\n i = i + 1u;\n }\n } else {\n // Internal node: push both children. Sibling that's closer to\n // p first (top of stack) so we hit the tighter bound earlier.\n let leftIdx = node.leafOrLeft;\n let rightIdx = node.countOrRight;\n let lNode = bvhNodes[leftIdx];\n let rNode = bvhNodes[rightIdx];\n let lDist = distSqPointAabb(\n p.x, p.y, p.z,\n lNode.aabbMinX, lNode.aabbMinY, lNode.aabbMinZ,\n lNode.aabbMaxX, lNode.aabbMaxY, lNode.aabbMaxZ,\n );\n let rDist = distSqPointAabb(\n p.x, p.y, p.z,\n rNode.aabbMinX, rNode.aabbMinY, rNode.aabbMinZ,\n rNode.aabbMaxX, rNode.aabbMaxY, rNode.aabbMaxZ,\n );\n // Push the farther child first \u2192 closer popped first.\n if (sp + 2u <= STACK_SIZE) {\n if (lDist < rDist) {\n stack[sp] = rightIdx; sp = sp + 1u;\n stack[sp] = leftIdx; sp = sp + 1u;\n } else {\n stack[sp] = leftIdx; sp = sp + 1u;\n stack[sp] = rightIdx; sp = sp + 1u;\n }\n }\n }\n }\n\n // Signed distance: project (p - closestPoint) onto the closest\n // triangle's normal. Positive \u21D2 p is on the outward side.\n let toPoint = p - bestPoint;\n let dist = sqrt(bestDistSq);\n let s = sign(dot(toPoint, bestNormal));\n var signed: f32 = s * dist;\n\n // Optional clip: keeps the histogram + ramp focused on near-surface\n // points. Past \u00B1maxRange the value pegs at the edge.\n if (params.maxRange > 0.0) {\n let mr = params.maxRange;\n if (signed > mr) { signed = mr; }\n if (signed < -mr) { signed = -mr; }\n }\n\n deviations[pi] = signed;\n}\n";
23
+ //# sourceMappingURL=deviation-shader.wgsl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deviation-shader.wgsl.d.ts","sourceRoot":"","sources":["../../src/deviation/deviation-shader.wgsl.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,eAAO,MAAM,qBAAqB,8kOAmNjC,CAAC"}
@@ -0,0 +1,237 @@
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
+ * Compute shader for BIM ↔ scan deviation.
6
+ *
7
+ * For each scan point, walk a per-triangle BVH and compute the signed
8
+ * distance to the nearest mesh surface. Sign is positive on the side
9
+ * the triangle's outward normal points to.
10
+ *
11
+ * Bind group layout:
12
+ * @binding(0) bvhNodes: array<vec4<f32>, 2> — 32-byte nodes (read)
13
+ * packed: [aabbMin, leafOrLeft]
14
+ * [aabbMax, leafOrRight]
15
+ * @binding(1) triangles: array<f32> — 12 floats per triangle
16
+ * @binding(2) positions: array<f32> — 6 floats per point
17
+ * (matches POINT_VERTEX_BYTES
18
+ * layout: vec3 + colorPacked)
19
+ * @binding(3) deviations: array<f32> — output, one float per point
20
+ * @binding(4) params: DeviationParams
21
+ *
22
+ * Workgroup size 64 (one wavefront / warp on most desktop GPUs).
23
+ * Each invocation processes one point.
24
+ */
25
+ export const deviationShaderSource = /* wgsl */ `
26
+ struct BvhNode {
27
+ aabbMinX: f32, aabbMinY: f32, aabbMinZ: f32,
28
+ // High bit: leaf flag. Low 31 bits: leaf=triStart, internal=leftChildIdx.
29
+ leafOrLeft: u32,
30
+ aabbMaxX: f32, aabbMaxY: f32, aabbMaxZ: f32,
31
+ // Leaf=triCount, internal=rightChildIdx.
32
+ countOrRight: u32,
33
+ }
34
+
35
+ struct DeviationParams {
36
+ pointCount: u32,
37
+ pointStrideF32: u32, // floats between successive points in positions buffer
38
+ positionOffsetF32: u32, // float offset of vec3 position within a point
39
+ // Optional clip range — when nonzero, signs above + below are kept
40
+ // but values past ±maxRange are clamped (saves shader work for
41
+ // points far outside the model).
42
+ maxRange: f32,
43
+ // Reserved padding to keep the struct 16-byte aligned for std140.
44
+ _pad0: u32, _pad1: u32, _pad2: u32, _pad3: u32,
45
+ }
46
+
47
+ @group(0) @binding(0) var<storage, read> bvhNodes: array<BvhNode>;
48
+ @group(0) @binding(1) var<storage, read> triangles: array<f32>;
49
+ @group(0) @binding(2) var<storage, read> positions: array<f32>;
50
+ @group(0) @binding(3) var<storage, read_write> deviations: array<f32>;
51
+ @group(0) @binding(4) var<uniform> params: DeviationParams;
52
+
53
+ const LEAF_FLAG: u32 = 0x80000000u;
54
+ const STACK_SIZE: u32 = 64u;
55
+
56
+ // Squared distance from point p to AABB [aabbMin, aabbMax].
57
+ // Returns 0 if p is inside the box.
58
+ fn distSqPointAabb(
59
+ px: f32, py: f32, pz: f32,
60
+ ax: f32, ay: f32, az: f32,
61
+ bx: f32, by: f32, bz: f32,
62
+ ) -> f32 {
63
+ let dx = max(max(ax - px, 0.0), px - bx);
64
+ let dy = max(max(ay - py, 0.0), py - by);
65
+ let dz = max(max(az - pz, 0.0), pz - bz);
66
+ return dx * dx + dy * dy + dz * dz;
67
+ }
68
+
69
+ struct ClosestResult {
70
+ point: vec3<f32>,
71
+ distSq: f32,
72
+ }
73
+
74
+ // Ericson, Real-Time Collision Detection §5.1.5: closest point on
75
+ // a triangle to an arbitrary point in space. Branches over the
76
+ // Voronoi regions of the triangle (3 verts, 3 edges, interior).
77
+ fn closestPointOnTriangle(p: vec3<f32>, a: vec3<f32>, b: vec3<f32>, c: vec3<f32>) -> ClosestResult {
78
+ let ab = b - a;
79
+ let ac = c - a;
80
+ let ap = p - a;
81
+ let d1 = dot(ab, ap);
82
+ let d2 = dot(ac, ap);
83
+ if (d1 <= 0.0 && d2 <= 0.0) {
84
+ let diff = p - a;
85
+ return ClosestResult(a, dot(diff, diff));
86
+ }
87
+ let bp = p - b;
88
+ let d3 = dot(ab, bp);
89
+ let d4 = dot(ac, bp);
90
+ if (d3 >= 0.0 && d4 <= d3) {
91
+ let diff = p - b;
92
+ return ClosestResult(b, dot(diff, diff));
93
+ }
94
+ let vc = d1 * d4 - d3 * d2;
95
+ if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) {
96
+ let v = d1 / (d1 - d3);
97
+ let q = a + v * ab;
98
+ let diff = p - q;
99
+ return ClosestResult(q, dot(diff, diff));
100
+ }
101
+ let cp = p - c;
102
+ let d5 = dot(ab, cp);
103
+ let d6 = dot(ac, cp);
104
+ if (d6 >= 0.0 && d5 <= d6) {
105
+ let diff = p - c;
106
+ return ClosestResult(c, dot(diff, diff));
107
+ }
108
+ let vb = d5 * d2 - d1 * d6;
109
+ if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) {
110
+ let w = d2 / (d2 - d6);
111
+ let q = a + w * ac;
112
+ let diff = p - q;
113
+ return ClosestResult(q, dot(diff, diff));
114
+ }
115
+ let va = d3 * d6 - d5 * d4;
116
+ if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0) {
117
+ let w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
118
+ let q = b + w * (c - b);
119
+ let diff = p - q;
120
+ return ClosestResult(q, dot(diff, diff));
121
+ }
122
+ // Inside the face: barycentric (v, w).
123
+ let denom = 1.0 / (va + vb + vc);
124
+ let v = vb * denom;
125
+ let w = vc * denom;
126
+ let q = a + ab * v + ac * w;
127
+ let diff = p - q;
128
+ return ClosestResult(q, dot(diff, diff));
129
+ }
130
+
131
+ @compute @workgroup_size(64)
132
+ fn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {
133
+ let pi = gid.x;
134
+ if (pi >= params.pointCount) {
135
+ return;
136
+ }
137
+ let posOff = pi * params.pointStrideF32 + params.positionOffsetF32;
138
+ let p = vec3<f32>(positions[posOff], positions[posOff + 1u], positions[posOff + 2u]);
139
+
140
+ // Best squared distance across all triangles. Stored squared so
141
+ // we can prune AABBs without taking sqrt every step.
142
+ var bestDistSq: f32 = 1.0e30;
143
+ var bestPoint: vec3<f32> = vec3<f32>(0.0);
144
+ var bestNormal: vec3<f32> = vec3<f32>(0.0, 1.0, 0.0);
145
+
146
+ // Stack-based BVH descent. Workgroup-uniform stack would let
147
+ // siblings cooperate; for v1 a per-thread stack in private memory
148
+ // is simpler and fast enough.
149
+ var stack: array<u32, STACK_SIZE>;
150
+ var sp: u32 = 0u;
151
+ stack[sp] = 0u;
152
+ sp = sp + 1u;
153
+
154
+ loop {
155
+ if (sp == 0u) { break; }
156
+ sp = sp - 1u;
157
+ let nodeIdx = stack[sp];
158
+ let node = bvhNodes[nodeIdx];
159
+
160
+ let aabbDistSq = distSqPointAabb(
161
+ p.x, p.y, p.z,
162
+ node.aabbMinX, node.aabbMinY, node.aabbMinZ,
163
+ node.aabbMaxX, node.aabbMaxY, node.aabbMaxZ,
164
+ );
165
+ if (aabbDistSq >= bestDistSq) {
166
+ continue;
167
+ }
168
+
169
+ let leafFlag = node.leafOrLeft & LEAF_FLAG;
170
+ if (leafFlag != 0u) {
171
+ let triStart = node.leafOrLeft & (~LEAF_FLAG);
172
+ let triCount = node.countOrRight;
173
+ var i: u32 = 0u;
174
+ loop {
175
+ if (i >= triCount) { break; }
176
+ let triOff = (triStart + i) * 12u;
177
+ let v0 = vec3<f32>(triangles[triOff], triangles[triOff + 1u], triangles[triOff + 2u]);
178
+ let v1 = vec3<f32>(triangles[triOff + 3u], triangles[triOff + 4u], triangles[triOff + 5u]);
179
+ let v2 = vec3<f32>(triangles[triOff + 6u], triangles[triOff + 7u], triangles[triOff + 8u]);
180
+ let n = vec3<f32>(triangles[triOff + 9u], triangles[triOff + 10u], triangles[triOff + 11u]);
181
+ let res = closestPointOnTriangle(p, v0, v1, v2);
182
+ if (res.distSq < bestDistSq) {
183
+ bestDistSq = res.distSq;
184
+ bestPoint = res.point;
185
+ bestNormal = n;
186
+ }
187
+ i = i + 1u;
188
+ }
189
+ } else {
190
+ // Internal node: push both children. Sibling that's closer to
191
+ // p first (top of stack) so we hit the tighter bound earlier.
192
+ let leftIdx = node.leafOrLeft;
193
+ let rightIdx = node.countOrRight;
194
+ let lNode = bvhNodes[leftIdx];
195
+ let rNode = bvhNodes[rightIdx];
196
+ let lDist = distSqPointAabb(
197
+ p.x, p.y, p.z,
198
+ lNode.aabbMinX, lNode.aabbMinY, lNode.aabbMinZ,
199
+ lNode.aabbMaxX, lNode.aabbMaxY, lNode.aabbMaxZ,
200
+ );
201
+ let rDist = distSqPointAabb(
202
+ p.x, p.y, p.z,
203
+ rNode.aabbMinX, rNode.aabbMinY, rNode.aabbMinZ,
204
+ rNode.aabbMaxX, rNode.aabbMaxY, rNode.aabbMaxZ,
205
+ );
206
+ // Push the farther child first → closer popped first.
207
+ if (sp + 2u <= STACK_SIZE) {
208
+ if (lDist < rDist) {
209
+ stack[sp] = rightIdx; sp = sp + 1u;
210
+ stack[sp] = leftIdx; sp = sp + 1u;
211
+ } else {
212
+ stack[sp] = leftIdx; sp = sp + 1u;
213
+ stack[sp] = rightIdx; sp = sp + 1u;
214
+ }
215
+ }
216
+ }
217
+ }
218
+
219
+ // Signed distance: project (p - closestPoint) onto the closest
220
+ // triangle's normal. Positive ⇒ p is on the outward side.
221
+ let toPoint = p - bestPoint;
222
+ let dist = sqrt(bestDistSq);
223
+ let s = sign(dot(toPoint, bestNormal));
224
+ var signed: f32 = s * dist;
225
+
226
+ // Optional clip: keeps the histogram + ramp focused on near-surface
227
+ // points. Past ±maxRange the value pegs at the edge.
228
+ if (params.maxRange > 0.0) {
229
+ let mr = params.maxRange;
230
+ if (signed > mr) { signed = mr; }
231
+ if (signed < -mr) { signed = -mr; }
232
+ }
233
+
234
+ deviations[pi] = signed;
235
+ }
236
+ `;
237
+ //# sourceMappingURL=deviation-shader.wgsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deviation-shader.wgsl.js","sourceRoot":"","sources":["../../src/deviation/deviation-shader.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmN/C,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Per-triangle BVH for closest-point queries (BIM ↔ scan deviation).
3
+ *
4
+ * Distinct from `bvh.ts` which is a per-mesh BVH used for raycasting:
5
+ * for closest-point we want fine-grained pruning, so each leaf holds
6
+ * a contiguous range of triangles. The build also flattens the tree
7
+ * to a `Float32Array` ready for direct GPU upload — no second pass.
8
+ *
9
+ * Node layout (32 bytes = 8 floats per node, packed Float32 + bitcast u32):
10
+ * [0..2] aabbMin (vec3<f32>)
11
+ * [3] childA / triStart (u32 bitcast: leaf flag = high bit)
12
+ * [4..6] aabbMax (vec3<f32>)
13
+ * [7] childB / triCount (u32 bitcast)
14
+ *
15
+ * Triangle layout (48 bytes = 12 floats per triangle):
16
+ * [0..2] v0 (vec3)
17
+ * [3..5] v1 (vec3)
18
+ * [6..8] v2 (vec3)
19
+ * [9..11] normalised face normal (vec3) — sign convention: outward
20
+ * from mesh interior assuming CCW winding (right-hand rule)
21
+ *
22
+ * Maximum supported triangles: ~2³¹ (one bit reserved for the leaf
23
+ * flag). Real BIMs top out around 10⁷ triangles before other bottle-
24
+ * necks kick in.
25
+ */
26
+ import type { MeshData } from '@ifc-lite/geometry';
27
+ export interface TriangleBVHResult {
28
+ /** Flat node buffer (Float32Array). Each node is 8 floats. */
29
+ nodes: Float32Array;
30
+ /** Flat triangle buffer (Float32Array). Each triangle is 12 floats. */
31
+ triangles: Float32Array;
32
+ /** Total number of triangles. */
33
+ triangleCount: number;
34
+ /** Total number of nodes (root at index 0). */
35
+ nodeCount: number;
36
+ /** Number of source meshes folded into this BVH. */
37
+ meshCount: number;
38
+ /** Aggregate bounds of all triangles. */
39
+ bounds: {
40
+ min: [number, number, number];
41
+ max: [number, number, number];
42
+ };
43
+ }
44
+ /**
45
+ * Build the per-triangle BVH.
46
+ *
47
+ * Splits leaves until each holds at most `maxTrisPerLeaf` triangles
48
+ * (default 16, balancing tree depth vs. per-leaf work). Median split
49
+ * along the longest AABB axis — fast O(n log n) build, no SAH for v1.
50
+ *
51
+ * For typical BIMs (1M triangles) the build runs in ~1–3 seconds on
52
+ * the main thread. Acceptable since it only re-runs when the mesh
53
+ * set changes (load / federation update).
54
+ */
55
+ export declare function buildTriangleBVH(meshes: ReadonlyArray<MeshData>, options?: {
56
+ maxTrisPerLeaf?: number;
57
+ }): TriangleBVHResult;
58
+ //# sourceMappingURL=triangle-bvh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triangle-bvh.d.ts","sourceRoot":"","sources":["../../src/deviation/triangle-bvh.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,KAAK,EAAE,YAAY,CAAC;IACpB,uEAAuE;IACvE,SAAS,EAAE,YAAY,CAAC;IACxB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,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,CAAC;CAC1E;AAKD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC/B,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GACxC,iBAAiB,CA+MnB"}