@inweb/viewer-three 26.7.6 → 26.8.1
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/plugins/loaders/IFCXLoader.js +1827 -881
- package/dist/plugins/loaders/IFCXLoader.js.map +1 -1
- package/dist/plugins/loaders/IFCXLoader.min.js +1 -1
- package/dist/plugins/loaders/IFCXLoader.module.js +477 -154
- package/dist/plugins/loaders/IFCXLoader.module.js.map +1 -1
- package/dist/viewer-three.js +372 -230
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +4 -2
- package/dist/viewer-three.module.js +336 -159
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/components/HighlighterComponent.d.ts +2 -2
- package/lib/Viewer/loaders/GLTFCloudDynamicLoader.d.ts +0 -2
- package/lib/Viewer/loaders/index.d.ts +1 -1
- package/package.json +5 -5
- package/plugins/loaders/IFCX/IFCXLoader.ts +4 -7
- package/plugins/loaders/IFCX/render.js +686 -181
- package/plugins/loaders/IFCXCloudLoader.ts +1 -1
- package/src/Viewer/commands/SetSelected.ts +1 -0
- package/src/Viewer/commands/index.ts +1 -1
- package/src/Viewer/components/HighlighterComponent.ts +38 -16
- package/src/Viewer/components/LightComponent.ts +17 -15
- package/src/Viewer/components/SelectionComponent.ts +1 -2
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +187 -65
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +7 -12
- package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +120 -114
- package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +7 -13
- package/src/Viewer/loaders/index.ts +1 -1
|
@@ -1,95 +1,109 @@
|
|
|
1
1
|
import { Loader as Loader$1, GLTFLoadingManager, ModelImpl, loaders } from "@inweb/viewer-three";
|
|
2
2
|
|
|
3
|
-
import { Box3, Vector3,
|
|
3
|
+
import { Box3, Vector3, Scene, PerspectiveCamera, Group, Matrix4, BufferGeometry, BufferAttribute, MeshLambertMaterial, Mesh, LineBasicMaterial, Line, Float32BufferAttribute, MeshStandardMaterial, Color, Euler, PointsMaterial, Points, Loader, FileLoader } from "three";
|
|
4
|
+
|
|
5
|
+
import { PCDLoader } from "three/examples/jsm/loaders/PCDLoader.js";
|
|
4
6
|
|
|
5
7
|
const THREE = {
|
|
6
8
|
Box3: Box3,
|
|
7
9
|
BufferAttribute: BufferAttribute,
|
|
8
10
|
BufferGeometry: BufferGeometry,
|
|
9
11
|
Color: Color,
|
|
12
|
+
Euler: Euler,
|
|
13
|
+
Float32BufferAttribute: Float32BufferAttribute,
|
|
10
14
|
Group: Group,
|
|
11
15
|
Line: Line,
|
|
12
16
|
LineBasicMaterial: LineBasicMaterial,
|
|
13
17
|
Matrix4: Matrix4,
|
|
14
18
|
Mesh: Mesh,
|
|
15
|
-
|
|
19
|
+
MeshLambertMaterial: MeshLambertMaterial,
|
|
20
|
+
MeshStandardMaterial: MeshStandardMaterial,
|
|
21
|
+
Points: Points,
|
|
22
|
+
PointsMaterial: PointsMaterial,
|
|
16
23
|
PerspectiveCamera: PerspectiveCamera,
|
|
17
24
|
Scene: Scene,
|
|
18
25
|
Vector3: Vector3
|
|
19
26
|
};
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
let start = root;
|
|
27
|
-
while (fragments.length && start && start.children) {
|
|
28
|
-
let f = fragments.shift();
|
|
29
|
-
start = start.children.find((i => i.name.split("/").reverse()[0] === f));
|
|
28
|
+
var StackedLayerProvider = class {
|
|
29
|
+
providers;
|
|
30
|
+
constructor(providers) {
|
|
31
|
+
this.providers = providers;
|
|
30
32
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
async GetLayerByURI(uri) {
|
|
34
|
+
let errorStack = [];
|
|
35
|
+
for (let provider of this.providers) {
|
|
36
|
+
let layer = await provider.GetLayerByURI(uri);
|
|
37
|
+
if (!(layer instanceof Error)) {
|
|
38
|
+
return layer;
|
|
39
|
+
} else {
|
|
40
|
+
errorStack.push(layer);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return new Error(JSON.stringify(errorStack));
|
|
33
44
|
}
|
|
34
|
-
}
|
|
45
|
+
};
|
|
35
46
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
var InMemoryLayerProvider = class {
|
|
48
|
+
layers;
|
|
49
|
+
constructor() {
|
|
50
|
+
this.layers = new Map;
|
|
51
|
+
}
|
|
52
|
+
GetLayerByURI(uri) {
|
|
53
|
+
if (!this.layers.has(uri)) {
|
|
54
|
+
return new Error(`File with uri "${uri}" not found`);
|
|
43
55
|
}
|
|
44
|
-
return
|
|
45
|
-
} else {
|
|
46
|
-
return null;
|
|
56
|
+
return Promise.resolve(this.layers.get(uri));
|
|
47
57
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
58
|
+
add(file) {
|
|
59
|
+
if (this.layers.has(file.header.id)) {
|
|
60
|
+
throw new Error(`Inserting file with duplicate ID "${file.header.id}"`);
|
|
61
|
+
}
|
|
62
|
+
this.layers.set(file.header.id, file);
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
AddAll(files) {
|
|
66
|
+
files.forEach((f => this.add(f)));
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
59
70
|
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
attributes: new Map
|
|
65
|
-
};
|
|
71
|
+
function log(bla) {
|
|
72
|
+
{
|
|
73
|
+
console.log(`${JSON.stringify(arguments)}`);
|
|
74
|
+
}
|
|
66
75
|
}
|
|
67
76
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
var FetchLayerProvider = class {
|
|
78
|
+
layers;
|
|
79
|
+
constructor() {
|
|
80
|
+
this.layers = new Map;
|
|
81
|
+
}
|
|
82
|
+
async FetchJson(url) {
|
|
83
|
+
let result = await fetch(url);
|
|
84
|
+
if (!result.ok) {
|
|
85
|
+
return new Error(`Failed to fetch ${url}: ${result.status}`);
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
return await result.json();
|
|
89
|
+
} catch (e) {
|
|
90
|
+
log(url);
|
|
91
|
+
return new Error(`Failed to parse json at ${url}: ${e}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async GetLayerByURI(uri) {
|
|
95
|
+
if (!this.layers.has(uri)) {
|
|
96
|
+
let fetched = await this.FetchJson(uri);
|
|
97
|
+
if (fetched instanceof Error) {
|
|
98
|
+
return new Error(`File with id "${uri}" not found`);
|
|
85
99
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
100
|
+
let file = fetched;
|
|
101
|
+
this.layers.set(uri, file);
|
|
102
|
+
return file;
|
|
103
|
+
}
|
|
104
|
+
return this.layers.get(uri);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
93
107
|
|
|
94
108
|
function MMSet(map, key, value) {
|
|
95
109
|
if (map.has(key)) {
|
|
@@ -99,6 +113,8 @@ function MMSet(map, key, value) {
|
|
|
99
113
|
}
|
|
100
114
|
}
|
|
101
115
|
|
|
116
|
+
var CycleError = class extends Error {};
|
|
117
|
+
|
|
102
118
|
function FindRootsOrCycles(nodes) {
|
|
103
119
|
let dependencies = new Map;
|
|
104
120
|
let dependents = new Map;
|
|
@@ -139,22 +155,78 @@ function FindRootsOrCycles(nodes) {
|
|
|
139
155
|
return roots;
|
|
140
156
|
}
|
|
141
157
|
|
|
142
|
-
function
|
|
158
|
+
function GetHead(path) {
|
|
159
|
+
return path.split("/")[0];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function GetTail(path) {
|
|
163
|
+
let parts = path.split("/");
|
|
164
|
+
parts.shift();
|
|
165
|
+
return parts.join("/");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function MakePostCompositionNode(node) {
|
|
169
|
+
return {
|
|
170
|
+
node: node,
|
|
171
|
+
children: new Map,
|
|
172
|
+
attributes: new Map
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function GetChildNodeWithPath(node, path) {
|
|
177
|
+
if (path === "") return node;
|
|
178
|
+
let parts = path.split("/");
|
|
179
|
+
let child = node.children.get(parts[0]);
|
|
180
|
+
if (child) {
|
|
181
|
+
if (parts.length === 1) {
|
|
182
|
+
return child;
|
|
183
|
+
}
|
|
184
|
+
return GetChildNodeWithPath(child, GetTail(path));
|
|
185
|
+
} else {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function FlattenPathToPreCompositionNode(path, inputNodes) {
|
|
191
|
+
let compositionNode = {
|
|
192
|
+
path: path,
|
|
193
|
+
children: {},
|
|
194
|
+
inherits: {},
|
|
195
|
+
attributes: {}
|
|
196
|
+
};
|
|
197
|
+
inputNodes.forEach((node => {
|
|
198
|
+
Object.keys(node.children).forEach((childName => {
|
|
199
|
+
compositionNode.children[childName] = node.children[childName];
|
|
200
|
+
}));
|
|
201
|
+
Object.keys(node.inherits).forEach((inheritName => {
|
|
202
|
+
let ih = node.inherits[inheritName];
|
|
203
|
+
if (ih === null) {
|
|
204
|
+
delete compositionNode.inherits[inheritName];
|
|
205
|
+
} else {
|
|
206
|
+
compositionNode.inherits[inheritName] = ih;
|
|
207
|
+
}
|
|
208
|
+
}));
|
|
209
|
+
Object.keys(node.attributes).forEach((attrName => {
|
|
210
|
+
compositionNode.attributes[attrName] = node.attributes[attrName];
|
|
211
|
+
}));
|
|
212
|
+
}));
|
|
213
|
+
return compositionNode;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function FlattenCompositionInput(input) {
|
|
143
217
|
let compositionNodes = new Map;
|
|
144
218
|
for (let [path, inputNodes] of input) {
|
|
145
|
-
compositionNodes.set(path,
|
|
219
|
+
compositionNodes.set(path, FlattenPathToPreCompositionNode(path, inputNodes));
|
|
146
220
|
}
|
|
147
221
|
return compositionNodes;
|
|
148
222
|
}
|
|
149
223
|
|
|
150
|
-
var CycleError = class extends Error {};
|
|
151
|
-
|
|
152
224
|
function ExpandFirstRootInInput(nodes) {
|
|
153
225
|
let roots = FindRootsOrCycles(nodes);
|
|
154
226
|
if (!roots) {
|
|
155
227
|
throw new CycleError;
|
|
156
228
|
}
|
|
157
|
-
return
|
|
229
|
+
return ComposeNodeFromPath([ ...roots.values() ][0], nodes);
|
|
158
230
|
}
|
|
159
231
|
|
|
160
232
|
function CreateArtificialRoot(nodes) {
|
|
@@ -168,31 +240,31 @@ function CreateArtificialRoot(nodes) {
|
|
|
168
240
|
children: new Map
|
|
169
241
|
};
|
|
170
242
|
roots.forEach((root => {
|
|
171
|
-
pseudoRoot.children.set(root,
|
|
243
|
+
pseudoRoot.children.set(root, ComposeNodeFromPath(root, nodes));
|
|
172
244
|
}));
|
|
173
245
|
return pseudoRoot;
|
|
174
246
|
}
|
|
175
247
|
|
|
176
|
-
function
|
|
177
|
-
return
|
|
248
|
+
function ComposeNodeFromPath(path, preCompositionNodes) {
|
|
249
|
+
return ComposeNode(path, MakePostCompositionNode(path), preCompositionNodes);
|
|
178
250
|
}
|
|
179
251
|
|
|
180
|
-
function
|
|
181
|
-
let
|
|
182
|
-
if (
|
|
183
|
-
|
|
252
|
+
function ComposeNode(path, postCompositionNode, preCompositionNodes) {
|
|
253
|
+
let preCompositionNode = preCompositionNodes.get(path);
|
|
254
|
+
if (preCompositionNode) {
|
|
255
|
+
AddDataFromPreComposition(preCompositionNode, postCompositionNode, preCompositionNodes);
|
|
184
256
|
}
|
|
185
|
-
|
|
186
|
-
|
|
257
|
+
postCompositionNode.children.forEach(((child, name) => {
|
|
258
|
+
ComposeNode(`${path}/${name}`, child, preCompositionNodes);
|
|
187
259
|
}));
|
|
188
|
-
return
|
|
260
|
+
return postCompositionNode;
|
|
189
261
|
}
|
|
190
262
|
|
|
191
|
-
function
|
|
192
|
-
Object.values(input.inherits).forEach((
|
|
193
|
-
let classNode =
|
|
194
|
-
let subnode =
|
|
195
|
-
if (!subnode) throw new Error(`Unknown node ${
|
|
263
|
+
function AddDataFromPreComposition(input, node, nodes) {
|
|
264
|
+
Object.values(input.inherits).forEach((inheritPath => {
|
|
265
|
+
let classNode = ComposeNodeFromPath(GetHead(inheritPath), nodes);
|
|
266
|
+
let subnode = GetChildNodeWithPath(classNode, GetTail(inheritPath));
|
|
267
|
+
if (!subnode) throw new Error(`Unknown node ${inheritPath}`);
|
|
196
268
|
subnode.children.forEach(((child, childName) => {
|
|
197
269
|
node.children.set(childName, child);
|
|
198
270
|
}));
|
|
@@ -202,8 +274,8 @@ function AddDataFromInput(input, node, nodes) {
|
|
|
202
274
|
}));
|
|
203
275
|
Object.entries(input.children).forEach((([childName, child]) => {
|
|
204
276
|
if (child !== null) {
|
|
205
|
-
let classNode =
|
|
206
|
-
let subnode =
|
|
277
|
+
let classNode = ComposeNodeFromPath(GetHead(child), nodes);
|
|
278
|
+
let subnode = GetChildNodeWithPath(classNode, GetTail(child));
|
|
207
279
|
if (!subnode) throw new Error(`Unknown node ${child}`);
|
|
208
280
|
node.children.set(childName, subnode);
|
|
209
281
|
} else {
|
|
@@ -215,31 +287,12 @@ function AddDataFromInput(input, node, nodes) {
|
|
|
215
287
|
}));
|
|
216
288
|
}
|
|
217
289
|
|
|
218
|
-
function MMSet2(map, key, value) {
|
|
219
|
-
if (map.has(key)) {
|
|
220
|
-
map.get(key)?.push(value);
|
|
221
|
-
} else {
|
|
222
|
-
map.set(key, [ value ]);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function ToInputNodes(data) {
|
|
227
|
-
let inputNodes = new Map;
|
|
228
|
-
data.forEach((ifcxNode => {
|
|
229
|
-
let node = {
|
|
230
|
-
path: ifcxNode.path,
|
|
231
|
-
children: ifcxNode.children ? ifcxNode.children : {},
|
|
232
|
-
inherits: ifcxNode.inherits ? ifcxNode.inherits : {},
|
|
233
|
-
attributes: ifcxNode.attributes ? ifcxNode.attributes : {}
|
|
234
|
-
};
|
|
235
|
-
MMSet2(inputNodes, node.path, node);
|
|
236
|
-
}));
|
|
237
|
-
return inputNodes;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
290
|
var SchemaValidationError = class extends Error {};
|
|
241
291
|
|
|
242
292
|
function ValidateAttributeValue(desc, value, path, schemas) {
|
|
293
|
+
if (desc.optional && value === undefined) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
243
296
|
if (desc.inherits) {
|
|
244
297
|
desc.inherits.forEach((inheritedSchemaID => {
|
|
245
298
|
let inheritedSchema = schemas[inheritedSchemaID];
|
|
@@ -277,7 +330,7 @@ function ValidateAttributeValue(desc, value, path, schemas) {
|
|
|
277
330
|
if (typeof value !== "number") {
|
|
278
331
|
throw new SchemaValidationError(`Expected "${value}" to be of type real`);
|
|
279
332
|
}
|
|
280
|
-
} else if (desc.dataType === "
|
|
333
|
+
} else if (desc.dataType === "Reference") {
|
|
281
334
|
if (typeof value !== "string") {
|
|
282
335
|
throw new SchemaValidationError(`Expected "${value}" to be of type string`);
|
|
283
336
|
}
|
|
@@ -287,7 +340,10 @@ function ValidateAttributeValue(desc, value, path, schemas) {
|
|
|
287
340
|
}
|
|
288
341
|
if (desc.objectRestrictions) {
|
|
289
342
|
Object.keys(desc.objectRestrictions.values).forEach((key => {
|
|
290
|
-
|
|
343
|
+
let optional = desc.objectRestrictions.values[key].optional;
|
|
344
|
+
let hasOwn = Object.hasOwn(value, key);
|
|
345
|
+
if (optional && !hasOwn) return;
|
|
346
|
+
if (!hasOwn) {
|
|
291
347
|
throw new SchemaValidationError(`Expected "${value}" to have key ${key}`);
|
|
292
348
|
}
|
|
293
349
|
ValidateAttributeValue(desc.objectRestrictions.values[key], value[key], path + "." + key, schemas);
|
|
@@ -307,7 +363,7 @@ function ValidateAttributeValue(desc, value, path, schemas) {
|
|
|
307
363
|
|
|
308
364
|
function Validate(schemas, inputNodes) {
|
|
309
365
|
inputNodes.forEach((node => {
|
|
310
|
-
Object.keys(node.attributes).forEach((schemaID => {
|
|
366
|
+
Object.keys(node.attributes).filter((v => !v.startsWith("__internal"))).forEach((schemaID => {
|
|
311
367
|
if (!schemas[schemaID]) {
|
|
312
368
|
throw new SchemaValidationError(`Missing schema "${schemaID}" referenced by ["${node.path}"].attributes`);
|
|
313
369
|
}
|
|
@@ -326,9 +382,23 @@ function Validate(schemas, inputNodes) {
|
|
|
326
382
|
}));
|
|
327
383
|
}
|
|
328
384
|
|
|
329
|
-
function
|
|
385
|
+
function ToInputNodes(data) {
|
|
386
|
+
let inputNodes = new Map;
|
|
387
|
+
data.forEach((ifcxNode => {
|
|
388
|
+
let node = {
|
|
389
|
+
path: ifcxNode.path,
|
|
390
|
+
children: ifcxNode.children ? ifcxNode.children : {},
|
|
391
|
+
inherits: ifcxNode.inherits ? ifcxNode.inherits : {},
|
|
392
|
+
attributes: ifcxNode.attributes ? ifcxNode.attributes : {}
|
|
393
|
+
};
|
|
394
|
+
MMSet(inputNodes, node.path, node);
|
|
395
|
+
}));
|
|
396
|
+
return inputNodes;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function LoadIfcxFile(file, checkSchemas = true, createArtificialRoot = true) {
|
|
330
400
|
let inputNodes = ToInputNodes(file.data);
|
|
331
|
-
let compositionNodes =
|
|
401
|
+
let compositionNodes = FlattenCompositionInput(inputNodes);
|
|
332
402
|
try {
|
|
333
403
|
if (checkSchemas) {
|
|
334
404
|
Validate(file.schemas, compositionNodes);
|
|
@@ -344,6 +414,9 @@ function LoadIfcxFile(file, checkSchemas = true, createArtificialRoot = false) {
|
|
|
344
414
|
}
|
|
345
415
|
|
|
346
416
|
function Federate(files) {
|
|
417
|
+
if (files.length === 0) {
|
|
418
|
+
throw new Error(`Trying to federate empty set of files`);
|
|
419
|
+
}
|
|
347
420
|
let result = {
|
|
348
421
|
header: files[0].header,
|
|
349
422
|
schemas: {},
|
|
@@ -395,6 +468,7 @@ function Collapse(nodes, deleteEmpty = false) {
|
|
|
395
468
|
function Prune(file, deleteEmpty = false) {
|
|
396
469
|
let result = {
|
|
397
470
|
header: file.header,
|
|
471
|
+
imports: [],
|
|
398
472
|
schemas: file.schemas,
|
|
399
473
|
data: []
|
|
400
474
|
};
|
|
@@ -411,6 +485,97 @@ function Prune(file, deleteEmpty = false) {
|
|
|
411
485
|
return result;
|
|
412
486
|
}
|
|
413
487
|
|
|
488
|
+
var IfcxLayerStack = class {
|
|
489
|
+
layers;
|
|
490
|
+
tree;
|
|
491
|
+
schemas;
|
|
492
|
+
federated;
|
|
493
|
+
constructor(layers) {
|
|
494
|
+
this.layers = layers;
|
|
495
|
+
this.Compose();
|
|
496
|
+
}
|
|
497
|
+
GetLayerIds() {
|
|
498
|
+
return this.layers.map((l => l.header.id));
|
|
499
|
+
}
|
|
500
|
+
Compose() {
|
|
501
|
+
this.federated = Federate(this.layers);
|
|
502
|
+
this.schemas = this.federated.schemas;
|
|
503
|
+
this.tree = LoadIfcxFile(this.federated);
|
|
504
|
+
}
|
|
505
|
+
GetFullTree() {
|
|
506
|
+
this.Compose();
|
|
507
|
+
return this.tree;
|
|
508
|
+
}
|
|
509
|
+
GetFederatedLayer() {
|
|
510
|
+
return this.federated;
|
|
511
|
+
}
|
|
512
|
+
GetSchemas() {
|
|
513
|
+
return this.schemas;
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
var IfcxLayerStackBuilder = class {
|
|
518
|
+
provider;
|
|
519
|
+
mainLayerId=null;
|
|
520
|
+
constructor(provider) {
|
|
521
|
+
this.provider = provider;
|
|
522
|
+
}
|
|
523
|
+
FromId(id) {
|
|
524
|
+
this.mainLayerId = id;
|
|
525
|
+
return this;
|
|
526
|
+
}
|
|
527
|
+
async Build() {
|
|
528
|
+
if (!this.mainLayerId) throw new Error(`no main layer ID specified`);
|
|
529
|
+
let layers = await this.BuildLayerSet(this.mainLayerId);
|
|
530
|
+
if (layers instanceof Error) {
|
|
531
|
+
return layers;
|
|
532
|
+
}
|
|
533
|
+
try {
|
|
534
|
+
return new IfcxLayerStack(layers);
|
|
535
|
+
} catch (e) {
|
|
536
|
+
return e;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
async SatisfyDependencies(activeLayer, placed, orderedLayers) {
|
|
540
|
+
let pending = [];
|
|
541
|
+
for (const impt of activeLayer.imports) {
|
|
542
|
+
if (!placed.has(impt.uri)) {
|
|
543
|
+
let layer = await this.provider.GetLayerByURI(impt.uri);
|
|
544
|
+
if (layer instanceof Error) {
|
|
545
|
+
return layer;
|
|
546
|
+
}
|
|
547
|
+
pending.push(layer);
|
|
548
|
+
placed.set(impt.uri, true);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
let temp = [];
|
|
552
|
+
for (const layer of pending) {
|
|
553
|
+
temp.push(layer);
|
|
554
|
+
let layers = await this.SatisfyDependencies(layer, placed, orderedLayers);
|
|
555
|
+
if (layers instanceof Error) {
|
|
556
|
+
return layers;
|
|
557
|
+
}
|
|
558
|
+
temp.push(...layers);
|
|
559
|
+
}
|
|
560
|
+
temp.forEach((t => orderedLayers.push(t)));
|
|
561
|
+
return temp;
|
|
562
|
+
}
|
|
563
|
+
async BuildLayerSet(activeLayerID) {
|
|
564
|
+
let activeLayer = await this.provider.GetLayerByURI(activeLayerID);
|
|
565
|
+
if (activeLayer instanceof Error) {
|
|
566
|
+
return activeLayer;
|
|
567
|
+
}
|
|
568
|
+
let layerSet = [ activeLayer ];
|
|
569
|
+
let placed = new Map;
|
|
570
|
+
placed.set(activeLayer.header.id, true);
|
|
571
|
+
let result = await this.SatisfyDependencies(activeLayer, placed, layerSet);
|
|
572
|
+
if (result instanceof Error) {
|
|
573
|
+
return result;
|
|
574
|
+
}
|
|
575
|
+
return layerSet;
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
|
|
414
579
|
function TreeNodeToComposedObject(path, node, schemas) {
|
|
415
580
|
let co = {
|
|
416
581
|
name: path,
|
|
@@ -445,10 +610,28 @@ function TreeNodeToComposedObject(path, node, schemas) {
|
|
|
445
610
|
return co;
|
|
446
611
|
}
|
|
447
612
|
|
|
448
|
-
function compose3(files) {
|
|
449
|
-
let
|
|
450
|
-
|
|
451
|
-
|
|
613
|
+
async function compose3(files) {
|
|
614
|
+
let userDefinedOrder = {
|
|
615
|
+
header: {
|
|
616
|
+
...files[0].header
|
|
617
|
+
},
|
|
618
|
+
imports: files.map((f => ({
|
|
619
|
+
uri: f.header.id
|
|
620
|
+
}))),
|
|
621
|
+
schemas: {},
|
|
622
|
+
data: []
|
|
623
|
+
};
|
|
624
|
+
userDefinedOrder.header.id = "USER_DEF";
|
|
625
|
+
let provider = new StackedLayerProvider([ (new InMemoryLayerProvider).AddAll([ userDefinedOrder, ...files ]), new FetchLayerProvider ]);
|
|
626
|
+
let layerStack = await new IfcxLayerStackBuilder(provider).FromId(userDefinedOrder.header.id).Build();
|
|
627
|
+
if (layerStack instanceof Error) {
|
|
628
|
+
throw layerStack;
|
|
629
|
+
}
|
|
630
|
+
layerStack.GetFederatedLayer().data.forEach(((n, i) => {
|
|
631
|
+
n.attributes = n.attributes || {};
|
|
632
|
+
n.attributes[`__internal_${i}`] = n.path;
|
|
633
|
+
}));
|
|
634
|
+
return TreeNodeToComposedObject("", layerStack.GetFullTree(), layerStack.GetSchemas());
|
|
452
635
|
}
|
|
453
636
|
|
|
454
637
|
var scene;
|
|
@@ -459,6 +642,12 @@ var datas = [];
|
|
|
459
642
|
|
|
460
643
|
var autoCamera = true;
|
|
461
644
|
|
|
645
|
+
var objectMap = {};
|
|
646
|
+
|
|
647
|
+
var primMap = {};
|
|
648
|
+
|
|
649
|
+
var envMap;
|
|
650
|
+
|
|
462
651
|
function init() {
|
|
463
652
|
scene = new THREE.Scene;
|
|
464
653
|
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, .1, 100);
|
|
@@ -474,32 +663,74 @@ function HasAttr(node, attrName) {
|
|
|
474
663
|
return !!node.attributes[attrName];
|
|
475
664
|
}
|
|
476
665
|
|
|
477
|
-
function
|
|
478
|
-
let
|
|
666
|
+
function tryCreateMeshGltfMaterial(path) {
|
|
667
|
+
for (let p of path) {
|
|
668
|
+
if (!p.attributes) {
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
const pbrMetallicRoughness = p.attributes["gltf::material::pbrMetallicRoughness"];
|
|
672
|
+
const normalTexture = p.attributes["gltf::material::normalTexture"];
|
|
673
|
+
const occlusionTexture = p.attributes["gltf::material::occlusionTexture"];
|
|
674
|
+
const emissiveTexture = p.attributes["gltf::material::emissiveTexture"];
|
|
675
|
+
const emissiveFactor = p.attributes["gltf::material::emissiveFactor"];
|
|
676
|
+
const alphaMode = p.attributes["gltf::material::alphaMode"];
|
|
677
|
+
const alphaCutoff = p.attributes["gltf::material::alphaCutoff"];
|
|
678
|
+
const doubleSided = p.attributes["gltf::material::doubleSided"];
|
|
679
|
+
if (!pbrMetallicRoughness && !normalTexture && !occlusionTexture && !emissiveTexture && !emissiveFactor && !alphaMode && !alphaCutoff && !doubleSided) {
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
let material = new THREE.MeshStandardMaterial;
|
|
683
|
+
material.color = new THREE.Color(1, 1, 1);
|
|
684
|
+
material.metalness = 1;
|
|
685
|
+
material.roughness = 1;
|
|
686
|
+
if (pbrMetallicRoughness) {
|
|
687
|
+
let baseColorFactor = pbrMetallicRoughness["baseColorFactor"];
|
|
688
|
+
if (baseColorFactor) {
|
|
689
|
+
material.color = new THREE.Color(baseColorFactor[0], baseColorFactor[1], baseColorFactor[2]);
|
|
690
|
+
}
|
|
691
|
+
let metallicFactor = pbrMetallicRoughness["metallicFactor"];
|
|
692
|
+
if (metallicFactor !== undefined) {
|
|
693
|
+
material.metalness = metallicFactor;
|
|
694
|
+
}
|
|
695
|
+
let roughnessFactor = pbrMetallicRoughness["roughnessFactor"];
|
|
696
|
+
if (roughnessFactor !== undefined) {
|
|
697
|
+
material.roughness = roughnessFactor;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
material.envMap = envMap;
|
|
701
|
+
material.needsUpdate = true;
|
|
702
|
+
material.envMapRotation = new THREE.Euler(.5 * Math.PI, 0, 0);
|
|
703
|
+
return material;
|
|
704
|
+
}
|
|
705
|
+
return undefined;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function createMaterialFromParent(path) {
|
|
479
709
|
let material = {
|
|
480
710
|
color: new THREE.Color(.6, .6, .6),
|
|
481
711
|
transparent: false,
|
|
482
712
|
opacity: 1
|
|
483
713
|
};
|
|
484
|
-
|
|
485
|
-
const
|
|
486
|
-
if (
|
|
487
|
-
let color = materialNode?.attributes["bsi::presentation::diffuseColor"];
|
|
714
|
+
for (let p of path) {
|
|
715
|
+
const color = p.attributes ? p.attributes["bsi::ifc::presentation::diffuseColor"] : null;
|
|
716
|
+
if (color) {
|
|
488
717
|
material.color = new THREE.Color(...color);
|
|
489
|
-
|
|
718
|
+
const opacity = p.attributes["bsi::ifc::presentation::opacity"];
|
|
719
|
+
if (opacity) {
|
|
490
720
|
material.transparent = true;
|
|
491
|
-
material.opacity =
|
|
721
|
+
material.opacity = opacity;
|
|
492
722
|
}
|
|
723
|
+
break;
|
|
493
724
|
}
|
|
494
725
|
}
|
|
495
726
|
return material;
|
|
496
727
|
}
|
|
497
728
|
|
|
498
|
-
function createCurveFromJson(
|
|
499
|
-
let points = new Float32Array(
|
|
729
|
+
function createCurveFromJson(path) {
|
|
730
|
+
let points = new Float32Array(path[0].attributes["usd::usdgeom::basiscurves::points"].flat());
|
|
500
731
|
const geometry = new THREE.BufferGeometry;
|
|
501
732
|
geometry.setAttribute("position", new THREE.BufferAttribute(points, 3));
|
|
502
|
-
const material = createMaterialFromParent(
|
|
733
|
+
const material = createMaterialFromParent(path);
|
|
503
734
|
let lineMaterial = new THREE.LineBasicMaterial({
|
|
504
735
|
...material
|
|
505
736
|
});
|
|
@@ -507,33 +738,125 @@ function createCurveFromJson(node, parent, root) {
|
|
|
507
738
|
return new THREE.Line(geometry, lineMaterial);
|
|
508
739
|
}
|
|
509
740
|
|
|
510
|
-
function createMeshFromJson(
|
|
511
|
-
let points = new Float32Array(
|
|
512
|
-
let indices = new Uint16Array(
|
|
741
|
+
function createMeshFromJson(path) {
|
|
742
|
+
let points = new Float32Array(path[0].attributes["usd::usdgeom::mesh::points"].flat());
|
|
743
|
+
let indices = new Uint16Array(path[0].attributes["usd::usdgeom::mesh::faceVertexIndices"]);
|
|
513
744
|
const geometry = new THREE.BufferGeometry;
|
|
514
745
|
geometry.setAttribute("position", new THREE.BufferAttribute(points, 3));
|
|
515
746
|
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
|
|
516
747
|
geometry.computeVertexNormals();
|
|
517
|
-
|
|
518
|
-
let
|
|
519
|
-
|
|
520
|
-
|
|
748
|
+
var meshMaterial;
|
|
749
|
+
let gltfPbrMaterial = tryCreateMeshGltfMaterial(path);
|
|
750
|
+
if (gltfPbrMaterial) {
|
|
751
|
+
meshMaterial = gltfPbrMaterial;
|
|
752
|
+
} else {
|
|
753
|
+
const m = createMaterialFromParent(path);
|
|
754
|
+
meshMaterial = new THREE.MeshLambertMaterial({
|
|
755
|
+
...m
|
|
756
|
+
});
|
|
757
|
+
}
|
|
521
758
|
return new THREE.Mesh(geometry, meshMaterial);
|
|
522
759
|
}
|
|
523
760
|
|
|
524
|
-
function
|
|
761
|
+
function createPointsFromJsonPcdBase64(path) {
|
|
762
|
+
const base64_string = path[0].attributes["pcd::base64"];
|
|
763
|
+
const decoded = atob(base64_string);
|
|
764
|
+
const len = decoded.length;
|
|
765
|
+
const bytes = new Uint8Array(len);
|
|
766
|
+
for (let i = 0; i < len; i++) {
|
|
767
|
+
bytes[i] = decoded.charCodeAt(i);
|
|
768
|
+
}
|
|
769
|
+
const loader = new PCDLoader;
|
|
770
|
+
const points = loader.parse(bytes.buffer);
|
|
771
|
+
points.material.sizeAttenuation = false;
|
|
772
|
+
points.material.size = 2;
|
|
773
|
+
return points;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function createPoints(geometry, withColors) {
|
|
777
|
+
const material = new THREE.PointsMaterial;
|
|
778
|
+
material.sizeAttenuation = false;
|
|
779
|
+
material.fog = true;
|
|
780
|
+
material.size = 5;
|
|
781
|
+
material.color = new THREE.Color(withColors ? 16777215 : 0);
|
|
782
|
+
if (withColors) {
|
|
783
|
+
material.vertexColors = true;
|
|
784
|
+
}
|
|
785
|
+
return new THREE.Points(geometry, material);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function createPointsFromJsonArray(path) {
|
|
789
|
+
const geometry = new THREE.BufferGeometry;
|
|
790
|
+
const positions = new Float32Array(path[0].attributes["points::array::positions"].flat());
|
|
791
|
+
geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
|
|
792
|
+
const colors = path[0].attributes["points::array::colors"];
|
|
793
|
+
if (colors) {
|
|
794
|
+
const colors_ = new Float32Array(colors.flat());
|
|
795
|
+
geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors_, 3));
|
|
796
|
+
}
|
|
797
|
+
return createPoints(geometry, colors);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function base64ToArrayBuffer(str) {
|
|
801
|
+
let binary;
|
|
802
|
+
try {
|
|
803
|
+
binary = atob(str);
|
|
804
|
+
} catch (e) {
|
|
805
|
+
throw new Error("base64 encoded string is invalid");
|
|
806
|
+
}
|
|
807
|
+
const bytes = new Uint8Array(binary.length);
|
|
808
|
+
for (let i = 0; i < binary.length; ++i) {
|
|
809
|
+
bytes[i] = binary.charCodeAt(i);
|
|
810
|
+
}
|
|
811
|
+
return bytes.buffer;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function createPointsFromJsonPositionBase64(path) {
|
|
815
|
+
const geometry = new THREE.BufferGeometry;
|
|
816
|
+
const positions_base64 = path[0].attributes["points::base64::positions"];
|
|
817
|
+
const positions_bytes = base64ToArrayBuffer(positions_base64);
|
|
818
|
+
if (!positions_bytes) {
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
const positions = new Float32Array(positions_bytes);
|
|
822
|
+
geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
|
|
823
|
+
const colors_base64 = path[0].attributes["points::base64::colors"];
|
|
824
|
+
if (colors_base64) {
|
|
825
|
+
const colors_bytes = base64ToArrayBuffer(colors_base64);
|
|
826
|
+
if (colors_bytes) {
|
|
827
|
+
const colors = new Float32Array(colors_bytes);
|
|
828
|
+
geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
return createPoints(geometry, colors_base64);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function traverseTree(path, parent, pathMapping) {
|
|
835
|
+
const node = path[0];
|
|
525
836
|
let elem = new THREE.Group;
|
|
526
837
|
if (HasAttr(node, "usd::usdgeom::visibility::visibility")) {
|
|
527
838
|
if (node.attributes["usd::usdgeom::visibility::visibility"] === "invisible") {
|
|
528
839
|
return;
|
|
529
840
|
}
|
|
530
841
|
} else if (HasAttr(node, "usd::usdgeom::mesh::points")) {
|
|
531
|
-
elem = createMeshFromJson(
|
|
842
|
+
elem = createMeshFromJson(path);
|
|
532
843
|
} else if (HasAttr(node, "usd::usdgeom::basiscurves::points")) {
|
|
533
|
-
elem = createCurveFromJson(
|
|
844
|
+
elem = createCurveFromJson(path);
|
|
845
|
+
} else if (HasAttr(node, "pcd::base64")) {
|
|
846
|
+
elem = createPointsFromJsonPcdBase64(path);
|
|
847
|
+
} else if (HasAttr(node, "points::array::positions")) {
|
|
848
|
+
elem = createPointsFromJsonArray(path);
|
|
849
|
+
} else if (HasAttr(node, "points::base64::positions")) {
|
|
850
|
+
elem = createPointsFromJsonPositionBase64(path);
|
|
851
|
+
}
|
|
852
|
+
objectMap[node.name] = elem;
|
|
853
|
+
primMap[node.name] = node;
|
|
854
|
+
elem.userData.path = node.name;
|
|
855
|
+
for (let path2 of Object.entries(node.attributes || {}).filter((([k, _]) => k.startsWith("__internal_"))).map((([_, v]) => v))) {
|
|
856
|
+
(pathMapping[String(path2)] = pathMapping[String(path2)] || []).push(node.name);
|
|
534
857
|
}
|
|
535
858
|
parent.add(elem);
|
|
536
|
-
if (
|
|
859
|
+
if (path.length > 1) {
|
|
537
860
|
elem.matrixAutoUpdate = false;
|
|
538
861
|
let matrixNode = node.attributes && node.attributes["usd::xformop::transform"] ? node.attributes["usd::xformop::transform"].flat() : null;
|
|
539
862
|
if (matrixNode) {
|
|
@@ -543,24 +866,30 @@ function traverseTree(node, parent, root, parentNode = undefined) {
|
|
|
543
866
|
elem.matrix = matrix;
|
|
544
867
|
}
|
|
545
868
|
}
|
|
546
|
-
(node.children || []).forEach((child => traverseTree(child, elem || parent,
|
|
869
|
+
(node.children || []).forEach((child => traverseTree([ child, ...path ], elem || parent, pathMapping)));
|
|
547
870
|
}
|
|
548
871
|
|
|
549
|
-
function composeAndRender() {
|
|
872
|
+
async function composeAndRender() {
|
|
550
873
|
if (scene) {
|
|
551
874
|
scene.children = [];
|
|
552
875
|
}
|
|
876
|
+
objectMap = {};
|
|
877
|
+
primMap = {};
|
|
553
878
|
if (datas.length === 0) {
|
|
554
879
|
return;
|
|
555
880
|
}
|
|
556
881
|
let tree = null;
|
|
557
882
|
let dataArray = datas.map((arr => arr[1]));
|
|
558
|
-
tree = compose3(dataArray);
|
|
883
|
+
tree = await compose3(dataArray);
|
|
559
884
|
if (!tree) {
|
|
560
885
|
console.error("No result from composition");
|
|
561
886
|
return;
|
|
562
887
|
}
|
|
563
|
-
|
|
888
|
+
if (!scene) {
|
|
889
|
+
init();
|
|
890
|
+
}
|
|
891
|
+
let pathMapping = {};
|
|
892
|
+
traverseTree([ tree ], scene, pathMapping);
|
|
564
893
|
if (autoCamera) {
|
|
565
894
|
const boundingBox = new THREE.Box3;
|
|
566
895
|
boundingBox.setFromObject(scene);
|
|
@@ -575,9 +904,9 @@ function composeAndRender() {
|
|
|
575
904
|
}
|
|
576
905
|
}
|
|
577
906
|
|
|
578
|
-
function parse(m, name) {
|
|
907
|
+
async function parse(m, name) {
|
|
579
908
|
datas.push([ name, m ]);
|
|
580
|
-
composeAndRender();
|
|
909
|
+
await composeAndRender();
|
|
581
910
|
return scene;
|
|
582
911
|
}
|
|
583
912
|
|
|
@@ -608,13 +937,7 @@ class IFCXLoader extends Loader {
|
|
|
608
937
|
loader.load(url, (json => this.parse(json, _onLoad, _onError)), onProgress, onError);
|
|
609
938
|
}
|
|
610
939
|
parse(json, onLoad, onError) {
|
|
611
|
-
|
|
612
|
-
onLoad(parse(json));
|
|
613
|
-
} catch (e) {
|
|
614
|
-
onError(e);
|
|
615
|
-
} finally {
|
|
616
|
-
clear();
|
|
617
|
-
}
|
|
940
|
+
parse(json).then((scene => onLoad(scene))).catch((err => onError(err))).finally((() => clear()));
|
|
618
941
|
}
|
|
619
942
|
}
|
|
620
943
|
|
|
@@ -688,7 +1011,7 @@ class IFCXCloudLoader extends Loader$1 {
|
|
|
688
1011
|
if (!this.viewer.scene) return this;
|
|
689
1012
|
const textDecoder = new TextDecoder;
|
|
690
1013
|
const json = JSON.parse(textDecoder.decode(arrayBuffer));
|
|
691
|
-
const scene = parse(json);
|
|
1014
|
+
const scene = await parse(json);
|
|
692
1015
|
clear();
|
|
693
1016
|
let handle = 0;
|
|
694
1017
|
scene.traverse((object => {
|