@ifc-lite/renderer 1.17.0 → 1.18.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 (39) hide show
  1. package/dist/edl-pass.d.ts +52 -0
  2. package/dist/edl-pass.d.ts.map +1 -0
  3. package/dist/edl-pass.js +204 -0
  4. package/dist/edl-pass.js.map +1 -0
  5. package/dist/index.d.ts +77 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +314 -19
  8. package/dist/index.js.map +1 -1
  9. package/dist/picker.d.ts +22 -3
  10. package/dist/picker.d.ts.map +1 -1
  11. package/dist/picker.js +48 -8
  12. package/dist/picker.js.map +1 -1
  13. package/dist/picking-manager.d.ts +14 -1
  14. package/dist/picking-manager.d.ts.map +1 -1
  15. package/dist/picking-manager.js +13 -1
  16. package/dist/picking-manager.js.map +1 -1
  17. package/dist/point-picker.d.ts +61 -0
  18. package/dist/point-picker.d.ts.map +1 -0
  19. package/dist/point-picker.js +223 -0
  20. package/dist/point-picker.js.map +1 -0
  21. package/dist/pointcloud/point-cloud-node.d.ts +56 -0
  22. package/dist/pointcloud/point-cloud-node.d.ts.map +1 -0
  23. package/dist/pointcloud/point-cloud-node.js +112 -0
  24. package/dist/pointcloud/point-cloud-node.js.map +1 -0
  25. package/dist/pointcloud/point-cloud-renderer.d.ts +135 -0
  26. package/dist/pointcloud/point-cloud-renderer.d.ts.map +1 -0
  27. package/dist/pointcloud/point-cloud-renderer.js +296 -0
  28. package/dist/pointcloud/point-cloud-renderer.js.map +1 -0
  29. package/dist/pointcloud/point-pipeline.d.ts +25 -0
  30. package/dist/pointcloud/point-pipeline.d.ts.map +1 -0
  31. package/dist/pointcloud/point-pipeline.js +111 -0
  32. package/dist/pointcloud/point-pipeline.js.map +1 -0
  33. package/dist/pointcloud/point-shader.wgsl.d.ts +22 -0
  34. package/dist/pointcloud/point-shader.wgsl.d.ts.map +1 -0
  35. package/dist/pointcloud/point-shader.wgsl.js +212 -0
  36. package/dist/pointcloud/point-shader.wgsl.js.map +1 -0
  37. package/dist/types.d.ts +15 -4
  38. package/dist/types.d.ts.map +1 -1
  39. package/package.json +3 -3
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Point cloud picker — sibling pipeline to `Picker` that draws splats
3
+ * into the same r32uint object-id target so points and meshes occlude
4
+ * each other correctly during a pick.
5
+ *
6
+ * Disambiguation: points emit `0x80000000 | (expressId & 0x7FFFFFFF)`,
7
+ * meshes emit `meshIndex+1` (always under 100K). The reader checks bit
8
+ * 31 to know which path produced the hit.
9
+ *
10
+ * Click tolerance: the picker pipeline inflates each splat by an extra
11
+ * ~2 px over its on-screen size. This makes picking forgiving even for
12
+ * sub-pixel splats (adaptive-world mode, dense scans) without changing
13
+ * the rendered look.
14
+ */
15
+ import type { WebGPUDevice } from './device.js';
16
+ export interface PointPickNode {
17
+ expressId: number;
18
+ modelIndex?: number;
19
+ chunks: ReadonlyArray<{
20
+ vertexBuffer: GPUBuffer;
21
+ pointCount: number;
22
+ }>;
23
+ }
24
+ /** Decode a r32uint sample from the picker target. */
25
+ export interface DecodedPickSample {
26
+ /** Mesh index (1-based) when bit 31 is clear and value > 0; 0 = no hit. */
27
+ meshIndexPlusOne: number;
28
+ /** Federated expressId when bit 31 is set; 0 otherwise. */
29
+ pointExpressId: number;
30
+ /** Convenience: which discipline produced the hit. */
31
+ kind: 'mesh' | 'point' | 'none';
32
+ }
33
+ export declare function decodePickSample(value: number): DecodedPickSample;
34
+ export declare class PointPicker {
35
+ private device;
36
+ private pipeline;
37
+ private bindGroupLayout;
38
+ private uniformBuffer;
39
+ private bindGroup;
40
+ private uniformScratch;
41
+ private uniformU32;
42
+ private destroyed;
43
+ constructor(device: WebGPUDevice);
44
+ /**
45
+ * Draw point pick splats into the (already-open) render pass. The
46
+ * caller is responsible for clearing the color + depth attachments
47
+ * and ending the pass.
48
+ */
49
+ drawIntoPass(pass: GPURenderPassEncoder, nodes: ReadonlyArray<PointPickNode>, viewProj: Float32Array, viewport: {
50
+ width: number;
51
+ height: number;
52
+ }, sizing: {
53
+ sizeMode: 0 | 1 | 2;
54
+ worldRadius: number;
55
+ pointSizePx: number;
56
+ clickTolerancePx: number;
57
+ }): void;
58
+ private writeUniforms;
59
+ destroy(): void;
60
+ }
61
+ //# sourceMappingURL=point-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"point-picker.d.ts","sourceRoot":"","sources":["../src/point-picker.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;QAAE,YAAY,EAAE,SAAS,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxE;AAKD,sDAAsD;AACtD,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,cAAc,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;CACjC;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAYjE;AAKD,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,cAAc,CAAuC;IAC7D,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,YAAY;IAiJhC;;;;OAIG;IACH,YAAY,CACV,IAAI,EAAE,oBAAoB,EAC1B,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,EACnC,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAC3C,MAAM,EAAE;QAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,GAClG,IAAI;IAmBP,OAAO,CAAC,aAAa;IA2BrB,OAAO,IAAI,IAAI;CAKhB"}
@@ -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,56 @@
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
+ pointCount: number;
13
+ bbox: {
14
+ min: [number, number, number];
15
+ max: [number, number, number];
16
+ };
17
+ }
18
+ /** Inputs to a single chunk upload. */
19
+ export interface PointCloudChunkInput {
20
+ positions: Float32Array;
21
+ /** RGB in 0..1; undefined → defaults to gray. */
22
+ colors?: Float32Array;
23
+ /** Per-point u8 LAS classification; undefined → 0. */
24
+ classifications?: Uint8Array;
25
+ /** Per-point u16 intensity; undefined → 0. */
26
+ intensities?: Uint16Array;
27
+ pointCount: number;
28
+ bbox: {
29
+ min: [number, number, number];
30
+ max: [number, number, number];
31
+ };
32
+ }
33
+ export interface PointCloudNodeMeta {
34
+ expressId: number;
35
+ ifcType?: string;
36
+ modelIndex?: number;
37
+ }
38
+ export interface PointCloudNode {
39
+ meta: PointCloudNodeMeta;
40
+ uniformBuffer: GPUBuffer;
41
+ bindGroup: GPUBindGroup;
42
+ chunks: PointCloudGpuChunk[];
43
+ bounds: {
44
+ min: [number, number, number];
45
+ max: [number, number, number];
46
+ };
47
+ pointCount: number;
48
+ }
49
+ /** Build an empty node — chunks are appended via `appendChunkToNode`. */
50
+ export declare function createNode(device: GPUDevice, pipeline: PointRenderPipeline, meta: PointCloudNodeMeta): PointCloudNode;
51
+ /** Convert a renderer-agnostic chunk into a GPU vertex buffer + metadata. */
52
+ export declare function appendChunkToNode(device: GPUDevice, node: PointCloudNode, chunk: PointCloudChunkInput): PointCloudGpuChunk;
53
+ /** One-shot upload — produces a node with a single GPU chunk. */
54
+ export declare function uploadAssetToGpu(device: GPUDevice, pipeline: PointRenderPipeline, asset: PointCloudAsset): PointCloudNode;
55
+ export declare function destroyNode(node: PointCloudNode): void;
56
+ //# 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,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;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,oBAAoB,GAC1B,kBAAkB,CAkDpB;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,CAOtD"}
@@ -0,0 +1,112 @@
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
+ /** Convert a renderer-agnostic chunk into a GPU vertex buffer + metadata. */
23
+ export function appendChunkToNode(device, node, chunk) {
24
+ const count = chunk.pointCount;
25
+ const bytes = new ArrayBuffer(count * POINT_VERTEX_BYTES);
26
+ const f32 = new Float32Array(bytes);
27
+ const u8 = new Uint8Array(bytes);
28
+ const u32 = new Uint32Array(bytes);
29
+ const positions = chunk.positions;
30
+ const colors = chunk.colors;
31
+ const classes = chunk.classifications;
32
+ const intensities = chunk.intensities;
33
+ const expressId = node.meta.expressId >>> 0;
34
+ for (let i = 0; i < count; i++) {
35
+ const fOff = i * 6;
36
+ f32[fOff] = positions[i * 3];
37
+ f32[fOff + 1] = positions[i * 3 + 1];
38
+ f32[fOff + 2] = positions[i * 3 + 2];
39
+ const byteOff = i * POINT_VERTEX_BYTES + 12;
40
+ if (colors) {
41
+ u8[byteOff] = clamp01(colors[i * 3]) * 255;
42
+ u8[byteOff + 1] = clamp01(colors[i * 3 + 1]) * 255;
43
+ u8[byteOff + 2] = clamp01(colors[i * 3 + 2]) * 255;
44
+ }
45
+ else {
46
+ u8[byteOff] = 200;
47
+ u8[byteOff + 1] = 200;
48
+ u8[byteOff + 2] = 200;
49
+ }
50
+ u8[byteOff + 3] = classes ? classes[i] : 0;
51
+ // intensity at offset +16, low 16 bits of a u32
52
+ u32[i * 6 + 4] = intensities ? intensities[i] & 0xffff : 0;
53
+ u32[i * 6 + 5] = expressId;
54
+ }
55
+ const vertexBuffer = device.createBuffer({
56
+ size: bytes.byteLength,
57
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
58
+ });
59
+ device.queue.writeBuffer(vertexBuffer, 0, bytes);
60
+ const gpuChunk = {
61
+ vertexBuffer,
62
+ pointCount: count,
63
+ bbox: chunk.bbox,
64
+ };
65
+ node.chunks.push(gpuChunk);
66
+ node.pointCount += count;
67
+ growBounds(node.bounds, chunk.bbox);
68
+ return gpuChunk;
69
+ }
70
+ /** One-shot upload — produces a node with a single GPU chunk. */
71
+ export function uploadAssetToGpu(device, pipeline, asset) {
72
+ const node = createNode(device, pipeline, {
73
+ expressId: asset.expressId,
74
+ ifcType: asset.ifcType,
75
+ modelIndex: asset.modelIndex,
76
+ });
77
+ appendChunkToNode(device, node, {
78
+ positions: asset.chunk.positions,
79
+ colors: asset.chunk.colors,
80
+ classifications: asset.chunk.classifications,
81
+ intensities: asset.chunk.intensities,
82
+ pointCount: asset.chunk.pointCount,
83
+ bbox: asset.chunk.bbox,
84
+ });
85
+ return node;
86
+ }
87
+ export function destroyNode(node) {
88
+ for (const chunk of node.chunks) {
89
+ chunk.vertexBuffer.destroy();
90
+ }
91
+ node.uniformBuffer.destroy();
92
+ node.chunks = [];
93
+ node.pointCount = 0;
94
+ }
95
+ function growBounds(bounds, bbox) {
96
+ if (bbox.min[0] < bounds.min[0])
97
+ bounds.min[0] = bbox.min[0];
98
+ if (bbox.min[1] < bounds.min[1])
99
+ bounds.min[1] = bbox.min[1];
100
+ if (bbox.min[2] < bounds.min[2])
101
+ bounds.min[2] = bbox.min[2];
102
+ if (bbox.max[0] > bounds.max[0])
103
+ bounds.max[0] = bbox.max[0];
104
+ if (bbox.max[1] > bounds.max[1])
105
+ bounds.max[1] = bbox.max[1];
106
+ if (bbox.max[2] > bounds.max[2])
107
+ bounds.max[2] = bbox.max[2];
108
+ }
109
+ function clamp01(v) {
110
+ return v < 0 ? 0 : v > 1 ? 1 : v;
111
+ }
112
+ //# 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,135 @@
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 PointCloudNodeMeta } from './point-cloud-node.js';
15
+ export interface ResolvedSectionPlane {
16
+ normal: [number, number, number];
17
+ distance: number;
18
+ enabled: boolean;
19
+ flipped?: boolean;
20
+ }
21
+ export type PointColorMode = 'rgb' | 'classification' | 'intensity' | 'height' | 'fixed';
22
+ /**
23
+ * How to size a splat on screen.
24
+ * - `fixed-px` every splat is `pointSize` pixels wide
25
+ * - `adaptive-world` splat covers `worldRadius` metres in source space,
26
+ * projected each frame (closer → bigger)
27
+ * - `attenuated` adaptive but clamped between 1 px and `pointSize`
28
+ * so splats stay visible at the far plane and don't
29
+ * blow up to half the screen when you nose into the
30
+ * cloud — usually the best default for nav.
31
+ */
32
+ export type PointSizeMode = 'fixed-px' | 'adaptive-world' | 'attenuated';
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
+ export interface PointCloudAssetHandle {
61
+ readonly id: number;
62
+ }
63
+ export declare class PointCloudRenderer {
64
+ private device;
65
+ private pipeline;
66
+ private nodes;
67
+ private nodeOwners;
68
+ private nextHandleId;
69
+ private uniformScratch;
70
+ private uniformScratchU32;
71
+ private options;
72
+ constructor(device: GPUDevice, colorFormat: GPUTextureFormat, depthFormat: GPUTextureFormat, sampleCount: number);
73
+ setOptions(opts: PointCloudRenderOptions): void;
74
+ getOptions(): Readonly<Required<PointCloudRenderOptions>>;
75
+ /**
76
+ * Replace every IFCx-owned asset with `assets`. Streamed assets are
77
+ * untouched. Use this from the viewer's IFCx sync hook.
78
+ */
79
+ setAssets(assets: ReadonlyArray<PointCloudAsset>): void;
80
+ addAsset(asset: PointCloudAsset): PointCloudAssetHandle;
81
+ /** Open an empty asset that chunks will be appended to. */
82
+ beginAsset(meta: PointCloudNodeMeta): PointCloudAssetHandle;
83
+ appendChunk(handle: PointCloudAssetHandle, chunk: PointCloudChunkInput): void;
84
+ /** Mark streaming complete. No-op for now — kept for symmetry. */
85
+ endAsset(handle: PointCloudAssetHandle): void;
86
+ removeAsset(handle: PointCloudAssetHandle): void;
87
+ /**
88
+ * Reassign a streamed asset's `expressId` after upload — used by
89
+ * `useIfcFederation` when the FederationRegistry hands out an
90
+ * `idOffset` for the model. The shader reads expressId from a
91
+ * per-asset uniform (flags.x), so this is just a metadata update;
92
+ * the next frame writes the new value into the GPU uniform without
93
+ * touching the per-vertex attributes.
94
+ */
95
+ relabelAsset(handle: PointCloudAssetHandle, newExpressId: number): void;
96
+ clear(): void;
97
+ private clearOwner;
98
+ hasAssets(): boolean;
99
+ getNodeCount(): number;
100
+ /** Total number of points currently uploaded across all assets. */
101
+ getPointCount(): number;
102
+ getBounds(): {
103
+ min: [number, number, number];
104
+ max: [number, number, number];
105
+ } | null;
106
+ /**
107
+ * Issue draw calls into an already-open render pass. The caller owns
108
+ * the encoder/pass and is responsible for the depth attachment.
109
+ */
110
+ draw(pass: GPURenderPassEncoder, state: PointCloudDrawState): void;
111
+ private writeUniforms;
112
+ /**
113
+ * Resolve a packed objectId rgba8 sample back to the asset that owns it.
114
+ * Returns null when the sample doesn't match any asset's expressId.
115
+ */
116
+ resolvePick(expressId: number): {
117
+ handle: PointCloudAssetHandle;
118
+ meta: PointCloudNodeMeta;
119
+ } | null;
120
+ /**
121
+ * Snapshot of nodes shaped for the picker — only the data the GPU
122
+ * picking pass actually needs (expressId, modelIndex, chunk vertex
123
+ * buffers + counts). Returns a fresh array; callers may iterate
124
+ * freely without worrying about mutation during a pick.
125
+ */
126
+ getPickNodes(): Array<{
127
+ expressId: number;
128
+ modelIndex?: number;
129
+ chunks: Array<{
130
+ vertexBuffer: GPUBuffer;
131
+ pointCount: number;
132
+ }>;
133
+ }>;
134
+ }
135
+ //# 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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"}