@inweb/viewer-three 27.2.2 → 27.3.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.
@@ -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) {
@@ -35219,14 +35362,20 @@ void main() {
35219
35362
  }
35220
35363
  };
35221
35364
  this.onKeyUp = (event) => {
35222
- 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
+ }
35223
35369
  this.update();
35370
+ }
35224
35371
  };
35225
35372
  this.camera = camera;
35226
35373
  this.groundObjects = groundObjects;
35227
35374
  for (const obj of groundObjects) {
35228
35375
  this._groundObjectBoxes.set(obj, new Box3().setFromObject(obj));
35229
35376
  }
35377
+ const pos = this.object.position;
35378
+ this._rebuildGroundBox(pos);
35230
35379
  this.raycaster = new Raycaster();
35231
35380
  this.raycaster.near = 0;
35232
35381
  this.raycaster.far = this.EYE_HEIGHT + this.FAILING_DISTANCE;
@@ -35835,6 +35984,7 @@ void main() {
35835
35984
  draggers.registerDragger("Orbit", (viewer) => new OrbitDragger(viewer));
35836
35985
  draggers.registerDragger("Zoom", (viewer) => new ZoomDragger(viewer));
35837
35986
  draggers.registerDragger("MeasureLine", (viewer) => new MeasureLineDragger(viewer));
35987
+ draggers.registerDragger("CuttingPlane", (viewer) => new CuttingPlaneDragger(viewer));
35838
35988
  draggers.registerDragger("CuttingPlaneXAxis", (viewer) => new CuttingPlaneXAxisDragger(viewer));
35839
35989
  draggers.registerDragger("CuttingPlaneYAxis", (viewer) => new CuttingPlaneYAxisDragger(viewer));
35840
35990
  draggers.registerDragger("CuttingPlaneZAxis", (viewer) => new CuttingPlaneZAxisDragger(viewer));
@@ -37369,7 +37519,7 @@ void main() {
37369
37519
  this.getMousePosition(event, this.downPosition);
37370
37520
  };
37371
37521
  this.onPointerUp = (event) => {
37372
- if (!event.isPrimary)
37522
+ if (!event.isPrimary || event.button !== 0)
37373
37523
  return;
37374
37524
  const upPosition = this.getMousePosition(event, new Vector2());
37375
37525
  if (upPosition.distanceTo(this.downPosition) !== 0)
@@ -37898,19 +38048,19 @@ void main() {
37898
38048
  return this;
37899
38049
  }
37900
38050
  explode(scale = 0, coeff = 4) {
37901
- const centers = new Map();
37902
- const getObjectCenter = (object, target) => {
38051
+ const centersCache = new Map();
38052
+ const calcObjectCenter = (object, target) => {
37903
38053
  const extents = new Box3().setFromObject(object);
37904
38054
  const handle = object.userData.handle;
37905
38055
  if (!handle)
37906
38056
  return extents.getCenter(target);
37907
- const center = centers.get(handle);
38057
+ const center = centersCache.get(handle);
37908
38058
  if (center)
37909
38059
  return target.copy(center);
37910
38060
  const objects = this.getObjectsByHandles(handle);
37911
38061
  objects.forEach((x) => extents.expandByObject(x));
37912
38062
  extents.getCenter(target);
37913
- centers.set(handle, target.clone());
38063
+ centersCache.set(handle, target.clone());
37914
38064
  return target;
37915
38065
  };
37916
38066
  function calcExplodeDepth(object, depth) {
@@ -37921,13 +38071,14 @@ void main() {
37921
38071
  result = objectDepth;
37922
38072
  });
37923
38073
  object.userData.originalPosition = object.position.clone();
37924
- object.userData.originalCenter = getObjectCenter(object, new Vector3());
38074
+ object.userData.originalCenter = calcObjectCenter(object, new Vector3());
37925
38075
  return result;
37926
38076
  }
37927
38077
  const explodeScale = scale / 100;
37928
38078
  const explodeRoot = this.scene;
37929
- if (!explodeRoot.userData.explodeDepth)
38079
+ if (!explodeRoot.userData.explodeDepth) {
37930
38080
  explodeRoot.userData.explodeDepth = calcExplodeDepth(explodeRoot, 1);
38081
+ }
37931
38082
  const maxDepth = explodeRoot.userData.explodeDepth;
37932
38083
  const scaledExplodeDepth = explodeScale * maxDepth + 1;
37933
38084
  const explodeDepth = 0 | scaledExplodeDepth;
@@ -37944,8 +38095,8 @@ void main() {
37944
38095
  objectScale *= currentSegmentFraction;
37945
38096
  const parentCenter = object.parent.userData.originalCenter;
37946
38097
  const objectCenter = object.userData.originalCenter;
37947
- const objectOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
37948
- object.position.add(objectOffset);
38098
+ const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
38099
+ object.position.add(localOffset);
37949
38100
  }
37950
38101
  object.children.forEach((x) => explodeObject(x, depth + 1));
37951
38102
  }
@@ -38037,67 +38188,73 @@ void main() {
38037
38188
  return this;
38038
38189
  }
38039
38190
  explode(scale = 0, coeff = 4) {
38040
- const centers = new Map();
38191
+ const centersCache = new Map();
38041
38192
  const calcObjectCenter = (object, target) => {
38042
38193
  const extents = new Box3().setFromObject(object);
38043
38194
  const handle = object.userData.handle;
38044
38195
  if (!handle)
38045
38196
  return extents.getCenter(target);
38046
- const center = centers.get(handle);
38197
+ const center = centersCache.get(handle);
38047
38198
  if (center)
38048
38199
  return target.copy(center);
38049
38200
  const objects = this.getObjectsByHandles(handle);
38050
38201
  objects.forEach((x) => extents.expandByObject(x));
38051
38202
  extents.getCenter(target);
38052
- centers.set(handle, target.clone());
38203
+ centersCache.set(handle, target.clone());
38053
38204
  return target;
38054
38205
  };
38055
- function calcExplodeDepth(object, depth) {
38056
- let result = depth;
38057
- object.children
38058
- .filter((x) => !x.userData.isOptimized)
38059
- .forEach((x) => {
38060
- const objectDepth = calcExplodeDepth(x, depth + 1);
38061
- if (result < objectDepth)
38062
- result = objectDepth;
38063
- });
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;
38064
38212
  object.userData.originalPosition = object.position.clone();
38065
38213
  object.userData.originalCenter = calcObjectCenter(object, new Vector3());
38066
- return result;
38067
- }
38214
+ return depth;
38215
+ };
38068
38216
  const explodeScale = scale / 100;
38069
38217
  const explodeRoot = this.scene.children[0];
38070
- if (!explodeRoot.userData.explodeDepth)
38071
- 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
+ }
38072
38227
  const maxDepth = explodeRoot.userData.explodeDepth;
38073
38228
  const scaledExplodeDepth = explodeScale * maxDepth + 1;
38074
38229
  const explodeDepth = 0 | scaledExplodeDepth;
38075
38230
  const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
38076
- const transformMap = new Map();
38077
- function explodeObject(object, depth) {
38078
- if (object.isCamera)
38079
- return;
38080
- if (object.userData.isHighlightWireframe)
38081
- return;
38082
- object.position.copy(object.userData.originalPosition);
38083
- 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) {
38084
38240
  let objectScale = explodeScale * coeff;
38085
38241
  if (depth === explodeDepth)
38086
38242
  objectScale *= currentSegmentFraction;
38087
- const parentCenter = object.parent.userData.originalCenter;
38243
+ const parentCenter = parent.userData.originalCenter;
38088
38244
  const objectCenter = object.userData.originalCenter;
38089
- const objectOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
38090
- object.position.add(objectOffset);
38091
- const matrix = new Matrix4().makeTranslation(objectOffset.x, objectOffset.y, objectOffset.z);
38092
- transformMap.set(object, matrix);
38245
+ const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
38246
+ target.add(localOffset);
38093
38247
  }
38094
- object.children
38095
- .filter((x) => !x.userData.isOptimized)
38096
- .forEach((x) => explodeObject(x, depth + 1));
38097
- }
38098
- explodeObject(explodeRoot, 0);
38099
- 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
+ });
38100
38256
  this.gltfLoader.applyObjectTransforms(transformMap);
