@inweb/viewer-three 26.8.0 → 26.8.2
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/components/RoomEnvironmentComponent.js +75 -40
- package/dist/plugins/components/RoomEnvironmentComponent.js.map +1 -1
- package/dist/plugins/components/RoomEnvironmentComponent.min.js +1 -1
- package/dist/plugins/components/StatsPanelComponent.js +1 -1
- package/dist/plugins/components/StatsPanelComponent.js.map +1 -1
- package/dist/plugins/components/StatsPanelComponent.min.js +1 -1
- package/dist/plugins/components/StatsPanelComponent.module.js +1 -1
- package/dist/plugins/components/StatsPanelComponent.module.js.map +1 -1
- package/dist/plugins/loaders/GLTFCloudLoader.js +225 -94
- package/dist/plugins/loaders/GLTFCloudLoader.js.map +1 -1
- package/dist/plugins/loaders/GLTFCloudLoader.min.js +1 -1
- package/dist/plugins/loaders/IFCXLoader.js +1977 -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 +31149 -5503
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +406 -298
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/Viewer.d.ts +17 -3
- package/lib/Viewer/commands/SetDefaultViewPosition.d.ts +6 -6
- package/lib/Viewer/components/HighlighterComponent.d.ts +5 -4
- package/lib/Viewer/components/SelectionComponent.d.ts +1 -1
- package/lib/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.d.ts +3 -1
- package/lib/Viewer/models/IModelImpl.d.ts +27 -0
- package/lib/Viewer/models/ModelImpl.d.ts +27 -0
- package/lib/Viewer/scenes/Helpers.d.ts +7 -0
- package/lib/index.d.ts +2 -1
- package/package.json +9 -9
- package/plugins/components/StatsPanelComponent.ts +1 -1
- 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/Viewer.ts +124 -48
- package/src/Viewer/commands/SetDefaultViewPosition.ts +8 -8
- package/src/Viewer/components/CameraComponent.ts +20 -16
- package/src/Viewer/components/ExtentsComponent.ts +1 -0
- package/src/Viewer/components/HighlighterComponent.ts +78 -80
- package/src/Viewer/components/LightComponent.ts +10 -4
- package/src/Viewer/components/ResizeCanvasComponent.ts +1 -0
- package/src/Viewer/components/SelectionComponent.ts +1 -1
- package/src/Viewer/helpers/WCSHelper.ts +8 -5
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +33 -16
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +12 -5
- package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +100 -20
- package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +4 -2
- package/src/Viewer/loaders/GLTFFileLoader.ts +1 -1
- package/src/Viewer/models/IModelImpl.ts +67 -0
- package/src/Viewer/models/ModelImpl.ts +214 -0
- package/src/Viewer/postprocessing/SSAARenderPass.js +245 -0
- package/src/Viewer/scenes/Helpers.ts +42 -0
- package/src/index.ts +2 -1
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
// (C) buildingSMART International
|
|
30
30
|
// published under MIT license
|
|
31
31
|
|
|
32
|
+
/* eslint-disable lines-between-class-members */
|
|
32
33
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
33
34
|
/* eslint-disable prefer-const */
|
|
34
35
|
/* eslint-disable no-useless-catch */
|
|
@@ -38,12 +39,18 @@ import {
|
|
|
38
39
|
BufferAttribute,
|
|
39
40
|
BufferGeometry,
|
|
40
41
|
Color,
|
|
42
|
+
Euler,
|
|
43
|
+
Float32BufferAttribute,
|
|
41
44
|
Group,
|
|
42
45
|
Line,
|
|
43
46
|
LineBasicMaterial,
|
|
44
47
|
Matrix4,
|
|
45
48
|
Mesh,
|
|
46
49
|
MeshBasicMaterial,
|
|
50
|
+
MeshLambertMaterial,
|
|
51
|
+
MeshStandardMaterial,
|
|
52
|
+
Points,
|
|
53
|
+
PointsMaterial,
|
|
47
54
|
PerspectiveCamera,
|
|
48
55
|
Scene,
|
|
49
56
|
Vector3,
|
|
@@ -54,88 +61,107 @@ const THREE = {
|
|
|
54
61
|
BufferAttribute,
|
|
55
62
|
BufferGeometry,
|
|
56
63
|
Color,
|
|
64
|
+
Euler,
|
|
65
|
+
Float32BufferAttribute,
|
|
57
66
|
Group,
|
|
58
67
|
Line,
|
|
59
68
|
LineBasicMaterial,
|
|
60
69
|
Matrix4,
|
|
61
70
|
Mesh,
|
|
62
71
|
MeshBasicMaterial,
|
|
72
|
+
MeshLambertMaterial,
|
|
73
|
+
MeshStandardMaterial,
|
|
74
|
+
Points,
|
|
75
|
+
PointsMaterial,
|
|
63
76
|
PerspectiveCamera,
|
|
64
77
|
Scene,
|
|
65
78
|
Vector3,
|
|
66
79
|
};
|
|
67
80
|
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
// ifcx-core/layers/layer-providers.ts
|
|
82
|
+
var StackedLayerProvider = class {
|
|
83
|
+
providers;
|
|
84
|
+
constructor(providers) {
|
|
85
|
+
this.providers = providers;
|
|
73
86
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
async GetLayerByURI(uri) {
|
|
88
|
+
let errorStack = [];
|
|
89
|
+
for (let provider of this.providers) {
|
|
90
|
+
let layer = await provider.GetLayerByURI(uri);
|
|
91
|
+
if (!(layer instanceof Error)) {
|
|
92
|
+
return layer;
|
|
93
|
+
} else {
|
|
94
|
+
errorStack.push(layer);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return new Error(JSON.stringify(errorStack));
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var InMemoryLayerProvider = class {
|
|
101
|
+
layers;
|
|
102
|
+
constructor() {
|
|
103
|
+
this.layers = /* @__PURE__ */ new Map();
|
|
104
|
+
}
|
|
105
|
+
GetLayerByURI(uri) {
|
|
106
|
+
if (!this.layers.has(uri)) {
|
|
107
|
+
return new Error(`File with uri "${uri}" not found`);
|
|
108
|
+
}
|
|
109
|
+
return Promise.resolve(this.layers.get(uri));
|
|
79
110
|
}
|
|
80
|
-
|
|
81
|
-
|
|
111
|
+
add(file) {
|
|
112
|
+
if (this.layers.has(file.header.id)) {
|
|
113
|
+
throw new Error(`Inserting file with duplicate ID "${file.header.id}"`);
|
|
114
|
+
}
|
|
115
|
+
this.layers.set(file.header.id, file);
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
AddAll(files) {
|
|
119
|
+
files.forEach((f) => this.add(f));
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// ifcx-core/util/log.ts
|
|
125
|
+
var LOG_ENABLED = true;
|
|
126
|
+
function log(bla) {
|
|
127
|
+
if (LOG_ENABLED) {
|
|
128
|
+
console.log(`${JSON.stringify(arguments)}`);
|
|
82
129
|
}
|
|
83
130
|
}
|
|
84
131
|
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
132
|
+
// ifcx-core/layers/fetch-layer-provider.ts
|
|
133
|
+
var FetchLayerProvider = class {
|
|
134
|
+
layers;
|
|
135
|
+
constructor() {
|
|
136
|
+
this.layers = /* @__PURE__ */ new Map();
|
|
137
|
+
}
|
|
138
|
+
async FetchJson(url) {
|
|
139
|
+
let result = await fetch(url);
|
|
140
|
+
if (!result.ok) {
|
|
141
|
+
return new Error(`Failed to fetch ${url}: ${result.status}`);
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
return await result.json();
|
|
145
|
+
} catch (e) {
|
|
146
|
+
log(url);
|
|
147
|
+
return new Error(`Failed to parse json at ${url}: ${e}`);
|
|
93
148
|
}
|
|
94
|
-
return GetNode(child, GetTail(path));
|
|
95
|
-
} else {
|
|
96
|
-
return null;
|
|
97
149
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
let parts = path.split("/");
|
|
104
|
-
parts.shift();
|
|
105
|
-
return parts.join("/");
|
|
106
|
-
}
|
|
107
|
-
function MakeNode(node) {
|
|
108
|
-
return {
|
|
109
|
-
node,
|
|
110
|
-
children: /* @__PURE__ */ new Map(),
|
|
111
|
-
attributes: /* @__PURE__ */ new Map(),
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
function ConvertToCompositionNode(path, inputNodes) {
|
|
115
|
-
let compositionNode = {
|
|
116
|
-
path,
|
|
117
|
-
children: {},
|
|
118
|
-
inherits: {},
|
|
119
|
-
attributes: {},
|
|
120
|
-
};
|
|
121
|
-
inputNodes.forEach((node) => {
|
|
122
|
-
Object.keys(node.children).forEach((childName) => {
|
|
123
|
-
compositionNode.children[childName] = node.children[childName];
|
|
124
|
-
});
|
|
125
|
-
Object.keys(node.inherits).forEach((inheritName) => {
|
|
126
|
-
let ih = node.inherits[inheritName];
|
|
127
|
-
if (ih === null) {
|
|
128
|
-
delete compositionNode.inherits[inheritName];
|
|
129
|
-
} else {
|
|
130
|
-
compositionNode.inherits[inheritName] = ih;
|
|
150
|
+
async GetLayerByURI(uri) {
|
|
151
|
+
if (!this.layers.has(uri)) {
|
|
152
|
+
let fetched = await this.FetchJson(uri);
|
|
153
|
+
if (fetched instanceof Error) {
|
|
154
|
+
return new Error(`File with id "${uri}" not found`);
|
|
131
155
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
156
|
+
let file = fetched;
|
|
157
|
+
this.layers.set(uri, file);
|
|
158
|
+
return file;
|
|
159
|
+
}
|
|
160
|
+
return this.layers.get(uri);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// ifcx-core/util/mm.ts
|
|
139
165
|
function MMSet(map, key, value) {
|
|
140
166
|
if (map.has(key)) {
|
|
141
167
|
map.get(key)?.push(value);
|
|
@@ -143,6 +169,9 @@ function MMSet(map, key, value) {
|
|
|
143
169
|
map.set(key, [value]);
|
|
144
170
|
}
|
|
145
171
|
}
|
|
172
|
+
|
|
173
|
+
// ifcx-core/composition/cycles.ts
|
|
174
|
+
var CycleError = class extends Error {};
|
|
146
175
|
function FindRootsOrCycles(nodes) {
|
|
147
176
|
let dependencies = /* @__PURE__ */ new Map();
|
|
148
177
|
let dependents = /* @__PURE__ */ new Map();
|
|
@@ -182,20 +211,78 @@ function FindRootsOrCycles(nodes) {
|
|
|
182
211
|
}
|
|
183
212
|
return roots;
|
|
184
213
|
}
|
|
185
|
-
|
|
214
|
+
|
|
215
|
+
// ifcx-core/composition/path.ts
|
|
216
|
+
function GetHead(path) {
|
|
217
|
+
return path.split("/")[0];
|
|
218
|
+
}
|
|
219
|
+
function GetTail(path) {
|
|
220
|
+
let parts = path.split("/");
|
|
221
|
+
parts.shift();
|
|
222
|
+
return parts.join("/");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ifcx-core/composition/node.ts
|
|
226
|
+
function MakePostCompositionNode(node) {
|
|
227
|
+
return {
|
|
228
|
+
node,
|
|
229
|
+
children: /* @__PURE__ */ new Map(),
|
|
230
|
+
attributes: /* @__PURE__ */ new Map(),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function GetChildNodeWithPath(node, path) {
|
|
234
|
+
if (path === "") return node;
|
|
235
|
+
let parts = path.split("/");
|
|
236
|
+
let child = node.children.get(parts[0]);
|
|
237
|
+
if (child) {
|
|
238
|
+
if (parts.length === 1) {
|
|
239
|
+
return child;
|
|
240
|
+
}
|
|
241
|
+
return GetChildNodeWithPath(child, GetTail(path));
|
|
242
|
+
} else {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ifcx-core/composition/compose.ts
|
|
248
|
+
function FlattenPathToPreCompositionNode(path, inputNodes) {
|
|
249
|
+
let compositionNode = {
|
|
250
|
+
path,
|
|
251
|
+
children: {},
|
|
252
|
+
inherits: {},
|
|
253
|
+
attributes: {},
|
|
254
|
+
};
|
|
255
|
+
inputNodes.forEach((node) => {
|
|
256
|
+
Object.keys(node.children).forEach((childName) => {
|
|
257
|
+
compositionNode.children[childName] = node.children[childName];
|
|
258
|
+
});
|
|
259
|
+
Object.keys(node.inherits).forEach((inheritName) => {
|
|
260
|
+
let ih = node.inherits[inheritName];
|
|
261
|
+
if (ih === null) {
|
|
262
|
+
delete compositionNode.inherits[inheritName];
|
|
263
|
+
} else {
|
|
264
|
+
compositionNode.inherits[inheritName] = ih;
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
Object.keys(node.attributes).forEach((attrName) => {
|
|
268
|
+
compositionNode.attributes[attrName] = node.attributes[attrName];
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
return compositionNode;
|
|
272
|
+
}
|
|
273
|
+
function FlattenCompositionInput(input) {
|
|
186
274
|
let compositionNodes = /* @__PURE__ */ new Map();
|
|
187
275
|
for (let [path, inputNodes] of input) {
|
|
188
|
-
compositionNodes.set(path,
|
|
276
|
+
compositionNodes.set(path, FlattenPathToPreCompositionNode(path, inputNodes));
|
|
189
277
|
}
|
|
190
278
|
return compositionNodes;
|
|
191
279
|
}
|
|
192
|
-
var CycleError = class extends Error {};
|
|
193
280
|
function ExpandFirstRootInInput(nodes) {
|
|
194
281
|
let roots = FindRootsOrCycles(nodes);
|
|
195
282
|
if (!roots) {
|
|
196
283
|
throw new CycleError();
|
|
197
284
|
}
|
|
198
|
-
return
|
|
285
|
+
return ComposeNodeFromPath([...roots.values()][0], nodes);
|
|
199
286
|
}
|
|
200
287
|
function CreateArtificialRoot(nodes) {
|
|
201
288
|
let roots = FindRootsOrCycles(nodes);
|
|
@@ -208,28 +295,28 @@ function CreateArtificialRoot(nodes) {
|
|
|
208
295
|
children: /* @__PURE__ */ new Map(),
|
|
209
296
|
};
|
|
210
297
|
roots.forEach((root) => {
|
|
211
|
-
pseudoRoot.children.set(root,
|
|
298
|
+
pseudoRoot.children.set(root, ComposeNodeFromPath(root, nodes));
|
|
212
299
|
});
|
|
213
300
|
return pseudoRoot;
|
|
214
301
|
}
|
|
215
|
-
function
|
|
216
|
-
return
|
|
302
|
+
function ComposeNodeFromPath(path, preCompositionNodes) {
|
|
303
|
+
return ComposeNode(path, MakePostCompositionNode(path), preCompositionNodes);
|
|
217
304
|
}
|
|
218
|
-
function
|
|
219
|
-
let
|
|
220
|
-
if (
|
|
221
|
-
|
|
305
|
+
function ComposeNode(path, postCompositionNode, preCompositionNodes) {
|
|
306
|
+
let preCompositionNode = preCompositionNodes.get(path);
|
|
307
|
+
if (preCompositionNode) {
|
|
308
|
+
AddDataFromPreComposition(preCompositionNode, postCompositionNode, preCompositionNodes);
|
|
222
309
|
}
|
|
223
|
-
|
|
224
|
-
|
|
310
|
+
postCompositionNode.children.forEach((child, name) => {
|
|
311
|
+
ComposeNode(`${path}/${name}`, child, preCompositionNodes);
|
|
225
312
|
});
|
|
226
|
-
return
|
|
313
|
+
return postCompositionNode;
|
|
227
314
|
}
|
|
228
|
-
function
|
|
229
|
-
Object.values(input.inherits).forEach((
|
|
230
|
-
let classNode =
|
|
231
|
-
let subnode =
|
|
232
|
-
if (!subnode) throw new Error(`Unknown node ${
|
|
315
|
+
function AddDataFromPreComposition(input, node, nodes) {
|
|
316
|
+
Object.values(input.inherits).forEach((inheritPath) => {
|
|
317
|
+
let classNode = ComposeNodeFromPath(GetHead(inheritPath), nodes);
|
|
318
|
+
let subnode = GetChildNodeWithPath(classNode, GetTail(inheritPath));
|
|
319
|
+
if (!subnode) throw new Error(`Unknown node ${inheritPath}`);
|
|
233
320
|
subnode.children.forEach((child, childName) => {
|
|
234
321
|
node.children.set(childName, child);
|
|
235
322
|
});
|
|
@@ -239,8 +326,8 @@ function AddDataFromInput(input, node, nodes) {
|
|
|
239
326
|
});
|
|
240
327
|
Object.entries(input.children).forEach(([childName, child]) => {
|
|
241
328
|
if (child !== null) {
|
|
242
|
-
let classNode =
|
|
243
|
-
let subnode =
|
|
329
|
+
let classNode = ComposeNodeFromPath(GetHead(child), nodes);
|
|
330
|
+
let subnode = GetChildNodeWithPath(classNode, GetTail(child));
|
|
244
331
|
if (!subnode) throw new Error(`Unknown node ${child}`);
|
|
245
332
|
node.children.set(childName, subnode);
|
|
246
333
|
} else {
|
|
@@ -252,29 +339,12 @@ function AddDataFromInput(input, node, nodes) {
|
|
|
252
339
|
});
|
|
253
340
|
}
|
|
254
341
|
|
|
255
|
-
//
|
|
256
|
-
function MMSet2(map, key, value) {
|
|
257
|
-
if (map.has(key)) {
|
|
258
|
-
map.get(key)?.push(value);
|
|
259
|
-
} else {
|
|
260
|
-
map.set(key, [value]);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
function ToInputNodes(data) {
|
|
264
|
-
let inputNodes = /* @__PURE__ */ new Map();
|
|
265
|
-
data.forEach((ifcxNode) => {
|
|
266
|
-
let node = {
|
|
267
|
-
path: ifcxNode.path,
|
|
268
|
-
children: ifcxNode.children ? ifcxNode.children : {},
|
|
269
|
-
inherits: ifcxNode.inherits ? ifcxNode.inherits : {},
|
|
270
|
-
attributes: ifcxNode.attributes ? ifcxNode.attributes : {},
|
|
271
|
-
};
|
|
272
|
-
MMSet2(inputNodes, node.path, node);
|
|
273
|
-
});
|
|
274
|
-
return inputNodes;
|
|
275
|
-
}
|
|
342
|
+
// ifcx-core/schema/schema-validation.ts
|
|
276
343
|
var SchemaValidationError = class extends Error {};
|
|
277
344
|
function ValidateAttributeValue(desc, value, path, schemas) {
|
|
345
|
+
if (desc.optional && value === void 0) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
278
348
|
if (desc.inherits) {
|
|
279
349
|
desc.inherits.forEach((inheritedSchemaID) => {
|
|
280
350
|
let inheritedSchema = schemas[inheritedSchemaID];
|
|
@@ -312,7 +382,7 @@ function ValidateAttributeValue(desc, value, path, schemas) {
|
|
|
312
382
|
if (typeof value !== "number") {
|
|
313
383
|
throw new SchemaValidationError(`Expected "${value}" to be of type real`);
|
|
314
384
|
}
|
|
315
|
-
} else if (desc.dataType === "
|
|
385
|
+
} else if (desc.dataType === "Reference") {
|
|
316
386
|
if (typeof value !== "string") {
|
|
317
387
|
throw new SchemaValidationError(`Expected "${value}" to be of type string`);
|
|
318
388
|
}
|
|
@@ -322,7 +392,10 @@ function ValidateAttributeValue(desc, value, path, schemas) {
|
|
|
322
392
|
}
|
|
323
393
|
if (desc.objectRestrictions) {
|
|
324
394
|
Object.keys(desc.objectRestrictions.values).forEach((key) => {
|
|
325
|
-
|
|
395
|
+
let optional = desc.objectRestrictions.values[key].optional;
|
|
396
|
+
let hasOwn = Object.hasOwn(value, key);
|
|
397
|
+
if (optional && !hasOwn) return;
|
|
398
|
+
if (!hasOwn) {
|
|
326
399
|
throw new SchemaValidationError(`Expected "${value}" to have key ${key}`);
|
|
327
400
|
}
|
|
328
401
|
ValidateAttributeValue(desc.objectRestrictions.values[key], value[key], path + "." + key, schemas);
|
|
@@ -341,27 +414,46 @@ function ValidateAttributeValue(desc, value, path, schemas) {
|
|
|
341
414
|
}
|
|
342
415
|
function Validate(schemas, inputNodes) {
|
|
343
416
|
inputNodes.forEach((node) => {
|
|
344
|
-
Object.keys(node.attributes)
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
let value = node.attributes[schemaID];
|
|
350
|
-
try {
|
|
351
|
-
ValidateAttributeValue(schema.value, value, "", schemas);
|
|
352
|
-
} catch (e) {
|
|
353
|
-
if (e instanceof SchemaValidationError) {
|
|
354
|
-
throw new SchemaValidationError(`Error validating ["${node.path}"].attributes["${schemaID}"]: ${e.message}`);
|
|
355
|
-
} else {
|
|
356
|
-
throw e;
|
|
417
|
+
Object.keys(node.attributes)
|
|
418
|
+
.filter((v) => !v.startsWith("__internal"))
|
|
419
|
+
.forEach((schemaID) => {
|
|
420
|
+
if (!schemas[schemaID]) {
|
|
421
|
+
throw new SchemaValidationError(`Missing schema "${schemaID}" referenced by ["${node.path}"].attributes`);
|
|
357
422
|
}
|
|
358
|
-
|
|
359
|
-
|
|
423
|
+
let schema = schemas[schemaID];
|
|
424
|
+
let value = node.attributes[schemaID];
|
|
425
|
+
try {
|
|
426
|
+
ValidateAttributeValue(schema.value, value, "", schemas);
|
|
427
|
+
} catch (e) {
|
|
428
|
+
if (e instanceof SchemaValidationError) {
|
|
429
|
+
throw new SchemaValidationError(
|
|
430
|
+
`Error validating ["${node.path}"].attributes["${schemaID}"]: ${e.message}`
|
|
431
|
+
);
|
|
432
|
+
} else {
|
|
433
|
+
throw e;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ifcx-core/workflows.ts
|
|
441
|
+
function ToInputNodes(data) {
|
|
442
|
+
let inputNodes = /* @__PURE__ */ new Map();
|
|
443
|
+
data.forEach((ifcxNode) => {
|
|
444
|
+
let node = {
|
|
445
|
+
path: ifcxNode.path,
|
|
446
|
+
children: ifcxNode.children ? ifcxNode.children : {},
|
|
447
|
+
inherits: ifcxNode.inherits ? ifcxNode.inherits : {},
|
|
448
|
+
attributes: ifcxNode.attributes ? ifcxNode.attributes : {},
|
|
449
|
+
};
|
|
450
|
+
MMSet(inputNodes, node.path, node);
|
|
360
451
|
});
|
|
452
|
+
return inputNodes;
|
|
361
453
|
}
|
|
362
|
-
function LoadIfcxFile(file, checkSchemas = true, createArtificialRoot =
|
|
454
|
+
function LoadIfcxFile(file, checkSchemas = true, createArtificialRoot = true) {
|
|
363
455
|
let inputNodes = ToInputNodes(file.data);
|
|
364
|
-
let compositionNodes =
|
|
456
|
+
let compositionNodes = FlattenCompositionInput(inputNodes);
|
|
365
457
|
try {
|
|
366
458
|
if (checkSchemas) {
|
|
367
459
|
Validate(file.schemas, compositionNodes);
|
|
@@ -376,8 +468,12 @@ function LoadIfcxFile(file, checkSchemas = true, createArtificialRoot = false) {
|
|
|
376
468
|
}
|
|
377
469
|
}
|
|
378
470
|
function Federate(files) {
|
|
471
|
+
if (files.length === 0) {
|
|
472
|
+
throw new Error(`Trying to federate empty set of files`);
|
|
473
|
+
}
|
|
379
474
|
let result = {
|
|
380
475
|
header: files[0].header,
|
|
476
|
+
imports: [],
|
|
381
477
|
schemas: {},
|
|
382
478
|
data: [],
|
|
383
479
|
};
|
|
@@ -425,6 +521,7 @@ function Collapse(nodes, deleteEmpty = false) {
|
|
|
425
521
|
function Prune(file, deleteEmpty = false) {
|
|
426
522
|
let result = {
|
|
427
523
|
header: file.header,
|
|
524
|
+
imports: [],
|
|
428
525
|
schemas: file.schemas,
|
|
429
526
|
data: [],
|
|
430
527
|
};
|
|
@@ -442,7 +539,99 @@ function Prune(file, deleteEmpty = false) {
|
|
|
442
539
|
return result;
|
|
443
540
|
}
|
|
444
541
|
|
|
445
|
-
//
|
|
542
|
+
// ifcx-core/layers/layer-stack.ts
|
|
543
|
+
var IfcxLayerStack = class {
|
|
544
|
+
// main layer at 0
|
|
545
|
+
layers;
|
|
546
|
+
tree;
|
|
547
|
+
schemas;
|
|
548
|
+
federated;
|
|
549
|
+
constructor(layers) {
|
|
550
|
+
this.layers = layers;
|
|
551
|
+
this.Compose();
|
|
552
|
+
}
|
|
553
|
+
GetLayerIds() {
|
|
554
|
+
return this.layers.map((l) => l.header.id);
|
|
555
|
+
}
|
|
556
|
+
Compose() {
|
|
557
|
+
this.federated = Federate(this.layers);
|
|
558
|
+
this.schemas = this.federated.schemas;
|
|
559
|
+
this.tree = LoadIfcxFile(this.federated);
|
|
560
|
+
}
|
|
561
|
+
GetFullTree() {
|
|
562
|
+
this.Compose();
|
|
563
|
+
return this.tree;
|
|
564
|
+
}
|
|
565
|
+
GetFederatedLayer() {
|
|
566
|
+
return this.federated;
|
|
567
|
+
}
|
|
568
|
+
GetSchemas() {
|
|
569
|
+
return this.schemas;
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
var IfcxLayerStackBuilder = class {
|
|
573
|
+
provider;
|
|
574
|
+
mainLayerId = null;
|
|
575
|
+
constructor(provider) {
|
|
576
|
+
this.provider = provider;
|
|
577
|
+
}
|
|
578
|
+
FromId(id) {
|
|
579
|
+
this.mainLayerId = id;
|
|
580
|
+
return this;
|
|
581
|
+
}
|
|
582
|
+
async Build() {
|
|
583
|
+
if (!this.mainLayerId) throw new Error(`no main layer ID specified`);
|
|
584
|
+
let layers = await this.BuildLayerSet(this.mainLayerId);
|
|
585
|
+
if (layers instanceof Error) {
|
|
586
|
+
return layers;
|
|
587
|
+
}
|
|
588
|
+
try {
|
|
589
|
+
return new IfcxLayerStack(layers);
|
|
590
|
+
} catch (e) {
|
|
591
|
+
return e;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
async SatisfyDependencies(activeLayer, placed, orderedLayers) {
|
|
595
|
+
let pending = [];
|
|
596
|
+
for (const impt of activeLayer.imports) {
|
|
597
|
+
if (!placed.has(impt.uri)) {
|
|
598
|
+
let layer = await this.provider.GetLayerByURI(impt.uri);
|
|
599
|
+
if (layer instanceof Error) {
|
|
600
|
+
return layer;
|
|
601
|
+
}
|
|
602
|
+
pending.push(layer);
|
|
603
|
+
placed.set(impt.uri, true);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
let temp = [];
|
|
607
|
+
for (const layer of pending) {
|
|
608
|
+
temp.push(layer);
|
|
609
|
+
let layers = await this.SatisfyDependencies(layer, placed, orderedLayers);
|
|
610
|
+
if (layers instanceof Error) {
|
|
611
|
+
return layers;
|
|
612
|
+
}
|
|
613
|
+
temp.push(...layers);
|
|
614
|
+
}
|
|
615
|
+
temp.forEach((t) => orderedLayers.push(t));
|
|
616
|
+
return temp;
|
|
617
|
+
}
|
|
618
|
+
async BuildLayerSet(activeLayerID) {
|
|
619
|
+
let activeLayer = await this.provider.GetLayerByURI(activeLayerID);
|
|
620
|
+
if (activeLayer instanceof Error) {
|
|
621
|
+
return activeLayer;
|
|
622
|
+
}
|
|
623
|
+
let layerSet = [activeLayer];
|
|
624
|
+
let placed = /* @__PURE__ */ new Map();
|
|
625
|
+
placed.set(activeLayer.header.id, true);
|
|
626
|
+
let result = await this.SatisfyDependencies(activeLayer, placed, layerSet);
|
|
627
|
+
if (result instanceof Error) {
|
|
628
|
+
return result;
|
|
629
|
+
}
|
|
630
|
+
return layerSet;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
// viewer/compose-flattened.ts
|
|
446
635
|
function TreeNodeToComposedObject(path, node, schemas) {
|
|
447
636
|
let co = {
|
|
448
637
|
name: path,
|
|
@@ -476,105 +665,345 @@ function TreeNodeToComposedObject(path, node, schemas) {
|
|
|
476
665
|
if (Object.keys(co.attributes).length === 0) delete co.attributes;
|
|
477
666
|
return co;
|
|
478
667
|
}
|
|
479
|
-
function compose3(files) {
|
|
480
|
-
let
|
|
481
|
-
|
|
482
|
-
|
|
668
|
+
async function compose3(files) {
|
|
669
|
+
let userDefinedOrder = {
|
|
670
|
+
header: { ...files[0].header },
|
|
671
|
+
imports: files.map((f) => {
|
|
672
|
+
return { uri: f.header.id };
|
|
673
|
+
}),
|
|
674
|
+
schemas: {},
|
|
675
|
+
data: [],
|
|
676
|
+
};
|
|
677
|
+
userDefinedOrder.header.id = "USER_DEF";
|
|
678
|
+
let provider = new StackedLayerProvider([
|
|
679
|
+
new InMemoryLayerProvider().AddAll([userDefinedOrder, ...files]),
|
|
680
|
+
new FetchLayerProvider(),
|
|
681
|
+
]);
|
|
682
|
+
let layerStack = await new IfcxLayerStackBuilder(provider).FromId(userDefinedOrder.header.id).Build();
|
|
683
|
+
if (layerStack instanceof Error) {
|
|
684
|
+
throw layerStack;
|
|
685
|
+
}
|
|
686
|
+
layerStack.GetFederatedLayer().data.forEach((n, i) => {
|
|
687
|
+
n.attributes = n.attributes || {};
|
|
688
|
+
n.attributes[`__internal_${i}`] = n.path;
|
|
689
|
+
});
|
|
690
|
+
return TreeNodeToComposedObject("", layerStack.GetFullTree(), layerStack.GetSchemas());
|
|
483
691
|
}
|
|
484
692
|
|
|
485
|
-
// render.ts
|
|
693
|
+
// viewer/render.ts
|
|
694
|
+
// import * as THREE from "three";
|
|
695
|
+
// import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
|
696
|
+
// import { RGBELoader } from "three/addons/loaders/RGBELoader.js";
|
|
697
|
+
import { PCDLoader } from "three/examples/jsm/loaders/PCDLoader.js";
|
|
486
698
|
// var controls;
|
|
487
699
|
// var renderer;
|
|
488
700
|
var scene;
|
|
489
701
|
var camera;
|
|
490
702
|
var datas = [];
|
|
491
703
|
var autoCamera = true;
|
|
492
|
-
|
|
704
|
+
var objectMap = {};
|
|
705
|
+
// var domMap = {};
|
|
706
|
+
var primMap = {};
|
|
707
|
+
// var currentPathMapping = null;
|
|
708
|
+
// var rootPrim = null;
|
|
709
|
+
// var selectedObject = null;
|
|
710
|
+
// var selectedDom = null;
|
|
711
|
+
// var raycaster = new THREE.Raycaster();
|
|
712
|
+
// var mouse = new THREE.Vector2();
|
|
713
|
+
var envMap;
|
|
493
714
|
function init() {
|
|
494
715
|
scene = new THREE.Scene();
|
|
716
|
+
// const ambient = new THREE.AmbientLight(14544639, 0.4);
|
|
717
|
+
// scene.add(ambient);
|
|
718
|
+
// const keyLight = new THREE.DirectionalLight(16777215, 1);
|
|
719
|
+
// keyLight.position.set(5, -10, 7.5);
|
|
720
|
+
// scene.add(keyLight);
|
|
721
|
+
// const fillLight = new THREE.DirectionalLight(16777215, 0.5);
|
|
722
|
+
// fillLight.position.set(-5, 5, 5);
|
|
723
|
+
// scene.add(fillLight);
|
|
724
|
+
// const rimLight = new THREE.DirectionalLight(16777215, 0.3);
|
|
725
|
+
// rimLight.position.set(0, 8, -10);
|
|
726
|
+
// scene.add(rimLight);
|
|
495
727
|
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
|
|
496
728
|
camera.up.set(0, 0, 1);
|
|
497
729
|
camera.position.set(50, 50, 50);
|
|
498
730
|
camera.lookAt(0, 0, 0);
|
|
499
|
-
|
|
500
|
-
//
|
|
501
|
-
//
|
|
502
|
-
//
|
|
731
|
+
scene.add(camera);
|
|
732
|
+
// const nd = document.querySelector(".viewport");
|
|
733
|
+
// renderer = new THREE.WebGLRenderer({
|
|
734
|
+
// alpha: true,
|
|
735
|
+
// logarithmicDepthBuffer: true,
|
|
736
|
+
// });
|
|
737
|
+
// const pmremGenerator = new THREE.PMREMGenerator(renderer);
|
|
738
|
+
// pmremGenerator.compileEquirectangularShader();
|
|
739
|
+
// new RGBELoader().load("images/wildflower_field_1k.hdr", function (texture) {
|
|
740
|
+
// envMap = pmremGenerator.fromEquirectangular(texture).texture;
|
|
741
|
+
// scene.environment = envMap;
|
|
742
|
+
// texture.dispose();
|
|
743
|
+
// pmremGenerator.dispose();
|
|
503
744
|
// });
|
|
504
745
|
// renderer.setSize(nd.offsetWidth, nd.offsetHeight);
|
|
505
|
-
// controls = new
|
|
746
|
+
// controls = new OrbitControls(camera, renderer.domElement);
|
|
506
747
|
// controls.enableDamping = true;
|
|
507
748
|
// controls.dampingFactor = 0.25;
|
|
508
749
|
// nd.appendChild(renderer.domElement);
|
|
509
|
-
|
|
750
|
+
// renderer.domElement.addEventListener("click", onCanvasClick);
|
|
510
751
|
return scene;
|
|
511
752
|
}
|
|
512
753
|
function HasAttr(node, attrName) {
|
|
513
754
|
if (!node || !node.attributes) return false;
|
|
514
755
|
return !!node.attributes[attrName];
|
|
515
756
|
}
|
|
516
|
-
function
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
757
|
+
// function setHighlight(obj, highlight) {
|
|
758
|
+
// if (!obj) return;
|
|
759
|
+
// obj.traverse((o) => {
|
|
760
|
+
// const mat = o.material;
|
|
761
|
+
// if (mat && mat.color) {
|
|
762
|
+
// if (highlight) {
|
|
763
|
+
// if (!o.userData._origColor) {
|
|
764
|
+
// o.userData._origColor = mat.color.clone();
|
|
765
|
+
// }
|
|
766
|
+
// o.material = mat.clone();
|
|
767
|
+
// o.material.color.set(16711680);
|
|
768
|
+
// } else if (o.userData._origColor) {
|
|
769
|
+
// mat.color.copy(o.userData._origColor);
|
|
770
|
+
// delete o.userData._origColor;
|
|
771
|
+
// }
|
|
772
|
+
// }
|
|
773
|
+
// });
|
|
774
|
+
// }
|
|
775
|
+
// function selectPath(path) {
|
|
776
|
+
// if (!path) {
|
|
777
|
+
// if (selectedObject) setHighlight(selectedObject, false);
|
|
778
|
+
// if (selectedDom) selectedDom.classList.remove("selected");
|
|
779
|
+
// selectedObject = null;
|
|
780
|
+
// selectedDom = null;
|
|
781
|
+
// return;
|
|
782
|
+
// }
|
|
783
|
+
// if (selectedObject) {
|
|
784
|
+
// setHighlight(selectedObject, false);
|
|
785
|
+
// }
|
|
786
|
+
// if (selectedDom) {
|
|
787
|
+
// selectedDom.classList.remove("selected");
|
|
788
|
+
// }
|
|
789
|
+
// selectedObject = objectMap[path] || null;
|
|
790
|
+
// selectedDom = domMap[path] || null;
|
|
791
|
+
// if (selectedObject) setHighlight(selectedObject, true);
|
|
792
|
+
// if (selectedDom) selectedDom.classList.add("selected");
|
|
793
|
+
// }
|
|
794
|
+
// function onCanvasClick(event) {
|
|
795
|
+
// const rect = renderer.domElement.getBoundingClientRect();
|
|
796
|
+
// mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
797
|
+
// mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
798
|
+
// raycaster.setFromCamera(mouse, camera);
|
|
799
|
+
// const intersects = raycaster.intersectObjects(Object.values(objectMap), true);
|
|
800
|
+
// if (intersects.length > 0) {
|
|
801
|
+
// let obj = intersects[0].object;
|
|
802
|
+
// while (obj && !obj.userData.path) obj = obj.parent;
|
|
803
|
+
// if (obj && obj.userData.path) {
|
|
804
|
+
// const path = obj.userData.path;
|
|
805
|
+
// const prim = primMap[path];
|
|
806
|
+
// if (prim) {
|
|
807
|
+
// handleClick(prim, currentPathMapping, rootPrim || prim);
|
|
808
|
+
// }
|
|
809
|
+
// selectPath(path);
|
|
810
|
+
// }
|
|
811
|
+
// } else {
|
|
812
|
+
// selectPath(null);
|
|
813
|
+
// }
|
|
814
|
+
// }
|
|
815
|
+
function tryCreateMeshGltfMaterial(path) {
|
|
816
|
+
for (let p of path) {
|
|
817
|
+
if (!p.attributes) {
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
const pbrMetallicRoughness = p.attributes["gltf::material::pbrMetallicRoughness"];
|
|
821
|
+
const normalTexture = p.attributes["gltf::material::normalTexture"];
|
|
822
|
+
const occlusionTexture = p.attributes["gltf::material::occlusionTexture"];
|
|
823
|
+
const emissiveTexture = p.attributes["gltf::material::emissiveTexture"];
|
|
824
|
+
const emissiveFactor = p.attributes["gltf::material::emissiveFactor"];
|
|
825
|
+
const alphaMode = p.attributes["gltf::material::alphaMode"];
|
|
826
|
+
const alphaCutoff = p.attributes["gltf::material::alphaCutoff"];
|
|
827
|
+
const doubleSided = p.attributes["gltf::material::doubleSided"];
|
|
828
|
+
if (
|
|
829
|
+
!pbrMetallicRoughness &&
|
|
830
|
+
!normalTexture &&
|
|
831
|
+
!occlusionTexture &&
|
|
832
|
+
!emissiveTexture &&
|
|
833
|
+
!emissiveFactor &&
|
|
834
|
+
!alphaMode &&
|
|
835
|
+
!alphaCutoff &&
|
|
836
|
+
!doubleSided
|
|
837
|
+
) {
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
let material = new THREE.MeshStandardMaterial();
|
|
841
|
+
material.color = new THREE.Color(1, 1, 1);
|
|
842
|
+
material.metalness = 1;
|
|
843
|
+
material.roughness = 1;
|
|
844
|
+
if (pbrMetallicRoughness) {
|
|
845
|
+
let baseColorFactor = pbrMetallicRoughness["baseColorFactor"];
|
|
846
|
+
if (baseColorFactor) {
|
|
847
|
+
material.color = new THREE.Color(baseColorFactor[0], baseColorFactor[1], baseColorFactor[2]);
|
|
848
|
+
}
|
|
849
|
+
let metallicFactor = pbrMetallicRoughness["metallicFactor"];
|
|
850
|
+
if (metallicFactor !== void 0) {
|
|
851
|
+
material.metalness = metallicFactor;
|
|
852
|
+
}
|
|
853
|
+
let roughnessFactor = pbrMetallicRoughness["roughnessFactor"];
|
|
854
|
+
if (roughnessFactor !== void 0) {
|
|
855
|
+
material.roughness = roughnessFactor;
|
|
856
|
+
}
|
|
521
857
|
}
|
|
858
|
+
material.envMap = envMap;
|
|
859
|
+
material.needsUpdate = true;
|
|
860
|
+
material.envMapRotation = new THREE.Euler(0.5 * Math.PI, 0, 0);
|
|
861
|
+
return material;
|
|
522
862
|
}
|
|
523
863
|
return void 0;
|
|
524
864
|
}
|
|
525
|
-
function createMaterialFromParent(
|
|
526
|
-
let reference = parent.attributes["usd::usdshade::materialbindingapi::material::binding"];
|
|
865
|
+
function createMaterialFromParent(path) {
|
|
527
866
|
let material = {
|
|
528
867
|
color: new THREE.Color(0.6, 0.6, 0.6),
|
|
529
868
|
transparent: false,
|
|
530
869
|
opacity: 1,
|
|
531
870
|
};
|
|
532
|
-
|
|
533
|
-
const
|
|
534
|
-
if (
|
|
535
|
-
let color = materialNode?.attributes["bsi::presentation::diffuseColor"];
|
|
871
|
+
for (let p of path) {
|
|
872
|
+
const color = p.attributes ? p.attributes["bsi::ifc::presentation::diffuseColor"] : null;
|
|
873
|
+
if (color) {
|
|
536
874
|
material.color = new THREE.Color(...color);
|
|
537
|
-
|
|
875
|
+
const opacity = p.attributes["bsi::ifc::presentation::opacity"];
|
|
876
|
+
if (opacity) {
|
|
538
877
|
material.transparent = true;
|
|
539
|
-
material.opacity =
|
|
878
|
+
material.opacity = opacity;
|
|
540
879
|
}
|
|
880
|
+
break;
|
|
541
881
|
}
|
|
542
882
|
}
|
|
543
883
|
return material;
|
|
544
884
|
}
|
|
545
|
-
function createCurveFromJson(
|
|
546
|
-
let points = new Float32Array(
|
|
885
|
+
function createCurveFromJson(path) {
|
|
886
|
+
let points = new Float32Array(path[0].attributes["usd::usdgeom::basiscurves::points"].flat());
|
|
547
887
|
const geometry = new THREE.BufferGeometry();
|
|
548
888
|
geometry.setAttribute("position", new THREE.BufferAttribute(points, 3));
|
|
549
|
-
const material = createMaterialFromParent(
|
|
889
|
+
const material = createMaterialFromParent(path);
|
|
550
890
|
let lineMaterial = new THREE.LineBasicMaterial({ ...material });
|
|
551
891
|
lineMaterial.color.multiplyScalar(0.8);
|
|
552
892
|
return new THREE.Line(geometry, lineMaterial);
|
|
553
893
|
}
|
|
554
|
-
function createMeshFromJson(
|
|
555
|
-
let points = new Float32Array(
|
|
556
|
-
let indices = new Uint16Array(
|
|
894
|
+
function createMeshFromJson(path) {
|
|
895
|
+
let points = new Float32Array(path[0].attributes["usd::usdgeom::mesh::points"].flat());
|
|
896
|
+
let indices = new Uint16Array(path[0].attributes["usd::usdgeom::mesh::faceVertexIndices"]);
|
|
557
897
|
const geometry = new THREE.BufferGeometry();
|
|
558
898
|
geometry.setAttribute("position", new THREE.BufferAttribute(points, 3));
|
|
559
899
|
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
|
|
560
900
|
geometry.computeVertexNormals();
|
|
561
|
-
|
|
562
|
-
let
|
|
901
|
+
var meshMaterial;
|
|
902
|
+
let gltfPbrMaterial = tryCreateMeshGltfMaterial(path);
|
|
903
|
+
if (gltfPbrMaterial) {
|
|
904
|
+
meshMaterial = gltfPbrMaterial;
|
|
905
|
+
} else {
|
|
906
|
+
const m = createMaterialFromParent(path);
|
|
907
|
+
meshMaterial = new THREE.MeshLambertMaterial({ ...m });
|
|
908
|
+
}
|
|
563
909
|
return new THREE.Mesh(geometry, meshMaterial);
|
|
564
910
|
}
|
|
565
|
-
function
|
|
911
|
+
function createPointsFromJsonPcdBase64(path) {
|
|
912
|
+
const base64_string = path[0].attributes["pcd::base64"];
|
|
913
|
+
const decoded = atob(base64_string);
|
|
914
|
+
const len = decoded.length;
|
|
915
|
+
const bytes = new Uint8Array(len);
|
|
916
|
+
for (let i = 0; i < len; i++) {
|
|
917
|
+
bytes[i] = decoded.charCodeAt(i);
|
|
918
|
+
}
|
|
919
|
+
const loader = new PCDLoader();
|
|
920
|
+
const points = loader.parse(bytes.buffer);
|
|
921
|
+
points.material.sizeAttenuation = false;
|
|
922
|
+
points.material.size = 2;
|
|
923
|
+
return points;
|
|
924
|
+
}
|
|
925
|
+
function createPoints(geometry, withColors) {
|
|
926
|
+
const material = new THREE.PointsMaterial();
|
|
927
|
+
material.sizeAttenuation = false;
|
|
928
|
+
material.fog = true;
|
|
929
|
+
material.size = 5;
|
|
930
|
+
material.color = new THREE.Color(withColors ? 16777215 : 0);
|
|
931
|
+
if (withColors) {
|
|
932
|
+
material.vertexColors = true;
|
|
933
|
+
}
|
|
934
|
+
return new THREE.Points(geometry, material);
|
|
935
|
+
}
|
|
936
|
+
function createPointsFromJsonArray(path) {
|
|
937
|
+
const geometry = new THREE.BufferGeometry();
|
|
938
|
+
const positions = new Float32Array(path[0].attributes["points::array::positions"].flat());
|
|
939
|
+
geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
|
|
940
|
+
const colors = path[0].attributes["points::array::colors"];
|
|
941
|
+
if (colors) {
|
|
942
|
+
const colors_ = new Float32Array(colors.flat());
|
|
943
|
+
geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors_, 3));
|
|
944
|
+
}
|
|
945
|
+
return createPoints(geometry, colors);
|
|
946
|
+
}
|
|
947
|
+
function base64ToArrayBuffer(str) {
|
|
948
|
+
let binary;
|
|
949
|
+
try {
|
|
950
|
+
binary = atob(str);
|
|
951
|
+
} catch (e) {
|
|
952
|
+
throw new Error("base64 encoded string is invalid");
|
|
953
|
+
}
|
|
954
|
+
const bytes = new Uint8Array(binary.length);
|
|
955
|
+
for (let i = 0; i < binary.length; ++i) {
|
|
956
|
+
bytes[i] = binary.charCodeAt(i);
|
|
957
|
+
}
|
|
958
|
+
return bytes.buffer;
|
|
959
|
+
}
|
|
960
|
+
function createPointsFromJsonPositionBase64(path) {
|
|
961
|
+
const geometry = new THREE.BufferGeometry();
|
|
962
|
+
const positions_base64 = path[0].attributes["points::base64::positions"];
|
|
963
|
+
const positions_bytes = base64ToArrayBuffer(positions_base64);
|
|
964
|
+
if (!positions_bytes) {
|
|
965
|
+
return null;
|
|
966
|
+
}
|
|
967
|
+
const positions = new Float32Array(positions_bytes);
|
|
968
|
+
geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
|
|
969
|
+
const colors_base64 = path[0].attributes["points::base64::colors"];
|
|
970
|
+
if (colors_base64) {
|
|
971
|
+
const colors_bytes = base64ToArrayBuffer(colors_base64);
|
|
972
|
+
if (colors_bytes) {
|
|
973
|
+
const colors = new Float32Array(colors_bytes);
|
|
974
|
+
geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return createPoints(geometry, colors_base64);
|
|
978
|
+
}
|
|
979
|
+
function traverseTree(path, parent, pathMapping) {
|
|
980
|
+
const node = path[0];
|
|
566
981
|
let elem = new THREE.Group();
|
|
567
982
|
if (HasAttr(node, "usd::usdgeom::visibility::visibility")) {
|
|
568
983
|
if (node.attributes["usd::usdgeom::visibility::visibility"] === "invisible") {
|
|
569
984
|
return;
|
|
570
985
|
}
|
|
571
986
|
} else if (HasAttr(node, "usd::usdgeom::mesh::points")) {
|
|
572
|
-
elem = createMeshFromJson(
|
|
987
|
+
elem = createMeshFromJson(path);
|
|
573
988
|
} else if (HasAttr(node, "usd::usdgeom::basiscurves::points")) {
|
|
574
|
-
elem = createCurveFromJson(
|
|
989
|
+
elem = createCurveFromJson(path);
|
|
990
|
+
} else if (HasAttr(node, "pcd::base64")) {
|
|
991
|
+
elem = createPointsFromJsonPcdBase64(path);
|
|
992
|
+
} else if (HasAttr(node, "points::array::positions")) {
|
|
993
|
+
elem = createPointsFromJsonArray(path);
|
|
994
|
+
} else if (HasAttr(node, "points::base64::positions")) {
|
|
995
|
+
elem = createPointsFromJsonPositionBase64(path);
|
|
996
|
+
}
|
|
997
|
+
objectMap[node.name] = elem;
|
|
998
|
+
primMap[node.name] = node;
|
|
999
|
+
elem.userData.path = node.name;
|
|
1000
|
+
for (let path2 of Object.entries(node.attributes || {})
|
|
1001
|
+
.filter(([k, _]) => k.startsWith("__internal_"))
|
|
1002
|
+
.map(([_, v]) => v)) {
|
|
1003
|
+
(pathMapping[String(path2)] = pathMapping[String(path2)] || []).push(node.name);
|
|
575
1004
|
}
|
|
576
1005
|
parent.add(elem);
|
|
577
|
-
if (
|
|
1006
|
+
if (path.length > 1) {
|
|
578
1007
|
elem.matrixAutoUpdate = false;
|
|
579
1008
|
let matrixNode =
|
|
580
1009
|
node.attributes && node.attributes["usd::xformop::transform"]
|
|
@@ -587,7 +1016,7 @@ function traverseTree(node, parent, root, parentNode = void 0) {
|
|
|
587
1016
|
elem.matrix = matrix;
|
|
588
1017
|
}
|
|
589
1018
|
}
|
|
590
|
-
(node.children || []).forEach((child) => traverseTree(child, elem || parent,
|
|
1019
|
+
(node.children || []).forEach((child) => traverseTree([child, ...path], elem || parent, pathMapping));
|
|
591
1020
|
}
|
|
592
1021
|
// function encodeHtmlEntities(str) {
|
|
593
1022
|
// const div = document.createElement("div");
|
|
@@ -598,44 +1027,120 @@ function traverseTree(node, parent, root, parentNode = void 0) {
|
|
|
598
1027
|
// "usd::usdgeom::mesh::points": "deployed_code",
|
|
599
1028
|
// "usd::usdgeom::basiscurves::points": "line_curve",
|
|
600
1029
|
// "usd::usdshade::material::outputs::surface.connect": "line_style",
|
|
1030
|
+
// "pcd::base64": "grain",
|
|
1031
|
+
// "points::array::positions": "grain",
|
|
1032
|
+
// "points::base64::positions": "grain",
|
|
601
1033
|
// };
|
|
602
|
-
// function
|
|
1034
|
+
// function handleClick(prim, pathMapping, root) {
|
|
1035
|
+
// const container = document.querySelector(".attributes .table");
|
|
1036
|
+
// if (container !== null) {
|
|
1037
|
+
// container.innerHTML = "";
|
|
1038
|
+
// const table = document.createElement("table");
|
|
1039
|
+
// table.setAttribute("border", "0");
|
|
1040
|
+
// const entries = [
|
|
1041
|
+
// ["name", prim.name],
|
|
1042
|
+
// ...Object.entries(prim.attributes).filter(([k, _]) => !k.startsWith("__internal_")),
|
|
1043
|
+
// ];
|
|
1044
|
+
// const format = (value) => {
|
|
1045
|
+
// if (Array.isArray(value)) {
|
|
1046
|
+
// let N = document.createElement("span");
|
|
1047
|
+
// N.appendChild(document.createTextNode("("));
|
|
1048
|
+
// let first = true;
|
|
1049
|
+
// for (let n of value.map(format)) {
|
|
1050
|
+
// if (!first) {
|
|
1051
|
+
// N.appendChild(document.createTextNode(","));
|
|
1052
|
+
// }
|
|
1053
|
+
// N.appendChild(n);
|
|
1054
|
+
// first = false;
|
|
1055
|
+
// }
|
|
1056
|
+
// N.appendChild(document.createTextNode(")"));
|
|
1057
|
+
// return N;
|
|
1058
|
+
// } else if (typeof value === "object") {
|
|
1059
|
+
// const ks = Object.keys(value);
|
|
1060
|
+
// if (ks.length == 1 && ks[0] === "ref" && pathMapping[value.ref] && pathMapping[value.ref].length == 1) {
|
|
1061
|
+
// let a = document.createElement("a");
|
|
1062
|
+
// let resolvedRefAsPath = pathMapping[value.ref][0];
|
|
1063
|
+
// a.setAttribute("href", "#");
|
|
1064
|
+
// a.textContent = resolvedRefAsPath;
|
|
1065
|
+
// a.onclick = () => {
|
|
1066
|
+
// let prim2 = null;
|
|
1067
|
+
// const recurse = (n) => {
|
|
1068
|
+
// if (n.name === resolvedRefAsPath) {
|
|
1069
|
+
// prim2 = n;
|
|
1070
|
+
// } else {
|
|
1071
|
+
// (n.children || []).forEach(recurse);
|
|
1072
|
+
// }
|
|
1073
|
+
// };
|
|
1074
|
+
// recurse(root);
|
|
1075
|
+
// if (prim2) {
|
|
1076
|
+
// handleClick(prim2, pathMapping, root);
|
|
1077
|
+
// }
|
|
1078
|
+
// };
|
|
1079
|
+
// return a;
|
|
1080
|
+
// } else {
|
|
1081
|
+
// return document.createTextNode(JSON.stringify(value));
|
|
1082
|
+
// }
|
|
1083
|
+
// } else {
|
|
1084
|
+
// return document.createTextNode(value);
|
|
1085
|
+
// }
|
|
1086
|
+
// };
|
|
1087
|
+
// entries.forEach(([key, value]) => {
|
|
1088
|
+
// const tr = document.createElement("tr");
|
|
1089
|
+
// const tdKey = document.createElement("td");
|
|
1090
|
+
// tdKey.textContent = encodeHtmlEntities(key);
|
|
1091
|
+
// const tdValue = document.createElement("td");
|
|
1092
|
+
// tdValue.appendChild(format(value));
|
|
1093
|
+
// tr.appendChild(tdKey);
|
|
1094
|
+
// tr.appendChild(tdValue);
|
|
1095
|
+
// table.appendChild(tr);
|
|
1096
|
+
// });
|
|
1097
|
+
// container.appendChild(table);
|
|
1098
|
+
// }
|
|
1099
|
+
// }
|
|
1100
|
+
// function buildDomTree(prim, node, pathMapping, root = null) {
|
|
603
1101
|
// const elem = document.createElement("div");
|
|
604
1102
|
// let span;
|
|
605
1103
|
// elem.appendChild(document.createTextNode(prim.name ? prim.name.split("/").reverse()[0] : "root"));
|
|
606
1104
|
// elem.appendChild((span = document.createElement("span")));
|
|
607
1105
|
// Object.entries(icons).forEach(([k, v]) => (span.innerText += (prim.attributes || {})[k] ? v : " "));
|
|
608
1106
|
// span.className = "material-symbols-outlined";
|
|
1107
|
+
// domMap[prim.name] = elem;
|
|
1108
|
+
// elem.dataset.path = prim.name;
|
|
609
1109
|
// elem.onclick = (evt) => {
|
|
610
|
-
//
|
|
611
|
-
//
|
|
612
|
-
// .map(
|
|
613
|
-
// ([k, v]) =>
|
|
614
|
-
// `<tr><td>${encodeHtmlEntities(k)}</td><td>${encodeHtmlEntities(typeof v === "object" ? JSON.stringify(v) : v)}</td>`
|
|
615
|
-
// )
|
|
616
|
-
// .join("");
|
|
617
|
-
// document.querySelector(".attributes .table").innerHTML = `<table border="0">${rows}</table>`;
|
|
1110
|
+
// handleClick(prim, pathMapping, root || prim);
|
|
1111
|
+
// selectPath(prim.name);
|
|
618
1112
|
// evt.stopPropagation();
|
|
619
1113
|
// };
|
|
620
1114
|
// node.appendChild(elem);
|
|
621
|
-
// (prim.children || []).forEach((p) => buildDomTree(p, elem));
|
|
1115
|
+
// (prim.children || []).forEach((p) => buildDomTree(p, elem, pathMapping, root || prim));
|
|
622
1116
|
// }
|
|
623
|
-
function composeAndRender() {
|
|
1117
|
+
async function composeAndRender() {
|
|
624
1118
|
if (scene) {
|
|
625
1119
|
scene.children = [];
|
|
626
1120
|
}
|
|
1121
|
+
objectMap = {};
|
|
1122
|
+
// domMap = {};
|
|
1123
|
+
primMap = {};
|
|
1124
|
+
// currentPathMapping = null;
|
|
1125
|
+
// rootPrim = null;
|
|
627
1126
|
// document.querySelector(".tree").innerHTML = "";
|
|
628
1127
|
if (datas.length === 0) {
|
|
629
1128
|
return;
|
|
630
1129
|
}
|
|
631
1130
|
let tree = null;
|
|
632
1131
|
let dataArray = datas.map((arr) => arr[1]);
|
|
633
|
-
tree = compose3(dataArray);
|
|
1132
|
+
tree = await compose3(dataArray);
|
|
634
1133
|
if (!tree) {
|
|
635
1134
|
console.error("No result from composition");
|
|
636
1135
|
return;
|
|
637
1136
|
}
|
|
638
|
-
|
|
1137
|
+
if (!scene) {
|
|
1138
|
+
init();
|
|
1139
|
+
}
|
|
1140
|
+
let pathMapping = {};
|
|
1141
|
+
traverseTree([tree], scene, pathMapping);
|
|
1142
|
+
// currentPathMapping = pathMapping;
|
|
1143
|
+
// rootPrim = tree;
|
|
639
1144
|
if (autoCamera) {
|
|
640
1145
|
const boundingBox = new THREE.Box3();
|
|
641
1146
|
boundingBox.setFromObject(scene);
|
|
@@ -650,7 +1155,7 @@ function composeAndRender() {
|
|
|
650
1155
|
autoCamera = false;
|
|
651
1156
|
}
|
|
652
1157
|
}
|
|
653
|
-
// buildDomTree(tree, document.querySelector(".tree"));
|
|
1158
|
+
// buildDomTree(tree, document.querySelector(".tree"), pathMapping);
|
|
654
1159
|
// animate();
|
|
655
1160
|
}
|
|
656
1161
|
// function createLayerDom() {
|
|
@@ -682,10 +1187,10 @@ function composeAndRender() {
|
|
|
682
1187
|
// document.querySelector(".layers div").appendChild(elem);
|
|
683
1188
|
// });
|
|
684
1189
|
// }
|
|
685
|
-
// function addModel(name, m) {
|
|
1190
|
+
// async function addModel(name, m) {
|
|
686
1191
|
// datas.push([name, m]);
|
|
687
1192
|
// createLayerDom();
|
|
688
|
-
// composeAndRender();
|
|
1193
|
+
// await composeAndRender();
|
|
689
1194
|
// }
|
|
690
1195
|
// function animate() {
|
|
691
1196
|
// requestAnimationFrame(animate);
|
|
@@ -694,9 +1199,9 @@ function composeAndRender() {
|
|
|
694
1199
|
// }
|
|
695
1200
|
// export { composeAndRender, addModel as default };
|
|
696
1201
|
|
|
697
|
-
export function parse(m, name) {
|
|
1202
|
+
export async function parse(m, name) {
|
|
698
1203
|
datas.push([name, m]);
|
|
699
|
-
composeAndRender();
|
|
1204
|
+
await composeAndRender();
|
|
700
1205
|
return scene;
|
|
701
1206
|
}
|
|
702
1207
|
export function clear() {
|