@inweb/viewer-three 26.10.6 → 26.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +7 -4
  2. package/dist/{plugins → extensions}/components/AxesHelperComponent.js +23 -1
  3. package/dist/extensions/components/AxesHelperComponent.js.map +1 -0
  4. package/dist/extensions/components/AxesHelperComponent.min.js +24 -0
  5. package/dist/{plugins → extensions}/components/AxesHelperComponent.module.js +24 -2
  6. package/dist/extensions/components/AxesHelperComponent.module.js.map +1 -0
  7. package/dist/{plugins → extensions}/components/ExtentsHelperComponent.js +18 -0
  8. package/dist/extensions/components/ExtentsHelperComponent.js.map +1 -0
  9. package/dist/{plugins/components/AxesHelperComponent.min.js → extensions/components/ExtentsHelperComponent.min.js} +1 -1
  10. package/dist/{plugins → extensions}/components/ExtentsHelperComponent.module.js +19 -1
  11. package/dist/extensions/components/ExtentsHelperComponent.module.js.map +1 -0
  12. package/dist/extensions/components/GridHelperComponent.js.map +1 -0
  13. package/dist/extensions/components/GridHelperComponent.module.js.map +1 -0
  14. package/dist/extensions/components/LightHelperComponent.js.map +1 -0
  15. package/dist/extensions/components/LightHelperComponent.module.js.map +1 -0
  16. package/dist/extensions/components/RoomEnvironmentComponent.js.map +1 -0
  17. package/dist/extensions/components/RoomEnvironmentComponent.module.js.map +1 -0
  18. package/dist/extensions/components/StatsPanelComponent.js.map +1 -0
  19. package/dist/extensions/components/StatsPanelComponent.module.js.map +1 -0
  20. package/dist/{plugins → extensions}/loaders/GLTFCloudLoader.js +2 -3
  21. package/dist/extensions/loaders/GLTFCloudLoader.js.map +1 -0
  22. package/dist/{plugins → extensions}/loaders/GLTFCloudLoader.min.js +1 -1
  23. package/dist/{plugins → extensions}/loaders/GLTFCloudLoader.module.js +2 -3
  24. package/dist/extensions/loaders/GLTFCloudLoader.module.js.map +1 -0
  25. package/dist/extensions/loaders/GLTFFileLoader.js +2499 -0
  26. package/dist/extensions/loaders/GLTFFileLoader.js.map +1 -0
  27. package/dist/extensions/loaders/GLTFFileLoader.min.js +24 -0
  28. package/dist/extensions/loaders/GLTFFileLoader.module.js +74 -0
  29. package/dist/extensions/loaders/GLTFFileLoader.module.js.map +1 -0
  30. package/dist/{plugins → extensions}/loaders/IFCXLoader.js +5 -7
  31. package/dist/extensions/loaders/IFCXLoader.js.map +1 -0
  32. package/dist/{plugins → extensions}/loaders/IFCXLoader.min.js +1 -1
  33. package/dist/{plugins → extensions}/loaders/IFCXLoader.module.js +5 -7
  34. package/dist/extensions/loaders/IFCXLoader.module.js.map +1 -0
  35. package/dist/{plugins → extensions}/loaders/PotreeLoader.js +1 -2
  36. package/dist/extensions/loaders/PotreeLoader.js.map +1 -0
  37. package/dist/{plugins → extensions}/loaders/PotreeLoader.min.js +1 -1
  38. package/dist/{plugins → extensions}/loaders/PotreeLoader.module.js +1 -2
  39. package/dist/extensions/loaders/PotreeLoader.module.js.map +1 -0
  40. package/dist/viewer-three.js +1015 -2926
  41. package/dist/viewer-three.js.map +1 -1
  42. package/dist/viewer-three.min.js +3 -3
  43. package/dist/viewer-three.module.js +847 -356
  44. package/dist/viewer-three.module.js.map +1 -1
  45. package/{plugins → extensions}/components/AxesHelperComponent.ts +31 -2
  46. package/{plugins → extensions}/components/ExtentsHelperComponent.ts +25 -0
  47. package/{plugins → extensions}/loaders/GLTFCloudLoader.ts +2 -3
  48. package/{src/Viewer → extensions}/loaders/GLTFFileLoader.ts +21 -12
  49. package/{plugins → extensions}/loaders/IFCX/IFCXCloudLoader.ts +5 -5
  50. package/{plugins → extensions}/loaders/IFCX/IFCXFileLoader.ts +3 -4
  51. package/{plugins → extensions}/loaders/Potree/PotreeFileLoader.ts +3 -4
  52. package/lib/Viewer/Viewer.d.ts +27 -20
  53. package/lib/Viewer/commands/GetSelected2.d.ts +2 -0
  54. package/lib/Viewer/commands/SelectModel.d.ts +1 -1
  55. package/lib/Viewer/commands/SetSelected2.d.ts +2 -0
  56. package/lib/Viewer/components/SelectionComponent.d.ts +1 -3
  57. package/lib/Viewer/components/index.d.ts +6 -6
  58. package/lib/Viewer/draggers/MeasureLineDragger.d.ts +7 -1
  59. package/lib/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.d.ts +0 -1
  60. package/lib/Viewer/loaders/GLTFBinaryExtension.d.ts +5 -0
  61. package/lib/Viewer/loaders/GLTFCloudDynamicLoader.d.ts +2 -2
  62. package/lib/Viewer/loaders/{GLTFFileLoader.d.ts → GLTFFileDynamicLoader.d.ts} +7 -1
  63. package/lib/Viewer/loaders/GLTFLoadingManager.d.ts +4 -3
  64. package/lib/Viewer/loaders/RangesLoader.d.ts +15 -0
  65. package/lib/Viewer/loaders/index.d.ts +22 -14
  66. package/lib/Viewer/measurement/Snapper.d.ts +15 -0
  67. package/lib/Viewer/measurement/UnitConverter.d.ts +63 -0
  68. package/lib/Viewer/measurement/UnitFormatter.d.ts +4 -0
  69. package/lib/Viewer/models/IModelImpl.d.ts +10 -8
  70. package/lib/Viewer/models/ModelImpl.d.ts +7 -5
  71. package/package.json +11 -11
  72. package/src/Viewer/Viewer.ts +120 -88
  73. package/src/Viewer/commands/ClearSelected.ts +3 -1
  74. package/src/Viewer/commands/GetModels.ts +1 -1
  75. package/src/Viewer/commands/GetSelected.ts +2 -2
  76. package/src/Viewer/commands/GetSelected2.ts +34 -0
  77. package/src/Viewer/commands/HideSelected.ts +3 -1
  78. package/src/Viewer/commands/SelectModel.ts +5 -5
  79. package/src/Viewer/commands/SetSelected.ts +9 -10
  80. package/src/Viewer/commands/SetSelected2.ts +42 -0
  81. package/src/Viewer/commands/ZoomToObjects.ts +5 -6
  82. package/src/Viewer/commands/ZoomToSelected.ts +3 -1
  83. package/src/Viewer/commands/index.ts +4 -0
  84. package/src/Viewer/components/CameraComponent.ts +6 -1
  85. package/src/Viewer/components/ExtentsComponent.ts +4 -1
  86. package/src/Viewer/components/SelectionComponent.ts +7 -30
  87. package/src/Viewer/components/index.ts +6 -6
  88. package/src/Viewer/draggers/MeasureLineDragger.ts +84 -226
  89. package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +263 -34
  90. package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +20 -10
  91. package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +4 -1
  92. package/src/Viewer/loaders/GLTFBinaryExtension.ts +91 -0
  93. package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +13 -19
  94. package/src/Viewer/loaders/GLTFFileDynamicLoader.ts +145 -0
  95. package/src/Viewer/loaders/GLTFLoadingManager.ts +5 -4
  96. package/src/Viewer/loaders/RangesLoader.ts +95 -0
  97. package/src/Viewer/loaders/index.ts +24 -16
  98. package/src/Viewer/measurement/Snapper.ts +208 -0
  99. package/src/Viewer/measurement/UnitConverter.ts +47 -0
  100. package/src/Viewer/measurement/UnitFormatter.ts +95 -0
  101. package/src/Viewer/models/IModelImpl.ts +15 -8
  102. package/src/Viewer/models/ModelImpl.ts +48 -17
  103. package/src/index-umd.ts +1 -1
  104. package/dist/plugins/components/AxesHelperComponent.js.map +0 -1
  105. package/dist/plugins/components/AxesHelperComponent.module.js.map +0 -1
  106. package/dist/plugins/components/ExtentsHelperComponent.js.map +0 -1
  107. package/dist/plugins/components/ExtentsHelperComponent.min.js +0 -24
  108. package/dist/plugins/components/ExtentsHelperComponent.module.js.map +0 -1
  109. package/dist/plugins/components/GridHelperComponent.js.map +0 -1
  110. package/dist/plugins/components/GridHelperComponent.module.js.map +0 -1
  111. package/dist/plugins/components/LightHelperComponent.js.map +0 -1
  112. package/dist/plugins/components/LightHelperComponent.module.js.map +0 -1
  113. package/dist/plugins/components/RoomEnvironmentComponent.js.map +0 -1
  114. package/dist/plugins/components/RoomEnvironmentComponent.module.js.map +0 -1
  115. package/dist/plugins/components/StatsPanelComponent.js.map +0 -1
  116. package/dist/plugins/components/StatsPanelComponent.module.js.map +0 -1
  117. package/dist/plugins/loaders/GLTFCloudLoader.js.map +0 -1
  118. package/dist/plugins/loaders/GLTFCloudLoader.module.js.map +0 -1
  119. package/dist/plugins/loaders/IFCXLoader.js.map +0 -1
  120. package/dist/plugins/loaders/IFCXLoader.module.js.map +0 -1
  121. package/dist/plugins/loaders/PotreeLoader.js.map +0 -1
  122. package/dist/plugins/loaders/PotreeLoader.module.js.map +0 -1
  123. /package/dist/{plugins → extensions}/components/GridHelperComponent.js +0 -0
  124. /package/dist/{plugins → extensions}/components/GridHelperComponent.min.js +0 -0
  125. /package/dist/{plugins → extensions}/components/GridHelperComponent.module.js +0 -0
  126. /package/dist/{plugins → extensions}/components/LightHelperComponent.js +0 -0
  127. /package/dist/{plugins → extensions}/components/LightHelperComponent.min.js +0 -0
  128. /package/dist/{plugins → extensions}/components/LightHelperComponent.module.js +0 -0
  129. /package/dist/{plugins → extensions}/components/RoomEnvironmentComponent.js +0 -0
  130. /package/dist/{plugins → extensions}/components/RoomEnvironmentComponent.min.js +0 -0
  131. /package/dist/{plugins → extensions}/components/RoomEnvironmentComponent.module.js +0 -0
  132. /package/dist/{plugins → extensions}/components/StatsPanelComponent.js +0 -0
  133. /package/dist/{plugins → extensions}/components/StatsPanelComponent.min.js +0 -0
  134. /package/dist/{plugins → extensions}/components/StatsPanelComponent.module.js +0 -0
  135. /package/{plugins → extensions}/components/GridHelperComponent.ts +0 -0
  136. /package/{plugins → extensions}/components/LightHelperComponent.ts +0 -0
  137. /package/{plugins → extensions}/components/RoomEnvironmentComponent.ts +0 -0
  138. /package/{plugins → extensions}/components/StatsPanelComponent.ts +0 -0
  139. /package/{plugins → extensions}/loaders/IFCX/IFCXLoader.ts +0 -0
  140. /package/{plugins → extensions}/loaders/IFCX/index.ts +0 -0
  141. /package/{plugins → extensions}/loaders/IFCX/render.js +0 -0
  142. /package/{plugins → extensions}/loaders/Potree/PotreeModelImpl.ts +0 -0
  143. /package/{plugins → extensions}/loaders/Potree/index.ts +0 -0