38257
+ this.scene.updateMatrixWorld();
38101
38258
  return this;
38102
38259
  }
38103
38260
  }
@@ -38823,6 +38980,8 @@ void main() {
38823
38980
  this.oldOptimizeObjects = new Set();
38824
38981
  this.objectTransforms = new Map();
38825
38982
  this.transformedGeometries = new Map();
38983
+ this.syncTransformsToOriginalObjects = true;
38984
+ this._originalObjectMatrices = new Map();
38826
38985
  this.activeChunkLoads = 0;
38827
38986
  this.chunkQueue = [];
38828
38987
  this.objectIdToIndex = new Map();
@@ -38832,6 +38991,81 @@ void main() {
38832
38991
  this.mergedObjectMap = new Map();
38833
38992
  this.mergedGeometryVisibility = new Map();
38834
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;
38835
39069
  }
38836
39070
  setVisibleEdges(visible) {
38837
39071
  this.visibleEdges = visible;
@@ -39721,36 +39955,82 @@ void main() {
39721
39955
  }
39722
39956
  }
39723
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;
39724
39964
  material.onBeforeCompile = (shader) => {
39965
+ shader.uniforms.transformTexture = uniforms.transformTexture;
39966
+ shader.uniforms.transformTextureSize = uniforms.transformTextureSize;
39725
39967
  shader.vertexShader = shader.vertexShader.replace(
39726
39968
  "#include <common>",
39727
39969
  `
39728
39970
  #include <common>
39971
+
39729
39972
  attribute float visibility;
39973
+ attribute float objectId;
39730
39974
  varying float vVisibility;
39731
- `
39732
- );
39733
- shader.fragmentShader = shader.fragmentShader.replace(
39734
- "#include <common>",
39735
- `
39736
- #include <common>
39737
- 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
+ }
39738
39992
  `
39739
39993
  );
39740
39994
  shader.vertexShader = shader.vertexShader.replace(
39741
39995
  "void main() {",
39742
39996
  `
39743
39997
  void main() {
39744
- vVisibility = visibility;
39998
+ mat4 batchingMatrix = getTransformMatrix(objectId);
39999
+ vVisibility = visibility;
39745
40000
  `
39746
40001
  );
39747
- shader.fragmentShader = shader.fragmentShader.replace(
39748
- "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;
40009
+ `
40010
+ );
40011
+ }
40012
+ shader.vertexShader = shader.vertexShader.replace(
40013
+ "#include <begin_vertex>",
39749
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
+ `
39750
40030
  void main() {
39751
40031
  if (vVisibility < 0.5) discard;
39752
40032
  `
39753
- );
40033
+ );
39754
40034
  };
39755
40035
  material.needsUpdate = true;
39756
40036
  return material;
@@ -39856,6 +40136,7 @@ void main() {
39856
40136
  this.isolatedObjects = [];
39857
40137
  this.objectTransforms.clear();
39858
40138
  this.transformedGeometries.clear();
40139
+ this._originalObjectMatrices.clear();
39859
40140
  this.totalLoadedObjects = 0;
39860
40141
  this.currentMemoryUsage = 0;
39861
40142
  this.pendingMemoryUsage = 0;
@@ -39865,6 +40146,8 @@ void main() {
39865
40146
  this.objectIdToIndex.clear();
39866
40147
  this.maxObjectId = 0;
39867
40148
  this.objectVisibility = new Float32Array();
40149
+ this.meshToNodeMap = null;
40150
+ this.visibilityMaterials.clear();
39868
40151
  }
39869
40152
  setStructureTransform(structureId, matrix) {
39870
40153
  const rootGroup = this.structureRoots.get(structureId);
@@ -39980,12 +40263,15 @@ void main() {
39980
40263
  });
39981
40264
  this.originalObjects.clear();
39982
40265
  this.originalObjectsToSelection.clear();
40266
+ this.objectIdToIndex.clear();
40267
+ this.maxObjectId = 0;
39983
40268
  const structureGroups = new Map();
39984
40269
  this.dispatchEvent("optimizationprogress", {
39985
40270
  phase: "collecting",
39986
40271
  progress: 5,
39987
40272
  message: "Collecting scene objects...",
39988
40273
  });
40274
+ let totalObjectsToMerge = 0;
39989
40275
  this.scene.traverse((object) => {
39990
40276
  if (object.userData.structureId) {
39991
40277
  const structureId = object.userData.structureId;
@@ -40003,17 +40289,32 @@ void main() {
40003
40289
  });
40004
40290
  }
40005
40291
  const group = structureGroups.get(structureId);
40292
+ let added = false;
40006
40293
  if (object instanceof Mesh) {
40007
40294
  this.addToMaterialGroup(object, group.mapMeshes, group.meshes);
40295
+ added = true;
40008
40296
  } else if (object instanceof LineSegments) {
40009
40297
  this.addToMaterialGroup(object, group.mapLineSegments, group.lineSegments);
40298
+ added = true;
40010
40299
  } else if (object instanceof Line$1) {
40011
40300
  this.addToMaterialGroup(object, group.mapLines, group.lines);
40301
+ added = true;
40012
40302
  } else if (object instanceof Points) {
40013
40303
  this.addToMaterialGroup(object, group.mapPoints, group.points);
40304
+ added = true;
40305
+ }
40306
+ if (added) {
40307
+ totalObjectsToMerge++;
40014
40308
  }
40015
40309
  }
40016
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
+ }
40017
40318
  let processedGroups = 0;
40018
40319
  const totalGroups = structureGroups.size;
40019
40320
  this.dispatchEvent("optimizationprogress", {
@@ -40058,7 +40359,6 @@ void main() {
40058
40359
  this.originalObjectsToSelection.add(obj);
40059
40360
  }
40060
40361
  });
40061
- this.initializeObjectVisibility();
40062
40362
  console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
40063
40363
  this.dispatchEvent("optimizationprogress", {
40064
40364
  phase: "complete",
@@ -40127,6 +40427,7 @@ void main() {
40127
40427
  }
40128
40428
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40129
40429
  const mergedMesh = new Mesh(mergedGeometry, visibilityMaterial);
40430
+ mergedMesh.frustumCulled = false;
40130
40431
  mergedMesh.userData.isOptimized = true;
40131
40432
  rootGroup.add(mergedMesh);
40132
40433
  this.mergedMesh.add(mergedMesh);
@@ -40243,6 +40544,7 @@ void main() {
40243
40544
  geometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40244
40545
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40245
40546
  const mergedLine = new LineSegments(geometry, visibilityMaterial);
40547
+ mergedLine.frustumCulled = false;
40246
40548
  mergedLine.userData.isEdge = isEdge;
40247
40549
  mergedLine.userData.isOptimized = true;
40248
40550
  const mergedObjects = [mergedLine];
@@ -40331,6 +40633,7 @@ void main() {
40331
40633
  mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40332
40634
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40333
40635
  const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
40636
+ mergedLine.frustumCulled = false;
40334
40637
  mergedLine.userData.isEdge = isEdge;
40335
40638
  mergedLine.userData.isOptimized = true;
40336
40639
  if (this.useVAO) {
@@ -40400,7 +40703,27 @@ void main() {
40400
40703
  const mergedObjects = [];
40401
40704
  if (geometries.length > 0) {
40402
40705
  const mergedGeometry = mergeGeometries(geometries, false);
40403
- 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;
40404
40727
  mergedPoints.userData.isOptimized = true;
40405
40728
  if (this.useVAO) {
40406
40729
  this.createVAO(mergedPoints);
@@ -40469,13 +40792,33 @@ void main() {
40469
40792
  geometriesWithIndex.push(clonedGeometry);
40470
40793
  });
40471
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));
40472
40813
  const material = new LineBasicMaterial({
40473
40814
  vertexColors: true,
40474
40815
  });
40816
+ const visibilityMaterial = this.createVisibilityMaterial(material);
40475
40817
  if (this.useVAO) {
40476
40818
  this.createVAO(finalGeometry);
40477
40819
  }
40478
- const mergedLine = new LineSegments(finalGeometry, material);
40820
+ const mergedLine = new LineSegments(finalGeometry, visibilityMaterial);
40821
+ mergedLine.frustumCulled = false;
40479
40822
  mergedLine.userData.structureId = structureId;
40480
40823
  mergedLine.userData.isOptimized = true;
40481
40824
  rootGroup.add(mergedLine);
@@ -40594,18 +40937,91 @@ void main() {
40594
40937
  console.warn("No merged objects to transform");
40595
40938
  return;
40596
40939
  }
40597
- this.objectTransforms = new Map(objectTransformMap);
40598
- for (const mesh of this.mergedMesh) {
40599
- this._applyTransformToMergedObject(mesh);
40940
+ if (!this.transformData) {
40941
+ console.warn("Transform texture not initialized");
40942
+ return;
40600
40943
  }
40601
- for (const line of this.mergedLines) {
40602
- 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
+ }
40603
40975
  }
40604
- for (const lineSegment of this.mergedLineSegments) {
40605
- 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
+ }
40606
40984
  }
40607
- for (const point of this.mergedPoints) {
40608
- 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
+ }
40609
41025
  }
40610
41026
  }
40611
41027
  createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
@@ -40622,21 +41038,66 @@ void main() {
40622
41038
  ? objects
40623
41039
  : Array.from(objects)
40624
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
+ }
40625
41050
  for (const obj of objectsArray) {
40626
41051
  if (!obj.geometry || !obj.geometry.attributes.position) continue;
40627
- const boundingBox = new Box3().setFromBufferAttribute(obj.geometry.attributes.position);
40628
- if (obj.matrixWorld) {
40629
- 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;
40630
41070
  }
40631
- if (boundingBox.isEmpty()) continue;
40632
- const objectCenter = new Vector3();
40633
- boundingBox.getCenter(objectCenter);
40634
- const direction = objectCenter.clone().sub(explodeCenter);
40635
- const distance = direction.length();
41071
+ const explodeVector = obj.userData.explodeVector;
41072
+ const distance = explodeVector.length();
40636
41073
  if (distance > 0) {
40637
- direction.normalize();
40638
- const offset = direction.multiplyScalar(distance * (explodeFactor - 1.0));
40639
- 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);
40640
41101
  transformMap.set(obj, matrix);
40641
41102
  }
40642
41103
  }
@@ -40644,115 +41105,19 @@ void main() {
40644
41105
  }
40645
41106
  clearTransforms() {
40646
41107
  this.objectTransforms.clear();
40647
- for (const mesh of this.mergedMesh) {
40648
- this._restoreOriginalGeometry(mesh);
40649
- }
40650
- for (const line of this.mergedLines) {
40651
- this._restoreOriginalGeometry(line);
40652
- }
40653
- for (const lineSegment of this.mergedLineSegments) {
40654
- this._restoreOriginalGeometry(lineSegment);
40655
- }
40656
- for (const point of this.mergedPoints) {
40657
- this._restoreOriginalGeometry(point);
40658
- }
40659
- }
40660
- clearHandleTransforms() {
40661
- this.clearTransforms();
40662
- }
40663
- _applyTransformToMergedObject(mergedObject) {
40664
- const objectData = this.mergedObjectMap.get(mergedObject.uuid);
40665
- if (!objectData || !objectData.objectMapping) return;
40666
- const geometry = mergedObject.geometry;
40667
- if (!geometry || !geometry.attributes.position) return;
40668
- const positionAttr = geometry.attributes.position;
40669
- const positions = positionAttr.array;
40670
- if (!this.transformedGeometries.has(mergedObject.uuid)) {
40671
- this.transformedGeometries.set(mergedObject.uuid, new Float32Array(positions));
40672
- }
40673
- const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
40674
- const tempVector = new Vector3();
40675
- for (const [originalMesh, mappingData] of objectData.objectMapping) {
40676
- const transform = this.objectTransforms.get(originalMesh);
40677
- if (!transform) {
40678
- const startIdx = mappingData.startVertexIndex * 3;
40679
- const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
40680
- for (let i = startIdx; i < endIdx; i++) {
40681
- positions[i] = originalPositions[i];
40682
- }
40683
- continue;
40684
- }
40685
- const startVertex = mappingData.startVertexIndex;
40686
- const vertexCount = mappingData.vertexCount;
40687
- for (let i = 0; i < vertexCount; i++) {
40688
- const idx = (startVertex + i) * 3;
40689
- tempVector.set(originalPositions[idx], originalPositions[idx + 1], originalPositions[idx + 2]);
40690
- tempVector.applyMatrix4(transform);
40691
- positions[idx] = tempVector.x;
40692
- positions[idx + 1] = tempVector.y;
40693
- positions[idx + 2] = tempVector.z;
40694
- }
40695
- }
40696
- if (geometry.attributes.normal) {
40697
- this._updateNormalsForTransform(geometry, objectData, originalPositions);
40698
- }
40699
- positionAttr.needsUpdate = true;
40700
- geometry.computeBoundingSphere();
40701
- geometry.computeBoundingBox();
40702
- }
40703
- _updateNormalsForTransform(geometry, objectData, originalPositions) {
40704
- const normalAttr = geometry.attributes.normal;
40705
- if (!normalAttr) return;
40706
- const normals = normalAttr.array;
40707
- const tempVector = new Vector3();
40708
- const normalMatrix = new Matrix4();
40709
- const normalsKey = `${geometry.uuid}_normals`;
40710
- if (!this.transformedGeometries.has(normalsKey)) {
40711
- this.transformedGeometries.set(normalsKey, new Float32Array(normals));
40712
- }
40713
- const originalNormals = this.transformedGeometries.get(normalsKey);
40714
- for (const [originalMesh, mappingData] of objectData.objectMapping) {
40715
- const transform = this.objectTransforms.get(originalMesh);
40716
- if (!transform) {
40717
- const startIdx = mappingData.startVertexIndex * 3;
40718
- const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
40719
- for (let i = startIdx; i < endIdx; i++) {
40720
- 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);
40721
41114
  }
40722
- continue;
40723
- }
40724
- normalMatrix.copy(transform).invert().transpose();
40725
- const startVertex = mappingData.startVertexIndex;
40726
- const vertexCount = mappingData.vertexCount;
40727
- for (let i = 0; i < vertexCount; i++) {
40728
- const idx = (startVertex + i) * 3;
40729
- tempVector.set(originalNormals[idx], originalNormals[idx + 1], originalNormals[idx + 2]);
40730
- tempVector.applyMatrix4(normalMatrix).normalize();
40731
- normals[idx] = tempVector.x;
40732
- normals[idx + 1] = tempVector.y;
40733
- normals[idx + 2] = tempVector.z;
40734
41115
  }
41116
+ this._originalObjectMatrices.clear();
40735
41117
  }
40736
- normalAttr.needsUpdate = true;
40737
41118
  }
40738
- _restoreOriginalGeometry(mergedObject) {
40739
- const geometry = mergedObject.geometry;
40740
- if (!geometry || !geometry.attributes.position) return;
40741
- const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
40742
- if (originalPositions) {
40743
- const positions = geometry.attributes.position.array;
40744
- positions.set(originalPositions);
40745
- geometry.attributes.position.needsUpdate = true;
40746
- }
40747
- const normalsKey = `${geometry.uuid}_normals`;
40748
- const originalNormals = this.transformedGeometries.get(normalsKey);
40749
- if (originalNormals && geometry.attributes.normal) {
40750
- const normals = geometry.attributes.normal.array;
40751
- normals.set(originalNormals);
40752
- geometry.attributes.normal.needsUpdate = true;
40753
- }
40754
- geometry.computeBoundingSphere();
40755
- geometry.computeBoundingBox();
41119
+ clearHandleTransforms() {
41120
+ this.clearTransforms();
40756
41121
  }
40757
41122
  syncHiddenObjects() {
40758
41123
  if (this.mergedObjectMap.size === 0) {