@inweb/viewer-three 25.12.1 → 25.12.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.
@@ -1,5 +1,4 @@
1
1
  import { Box3, Object3D, PerspectiveCamera, Scene, Vector3, WebGLRenderer } from "three";
2
- import { GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
3
2
  import { EventEmitter2 } from "@inweb/eventemitter2";
4
3
  import { Assembly, Client, Model, File } from "@inweb/client";
5
4
  import { CanvasEventMap, IDragger, IOptions, IViewer, IViewpoint, Options, OptionsEventMap, ViewerEventMap } from "@inweb/viewer-core";
@@ -17,7 +16,7 @@ export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMa
17
16
  helpers: Scene | undefined;
18
17
  camera: PerspectiveCamera | undefined;
19
18
  renderer: WebGLRenderer | undefined;
20
- models: Array<GLTF>;
19
+ models: Array<any>;
21
20
  selected: Array<Object3D>;
22
21
  extents: Box3;
23
22
  target: Vector3;
@@ -99,8 +98,11 @@ export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMa
99
98
  * or {@link https://developer.mozilla.org/docs/Web/API/Blob/Blob | Blob}, or
100
99
  * {@link https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs | Data URL} string,
101
100
  * @param params - Loader parameters.
102
- * @param params.path - The base path from which to find subsequent glTF resources such as
103
- * textures and .bin data files. If not defined, the base path of the file URL will be used.
101
+ * @param params.fileFormat - File format. If no format is specified, the file extension from
102
+ * the file URL will be used, or `gltf` for binary data. If no appropriate loader is found
103
+ * for the specified format, an exception will be thrown.
104
+ * @param params.path - The base path from which additional resources like textures will be
105
+ * loaded. If not defined, the base path of the file URL will be used.
104
106
  * @param params.requestHeader - The
105
107
  * {@link https://developer.mozilla.org/docs/Glossary/Request_header | request header} used
106
108
  * in HTTP request.
@@ -111,6 +113,7 @@ export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMa
111
113
  * {@link https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/withCredentials | XMLHttpRequest.withCredentials}.
112
114
  */
113
115
  openGltfFile(file: string | globalThis.File | ArrayBuffer | Blob, externalData?: Map<string, string | globalThis.File | ArrayBuffer | Blob>, params?: {
116
+ fileFormat?: string;
114
117
  path?: string;
115
118
  requestHeader?: HeadersInit;
116
119
  crossOrigin?: string;
@@ -136,8 +139,11 @@ export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMa
136
139
  * or {@link https://developer.mozilla.org/docs/Web/API/Blob/Blob | Blob}, or
137
140
  * {@link https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs | Data URL} string,
138
141
  * @param params - Loader parameters.
139
- * @param params.path - The base path from which to find subsequent glTF resources such as
140
- * textures and .bin data files.
142
+ * @param params.fileFormat - File format. If no format is specified, the file extension from
143
+ * the file URL will be used, or `gltf` for binary data. If no appropriate loader is found
144
+ * for the specified format, an exception will be thrown.
145
+ * @param params.path - The base path from which additional resources like textures will be
146
+ * loaded. If not defined, the base path of the file URL will be used.
141
147
  * @param params.requestHeader - The
142
148
  * {@link https://developer.mozilla.org/docs/Glossary/Request_header | request header} used
143
149
  * in HTTP request.
@@ -148,6 +154,7 @@ export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMa
148
154
  * {@link https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/withCredentials | XMLHttpRequest.withCredentials}.
149
155
  */
150
156
  loadGltfFile(file: string | globalThis.File | ArrayBuffer | Blob, externalData?: Map<string, string | globalThis.File | ArrayBuffer | Blob>, params?: {
157
+ fileFormat?: string;
151
158
  path?: string;
152
159
  requestHeader?: HeadersInit;
153
160
  crossOrigin?: string;
@@ -0,0 +1,8 @@
1
+ import { Group, Loader } from "three";
2
+ export interface IFCX {
3
+ scene: Group;
4
+ }
5
+ declare class IFCXLoader extends Loader<IFCX> {
6
+ load(url: any, onLoad: (ifc: IFCX) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
7
+ }
8
+ export { IFCXLoader };
@@ -0,0 +1,10 @@
1
+ import { Group } from "three";
2
+ import { USDZLoader } from "three/examples/jsm/loaders/USDZLoader.js";
3
+ export interface USDZ {
4
+ scene: Group;
5
+ animations: boolean;
6
+ }
7
+ export declare class USDZLoader2 extends USDZLoader {
8
+ parse(buffer: any): Group<import("three").Object3DEventMap>;
9
+ load(url: string, onLoad: (data: any) => void, onProgress?: (event: ProgressEvent) => void, onError?: (err: unknown) => void): void;
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inweb/viewer-three",
3
- "version": "25.12.1",
3
+ "version": "25.12.2",
4
4
  "description": "JavaScript library for rendering CAD and BIM files in a browser using Three.js",
5
5
  "homepage": "https://cloud.opendesign.com/docs/index.html",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -28,10 +28,10 @@
28
28
  "docs": "typedoc"
29
29
  },
30
30
  "dependencies": {
31
- "@inweb/client": "~25.12.1",
32
- "@inweb/eventemitter2": "~25.12.1",
33
- "@inweb/markup": "~25.12.1",
34
- "@inweb/viewer-core": "~25.12.1"
31
+ "@inweb/client": "~25.12.2",
32
+ "@inweb/eventemitter2": "~25.12.2",
33
+ "@inweb/markup": "~25.12.2",
34
+ "@inweb/viewer-core": "~25.12.2"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/three": "^0.169.0",
@@ -22,8 +22,10 @@
22
22
  ///////////////////////////////////////////////////////////////////////////////
23
23
 
24
24
  import { Box3, LinearToneMapping, Object3D, PerspectiveCamera, Plane, Scene, Vector3, WebGLRenderer } from "three";
25
- import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
25
+ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
26
26
  import { GLTFLoadingManager } from "./loaders/GLTFLoadingManager";
27
+ import { USDZLoader2 } from "./loaders/USDZLoader2";
28
+ import { IFCXLoader } from "./loaders/IFCXLoader";
27
29
 
28
30
  import { EventEmitter2 } from "@inweb/eventemitter2";
29
31
  import { Assembly, Client, Model, File } from "@inweb/client";
@@ -86,7 +88,7 @@ export class Viewer
86
88
  public helpers: Scene | undefined;
87
89
  public camera: PerspectiveCamera | undefined;
88
90
  public renderer: WebGLRenderer | undefined;
89
- public models: Array<GLTF>;
91
+ public models: Array<any>;
90
92
  public selected: Array<Object3D>;
91
93
  public extents: Box3;
92
94
  public target: Vector3;
@@ -311,20 +313,25 @@ export class Viewer
311
313
 
312
314
  this.emitEvent({ type: "open", file, model: file });
313
315
 
314
- let model: Model | undefined = undefined;
315
- if (file) {
316
- const models = (await file.getModels()) || [];
317
- model = models.find((model: Model) => model.default) || models[0];
318
- }
319
- if (!model) throw new Error("No default model found");
316
+ if (!file) throw new Error("No default model found");
317
+
318
+ let database = "";
319
+ let path = "";
320
320
 
321
- const geometryType = model.database.split(".").pop();
322
- if (geometryType !== "gltf") throw new Error(`Unknown geometry type: ${geometryType}`);
321
+ const models = (await file.getModels()) || [];
322
+ const model = models.find((model: Model) => model.default) || models[0];
323
+ if (model) {
324
+ database = model.database;
325
+ path = model.path;
326
+ } else {
327
+ database = file.id + file.type;
328
+ path = `${file.path}/downloads`;
329
+ }
323
330
 
324
- const url = `${model.httpClient.serverUrl}${model.path}/${model.database}`;
325
- const params = { requestHeader: model.httpClient.headers };
331
+ const url = `${file.httpClient.serverUrl}${path}/${database}`;
332
+ const params = { requestHeader: file.httpClient.headers };
326
333
 
327
- await this.loadReferences(model);
334
+ await this.loadReferences(file);
328
335
  await this.loadGltfFile(url, undefined, params);
329
336
 
330
337
  return this;
@@ -358,8 +365,11 @@ export class Viewer
358
365
  * or {@link https://developer.mozilla.org/docs/Web/API/Blob/Blob | Blob}, or
359
366
  * {@link https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs | Data URL} string,
360
367
  * @param params - Loader parameters.
361
- * @param params.path - The base path from which to find subsequent glTF resources such as
362
- * textures and .bin data files. If not defined, the base path of the file URL will be used.
368
+ * @param params.fileFormat - File format. If no format is specified, the file extension from
369
+ * the file URL will be used, or `gltf` for binary data. If no appropriate loader is found
370
+ * for the specified format, an exception will be thrown.
371
+ * @param params.path - The base path from which additional resources like textures will be
372
+ * loaded. If not defined, the base path of the file URL will be used.
363
373
  * @param params.requestHeader - The
364
374
  * {@link https://developer.mozilla.org/docs/Glossary/Request_header | request header} used
365
375
  * in HTTP request.
@@ -373,6 +383,7 @@ export class Viewer
373
383
  file: string | globalThis.File | ArrayBuffer | Blob,
374
384
  externalData: Map<string, string | globalThis.File | ArrayBuffer | Blob> = new Map(),
375
385
  params: {
386
+ fileFormat?: string;
376
387
  path?: string;
377
388
  requestHeader?: HeadersInit;
378
389
  crossOrigin?: string;
@@ -409,8 +420,11 @@ export class Viewer
409
420
  * or {@link https://developer.mozilla.org/docs/Web/API/Blob/Blob | Blob}, or
410
421
  * {@link https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs | Data URL} string,
411
422
  * @param params - Loader parameters.
412
- * @param params.path - The base path from which to find subsequent glTF resources such as
413
- * textures and .bin data files.
423
+ * @param params.fileFormat - File format. If no format is specified, the file extension from
424
+ * the file URL will be used, or `gltf` for binary data. If no appropriate loader is found
425
+ * for the specified format, an exception will be thrown.
426
+ * @param params.path - The base path from which additional resources like textures will be
427
+ * loaded. If not defined, the base path of the file URL will be used.
414
428
  * @param params.requestHeader - The
415
429
  * {@link https://developer.mozilla.org/docs/Glossary/Request_header | request header} used
416
430
  * in HTTP request.
@@ -424,6 +438,7 @@ export class Viewer
424
438
  file: string | globalThis.File | ArrayBuffer | Blob,
425
439
  externalData: Map<string, string | globalThis.File | ArrayBuffer | Blob> = new Map(),
426
440
  params: {
441
+ fileFormat?: string;
427
442
  path?: string;
428
443
  requestHeader?: HeadersInit;
429
444
  crossOrigin?: string;
@@ -434,30 +449,44 @@ export class Viewer
434
449
  try {
435
450
  this.emitEvent({ type: "geometrystart" });
436
451
 
437
- const loader = new GLTFLoader(manager);
452
+ const loaders = {
453
+ gltf: GLTFLoader,
454
+ usdz: USDZLoader2,
455
+ ifcx: IFCXLoader,
456
+ };
457
+
458
+ let format = params.fileFormat;
459
+ if (!format && typeof file === "string") format = file.split(".").pop().toLocaleLowerCase();
460
+ if (!format && file instanceof globalThis.File) format = file.name.split(".").pop().toLocaleLowerCase();
461
+ if (!format) format = "gltf";
462
+ if (!loaders[format]) throw new Error(`Unknown geometry type: ${format}`);
463
+
464
+ const loader = new loaders[format](manager);
438
465
  loader.setPath(manager.path);
439
466
  loader.setRequestHeader(params.requestHeader as any);
440
467
  loader.setCrossOrigin(params.crossOrigin || loader.crossOrigin);
441
468
  loader.setWithCredentials(params.withCredentials || loader.withCredentials);
442
469
 
443
- const gltf = await loader.loadAsync(manager.fileURL, (event: ProgressEvent) => {
470
+ const model = await loader.loadAsync(manager.fileURL, (event: ProgressEvent) => {
444
471
  const { lengthComputable, loaded, total } = event;
445
472
  const progress = lengthComputable ? loaded / total : 1;
446
473
  this.emitEvent({ type: "geometryprogress", data: progress });
447
474
  });
448
475
 
449
476
  if (!this.scene) return this;
450
- if (!gltf.scene) throw new Error("No glTF scene found");
477
+ if (!model.scene) throw new Error("No scene found");
451
478
 
452
- this.models.push(gltf);
453
- this.scene.add(gltf.scene);
479
+ this.models.push(model);
480
+ this.scene.add(model.scene);
454
481
 
455
482
  this.syncOptions();
456
483
  this.syncOverlay();
457
484
  this.update();
458
485
 
486
+ setTimeout(this.update, 0); // <- USDZ specific
487
+
459
488
  this.emitEvent({ type: "databasechunk" });
460
- this.emitEvent({ type: "geometryend", data: gltf.scene });
489
+ this.emitEvent({ type: "geometryend", data: model.scene });
461
490
  } catch (error) {
462
491
  this.emitEvent({ type: "geometryerror", data: error });
463
492
  throw error;
@@ -492,8 +521,8 @@ export class Viewer
492
521
  this.helpers.traverse(disposeObject);
493
522
  this.helpers.clear();
494
523
 
495
- this.models.forEach((gltf) => gltf.scene.traverse(disposeObject));
496
- this.models.forEach((gltf) => gltf.scene.removeFromParent());
524
+ this.models.forEach((model) => model.scene.traverse(disposeObject));
525
+ this.models.forEach((model) => model.scene.removeFromParent());
497
526
  this.models = [];
498
527
 
499
528
  this.scene.clear();
@@ -27,18 +27,31 @@ import { commands } from "@inweb/viewer-core";
27
27
  import type { Viewer } from "../Viewer";
28
28
 
29
29
  export const defaultViewPositions = {
30
- top: new Vector3(0, 0, 1),
31
- bottom: new Vector3(0, 0, -1),
30
+ top: new Vector3(0, 1, 0),
31
+ bottom: new Vector3(0, -1, 0),
32
32
  left: new Vector3(-1, 0, 0),
33
33
  right: new Vector3(1, 0, 0),
34
- front: new Vector3(0, -1, 0),
35
- back: new Vector3(0, 1, 0),
34
+ front: new Vector3(0, 0, 1),
35
+ back: new Vector3(0, 0, -1),
36
36
  sw: new Vector3(-0.5, -0.5, 1.0).normalize(),
37
37
  se: new Vector3(0.5, -0.5, 1.0).normalize(),
38
38
  ne: new Vector3(0.5, 0.5, 1.0).normalize(),
39
39
  nw: new Vector3(-0.5, 0.5, 1.0).normalize(),
40
40
  };
41
41
 
42
+ // export const defaultViewPositions = {
43
+ // top: new Vector3(0, 0, 1),
44
+ // bottom: new Vector3(0, 0, -1),
45
+ // left: new Vector3(-1, 0, 0),
46
+ // right: new Vector3(1, 0, 0),
47
+ // front: new Vector3(0, -1, 0),
48
+ // back: new Vector3(0, 1, 0),
49
+ // sw: new Vector3(-0.5, -0.5, 1.0).normalize(),
50
+ // se: new Vector3(0.5, -0.5, 1.0).normalize(),
51
+ // ne: new Vector3(0.5, 0.5, 1.0).normalize(),
52
+ // nw: new Vector3(-0.5, 0.5, 1.0).normalize(),
53
+ // };
54
+
42
55
  function setDefaultViewPosition(viewer: Viewer, position: string): void {
43
56
  const direction = defaultViewPositions[position] || defaultViewPositions["sw"];
44
57
 
@@ -49,7 +49,7 @@ export class DefaultPositionComponent implements IDisposable {
49
49
  this.viewer.camera.updateMatrixWorld();
50
50
  this.viewer.camera.updateProjectionMatrix();
51
51
 
52
- this.viewer.executeCommand("setDefaultViewPosition", "sw");
52
+ this.viewer.executeCommand("setDefaultViewPosition", "front");
53
53
  this.viewer.executeCommand("zoomToExtents");
54
54
  };
55
55
  }
@@ -34,16 +34,25 @@ export class ExtentsComponent implements IDisposable {
34
34
  this.viewer.addEventListener("geometryend", this.syncExtents);
35
35
  this.viewer.addEventListener("clear", this.syncExtents);
36
36
  this.viewer.on("explode", this.syncExtents);
37
+ this.viewer.on("isolate", this.syncExtents);
38
+ this.viewer.on("hide", this.syncExtents);
39
+ this.viewer.on("showall", this.syncExtents);
37
40
  }
38
41
 
39
42
  dispose() {
40
43
  this.viewer.removeEventListener("geometryend", this.syncExtents);
41
44
  this.viewer.removeEventListener("clear", this.syncExtents);
42
45
  this.viewer.off("explode", this.syncExtents);
46
+ this.viewer.off("isolate", this.syncExtents);
47
+ this.viewer.off("hide", this.syncExtents);
48
+ this.viewer.off("showall", this.syncExtents);
43
49
  }
44
50
 
45
51
  syncExtents = () => {
46
- const extents = this.viewer.models.reduce((result: Box3, gltf) => result.expandByObject(gltf.scene), new Box3());
52
+ const extents = new Box3();
53
+ this.viewer.models.forEach((model) =>
54
+ model.scene.traverseVisible((object) => !object.children.length && extents.expandByObject(object))
55
+ );
47
56
 
48
57
  this.viewer.extents.copy(extents);
49
58
  this.viewer.target.copy(extents.getCenter(new Vector3()));
@@ -0,0 +1,71 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2002-2024, Open Design Alliance (the "Alliance").
3
+ // All rights reserved.
4
+ //
5
+ // This software and its documentation and related materials are owned by
6
+ // the Alliance. The software may only be incorporated into application
7
+ // programs owned by members of the Alliance, subject to a signed
8
+ // Membership Agreement and Supplemental Software License Agreement with the
9
+ // Alliance. The structure and organization of this software are the valuable
10
+ // trade secrets of the Alliance and its suppliers. The software is also
11
+ // protected by copyright law and international treaty provisions. Application
12
+ // programs incorporating this software must include the following statement
13
+ // with their copyright notices:
14
+ //
15
+ // This application incorporates Open Design Alliance software pursuant to a
16
+ // license agreement with Open Design Alliance.
17
+ // Open Design Alliance Copyright (C) 2002-2024 by Open Design Alliance.
18
+ // All rights reserved.
19
+ //
20
+ // By use of this software, its documentation or related materials, you
21
+ // acknowledge and accept the above terms.
22
+ ///////////////////////////////////////////////////////////////////////////////
23
+
24
+ import { FileLoader, Group, Loader } from "three";
25
+ import addModel, { clear } from "./render.mjs";
26
+
27
+ export interface IFCX {
28
+ scene: Group;
29
+ }
30
+
31
+ class IFCXLoader extends Loader<IFCX> {
32
+ override load(
33
+ url: any,
34
+ onLoad: (ifc: IFCX) => void,
35
+ onProgress?: (event: ProgressEvent) => void,
36
+ onError?: (event: ErrorEvent) => void
37
+ ) {
38
+ const loader = new FileLoader(this.manager);
39
+ loader.setPath(this.path);
40
+ loader.setResponseType("json");
41
+ loader.setRequestHeader(this.requestHeader);
42
+ loader.setWithCredentials(this.withCredentials);
43
+ loader.load(
44
+ url,
45
+ (buffer) => {
46
+ try {
47
+ const scene = addModel("", buffer);
48
+ let handle = 0;
49
+ scene.traverse((object) => {
50
+ object.userData = { ...object.userData, handle };
51
+ handle++;
52
+ });
53
+ onLoad({ scene });
54
+ } catch (e: any) {
55
+ if (onError) {
56
+ onError(e);
57
+ } else {
58
+ console.error(e);
59
+ }
60
+ this.manager.itemError(url);
61
+ } finally {
62
+ clear();
63
+ }
64
+ },
65
+ onProgress,
66
+ onError
67
+ );
68
+ }
69
+ }
70
+
71
+ export { IFCXLoader };
@@ -0,0 +1,28 @@
1
+ import { Group } from "three";
2
+ import { USDZLoader } from "three/examples/jsm/loaders/USDZLoader.js";
3
+
4
+ export interface USDZ {
5
+ scene: Group;
6
+ animations: boolean;
7
+ }
8
+
9
+ export class USDZLoader2 extends USDZLoader {
10
+ override parse(buffer) {
11
+ const scene = super.parse(buffer);
12
+ let handle = 0;
13
+ scene.traverse((object) => {
14
+ object.userData = { ...object.userData, handle };
15
+ handle++;
16
+ });
17
+ return scene;
18
+ }
19
+
20
+ override load(
21
+ url: string,
22
+ onLoad: (data: any) => void,
23
+ onProgress?: (event: ProgressEvent) => void,
24
+ onError?: (err: unknown) => void
25
+ ) {
26
+ super.load(url, (scene) => onLoad({ scene }), onProgress, onError);
27
+ }
28
+ }