@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.
@@ -1,95 +1,109 @@
1
1
  import { Loader as Loader$1, GLTFLoadingManager, ModelImpl, loaders } from "@inweb/viewer-three";
2
2
 
3
- import { Box3, Vector3, Group, Matrix4, Scene, PerspectiveCamera, BufferGeometry, BufferAttribute, MeshBasicMaterial, Mesh, LineBasicMaterial, Line, Color, Loader, FileLoader } from "three";
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
- MeshBasicMaterial: MeshBasicMaterial,
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
- function getChildByName(root, childName, skip = 0) {
22
- let fragments = childName.replace(/^<\/|^\/|>$/g, "").split("/");
23
- for (let i = 0; i < skip; ++i) {
24
- fragments.shift();
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
- if (fragments.length == 0) {
32
- return start;
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
- function GetNode(node, path) {
37
- if (path === "") return node;
38
- let parts = path.split("/");
39
- let child = node.children.get(parts[0]);
40
- if (child) {
41
- if (parts.length === 1) {
42
- return child;
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 GetNode(child, GetTail(path));
45
- } else {
46
- return null;
56
+ return Promise.resolve(this.layers.get(uri));
47
57
  }
48
- }
49
-
50
- function GetHead(path) {
51
- return path.split("/")[0];
52
- }
53
-
54
- function GetTail(path) {
55
- let parts = path.split("/");
56
- parts.shift();
57
- return parts.join("/");
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 MakeNode(node) {
61
- return {
62
- node: node,
63
- children: new Map,
64
- attributes: new Map
65
- };
71
+ function log(bla) {
72
+ {
73
+ console.log(`${JSON.stringify(arguments)}`);
74
+ }
66
75
  }
67
76
 
68
- function ConvertToCompositionNode(path, inputNodes) {
69
- let compositionNode = {
70
- path: path,
71
- children: {},
72
- inherits: {},
73
- attributes: {}
74
- };
75
- inputNodes.forEach((node => {
76
- Object.keys(node.children).forEach((childName => {
77
- compositionNode.children[childName] = node.children[childName];
78
- }));
79
- Object.keys(node.inherits).forEach((inheritName => {
80
- let ih = node.inherits[inheritName];
81
- if (ih === null) {
82
- delete compositionNode.inherits[inheritName];
83
- } else {
84
- compositionNode.inherits[inheritName] = ih;
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
- Object.keys(node.attributes).forEach((attrName => {
88
- compositionNode.attributes[attrName] = node.attributes[attrName];
89
- }));
90
- }));
91
- return compositionNode;
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 ConvertNodes(input) {
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, ConvertToCompositionNode(path, inputNodes));
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 ExpandNewNode([ ...roots.values() ][0], nodes);
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, ExpandNewNode(root, nodes));
243
+ pseudoRoot.children.set(root, ComposeNodeFromPath(root, nodes));
172
244
  }));
173
245
  return pseudoRoot;
174
246
  }
175
247
 
176
- function ExpandNewNode(node, nodes) {
177
- return ExpandNode(node, MakeNode(node), nodes);
248
+ function ComposeNodeFromPath(path, preCompositionNodes) {
249
+ return ComposeNode(path, MakePostCompositionNode(path), preCompositionNodes);
178
250
  }
179
251
 
180
- function ExpandNode(path, node, nodes) {
181
- let input = nodes.get(path);
182
- if (input) {
183
- AddDataFromInput(input, node, nodes);
252
+ function ComposeNode(path, postCompositionNode, preCompositionNodes) {
253
+ let preCompositionNode = preCompositionNodes.get(path);
254
+ if (preCompositionNode) {
255
+ AddDataFromPreComposition(preCompositionNode, postCompositionNode, preCompositionNodes);
184
256
  }
185
- node.children.forEach(((child, name) => {
186
- ExpandNode(`${path}/${name}`, child, nodes);
257
+ postCompositionNode.children.forEach(((child, name) => {
258
+ ComposeNode(`${path}/${name}`, child, preCompositionNodes);
187
259
  }));
188
- return node;
260
+ return postCompositionNode;
189
261
  }
190
262
 
191
- function AddDataFromInput(input, node, nodes) {
192
- Object.values(input.inherits).forEach((inherit => {
193
- let classNode = ExpandNewNode(GetHead(inherit), nodes);
194
- let subnode = GetNode(classNode, GetTail(inherit));
195
- if (!subnode) throw new Error(`Unknown node ${inherit}`);
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 = ExpandNewNode(GetHead(child), nodes);
206
- let subnode = GetNode(classNode, GetTail(child));
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 === "Relation") {
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
- if (!Object.hasOwn(value, key)) {
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 LoadIfcxFile(file, checkSchemas = true, createArtificialRoot = false) {
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 = ConvertNodes(inputNodes);
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 federated = Federate(files);
450
- let tree = LoadIfcxFile(federated, true, true);
451
- return TreeNodeToComposedObject("", tree, federated.schemas);
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 createMaterialFromParent(parent, root) {
478
- let reference = parent.attributes["usd::usdshade::materialbindingapi::material::binding"];
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
- if (reference) {
485
- const materialNode = getChildByName(root, reference.ref);
486
- if (materialNode) {
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
- if (materialNode?.attributes["bsi::presentation::opacity"]) {
718
+ const opacity = p.attributes["bsi::ifc::presentation::opacity"];
719
+ if (opacity) {
490
720
  material.transparent = true;
491
- material.opacity = materialNode.attributes["bsi::presentation::opacity"];
721
+ material.opacity = opacity;
492
722
  }
723
+ break;
493
724
  }
494
725
  }
495
726
  return material;
496
727
  }
497
728
 
498
- function createCurveFromJson(node, parent, root) {
499
- let points = new Float32Array(node.attributes["usd::usdgeom::basiscurves::points"].flat());
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(parent, root);
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(node, parent, root) {
511
- let points = new Float32Array(node.attributes["usd::usdgeom::mesh::points"].flat());
512
- let indices = new Uint16Array(node.attributes["usd::usdgeom::mesh::faceVertexIndices"]);
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
- const material = createMaterialFromParent(parent, root);
518
- let meshMaterial = new THREE.MeshBasicMaterial({
519
- ...material
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 traverseTree(node, parent, root, parentNode = undefined) {
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(node, parentNode, root);
842
+ elem = createMeshFromJson(path);
532
843
  } else if (HasAttr(node, "usd::usdgeom::basiscurves::points")) {
533
- elem = createCurveFromJson(node, parentNode, root);
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 (node !== root) {
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, root, node)));
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
- traverseTree(tree, scene || init(), tree);
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
- try {
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 => {