@inweb/viewer-three 27.2.4 → 27.3.1

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 (159) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +1 -1
  3. package/dist/extensions/components/AxesHelperComponent.js +2 -2
  4. package/dist/extensions/components/AxesHelperComponent.js.map +1 -1
  5. package/dist/extensions/components/AxesHelperComponent.min.js +2 -2
  6. package/dist/extensions/components/AxesHelperComponent.module.js +2 -2
  7. package/dist/extensions/components/AxesHelperComponent.module.js.map +1 -1
  8. package/dist/extensions/components/ExtentsHelperComponent.js +2 -2
  9. package/dist/extensions/components/ExtentsHelperComponent.js.map +1 -1
  10. package/dist/extensions/components/ExtentsHelperComponent.min.js +2 -2
  11. package/dist/extensions/components/ExtentsHelperComponent.module.js +2 -2
  12. package/dist/extensions/components/ExtentsHelperComponent.module.js.map +1 -1
  13. package/dist/extensions/components/GridHelperComponent.js +2 -2
  14. package/dist/extensions/components/GridHelperComponent.js.map +1 -1
  15. package/dist/extensions/components/GridHelperComponent.min.js +2 -2
  16. package/dist/extensions/components/GridHelperComponent.module.js +2 -2
  17. package/dist/extensions/components/GridHelperComponent.module.js.map +1 -1
  18. package/dist/extensions/components/InfoPanelComponent.js +2 -2
  19. package/dist/extensions/components/InfoPanelComponent.js.map +1 -1
  20. package/dist/extensions/components/InfoPanelComponent.min.js +2 -2
  21. package/dist/extensions/components/InfoPanelComponent.module.js +2 -2
  22. package/dist/extensions/components/InfoPanelComponent.module.js.map +1 -1
  23. package/dist/extensions/components/LightHelperComponent.js +2 -2
  24. package/dist/extensions/components/LightHelperComponent.js.map +1 -1
  25. package/dist/extensions/components/LightHelperComponent.min.js +2 -2
  26. package/dist/extensions/components/LightHelperComponent.module.js +2 -2
  27. package/dist/extensions/components/LightHelperComponent.module.js.map +1 -1
  28. package/dist/extensions/components/RoomEnvironmentComponent.js +2 -2
  29. package/dist/extensions/components/RoomEnvironmentComponent.js.map +1 -1
  30. package/dist/extensions/components/RoomEnvironmentComponent.min.js +2 -2
  31. package/dist/extensions/components/RoomEnvironmentComponent.module.js +2 -2
  32. package/dist/extensions/components/RoomEnvironmentComponent.module.js.map +1 -1
  33. package/dist/extensions/components/StatsPanelComponent.js +2 -2
  34. package/dist/extensions/components/StatsPanelComponent.js.map +1 -1
  35. package/dist/extensions/components/StatsPanelComponent.min.js +2 -2
  36. package/dist/extensions/components/StatsPanelComponent.module.js +2 -2
  37. package/dist/extensions/components/StatsPanelComponent.module.js.map +1 -1
  38. package/dist/extensions/loaders/GLTFCloudLoader.js +2 -2
  39. package/dist/extensions/loaders/GLTFCloudLoader.js.map +1 -1
  40. package/dist/extensions/loaders/GLTFCloudLoader.min.js +2 -2
  41. package/dist/extensions/loaders/GLTFCloudLoader.module.js +2 -2
  42. package/dist/extensions/loaders/GLTFCloudLoader.module.js.map +1 -1
  43. package/dist/extensions/loaders/GLTFFileLoader.js +2 -2
  44. package/dist/extensions/loaders/GLTFFileLoader.js.map +1 -1
  45. package/dist/extensions/loaders/GLTFFileLoader.min.js +2 -2
  46. package/dist/extensions/loaders/GLTFFileLoader.module.js +2 -2
  47. package/dist/extensions/loaders/GLTFFileLoader.module.js.map +1 -1
  48. package/dist/extensions/loaders/IFCXLoader.js +2 -2
  49. package/dist/extensions/loaders/IFCXLoader.js.map +1 -1
  50. package/dist/extensions/loaders/IFCXLoader.min.js +2 -2
  51. package/dist/extensions/loaders/IFCXLoader.module.js +2 -2
  52. package/dist/extensions/loaders/IFCXLoader.module.js.map +1 -1
  53. package/dist/extensions/loaders/PotreeLoader.js +2 -2
  54. package/dist/extensions/loaders/PotreeLoader.js.map +1 -1
  55. package/dist/extensions/loaders/PotreeLoader.min.js +2 -2
  56. package/dist/extensions/loaders/PotreeLoader.module.js +2 -2
  57. package/dist/extensions/loaders/PotreeLoader.module.js.map +1 -1
  58. package/dist/viewer-three.js +755 -362
  59. package/dist/viewer-three.js.map +1 -1
  60. package/dist/viewer-three.min.js +5 -5
  61. package/dist/viewer-three.module.js +756 -363
  62. package/dist/viewer-three.module.js.map +1 -1
  63. package/extensions/components/AxesHelperComponent.ts +2 -2
  64. package/extensions/components/ExtentsHelperComponent.ts +2 -2
  65. package/extensions/components/GridHelperComponent.ts +2 -2
  66. package/extensions/components/InfoPanelComponent.ts +2 -2
  67. package/extensions/components/LightHelperComponent.ts +2 -2
  68. package/extensions/components/RoomEnvironmentComponent.ts +2 -2
  69. package/extensions/components/StatsPanelComponent.ts +2 -2
  70. package/extensions/loaders/GLTFCloudLoader.ts +2 -2
  71. package/extensions/loaders/GLTFFileLoader.ts +2 -2
  72. package/extensions/loaders/IFCX/IFCXCloudLoader.ts +2 -2
  73. package/extensions/loaders/IFCX/IFCXFileLoader.ts +2 -2
  74. package/extensions/loaders/IFCX/IFCXLoader.ts +2 -2
  75. package/extensions/loaders/IFCX/index.ts +2 -2
  76. package/extensions/loaders/IFCX/render.js +2 -2
  77. package/extensions/loaders/Potree/PotreeFileLoader.ts +2 -2
  78. package/extensions/loaders/Potree/PotreeModelImpl.ts +2 -2
  79. package/extensions/loaders/Potree/index.ts +2 -2
  80. package/lib/Viewer/controls/WalkControls.d.ts +8 -0
  81. package/lib/Viewer/draggers/CuttingPlaneDragger.d.ts +23 -5
  82. package/lib/Viewer/helpers/PlaneHelper2.d.ts +8 -4
  83. package/lib/Viewer/measurement/Snapper.d.ts +1 -1
  84. package/package.json +5 -5
  85. package/src/Viewer/Viewer.ts +2 -2
  86. package/src/Viewer/commands/ApplyModelTransform.ts +2 -2
  87. package/src/Viewer/commands/ClearMarkup.ts +2 -2
  88. package/src/Viewer/commands/ClearSelected.ts +2 -2
  89. package/src/Viewer/commands/ClearSlices.ts +2 -2
  90. package/src/Viewer/commands/Explode.ts +2 -2
  91. package/src/Viewer/commands/GetDefaultViewPositions.ts +2 -2
  92. package/src/Viewer/commands/GetModels.ts +2 -2
  93. package/src/Viewer/commands/GetSelected.ts +2 -2
  94. package/src/Viewer/commands/GetSelected2.ts +2 -2
  95. package/src/Viewer/commands/GetSnapshot.ts +2 -2
  96. package/src/Viewer/commands/HideSelected.ts +2 -2
  97. package/src/Viewer/commands/IsolateSelected.ts +2 -2
  98. package/src/Viewer/commands/RegenerateAll.ts +2 -2
  99. package/src/Viewer/commands/ResetView.ts +2 -2
  100. package/src/Viewer/commands/SelectModel.ts +2 -2
  101. package/src/Viewer/commands/SetActiveDragger.ts +2 -2
  102. package/src/Viewer/commands/SetDefaultViewPosition.ts +2 -2
  103. package/src/Viewer/commands/SetMarkupColor.ts +2 -2
  104. package/src/Viewer/commands/SetSelected.ts +2 -2
  105. package/src/Viewer/commands/SetSelected2.ts +2 -2
  106. package/src/Viewer/commands/ShowAll.ts +2 -2
  107. package/src/Viewer/commands/ZoomTo.ts +2 -2
  108. package/src/Viewer/commands/ZoomToExtents.ts +2 -2
  109. package/src/Viewer/commands/ZoomToObjects.ts +2 -2
  110. package/src/Viewer/commands/ZoomToSelected.ts +2 -2
  111. package/src/Viewer/commands/index.ts +2 -2
  112. package/src/Viewer/components/BackgroundComponent.ts +2 -2
  113. package/src/Viewer/components/CameraComponent.ts +2 -2
  114. package/src/Viewer/components/CanvasRemoveComponent.ts +2 -2
  115. package/src/Viewer/components/CanvasResizeComponent.ts +2 -2
  116. package/src/Viewer/components/ExtentsComponent.ts +2 -2
  117. package/src/Viewer/components/HighlighterComponent.ts +2 -2
  118. package/src/Viewer/components/HighlighterUtils.ts +2 -2
  119. package/src/Viewer/components/InfoComponent.ts +2 -2
  120. package/src/Viewer/components/LightComponent.ts +2 -2
  121. package/src/Viewer/components/RenderLoopComponent.ts +2 -2
  122. package/src/Viewer/components/ResetComponent.ts +2 -2
  123. package/src/Viewer/components/SelectionComponent.ts +3 -3
  124. package/src/Viewer/components/ViewPositionComponent.ts +2 -2
  125. package/src/Viewer/components/WCSHelperComponent.ts +2 -2
  126. package/src/Viewer/components/index.ts +2 -2
  127. package/src/Viewer/controls/FlyControls.ts +2 -2
  128. package/src/Viewer/controls/JoyStickControls.ts +2 -2
  129. package/src/Viewer/controls/WalkControls.ts +52 -6
  130. package/src/Viewer/draggers/CuttingPlaneDragger.ts +193 -33
  131. package/src/Viewer/draggers/CuttingPlaneXAxis.ts +4 -5
  132. package/src/Viewer/draggers/CuttingPlaneYAxis.ts +4 -5
  133. package/src/Viewer/draggers/CuttingPlaneZAxis.ts +4 -5
  134. package/src/Viewer/draggers/FlyDragger.ts +2 -2
  135. package/src/Viewer/draggers/MeasureLineDragger.ts +2 -2
  136. package/src/Viewer/draggers/OrbitDragger.ts +2 -2
  137. package/src/Viewer/draggers/PanDragger.ts +2 -2
  138. package/src/Viewer/draggers/WalkDragger.ts +2 -2
  139. package/src/Viewer/draggers/ZoomDragger.ts +2 -2
  140. package/src/Viewer/draggers/index.ts +4 -2
  141. package/src/Viewer/helpers/PlaneHelper2.ts +32 -19
  142. package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +495 -182
  143. package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +46 -35
  144. package/src/Viewer/loaders/GLTFBinaryParser.ts +2 -2
  145. package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +2 -2
  146. package/src/Viewer/loaders/GLTFFileDynamicLoader.ts +2 -2
  147. package/src/Viewer/loaders/GLTFLoadingManager.ts +2 -2
  148. package/src/Viewer/loaders/JSONStreamParser.ts +2 -2
  149. package/src/Viewer/loaders/RangesLoader.ts +2 -2
  150. package/src/Viewer/loaders/index.ts +2 -2
  151. package/src/Viewer/measurement/Snapper.ts +15 -7
  152. package/src/Viewer/measurement/UnitConverter.ts +2 -2
  153. package/src/Viewer/measurement/UnitFormatter.ts +2 -2
  154. package/src/Viewer/models/IModelImpl.ts +2 -2
  155. package/src/Viewer/models/ModelImpl.ts +15 -12
  156. package/src/Viewer/postprocessing/SSAARenderPass.js +2 -2
  157. package/src/Viewer/scenes/Helpers.ts +2 -2
  158. package/src/index-umd.ts +2 -2
  159. package/src/index.ts +2 -2