@@ -0,0 +1,91 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2002-2025, 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-2025 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
+ // ===================== AI-CODE-FILE ======================
25
+ // Source: Claude Sonnet 4.5
26
+ // Date: 2025-28-10
27
+ // Reviewer: roman.mochalov@opendesign.com
28
+ // Issue: CLOUD-5933
29
+ // Notes: Originally AI-generated, modified manually
30
+ // =========================================================
31
+
32
+ const BINARY_EXTENSION_HEADER_MAGIC = "glTF";
33
+ const BINARY_EXTENSION_HEADER_LENGTH = 12;
34
+ const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4e4f534a, BIN: 0x004e4042 };
35
+
36
+ export class GLTFBinaryExtension {
37
+ public content: string;
38
+ public body: ArrayBuffer;
39
+
40
+ constructor(data: ArrayBuffer) {
41
+ const headerView = new DataView(data, 0, BINARY_EXTENSION_HEADER_LENGTH);
42
+ const textDecoder = new TextDecoder();
43
+
44
+ const magic = textDecoder.decode(new Uint8Array(data.slice(0, 4)));
45
+
46
+ if (magic !== BINARY_EXTENSION_HEADER_MAGIC) {
47
+ this.content = textDecoder.decode(data);
48
+ return;
49
+ }
50
+
51
+ const header = {
52
+ magic,
53
+ version: headerView.getUint32(4, true),
54
+ length: headerView.getUint32(8, true),
55
+ };
56
+
57
+ if (header.magic !== BINARY_EXTENSION_HEADER_MAGIC) {
58
+ throw new Error("Unsupported glTF-Binary header.");
59
+ }
60
+
61
+ if (header.version < 2.0) {
62
+ throw new Error("Legacy binary file detected.");
63
+ }
64
+
65
+ const chunkContentsLength = header.length - BINARY_EXTENSION_HEADER_LENGTH;
66
+ const chunkView = new DataView(data, BINARY_EXTENSION_HEADER_LENGTH);
67
+ let chunkIndex = 0;
68
+
69
+ while (chunkIndex < chunkContentsLength) {
70
+ const chunkLength = chunkView.getUint32(chunkIndex, true);
71
+ chunkIndex += 4;
72
+
73
+ const chunkType = chunkView.getUint32(chunkIndex, true);
74
+ chunkIndex += 4;
75
+
76
+ if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON) {
77
+ const contentArray = new Uint8Array(data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength);
78
+ this.content = textDecoder.decode(contentArray);
79
+ } else if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN) {
80
+ const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex;
81
+ this.body = data.slice(byteOffset, byteOffset + chunkLength);
82
+ }
83
+
84
+ chunkIndex += chunkLength;
85
+ }
86
+
87
+ if (typeof this.content === "undefined") {
88
+ throw new Error("JSON content not found.");
89
+ }
90
+ }
91
+ }
@@ -22,27 +22,28 @@
22
22
  ///////////////////////////////////////////////////////////////////////////////
