@inweb/viewer-three 26.10.6 → 26.12.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 (153) 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/InfoPanelComponent.js +170 -0
  15. package/dist/extensions/components/InfoPanelComponent.js.map +1 -0
  16. package/dist/extensions/components/InfoPanelComponent.min.js +24 -0
  17. package/dist/extensions/components/InfoPanelComponent.module.js +164 -0
  18. package/dist/extensions/components/InfoPanelComponent.module.js.map +1 -0
  19. package/dist/extensions/components/LightHelperComponent.js.map +1 -0
  20. package/dist/extensions/components/LightHelperComponent.module.js.map +1 -0
  21. package/dist/extensions/components/RoomEnvironmentComponent.js.map +1 -0
  22. package/dist/extensions/components/RoomEnvironmentComponent.module.js.map +1 -0
  23. package/dist/{plugins → extensions}/components/StatsPanelComponent.js +9 -3
  24. package/dist/extensions/components/StatsPanelComponent.js.map +1 -0
  25. package/dist/extensions/components/StatsPanelComponent.min.js +24 -0
  26. package/dist/{plugins → extensions}/components/StatsPanelComponent.module.js +9 -3
  27. package/dist/extensions/components/StatsPanelComponent.module.js.map +1 -0
  28. package/dist/{plugins → extensions}/loaders/GLTFCloudLoader.js +2 -3
  29. package/dist/extensions/loaders/GLTFCloudLoader.js.map +1 -0
  30. package/dist/{plugins → extensions}/loaders/GLTFCloudLoader.min.js +1 -1
  31. package/dist/{plugins → extensions}/loaders/GLTFCloudLoader.module.js +2 -3
  32. package/dist/extensions/loaders/GLTFCloudLoader.module.js.map +1 -0
  33. package/dist/extensions/loaders/GLTFFileLoader.js +2499 -0
  34. package/dist/extensions/loaders/GLTFFileLoader.js.map +1 -0
  35. package/dist/extensions/loaders/GLTFFileLoader.min.js +24 -0
  36. package/dist/extensions/loaders/GLTFFileLoader.module.js +74 -0
  37. package/dist/extensions/loaders/GLTFFileLoader.module.js.map +1 -0
  38. package/dist/{plugins → extensions}/loaders/IFCXLoader.js +5 -7
  39. package/dist/extensions/loaders/IFCXLoader.js.map +1 -0
  40. package/dist/{plugins → extensions}/loaders/IFCXLoader.min.js +1 -1
  41. package/dist/{plugins → extensions}/loaders/IFCXLoader.module.js +5 -7
  42. package/dist/extensions/loaders/IFCXLoader.module.js.map +1 -0
  43. package/dist/{plugins → extensions}/loaders/PotreeLoader.js +56 -6
  44. package/dist/extensions/loaders/PotreeLoader.js.map +1 -0
  45. package/dist/extensions/loaders/PotreeLoader.min.js +24 -0
  46. package/dist/{plugins → extensions}/loaders/PotreeLoader.module.js +53 -2
  47. package/dist/extensions/loaders/PotreeLoader.module.js.map +1 -0
  48. package/dist/viewer-three.js +1416 -2930
  49. package/dist/viewer-three.js.map +1 -1
  50. package/dist/viewer-three.min.js +8 -3
  51. package/dist/viewer-three.module.js +1205 -363
  52. package/dist/viewer-three.module.js.map +1 -1
  53. package/{plugins → extensions}/components/AxesHelperComponent.ts +31 -2
  54. package/{plugins → extensions}/components/ExtentsHelperComponent.ts +25 -0
  55. package/extensions/components/InfoPanelComponent.ts +197 -0
  56. package/{plugins → extensions}/components/StatsPanelComponent.ts +10 -3
  57. package/{plugins → extensions}/loaders/GLTFCloudLoader.ts +2 -3
  58. package/{src/Viewer → extensions}/loaders/GLTFFileLoader.ts +21 -12
  59. package/{plugins → extensions}/loaders/IFCX/IFCXCloudLoader.ts +5 -5
  60. package/{plugins → extensions}/loaders/IFCX/IFCXFileLoader.ts +3 -4
  61. package/{plugins → extensions}/loaders/Potree/PotreeFileLoader.ts +3 -4
  62. package/extensions/loaders/Potree/PotreeModelImpl.ts +108 -0
  63. package/lib/Viewer/Viewer.d.ts +28 -20
  64. package/lib/Viewer/commands/GetSelected2.d.ts +2 -0
  65. package/lib/Viewer/commands/SelectModel.d.ts +1 -1
  66. package/lib/Viewer/commands/SetSelected2.d.ts +2 -0
  67. package/lib/Viewer/components/InfoComponent.d.ts +22 -0
  68. package/lib/Viewer/components/SelectionComponent.d.ts +1 -3
  69. package/lib/Viewer/components/index.d.ts +6 -6
  70. package/lib/Viewer/draggers/MeasureLineDragger.d.ts +7 -1
  71. package/lib/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.d.ts +2 -1
  72. package/lib/Viewer/loaders/GLTFBinaryExtension.d.ts +5 -0
  73. package/lib/Viewer/loaders/GLTFCloudDynamicLoader.d.ts +2 -2
  74. package/lib/Viewer/loaders/{GLTFFileLoader.d.ts → GLTFFileDynamicLoader.d.ts} +7 -1
  75. package/lib/Viewer/loaders/GLTFLoadingManager.d.ts +4 -3
  76. package/lib/Viewer/loaders/RangesLoader.d.ts +15 -0
  77. package/lib/Viewer/loaders/index.d.ts +22 -14
  78. package/lib/Viewer/measurement/Snapper.d.ts +15 -0
  79. package/lib/Viewer/measurement/UnitConverter.d.ts +63 -0
  80. package/lib/Viewer/measurement/UnitFormatter.d.ts +4 -0
  81. package/lib/Viewer/models/IModelImpl.d.ts +11 -8
  82. package/lib/Viewer/models/ModelImpl.d.ts +9 -5
  83. package/package.json +11 -11
  84. package/src/Viewer/Viewer.ts +127 -88
  85. package/src/Viewer/commands/ClearSelected.ts +3 -1
  86. package/src/Viewer/commands/GetModels.ts +1 -1
  87. package/src/Viewer/commands/GetSelected.ts +2 -2
  88. package/{plugins/loaders/Potree/PotreeModelImpl.ts → src/Viewer/commands/GetSelected2.ts} +7 -9
  89. package/src/Viewer/commands/HideSelected.ts +3 -1
  90. package/src/Viewer/commands/SelectModel.ts +5 -5
  91. package/src/Viewer/commands/SetSelected.ts +9 -10
  92. package/src/Viewer/commands/SetSelected2.ts +42 -0
  93. package/src/Viewer/commands/ZoomToObjects.ts +5 -6
  94. package/src/Viewer/commands/ZoomToSelected.ts +3 -1
  95. package/src/Viewer/commands/index.ts +4 -0
  96. package/src/Viewer/components/CameraComponent.ts +6 -1
  97. package/src/Viewer/components/ExtentsComponent.ts +4 -1
  98. package/src/Viewer/components/InfoComponent.ts +187 -0
  99. package/src/Viewer/components/SelectionComponent.ts +7 -30
  100. package/src/Viewer/components/index.ts +8 -6
  101. package/src/Viewer/draggers/MeasureLineDragger.ts +84 -226
  102. package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +276 -39
  103. package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +45 -10
  104. package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +71 -2
  105. package/src/Viewer/loaders/GLTFBinaryExtension.ts +91 -0
  106. package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +13 -19
  107. package/src/Viewer/loaders/GLTFFileDynamicLoader.ts +145 -0
  108. package/src/Viewer/loaders/GLTFLoadingManager.ts +5 -4
  109. package/src/Viewer/loaders/RangesLoader.ts +105 -0
  110. package/src/Viewer/loaders/index.ts +24 -16
  111. package/src/Viewer/measurement/Snapper.ts +208 -0
  112. package/src/Viewer/measurement/UnitConverter.ts +47 -0
  113. package/src/Viewer/measurement/UnitFormatter.ts +95 -0
  114. package/src/Viewer/models/IModelImpl.ts +17 -8
  115. package/src/Viewer/models/ModelImpl.ts +205 -16
  116. package/src/index-umd.ts +1 -1
  117. package/dist/plugins/components/AxesHelperComponent.js.map +0 -1
  118. package/dist/plugins/components/AxesHelperComponent.module.js.map +0 -1
  119. package/dist/plugins/components/ExtentsHelperComponent.js.map +0 -1
  120. package/dist/plugins/components/ExtentsHelperComponent.min.js +0 -24
  121. package/dist/plugins/components/ExtentsHelperComponent.module.js.map +0 -1
  122. package/dist/plugins/components/GridHelperComponent.js.map +0 -1
  123. package/dist/plugins/components/GridHelperComponent.module.js.map +0 -1
  124. package/dist/plugins/components/LightHelperComponent.js.map +0 -1
  125. package/dist/plugins/components/LightHelperComponent.module.js.map +0 -1
  126. package/dist/plugins/components/RoomEnvironmentComponent.js.map +0 -1
  127. package/dist/plugins/components/RoomEnvironmentComponent.module.js.map +0 -1
  128. package/dist/plugins/components/StatsPanelComponent.js.map +0 -1
  129. package/dist/plugins/components/StatsPanelComponent.min.js +0 -24
  130. package/dist/plugins/components/StatsPanelComponent.module.js.map +0 -1
  131. package/dist/plugins/loaders/GLTFCloudLoader.js.map +0 -1
  132. package/dist/plugins/loaders/GLTFCloudLoader.module.js.map +0 -1
  133. package/dist/plugins/loaders/IFCXLoader.js.map +0 -1
  134. package/dist/plugins/loaders/IFCXLoader.module.js.map +0 -1
  135. package/dist/plugins/loaders/PotreeLoader.js.map +0 -1
  136. package/dist/plugins/loaders/PotreeLoader.min.js +0 -24
  137. package/dist/plugins/loaders/PotreeLoader.module.js.map +0 -1
  138. /package/dist/{plugins → extensions}/components/GridHelperComponent.js +0 -0
  139. /package/dist/{plugins → extensions}/components/GridHelperComponent.min.js +0 -0
  140. /package/dist/{plugins → extensions}/components/GridHelperComponent.module.js +0 -0
  141. /package/dist/{plugins → extensions}/components/LightHelperComponent.js +0 -0
  142. /package/dist/{plugins → extensions}/components/LightHelperComponent.min.js +0 -0
  143. /package/dist/{plugins → extensions}/components/LightHelperComponent.module.js +0 -0
  144. /package/dist/{plugins → extensions}/components/RoomEnvironmentComponent.js +0 -0
  145. /package/dist/{plugins → extensions}/components/RoomEnvironmentComponent.min.js +0 -0
  146. /package/dist/{plugins → extensions}/components/RoomEnvironmentComponent.module.js +0 -0
  147. /package/{plugins → extensions}/components/GridHelperComponent.ts +0 -0
  148. /package/{plugins → extensions}/components/LightHelperComponent.ts +0 -0
  149. /package/{plugins → extensions}/components/RoomEnvironmentComponent.ts +0 -0
  150. /package/{plugins → extensions}/loaders/IFCX/IFCXLoader.ts +0 -0
  151. /package/{plugins → extensions}/loaders/IFCX/index.ts +0 -0
  152. /package/{plugins → extensions}/loaders/IFCX/render.js +0 -0
  153. /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;
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();
@@ -464,10 +604,17 @@ export class DynamicGltfLoader {
464
604
  onLoadFinishCb();
465
605
  }