@@ -1,5 +1,5 @@
1
1
  ///////////////////////////////////////////////////////////////////////////////
2
- // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
2
+ // Copyright (C) 2002-2026, Open Design Alliance (the "Alliance").
3
3
  // All rights reserved.
4
4
  //
5
5
  // This software and its documentation and related materials are owned by
@@ -14,7 +14,7 @@
14
14
  //
15
15
  // This application incorporates Open Design Alliance software pursuant to a
16
16
  // license agreement with Open Design Alliance.
17
- // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
17
+ // Open Design Alliance Copyright (C) 2002-2026 by Open Design Alliance.
18
18
  // All rights reserved.
19
19
  //
20
20
  // By use of this software, its documentation or related materials, you
@@ -33644,41 +33644,6 @@ void main() {
33644
33644
  }
33645
33645
  }
33646
33646
 
33647
- class PlaneHelper2 extends Line$1 {
33648
- constructor(size = 1, color = 0xc0c0c0) {
33649
- const positions = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0];
33650
- const geometry = new BufferGeometry();
33651
- geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
33652
- geometry.computeBoundingSphere();
33653
- super(geometry, new LineBasicMaterial({ color, toneMapped: false }));
33654
- this.type = "PlaneHelper2";
33655
- this.size = size;
33656
- const positions2 = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0];
33657
- const geometry2 = new BufferGeometry();
33658
- geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3));
33659
- geometry2.computeBoundingSphere();
33660
- this.helper = new Mesh(geometry2, new MeshBasicMaterial({
33661
- color,
33662
- opacity: 0.2,
33663
- transparent: true,
33664
- depthWrite: false,
33665
- toneMapped: false,
33666
- side: DoubleSide,
33667
- }));
33668
- this.add(this.helper);
33669
- }
33670
- dispose() {
33671
- this.geometry.dispose();
33672
- this.material.dispose();
33673
- this.helper.geometry.dispose();
33674
- this.helper.material.dispose();
33675
- }
33676
- updateMatrixWorld(force) {
33677
- this.scale.set(0.5 * this.size, 0.5 * this.size, 1);
33678
- super.updateMatrixWorld(force);
33679
- }
33680
- }
33681
-
33682
33647
  const _changeEvent = { type: "change" };
33683
33648
  const _startEvent = { type: "start" };
33684
33649
  const _endEvent = { type: "end" };
@@ -34337,6 +34302,166 @@ void main() {
34337
34302
  }
34338
34303
  }
34339
34304
 
