@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,223 @@
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
+ import { POINT_QUAD_VERTS, POINT_VERTEX_BYTES } from './pointcloud/point-pipeline.js';
5
+ const POINT_PICK_MARKER = 0x80000000;
6
+ const POINT_PICK_MASK = 0x7fffffff;
7
+ export function decodePickSample(value) {
8
+ if (value === 0) {
9
+ return { meshIndexPlusOne: 0, pointExpressId: 0, kind: 'none' };
10
+ }
11
+ if ((value & POINT_PICK_MARKER) !== 0) {
12
+ return {
13
+ meshIndexPlusOne: 0,
14
+ pointExpressId: value & POINT_PICK_MASK,
15
+ kind: 'point',
16
+ };
17
+ }
18
+ return { meshIndexPlusOne: value, pointExpressId: 0, kind: 'mesh' };
19
+ }
20
+ // mat4x4 (64) + vec4 viewport (16) + vec4 sizing (16) + vec4 entityIdOverride (16)
21
+ const UNIFORM_BYTES = 112;
22
+ export class PointPicker {
23
+ device;
24
+ pipeline;
25
+ bindGroupLayout;
26
+ uniformBuffer;
27
+ bindGroup;
28
+ uniformScratch = new Float32Array(UNIFORM_BYTES / 4);
29
+ uniformU32 = new Uint32Array(this.uniformScratch.buffer);
30
+ destroyed = false;
31
+ constructor(device) {
32
+ this.device = device.getDevice();
33
+ this.bindGroupLayout = this.device.createBindGroupLayout({
34
+ entries: [
35
+ {
36
+ binding: 0,
37
+ visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
38
+ buffer: { type: 'uniform' },
39
+ },
40
+ ],
41
+ });
42
+ this.uniformBuffer = this.device.createBuffer({
43
+ size: UNIFORM_BYTES,
44
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
45
+ });
46
+ this.bindGroup = this.device.createBindGroup({
47
+ layout: this.bindGroupLayout,
48
+ entries: [{ binding: 0, resource: { buffer: this.uniformBuffer } }],
49
+ });
50
+ const shader = this.device.createShaderModule({
51
+ code: `
52
+ struct U {
53
+ viewProj: mat4x4<f32>,
54
+ viewport: vec4<f32>, // x, y = w, h; z, w = unused
55
+ sizing: vec4<f32>, // x = sizeMode, y = worldRadius,
56
+ // z = pointSizePx, w = clickToleranceExtraPx
57
+ // x = assetExpressId override (federation-aware globalId);
58
+ // 0 means "use the per-vertex entityId attribute".
59
+ entityIdOverride: vec4<u32>,
60
+ }
61
+ @binding(0) @group(0) var<uniform> u: U;
62
+
63
+ struct VIn {
64
+ @location(0) position: vec3<f32>,
65
+ @location(1) entityId: u32,
66
+ }
67
+
68
+ struct VOut {
69
+ @builtin(position) pos: vec4<f32>,
70
+ @location(0) @interpolate(flat) entityId: u32,
71
+ @location(1) quadUv: vec2<f32>,
72
+ }
73
+
74
+ @vertex
75
+ fn vs_main(input: VIn, @builtin(vertex_index) vId: u32) -> VOut {
76
+ var corners = array<vec2<f32>, 6>(
77
+ vec2<f32>(-1.0, -1.0),
78
+ vec2<f32>( 1.0, -1.0),
79
+ vec2<f32>( 1.0, 1.0),
80
+ vec2<f32>(-1.0, -1.0),
81
+ vec2<f32>( 1.0, 1.0),
82
+ vec2<f32>(-1.0, 1.0),
83
+ );
84
+ let corner = corners[vId];
85
+
86
+ var clip = u.viewProj * vec4<f32>(input.position, 1.0);
87
+
88
+ let sizeMode = u32(u.sizing.x);
89
+ let worldRadius = u.sizing.y;
90
+ let pointSizePx = u.sizing.z;
91
+ let extraPx = u.sizing.w;
92
+ let viewport = u.viewport.xy;
93
+
94
+ // halfPx is the RADIUS; pointSizePx is the user-facing diameter, so /2.
95
+ var halfPx: f32;
96
+ if (sizeMode == 0u) {
97
+ halfPx = max(0.5, pointSizePx * 0.5);
98
+ } else {
99
+ let edgePos = u.viewProj * vec4<f32>(input.position + vec3<f32>(worldRadius, 0.0, 0.0), 1.0);
100
+ let centerNdcX = clip.x / max(abs(clip.w), 1e-6);
101
+ let edgeNdcX = edgePos.x / max(abs(edgePos.w), 1e-6);
102
+ let projectedPx = abs(edgeNdcX - centerNdcX) * 0.5 * viewport.x;
103
+ if (sizeMode == 2u) {
104
+ halfPx = clamp(projectedPx, 0.5, max(0.5, pointSizePx * 0.5));
105
+ } else {
106
+ halfPx = max(0.5, projectedPx);
107
+ }
108
+ }
109
+ // Extra click-tolerance pixels on every side.
110
+ halfPx = halfPx + extraPx;
111
+
112
+ let halfClip = vec2<f32>(halfPx) / max(viewport, vec2<f32>(1.0)) * 2.0 * abs(clip.w);
113
+ clip.x = clip.x + corner.x * halfClip.x;
114
+ clip.y = clip.y + corner.y * halfClip.y;
115
+
116
+ var o: VOut;
117
+ o.pos = clip;
118
+ o.entityId = input.entityId;
119
+ o.quadUv = corner;
120
+ return o;
121
+ }
122
+
123
+ @fragment
124
+ fn fs_main(input: VOut) -> @location(0) u32 {
125
+ // Round mask — picking ignores the corner area outside the unit disc
126
+ // so users can't accidentally select a point by clicking 1.4 splat-
127
+ // radii away from its centre.
128
+ if (dot(input.quadUv, input.quadUv) > 1.0) {
129
+ discard;
130
+ }
131
+ // Prefer the asset-level expressId override when set (federation
132
+ // relabels apply post-stream so the per-vertex attribute can go
133
+ // stale). Fallback to the per-vertex value when no override is set.
134
+ let id = select(input.entityId, u.entityIdOverride.x, u.entityIdOverride.x != 0u);
135
+ return 0x80000000u | (id & 0x7FFFFFFFu);
136
+ }
137
+ `,
138
+ });
139
+ this.pipeline = this.device.createRenderPipeline({
140
+ layout: this.device.createPipelineLayout({ bindGroupLayouts: [this.bindGroupLayout] }),
141
+ vertex: {
142
+ module: shader,
143
+ entryPoint: 'vs_main',
144
+ buffers: [
145
+ {
146
+ arrayStride: POINT_VERTEX_BYTES,
147
+ stepMode: 'instance',
148
+ attributes: [
149
+ { shaderLocation: 0, offset: 0, format: 'float32x3' },
150
+ // Skip color (offset 12) and intensity (offset 16) — picker
151
+ // doesn't need them. EntityId lives at offset 20.
152
+ { shaderLocation: 1, offset: 20, format: 'uint32' },
153
+ ],
154
+ },
155
+ ],
156
+ },
157
+ fragment: {
158
+ module: shader,
159
+ entryPoint: 'fs_main',
160
+ targets: [{ format: 'r32uint' }],
161
+ },
162
+ primitive: { topology: 'triangle-list' },
163
+ depthStencil: {
164
+ format: 'depth32float',
165
+ depthWriteEnabled: true,
166
+ depthCompare: 'greater',
167
+ },
168
+ });
169
+ }
170
+ /**
171
+ * Draw point pick splats into the (already-open) render pass. The
172
+ * caller is responsible for clearing the color + depth attachments
173
+ * and ending the pass.
174
+ */
175
+ drawIntoPass(pass, nodes, viewProj, viewport, sizing) {
176
+ if (this.destroyed || nodes.length === 0)
177
+ return;
178
+ pass.setPipeline(this.pipeline);
179
+ pass.setBindGroup(0, this.bindGroup);
180
+ // Per-node uniform write so federation-relabelled IDs surface in
181
+ // the picker too. The per-vertex `entityId` attribute is baked at
182
+ // upload time and goes stale once the FederationRegistry assigns
183
+ // an idOffset to the model — the override forces the picker to
184
+ // emit the asset's CURRENT expressId regardless.
185
+ for (const node of nodes) {
186
+ this.writeUniforms(viewProj, viewport, sizing, node.expressId >>> 0);
187
+ for (const chunk of node.chunks) {
188
+ if (chunk.pointCount === 0)
189
+ continue;
190
+ pass.setVertexBuffer(0, chunk.vertexBuffer);
191
+ pass.draw(POINT_QUAD_VERTS, chunk.pointCount, 0, 0);
192
+ }
193
+ }
194
+ }
195
+ writeUniforms(viewProj, viewport, sizing, entityIdOverride) {
196
+ const u = this.uniformScratch;
197
+ const u32 = this.uniformU32;
198
+ u.set(viewProj.subarray(0, 16), 0);
199
+ u[16] = Math.max(1, viewport.width);
200
+ u[17] = Math.max(1, viewport.height);
201
+ u[18] = 0;
202
+ u[19] = 0;
203
+ u[20] = sizing.sizeMode;
204
+ u[21] = sizing.worldRadius;
205
+ u[22] = sizing.pointSizePx;
206
+ u[23] = sizing.clickTolerancePx;
207
+ // entityIdOverride at u32 offset 24..27. 0 means "use per-vertex
208
+ // attribute" (the shader's select(...) will pick the per-vertex
209
+ // value); any non-zero value wins.
210
+ u32[24] = entityIdOverride >>> 0;
211
+ u32[25] = 0;
212
+ u32[26] = 0;
213
+ u32[27] = 0;
214
+ this.device.queue.writeBuffer(this.uniformBuffer, 0, u.buffer, u.byteOffset, UNIFORM_BYTES);
215
+ }
216
+ destroy() {
217
+ if (this.destroyed)
218
+ return;
219
+ this.destroyed = true;
220
+ this.uniformBuffer.destroy();
221
+ }
222
+ }
223
+ //# sourceMappingURL=point-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"point-picker.js","sourceRoot":"","sources":["../src/point-picker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * GPU resources for one point cloud asset.
3
+ *
4
+ * Phase 1+ supports multi-chunk assets: streaming sources push chunks
5
+ * into a node one at a time, each becoming its own GPU vertex buffer.
6
+ * Each chunk is drawn with one draw call sharing the asset's bind group.
7
+ */
8
+ import type { PointCloudAsset } from '@ifc-lite/geometry';
9
+ import type { PointRenderPipeline } from './point-pipeline.js';
10
+ export interface PointCloudGpuChunk {
11
+ vertexBuffer: GPUBuffer;
12
+ /**
13
+ * Per-point signed-distance buffer. Always allocated alongside the
14
+ * vertex buffer (4 bytes per point) so the compute pass and splat
15
+ * pipeline can both bind it without a "deviation present?" branch.
16
+ * Initialised to zeros — `Renderer.computeDeviations` overwrites.
17
+ */
18
+ deviationBuffer: GPUBuffer;
19
+ pointCount: number;
20
+ bbox: {
21
+ min: [number, number, number];
22
+ max: [number, number, number];
23
+ };
24
+ }
25
+ /** Inputs to a single chunk upload. */
26
+ export interface PointCloudChunkInput {
27
+ positions: Float32Array;
28
+ /** RGB in 0..1; undefined → defaults to gray. */
29
+ colors?: Float32Array;
30
+ /** Per-point u8 LAS classification; undefined → 0. */
31
+ classifications?: Uint8Array;
32
+ /** Per-point u16 intensity; undefined → 0. */
33
+ intensities?: Uint16Array;
34
+ pointCount: number;
35
+ bbox: {
36
+ min: [number, number, number];
37
+ max: [number, number, number];
38
+ };
39
+ }
40
+ export interface PointCloudNodeMeta {
41
+ expressId: number;
42
+ ifcType?: string;
43
+ modelIndex?: number;
44
+ }
45
+ export interface PointCloudNode {
46
+ meta: PointCloudNodeMeta;
47
+ uniformBuffer: GPUBuffer;
48
+ bindGroup: GPUBindGroup;
49
+ chunks: PointCloudGpuChunk[];
50
+ bounds: {
51
+ min: [number, number, number];
52
+ max: [number, number, number];
53
+ };
54
+ pointCount: number;
55
+ }
56
+ /** Build an empty node — chunks are appended via `appendChunkToNode`. */
57
+ export declare function createNode(device: GPUDevice, pipeline: PointRenderPipeline, meta: PointCloudNodeMeta): PointCloudNode;
58
+ /** Convert a renderer-agnostic chunk into a GPU vertex buffer + metadata. */
59
+ export declare function appendChunkToNode(device: GPUDevice, node: PointCloudNode, chunk: PointCloudChunkInput): PointCloudGpuChunk;
60
+ /** One-shot upload — produces a node with a single GPU chunk. */
61
+ export declare function uploadAssetToGpu(device: GPUDevice, pipeline: PointRenderPipeline, asset: PointCloudAsset): PointCloudNode;
62
+ export declare function destroyNode(node: PointCloudNode): void;
63
+ //# sourceMappingURL=point-cloud-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"point-cloud-node.d.ts","sourceRoot":"","sources":["../../src/pointcloud/point-cloud-node.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAG/D,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,SAAS,CAAC;IACxB;;;;;OAKG;IACH,eAAe,EAAE,SAAS,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,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;CACxE;AAED,uCAAuC;AACvC,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,YAAY,CAAC;IACxB,iDAAiD;IACjD,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,sDAAsD;IACtD,eAAe,CAAC,EAAE,UAAU,CAAC;IAC7B,8CAA8C;IAC9C,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,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;CACxE;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,kBAAkB,CAAC;IACzB,aAAa,EAAE,SAAS,CAAC;IACzB,SAAS,EAAE,YAAY,CAAC;IACxB,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,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;IACzE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,yEAAyE;AACzE,wBAAgB,UAAU,CACxB,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,mBAAmB,EAC7B,IAAI,EAAE,kBAAkB,GACvB,cAAc,CAehB;AAYD,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,oBAAoB,GAC1B,kBAAkB,CAiFpB;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,eAAe,GACrB,cAAc,CAehB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAQtD"}
@@ -0,0 +1,149 @@
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
+ import { POINT_VERTEX_BYTES } from './point-pipeline.js';
5
+ /** Build an empty node — chunks are appended via `appendChunkToNode`. */
6
+ export function createNode(device, pipeline, meta) {
7
+ void device;
8
+ const uniformBuffer = pipeline.createUniformBuffer();
9
+ const bindGroup = pipeline.createBindGroup(uniformBuffer);
10
+ return {
11
+ meta,
12
+ uniformBuffer,
13
+ bindGroup,
14
+ chunks: [],
15
+ bounds: {
16
+ min: [Infinity, Infinity, Infinity],
17
+ max: [-Infinity, -Infinity, -Infinity],
18
+ },
19
+ pointCount: 0,
20
+ };
21
+ }
22
+ /**
23
+ * Per-page-session counter for the vertex-buffer class-byte
24
+ * diagnostic. Mirrors the host-side log in `pointCloudIngest.ts`
25
+ * so the two can be cross-checked: if the host log shows non-zero
26
+ * classes but the vertex log shows all 0, the packing path is
27
+ * dropping them.
28
+ */
29
+ const DEBUG_VERTEX_CLASS_LOG_LIMIT = 3;
30
+ let debugVertexClassLogs = 0;
31
+ /** Convert a renderer-agnostic chunk into a GPU vertex buffer + metadata. */
32
+ export function appendChunkToNode(device, node, chunk) {
33
+ const count = chunk.pointCount;
34
+ const bytes = new ArrayBuffer(count * POINT_VERTEX_BYTES);
35
+ const f32 = new Float32Array(bytes);
36
+ const u8 = new Uint8Array(bytes);
37
+ const u32 = new Uint32Array(bytes);
38
+ const positions = chunk.positions;
39
+ const colors = chunk.colors;
40
+ const classes = chunk.classifications;
41
+ const intensities = chunk.intensities;
42
+ const expressId = node.meta.expressId >>> 0;
43
+ for (let i = 0; i < count; i++) {
44
+ const fOff = i * 6;
45
+ f32[fOff] = positions[i * 3];
46
+ f32[fOff + 1] = positions[i * 3 + 1];
47
+ f32[fOff + 2] = positions[i * 3 + 2];
48
+ const byteOff = i * POINT_VERTEX_BYTES + 12;
49
+ if (colors) {
50
+ u8[byteOff] = clamp01(colors[i * 3]) * 255;
51
+ u8[byteOff + 1] = clamp01(colors[i * 3 + 1]) * 255;
52
+ u8[byteOff + 2] = clamp01(colors[i * 3 + 2]) * 255;
53
+ }
54
+ else {
55
+ u8[byteOff] = 200;
56
+ u8[byteOff + 1] = 200;
57
+ u8[byteOff + 2] = 200;
58
+ }
59
+ u8[byteOff + 3] = classes ? classes[i] : 0;
60
+ // intensity at offset +16, low 16 bits of a u32
61
+ u32[i * 6 + 4] = intensities ? intensities[i] & 0xffff : 0;
62
+ u32[i * 6 + 5] = expressId;
63
+ }
64
+ // Sanity-check the packed buffer: read back the class byte for
65
+ // the first few vertices so the console shows exactly what the
66
+ // splat shader will see at `rgbAndClass.a * 255`. Catches the
67
+ // case where the chunk had non-trivial classes but they got
68
+ // zeroed during packing (e.g. a buffer-view mismatch).
69
+ if (debugVertexClassLogs < DEBUG_VERTEX_CLASS_LOG_LIMIT && classes) {
70
+ debugVertexClassLogs++;
71
+ const sample = [];
72
+ for (let i = 0; i < Math.min(8, count); i++) {
73
+ sample.push(u8[i * POINT_VERTEX_BYTES + 15]);
74
+ }
75
+ console.log(`[pointcloud-debug] vertex-buffer chunk #${debugVertexClassLogs}: `
76
+ + `packed class bytes (offset +15) first8=[${sample.join(',')}]`);
77
+ }
78
+ const vertexBuffer = device.createBuffer({
79
+ size: bytes.byteLength,
80
+ // STORAGE so the deviation compute shader can read positions
81
+ // straight from the vertex buffer (avoids a duplicate copy).
82
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST | GPUBufferUsage.STORAGE,
83
+ });
84
+ device.queue.writeBuffer(vertexBuffer, 0, bytes);
85
+ // Pre-allocate the per-point deviation buffer (zero-initialised).
86
+ // Bound as a vertex attribute by the splat pipeline AND as a
87
+ // storage buffer by the deviation compute pass.
88
+ const deviationBuffer = device.createBuffer({
89
+ size: count * 4,
90
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
91
+ });
92
+ // Zero-init explicitly — WebGPU spec doesn't promise zeroed buffers
93
+ // and some implementations skip the initial clear when STORAGE is set.
94
+ device.queue.writeBuffer(deviationBuffer, 0, new Float32Array(count));
95
+ const gpuChunk = {
96
+ vertexBuffer,
97
+ deviationBuffer,
98
+ pointCount: count,
99
+ bbox: chunk.bbox,
100
+ };
101
+ node.chunks.push(gpuChunk);
102
+ node.pointCount += count;
103
+ growBounds(node.bounds, chunk.bbox);
104
+ return gpuChunk;
105
+ }
106
+ /** One-shot upload — produces a node with a single GPU chunk. */
107
+ export function uploadAssetToGpu(device, pipeline, asset) {
108
+ const node = createNode(device, pipeline, {
109
+ expressId: asset.expressId,
110
+ ifcType: asset.ifcType,
111
+ modelIndex: asset.modelIndex,
112
+ });
113
+ appendChunkToNode(device, node, {
114
+ positions: asset.chunk.positions,
115
+ colors: asset.chunk.colors,
116
+ classifications: asset.chunk.classifications,
117
+ intensities: asset.chunk.intensities,
118
+ pointCount: asset.chunk.pointCount,
119
+ bbox: asset.chunk.bbox,
120
+ });
121
+ return node;
122
+ }
123
+ export function destroyNode(node) {
124
+ for (const chunk of node.chunks) {
125
+ chunk.vertexBuffer.destroy();
126
+ chunk.deviationBuffer.destroy();
127
+ }
128
+ node.uniformBuffer.destroy();
129
+ node.chunks = [];
130
+ node.pointCount = 0;
131
+ }
132
+ function growBounds(bounds, bbox) {
133
+ if (bbox.min[0] < bounds.min[0])
134
+ bounds.min[0] = bbox.min[0];
135
+ if (bbox.min[1] < bounds.min[1])
136
+ bounds.min[1] = bbox.min[1];
137
+ if (bbox.min[2] < bounds.min[2])
138
+ bounds.min[2] = bbox.min[2];
139
+ if (bbox.max[0] > bounds.max[0])
140
+ bounds.max[0] = bbox.max[0];
141
+ if (bbox.max[1] > bounds.max[1])
142
+ bounds.max[1] = bbox.max[1];
143
+ if (bbox.max[2] > bounds.max[2])
144
+ bounds.max[2] = bbox.max[2];
145
+ }
146
+ function clamp01(v) {
147
+ return v < 0 ? 0 : v > 1 ? 1 : v;
148
+ }
149
+ //# sourceMappingURL=point-cloud-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"point-cloud-node.js","sourceRoot":"","sources":["../../src/pointcloud/point-cloud-node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Manages point cloud assets in the renderer.
3
+ *
4
+ * Supports two ingest modes:
5
+ * - One-shot: `addAsset(asset)` for inline IFCx pointclouds.
6
+ * - Streaming: `beginAsset(meta) → handle`, `appendChunk(handle, chunk)`,
7
+ * `endAsset(handle)` for LAS/LAZ files arriving in chunks.
8
+ *
9
+ * The renderer owns the pipeline, per-asset GPU resources, and the per-frame
10
+ * draw call. Designed to slot into the existing `Renderer.render()` so points
11
+ * share the depth buffer and section-plane state with triangle meshes.
12
+ */
13
+ import type { PointCloudAsset } from '@ifc-lite/geometry';
14
+ import { type PointCloudChunkInput, type PointCloudNode, type PointCloudNodeMeta } from './point-cloud-node.js';
15
+ import { type PointColorMode, type PointSizeMode } from './point-cloud-uniforms.js';
16
+ export interface ResolvedSectionPlane {
17
+ normal: [number, number, number];
18
+ distance: number;
19
+ enabled: boolean;
20
+ flipped?: boolean;
21
+ }
22
+ export type { PointColorMode, PointSizeMode };
23
+ /**
24
+ * How to size a splat on screen.
25
+ * - `fixed-px` every splat is `pointSize` pixels wide
26
+ * - `adaptive-world` splat covers `worldRadius` metres in source space,
27
+ * projected each frame (closer → bigger)
28
+ * - `attenuated` adaptive but clamped between 1 px and `pointSize`
29
+ * so splats stay visible at the far plane and don't
30
+ * blow up to half the screen when you nose into the
31
+ * cloud — usually the best default for nav.
32
+ */
33
+ export interface PointCloudDrawState {
34
+ /** column-major view-projection matrix (16 floats) */
35
+ viewProj: Float32Array;
36
+ /** Section plane already resolved by the main render path. */
37
+ sectionPlane?: ResolvedSectionPlane | null;
38
+ /** Viewport size in pixels — needed by the splat shader to convert
39
+ * pixel sizes into clip-space offsets. */
40
+ viewport?: {
41
+ width: number;
42
+ height: number;
43
+ };
44
+ }
45
+ export interface PointCloudRenderOptions {
46
+ /** How to color points each frame. Defaults to 'rgb'. */
47
+ colorMode?: PointColorMode;
48
+ /** RGBA in 0..1, used when colorMode === 'fixed'. */
49
+ fixedColor?: [number, number, number, number];
50
+ /** Splat size in pixels (mode='fixed-px'/'attenuated') or maximum size cap. */
51
+ pointSize?: number;
52
+ /** Splat sizing strategy. Defaults to `attenuated`. */
53
+ sizeMode?: PointSizeMode;
54
+ /** World-space splat radius in metres for adaptive / attenuated modes.
55
+ * Defaults to 0.02 m which works well for typical 5–20 mm scan spacing. */
56
+ worldRadius?: number;
57
+ /** Render splats as discs instead of squares. Defaults to true. */
58
+ roundShape?: boolean;
59
+ /**
60
+ * Per-ASPRS-class visibility bitmask. Bit `i` set → class `i` is
61
+ * visible. Defaults to `0xFFFFFFFF` (all 32 classes shown). Only
62
+ * affects points carrying classifications; meshes ignore it.
63
+ * Stored as an unsigned 32-bit integer in the uniform block.
64
+ */
65
+ classMask?: number;
66
+ /**
67
+ * Stride-cull factor for the splat shader: 1 = render every point,
68
+ * 2 = every other, 4 = every fourth, etc. Used by the section-plane
69
+ * preview path so dragging a slider over a 100M-point scan stays
70
+ * responsive — UI flips this to e.g. 4 on drag start and back to 1
71
+ * on drag end. Default 1.
72
+ */
73
+ previewStride?: number;
74
+ /**
75
+ * BIM↔scan deviation heatmap range. `centerOffset` shifts the
76
+ * "white" point off zero (handy when a scan has a global offset
77
+ * from the model); `halfRange` is the metres mapped to ±1 on the
78
+ * blue→white→red ramp. Defaults to (0, 0.05) → ±5cm.
79
+ * Only consulted when `colorMode === 'deviation'`.
80
+ */
81
+ deviationRange?: {
82
+ centerOffset: number;
83
+ halfRange: number;
84
+ };
85
+ }
86
+ export interface PointCloudAssetHandle {
87
+ readonly id: number;
88
+ }
89
+ export declare class PointCloudRenderer {
90
+ private device;
91
+ private pipeline;
92
+ private nodes;
93
+ private nodeOwners;
94
+ private nextHandleId;
95
+ private uniformScratch;
96
+ private uniformScratchU32;
97
+ private options;
98
+ constructor(device: GPUDevice, colorFormat: GPUTextureFormat, depthFormat: GPUTextureFormat, sampleCount: number);
99
+ setOptions(opts: PointCloudRenderOptions): void;
100
+ getOptions(): Readonly<Required<PointCloudRenderOptions>>;
101
+ /**
102
+ * Replace every IFCx-owned asset with `assets`. Streamed assets are
103
+ * untouched. Use this from the viewer's IFCx sync hook.
104
+ */
105
+ setAssets(assets: ReadonlyArray<PointCloudAsset>): void;
106
+ addAsset(asset: PointCloudAsset): PointCloudAssetHandle;
107
+ /** Open an empty asset that chunks will be appended to. */
108
+ beginAsset(meta: PointCloudNodeMeta): PointCloudAssetHandle;
109
+ appendChunk(handle: PointCloudAssetHandle, chunk: PointCloudChunkInput): void;
110
+ /** Mark streaming complete. No-op for now — kept for symmetry. */
111
+ endAsset(handle: PointCloudAssetHandle): void;
112
+ removeAsset(handle: PointCloudAssetHandle): void;
113
+ /**
114
+ * Reassign a streamed asset's `expressId` after upload — used by
115
+ * `useIfcFederation` when the FederationRegistry hands out an
116
+ * `idOffset` for the model. The shader reads expressId from a
117
+ * per-asset uniform (flags.x), so this is just a metadata update;
118
+ * the next frame writes the new value into the GPU uniform without
119
+ * touching the per-vertex attributes.
120
+ */
121
+ relabelAsset(handle: PointCloudAssetHandle, newExpressId: number): void;
122
+ clear(): void;
123
+ private clearOwner;
124
+ hasAssets(): boolean;
125
+ getNodeCount(): number;
126
+ /**
127
+ * Iterate every uploaded node. Exposed so the deviation compute
128
+ * pass can reach each node's vertex + deviation buffers without
129
+ * the renderer having to mirror its internal map.
130
+ */
131
+ getInternalNodes(): Iterable<PointCloudNode>;
132
+ /** Total number of points currently uploaded across all assets. */
133
+ getPointCount(): number;
134
+ getBounds(): {
135
+ min: [number, number, number];
136
+ max: [number, number, number];
137
+ } | null;
138
+ /**
139
+ * Issue draw calls into an already-open render pass. The caller owns
140
+ * the encoder/pass and is responsible for the depth attachment.
141
+ */
142
+ draw(pass: GPURenderPassEncoder, state: PointCloudDrawState): void;
143
+ /**
144
+ * Resolve a packed objectId rgba8 sample back to the asset that owns it.
145
+ * Returns null when the sample doesn't match any asset's expressId.
146
+ */
147
+ resolvePick(expressId: number): {
148
+ handle: PointCloudAssetHandle;
149
+ meta: PointCloudNodeMeta;
150
+ } | null;
151
+ /**
152
+ * Snapshot of nodes shaped for the picker — only the data the GPU
153
+ * picking pass actually needs (expressId, modelIndex, chunk vertex
154
+ * buffers + counts). Returns a fresh array; callers may iterate
155
+ * freely without worrying about mutation during a pick.
156
+ */
157
+ getPickNodes(): Array<{
158
+ expressId: number;
159
+ modelIndex?: number;
160
+ chunks: Array<{
161
+ vertexBuffer: GPUBuffer;
162
+ pointCount: number;
163
+ }>;
164
+ }>;
165
+ }
166
+ //# sourceMappingURL=point-cloud-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"point-cloud-renderer.d.ts","sourceRoot":"","sources":["../../src/pointcloud/point-cloud-renderer.ts"],"names":[],"mappings":"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"}