466
606
  } catch (error) {
467
- if (error.name !== "AbortError") {
468
- console.error(`Error loading node ${nodeId}:`, error);
469
- }
470
607
  node.loading = false;
608
+
609
+ if (error.name === "AbortError") {
610
+ return;
611
+ }
612
+
613
+ if (node.structure && node.structure.loadingAborted) {
614
+ return;
615
+ }
616
+
617
+ console.error(`Error loading node ${nodeId}:`, error);
471
618
  }
472
619
  }
473
620
 
@@ -601,13 +748,17 @@ export class DynamicGltfLoader {
601
748
  });
602
749
  }
603
750
 
751
+ getFullHandle(structureId, originalHandle) {
752
+ return `${structureId}${STRUCTURE_ID_SEPARATOR}${originalHandle}`;
753
+ }
754
+
604
755
  async processNodeHierarchy(structure, nodeId, parentGroup) {
605
756
  const nodeDef = structure.json.nodes[nodeId];
606
757
  let nodeGroup = null;
607
758
 
608
759
  let handle = null;
609
760
  if (nodeDef.extras?.handle) {
610
- handle = `${structure.id}_${nodeDef.extras.handle}`;
761
+ handle = this.getFullHandle(structure.id, nodeDef.extras.handle);
611
762
  }
612
763
 
613
764
  if (nodeDef.camera !== undefined) {
@@ -628,7 +779,7 @@ export class DynamicGltfLoader {
628
779
  if (nodeDef.extras) {
629
780
  nodeGroup.userData = { ...nodeDef.extras };
630
781
  if (nodeGroup.userData.handle) {
631
- nodeGroup.userData.handle = `${structure.id}_${nodeGroup.userData.handle}`;
782
+ nodeGroup.userData.handle = this.getFullHandle(structure.id, nodeGroup.userData.handle);
632
783
  }
633
784
  }
634
785
 
@@ -680,7 +831,7 @@ export class DynamicGltfLoader {
680
831
  }
681
832
 
682
833
  if (meshDef.extras && meshDef.extras.handle) {
683
- handle = `${structure.id}_${meshDef.extras.handle}`;
834
+ handle = this.getFullHandle(structure.id, meshDef.extras.handle);
684
835
  }
685
836
 
686
837
  this.nodes.set(uniqueNodeId, {
@@ -694,7 +845,7 @@ export class DynamicGltfLoader {
694
845
  structure,
695
846
  extras: nodeDef.extras,
696
847
  geometryExtents,
697
- handle,
848
+ handle: handle || this.getFullHandle(structure.id, structure._nextObjectId++),
698
849
  });
699
850
  }
700
851
 
@@ -785,13 +936,13 @@ export class DynamicGltfLoader {
785
936
  }
786
937
 
787
938
  async loadNodes() {
788
- console.time("process nodes");
939
+ console.time("Process nodes");
789
940
  await this.processNodes();
790
- console.timeEnd("process nodes");
941
+ console.timeEnd("Process nodes");
791
942
 
792
- console.time("optimize scene");
943
+ console.time("Optimize scene");
793
944
  await this.optimizeScene();
794
- console.timeEnd("optimize scene");
945
+ console.timeEnd("Optimize scene");
795
946
  }
796
947
 
797
948
  cleanupPartialLoad() {
@@ -955,7 +1106,6 @@ export class DynamicGltfLoader {
955
1106
  }
956
1107
 
957
1108
  createVisibilityMaterial(material) {
958
- // Применяем шейдер напрямую к оригинальному материалу
959
1109
  material.onBeforeCompile = (shader) => {
960
1110
  shader.vertexShader = shader.vertexShader.replace(
961
1111
  "#include <common>",
@@ -990,8 +1140,6 @@ export class DynamicGltfLoader {
990
1140
  `
991
1141
  );
992
1142
  };
993
-
994
- // Принудительная перекомпиляция материала
995
1143
  material.needsUpdate = true;
996
1144
 
997
1145
  return material;
@@ -1026,7 +1174,6 @@ export class DynamicGltfLoader {
1026
1174
  });
1027
1175
  this.nodes.clear();
1028
1176
 
1029
- // Clear all loaded meshes
1030
1177
  this.loadedMeshes.forEach((mesh) => {
1031
1178
  if (mesh.geometry) mesh.geometry.dispose();
1032
1179
  if (mesh.material) {
@@ -1162,7 +1309,7 @@ export class DynamicGltfLoader {
1162
1309
  }
1163
1310
  this.handleToObjects.get(fullHandle).add(object);
1164
1311
 
1165
- object.userData.structureId = object.userData.handle.split("_")[0];
1312
+ object.userData.structureId = object.userData.handle.split(STRUCTURE_ID_SEPARATOR)[0];
1166
1313
  }
1167
1314
 
1168
1315
  getObjectsByHandle(handle) {
@@ -1237,11 +1384,32 @@ export class DynamicGltfLoader {
1237
1384
  this.originalObjects.add(object);
1238
1385
  }
1239
1386
 
1240
- optimizeScene() {
1387
+ yieldToUI() {
1388
+ return new Promise((resolve) => {
1389
+ requestAnimationFrame(() => {
1390
+ setTimeout(resolve, 0);
1391
+ });
1392
+ });
1393
+ }
1394
+
1395
+ async optimizeScene() {
1396
+ console.log("Starting scene optimization...");
1397
+ this.dispatchEvent("optimizationprogress", {
1398
+ phase: "start",
1399
+ progress: 0,
1400
+ message: "Starting optimization...",
1401
+ });
1402
+
1241
1403
  this.originalObjects.clear();
1242
1404
  this.originalObjectsToSelection.clear();
1243
1405
  const structureGroups = new Map();
1244
1406
 
1407
+ this.dispatchEvent("optimizationprogress", {
1408
+ phase: "collecting",
1409
+ progress: 5,
1410
+ message: "Collecting scene objects...",
1411
+ });
1412
+
1245
1413
  this.scene.traverse((object) => {
1246
1414
  if (object.userData.structureId) {
1247
1415
  const structureId = object.userData.structureId;
@@ -1274,32 +1442,78 @@ export class DynamicGltfLoader {
1274
1442
  }
1275
1443
  });
1276
1444
 
1445
+ let processedGroups = 0;
1446
+ const totalGroups = structureGroups.size;
1447
+
1448
+ this.dispatchEvent("optimizationprogress", {
1449
+ phase: "merging",
1450
+ progress: 10,
1451
+ message: `Merging ${totalGroups} structure groups...`,
1452
+ current: 0,
1453
+ total: totalGroups,
1454
+ });
1455
+
1277
1456
  for (const group of structureGroups.values()) {
1278
1457
  group.mapMeshes.clear();
1279
1458
  group.mapLines.clear();
1280
1459
  group.mapLineSegments.clear();
1281
1460
  group.mapPoints.clear();
1282
1461
 
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);
1462
+ await this.mergeMeshGroups(group.meshes, group.rootGroup);
1463
+ await this.yieldToUI();
1464
+
1465
+ await this.mergeLineGroups(group.lines, group.rootGroup);
1466
+ await this.yieldToUI();
1467
+
1468
+ await this.mergeLineSegmentGroups(group.lineSegments, group.rootGroup);
1469
+ await this.yieldToUI();
1470
+
1471
+ await this.mergePointsGroups(group.points, group.rootGroup);
1472
+
1473
+ processedGroups++;
1474
+ const progress = 10 + Math.round((processedGroups / totalGroups) * 80);
1475
+
1476
+ this.dispatchEvent("optimizationprogress", {
1477
+ phase: "merging",
1478
+ progress,
1479
+ message: `Processing structure ${processedGroups}/${totalGroups}...`,
1480
+ current: processedGroups,
1481
+ total: totalGroups,
1482
+ });
1483
+
1484
+ console.log(`Optimization progress: ${processedGroups}/${totalGroups} structure groups processed (${progress}%)`);
1485
+
1486
+ await this.yieldToUI();
1287
1487
  }
1288
1488
 
1489
+ this.dispatchEvent("optimizationprogress", {
1490
+ phase: "finalizing",
1491
+ progress: 95,
1492
+ message: "Finalizing optimization...",
1493
+ });
1494
+
1289
1495
  this.originalObjects.forEach((obj) => {
1290
1496
  obj.visible = false;
1291
1497
  if (!(obj instanceof Points) && !obj.userData.isEdge) {
1292
1498
  this.originalObjectsToSelection.add(obj);
1293
1499
  }
1294
1500
  });
1501
+
1295
1502
  this.initializeObjectVisibility();
1296
1503
 
1297
1504
  console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
1298
1505
 
1506
+ this.dispatchEvent("optimizationprogress", {
1507
+ phase: "complete",
1508
+ progress: 100,
1509
+ message: `Optimization complete! ${this.maxObjectId} objects processed.`,
1510
+ });
1511
+
1299
1512
  this.dispatchEvent("update");
1300
1513
  }
1301
1514
 
1302
- mergeMeshGroups(materialGroups, rootGroup) {
1515
+ async mergeMeshGroups(materialGroups, rootGroup) {
1516
+ let processedGroups = 0;
1303
1517
  for (const group of materialGroups) {
1304
1518
  if (!group.material) {
1305
1519
  console.warn("Skipping mesh group with null material");
@@ -1315,8 +1529,6 @@ export class DynamicGltfLoader {
1315
1529
 
1316
1530
  for (const mesh of group.objects) {
1317
1531
  const geometry = mesh.geometry.clone();
1318
- mesh.updateWorldMatrix(true, false);
1319
- geometry.applyMatrix4(mesh.matrixWorld);
1320
1532
 
1321
1533
  const handle = mesh.userData.handle;
1322
1534
  if (!this.objectIdToIndex.has(handle)) {
@@ -1369,6 +1581,7 @@ export class DynamicGltfLoader {
1369
1581
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
1370
1582
 
1371
1583
  const mergedMesh = new Mesh(mergedGeometry, visibilityMaterial);
1584
+ mergedMesh.userData.isOptimized = true;
1372
1585
  rootGroup.add(mergedMesh);
1373
1586
 
1374
1587
  this.mergedMesh.add(mergedMesh);
@@ -1399,6 +1612,11 @@ export class DynamicGltfLoader {
1399
1612
  this.handleToOptimizedObjects.set(handle, mergedObjects);
1400
1613
  }
1401
1614
  });
1615
+
1616
+ processedGroups++;
1617
+ if (processedGroups % 5 === 0) {
1618
+ await this.yieldToUI();
1619
+ }
1402
1620
  } catch (error) {
1403
1621
  console.error("Failed to merge meshes for material:", error);
1404
1622
  group.objects.forEach((mesh) => {
@@ -1408,7 +1626,8 @@ export class DynamicGltfLoader {
1408
1626
  }
1409
1627
  }
1410
1628
 
1411
- mergeLineGroups(materialGroups, rootGroup) {
1629
+ async mergeLineGroups(materialGroups, rootGroup) {
1630
+ let processedGroups = 0;
1412
1631
  for (const group of materialGroups) {
1413
1632
  if (group.objects.length === 0) continue;
1414
1633
 
@@ -1433,7 +1652,9 @@ export class DynamicGltfLoader {
1433
1652
  const indices = [];
1434
1653
  let vertexOffset = 0;
1435
1654
 
1655
+ let isEdge = false;
1436
1656
  group.objects.forEach((line) => {
1657
+ isEdge = line.userData.isEdge;
1437
1658
  const geometry = line.geometry;
1438
1659
  const positionAttr = geometry.attributes.position;
1439
1660
  const vertexCount = positionAttr.count;
@@ -1442,21 +1663,16 @@ export class DynamicGltfLoader {
1442
1663
  if (!this.objectIdToIndex.has(handle)) {
1443
1664
  this.objectIdToIndex.set(handle, this.maxObjectId++);
1444
1665
  }
1445
- // const objectId = this.objectIdToIndex.get(handle);
1446
1666
 
1447
1667
  objectMapping.set(line, {
1448
1668
  startVertexIndex: currentVertexOffset,
1449
1669
  vertexCount,
1450
1670
  });
1451
1671
  currentVertexOffset += vertexCount;
1452
-
1453
- line.updateWorldMatrix(true, false);
1454
- const matrix = line.matrixWorld;
1455
1672
  const vector = new Vector3();
1456
1673
 
1457
1674
  for (let i = 0; i < vertexCount; i++) {
1458
1675
  vector.fromBufferAttribute(positionAttr, i);
1459
- vector.applyMatrix4(matrix);
1460
1676
  positions[posOffset++] = vector.x;
1461
1677
  positions[posOffset++] = vector.y;
1462
1678
  positions[posOffset++] = vector.z;
@@ -1497,6 +1713,9 @@ export class DynamicGltfLoader {
1497
1713
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
1498
1714
 
1499
1715
  const mergedLine = new LineSegments(geometry, visibilityMaterial);
1716
+ mergedLine.userData.isEdge = isEdge;
1717
+ mergedLine.userData.isOptimized = true;
1718
+
1500
1719
  const mergedObjects = [mergedLine];
1501
1720
  if (this.useVAO) {
1502
1721
  this.createVAO(mergedLine);
@@ -1522,10 +1741,16 @@ export class DynamicGltfLoader {
1522
1741
  this.handleToOptimizedObjects.set(handle, mergedObjects);
1523
1742
  }
1524
1743
  });
1744
+
1745
+ processedGroups++;
1746
+ if (processedGroups % 5 === 0) {
1747
+ await this.yieldToUI();
1748
+ }
1525
1749
  }
1526
1750
  }
1527
1751
 
1528
- mergeLineSegmentGroups(materialGroups, rootGroup) {
1752
+ async mergeLineSegmentGroups(materialGroups, rootGroup) {
1753
+ let processedGroups = 0;
1529
1754
  for (const group of materialGroups) {
1530
1755
  if (!group.material) {
1531
1756
  console.warn("Skipping line segment group with null material");
@@ -1538,11 +1763,11 @@ export class DynamicGltfLoader {
1538
1763
  const handles = new Set();
1539
1764
  const objectMapping = new Map();
1540
1765
  let currentVertexOffset = 0;
1766
+ let isEdge = false;
1541
1767
 
1542
1768
  for (const line of group.objects) {
1769
+ isEdge = line.userData.isEdge;
1543
1770
  const geometry = line.geometry.clone();
1544
- line.updateWorldMatrix(true, false);
1545
- geometry.applyMatrix4(line.matrixWorld);
1546
1771
 
1547
1772
  const handle = line.userData.handle;
1548
1773
  if (!this.objectIdToIndex.has(handle)) {
@@ -1585,6 +1810,8 @@ export class DynamicGltfLoader {
1585
1810
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
1586
1811
 
1587
1812
  const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
1813
+ mergedLine.userData.isEdge = isEdge;
1814
+ mergedLine.userData.isOptimized = true;
1588
1815
 
1589
1816
  if (this.useVAO) {
1590
1817
  this.createVAO(mergedLine);
@@ -1618,6 +1845,11 @@ export class DynamicGltfLoader {
1618
1845
  this.handleToOptimizedObjects.set(handle, mergedObjects);
1619
1846
  }
1620
1847
  });
1848
+
1849
+ processedGroups++;
1850
+ if (processedGroups % 5 === 0) {
1851
+ await this.yieldToUI();
1852
+ }
1621
1853
  } catch (error) {
1622
1854
  console.warn("Failed to merge line segments for material:", error);
1623
1855
  group.objects.forEach((line) => {
@@ -1627,7 +1859,8 @@ export class DynamicGltfLoader {
1627
1859
  }
1628
1860
  }
1629
1861
 
1630
- mergePointsGroups(materialGroups, rootGroup) {
1862
+ async mergePointsGroups(materialGroups, rootGroup) {
1863
+ let processedGroups = 0;
1631
1864
  for (const group of materialGroups) {
1632
1865
  if (!group.material) {
1633
1866
  console.warn("Skipping points group with null material");
@@ -1641,8 +1874,6 @@ export class DynamicGltfLoader {
1641
1874
 
1642
1875
  for (const points of group.objects) {
1643
1876
  const geometry = points.geometry.clone();
1644
- points.updateWorldMatrix(true, false);
1645
- geometry.applyMatrix4(points.matrixWorld);
1646
1877
  geometries.push(geometry);
1647
1878
  optimizedObjects.push(points);
1648
1879
  handles.add(points.userData.handle);
@@ -1653,6 +1884,7 @@ export class DynamicGltfLoader {
1653
1884
  if (geometries.length > 0) {
1654
1885
  const mergedGeometry = mergeGeometries(geometries, false);
1655
1886
  const mergedPoints = new Points(mergedGeometry, group.material);
1887
+ mergedPoints.userData.isOptimized = true;
1656
1888
 
1657
1889
  if (this.useVAO) {
1658
1890
  this.createVAO(mergedPoints);
@@ -1678,6 +1910,11 @@ export class DynamicGltfLoader {
1678
1910
  this.handleToOptimizedObjects.set(handle, mergedObjects);
1679
1911
  }
1680
1912
  });
1913
+
1914
+ processedGroups++;
1915
+ if (processedGroups % 5 === 0) {
1916
+ await this.yieldToUI();
1917
+ }
1681
1918
  } catch (error) {
1682
1919
  console.warn("Failed to merge points for material:", error);
1683
1920
  group.objects.forEach((points) => {
@@ -1700,7 +1937,6 @@ export class DynamicGltfLoader {
1700
1937
 
1701
1938
  lineSegmentsArray.forEach((segment) => {
1702
1939
  const clonedGeometry = segment.geometry.clone();
1703
- segment.updateWorldMatrix(true, false);
1704
1940
  clonedGeometry.applyMatrix4(segment.matrixWorld);
1705
1941
 
1706
1942
  if (hasNormals && !clonedGeometry.attributes.normal) {
@@ -1741,6 +1977,7 @@ export class DynamicGltfLoader {
1741
1977
 
1742
1978
  const mergedLine = new LineSegments(finalGeometry, material);
1743
1979
  mergedLine.userData.structureId = structureId;
1980
+ mergedLine.userData.isOptimized = true;
1744
1981
  rootGroup.add(mergedLine);
1745
1982
  this.mergedLineSegments.add(mergedLine);
1746
1983