@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
@@ -10,6 +10,7 @@ import {
10
10
  LineLoop,
11
11
  Group,
12
12
  Vector3,
13
+ Vector2,
13
14
  Quaternion,
14
15
  Matrix4,
15
16
  Box3,
@@ -26,6 +27,8 @@ import {
26
27
  import { GL_CONSTANTS } from "./GltfStructure.js";
27
28
  import { mergeGeometries } from "three/examples/jsm/utils/BufferGeometryUtils.js";
28
29
 
30
+ const STRUCTURE_ID_SEPARATOR = ":";
31
+
29
32
  //#AI-GENERATED using Gemini 2.5 Pro, Claude-4-sonnet
30
33
  //#Reviewed and adapted by dborysov@opendesign.com
31
34
 
@@ -42,6 +45,7 @@ export class DynamicGltfLoader {
42
45
  geometryerror: [],
43
46
  update: [],
44
47
  geometrymemory: [],
48
+ optimizationprogress: [],
45
49
  };
46
50
 
47
51
  this.loadDistance = 100;
@@ -98,7 +102,6 @@ export class DynamicGltfLoader {
98
102
  this.newOptimizedObjects = new Set();
99
103
  this.oldOptimizeObjects = new Set();
100
104
 
101
- this.maxConcurrentChunks = 8;
102
105
  this.activeChunkLoads = 0;
103
106
  this.chunkQueue = [];
104
107
 
@@ -113,6 +116,8 @@ export class DynamicGltfLoader {
113
116
  // Merged geometry tracking
114
117
  this.mergedObjectMap = new Map(); // objectId -> {mergedObject, startIndex, endIndex, vertexCount}
115
118
  this.mergedGeometryVisibility = new Map(); // mergedObject -> visibility array
119
+
120
+ this._webglInfoCache = null; // { renderer, vendor }
116
121
  }
117
122
 
118
123
  setVisibleEdges(visible) {
@@ -244,6 +249,136 @@ export class DynamicGltfLoader {
244
249
  console.log(`Final memory usage: ${Math.round(currentMemoryUsage / (1024 * 1024))}MB`);
245
250
  }
246
251
 
252
+ getStats() {
253
+ let totalObjects = 0;
254
+ let renderedObjects = 0;
255
+ let totalTriangles = 0;
256
+ let renderedTriangles = 0;
257
+ let totalLines = 0;
258
+ let renderedLines = 0;
259
+ let totalEdges = 0;
260
+ let renderedEdges = 0;
261
+
262
+ this.scene.traverse((object) => {
263
+ totalObjects++;
264
+
265
+ const geometry = object.geometry;
266
+ if (!geometry) return;
267
+
268
+ let triCount = 0;
269
+ if (geometry.index) {
270
+ triCount = Math.floor(geometry.index.count / 3);
271
+ } else if (geometry.attributes && geometry.attributes.position) {
272
+ triCount = Math.floor(geometry.attributes.position.count / 3);
273
+ }
274
+ totalTriangles += triCount;
275
+
276
+ let lineCount = 0;
277
+ if (geometry.index) {
278
+ lineCount = Math.floor(geometry.index.count / 2);
279
+ } else if (geometry.attributes && geometry.attributes.position) {
280
+ lineCount = Math.floor(geometry.attributes.position.count / 2);
281
+ }
282
+
283
+ if (object.type === "Line" || object.type === "LineSegments" || object.type === "LineLoop") {
284
+ if (object.userData.isEdge) {
285
+ totalEdges += lineCount;
286
+ } else {
287
+ totalLines += lineCount;
288
+ }
289
+ }
290
+
291
+ if (object.visible !== false) {
292
+ if (object.isMesh || object.isLine || object.isPoints) {
293
+ renderedObjects++;
294
+ if (object.isMesh) {
295
+ renderedTriangles += triCount;
296
+ } else if (object.type === "Line" || object.type === "LineSegments" || object.type === "LineLoop") {
297
+ if (object.userData.isEdge) {
298
+ renderedEdges += lineCount;
299
+ } else {
300
+ renderedLines += lineCount;
301
+ }
302
+ }
303
+ }
304
+ }
305
+ });
306
+
307
+ const geometryCount = this.geometryCache ? this.geometryCache.size : 0;
308
+ const geometryMemoryBytes = Array.from(this.geometryCache?.values?.() || []).reduce((a, b) => a + b, 0);
309
+
310
+ const uniqueMaterialIds = new Set();
311
+ const uniqueTextureIds = new Set();
312
+ if (Array.isArray(this.structures)) {
313
+ for (const structure of this.structures) {
314
+ console.log(structure.materialCache.values());
315
+ try {
316
+ for (const entry of structure.materialCache.values()) {
317
+ if (entry?.mesh?.uuid) uniqueMaterialIds.add(entry.mesh.uuid);
318
+ if (entry?.points?.uuid) uniqueMaterialIds.add(entry.points.uuid);
319
+ if (entry?.lines?.uuid) uniqueMaterialIds.add(entry.lines.uuid);
320
+ }
321
+ } catch (exp) {
322
+ console.error("Error adding material to uniqueMaterialIds", exp);
323
+ }
324
+ }
325
+ }
326
+ const materialCount = uniqueMaterialIds.size;
327
+ const textureCount = uniqueTextureIds.size;
328
+ const estimatedGpuMemoryBytes = geometryMemoryBytes;
329
+
330
+ if (!this._webglInfoCache) {
331
+ try {
332
+ const gl = this.renderer.getContext();
333
+ const dbgInfo = gl.getExtension("WEBGL_debug_renderer_info");
334
+ if (dbgInfo) {
335
+ const rendererStr = gl.getParameter(dbgInfo.UNMASKED_RENDERER_WEBGL);
336
+ const vendorStr = gl.getParameter(dbgInfo.UNMASKED_VENDOR_WEBGL);
337
+ this._webglInfoCache = { renderer: rendererStr, vendor: vendorStr };
338
+ } else {
339
+ this._webglInfoCache = { renderer: null, vendor: null };
340
+ }
341
+ } catch (e) {
342
+ console.error("Error getting webgl info", e);
343
+ this._webglInfoCache = { renderer: null, vendor: null };
344
+ }
345
+ }
346
+
347
+ const size = new Vector2();
348
+ if (this.renderer && this.renderer.getSize) {
349
+ this.renderer.getSize(size);
350
+ }
351
+
352
+ return {
353
+ scene: {
354
+ beforeOptimization: {
355
+ objects: totalObjects - renderedObjects,
356
+ triangles: totalTriangles - renderedTriangles,
357
+ lines: totalLines - renderedLines,
358
+ edges: totalEdges - renderedEdges,
359
+ },
360
+
361
+ afterOptimization: {
362
+ objects: renderedObjects,
363
+ triangles: renderedTriangles,
364
+ lines: renderedLines,
365
+ edges: renderedEdges,
366
+ },
367
+ },
368
+ memory: {
369
+ geometries: { count: geometryCount, bytes: geometryMemoryBytes },
370
+ textures: { count: textureCount },
371
+ materials: { count: materialCount },
372
+ totalEstimatedGpuBytes: estimatedGpuMemoryBytes,
373
+ },
374
+ system: {
375
+ webglRenderer: this._webglInfoCache?.renderer || "",
376
+ webglVendor: this._webglInfoCache?.vendor || "",
377
+ viewport: { width: size.x || 0, height: size.y || 0 },
378
+ },
379
+ };
380
+ }
381
+
247
382
  async loadNode(nodeId, onLoadFinishCb) {
248
383
  const node = this.nodes.get(nodeId);
249
384
  if (!node || node.loaded || node.loading) return;
@@ -419,6 +554,7 @@ export class DynamicGltfLoader {
419
554
  } else if (primitive.mode === GL_CONSTANTS.LINE_LOOP) {
420
555
  mesh = new LineLoop(geometry, material);
421
556
  }
557
+
422
558
  if (node.extras) {
423
559
  mesh.userData = { ...mesh.userData, ...node.extras };
424
560
  }
@@ -428,17 +564,21 @@ export class DynamicGltfLoader {
428
564
  if (primitive.extras) {
429
565
  mesh.userData = { ...mesh.userData, ...primitive.extras };
430
566
  }
567
+
431
568
  if (node.handle) {
432
569
  mesh.userData.handle = node.handle;
433
570
  } else {
434
- mesh.userData.handle = `${node.structure.id}_${mesh.userData.handle}`;
571
+ mesh.userData.handle = this.getFullHandle(node.structure.id, mesh.userData.handle);
435
572
  }
573
+
436
574
  if (mesh.material.name === "edges") {
437
575
  mesh.userData.isEdge = true;
438
576
  } else {
439
577
  mesh.userData.isEdge = false;
440
578
  }
579
+
441
580
  this.registerObjectWithHandle(mesh, mesh.userData.handle);
581
+
442
582
  mesh.position.copy(node.position);
443
583
  if (!geometry.attributes.normal) {
444
584
  geometry.computeVertexNormals();
@@ -601,13 +741,17 @@ export class DynamicGltfLoader {
601
741
  });
602
742
  }
603
743
 
744
+ getFullHandle(structureId, originalHandle) {
745
+ return `${structureId}${STRUCTURE_ID_SEPARATOR}${originalHandle}`;
746
+ }
747
+
604
748
  async processNodeHierarchy(structure, nodeId, parentGroup) {
605
749
  const nodeDef = structure.json.nodes[nodeId];
606
750
  let nodeGroup = null;
607
751
 
608
752
  let handle = null;
609
753
  if (nodeDef.extras?.handle) {
610
- handle = `${structure.id}_${nodeDef.extras.handle}`;
754
+ handle = this.getFullHandle(structure.id, nodeDef.extras.handle);
611
755
  }
612
756
 
613
757
  if (nodeDef.camera !== undefined) {
@@ -628,7 +772,7 @@ export class DynamicGltfLoader {
628
772
  if (nodeDef.extras) {
629
773
  nodeGroup.userData = { ...nodeDef.extras };
630
774
  if (nodeGroup.userData.handle) {
631
- nodeGroup.userData.handle = `${structure.id}_${nodeGroup.userData.handle}`;
775
+ nodeGroup.userData.handle = this.getFullHandle(structure.id, nodeGroup.userData.handle);
632
776
  }
633
777
  }
634
778
 
@@ -680,7 +824,7 @@ export class DynamicGltfLoader {
680
824
  }
681
825
 
682
826
  if (meshDef.extras && meshDef.extras.handle) {
683
- handle = `${structure.id}_${meshDef.extras.handle}`;
827
+ handle = this.getFullHandle(structure.id, meshDef.extras.handle);
684
828
  }
685
829
 
686
830
  this.nodes.set(uniqueNodeId, {
@@ -694,7 +838,7 @@ export class DynamicGltfLoader {
694
838
  structure,
695
839
  extras: nodeDef.extras,
696
840
  geometryExtents,
697
- handle,
841
+ handle: handle || this.getFullHandle(structure.id, structure._nextObjectId++),
698
842
  });
699
843
  }
700
844
 
@@ -785,13 +929,13 @@ export class DynamicGltfLoader {
785
929
  }
786
930
 
787
931
  async loadNodes() {
788
- console.time("process nodes");
932
+ console.time("Process nodes");
789
933
  await this.processNodes();
790
- console.timeEnd("process nodes");
934
+ console.timeEnd("Process nodes");
791
935
 
792
- console.time("optimize scene");
936
+ console.time("Optimize scene");
793
937
  await this.optimizeScene();
794
- console.timeEnd("optimize scene");
938
+ console.timeEnd("Optimize scene");
795
939
  }
796
940
 
797
941
  cleanupPartialLoad() {
@@ -955,7 +1099,7 @@ export class DynamicGltfLoader {
955
1099
  }
956
1100
 
957
1101
  createVisibilityMaterial(material) {
958
- // Применяем шейдер напрямую к оригинальному материалу
1102
+ // Apply shader directly to the original material
959
1103
  material.onBeforeCompile = (shader) => {
960
1104
  shader.vertexShader = shader.vertexShader.replace(
961
1105
  "#include <common>",
@@ -991,7 +1135,7 @@ export class DynamicGltfLoader {
991
1135
  );
992
1136
  };
993
1137
 
994
- // Принудительная перекомпиляция материала
1138
+ // Force recompilation of material
995
1139
  material.needsUpdate = true;
996
1140
 
997
1141
  return material;
@@ -1162,7 +1306,7 @@ export class DynamicGltfLoader {
1162
1306
  }
1163
1307
  this.handleToObjects.get(fullHandle).add(object);
1164
1308
 
1165
- object.userData.structureId = object.userData.handle.split("_")[0];
1309
+ object.userData.structureId = object.userData.handle.split(STRUCTURE_ID_SEPARATOR)[0];
1166
1310
  }
1167
1311
 
1168
1312
  getObjectsByHandle(handle) {
@@ -1237,11 +1381,32 @@ export class DynamicGltfLoader {
1237
1381
  this.originalObjects.add(object);
1238
1382
  }
1239
1383
 
1240
- optimizeScene() {
1384
+ yieldToUI() {
1385
+ return new Promise((resolve) => {
1386
+ requestAnimationFrame(() => {
1387
+ setTimeout(resolve, 0);
1388
+ });
1389
+ });
1390
+ }
1391
+
1392
+ async optimizeScene() {
1393
+ console.log("Starting scene optimization...");
1394
+ this.dispatchEvent("optimizationprogress", {
1395
+ phase: "start",
1396
+ progress: 0,
1397
+ message: "Starting optimization...",
1398
+ });
1399
+
1241
1400
  this.originalObjects.clear();
1242
1401
  this.originalObjectsToSelection.clear();
1243
1402
  const structureGroups = new Map();
1244
1403
 
1404
+ this.dispatchEvent("optimizationprogress", {
1405
+ phase: "collecting",
1406
+ progress: 5,
1407
+ message: "Collecting scene objects...",
1408
+ });
1409
+
1245
1410
  this.scene.traverse((object) => {
1246
1411
  if (object.userData.structureId) {
1247
1412
  const structureId = object.userData.structureId;
@@ -1274,32 +1439,78 @@ export class DynamicGltfLoader {
1274
1439
  }
1275
1440
  });
1276
1441
 
1442
+ let processedGroups = 0;
1443
+ const totalGroups = structureGroups.size;
1444
+
1445
+ this.dispatchEvent("optimizationprogress", {
1446
+ phase: "merging",
1447
+ progress: 10,
1448
+ message: `Merging ${totalGroups} structure groups...`,
1449
+ current: 0,
1450
+ total: totalGroups,
1451
+ });
1452
+
1277
1453
  for (const group of structureGroups.values()) {
1278
1454
  group.mapMeshes.clear();
1279
1455
  group.mapLines.clear();
1280
1456
  group.mapLineSegments.clear();
1281
1457
  group.mapPoints.clear();
1282
1458
 
1283
- this.mergeMeshGroups(group.meshes, group.rootGroup);
1284
- this.mergeLineGroups(group.lines, group.rootGroup);
1285
- this.mergeLineSegmentGroups(group.lineSegments, group.rootGroup);
1286
- this.mergePointsGroups(group.points, group.rootGroup);
1459
+ await this.mergeMeshGroups(group.meshes, group.rootGroup);
1460
+ await this.yieldToUI();
1461
+
1462
+ await this.mergeLineGroups(group.lines, group.rootGroup);
1463
+ await this.yieldToUI();
1464
+
1465
+ await this.mergeLineSegmentGroups(group.lineSegments, group.rootGroup);
1466
+ await this.yieldToUI();
1467
+
1468
+ await this.mergePointsGroups(group.points, group.rootGroup);
1469
+
1470
+ processedGroups++;
1471
+ const progress = 10 + Math.round((processedGroups / totalGroups) * 80);
1472
+
1473
+ this.dispatchEvent("optimizationprogress", {
1474
+ phase: "merging",
1475
+ progress,
1476
+ message: `Processing structure ${processedGroups}/${totalGroups}...`,
1477
+ current: processedGroups,
1478
+ total: totalGroups,
1479
+ });
1480
+
1481
+ console.log(`Optimization progress: ${processedGroups}/${totalGroups} structure groups processed (${progress}%)`);
1482
+
1483
+ await this.yieldToUI();
1287
1484
  }
1288
1485
 
1486
+ this.dispatchEvent("optimizationprogress", {
1487
+ phase: "finalizing",
1488
+ progress: 95,
1489
+ message: "Finalizing optimization...",
1490
+ });
1491
+
1289
1492
  this.originalObjects.forEach((obj) => {
1290
1493
  obj.visible = false;
1291
1494
  if (!(obj instanceof Points) && !obj.userData.isEdge) {
1292
1495
  this.originalObjectsToSelection.add(obj);
1293
1496
  }
1294
1497
  });
1498
+
1295
1499
  this.initializeObjectVisibility();
1296
1500
 
1297
1501
  console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
1298
1502
 
1503
+ this.dispatchEvent("optimizationprogress", {
1504
+ phase: "complete",
1505
+ progress: 100,
1506
+ message: `Optimization complete! ${this.maxObjectId} objects processed.`,
1507
+ });
1508
+
1299
1509
  this.dispatchEvent("update");
1300
1510
  }
1301
1511
 
1302
- mergeMeshGroups(materialGroups, rootGroup) {
1512
+ async mergeMeshGroups(materialGroups, rootGroup) {
1513
+ let processedGroups = 0;
1303
1514
  for (const group of materialGroups) {
1304
1515
  if (!group.material) {
1305
1516
  console.warn("Skipping mesh group with null material");
@@ -1315,8 +1526,6 @@ export class DynamicGltfLoader {
1315
1526
 
1316
1527
  for (const mesh of group.objects) {
1317
1528
  const geometry = mesh.geometry.clone();
1318
- mesh.updateWorldMatrix(true, false);
1319
- geometry.applyMatrix4(mesh.matrixWorld);
1320
1529
 
1321
1530
  const handle = mesh.userData.handle;
1322
1531
  if (!this.objectIdToIndex.has(handle)) {
@@ -1399,6 +1608,11 @@ export class DynamicGltfLoader {
1399
1608
  this.handleToOptimizedObjects.set(handle, mergedObjects);
1400
1609
  }
1401
1610
  });
1611
+
1612
+ processedGroups++;
1613
+ if (processedGroups % 5 === 0) {
1614
+ await this.yieldToUI();
1615
+ }
1402
1616
  } catch (error) {
1403
1617
  console.error("Failed to merge meshes for material:", error);
1404
1618
  group.objects.forEach((mesh) => {
@@ -1408,7 +1622,8 @@ export class DynamicGltfLoader {
1408
1622
  }
1409
1623
  }
1410
1624
 
1411
- mergeLineGroups(materialGroups, rootGroup) {
1625
+ async mergeLineGroups(materialGroups, rootGroup) {
1626
+ let processedGroups = 0;
1412
1627
  for (const group of materialGroups) {
1413
1628
  if (group.objects.length === 0) continue;
1414
1629
 
@@ -1433,7 +1648,9 @@ export class DynamicGltfLoader {
1433
1648
  const indices = [];
1434
1649
  let vertexOffset = 0;
1435
1650
 
1651
+ let isEdge = false;
1436
1652
  group.objects.forEach((line) => {
1653
+ isEdge = line.userData.isEdge;
1437
1654
  const geometry = line.geometry;
1438
1655
  const positionAttr = geometry.attributes.position;
1439
1656
  const vertexCount = positionAttr.count;
@@ -1442,21 +1659,16 @@ export class DynamicGltfLoader {
1442
1659
  if (!this.objectIdToIndex.has(handle)) {
1443
1660
  this.objectIdToIndex.set(handle, this.maxObjectId++);
1444
1661
  }
1445
- // const objectId = this.objectIdToIndex.get(handle);
1446
1662
 
1447
1663
  objectMapping.set(line, {
1448
1664
  startVertexIndex: currentVertexOffset,
1449
1665
  vertexCount,
1450
1666
  });
1451
1667
  currentVertexOffset += vertexCount;
1452
-
1453
- line.updateWorldMatrix(true, false);
1454
- const matrix = line.matrixWorld;
1455
1668
  const vector = new Vector3();
1456
1669
 
1457
1670
  for (let i = 0; i < vertexCount; i++) {
1458
1671
  vector.fromBufferAttribute(positionAttr, i);
1459
- vector.applyMatrix4(matrix);
1460
1672
  positions[posOffset++] = vector.x;
1461
1673
  positions[posOffset++] = vector.y;
1462
1674
  positions[posOffset++] = vector.z;
@@ -1497,6 +1709,8 @@ export class DynamicGltfLoader {
1497
1709
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
1498
1710
 
1499
1711
  const mergedLine = new LineSegments(geometry, visibilityMaterial);
1712
+ mergedLine.userData.isEdge = isEdge;
1713
+
1500
1714
  const mergedObjects = [mergedLine];
1501
1715
  if (this.useVAO) {
1502
1716
  this.createVAO(mergedLine);
@@ -1522,10 +1736,16 @@ export class DynamicGltfLoader {
1522
1736
  this.handleToOptimizedObjects.set(handle, mergedObjects);
1523
1737
  }
1524
1738
  });
1739
+
1740
+ processedGroups++;
1741
+ if (processedGroups % 5 === 0) {
1742
+ await this.yieldToUI();
1743
+ }
1525
1744
  }
1526
1745
  }
1527
1746
 
1528
- mergeLineSegmentGroups(materialGroups, rootGroup) {
1747
+ async mergeLineSegmentGroups(materialGroups, rootGroup) {
1748
+ let processedGroups = 0;
1529
1749
  for (const group of materialGroups) {
1530
1750
  if (!group.material) {
1531
1751
  console.warn("Skipping line segment group with null material");
@@ -1538,11 +1758,11 @@ export class DynamicGltfLoader {
1538
1758
  const handles = new Set();
1539
1759
  const objectMapping = new Map();
1540
1760
  let currentVertexOffset = 0;
1761
+ let isEdge = false;
1541
1762
 
1542
1763
  for (const line of group.objects) {
1764
+ isEdge = line.userData.isEdge;
1543
1765
  const geometry = line.geometry.clone();
1544
- line.updateWorldMatrix(true, false);
1545
- geometry.applyMatrix4(line.matrixWorld);
1546
1766
 
1547
1767
  const handle = line.userData.handle;
1548
1768
  if (!this.objectIdToIndex.has(handle)) {
@@ -1585,6 +1805,7 @@ export class DynamicGltfLoader {
1585
1805
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
1586
1806
 
1587
1807
  const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
1808
+ mergedLine.userData.isEdge = isEdge;
1588
1809
 
1589
1810
  if (this.useVAO) {
1590
1811
  this.createVAO(mergedLine);
@@ -1618,6 +1839,11 @@ export class DynamicGltfLoader {
1618
1839
  this.handleToOptimizedObjects.set(handle, mergedObjects);
1619
1840
  }
1620
1841
  });
1842
+
1843
+ processedGroups++;
1844
+ if (processedGroups % 5 === 0) {
1845
+ await this.yieldToUI();
1846
+ }
1621
1847
  } catch (error) {
1622
1848
  console.warn("Failed to merge line segments for material:", error);
1623
1849
  group.objects.forEach((line) => {
@@ -1627,7 +1853,8 @@ export class DynamicGltfLoader {
1627
1853
  }
1628
1854
  }
1629
1855
 
1630
- mergePointsGroups(materialGroups, rootGroup) {
1856
+ async mergePointsGroups(materialGroups, rootGroup) {
1857
+ let processedGroups = 0;
1631
1858
  for (const group of materialGroups) {
1632
1859
  if (!group.material) {
1633
1860
  console.warn("Skipping points group with null material");
@@ -1641,8 +1868,6 @@ export class DynamicGltfLoader {
1641
1868
 
1642
1869
  for (const points of group.objects) {
1643
1870
  const geometry = points.geometry.clone();
1644
- points.updateWorldMatrix(true, false);
1645
- geometry.applyMatrix4(points.matrixWorld);
1646
1871
  geometries.push(geometry);
1647
1872
  optimizedObjects.push(points);
1648
1873
  handles.add(points.userData.handle);
@@ -1678,6 +1903,11 @@ export class DynamicGltfLoader {
1678
1903
  this.handleToOptimizedObjects.set(handle, mergedObjects);
1679
1904
  }
1680
1905
  });
1906
+
1907
+ processedGroups++;
1908
+ if (processedGroups % 5 === 0) {
1909
+ await this.yieldToUI();
1910
+ }
1681
1911
  } catch (error) {
1682
1912
  console.warn("Failed to merge points for material:", error);
1683
1913
  group.objects.forEach((points) => {
@@ -1700,7 +1930,6 @@ export class DynamicGltfLoader {
1700
1930
 
1701
1931
  lineSegmentsArray.forEach((segment) => {
1702
1932
  const clonedGeometry = segment.geometry.clone();
1703
- segment.updateWorldMatrix(true, false);
1704
1933
  clonedGeometry.applyMatrix4(segment.matrixWorld);
1705
1934
 
1706
1935
  if (hasNormals && !clonedGeometry.attributes.normal) {
@@ -30,7 +30,6 @@ import { DynamicGltfLoader } from "./DynamicGltfLoader.js";
30
30
 
31
31
  export class DynamicModelImpl extends ModelImpl {
32
32
  public gltfLoader: DynamicGltfLoader;
33
- public modelId: number;
34
33
 
35
34
  override getExtents(target: Box3): Box3 {
36
35
  return target.union(this.gltfLoader.getTotalGeometryExtent());
@@ -53,35 +52,46 @@ export class DynamicModelImpl extends ModelImpl {
53
52
  }
54
53
 
55
54
  override getObjectsByHandles(handles: string | string[]): Object3D[] {
56
- const handlesSet = new Set(handles);
55
+ const ownHandles = this.getOwnHandles(handles);
56
+ if (ownHandles.length === 0) return [];
57
+
58
+ const handlesSet = new Set(ownHandles);
59
+
57
60
  const objects = [];
58
61
  handlesSet.forEach((handle) => {
59
- const handle2 = `${this.modelId}_${handle}`; // <- props fix: Dynamic Loader uses handle with structure ID prefix
60
- const handles = this.gltfLoader.handleToObjects.get(handle2) || [];
61
- objects.push(...Array.from(handles));
62
+ objects.push(...this.gltfLoader.getObjectsByHandle(handle));
62
63
  });
64
+
63
65
  return objects;
64
66
  }
65
67
 
66
68
  override getHandlesByObjects(objects: Object3D | Object3D[]): string[] {
67
- const handles = super.getHandlesByObjects(objects);
68
- return handles.map((x) => x.split("_").pop()); // <- props fix: remove structure ID prefix by the Dynamic Loader
69
+ const ownObjects = this.getOwnObjects(objects);
70
+ if (ownObjects.length === 0) return [];
71
+
72
+ const handleSet = new Set<string>();
73
+ ownObjects.forEach((object) => {
74
+ const handle = object.userData.handle;
75
+ if (handle) handleSet.add(handle);
76
+ });
77
+
78
+ return Array.from(handleSet);
69
79
  }
70
80
 
71
81
  override hideObjects(objects: Object3D | Object3D[]): this {
72
- const handles = super.getHandlesByObjects(objects);
82
+ const handles = this.getHandlesByObjects(objects);
73
83
  this.gltfLoader.hideObjects(handles);
74
84
  return this;
75
85
  }
76
86
 
77
87
  override isolateObjects(objects: Object3D | Object3D[]): this {
78
- const handles = super.getHandlesByObjects(objects);
88
+ const handles = this.getHandlesByObjects(objects);
79
89
  this.gltfLoader.isolateObjects(new Set(handles));
80
90
  return this;
81
91
  }
82
92
 
83
93
  override showObjects(objects: Object3D | Object3D[]): this {
84
- const handles = super.getHandlesByObjects(objects);
94
+ const handles = this.getHandlesByObjects(objects);
85
95
  this.gltfLoader.showObjects(handles);
86
96
  return this;
87
97
  }
@@ -58,12 +58,15 @@ export class GltfStructure {
58
58
  this.materials = new Map();
59
59
  this.textureCache = new Map();
60
60
  this.materialCache = new Map();
61
+ this.uri = "";
62
+ this._nextObjectId = 0;
61
63
  }
62
64
 
63
65
  async initialize(loader) {
64
66
  this.json = await this.loadController.loadJson();
65
67
  this.baseUrl = await this.loadController.baseUrl();
66
68
  this.loader = loader;
69
+ this.uri = this.json.buffers[0].uri || "";
67
70
  }
68
71
 
69
72
  clear() {
@@ -166,7 +169,7 @@ export class GltfStructure {
166
169
 
167
170
  try {
168
171
  const length = range.end - range.start;
169
- const buffer = await this.loadController.loadBinaryData([{ offset: range.start, length }]);
172
+ const buffer = await this.loadController.loadBinaryData([{ offset: range.start, length }], this.uri);
170
173
 
171
174
  for (const req of range.requests) {
172
175
  const relOffset = req.offset - range.start;