@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.
- package/dist/edl-pass.d.ts +52 -0
- package/dist/edl-pass.d.ts.map +1 -0
- package/dist/edl-pass.js +204 -0
- package/dist/edl-pass.js.map +1 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +314 -19
- package/dist/index.js.map +1 -1
- package/dist/picker.d.ts +22 -3
- package/dist/picker.d.ts.map +1 -1
- package/dist/picker.js +48 -8
- package/dist/picker.js.map +1 -1
- package/dist/picking-manager.d.ts +14 -1
- package/dist/picking-manager.d.ts.map +1 -1
- package/dist/picking-manager.js +13 -1
- package/dist/picking-manager.js.map +1 -1
- package/dist/point-picker.d.ts +61 -0
- package/dist/point-picker.d.ts.map +1 -0
- package/dist/point-picker.js +223 -0
- package/dist/point-picker.js.map +1 -0
- package/dist/pointcloud/point-cloud-node.d.ts +56 -0
- package/dist/pointcloud/point-cloud-node.d.ts.map +1 -0
- package/dist/pointcloud/point-cloud-node.js +112 -0
- package/dist/pointcloud/point-cloud-node.js.map +1 -0
- package/dist/pointcloud/point-cloud-renderer.d.ts +135 -0
- package/dist/pointcloud/point-cloud-renderer.d.ts.map +1 -0
- package/dist/pointcloud/point-cloud-renderer.js +296 -0
- package/dist/pointcloud/point-cloud-renderer.js.map +1 -0
- package/dist/pointcloud/point-pipeline.d.ts +25 -0
- package/dist/pointcloud/point-pipeline.d.ts.map +1 -0
- package/dist/pointcloud/point-pipeline.js +111 -0
- package/dist/pointcloud/point-pipeline.js.map +1 -0
- package/dist/pointcloud/point-shader.wgsl.d.ts +22 -0
- package/dist/pointcloud/point-shader.wgsl.d.ts.map +1 -0
- package/dist/pointcloud/point-shader.wgsl.js +212 -0
- package/dist/pointcloud/point-shader.wgsl.js.map +1 -0
- package/dist/types.d.ts +15 -4
- package/dist/types.d.ts.map +1 -1
- 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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"}
|