@ifc-lite/renderer 1.40.0 → 1.41.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/picking-manager.d.ts +33 -5
- package/dist/picking-manager.d.ts.map +1 -1
- package/dist/picking-manager.js +162 -101
- package/dist/picking-manager.js.map +1 -1
- package/dist/scene-rect-select.d.ts +51 -0
- package/dist/scene-rect-select.d.ts.map +1 -0
- package/dist/scene-rect-select.js +207 -0
- package/dist/scene-rect-select.js.map +1 -0
- package/dist/scene.d.ts +15 -0
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +35 -0
- package/dist/scene.js.map +1 -1
- package/package.json +1 -1
|
@@ -31,6 +31,22 @@ export declare class PickingManager {
|
|
|
31
31
|
* Update the picker reference (e.g., after init)
|
|
32
32
|
*/
|
|
33
33
|
setPicker(picker: Picker | null): void;
|
|
34
|
+
/**
|
|
35
|
+
* Prepare batched geometry for a GPU pick pass, shared by `pick()` and
|
|
36
|
+
* `pickRect()`.
|
|
37
|
+
*
|
|
38
|
+
* Returns `'cpu'` when the caller must fall back to a CPU test — either JS
|
|
39
|
+
* geometry data was released, or hydrating an individual mesh per visible
|
|
40
|
+
* piece would exceed the pick-mesh budget. Returns `'gpu'` after hydrating
|
|
41
|
+
* whatever was missing, so a following `scene.getMeshes()` covers every
|
|
42
|
+
* visible entity.
|
|
43
|
+
*
|
|
44
|
+
* Both pick paths MUST route through this. `pickRect` used to read
|
|
45
|
+
* `scene.getMeshes()` directly, which on a batched model is empty or
|
|
46
|
+
* partial, so rectangle select returned an empty set no matter how many
|
|
47
|
+
* elements the rect covered (#1904).
|
|
48
|
+
*/
|
|
49
|
+
private prepareBatchedPick;
|
|
34
50
|
/**
|
|
35
51
|
* Pick object at screen coordinates
|
|
36
52
|
* Respects visibility filtering so users can only select visible elements
|
|
@@ -51,11 +67,23 @@ export declare class PickingManager {
|
|
|
51
67
|
* filtered (per-asset visibility is binary and the asset count is
|
|
52
68
|
* tiny).
|
|
53
69
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
70
|
+
* Batched models take the same route as `pick()`: hydrate the
|
|
71
|
+
* missing individual meshes when that is affordable, otherwise fall
|
|
72
|
+
* back to `Scene.selectRect`, which over-selects relative to the
|
|
73
|
+
* pixel-exact GPU pass in three ways: it is bounding-box granular
|
|
74
|
+
* (a rect over empty space inside an element's bounds selects it),
|
|
75
|
+
* it has no depth test (occluded entities are selected), and it
|
|
76
|
+
* drops a section-planed or cropped entity only when the whole box
|
|
77
|
+
* is clipped away, where the pick shader discards per fragment.
|
|
78
|
+
* Hidden and isolation filtering do apply there. Reading
|
|
79
|
+
* `scene.getMeshes()` unconditionally is what made this return an
|
|
80
|
+
* empty set on every batched model (#1904).
|
|
81
|
+
*
|
|
82
|
+
* Point clouds never depend on mesh hydration — splats render into
|
|
83
|
+
* the pick pass on their own — so the CPU branch still runs the GPU
|
|
84
|
+
* pass for them and unions the two results. Skipping it would drop
|
|
85
|
+
* point selection on any mixed scene big enough to miss the
|
|
86
|
+
* pick-mesh budget.
|
|
59
87
|
*/
|
|
60
88
|
pickRect(x0: number, y0: number, x1: number, y1: number, options?: PickOptions, clip?: PickClipState | null): Promise<Set<number>>;
|
|
61
89
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"picking-manager.d.ts","sourceRoot":"","sources":["../src/picking-manager.ts"],"names":[],"mappings":"AAIA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAC5B;IAAE,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GAChE,IAAI,CAAC;AAET,qBAAa,cAAc;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,oBAAoB,CAA+B;IAC3D,OAAO,CAAC,iBAAiB,CAAkC;gBAGvD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,iBAAiB,EACzB,oBAAoB,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI;IAStD,+EAA+E;IAC/E,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI;IAI9D;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAItC;;;;;;;OAOG;IACG,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"picking-manager.d.ts","sourceRoot":"","sources":["../src/picking-manager.ts"],"names":[],"mappings":"AAIA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAC5B;IAAE,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GAChE,IAAI,CAAC;AAET,qBAAa,cAAc;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,oBAAoB,CAA+B;IAC3D,OAAO,CAAC,iBAAiB,CAAkC;gBAGvD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,iBAAiB,EACzB,oBAAoB,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI;IAStD,+EAA+E;IAC/E,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI;IAI9D;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAItC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,kBAAkB;IAoF1B;;;;;;;OAOG;IACG,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAmEhH;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACG,QAAQ,CACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,WAAW,EACrB,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,GAC5B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;CAsF1B"}
|
package/dist/picking-manager.js
CHANGED
|
@@ -25,6 +25,99 @@ export class PickingManager {
|
|
|
25
25
|
setPicker(picker) {
|
|
26
26
|
this.picker = picker;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Prepare batched geometry for a GPU pick pass, shared by `pick()` and
|
|
30
|
+
* `pickRect()`.
|
|
31
|
+
*
|
|
32
|
+
* Returns `'cpu'` when the caller must fall back to a CPU test — either JS
|
|
33
|
+
* geometry data was released, or hydrating an individual mesh per visible
|
|
34
|
+
* piece would exceed the pick-mesh budget. Returns `'gpu'` after hydrating
|
|
35
|
+
* whatever was missing, so a following `scene.getMeshes()` covers every
|
|
36
|
+
* visible entity.
|
|
37
|
+
*
|
|
38
|
+
* Both pick paths MUST route through this. `pickRect` used to read
|
|
39
|
+
* `scene.getMeshes()` directly, which on a batched model is empty or
|
|
40
|
+
* partial, so rectangle select returned an empty set no matter how many
|
|
41
|
+
* elements the rect covered (#1904).
|
|
42
|
+
*/
|
|
43
|
+
prepareBatchedPick(options) {
|
|
44
|
+
const batchedMeshes = this.scene.getBatchedMeshes();
|
|
45
|
+
// Nothing batched: every mesh is already individually hydrated.
|
|
46
|
+
if (batchedMeshes.length === 0)
|
|
47
|
+
return 'gpu';
|
|
48
|
+
if (this.scene.isGeometryDataReleased())
|
|
49
|
+
return 'cpu';
|
|
50
|
+
// Collect every pickable expressId. We use the scene's authoritative
|
|
51
|
+
// mesh-data id set rather than each batch's `expressIds`, because a batch
|
|
52
|
+
// only records the PRIMARY expressId of each merged piece. When a door or
|
|
53
|
+
// window is colour-fused into a batch keyed by its host wall / opening,
|
|
54
|
+
// the filler's id lives only in the per-vertex entityIds (registered in
|
|
55
|
+
// meshDataMap by Scene.addMeshData). Reading batch.expressIds alone would
|
|
56
|
+
// skip the filler, so under isolation its mesh is never hydrated and
|
|
57
|
+
// pick() returns null (#1358).
|
|
58
|
+
const expressIds = new Set(this.scene.getAllMeshDataExpressIds());
|
|
59
|
+
// Track how many individual mesh pieces already exist for each (expressId:modelIndex).
|
|
60
|
+
// Multi-piece elements (windows/doors with submeshes) need all pieces for reliable picking.
|
|
61
|
+
const existingPieceCounts = new Map();
|
|
62
|
+
for (const mesh of this.scene.getMeshes()) {
|
|
63
|
+
const key = `${mesh.expressId}:${mesh.modelIndex ?? 'any'}`;
|
|
64
|
+
existingPieceCounts.set(key, (existingPieceCounts.get(key) ?? 0) + 1);
|
|
65
|
+
}
|
|
66
|
+
// Build required piece counts from MeshData for all visible entities.
|
|
67
|
+
const requiredPieceCounts = new Map();
|
|
68
|
+
const visibleExpressIds = [];
|
|
69
|
+
for (const expressId of expressIds) {
|
|
70
|
+
if (options?.hiddenIds?.has(expressId))
|
|
71
|
+
continue;
|
|
72
|
+
if (options?.isolatedIds !== null && options?.isolatedIds !== undefined && !options.isolatedIds.has(expressId))
|
|
73
|
+
continue;
|
|
74
|
+
visibleExpressIds.push(expressId);
|
|
75
|
+
const pieces = this.scene.getMeshDataPieces(expressId);
|
|
76
|
+
if (!pieces)
|
|
77
|
+
continue;
|
|
78
|
+
for (const piece of pieces) {
|
|
79
|
+
const key = `${piece.expressId}:${piece.modelIndex ?? 'any'}`;
|
|
80
|
+
requiredPieceCounts.set(key, (requiredPieceCounts.get(key) ?? 0) + 1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Count how many meshes we'd need to create for full GPU picking
|
|
84
|
+
// For multi-model and multi-piece elements, count missing piece instances per key.
|
|
85
|
+
let toCreate = 0;
|
|
86
|
+
for (const [key, requiredCount] of requiredPieceCounts) {
|
|
87
|
+
const existingCount = existingPieceCounts.get(key) ?? 0;
|
|
88
|
+
if (requiredCount > existingCount) {
|
|
89
|
+
toCreate += requiredCount - existingCount;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// PERFORMANCE: fall back to CPU for large models instead of creating GPU meshes.
|
|
93
|
+
// GPU picking requires individual mesh buffers; for 60K+ elements this is too slow.
|
|
94
|
+
// The CPU paths use bounding boxes - no GPU buffers needed.
|
|
95
|
+
const MAX_PICK_MESH_CREATION = 500;
|
|
96
|
+
if (toCreate > MAX_PICK_MESH_CREATION || (visibleExpressIds.length > 0 && requiredPieceCounts.size === 0)) {
|
|
97
|
+
return 'cpu';
|
|
98
|
+
}
|
|
99
|
+
// For smaller models, create GPU meshes for picking
|
|
100
|
+
// Only create meshes for VISIBLE elements (not hidden, and either no isolation or in isolated set)
|
|
101
|
+
// For multi-model support: create meshes for ALL (expressId, modelIndex) pairs
|
|
102
|
+
const baselineExistingCounts = new Map(existingPieceCounts);
|
|
103
|
+
const seenOrdinalsByKey = new Map();
|
|
104
|
+
for (const expressId of visibleExpressIds) {
|
|
105
|
+
const pieces = this.scene.getMeshDataPieces(expressId);
|
|
106
|
+
if (pieces) {
|
|
107
|
+
for (const piece of pieces) {
|
|
108
|
+
const meshKey = `${piece.expressId}:${piece.modelIndex ?? 'any'}`;
|
|
109
|
+
const ordinal = seenOrdinalsByKey.get(meshKey) ?? 0;
|
|
110
|
+
seenOrdinalsByKey.set(meshKey, ordinal + 1);
|
|
111
|
+
const baselineExisting = baselineExistingCounts.get(meshKey) ?? 0;
|
|
112
|
+
// Assume existing pieces correspond to the first N pieces in stable order.
|
|
113
|
+
if (ordinal < baselineExisting)
|
|
114
|
+
continue;
|
|
115
|
+
this.createMeshFromDataFn(piece);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return 'gpu';
|
|
120
|
+
}
|
|
28
121
|
/**
|
|
29
122
|
* Pick object at screen coordinates
|
|
30
123
|
* Respects visibility filtering so users can only select visible elements
|
|
@@ -52,103 +145,18 @@ export class PickingManager {
|
|
|
52
145
|
if (options?.isStreaming) {
|
|
53
146
|
return null;
|
|
54
147
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return null;
|
|
66
|
-
return {
|
|
67
|
-
expressId: hit.expressId,
|
|
68
|
-
modelIndex: hit.modelIndex,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
// Collect every pickable expressId. We use the scene's authoritative
|
|
72
|
-
// mesh-data id set rather than each batch's `expressIds`, because a batch
|
|
73
|
-
// only records the PRIMARY expressId of each merged piece. When a door or
|
|
74
|
-
// window is colour-fused into a batch keyed by its host wall / opening,
|
|
75
|
-
// the filler's id lives only in the per-vertex entityIds (registered in
|
|
76
|
-
// meshDataMap by Scene.addMeshData). Reading batch.expressIds alone would
|
|
77
|
-
// skip the filler, so under isolation its mesh is never hydrated and
|
|
78
|
-
// pick() returns null (#1358).
|
|
79
|
-
const expressIds = new Set(this.scene.getAllMeshDataExpressIds());
|
|
80
|
-
// Track how many individual mesh pieces already exist for each (expressId:modelIndex).
|
|
81
|
-
// Multi-piece elements (windows/doors with submeshes) need all pieces for reliable picking.
|
|
82
|
-
const existingPieceCounts = new Map();
|
|
83
|
-
for (const mesh of meshes) {
|
|
84
|
-
const key = `${mesh.expressId}:${mesh.modelIndex ?? 'any'}`;
|
|
85
|
-
existingPieceCounts.set(key, (existingPieceCounts.get(key) ?? 0) + 1);
|
|
86
|
-
}
|
|
87
|
-
// Build required piece counts from MeshData for all visible entities.
|
|
88
|
-
const requiredPieceCounts = new Map();
|
|
89
|
-
const visibleExpressIds = [];
|
|
90
|
-
for (const expressId of expressIds) {
|
|
91
|
-
if (options?.hiddenIds?.has(expressId))
|
|
92
|
-
continue;
|
|
93
|
-
if (options?.isolatedIds !== null && options?.isolatedIds !== undefined && !options.isolatedIds.has(expressId))
|
|
94
|
-
continue;
|
|
95
|
-
visibleExpressIds.push(expressId);
|
|
96
|
-
const pieces = this.scene.getMeshDataPieces(expressId);
|
|
97
|
-
if (!pieces)
|
|
98
|
-
continue;
|
|
99
|
-
for (const piece of pieces) {
|
|
100
|
-
const key = `${piece.expressId}:${piece.modelIndex ?? 'any'}`;
|
|
101
|
-
requiredPieceCounts.set(key, (requiredPieceCounts.get(key) ?? 0) + 1);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
// Count how many meshes we'd need to create for full GPU picking
|
|
105
|
-
// For multi-model and multi-piece elements, count missing piece instances per key.
|
|
106
|
-
let toCreate = 0;
|
|
107
|
-
for (const [key, requiredCount] of requiredPieceCounts) {
|
|
108
|
-
const existingCount = existingPieceCounts.get(key) ?? 0;
|
|
109
|
-
if (requiredCount > existingCount) {
|
|
110
|
-
toCreate += requiredCount - existingCount;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// PERFORMANCE FIX: Use CPU raycasting for large models instead of creating GPU meshes
|
|
114
|
-
// GPU picking requires individual mesh buffers; for 60K+ elements this is too slow
|
|
115
|
-
// CPU raycasting uses bounding box filtering + triangle tests - no GPU buffers needed
|
|
116
|
-
const MAX_PICK_MESH_CREATION = 500;
|
|
117
|
-
if (toCreate > MAX_PICK_MESH_CREATION || (visibleExpressIds.length > 0 && requiredPieceCounts.size === 0)) {
|
|
118
|
-
// Use CPU raycasting fallback - works regardless of how many individual meshes exist
|
|
119
|
-
const ray = this.camera.unprojectToRay(scaledX, scaledY, this.canvas.width, this.canvas.height);
|
|
120
|
-
const hit = this.scene.raycast(ray.origin, ray.direction, options?.hiddenIds, options?.isolatedIds, clip);
|
|
121
|
-
if (!hit)
|
|
122
|
-
return null;
|
|
123
|
-
// CPU raycasting returns expressId and modelIndex
|
|
124
|
-
return {
|
|
125
|
-
expressId: hit.expressId,
|
|
126
|
-
modelIndex: hit.modelIndex,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
// For smaller models, create GPU meshes for picking
|
|
130
|
-
// Only create meshes for VISIBLE elements (not hidden, and either no isolation or in isolated set)
|
|
131
|
-
// For multi-model support: create meshes for ALL (expressId, modelIndex) pairs
|
|
132
|
-
const baselineExistingCounts = new Map(existingPieceCounts);
|
|
133
|
-
const seenOrdinalsByKey = new Map();
|
|
134
|
-
for (const expressId of visibleExpressIds) {
|
|
135
|
-
const pieces = this.scene.getMeshDataPieces(expressId);
|
|
136
|
-
if (pieces) {
|
|
137
|
-
for (const piece of pieces) {
|
|
138
|
-
const meshKey = `${piece.expressId}:${piece.modelIndex ?? 'any'}`;
|
|
139
|
-
const ordinal = seenOrdinalsByKey.get(meshKey) ?? 0;
|
|
140
|
-
seenOrdinalsByKey.set(meshKey, ordinal + 1);
|
|
141
|
-
const baselineExisting = baselineExistingCounts.get(meshKey) ?? 0;
|
|
142
|
-
// Assume existing pieces correspond to the first N pieces in stable order.
|
|
143
|
-
if (ordinal < baselineExisting)
|
|
144
|
-
continue;
|
|
145
|
-
this.createMeshFromDataFn(piece);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
// Get updated meshes list (includes newly created ones)
|
|
150
|
-
meshes = this.scene.getMeshes();
|
|
148
|
+
if (this.prepareBatchedPick(options) === 'cpu') {
|
|
149
|
+
const ray = this.camera.unprojectToRay(scaledX, scaledY, this.canvas.width, this.canvas.height);
|
|
150
|
+
const hit = this.scene.raycast(ray.origin, ray.direction, options?.hiddenIds, options?.isolatedIds, clip);
|
|
151
|
+
if (!hit)
|
|
152
|
+
return null;
|
|
153
|
+
// CPU raycasting returns expressId and modelIndex
|
|
154
|
+
return {
|
|
155
|
+
expressId: hit.expressId,
|
|
156
|
+
modelIndex: hit.modelIndex,
|
|
157
|
+
};
|
|
151
158
|
}
|
|
159
|
+
let meshes = this.scene.getMeshes();
|
|
152
160
|
// Apply visibility filtering to meshes before picking
|
|
153
161
|
// This ensures users can only select elements that are actually visible
|
|
154
162
|
if (options?.hiddenIds && options.hiddenIds.size > 0) {
|
|
@@ -179,11 +187,23 @@ export class PickingManager {
|
|
|
179
187
|
* filtered (per-asset visibility is binary and the asset count is
|
|
180
188
|
* tiny).
|
|
181
189
|
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
190
|
+
* Batched models take the same route as `pick()`: hydrate the
|
|
191
|
+
* missing individual meshes when that is affordable, otherwise fall
|
|
192
|
+
* back to `Scene.selectRect`, which over-selects relative to the
|
|
193
|
+
* pixel-exact GPU pass in three ways: it is bounding-box granular
|
|
194
|
+
* (a rect over empty space inside an element's bounds selects it),
|
|
195
|
+
* it has no depth test (occluded entities are selected), and it
|
|
196
|
+
* drops a section-planed or cropped entity only when the whole box
|
|
197
|
+
* is clipped away, where the pick shader discards per fragment.
|
|
198
|
+
* Hidden and isolation filtering do apply there. Reading
|
|
199
|
+
* `scene.getMeshes()` unconditionally is what made this return an
|
|
200
|
+
* empty set on every batched model (#1904).
|
|
201
|
+
*
|
|
202
|
+
* Point clouds never depend on mesh hydration — splats render into
|
|
203
|
+
* the pick pass on their own — so the CPU branch still runs the GPU
|
|
204
|
+
* pass for them and unions the two results. Skipping it would drop
|
|
205
|
+
* point selection on any mixed scene big enough to miss the
|
|
206
|
+
* pick-mesh budget.
|
|
187
207
|
*/
|
|
188
208
|
async pickRect(x0, y0, x1, y1, options, clip) {
|
|
189
209
|
if (!this.picker)
|
|
@@ -197,6 +217,47 @@ export class PickingManager {
|
|
|
197
217
|
const sx1 = x1 * scaleX, sy1 = y1 * scaleY;
|
|
198
218
|
if (options?.isStreaming)
|
|
199
219
|
return new Set();
|
|
220
|
+
if (this.prepareBatchedPick(options) === 'cpu') {
|
|
221
|
+
const boxHits = this.scene.selectRect(sx0, sy0, sx1, sy1, this.canvas.width, this.canvas.height, this.camera.getViewProjMatrix().m, options?.hiddenIds, options?.isolatedIds, clip);
|
|
222
|
+
const cpuPointSnap = this.pointPickProvider?.() ?? null;
|
|
223
|
+
if (!cpuPointSnap || cpuPointSnap.nodes.length === 0)
|
|
224
|
+
return boxHits;
|
|
225
|
+
// Point-only pick pass. Meshes are deliberately `[]`: this branch runs
|
|
226
|
+
// precisely because the per-element pick buffers were NOT hydrated, so
|
|
227
|
+
// `scene.getMeshes()` holds an arbitrary partial set (highlight meshes
|
|
228
|
+
// and whatever an earlier pick left behind) whose ids `selectRect`
|
|
229
|
+
// already covers.
|
|
230
|
+
//
|
|
231
|
+
// Instanced templates are left out for a different reason. The
|
|
232
|
+
// instanced pick pass IS visibility-filtered — its fragment shader
|
|
233
|
+
// discards on `instFlags` bit 1, which `Scene` sets for every
|
|
234
|
+
// occurrence that is hidden or excluded by isolation — so unioning its
|
|
235
|
+
// hits would not re-admit anything. It is left out because it would
|
|
236
|
+
// add no ids and can only subtract. Every instanced occurrence whose
|
|
237
|
+
// template has geometry folds its world AABB into
|
|
238
|
+
// `Scene.boundingBoxes`, so `selectRect` above has already returned
|
|
239
|
+
// those ids under the same two filters (a template with no finite
|
|
240
|
+
// local box is skipped there, and rasterises nothing here either).
|
|
241
|
+
// Meanwhile the instanced draw shares this pass's depth target, so an
|
|
242
|
+
// occurrence in front of a splat would occlude it and drop a point hit
|
|
243
|
+
// that nothing else on this path can supply — point assets have no
|
|
244
|
+
// entry in `boundingBoxes` at all.
|
|
245
|
+
let pointHits;
|
|
246
|
+
try {
|
|
247
|
+
pointHits = await this.picker.pickRect(sx0, sy0, sx1, sy1, this.canvas.width, this.canvas.height, [], this.camera.getViewProjMatrix().m, cpuPointSnap.nodes, cpuPointSnap.sizing, undefined, clip);
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
// `picker.pickRect` rethrows any readback failure that is not a
|
|
251
|
+
// device-loss abort. The box hits are already computed and this
|
|
252
|
+
// branch could not throw at all before the point pass was added,
|
|
253
|
+
// so degrade to them instead of failing the whole rectangle select.
|
|
254
|
+
console.warn('[PickingManager] point-cloud rect pick failed; returning bounding-box hits only:', err);
|
|
255
|
+
return boxHits;
|
|
256
|
+
}
|
|
257
|
+
for (const id of pointHits)
|
|
258
|
+
boxHits.add(id);
|
|
259
|
+
return boxHits;
|
|
260
|
+
}
|
|
200
261
|
let meshes = this.scene.getMeshes();
|
|
201
262
|
if (options?.hiddenIds && options.hiddenIds.size > 0) {
|
|
202
263
|
meshes = meshes.filter((m) => !options.hiddenIds.has(m.expressId));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"picking-manager.js","sourceRoot":"","sources":["../src/picking-manager.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAuB/D,MAAM,OAAO,cAAc;IACf,MAAM,CAAS;IACf,KAAK,CAAQ;IACb,MAAM,CAAgB;IACtB,MAAM,CAAoB;IAC1B,oBAAoB,CAA+B;IACnD,iBAAiB,GAA6B,IAAI,CAAC;IAE3D,YACI,MAAc,EACd,KAAY,EACZ,MAAqB,EACrB,MAAyB,EACzB,oBAAkD;QAElD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,oBAAoB,GAAG,oBAAoB,CAAC;IACrD,CAAC;IAED,+EAA+E;IAC/E,oBAAoB,CAAC,QAAkC;QACnD,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,MAAqB;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAED
|
|
1
|
+
{"version":3,"file":"picking-manager.js","sourceRoot":"","sources":["../src/picking-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CPU rectangle selection against bounding-box data.
|
|
3
|
+
*
|
|
4
|
+
* The rect analogue of `raycastBoundingBoxes` in `scene-raycaster.ts`, and it
|
|
5
|
+
* exists for the same reason: on a batched model the GPU pick pass only sees
|
|
6
|
+
* individually hydrated meshes, and hydrating every visible piece is too
|
|
7
|
+
* expensive past a few hundred entities. `pick()` has always had a CPU escape
|
|
8
|
+
* hatch for that case; rectangle select had none, which is why it returned an
|
|
9
|
+
* empty set on batched models (#1904).
|
|
10
|
+
*
|
|
11
|
+
* Granularity: bounding box, not triangle. A box whose screen-space AABB
|
|
12
|
+
* overlaps the rect counts as selected, so this over-selects relative to the
|
|
13
|
+
* pixel-exact GPU path — an entity can be picked when the rect covers only
|
|
14
|
+
* empty space inside its bounds. That granularity is the same one the
|
|
15
|
+
* released-geometry raycast path already has (`raycastBoundingBoxes` also tests
|
|
16
|
+
* boxes, not triangles), and over-selection is recoverable by the user in a way
|
|
17
|
+
* that selecting nothing is not.
|
|
18
|
+
*
|
|
19
|
+
* Two further gaps are NOT shared with that path, and both over-select. There is
|
|
20
|
+
* no depth test at all here, so an entity fully occluded by another is still
|
|
21
|
+
* selected, whereas the raycast keeps only the nearest box (`tNear <
|
|
22
|
+
* closestDistance`). And section-plane / crop-box filtering is whole-box: the
|
|
23
|
+
* `boxFullyClipped` call below skips an entity only when nothing of its box
|
|
24
|
+
* could be visible, so a rect over the sectioned-away half of a box still
|
|
25
|
+
* selects it — the raycast measures entry into the *visible* part of the box
|
|
26
|
+
* (`clippedBoxEntryDistance`), and the pick shader discards per fragment.
|
|
27
|
+
*/
|
|
28
|
+
import { type BoundingBox } from './scene-raycaster.js';
|
|
29
|
+
import type { PickClipState } from './types.js';
|
|
30
|
+
/** Selection rectangle in canvas (device) pixels. Corners may be in any order. */
|
|
31
|
+
export interface ScreenRect {
|
|
32
|
+
x0: number;
|
|
33
|
+
y0: number;
|
|
34
|
+
x1: number;
|
|
35
|
+
y1: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Every entity whose bounding box projects onto the given screen rect.
|
|
39
|
+
*
|
|
40
|
+
* @param boundingBoxes World-space AABB per expressId
|
|
41
|
+
* @param viewProj Camera view-projection, column-major (see below)
|
|
42
|
+
* @param rect Selection rect in canvas pixels
|
|
43
|
+
* @param viewportWidth Canvas width in pixels
|
|
44
|
+
* @param viewportHeight Canvas height in pixels
|
|
45
|
+
* @param hiddenIds Skipped entirely
|
|
46
|
+
* @param isolatedIds When non-null, only these ids are considered
|
|
47
|
+
* @param clip Active section plane / crop box, so selection matches
|
|
48
|
+
* what is visible (same contract as the raycast paths)
|
|
49
|
+
*/
|
|
50
|
+
export declare function selectBoundingBoxesInRect(boundingBoxes: Map<number, BoundingBox>, viewProj: Float32Array, rect: ScreenRect, viewportWidth: number, viewportHeight: number, hiddenIds?: Set<number>, isolatedIds?: Set<number> | null, clip?: PickClipState | null): Set<number>;
|
|
51
|
+
//# sourceMappingURL=scene-rect-select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-rect-select.d.ts","sourceRoot":"","sources":["../src/scene-rect-select.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAiC,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACvF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAiBhD,kFAAkF;AAClF,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACrC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,EACvC,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE,UAAU,EAChB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EACvB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAChC,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,GAC5B,GAAG,CAAC,MAAM,CAAC,CAyIb"}
|