34305
+ class PlaneHelper2 extends Object3D {
34306
+ constructor(size = 1, color = 0xf0f0f0, opacity = 0.15) {
34307
+ super();
34308
+ this.type = "PlaneHelper2";
34309
+ this.size = size;
34310
+ const positions = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0];
34311
+ const geometry = new BufferGeometry();
34312
+ geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
34313
+ geometry.computeBoundingSphere();
34314
+ this.outline = new Line$1(geometry, new LineBasicMaterial({ color, toneMapped: false }));
34315
+ const positions2 = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0];
34316
+ const geometry2 = new BufferGeometry();
34317
+ geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3));
34318
+ geometry2.computeBoundingSphere();
34319
+ this.mesh = new Mesh(geometry2, new MeshBasicMaterial({
34320
+ color,
34321
+ opacity,
34322
+ transparent: true,
34323
+ depthWrite: false,
34324
+ toneMapped: false,
34325
+ side: DoubleSide,
34326
+ }));
34327
+ this.add(this.outline);
34328
+ this.add(this.mesh);
34329
+ }
34330
+ dispose() {
34331
+ this.outline.geometry.dispose();
34332
+ this.outline.material.dispose();
34333
+ this.mesh.geometry.dispose();
34334
+ this.mesh.material.dispose();
34335
+ }
34336
+ updateMatrixWorld(force) {
34337
+ this.scale.set(0.5 * this.size, 0.5 * this.size, 1);
34338
+ super.updateMatrixWorld(force);
34339
+ }
34340
+ getLineMaterial() {
34341
+ return this.outline.material;
34342
+ }
34343
+ getMeshMaterial() {
34344
+ return this.mesh.material;
34345
+ }
34346
+ }
34347
+
34348
+ const DESKTOP_SNAP_DISTANCE = 10;
34349
+ const MOBILE_SNAP_DISTANCE = 50;
34350
+ const _vertex = new Vector3();
34351
+ const _start$1 = new Vector3();
34352
+ const _end$1 = new Vector3();
34353
+ const _line = new Line3();
34354
+ const _center = new Vector3();
34355
+ const _projection = new Vector3();
34356
+ class Snapper {
34357
+ constructor(camera, renderer, canvas) {
34358
+ this.camera = camera;
34359
+ this.renderer = renderer;
34360
+ this.canvas = canvas;
34361
+ this.threshold = 0.0001;
34362
+ this.raycaster = new Raycaster();
34363
+ this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
34364
+ this.edgesCache = new WeakMap();
34365
+ }
34366
+ isMobile() {
34367
+ if (typeof navigator === "undefined")
34368
+ return false;
34369
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
34370
+ }
34371
+ getMousePosition(event, target) {
34372
+ return target.set(event.clientX, event.clientY);
34373
+ }
34374
+ getPointerIntersects(mouse, objects, recursive = false, clip = true) {
34375
+ const rect = this.canvas.getBoundingClientRect();
34376
+ const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
34377
+ const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
34378
+ const coords = new Vector2(x, y);
34379
+ this.raycaster.setFromCamera(coords, this.camera);
34380
+ this.raycaster.params = {
34381
+ Mesh: {},
34382
+ Line: { threshold: this.threshold },
34383
+ Line2: { threshold: this.threshold },
34384
+ LOD: {},
34385
+ Points: { threshold: this.threshold },
34386
+ Sprite: {},
34387
+ };
34388
+ let intersects = this.raycaster.intersectObjects(objects, recursive);
34389
+ if (clip) {
34390
+ const clippingPlanes = this.renderer.clippingPlanes || [];
34391
+ clippingPlanes.forEach((plane) => {
34392
+ intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
34393
+ });
34394
+ }
34395
+ return intersects;
34396
+ }
34397
+ getDetectRadius(point) {
34398
+ const camera = this.camera;
34399
+ if (camera.isOrthographicCamera) {
34400
+ const fieldHeight = (camera.top - camera.bottom) / camera.zoom;
34401
+ const canvasHeight = this.canvas.height;
34402
+ const worldUnitsPerPixel = fieldHeight / canvasHeight;
34403
+ return this.detectRadiusInPixels * worldUnitsPerPixel;
34404
+ }
34405
+ if (camera.isPerspectiveCamera) {
34406
+ const distance = camera.position.distanceTo(point);
34407
+ const fieldHeight = 2 * Math.tan(MathUtils.degToRad(camera.fov * 0.5)) * distance;
34408
+ const canvasHeight = this.canvas.height;
34409
+ const worldUnitsPerPixel = fieldHeight / canvasHeight;
34410
+ return this.detectRadiusInPixels * worldUnitsPerPixel;
34411
+ }
34412
+ return 0.1;
34413
+ }
34414
+ getSnapPoint(mouse, objects) {
34415
+ const intersections = this.getPointerIntersects(mouse, objects);
34416
+ if (intersections.length === 0)
34417
+ return undefined;
34418
+ const object = intersections[0].object;
34419
+ const intersectionPoint = intersections[0].point;
34420
+ const localPoint = object.worldToLocal(intersectionPoint.clone());
34421
+ let snapPoint;
34422
+ let snapDistance = this.getDetectRadius(intersectionPoint);
34423
+ const geometry = object.geometry;
34424
+ const positions = geometry.attributes.position.array;
34425
+ for (let i = 0; i < positions.length; i += 3) {
34426
+ _vertex.set(positions[i], positions[i + 1], positions[i + 2]);
34427
+ const distance = _vertex.distanceTo(localPoint);
34428
+ if (distance < snapDistance) {
34429
+ snapDistance = distance;
34430
+ snapPoint = _vertex.clone();
34431
+ }
34432
+ }
34433
+ if (snapPoint)
34434
+ return object.localToWorld(snapPoint);
34435
+ let edges = this.edgesCache.get(geometry);
34436
+ if (!edges) {
34437
+ edges = new EdgesGeometry(geometry);
34438
+ this.edgesCache.set(geometry, edges);
34439
+ }
34440
+ const edgePositions = edges.attributes.position.array;
34441
+ for (let i = 0; i < edgePositions.length; i += 6) {
34442
+ _start$1.set(edgePositions[i], edgePositions[i + 1], edgePositions[i + 2]);
34443
+ _end$1.set(edgePositions[i + 3], edgePositions[i + 4], edgePositions[i + 5]);
34444
+ _line.set(_start$1, _end$1);
34445
+ _line.getCenter(_center);
34446
+ const centerDistance = _center.distanceTo(localPoint);
34447
+ if (centerDistance < snapDistance) {
34448
+ snapDistance = centerDistance;
34449
+ snapPoint = _center.clone();
34450
+ continue;
34451
+ }
34452
+ _line.closestPointToPoint(localPoint, true, _projection);
34453
+ const lineDistance = _projection.distanceTo(localPoint);
34454
+ if (lineDistance < snapDistance) {
34455
+ snapDistance = lineDistance;
34456
+ snapPoint = _projection.clone();
34457
+ }
34458
+ }
34459
+ if (snapPoint)
34460
+ return object.localToWorld(snapPoint);
34461
+ return intersectionPoint.clone();
34462
+ }
34463
+ }
34464
+
34340
34465
  class OrbitDragger {
34341
34466
  constructor(viewer) {
34342
34467
  this.updateControls = () => {
@@ -34429,12 +34554,18 @@ void main() {
34429
34554
  }
34430
34555
 
34431
34556
  class CuttingPlaneDragger extends OrbitDragger {
34432
- constructor(viewer, normal) {
34557
+ constructor(viewer) {
34433
34558
  super(viewer);
34559
+ this.helpers = [];
34560
+ this.activeHelper = null;
34434
34561
  this.transformChange = () => {
34435
- this.plane.normal.copy(new Vector3(0, 0, -1)).applyQuaternion(this.planeCenter.quaternion);
34436
- this.plane.constant = -this.planeCenter.position.dot(this.plane.normal);
34562
+ if (!this.activeHelper)
34563
+ return;
34564
+ const plane = this.activeHelper.plane;
34565
+ plane.normal.copy(new Vector3(0, 0, -1)).applyQuaternion(this.activeHelper.quaternion);
34566
+ plane.constant = -this.activeHelper.position.dot(plane.normal);
34437
34567
  this.viewer.update();
34568
+ this.changed = true;
34438
34569
  };
34439
34570
  this.translateDrag = (event) => {
34440
34571
  this.orbit.enabled = !event.value;
@@ -34445,45 +34576,83 @@ void main() {
34445
34576
  this.translate.enabled = !event.value;
34446
34577
  };
34447
34578
  this.updatePlaneSize = () => {
34448
- this.planeHelper.size = this.viewer.extents.getSize(new Vector3()).length() || 1;
34579
+ const extentsSize = this.viewer.extents.getSize(new Vector3()).length() || 1;
34580
+ this.helpers.forEach((planeHelper) => (planeHelper.size = extentsSize));
34449
34581
  this.viewer.update();
34450
34582
  };
34451
34583
  this.updateTransformCamera = () => {
34452
34584
  this.translate.camera = this.viewer.camera;
34453
34585
  this.rotate.camera = this.viewer.camera;
34586
+ this.snapper.camera = this.viewer.camera;
34587
+ };
34588
+ this.clearHelpers = () => {
34589
+ this.setActiveHelper();
34590
+ this.helpers.forEach((helper) => {
34591
+ helper.removeFromParent();
34592
+ helper.dispose();
34593
+ });
34594
+ this.helpers = [];
34595
+ this.viewer.update();
34454
34596
  };
34455
34597
  this.onKeyDown = (event) => {
34456
34598
  if (event.key === "Shift")
34457
34599
  this.rotate.setRotationSnap(Math.PI / 4);
34600
+ if (event.key === "Delete" || event.key === "Backspace")
34601
+ this.deleteActivePlane();
34602
+ if (event.key === "Escape" && (this.translate.dragging || this.rotate.dragging))
34603
+ this.reset();
34458
34604
  };
34459
34605
  this.onKeyUp = (event) => {
34460
34606
  if (event.key === "Shift")
34461
34607
  this.rotate.setRotationSnap(null);
34462
34608
  };
34609
+ this.onPointerDown = (event) => {
34610
+ if (event.button !== 0 || !event.isPrimary)
34611
+ return;
34612
+ this.snapper.getMousePosition(event, this.downPosition);
34613
+ this.saveState();
34614
+ };
34615
+ this.onPointerUp = (event) => {
34616
+ if (event.button !== 0)
34617
+ return;
34618
+ const upPosition = this.snapper.getMousePosition(event, new Vector2());
34619
+ if (upPosition.distanceTo(this.downPosition) !== 0)
34620
+ return;
34621
+ const intersects = this.snapper.getPointerIntersects(upPosition, this.helpers, true, false);
34622
+ if (intersects.length === 0)
34623
+ return;
34624
+ this.setActiveHelper(intersects[0].object.parent);
34625
+ };
34626
+ this.onPointerCancel = (event) => {
34627
+ this.viewer.canvas.dispatchEvent(new PointerEvent("pointerup", event));
34628
+ };
34463
34629
  this.onDoubleClick = (event) => {
34464
- event.stopPropagation();
34465
- this.planeCenter.rotateOnAxis(new Vector3(0, 1, 0), Math.PI);
34630
+ if (!this.activeHelper)
34631
+ return;
34632
+ const mousePosition = this.snapper.getMousePosition(event, new Vector2());
34633
+ const intersects = this.snapper.getPointerIntersects(mousePosition, [this.activeHelper], true, false);
34634
+ if (intersects.length === 0)
34635
+ return;
34636
+ this.activeHelper.rotateOnAxis(new Vector3(0, 1, 0), Math.PI);
34466
34637
  this.transformChange();
34638
+ event.stopPropagation();
34467
34639
  };
34468
- const extentsSize = viewer.extents.getSize(new Vector3()).length() || 1;
34469
- const extentsCenter = viewer.extents.getCenter(new Vector3());
34470
- const constant = -extentsCenter.dot(normal);
34471
- this.plane = new Plane(normal, constant);
34472
34640
  if (!viewer.renderer.clippingPlanes)
34473
34641
  viewer.renderer.clippingPlanes = [];
34474
- viewer.renderer.clippingPlanes.push(this.plane);
34475
- this.planeCenter = new Object3D();
34476
- this.planeCenter.position.copy(extentsCenter);
34477
- this.planeCenter.quaternion.setFromUnitVectors(new Vector3(0, 0, -1), normal);
34478
- this.viewer.helpers.add(this.planeCenter);
34479
- this.planeHelper = new PlaneHelper2(extentsSize);
34480
- this.planeCenter.add(this.planeHelper);
34642
+ this.clippingPlanes = viewer.renderer.clippingPlanes;
34643
+ this.clippingPlanes.forEach((plane) => this.addHelper(plane));
34644
+ const extentsSize = viewer.extents.getSize(new Vector3()).length() || 1;
34645
+ this.snapper = new Snapper(viewer.camera, viewer.renderer, viewer.canvas);
34646
+ this.snapper.threshold = extentsSize / 10000;
34647
+ this.downPosition = new Vector2();
34648
+ this.position0 = new Vector3();
34649
+ this.quaternion0 = new Quaternion();
34481
34650
  this.translate = new TransformControls(viewer.camera, viewer.canvas);
34651
+ this.translate.setMode("translate");
34482
34652
  this.translate.setSpace("local");
34483
34653
  this.translate.showX = false;
34484
34654
  this.translate.showY = false;
34485
34655
  this.translate.showZ = true;
34486
- this.translate.attach(this.planeCenter);
34487
34656
  this.translate.addEventListener("change", this.transformChange);
34488
34657
  this.translate.addEventListener("dragging-changed", this.translateDrag);
34489
34658
  this.viewer.helpers.add(this.translate.getHelper());
@@ -34493,14 +34662,18 @@ void main() {
34493
34662
  this.rotate.showX = true;
34494
34663
  this.rotate.showY = true;
34495
34664
  this.rotate.showZ = false;
34496
- this.rotate.attach(this.planeCenter);
34497
34665
  this.rotate.addEventListener("change", this.transformChange);
34498
34666
  this.rotate.addEventListener("dragging-changed", this.rotateDrag);
34499
34667
  this.viewer.helpers.add(this.rotate.getHelper());
34668
+ this.setActiveHelper(this.helpers[this.helpers.length - 1]);
34500
34669
  this.viewer.addEventListener("explode", this.updatePlaneSize);
34501
34670
  this.viewer.addEventListener("show", this.updatePlaneSize);
34502
34671
  this.viewer.addEventListener("showall", this.updatePlaneSize);
34503
34672
  this.viewer.addEventListener("changecameramode", this.updateTransformCamera);
34673
+ this.viewer.addEventListener("clearslices", this.clearHelpers);
34674
+ this.viewer.canvas.addEventListener("pointerdown", this.onPointerDown, true);
34675
+ this.viewer.canvas.addEventListener("pointerup", this.onPointerUp, true);
34676
+ this.viewer.canvas.addEventListener("pointercancel", this.onPointerCancel, true);
34504
34677
  this.viewer.canvas.addEventListener("dblclick", this.onDoubleClick, true);
34505
34678
  window.addEventListener("keydown", this.onKeyDown);
34506
34679
  window.addEventListener("keyup", this.onKeyUp);
@@ -34511,6 +34684,10 @@ void main() {
34511
34684
  this.viewer.removeEventListener("show", this.updatePlaneSize);
34512
34685
  this.viewer.removeEventListener("showall", this.updatePlaneSize);
34513
34686
  this.viewer.removeEventListener("changecameramode", this.updateTransformCamera);
34687
+ this.viewer.removeEventListener("clearslices", this.clearHelpers);
34688
+ this.viewer.canvas.removeEventListener("pointerdown", this.onPointerDown, true);
34689
+ this.viewer.canvas.removeEventListener("pointerup", this.onPointerUp, true);
34690
+ this.viewer.canvas.removeEventListener("pointercancel", this.onPointerCancel, true);
34514
34691
  this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
34515
34692
  window.removeEventListener("keydown", this.onKeyDown);
34516
34693
  window.removeEventListener("keyup", this.onKeyUp);
@@ -34524,28 +34701,108 @@ void main() {
34524
34701
  this.rotate.getHelper().removeFromParent();
34525
34702
  this.rotate.detach();
34526
34703
  this.rotate.dispose();
34527
- this.planeHelper.removeFromParent();
34528
- this.planeHelper.dispose();
34529
- this.planeCenter.removeFromParent();
34704
+ this.helpers.forEach((helper) => {
34705
+ helper.removeFromParent();
34706
+ helper.dispose();
34707
+ });
34708
+ this.helpers = [];
34709
+ this.activeHelper = null;
34530
34710
  super.dispose();
34531
34711
  }
34712
+ addHelper(plane) {
34713
+ const extentsSize = this.viewer.extents.getSize(new Vector3()).length() || 1;
34714
+ const extentsCenter = this.viewer.extents.getCenter(new Vector3());
34715
+ const helper = new PlaneHelper2(extentsSize);
34716
+ helper.plane = plane;
34717
+ helper.position.copy(plane.projectPoint(extentsCenter, new Vector3()));
34718
+ helper.quaternion.setFromUnitVectors(new Vector3(0, 0, -1), plane.normal);
34719
+ this.helpers.push(helper);
34720
+ this.viewer.helpers.add(helper);
34721
+ return helper;
34722
+ }
34723
+ setActiveHelper(helper) {
34724
+ if (helper === this.activeHelper)
34725
+ return;
34726
+ if (this.activeHelper) {
34727
+ this.activeHelper.getLineMaterial().color.setHex(0xf0f0f0);
34728
+ this.activeHelper.getMeshMaterial().opacity = 0.15;
34729
+ this.translate.detach();
34730
+ this.rotate.detach();
34731
+ }
34732
+ this.activeHelper = helper;
34733
+ if (this.activeHelper) {
34734
+ this.activeHelper.getLineMaterial().color.setHex(0xd0d0d0);
34735
+ this.activeHelper.getMeshMaterial().opacity = 0.3;
34736
+ this.translate.attach(this.activeHelper);
34737
+ this.rotate.attach(this.activeHelper);
34738
+ }
34739
+ this.viewer.update();
34740
+ }
34741
+ saveState() {
34742
+ if (!this.activeHelper)
34743
+ return;
34744
+ this.position0.copy(this.activeHelper.position);
34745
+ this.quaternion0.copy(this.activeHelper.quaternion);
34746
+ }
34747
+ reset() {
34748
+ if (!this.activeHelper)
34749
+ return;
34750
+ this.translate.dragging = false;
34751
+ this.rotate.dragging = false;
34752
+ this.orbit.state = STATE.NONE;
34753
+ this.activeHelper.position.copy(this.position0);
34754
+ this.activeHelper.quaternion.copy(this.quaternion0);
34755
+ this.transformChange();
34756
+ }
34757
+ addPlane(normal) {
34758
+ const extentsCenter = this.viewer.extents.getCenter(new Vector3());
34759
+ const constant = -extentsCenter.dot(normal);
34760
+ const plane = new Plane(normal, constant);
34761
+ this.clippingPlanes.push(plane);
34762
+ const helper = this.addHelper(plane);
34763
+ this.setActiveHelper(helper);
34764
+ }
34765
+ addPlaneX() {
34766
+ this.addPlane(new Vector3(-1, 0, 0));
34767
+ }
34768
+ addPlaneY() {
34769
+ this.addPlane(new Vector3(0, -1, 0));
34770
+ }
34771
+ addPlaneZ() {
34772
+ this.addPlane(new Vector3(0, 0, -1));
34773
+ }
34774
+ deleteActivePlane() {
34775
+ if (!this.activeHelper)
34776
+ return;
34777
+ const helper = this.activeHelper;
34778
+ const index = this.clippingPlanes.indexOf(helper.plane);
34779
+ if (index !== -1)
34780
+ this.clippingPlanes.splice(index, 1);
34781
+ this.helpers = this.helpers.filter((x) => x !== helper);
34782
+ helper.removeFromParent();
34783
+ helper.dispose();
34784
+ this.setActiveHelper(this.helpers[this.helpers.length - 1]);
34785
+ }
34532
34786
  }
34533
34787
 
34534
34788
  class CuttingPlaneXAxisDragger extends CuttingPlaneDragger {
34535
34789
  constructor(viewer) {
34536
- super(viewer, new Vector3(-1, 0, 0));
34790
+ super(viewer);
34791
+ this.addPlaneX();
34537
34792
  }
34538
34793
  }
34539
34794
 
34540
34795
  class CuttingPlaneYAxisDragger extends CuttingPlaneDragger {
34541
34796
  constructor(viewer) {
34542
- super(viewer, new Vector3(0, -1, 0));
34797
+ super(viewer);
34798
+ this.addPlaneY();
34543
34799
  }
34544
34800
  }
34545
34801
 
34546
34802
  class CuttingPlaneZAxisDragger extends CuttingPlaneDragger {
34547
34803
  constructor(viewer) {
34548
- super(viewer, new Vector3(0, 0, -1));
34804
+ super(viewer);
34805
+ this.addPlaneZ();
34549
34806
  }
34550
34807
  }
34551
34808
 
@@ -34622,120 +34879,6 @@ void main() {
34622
34879
  }
34623
34880
  }
34624
34881
 
34625
- const DESKTOP_SNAP_DISTANCE = 10;
34626
- const MOBILE_SNAP_DISTANCE = 50;
34627
- const _vertex = new Vector3();
34628
- const _start$1 = new Vector3();
34629
- const _end$1 = new Vector3();
34630
- const _line = new Line3();
34631
- const _center = new Vector3();
34632
- const _projection = new Vector3();
34633
- class Snapper {
34634
- constructor(camera, renderer, canvas) {
34635
- this.camera = camera;
34636
- this.renderer = renderer;
34637
- this.canvas = canvas;
34638
- this.threshold = 0.0001;
34639
- this.raycaster = new Raycaster();
34640
- this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
34641
- this.edgesCache = new WeakMap();
34642
- }
34643
- isMobile() {
34644
- if (typeof navigator === "undefined")
34645
- return false;
34646
- return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
34647
- }
34648
- getMousePosition(event, target) {
34649
- return target.set(event.clientX, event.clientY);
34650
- }
34651
- getPointerIntersects(mouse, objects) {
34652
- const rect = this.canvas.getBoundingClientRect();
34653
- const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
34654
- const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
34655
- const coords = new Vector2(x, y);
34656
- this.raycaster.setFromCamera(coords, this.camera);
34657
- this.raycaster.params = {
34658
- Mesh: {},
34659
- Line: { threshold: this.threshold },
34660
- Line2: { threshold: this.threshold },
34661
- LOD: {},
34662
- Points: { threshold: this.threshold },
34663
- Sprite: {},
34664
- };
34665
- let intersects = this.raycaster.intersectObjects(objects, false);
34666
- (this.renderer.clippingPlanes || []).forEach((plane) => {
34667
- intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
34668
- });
34669
- return intersects;
34670
- }
34671
- getDetectRadius(point) {
34672
- const camera = this.camera;
34673
- if (camera.isOrthographicCamera) {
34674
- const fieldHeight = (camera.top - camera.bottom) / camera.zoom;
34675
- const canvasHeight = this.canvas.height;
34676
- const worldUnitsPerPixel = fieldHeight / canvasHeight;
34677
- return this.detectRadiusInPixels * worldUnitsPerPixel;
34678
- }
34679
- if (camera.isPerspectiveCamera) {
34680
- const distance = camera.position.distanceTo(point);
34681
- const fieldHeight = 2 * Math.tan(MathUtils.degToRad(camera.fov * 0.5)) * distance;
34682
- const canvasHeight = this.canvas.height;
34683
- const worldUnitsPerPixel = fieldHeight / canvasHeight;
34684
- return this.detectRadiusInPixels * worldUnitsPerPixel;
34685
- }
34686
- return 0.1;
34687
- }
34688
- getSnapPoint(mouse, objects) {
34689
- const intersections = this.getPointerIntersects(mouse, objects);
34690
- if (intersections.length === 0)
34691
- return undefined;
34692
- const object = intersections[0].object;
34693
- const intersectionPoint = intersections[0].point;
34694
- const localPoint = object.worldToLocal(intersectionPoint.clone());
34695
- let snapPoint;
34696
- let snapDistance = this.getDetectRadius(intersectionPoint);
34697
- const geometry = object.geometry;
34698
- const positions = geometry.attributes.position.array;
34699
- for (let i = 0; i < positions.length; i += 3) {
34700
- _vertex.set(positions[i], positions[i + 1], positions[i + 2]);
34701
- const distance = _vertex.distanceTo(localPoint);
34702
- if (distance < snapDistance) {
34703
- snapDistance = distance;
34704
- snapPoint = _vertex.clone();
34705
- }
34706
- }
34707
- if (snapPoint)
34708
- return object.localToWorld(snapPoint);
34709
- let edges = this.edgesCache.get(geometry);
34710
- if (!edges) {
34711
- edges = new EdgesGeometry(geometry);
34712
- this.edgesCache.set(geometry, edges);
34713
- }
34714
- const edgePositions = edges.attributes.position.array;
34715
- for (let i = 0; i < edgePositions.length; i += 6) {
34716
- _start$1.set(edgePositions[i], edgePositions[i + 1], edgePositions[i + 2]);
34717
- _end$1.set(edgePositions[i + 3], edgePositions[i + 4], edgePositions[i + 5]);
34718
- _line.set(_start$1, _end$1);
34719
- _line.getCenter(_center);
34720
- const centerDistance = _center.distanceTo(localPoint);
34721
- if (centerDistance < snapDistance) {
34722
- snapDistance = centerDistance;
34723
- snapPoint = _center.clone();
34724
- continue;
34725
- }
34726
- _line.closestPointToPoint(localPoint, true, _projection);
34727
- const lineDistance = _projection.distanceTo(localPoint);
34728
- if (lineDistance < snapDistance) {
34729
- snapDistance = lineDistance;
34730
- snapPoint = _projection.clone();
34731
- }
34732
- }
34733
- if (snapPoint)
34734
- return object.localToWorld(snapPoint);
34735
- return intersectionPoint.clone();
34736
- }
34737
- }
34738
-
34739
34882
  const _downPoint = new Vector2();
34740
34883
  class MeasureLineDragger extends OrbitDragger {
34741
34884
  constructor(viewer) {
@@ -35147,6 +35290,12 @@ void main() {
35147
35290
  this.movementSpeed = 0.1;
35148
35291
  this.multiplier = 3;
35149
35292
  this.groundFollowingSkippedFrames = 0;
35293
+ this.GROUND_BOX_HALF_SIZE = 20;
35294
+ this.GROUND_BOX_REFRESH_THRESHOLD = 0.3;
35295
+ this._groundObjectBoxes = new Map();
35296
+ this._activeGroundObjects = [];
35297
+ this._groundBox = new Box3();
35298
+ this._groundBoxCenter = new Vector3();
35150
35299
  this.moveWheel = 0;
35151
35300
  this.mouseDragOn = false;
35152
35301
  this._up = new Vector3();
@@ -35213,11 +35362,20 @@ void main() {
35213
35362
  }
35214
35363
  };
35215
35364
  this.onKeyUp = (event) => {
35216
- if (this.moveKeys.delete(event.code))
35365
+ if (this.moveKeys.delete(event.code)) {
35366
+ if (this.moveKeys.size === 0) {
35367
+ this._rebuildGroundBox(this.object.position);
35368
+ }
35217
35369
  this.update();
35370
+ }
35218
35371
  };
35219
35372
  this.camera = camera;
35220
35373
  this.groundObjects = groundObjects;
35374
+ for (const obj of groundObjects) {
35375
+ this._groundObjectBoxes.set(obj, new Box3().setFromObject(obj));
35376
+ }
35377
+ const pos = this.object.position;
35378
+ this._rebuildGroundBox(pos);
35221
35379
  this.raycaster = new Raycaster();
35222
35380
  this.raycaster.near = 0;
35223
35381
  this.raycaster.far = this.EYE_HEIGHT + this.FAILING_DISTANCE;
@@ -35252,10 +35410,29 @@ void main() {
35252
35410
  window.removeEventListener("keyup", this.onKeyUp);
35253
35411
  super.dispose();
35254
35412
  }
35413
+ _rebuildGroundBox(center) {
35414
+ const h = this.GROUND_BOX_HALF_SIZE;
35415
+ this._groundBoxCenter.copy(center);
35416
+ this._groundBox.set(new Vector3(center.x - h, center.y - h * 4, center.z - h), new Vector3(center.x + h, center.y + h * 4, center.z + h));
35417
+ this._activeGroundObjects = this.groundObjects.filter((obj) => {
35418
+ const objectBox = this._groundObjectBoxes.get(obj);
35419
+ return objectBox !== undefined && this._groundBox.intersectsBox(objectBox);
35420
+ });
35421
+ }
35422
+ _needsGroundBoxRebuild(pos) {
35423
+ if (this._activeGroundObjects.length === 0 && this.groundObjects.length > 0)
35424
+ return true;
35425
+ const threshold = this.GROUND_BOX_HALF_SIZE * this.GROUND_BOX_REFRESH_THRESHOLD;
35426
+ return (Math.abs(pos.x - this._groundBoxCenter.x) > threshold || Math.abs(pos.z - this._groundBoxCenter.z) > threshold);
35427
+ }
35255
35428
  updateGroundFollowing() {
35429
+ const pos = this.object.position;
35430
+ if (this._needsGroundBoxRebuild(pos)) {
35431
+ this._rebuildGroundBox(pos);
35432
+ }
35256
35433
  this._up.copy(this.camera.up).negate();
35257
- this.raycaster.set(this.object.position, this._up);
35258
- const intersects = this.raycaster.intersectObjects(this.groundObjects, false);
35434
+ this.raycaster.set(pos, this._up);
35435
+ const intersects = this.raycaster.intersectObjects(this._activeGroundObjects, false);
35259
35436
  if (intersects.length > 0) {
35260
35437
  const groundY = intersects[0].point.y;
35261
35438
  const targetY = groundY + this.EYE_HEIGHT;
@@ -35807,6 +35984,7 @@ void main() {
35807
35984
  draggers.registerDragger("Orbit", (viewer) => new OrbitDragger(viewer));
35808
35985
  draggers.registerDragger("Zoom", (viewer) => new ZoomDragger(viewer));
35809
35986
  draggers.registerDragger("MeasureLine", (viewer) => new MeasureLineDragger(viewer));
35987
+ draggers.registerDragger("CuttingPlane", (viewer) => new CuttingPlaneDragger(viewer));
35810
35988
  draggers.registerDragger("CuttingPlaneXAxis", (viewer) => new CuttingPlaneXAxisDragger(viewer));
35811
35989
  draggers.registerDragger("CuttingPlaneYAxis", (viewer) => new CuttingPlaneYAxisDragger(viewer));
35812
35990
  draggers.registerDragger("CuttingPlaneZAxis", (viewer) => new CuttingPlaneZAxisDragger(viewer));
@@ -37341,7 +37519,7 @@ void main() {
37341
37519
  this.getMousePosition(event, this.downPosition);
37342
37520
  };
37343
37521
  this.onPointerUp = (event) => {
37344
- if (!event.isPrimary)
37522
+ if (!event.isPrimary || event.button !== 0)
37345
37523
  return;
37346
37524
  const upPosition = this.getMousePosition(event, new Vector2());
37347
37525
  if (upPosition.distanceTo(this.downPosition) !== 0)
@@ -37870,19 +38048,19 @@ void main() {
37870
38048
  return this;
37871
38049
  }
37872
38050
  explode(scale = 0, coeff = 4) {
37873
- const centers = new Map();
37874
- const getObjectCenter = (object, target) => {
38051
+ const centersCache = new Map();
38052
+ const calcObjectCenter = (object, target) => {
37875
38053
  const extents = new Box3().setFromObject(object);
37876
38054
  const handle = object.userData.handle;
37877
38055
  if (!handle)
37878
38056
  return extents.getCenter(target);
37879
- const center = centers.get(handle);
38057
+ const center = centersCache.get(handle);
37880
38058
  if (center)
37881
38059
  return target.copy(center);
37882
38060
  const objects = this.getObjectsByHandles(handle);
37883
38061
  objects.forEach((x) => extents.expandByObject(x));
37884
38062
  extents.getCenter(target);
37885
- centers.set(handle, target.clone());
38063
+ centersCache.set(handle, target.clone());
37886
38064
  return target;
37887
38065
  };
37888
38066
  function calcExplodeDepth(object, depth) {
@@ -37893,13 +38071,14 @@ void main() {
37893
38071
  result = objectDepth;
37894
38072
  });
37895
38073
  object.userData.originalPosition = object.position.clone();
37896
- object.userData.originalCenter = getObjectCenter(object, new Vector3());
38074
+ object.userData.originalCenter = calcObjectCenter(object, new Vector3());
37897
38075
  return result;
37898
38076
  }
37899
38077
  const explodeScale = scale / 100;
37900
38078
  const explodeRoot = this.scene;
37901
- if (!explodeRoot.userData.explodeDepth)
38079
+ if (!explodeRoot.userData.explodeDepth) {
37902
38080
  explodeRoot.userData.explodeDepth = calcExplodeDepth(explodeRoot, 1);
38081
+ }
37903
38082
  const maxDepth = explodeRoot.userData.explodeDepth;
37904
38083
  const scaledExplodeDepth = explodeScale * maxDepth + 1;
37905
38084
  const explodeDepth = 0 | scaledExplodeDepth;
@@ -37916,8 +38095,8 @@ void main() {
37916
38095
  objectScale *= currentSegmentFraction;
37917
38096
  const parentCenter = object.parent.userData.originalCenter;
37918
38097
  const objectCenter = object.userData.originalCenter;
37919
- const objectOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
37920
- object.position.add(objectOffset);
38098
+ const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
38099
+ object.position.add(localOffset);
37921
38100
  }
37922
38101
  object.children.forEach((x) => explodeObject(x, depth + 1));
37923
38102
  }
@@ -38009,67 +38188,73 @@ void main() {
38009
38188
  return this;
38010
38189
  }
38011
38190
  explode(scale = 0, coeff = 4) {
38012
- const centers = new Map();
38191
+ const centersCache = new Map();
38013
38192
  const calcObjectCenter = (object, target) => {
38014
38193
  const extents = new Box3().setFromObject(object);
38015
38194
  const handle = object.userData.handle;
38016
38195
  if (!handle)
38017
38196
  return extents.getCenter(target);
38018
- const center = centers.get(handle);
38197
+ const center = centersCache.get(handle);
38019
38198
  if (center)
38020
38199
  return target.copy(center);
38021
38200
  const objects = this.getObjectsByHandles(handle);
38022
38201
  objects.forEach((x) => extents.expandByObject(x));
38023
38202
  extents.getCenter(target);
38024
- centers.set(handle, target.clone());
38203
+ centersCache.set(handle, target.clone());
38025
38204
  return target;
38026
38205
  };
38027
- function calcExplodeDepth(object, depth) {
38028
- let result = depth;
38029
- object.children
38030
- .filter((x) => !x.userData.isOptimized)
38031
- .forEach((x) => {
38032
- const objectDepth = calcExplodeDepth(x, depth + 1);
38033
- if (result < objectDepth)
38034
- result = objectDepth;
38035
- });
38206
+ const calcObjectDepth = (object) => {
38207
+ if (object.userData.depth !== undefined)
38208
+ return object.userData.depth;
38209
+ const parent = object.parent;
38210
+ const depth = parent && object !== explodeRoot ? calcObjectDepth(parent) + 1 : 0;
38211
+ object.userData.depth = depth;
38036
38212
  object.userData.originalPosition = object.position.clone();
38037
38213
  object.userData.originalCenter = calcObjectCenter(object, new Vector3());
38038
- return result;
38039
- }
38214
+ return depth;
38215
+ };
38040
38216
  const explodeScale = scale / 100;
38041
38217
  const explodeRoot = this.scene.children[0];
38042
- if (!explodeRoot.userData.explodeDepth)
38043
- explodeRoot.userData.explodeDepth = calcExplodeDepth(explodeRoot, 1);
38218
+ if (!explodeRoot.userData.explodeDepth) {
38219
+ let maxDepth = 0;
38220
+ this.gltfLoader.originalObjects.forEach((object) => {
38221
+ const depth = calcObjectDepth(object);
38222
+ if (depth > maxDepth)
38223
+ maxDepth = depth;
38224
+ });
38225
+ explodeRoot.userData.explodeDepth = maxDepth;
38226
+ }
38044
38227
  const maxDepth = explodeRoot.userData.explodeDepth;
38045
38228
  const scaledExplodeDepth = explodeScale * maxDepth + 1;
38046
38229
  const explodeDepth = 0 | scaledExplodeDepth;
38047
38230
  const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
38048
- const transformMap = new Map();
38049
- function explodeObject(object, depth) {
38050
- if (object.isCamera)
38051
- return;
38052
- if (object.userData.isHighlightWireframe)
38053
- return;
38054
- object.position.copy(object.userData.originalPosition);
38055
- if (depth > 0 && depth <= explodeDepth && !object.userData.isExplodeLocked) {
38231
+ const offsetCache = new Map();
38232
+ const calcObjectOffset = (object, target) => {
38233
+ if (offsetCache.has(object))
38234
+ return target.copy(offsetCache.get(object));
38235
+ const parent = object.parent;
38236
+ if (parent && object !== explodeRoot)
38237
+ calcObjectOffset(parent, target);
38238
+ const depth = object.userData.depth;
38239
+ if (depth > 0 && depth <= explodeDepth) {
38056
38240
  let objectScale = explodeScale * coeff;
38057
38241
  if (depth === explodeDepth)
38058
38242
  objectScale *= currentSegmentFraction;
38059
- const parentCenter = object.parent.userData.originalCenter;
38243
+ const parentCenter = parent.userData.originalCenter;
38060
38244
  const objectCenter = object.userData.originalCenter;
38061
- const objectOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
38062
- object.position.add(objectOffset);
38063
- const matrix = new Matrix4().makeTranslation(objectOffset.x, objectOffset.y, objectOffset.z);
38064
- transformMap.set(object, matrix);
38245
+ const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
38246
+ target.add(localOffset);
38065
38247
  }
38066
- object.children
38067
- .filter((x) => !x.userData.isOptimized)
38068
- .forEach((x) => explodeObject(x, depth + 1));
38069
- }
38070
- explodeObject(explodeRoot, 0);
38071
- this.scene.updateMatrixWorld();
38248
+ offsetCache.set(object, target.clone());
38249
+ return target;
38250
+ };
38251
+ const transformMap = new Map();
38252
+ this.gltfLoader.originalObjects.forEach((object) => {
38253
+ const globalOffset = calcObjectOffset(object, new Vector3());
38254
+ transformMap.set(object, new Matrix4().makeTranslation(globalOffset));
38255
+ });
38072
38256
  this.gltfLoader.applyObjectTransforms(transformMap);
38257
+ this.scene.updateMatrixWorld();
38073
38258
  return this;
38074
38259
  }
38075
38260
  }
@@ -38795,6 +38980,8 @@ void main() {
38795
38980
  this.oldOptimizeObjects = new Set();
38796
38981
  this.objectTransforms = new Map();
38797
38982
  this.transformedGeometries = new Map();
38983
+ this.syncTransformsToOriginalObjects = true;
38984
+ this._originalObjectMatrices = new Map();
38798
38985
  this.activeChunkLoads = 0;
38799
38986
  this.chunkQueue = [];
38800
38987
  this.objectIdToIndex = new Map();
@@ -38804,6 +38991,81 @@ void main() {
38804
38991
  this.mergedObjectMap = new Map();
38805
38992
  this.mergedGeometryVisibility = new Map();
38806
38993
  this._webglInfoCache = null;
38994
+ this.transformTextureSize = 1024;
38995
+ this.transformTexture = this.createDummyTexture();
38996
+ this.transformData = null;
38997
+ this.identityTransformData = null;
38998
+ this.visibilityMaterials = new Set();
38999
+ }
39000
+ createDummyTexture() {
39001
+ const data = new Float32Array(16);
39002
+ const identity = new Matrix4();
39003
+ identity.toArray(data);
39004
+ const dummyData = new Float32Array(16);
39005
+ identity.toArray(dummyData);
39006
+ const dummyTexture = new DataTexture(dummyData, 4, 1, RGBAFormat, FloatType);
39007
+ dummyTexture.minFilter = NearestFilter;
39008
+ dummyTexture.magFilter = NearestFilter;
39009
+ dummyTexture.needsUpdate = true;
39010
+ return dummyTexture;
39011
+ }
39012
+ initTransformTexture() {
39013
+ if (this.transformTexture) {
39014
+ this.transformTexture.dispose();
39015
+ }
39016
+ const maxInstanceCount = this.maxObjectId + 1;
39017
+ let size = Math.sqrt(maxInstanceCount * 4);
39018
+ size = Math.ceil(size / 4) * 4;
39019
+ size = Math.max(size, 4);
39020
+ this.transformTextureSize = size;
39021
+ const arraySize = size * size * 4;
39022
+ this.transformData = new Float32Array(arraySize);
39023
+ this.identityTransformData = new Float32Array(arraySize);
39024
+ for (let i = 0; i <= this.maxObjectId; i++) {
39025
+ const base = i * 16;
39026
+ if (base + 15 < arraySize) {
39027
+ this.identityTransformData[base + 0] = 1;
39028
+ this.identityTransformData[base + 5] = 1;
39029
+ this.identityTransformData[base + 10] = 1;
39030
+ this.identityTransformData[base + 15] = 1;
39031
+ }
39032
+ }
39033
+ this._resetTransformData(false);
39034
+ this.transformTexture = new DataTexture(this.transformData, size, size, RGBAFormat, FloatType);
39035
+ this.transformTexture.needsUpdate = true;
39036
+ this.transformTexture.generateMipmaps = false;
39037
+ console.log(`Initialized transform texture: ${size}x${size} for ${maxInstanceCount} objects`);
39038
+ this.updateMaterialUniforms();
39039
+ this.visibilityMaterials.forEach((material) => {
39040
+ material.needsUpdate = true;
39041
+ });
39042
+ }
39043
+ _resetTransformData(updateTexture = true) {
39044
+ if (!this.transformData || !this.identityTransformData) return;
39045
+ this.transformData.set(this.identityTransformData);
39046
+ if (updateTexture) {
39047
+ this.updateTransformTexture();
39048
+ }
39049
+ }
39050
+ updateMaterialUniforms() {
39051
+ if (
39052
+ this._lastTransformTexture === this.transformTexture &&
39053
+ this._lastTransformTextureSize === this.transformTextureSize
39054
+ ) {
39055
+ return;
39056
+ }
39057
+ this._lastTransformTexture = this.transformTexture;
39058
+ this._lastTransformTextureSize = this.transformTextureSize;
39059
+ this.visibilityMaterials.forEach((material) => {
39060
+ if (material.userData && material.userData.visibilityUniforms) {
39061
+ material.userData.visibilityUniforms.transformTexture.value = this.transformTexture;
39062
+ material.userData.visibilityUniforms.transformTextureSize.value = this.transformTextureSize;
39063
+ }
39064
+ });
39065
+ }
39066
+ updateTransformTexture() {
39067
+ if (!this.transformTexture) return;
39068
+ this.transformTexture.needsUpdate = true;
38807
39069
  }
38808
39070
  setVisibleEdges(visible) {
38809
39071
  this.visibleEdges = visible;
@@ -39693,36 +39955,82 @@ void main() {
39693
39955
  }
39694
39956
  }
39695
39957
  createVisibilityMaterial(material) {
39958
+ this.visibilityMaterials.add(material);
39959
+ const uniforms = {
39960
+ transformTexture: { value: this.transformTexture },
39961
+ transformTextureSize: { value: this.transformTextureSize },
39962
+ };
39963
+ material.userData.visibilityUniforms = uniforms;
39696
39964
  material.onBeforeCompile = (shader) => {
39965
+ shader.uniforms.transformTexture = uniforms.transformTexture;
39966
+ shader.uniforms.transformTextureSize = uniforms.transformTextureSize;
39697
39967
  shader.vertexShader = shader.vertexShader.replace(
39698
39968
  "#include <common>",
39699
39969
  `
39700
39970
  #include <common>
39971
+
39701
39972
  attribute float visibility;
39973
+ attribute float objectId;
39702
39974
  varying float vVisibility;
39703
- `
39704
- );
39705
- shader.fragmentShader = shader.fragmentShader.replace(
39706
- "#include <common>",
39707
- `
39708
- #include <common>
39709
- varying float vVisibility;
39975
+ uniform highp sampler2D transformTexture;
39976
+ uniform float transformTextureSize;
39977
+
39978
+ mat4 getTransformMatrix(float instanceId) {
39979
+ int size = int(transformTextureSize);
39980
+ int index = int(instanceId) * 4;
39981
+
39982
+ int x0 = index % size;
39983
+ int y0 = index / size;
39984
+
39985
+ vec4 row0 = texelFetch(transformTexture, ivec2(x0, y0), 0);
39986
+ vec4 row1 = texelFetch(transformTexture, ivec2(x0 + 1, y0), 0);
39987
+ vec4 row2 = texelFetch(transformTexture, ivec2(x0 + 2, y0), 0);
39988
+ vec4 row3 = texelFetch(transformTexture, ivec2(x0 + 3, y0), 0);
39989
+
39990
+ return mat4(row0, row1, row2, row3);
39991
+ }
39710
39992
  `
39711
39993
  );
39712
39994
  shader.vertexShader = shader.vertexShader.replace(
39713
39995
  "void main() {",
39714
39996
  `
39715
39997
  void main() {
39716
- vVisibility = visibility;
39998
+ mat4 batchingMatrix = getTransformMatrix(objectId);
39999
+ vVisibility = visibility;
39717
40000
  `
39718
40001
  );
39719
- shader.fragmentShader = shader.fragmentShader.replace(
39720
- "void main() {",
40002
+ if (shader.vertexShader.includes("#include <beginnormal_vertex>")) {
40003
+ shader.vertexShader = shader.vertexShader.replace(
40004
+ "#include <beginnormal_vertex>",
40005
+ `
40006
+ vec3 objectNormal = vec3( normal );
40007
+ mat3 bm = mat3( batchingMatrix );
40008
+ objectNormal = bm * objectNormal;
39721
40009
  `
40010
+ );
40011
+ }
40012
+ shader.vertexShader = shader.vertexShader.replace(
40013
+ "#include <begin_vertex>",
40014
+ `
40015
+ vec3 transformed = vec3( position );
40016
+ transformed = ( batchingMatrix * vec4( transformed, 1.0 ) ).xyz;
40017
+ `
40018
+ );
40019
+ shader.fragmentShader = shader.fragmentShader
40020
+ .replace(
40021
+ "#include <common>",
40022
+ `
40023
+ #include <common>
40024
+ varying float vVisibility;
40025
+ `
40026
+ )
40027
+ .replace(
40028
+ "void main() {",
40029
+ `
39722
40030
  void main() {
39723
40031
  if (vVisibility < 0.5) discard;
39724
40032
  `
39725
- );
40033
+ );
39726
40034
  };
39727
40035
  material.needsUpdate = true;
39728
40036
  return material;
@@ -39828,6 +40136,7 @@ void main() {
39828
40136
  this.isolatedObjects = [];
39829
40137
  this.objectTransforms.clear();
39830
40138
  this.transformedGeometries.clear();
40139
+ this._originalObjectMatrices.clear();
39831
40140
  this.totalLoadedObjects = 0;
39832
40141
  this.currentMemoryUsage = 0;
39833
40142
  this.pendingMemoryUsage = 0;
@@ -39837,6 +40146,8 @@ void main() {
39837
40146
  this.objectIdToIndex.clear();
39838
40147
  this.maxObjectId = 0;
39839
40148
  this.objectVisibility = new Float32Array();
40149
+ this.meshToNodeMap = null;
40150
+ this.visibilityMaterials.clear();
39840
40151
  }
39841
40152
  setStructureTransform(structureId, matrix) {
39842
40153
  const rootGroup = this.structureRoots.get(structureId);
@@ -39952,12 +40263,15 @@ void main() {
39952
40263
  });
39953
40264
  this.originalObjects.clear();
39954
40265
  this.originalObjectsToSelection.clear();
40266
+ this.objectIdToIndex.clear();
40267
+ this.maxObjectId = 0;
39955
40268
  const structureGroups = new Map();
39956
40269
  this.dispatchEvent("optimizationprogress", {
39957
40270
  phase: "collecting",
39958
40271
  progress: 5,
39959
40272
  message: "Collecting scene objects...",
39960
40273
  });
40274
+ let totalObjectsToMerge = 0;
39961
40275
  this.scene.traverse((object) => {
39962
40276
  if (object.userData.structureId) {
39963
40277
  const structureId = object.userData.structureId;
@@ -39975,17 +40289,32 @@ void main() {
39975
40289
  });
39976
40290
  }
39977
40291
  const group = structureGroups.get(structureId);
40292
+ let added = false;
39978
40293
  if (object instanceof Mesh) {
39979
40294
  this.addToMaterialGroup(object, group.mapMeshes, group.meshes);
40295
+ added = true;
39980
40296
  } else if (object instanceof LineSegments) {
39981
40297
  this.addToMaterialGroup(object, group.mapLineSegments, group.lineSegments);
40298
+ added = true;
39982
40299
  } else if (object instanceof Line$1) {
39983
40300
  this.addToMaterialGroup(object, group.mapLines, group.lines);
40301
+ added = true;
39984
40302
  } else if (object instanceof Points) {
39985
40303
  this.addToMaterialGroup(object, group.mapPoints, group.points);
40304
+ added = true;
40305
+ }
40306
+ if (added) {
40307
+ totalObjectsToMerge++;
39986
40308
  }
39987
40309
  }
39988
40310
  });
40311
+ if (totalObjectsToMerge > 0) {
40312
+ console.log(`Pre-allocating transform texture for ${totalObjectsToMerge} objects`);
40313
+ this.maxObjectId = totalObjectsToMerge;
40314
+ this.initTransformTexture();
40315
+ this.initializeObjectVisibility();
40316
+ this.maxObjectId = 0;
40317
+ }
39989
40318
  let processedGroups = 0;
39990
40319
  const totalGroups = structureGroups.size;
39991
40320
  this.dispatchEvent("optimizationprogress", {
@@ -40030,7 +40359,6 @@ void main() {
40030
40359
  this.originalObjectsToSelection.add(obj);
40031
40360
  }
40032
40361
  });
40033
- this.initializeObjectVisibility();
40034
40362
  console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
40035
40363
  this.dispatchEvent("optimizationprogress", {
40036
40364
  phase: "complete",
@@ -40099,6 +40427,7 @@ void main() {
40099
40427
  }
40100
40428
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40101
40429
  const mergedMesh = new Mesh(mergedGeometry, visibilityMaterial);
40430
+ mergedMesh.frustumCulled = false;
40102
40431
  mergedMesh.userData.isOptimized = true;
40103
40432
  rootGroup.add(mergedMesh);
40104
40433
  this.mergedMesh.add(mergedMesh);
@@ -40215,6 +40544,7 @@ void main() {
40215
40544
  geometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40216
40545
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40217
40546
  const mergedLine = new LineSegments(geometry, visibilityMaterial);
40547
+ mergedLine.frustumCulled = false;
40218
40548
  mergedLine.userData.isEdge = isEdge;
40219
40549
  mergedLine.userData.isOptimized = true;
40220
40550
  const mergedObjects = [mergedLine];
@@ -40303,6 +40633,7 @@ void main() {
40303
40633
  mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40304
40634
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40305
40635
  const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
40636
+ mergedLine.frustumCulled = false;
40306
40637
  mergedLine.userData.isEdge = isEdge;
40307
40638
  mergedLine.userData.isOptimized = true;
40308
40639
  if (this.useVAO) {
@@ -40372,7 +40703,27 @@ void main() {
40372
40703
  const mergedObjects = [];
40373
40704
  if (geometries.length > 0) {
40374
40705
  const mergedGeometry = mergeGeometries(geometries, false);
40375
- const mergedPoints = new Points(mergedGeometry, group.material);
40706
+ const totalVertices = mergedGeometry.attributes.position.count;
40707
+ const objectIds = new Float32Array(totalVertices);
40708
+ let vertexOffset = 0;
40709
+ group.objects.forEach((points) => {
40710
+ const handle = points.userData.handle;
40711
+ if (!this.objectIdToIndex.has(handle)) {
40712
+ this.objectIdToIndex.set(handle, this.maxObjectId++);
40713
+ }
40714
+ const objectId = this.objectIdToIndex.get(handle);
40715
+ const count = points.geometry.attributes.position.count;
40716
+ for (let i = 0; i < count; i++) {
40717
+ objectIds[vertexOffset++] = objectId;
40718
+ }
40719
+ });
40720
+ mergedGeometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
40721
+ const visibilityArray = new Float32Array(totalVertices);
40722
+ visibilityArray.fill(1.0);
40723
+ mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40724
+ const visibilityMaterial = this.createVisibilityMaterial(group.material);
40725
+ const mergedPoints = new Points(mergedGeometry, visibilityMaterial);
40726
+ mergedPoints.frustumCulled = false;
40376
40727
  mergedPoints.userData.isOptimized = true;
40377
40728
  if (this.useVAO) {
40378
40729
  this.createVAO(mergedPoints);
@@ -40441,13 +40792,33 @@ void main() {
40441
40792
  geometriesWithIndex.push(clonedGeometry);
40442
40793
  });
40443
40794
  const finalGeometry = mergeGeometries(geometriesWithIndex, false);
40795
+ const totalVertices = finalGeometry.attributes.position.count;
40796
+ const objectIds = new Float32Array(totalVertices);
40797
+ let vertexOffset = 0;
40798
+ lineSegmentsArray.forEach((segment) => {
40799
+ const handle = segment.userData.handle;
40800
+ if (!this.objectIdToIndex.has(handle)) {
40801
+ this.objectIdToIndex.set(handle, this.maxObjectId++);
40802
+ }
40803
+ const objectId = this.objectIdToIndex.get(handle);
40804
+ const count = segment.geometry.attributes.position.count;
40805
+ for (let i = 0; i < count; i++) {
40806
+ objectIds[vertexOffset++] = objectId;
40807
+ }
40808
+ });
40809
+ finalGeometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
40810
+ const visibilityArray = new Float32Array(totalVertices);
40811
+ visibilityArray.fill(1.0);
40812
+ finalGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40444
40813
  const material = new LineBasicMaterial({
40445
40814
  vertexColors: true,
40446
40815
  });
40816
+ const visibilityMaterial = this.createVisibilityMaterial(material);
40447
40817
  if (this.useVAO) {
40448
40818
  this.createVAO(finalGeometry);
40449
40819
  }
40450
- const mergedLine = new LineSegments(finalGeometry, material);
40820
+ const mergedLine = new LineSegments(finalGeometry, visibilityMaterial);
40821
+ mergedLine.frustumCulled = false;
40451
40822
  mergedLine.userData.structureId = structureId;
40452
40823
  mergedLine.userData.isOptimized = true;
40453
40824
  rootGroup.add(mergedLine);
@@ -40566,18 +40937,91 @@ void main() {
40566
40937
  console.warn("No merged objects to transform");
40567
40938
  return;
40568
40939
  }
40569
- this.objectTransforms = new Map(objectTransformMap);
40570
- for (const mesh of this.mergedMesh) {
40571
- this._applyTransformToMergedObject(mesh);
40940
+ if (!this.transformData) {
40941
+ console.warn("Transform texture not initialized");
40942
+ return;
40572
40943
  }
40573
- for (const line of this.mergedLines) {
40574
- this._applyTransformToMergedObject(line);
40944
+ this.objectTransforms = objectTransformMap;
40945
+ this._resetTransformData(false);
40946
+ const transformData = this.transformData;
40947
+ const objectIdToIndex = this.objectIdToIndex;
40948
+ let textureNeedsUpdate = false;
40949
+ if (objectTransformMap instanceof Map) {
40950
+ for (const [object, matrix] of objectTransformMap.entries()) {
40951
+ const userData = object.userData;
40952
+ if (!userData) continue;
40953
+ const handle = userData.handle;
40954
+ if (handle === undefined) continue;
40955
+ const objectId = objectIdToIndex.get(handle);
40956
+ if (objectId !== undefined) {
40957
+ transformData.set(matrix.elements, objectId * 16);
40958
+ textureNeedsUpdate = true;
40959
+ }
40960
+ }
40961
+ } else {
40962
+ const len = objectTransformMap.length;
40963
+ for (let i = 0; i < len; i++) {
40964
+ const pair = objectTransformMap[i];
40965
+ const userData = pair[0].userData;
40966
+ if (!userData) continue;
40967
+ const handle = userData.handle;
40968
+ if (handle === undefined) continue;
40969
+ const objectId = objectIdToIndex.get(handle);
40970
+ if (objectId !== undefined) {
40971
+ transformData.set(pair[1].elements, objectId * 16);
40972
+ textureNeedsUpdate = true;
40973
+ }
40974
+ }
40575
40975
  }
40576
- for (const lineSegment of this.mergedLineSegments) {
40577
- this._applyTransformToMergedObject(lineSegment);
40976
+ if (textureNeedsUpdate) {
40977
+ this.updateTransformTexture();
40978
+ if (
40979
+ this._lastTransformTexture !== this.transformTexture ||
40980
+ this._lastTransformTextureSize !== this.transformTextureSize
40981
+ ) {
40982
+ this.updateMaterialUniforms();
40983
+ }
40578
40984
  }
40579
- for (const point of this.mergedPoints) {
40580
- this._applyTransformToMergedObject(point);
40985
+ if (this.syncTransformsToOriginalObjects) {
40986
+ this._syncOriginalObjectTransforms(objectTransformMap);
40987
+ }
40988
+ }
40989
+ _syncOriginalObjectTransforms(objectTransformMap) {
40990
+ for (const [obj, savedPos] of this._originalObjectMatrices) {
40991
+ obj.position.copy(savedPos);
40992
+ if (obj.userData.highlight) {
40993
+ obj.userData.highlight.position.copy(savedPos);
40994
+ }
40995
+ }
40996
+ this._originalObjectMatrices.clear();
40997
+ const _offset = new Vector3();
40998
+ const _parentInverse = new Matrix4();
40999
+ if (objectTransformMap instanceof Map) {
41000
+ for (const [object, matrix] of objectTransformMap.entries()) {
41001
+ if (!object.userData?.handle) continue;
41002
+ if (!this._originalObjectMatrices.has(object)) {
41003
+ this._originalObjectMatrices.set(object, object.position.clone());
41004
+ }
41005
+ _offset.setFromMatrixPosition(matrix);
41006
+ if (object.userData.structureId) {
41007
+ const rootGroup = this.structureRoots.get(object.userData.structureId);
41008
+ if (rootGroup && object.parent && object.parent !== rootGroup) {
41009
+ const origin = new Vector3(0, 0, 0);
41010
+ origin.applyMatrix4(rootGroup.matrixWorld);
41011
+ _offset.applyMatrix4(rootGroup.matrixWorld);
41012
+ _offset.sub(origin);
41013
+ const parentOrigin = new Vector3(0, 0, 0);
41014
+ _parentInverse.copy(object.parent.matrixWorld).invert();
41015
+ parentOrigin.applyMatrix4(_parentInverse);
41016
+ _offset.applyMatrix4(_parentInverse);
41017
+ _offset.sub(parentOrigin);
41018
+ }
41019
+ }
41020
+ object.position.add(_offset);
41021
+ if (object.userData.highlight) {
41022
+ object.userData.highlight.position.copy(object.position);
41023
+ }
41024
+ }
40581
41025
  }
40582
41026
  }
40583
41027
  createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
@@ -40594,21 +41038,66 @@ void main() {
40594
41038
  ? objects
40595
41039
  : Array.from(objects)
40596
41040
  : Array.from(this.originalObjects);
41041
+ const structureInverseMatrices = new Map();
41042
+ if (!this.meshToNodeMap) {
41043
+ this.meshToNodeMap = new Map();
41044
+ for (const node of this.nodes.values()) {
41045
+ if (node.object) {
41046
+ this.meshToNodeMap.set(node.object, node);
41047
+ }
41048
+ }
41049
+ }
40597
41050
  for (const obj of objectsArray) {
40598
41051
  if (!obj.geometry || !obj.geometry.attributes.position) continue;
40599
- const boundingBox = new Box3().setFromBufferAttribute(obj.geometry.attributes.position);
40600
- if (obj.matrixWorld) {
40601
- boundingBox.applyMatrix4(obj.matrixWorld);
41052
+ if (!obj.userData.explodeVector) {
41053
+ let center = null;
41054
+ const node = this.meshToNodeMap.get(obj);
41055
+ if (node && node.geometryExtents) {
41056
+ const box = node.geometryExtents.clone();
41057
+ box.applyMatrix4(obj.matrixWorld);
41058
+ center = new Vector3();
41059
+ box.getCenter(center);
41060
+ }
41061
+ if (!center) {
41062
+ if (!obj.geometry.boundingBox) obj.geometry.computeBoundingBox();
41063
+ const box = obj.geometry.boundingBox.clone();
41064
+ box.applyMatrix4(obj.matrixWorld);
41065
+ center = new Vector3();
41066
+ box.getCenter(center);
41067
+ }
41068
+ const explodeVector = center.sub(explodeCenter);
41069
+ obj.userData.explodeVector = explodeVector;
40602
41070
  }
40603
- if (boundingBox.isEmpty()) continue;
40604
- const objectCenter = new Vector3();
40605
- boundingBox.getCenter(objectCenter);
40606
- const direction = objectCenter.clone().sub(explodeCenter);
40607
- const distance = direction.length();
41071
+ const explodeVector = obj.userData.explodeVector;
41072
+ const distance = explodeVector.length();
40608
41073
  if (distance > 0) {
40609
- direction.normalize();
40610
- const offset = direction.multiplyScalar(distance * (explodeFactor - 1.0));
40611
- const matrix = new Matrix4().makeTranslation(offset.x, offset.y, offset.z);
41074
+ const offset = explodeVector.clone().multiplyScalar(explodeFactor - 1.0);
41075
+ const localOffset = offset.clone();
41076
+ if (obj.userData.structureId) {
41077
+ const structureId = obj.userData.structureId;
41078
+ let inverseMatrix = structureInverseMatrices.get(structureId);
41079
+ if (!inverseMatrix) {
41080
+ const rootGroup = this.structureRoots.get(structureId);
41081
+ if (rootGroup) {
41082
+ if (!rootGroup.userData.inverseWorldMatrix) {
41083
+ rootGroup.userData.inverseWorldMatrix = new Matrix4().copy(rootGroup.matrixWorld).invert();
41084
+ }
41085
+ inverseMatrix = rootGroup.userData.inverseWorldMatrix;
41086
+ structureInverseMatrices.set(structureId, inverseMatrix);
41087
+ }
41088
+ }
41089
+ if (inverseMatrix) {
41090
+ const zero = new Vector3(0, 0, 0).applyMatrix4(inverseMatrix);
41091
+ const vec = offset.clone().applyMatrix4(inverseMatrix).sub(zero);
41092
+ localOffset.copy(vec);
41093
+ }
41094
+ }
41095
+ let matrix = obj.userData.explodeMatrix;
41096
+ if (!matrix) {
41097
+ matrix = new Matrix4();
41098
+ obj.userData.explodeMatrix = matrix;
41099
+ }
41100
+ matrix.makeTranslation(localOffset.x, localOffset.y, localOffset.z);
40612
41101
  transformMap.set(obj, matrix);
40613
41102
  }
40614
41103
  }
@@ -40616,115 +41105,19 @@ void main() {
40616
41105
  }
40617
41106
  clearTransforms() {
40618
41107
  this.objectTransforms.clear();
40619
- for (const mesh of this.mergedMesh) {
40620
- this._restoreOriginalGeometry(mesh);
40621
- }
40622
- for (const line of this.mergedLines) {
40623
- this._restoreOriginalGeometry(line);
40624
- }
40625
- for (const lineSegment of this.mergedLineSegments) {
40626
- this._restoreOriginalGeometry(lineSegment);
40627
- }
40628
- for (const point of this.mergedPoints) {
40629
- this._restoreOriginalGeometry(point);
40630
- }
40631
- }
40632
- clearHandleTransforms() {
40633
- this.clearTransforms();
40634
- }
40635
- _applyTransformToMergedObject(mergedObject) {
40636
- const objectData = this.mergedObjectMap.get(mergedObject.uuid);
40637
- if (!objectData || !objectData.objectMapping) return;
40638
- const geometry = mergedObject.geometry;
40639
- if (!geometry || !geometry.attributes.position) return;
40640
- const positionAttr = geometry.attributes.position;
40641
- const positions = positionAttr.array;
40642
- if (!this.transformedGeometries.has(mergedObject.uuid)) {
40643
- this.transformedGeometries.set(mergedObject.uuid, new Float32Array(positions));
40644
- }
40645
- const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
40646
- const tempVector = new Vector3();
40647
- for (const [originalMesh, mappingData] of objectData.objectMapping) {
40648
- const transform = this.objectTransforms.get(originalMesh);
40649
- if (!transform) {
40650
- const startIdx = mappingData.startVertexIndex * 3;
40651
- const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
40652
- for (let i = startIdx; i < endIdx; i++) {
40653
- positions[i] = originalPositions[i];
40654
- }
40655
- continue;
40656
- }
40657
- const startVertex = mappingData.startVertexIndex;
40658
- const vertexCount = mappingData.vertexCount;
40659
- for (let i = 0; i < vertexCount; i++) {
40660
- const idx = (startVertex + i) * 3;
40661
- tempVector.set(originalPositions[idx], originalPositions[idx + 1], originalPositions[idx + 2]);
40662
- tempVector.applyMatrix4(transform);
40663
- positions[idx] = tempVector.x;
40664
- positions[idx + 1] = tempVector.y;
40665
- positions[idx + 2] = tempVector.z;
40666
- }
40667
- }
40668
- if (geometry.attributes.normal) {
40669
- this._updateNormalsForTransform(geometry, objectData, originalPositions);
40670
- }
40671
- positionAttr.needsUpdate = true;
40672
- geometry.computeBoundingSphere();
40673
- geometry.computeBoundingBox();
40674
- }
40675
- _updateNormalsForTransform(geometry, objectData, originalPositions) {
40676
- const normalAttr = geometry.attributes.normal;
40677
- if (!normalAttr) return;
40678
- const normals = normalAttr.array;
40679
- const tempVector = new Vector3();
40680
- const normalMatrix = new Matrix4();
40681
- const normalsKey = `${geometry.uuid}_normals`;
40682
- if (!this.transformedGeometries.has(normalsKey)) {
40683
- this.transformedGeometries.set(normalsKey, new Float32Array(normals));
40684
- }
40685
- const originalNormals = this.transformedGeometries.get(normalsKey);
40686
- for (const [originalMesh, mappingData] of objectData.objectMapping) {
40687
- const transform = this.objectTransforms.get(originalMesh);
40688
- if (!transform) {
40689
- const startIdx = mappingData.startVertexIndex * 3;
40690
- const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
40691
- for (let i = startIdx; i < endIdx; i++) {
40692
- normals[i] = originalNormals[i];
41108
+ this._resetTransformData(true);
41109
+ if (this.syncTransformsToOriginalObjects) {
41110
+ for (const [obj, savedPos] of this._originalObjectMatrices) {
41111
+ obj.position.copy(savedPos);
41112
+ if (obj.userData.highlight) {
41113
+ obj.userData.highlight.position.copy(savedPos);
40693
41114
  }
40694
- continue;
40695
- }
40696
- normalMatrix.copy(transform).invert().transpose();
40697
- const startVertex = mappingData.startVertexIndex;
40698
- const vertexCount = mappingData.vertexCount;
40699
- for (let i = 0; i < vertexCount; i++) {
40700
- const idx = (startVertex + i) * 3;
40701
- tempVector.set(originalNormals[idx], originalNormals[idx + 1], originalNormals[idx + 2]);
40702
- tempVector.applyMatrix4(normalMatrix).normalize();
40703
- normals[idx] = tempVector.x;
40704
- normals[idx + 1] = tempVector.y;
40705
- normals[idx + 2] = tempVector.z;
40706
41115
  }
41116
+ this._originalObjectMatrices.clear();
40707
41117
  }
40708
- normalAttr.needsUpdate = true;
40709
41118
  }
40710
- _restoreOriginalGeometry(mergedObject) {
40711
- const geometry = mergedObject.geometry;
40712
- if (!geometry || !geometry.attributes.position) return;
40713
- const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
40714
- if (originalPositions) {
40715
- const positions = geometry.attributes.position.array;
40716
- positions.set(originalPositions);
40717
- geometry.attributes.position.needsUpdate = true;
40718
- }
40719
- const normalsKey = `${geometry.uuid}_normals`;
40720
- const originalNormals = this.transformedGeometries.get(normalsKey);
40721
- if (originalNormals && geometry.attributes.normal) {
40722
- const normals = geometry.attributes.normal.array;
40723
- normals.set(originalNormals);
40724
- geometry.attributes.normal.needsUpdate = true;
40725
- }
40726
- geometry.computeBoundingSphere();
40727
- geometry.computeBoundingBox();
41119
+ clearHandleTransforms() {
41120
+ this.clearTransforms();
40728
41121
  }
40729
41122
  syncHiddenObjects() {
40730
41123
  if (this.mergedObjectMap.size === 0) {