23
23
 
24
24
  import { Group } from "three";
25
- import { ILoader, LoadParams } from "@inweb/viewer-core";
25
+ import { Loader, LoadParams } from "@inweb/viewer-core";
26
26
 
27
27
  import { Viewer } from "../Viewer";
28
28
  import { DynamicModelImpl } from "./DynamicGltfLoader/DynamicModelImpl";
29
29
  import { DynamicGltfLoader } from "./DynamicGltfLoader/DynamicGltfLoader.js";
30
30
  import { GltfStructure } from "./DynamicGltfLoader/GltfStructure.js";
31
31
 
32
- export class GLTFCloudDynamicLoader implements ILoader {
32
+ export class GLTFCloudDynamicLoader extends Loader {
33
33
  public viewer: Viewer;
34
34
  public gltfLoader: DynamicGltfLoader;
35
35
  public requestId = 0;
36
36
 
37
37
  constructor(viewer: Viewer) {
38
+ super();
38
39
  this.viewer = viewer;
39
40
  }
40
41
 
41
- dispose() {
42
+ override dispose() {
42
43
  if (this.gltfLoader) this.gltfLoader.clear();
43
44
  }
44
45
 
45
- isSupport(file: any): boolean {
46
+ override isSupport(file: any): boolean {
46
47
  return (
47
48
  typeof file === "object" &&
48
49
  typeof file.database === "string" &&
@@ -52,7 +53,7 @@ export class GLTFCloudDynamicLoader implements ILoader {
52
53
  );
53
54
  }
54
55
 
55
- async load(model: any, format?: string, params?: LoadParams): Promise<this> {
56
+ override async load(model: any, format?: string, params: LoadParams = {}): Promise<this> {
56
57
  const scene = new Group();
57
58
 
58
59
  this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, scene, this.viewer.renderer);
@@ -60,13 +61,11 @@ export class GLTFCloudDynamicLoader implements ILoader {
60
61
  this.gltfLoader.setVisibleEdges(this.viewer.options.edgeModel);
61
62
  // this.gltfLoader.setMaxConcurrentChunks(this.viewer.options.maxConcurrentChunks);
62
63
 
63
- this.gltfLoader.addEventListener("databasechunk", (data) => {
64
- const modelImpl = new DynamicModelImpl(scene);
65
- modelImpl.loader = this;
66
- modelImpl.viewer = this.viewer;
67
- modelImpl.gltfLoader = this.gltfLoader;
68
- modelImpl.modelId = model.id;
64
+ const modelImpl = new DynamicModelImpl(scene);
65
+ modelImpl.id = model.file.id;
66
+ modelImpl.gltfLoader = this.gltfLoader;
69
67
 
68
+ this.gltfLoader.addEventListener("databasechunk", (data) => {
70
69
  this.viewer.scene.add(scene);
71
70
  this.viewer.models.push(modelImpl);
72
71
 
@@ -77,11 +76,6 @@ export class GLTFCloudDynamicLoader implements ILoader {
77
76
  this.viewer.emitEvent({ type: "databasechunk", data: scene, file: model.file, model });
78
77
  });
79
78
 
80
- this.gltfLoader.addEventListener("geometryprogress", (data) => {
81
- const progress = data.loaded / data.total;
82
- this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model.file, model });
83
- });
84
-
85
79
  this.gltfLoader.addEventListener("geometryerror", (data) => {
86
80
  this.viewer.emitEvent({ type: "geometryerror", data, file: model.file, model });
87
81
  });
@@ -93,7 +87,7 @@ export class GLTFCloudDynamicLoader implements ILoader {
93
87
  const loadController = {
94
88
  loadJson: async () => {
95
89
  const progress = (progress: number) => {
96
- this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model });
90
+ this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model.file, model });
97
91
  };
98
92
 
99
93
  const arrayBuffer = await model.downloadResource(
@@ -127,7 +121,7 @@ export class GLTFCloudDynamicLoader implements ILoader {
127
121
  baseUrl: () => Promise.resolve(`${model.httpClient.serverUrl}${model.path}/`),
128
122
  };
129
123
 
130
- const structure = new GltfStructure(model.id, loadController);
124
+ const structure = new GltfStructure(modelImpl.id, loadController);
131
125
 
132
126
  await this.gltfLoader.loadStructure(structure);
133
127
  await this.gltfLoader.loadNodes();
@@ -135,7 +129,7 @@ export class GLTFCloudDynamicLoader implements ILoader {
135
129
  return this;
136
130
  }
137
131
 
138
- cancel() {
132
+ override cancel() {
139
133
  if (this.gltfLoader) this.gltfLoader.abortLoading();
140
134
  }
141
135
  }
@@ -0,0 +1,145 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2002-2025, 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-2025 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, LoaderUtils } from "three";
25
+ import { Loader } from "@inweb/viewer-core";
26
+
27
+ import { Viewer } from "../Viewer";
28
+ import { DynamicModelImpl } from "./DynamicGltfLoader/DynamicModelImpl";
29
+ import { DynamicGltfLoader } from "./DynamicGltfLoader/DynamicGltfLoader.js";
30
+ import { GltfStructure } from "./DynamicGltfLoader/GltfStructure.js";
31
+ import { GLTFLoadingManager, GLTFLoadParams } from "./GLTFLoadingManager";
32
+ import { GLTFBinaryExtension } from "./GLTFBinaryExtension";
33
+ import { RangesLoader } from "./RangesLoader";
34
+
35
+ export class GLTFFileDynamicLoader extends Loader {
36
+ public viewer: Viewer;
37
+ private gltfLoader: DynamicGltfLoader;
38
+ private manager: GLTFLoadingManager;
39
+ private gltf: any;
40
+ private bin: ArrayBuffer;
41
+
42
+ constructor(viewer: Viewer) {
43
+ super();
44
+ this.viewer = viewer;
45
+ }
46
+
47
+ override dispose() {
48
+ if (this.gltfLoader) this.gltfLoader.clear();
49
+ if (this.manager) this.manager.dispose();
50
+ }
51
+
52
+ override isSupport(file: any, format?: string): boolean {
53
+ return (
54
+ (typeof file === "string" || file instanceof globalThis.File || file instanceof ArrayBuffer) &&
55
+ /(gltf|glb)$/i.test(format)
56
+ );
57
+ }
58
+
59
+ override async load(file: any, format?: string, params?: GLTFLoadParams): Promise<this> {
60
+ this.manager = new GLTFLoadingManager(file, params);
61
+
62
+ const scene = new Group();
63
+
64
+ this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, scene, this.viewer.renderer);
65
+ this.gltfLoader.memoryLimit = this.viewer.options.memoryLimit;
66
+ this.gltfLoader.visibleEdges = this.viewer.options.edgeModel;
67
+
68
+ const modelImpl = new DynamicModelImpl(scene);
69
+ modelImpl.id = params.modelId || this.extractFileName(file);
70
+ modelImpl.gltfLoader = this.gltfLoader;
71
+
72
+ this.gltfLoader.addEventListener("databasechunk", () => {
73
+ this.viewer.scene.add(scene);
74
+ this.viewer.models.push(modelImpl);
75
+
76
+ this.viewer.syncOptions();
77
+ this.viewer.syncOverlay();
78
+ this.viewer.update();
79
+
80
+ this.viewer.emitEvent({ type: "databasechunk", data: scene, file });
81
+ });
82
+
83
+ this.gltfLoader.addEventListener("geometryerror", (data) => {
84
+ this.viewer.emitEvent({ type: "geometryerror", data, file });
85
+ });
86
+
87
+ this.gltfLoader.addEventListener("update", (data) => {
88
+ this.viewer.update();
89
+ });
90
+
91
+ const loadController = {
92
+ loadJson: async () => {
93
+ const loader = new FileLoader(this.manager);
94
+ loader.setPath(this.manager.path);
95
+ loader.setRequestHeader((params.requestHeader as any) || {});
96
+ loader.setWithCredentials(params.withCredentials || loader.withCredentials);
97
+ loader.setResponseType("arraybuffer");
98
+
99
+ const progress = (event: ProgressEvent) => {
100
+ const { lengthComputable, loaded, total } = event;
101
+ const progress = lengthComputable ? loaded / total : 1;
102
+ this.viewer.emitEvent({ type: "geometryprogress", data: progress, file });
103
+ };
104
+
105
+ const data = await loader.loadAsync(this.manager.fileURL, progress);
106
+
107
+ const extension = new GLTFBinaryExtension(data as ArrayBuffer);
108
+ this.gltf = JSON.parse(extension.content);
109
+ this.bin = extension.body;
110
+
111
+ return this.gltf;
112
+ },
113
+
114
+ loadBinaryData: (ranges, uri = "") => {
115
+ const loader = new RangesLoader();
116
+ loader.setRequestHeader((params.requestHeader as any) || {});
117
+ loader.setWithCredentials(params.withCredentials || false);
118
+ loader.setAbortSignal(this.gltfLoader.abortController.signal);
119
+
120
+ if (this.bin) return loader.extractRanges(this.bin, ranges);
121
+
122
+ const path = this.manager.path || this.manager.resourcePath;
123
+ const url = LoaderUtils.resolveURL(uri, path);
124
+
125
+ return loader.load(this.manager.resolveURL(url), ranges);
126
+ },
127
+
128
+ baseUrl: () => {
129
+ const path = this.manager.path || this.manager.resourcePath;
130
+ return Promise.resolve(path);
131
+ },
132
+ };
133
+
134
+ const structure = new GltfStructure(modelImpl.id, loadController);
135
+
136
+ await this.gltfLoader.loadStructure(structure);
137
+ await this.gltfLoader.loadNodes();
138
+
139
+ return this;
140
+ }
141
+
142
+ override cancel() {
143
+ if (this.gltfLoader) this.gltfLoader.abortLoading();
144
+ }
145
+ }
@@ -21,16 +21,17 @@
21
21
  // acknowledge and accept the above terms.
22
22
  ///////////////////////////////////////////////////////////////////////////////
23
23
 
24
+ import { LoadParams } from "@inweb/viewer-core";
24
25
  import { LoadingManager, LoaderUtils } from "three";
25
26
 
26
27
  export type GLTFFileSource = string | globalThis.File | ArrayBuffer;
27
28
 
28
- export type GLTFLoadParams = {
29
+ export type GLTFLoadParams = LoadParams & {
30
+ requestHeader?: HeadersInit;
31
+ withCredentials?: boolean;
29
32
  path?: string;
30
33
  externalFiles?: Map<string, GLTFFileSource>;
31
34
  crossOrigin?: string;
32
- requestHeader?: HeadersInit;
33
- withCredentials?: boolean;
34
35
  };
35
36
 
36
37
  export class GLTFLoadingManager extends LoadingManager {
@@ -72,6 +73,6 @@ export class GLTFLoadingManager extends LoadingManager {
72
73
  }
73
74
 
74
75
  dispose() {
75
- this.dataURLs.forEach(URL.revokeObjectURL);
76
+ this.dataURLs.forEach((dataURL) => URL.revokeObjectURL(dataURL));
76
77
  }
77
78
  }
@@ -0,0 +1,95 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2002-2025, 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-2025 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
+ export interface Range {
25
+ offset: number;
26
+ length: number;
27
+ }
28
+
29
+ export class RangesLoader {
30
+ private requestHeader: HeadersInit;
31
+ private withCredentials: boolean;
32
+ private abortSignal: AbortSignal | undefined;
33
+
34
+ constructor() {
35
+ this.requestHeader = {};
36
+ this.withCredentials = false;
37
+ this.abortSignal = undefined;
38
+ }
39
+
40
+ setRequestHeader(requestHeader: HeadersInit) {
41
+ this.requestHeader = requestHeader;
42
+ }
43
+
44
+ setWithCredentials(withCredentials: boolean) {
45
+ this.withCredentials = withCredentials;
46
+ }
47
+
48
+ setAbortSignal(abortSignal: AbortSignal) {
49
+ this.abortSignal = abortSignal;
50
+ }
51
+
52
+ async load(url: string, ranges: Range[]): Promise<ArrayBuffer> {
53
+ const init: RequestInit = {
54
+ headers: {
55
+ ...this.requestHeader,
56
+ Range: "bytes=" + ranges.map((x) => `${x.offset}-${x.offset + x.length - 1}`).join(","),
57
+ },
58
+ credentials: this.withCredentials ? "include" : "same-origin",
59
+ signal: this.abortSignal,
60
+ };
61
+
62
+ const response = await fetch(url, init);
63
+ if (!response.ok) {
64
+ throw new Error(`Failed to fetch "${url}", status ${response.status}`);
65
+ }
66
+
67
+ if (response.status !== 206) {
68
+ const arrayBuffer = await response.arrayBuffer();
69
+ return this.extractRanges(arrayBuffer, ranges);
70
+ }
71
+
72
+ return response.arrayBuffer();
73
+ }
74
+
75
+ // ===================== AI-CODE-START ======================
76
+ // Source: Claude Sonnet 4.5
77
+ // Date: 2025-28-10
78
+ // Reviewer: roman.mochalov@opendesign.com
79
+ // Issue: CLOUD-5933
80
+
81
+ extractRanges(arrayBuffer: ArrayBuffer, ranges: Range[]): ArrayBuffer {
82
+ const totalLength = ranges.reduce((sum, range) => sum + range.length, 0);
83
+ const result = new Uint8Array(totalLength);
84
+
85
+ let offset = 0;
86
+ for (const range of ranges) {
87
+ const chunk = new Uint8Array(arrayBuffer, range.offset, range.length);
88
+ result.set(chunk, offset);
89
+ offset += range.length;
90
+ }
91
+
92
+ return result.buffer;
93
+ }
94
+ // ===================== AI-CODE-END ======================
95
+ }
@@ -23,7 +23,7 @@
23
23
 
24
24
  import { ILoadersRegistry, loadersRegistry } from "@inweb/viewer-core";
25
25
 
26
- import { GLTFFileLoader } from "./GLTFFileLoader";
26
+ import { GLTFFileDynamicLoader } from "./GLTFFileDynamicLoader";
27
27
  import { GLTFCloudDynamicLoader } from "./GLTFCloudDynamicLoader";
28
28
 
29
29
  /**
@@ -33,29 +33,30 @@ import { GLTFCloudDynamicLoader } from "./GLTFCloudDynamicLoader";
33
33
  *
34
34
  * 1. Define a loader class implements {@link ILoader}.
35
35
  * 2. Define a constructor with a `viewer` parameter.
36
- * 3. Override {@link ILoader.isSupport} and сheck if the loader can load the specified file.
36
+ * 3. Override {@link ILoader.isSupport} and check if the loader can load the specified file.
37
37
  * 4. Override {@link ILoader.load} and define the logic for loading the scene from the file.
38
38
  *
39
39
  * The loader should do:
40
40
  *
41
41
  * - Load raw data from file and convert it to the `Three.js` scene.
42
42
  * - Add scene to the viewer `scene`.
43
- * - Create `ModelImpl` for the scene and to the viewer `models` list.
43
+ * - Create `ModelImpl` instance with unique model ID add it to the viewer `models` list.
44
44
  * - Synchronize viewer options and overlay.
45
45
  * - Update the viewer.
46
46
  *
47
47
  * The loader must emit events:
48
48
  *
49
- * - `geometryprogress` - during loading. If progress is not supported, emit it once with a value of 100%
50
- * after the load is complete.
49
+ * - `geometryprogress` - during loading (or once at 100% when complete).
51
50
  * - `databasechunk` - when scene is loaded and ready to render.
52
51
  * 5. Override {@link ILoader.dispose} and release loader resources, if required.
53
- * 6. Register loader provider in the loaders registry by calling the {@link loaders.registerLoader}.
52
+ * 6. Use `this.abortController` (defined in `Loader` class) to abort loading raw data.
53
+ * 7. Register loader provider in the loaders registry by calling the {@link loaders.registerLoader}.
54
54
  *
55
55
  * @example Implementing a custom loader.
56
56
  *
57
57
  * ```javascript
58
- * import { loaders, Loader, ModelImpl, Viewer } from "@inweb/viewer-three";
58
+ * import { Scene } from "three";
59
+ * import { Loader, loaders, ModelImpl, Viewer } from "@inweb/viewer-three";
59
60
  *
60
61
  * class MyLoader extends Loader {
61
62
  * public viewer: Viewer;
@@ -66,18 +67,19 @@ import { GLTFCloudDynamicLoader } from "./GLTFCloudDynamicLoader";
66
67
  * }
67
68
  *
68
69
  * override isSupport(file, format): Boolean {
69
- * // place custom logic here
70
- * return ...;
70
+ * // check if this loader supports the file source and format
71
+ * return type file === "string" && format === "myformat";
71
72
  * }
72
73
  *
73
- * override load(file, format, params): Promise<this> {
74
+ * override load(file, format, params = {}): Promise<this> {
75
+ * // load raw data from file (custom loading logic)
76
+ * const data = await fetch(file).then((result) => result.arrayBuffer());
74
77
  *
75
- * // place custom loading logic here
76
- * const scene = ...;
78
+ * // convert raw data to the Three.js scene (custom parsing logic)
79
+ * const scene = this.parse(data);
77
80
  *
78
81
  * const modelImpl = new ModelImpl(scene);
79
- * modelImpl.loader = this;
80
- * modelImpl.viewer = this.viewer;
82
+ * modelImpl.id = params.modelId;
81
83
  *
82
84
  * this.viewer.scene.add(scene);
83
85
  * this.viewer.models.push(modelImpl);
@@ -86,11 +88,17 @@ import { GLTFCloudDynamicLoader } from "./GLTFCloudDynamicLoader";
86
88
  * this.viewer.syncOverlay();
87
89
  * this.viewer.update();
88
90
  *
91
+ * this.viewer.emitEvent({ type: "geometryprogress", data: 1, file });
89
92
  * this.viewer.emitEvent({ type: "databasechunk", data: scene, file });
90
93
  *
91
94
  * return Promise.resove(this);
92
95
  * };
93
- * }
96
+ *
97
+ * private parse(data: ArrayBuffer): Scene {
98
+ * // custom parsing logic
99
+ * return new Scene();
100
+ * }
101
+ * }
94
102
  *
95
103
  * loaders.registerLoader("MyLoader", (viewer) => new MyLoader(viewer));
96
104
  * ```
@@ -99,5 +107,5 @@ export const loaders: ILoadersRegistry = loadersRegistry("threejs");
99
107
 
100
108
  // build-in loaders
101
109
 
102
- loaders.registerLoader("gltf-file", (viewer: any) => new GLTFFileLoader(viewer));
110
+ loaders.registerLoader("gltf-file", (viewer: any) => new GLTFFileDynamicLoader(viewer));
103
111
  loaders.registerLoader("gltf-cloud", (viewer: any) => new GLTFCloudDynamicLoader(viewer));