@inweb/viewer-three 27.2.3 → 27.2.4

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.
@@ -23,7 +23,7 @@
23
23
 
24
24
  import { draggersRegistry, commandsRegistry, Options, componentsRegistry, Info, Loader, loadersRegistry, CANVAS_EVENTS } from '@inweb/viewer-core';
25
25
  export * from '@inweb/viewer-core';
26
- import { EventDispatcher, Vector3, MOUSE, TOUCH, Spherical, Quaternion, Vector2, Object3D, BufferGeometry, Float32BufferAttribute, Line, LineBasicMaterial, Mesh, MeshBasicMaterial, DoubleSide, Line3, Raycaster, MathUtils, EdgesGeometry, Plane, Matrix4, Vector4, Controls, Box3, Clock, Color, PerspectiveCamera, OrthographicCamera, AmbientLight, DirectionalLight, HemisphereLight, REVISION, MeshPhongMaterial, WebGLRenderTarget, UnsignedByteType, RGBAFormat, CylinderGeometry, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial, TextureLoader, BufferAttribute, PointsMaterial, DataTexture, FloatType, NearestFilter, Points, TriangleStripDrawMode, TriangleFanDrawMode, LineSegments, LineLoop, Group, NormalBlending, LoadingManager, LoaderUtils, FileLoader, UniformsUtils, ShaderMaterial, AdditiveBlending, HalfFloatType, Scene, WebGLRenderer, LinearSRGBColorSpace } from 'three';
26
+ import { Line, BufferGeometry, Float32BufferAttribute, LineBasicMaterial, Mesh, MeshBasicMaterial, DoubleSide, EventDispatcher, Vector3, MOUSE, TOUCH, Spherical, Quaternion, Vector2, Plane, Object3D, Line3, Raycaster, MathUtils, EdgesGeometry, Matrix4, Vector4, Controls, Clock, Box3, Color, PerspectiveCamera, OrthographicCamera, AmbientLight, DirectionalLight, HemisphereLight, REVISION, MeshPhongMaterial, WebGLRenderTarget, UnsignedByteType, RGBAFormat, CylinderGeometry, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial, TextureLoader, BufferAttribute, PointsMaterial, Points, TriangleStripDrawMode, TriangleFanDrawMode, LineSegments, LineLoop, Group, NormalBlending, LoadingManager, LoaderUtils, FileLoader, UniformsUtils, ShaderMaterial, AdditiveBlending, HalfFloatType, Scene, WebGLRenderer, LinearSRGBColorSpace } from 'three';
27
27
  import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
28
28
  import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js';
29
29
  import { Wireframe } from 'three/examples/jsm/lines/Wireframe.js';
@@ -41,6 +41,41 @@ import { EventEmitter2 } from '@inweb/eventemitter2';
41
41
  import { Markup } from '@inweb/markup';
42
42
  export * from '@inweb/markup';
43
43
 
44
+ class PlaneHelper2 extends Line {
45
+ constructor(size = 1, color = 0xc0c0c0) {
46
+ const positions = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0];
47
+ const geometry = new BufferGeometry();
48
+ geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
49
+ geometry.computeBoundingSphere();
50
+ super(geometry, new LineBasicMaterial({ color, toneMapped: false }));
51
+ this.type = "PlaneHelper2";
52
+ this.size = size;
53
+ const positions2 = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0];
54
+ const geometry2 = new BufferGeometry();
55
+ geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3));
56
+ geometry2.computeBoundingSphere();
57
+ this.helper = new Mesh(geometry2, new MeshBasicMaterial({
58
+ color,
59
+ opacity: 0.2,
60
+ transparent: true,
61
+ depthWrite: false,
62
+ toneMapped: false,
63
+ side: DoubleSide,
64
+ }));
65
+ this.add(this.helper);
66
+ }
67
+ dispose() {
68
+ this.geometry.dispose();
69
+ this.material.dispose();
70
+ this.helper.geometry.dispose();
71
+ this.helper.material.dispose();
72
+ }
73
+ updateMatrixWorld(force) {
74
+ this.scale.set(0.5 * this.size, 0.5 * this.size, 1);
75
+ super.updateMatrixWorld(force);
76
+ }
77
+ }
78
+
44
79
  const _changeEvent = { type: "change" };
45
80
  const _startEvent = { type: "start" };
46
81
  const _endEvent = { type: "end" };
@@ -699,166 +734,6 @@ class OrbitControls extends EventDispatcher {
699
734
  }
700
735
  }
701
736
 
702
- class PlaneHelper2 extends Object3D {
703
- constructor(size = 1, color = 0xf0f0f0, opacity = 0.15) {
704
- super();
705
- this.type = "PlaneHelper2";
706
- this.size = size;
707
- const positions = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0];
708
- const geometry = new BufferGeometry();
709
- geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
710
- geometry.computeBoundingSphere();
711
- this.outline = new Line(geometry, new LineBasicMaterial({ color, toneMapped: false }));
712
- const positions2 = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0];
713
- const geometry2 = new BufferGeometry();
714
- geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3));
715
- geometry2.computeBoundingSphere();
716
- this.mesh = new Mesh(geometry2, new MeshBasicMaterial({
717
- color,
718
- opacity,
719
- transparent: true,
720
- depthWrite: false,
721
- toneMapped: false,
722
- side: DoubleSide,
723
- }));
724
- this.add(this.outline);
725
- this.add(this.mesh);
726
- }
727
- dispose() {
728
- this.outline.geometry.dispose();
729
- this.outline.material.dispose();
730
- this.mesh.geometry.dispose();
731
- this.mesh.material.dispose();
732
- }
733
- updateMatrixWorld(force) {
734
- this.scale.set(0.5 * this.size, 0.5 * this.size, 1);
735
- super.updateMatrixWorld(force);
736
- }
737
- getLineMaterial() {
738
- return this.outline.material;
739
- }
740
- getMeshMaterial() {
741
- return this.mesh.material;
742
- }
743
- }
744
-
745
- const DESKTOP_SNAP_DISTANCE = 10;
746
- const MOBILE_SNAP_DISTANCE = 50;
747
- const _vertex = new Vector3();
748
- const _start = new Vector3();
749
- const _end = new Vector3();
750
- const _line = new Line3();
751
- const _center = new Vector3();
752
- const _projection = new Vector3();
753
- class Snapper {
754
- constructor(camera, renderer, canvas) {
755
- this.camera = camera;
756
- this.renderer = renderer;
757
- this.canvas = canvas;
758
- this.threshold = 0.0001;
759
- this.raycaster = new Raycaster();
760
- this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
761
- this.edgesCache = new WeakMap();
762
- }
763
- isMobile() {
764
- if (typeof navigator === "undefined")
765
- return false;
766
- return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
767
- }
768
- getMousePosition(event, target) {
769
- return target.set(event.clientX, event.clientY);
770
- }
771
- getPointerIntersects(mouse, objects, recursive = false, clip = true) {
772
- const rect = this.canvas.getBoundingClientRect();
773
- const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
774
- const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
775
- const coords = new Vector2(x, y);
776
- this.raycaster.setFromCamera(coords, this.camera);
777
- this.raycaster.params = {
778
- Mesh: {},
779
- Line: { threshold: this.threshold },
780
- Line2: { threshold: this.threshold },
781
- LOD: {},
782
- Points: { threshold: this.threshold },
783
- Sprite: {},
784
- };
785
- let intersects = this.raycaster.intersectObjects(objects, recursive);
786
- if (clip) {
787
- const clippingPlanes = this.renderer.clippingPlanes || [];
788
- clippingPlanes.forEach((plane) => {
789
- intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
790
- });
791
- }
792
- return intersects;
793
- }
794
- getDetectRadius(point) {
795
- const camera = this.camera;
796
- if (camera.isOrthographicCamera) {
797
- const fieldHeight = (camera.top - camera.bottom) / camera.zoom;
798
- const canvasHeight = this.canvas.height;
799
- const worldUnitsPerPixel = fieldHeight / canvasHeight;
800
- return this.detectRadiusInPixels * worldUnitsPerPixel;
801
- }
802
- if (camera.isPerspectiveCamera) {
803
- const distance = camera.position.distanceTo(point);
804
- const fieldHeight = 2 * Math.tan(MathUtils.degToRad(camera.fov * 0.5)) * distance;
805
- const canvasHeight = this.canvas.height;
806
- const worldUnitsPerPixel = fieldHeight / canvasHeight;
807
- return this.detectRadiusInPixels * worldUnitsPerPixel;
808
- }
809
- return 0.1;
810
- }
811
- getSnapPoint(mouse, objects) {
812
- const intersections = this.getPointerIntersects(mouse, objects);
813
- if (intersections.length === 0)
814
- return undefined;
815
- const object = intersections[0].object;
816
- const intersectionPoint = intersections[0].point;
817
- const localPoint = object.worldToLocal(intersectionPoint.clone());
818
- let snapPoint;
819
- let snapDistance = this.getDetectRadius(intersectionPoint);
820
- const geometry = object.geometry;
821
- const positions = geometry.attributes.position.array;
822
- for (let i = 0; i < positions.length; i += 3) {
823
- _vertex.set(positions[i], positions[i + 1], positions[i + 2]);
824
- const distance = _vertex.distanceTo(localPoint);
825
- if (distance < snapDistance) {
826
- snapDistance = distance;
827
- snapPoint = _vertex.clone();
828
- }
829
- }
830
- if (snapPoint)
831
- return object.localToWorld(snapPoint);
832
- let edges = this.edgesCache.get(geometry);
833
- if (!edges) {
834
- edges = new EdgesGeometry(geometry);
835
- this.edgesCache.set(geometry, edges);
836
- }
837
- const edgePositions = edges.attributes.position.array;
838
- for (let i = 0; i < edgePositions.length; i += 6) {
839
- _start.set(edgePositions[i], edgePositions[i + 1], edgePositions[i + 2]);
840
- _end.set(edgePositions[i + 3], edgePositions[i + 4], edgePositions[i + 5]);
841
- _line.set(_start, _end);
842
- _line.getCenter(_center);
843
- const centerDistance = _center.distanceTo(localPoint);
844
- if (centerDistance < snapDistance) {
845
- snapDistance = centerDistance;
846
- snapPoint = _center.clone();
847
- continue;
848
- }
849
- _line.closestPointToPoint(localPoint, true, _projection);
850
- const lineDistance = _projection.distanceTo(localPoint);
851
- if (lineDistance < snapDistance) {
852
- snapDistance = lineDistance;
853
- snapPoint = _projection.clone();
854
- }
855
- }
856
- if (snapPoint)
857
- return object.localToWorld(snapPoint);
858
- return intersectionPoint.clone();
859
- }
860
- }
861
-
862
737
  class OrbitDragger {
863
738
  constructor(viewer) {
864
739
  this.updateControls = () => {
@@ -951,18 +826,12 @@ class OrbitDragger {
951
826
  }
952
827
 
953
828
  class CuttingPlaneDragger extends OrbitDragger {
954
- constructor(viewer) {
829
+ constructor(viewer, normal) {
955
830
  super(viewer);
956
- this.helpers = [];
957
- this.activeHelper = null;
958
831
  this.transformChange = () => {
959
- if (!this.activeHelper)
960
- return;
961
- const plane = this.activeHelper.plane;
962
- plane.normal.copy(new Vector3(0, 0, -1)).applyQuaternion(this.activeHelper.quaternion);
963
- plane.constant = -this.activeHelper.position.dot(plane.normal);
832
+ this.plane.normal.copy(new Vector3(0, 0, -1)).applyQuaternion(this.planeCenter.quaternion);
833
+ this.plane.constant = -this.planeCenter.position.dot(this.plane.normal);
964
834
  this.viewer.update();
965
- this.changed = true;
966
835
  };
967
836
  this.translateDrag = (event) => {
968
837
  this.orbit.enabled = !event.value;
@@ -973,83 +842,45 @@ class CuttingPlaneDragger extends OrbitDragger {
973
842
  this.translate.enabled = !event.value;
974
843
  };
975
844
  this.updatePlaneSize = () => {
976
- const extentsSize = this.viewer.extents.getSize(new Vector3()).length() || 1;
977
- this.helpers.forEach((planeHelper) => (planeHelper.size = extentsSize));
845
+ this.planeHelper.size = this.viewer.extents.getSize(new Vector3()).length() || 1;
978
846
  this.viewer.update();
979
847
  };
980
848
  this.updateTransformCamera = () => {
981
849
  this.translate.camera = this.viewer.camera;
982
850
  this.rotate.camera = this.viewer.camera;
983
- this.snapper.camera = this.viewer.camera;
984
- };
985
- this.clearHelpers = () => {
986
- this.setActiveHelper();
987
- this.helpers.forEach((helper) => {
988
- helper.removeFromParent();
989
- helper.dispose();
990
- });
991
- this.helpers = [];
992
- this.viewer.update();
993
851
  };
994
852
  this.onKeyDown = (event) => {
995
853
  if (event.key === "Shift")
996
854
  this.rotate.setRotationSnap(Math.PI / 4);
997
- if (event.key === "Delete" || event.key === "Backspace")
998
- this.deleteActivePlane();
999
- if (event.key === "Escape" && (this.translate.dragging || this.rotate.dragging))
1000
- this.reset();
1001
855
  };
1002
856
  this.onKeyUp = (event) => {
1003
857
  if (event.key === "Shift")
1004
858
  this.rotate.setRotationSnap(null);
1005
859
  };
1006
- this.onPointerDown = (event) => {
1007
- if (event.button !== 0 || !event.isPrimary)
1008
- return;
1009
- this.snapper.getMousePosition(event, this.downPosition);
1010
- this.saveState();
1011
- };
1012
- this.onPointerUp = (event) => {
1013
- if (event.button !== 0)
1014
- return;
1015
- const upPosition = this.snapper.getMousePosition(event, new Vector2());
1016
- if (upPosition.distanceTo(this.downPosition) !== 0)
1017
- return;
1018
- const intersects = this.snapper.getPointerIntersects(upPosition, this.helpers, true, false);
1019
- if (intersects.length === 0)
1020
- return;
1021
- this.setActiveHelper(intersects[0].object.parent);
1022
- };
1023
- this.onPointerCancel = (event) => {
1024
- this.viewer.canvas.dispatchEvent(new PointerEvent("pointerup", event));
1025
- };
1026
860
  this.onDoubleClick = (event) => {
1027
- if (!this.activeHelper)
1028
- return;
1029
- const mousePosition = this.snapper.getMousePosition(event, new Vector2());
1030
- const intersects = this.snapper.getPointerIntersects(mousePosition, [this.activeHelper], true, false);
1031
- if (intersects.length === 0)
1032
- return;
1033
- this.activeHelper.rotateOnAxis(new Vector3(0, 1, 0), Math.PI);
1034
- this.transformChange();
1035
861
  event.stopPropagation();
862
+ this.planeCenter.rotateOnAxis(new Vector3(0, 1, 0), Math.PI);
863
+ this.transformChange();
1036
864
  };
865
+ const extentsSize = viewer.extents.getSize(new Vector3()).length() || 1;
866
+ const extentsCenter = viewer.extents.getCenter(new Vector3());
867
+ const constant = -extentsCenter.dot(normal);
868
+ this.plane = new Plane(normal, constant);
1037
869
  if (!viewer.renderer.clippingPlanes)
1038
870
  viewer.renderer.clippingPlanes = [];
1039
- this.clippingPlanes = viewer.renderer.clippingPlanes;
1040
- this.clippingPlanes.forEach((plane) => this.addHelper(plane));
1041
- const extentsSize = viewer.extents.getSize(new Vector3()).length() || 1;
1042
- this.snapper = new Snapper(viewer.camera, viewer.renderer, viewer.canvas);
1043
- this.snapper.threshold = extentsSize / 10000;
1044
- this.downPosition = new Vector2();
1045
- this.position0 = new Vector3();
1046
- this.quaternion0 = new Quaternion();
871
+ viewer.renderer.clippingPlanes.push(this.plane);
872
+ this.planeCenter = new Object3D();
873
+ this.planeCenter.position.copy(extentsCenter);
874
+ this.planeCenter.quaternion.setFromUnitVectors(new Vector3(0, 0, -1), normal);
875
+ this.viewer.helpers.add(this.planeCenter);
876
+ this.planeHelper = new PlaneHelper2(extentsSize);
877
+ this.planeCenter.add(this.planeHelper);
1047
878
  this.translate = new TransformControls(viewer.camera, viewer.canvas);
1048
- this.translate.setMode("translate");
1049
879
  this.translate.setSpace("local");
1050
880
  this.translate.showX = false;
1051
881
  this.translate.showY = false;
1052
882
  this.translate.showZ = true;
883
+ this.translate.attach(this.planeCenter);
1053
884
  this.translate.addEventListener("change", this.transformChange);
1054
885
  this.translate.addEventListener("dragging-changed", this.translateDrag);
1055
886
  this.viewer.helpers.add(this.translate.getHelper());
@@ -1059,18 +890,14 @@ class CuttingPlaneDragger extends OrbitDragger {
1059
890
  this.rotate.showX = true;
1060
891
  this.rotate.showY = true;
1061
892
  this.rotate.showZ = false;
893
+ this.rotate.attach(this.planeCenter);
1062
894
  this.rotate.addEventListener("change", this.transformChange);
1063
895
  this.rotate.addEventListener("dragging-changed", this.rotateDrag);
1064
896
  this.viewer.helpers.add(this.rotate.getHelper());
1065
- this.setActiveHelper(this.helpers[this.helpers.length - 1]);
1066
897
  this.viewer.addEventListener("explode", this.updatePlaneSize);
1067
898
  this.viewer.addEventListener("show", this.updatePlaneSize);
1068
899
  this.viewer.addEventListener("showall", this.updatePlaneSize);
1069
900
  this.viewer.addEventListener("changecameramode", this.updateTransformCamera);
1070
- this.viewer.addEventListener("clearslices", this.clearHelpers);
1071
- this.viewer.canvas.addEventListener("pointerdown", this.onPointerDown, true);
1072
- this.viewer.canvas.addEventListener("pointerup", this.onPointerUp, true);
1073
- this.viewer.canvas.addEventListener("pointercancel", this.onPointerCancel, true);
1074
901
  this.viewer.canvas.addEventListener("dblclick", this.onDoubleClick, true);
1075
902
  window.addEventListener("keydown", this.onKeyDown);
1076
903
  window.addEventListener("keyup", this.onKeyUp);
@@ -1081,10 +908,6 @@ class CuttingPlaneDragger extends OrbitDragger {
1081
908
  this.viewer.removeEventListener("show", this.updatePlaneSize);
1082
909
  this.viewer.removeEventListener("showall", this.updatePlaneSize);
1083
910
  this.viewer.removeEventListener("changecameramode", this.updateTransformCamera);
1084
- this.viewer.removeEventListener("clearslices", this.clearHelpers);
1085
- this.viewer.canvas.removeEventListener("pointerdown", this.onPointerDown, true);
1086
- this.viewer.canvas.removeEventListener("pointerup", this.onPointerUp, true);
1087
- this.viewer.canvas.removeEventListener("pointercancel", this.onPointerCancel, true);
1088
911
  this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
1089
912
  window.removeEventListener("keydown", this.onKeyDown);
1090
913
  window.removeEventListener("keyup", this.onKeyUp);
@@ -1098,108 +921,28 @@ class CuttingPlaneDragger extends OrbitDragger {
1098
921
  this.rotate.getHelper().removeFromParent();
1099
922
  this.rotate.detach();
1100
923
  this.rotate.dispose();
1101
- this.helpers.forEach((helper) => {
1102
- helper.removeFromParent();
1103
- helper.dispose();
1104
- });
1105
- this.helpers = [];
1106
- this.activeHelper = null;
924
+ this.planeHelper.removeFromParent();
925
+ this.planeHelper.dispose();
926
+ this.planeCenter.removeFromParent();
1107
927
  super.dispose();
1108
928
  }
1109
- addHelper(plane) {
1110
- const extentsSize = this.viewer.extents.getSize(new Vector3()).length() || 1;
1111
- const extentsCenter = this.viewer.extents.getCenter(new Vector3());
1112
- const helper = new PlaneHelper2(extentsSize);
1113
- helper.plane = plane;
1114
- helper.position.copy(plane.projectPoint(extentsCenter, new Vector3()));
1115
- helper.quaternion.setFromUnitVectors(new Vector3(0, 0, -1), plane.normal);
1116
- this.helpers.push(helper);
1117
- this.viewer.helpers.add(helper);
1118
- return helper;
1119
- }
1120
- setActiveHelper(helper) {
1121
- if (helper === this.activeHelper)
1122
- return;
1123
- if (this.activeHelper) {
1124
- this.activeHelper.getLineMaterial().color.setHex(0xf0f0f0);
1125
- this.activeHelper.getMeshMaterial().opacity = 0.15;
1126
- this.translate.detach();
1127
- this.rotate.detach();
1128
- }
1129
- this.activeHelper = helper;
1130
- if (this.activeHelper) {
1131
- this.activeHelper.getLineMaterial().color.setHex(0xd0d0d0);
1132
- this.activeHelper.getMeshMaterial().opacity = 0.3;
1133
- this.translate.attach(this.activeHelper);
1134
- this.rotate.attach(this.activeHelper);
1135
- }
1136
- this.viewer.update();
1137
- }
1138
- saveState() {
1139
- if (!this.activeHelper)
1140
- return;
1141
- this.position0.copy(this.activeHelper.position);
1142
- this.quaternion0.copy(this.activeHelper.quaternion);
1143
- }
1144
- reset() {
1145
- if (!this.activeHelper)
1146
- return;
1147
- this.translate.dragging = false;
1148
- this.rotate.dragging = false;
1149
- this.orbit.state = STATE.NONE;
1150
- this.activeHelper.position.copy(this.position0);
1151
- this.activeHelper.quaternion.copy(this.quaternion0);
1152
- this.transformChange();
1153
- }
1154
- addPlane(normal) {
1155
- const extentsCenter = this.viewer.extents.getCenter(new Vector3());
1156
- const constant = -extentsCenter.dot(normal);
1157
- const plane = new Plane(normal, constant);
1158
- this.clippingPlanes.push(plane);
1159
- const helper = this.addHelper(plane);
1160
- this.setActiveHelper(helper);
1161
- }
1162
- addPlaneX() {
1163
- this.addPlane(new Vector3(-1, 0, 0));
1164
- }
1165
- addPlaneY() {
1166
- this.addPlane(new Vector3(0, -1, 0));
1167
- }
1168
- addPlaneZ() {
1169
- this.addPlane(new Vector3(0, 0, -1));
1170
- }
1171
- deleteActivePlane() {
1172
- if (!this.activeHelper)
1173
- return;
1174
- const helper = this.activeHelper;
1175
- const index = this.clippingPlanes.indexOf(helper.plane);
1176
- if (index !== -1)
1177
- this.clippingPlanes.splice(index, 1);
1178
- this.helpers = this.helpers.filter((x) => x !== helper);
1179
- helper.removeFromParent();
1180
- helper.dispose();
1181
- this.setActiveHelper(this.helpers[this.helpers.length - 1]);
1182
- }
1183
929
  }
1184
930
 
1185
931
  class CuttingPlaneXAxisDragger extends CuttingPlaneDragger {
1186
932
  constructor(viewer) {
1187
- super(viewer);
1188
- this.addPlaneX();
933
+ super(viewer, new Vector3(-1, 0, 0));
1189
934
  }
1190
935
  }
1191
936
 
1192
937
  class CuttingPlaneYAxisDragger extends CuttingPlaneDragger {
1193
938
  constructor(viewer) {
1194
- super(viewer);
1195
- this.addPlaneY();
939
+ super(viewer, new Vector3(0, -1, 0));
1196
940
  }
1197
941
  }
1198
942
 
1199
943
  class CuttingPlaneZAxisDragger extends CuttingPlaneDragger {
1200
944
  constructor(viewer) {
1201
- super(viewer);
1202
- this.addPlaneZ();
945
+ super(viewer, new Vector3(0, 0, -1));
1203
946
  }
1204
947
  }
1205
948
 
@@ -1276,6 +1019,120 @@ function formatDistance(distance, units, precision = 2) {
1276
1019
  }
1277
1020
  }
1278
1021
 
1022
+ const DESKTOP_SNAP_DISTANCE = 10;
1023
+ const MOBILE_SNAP_DISTANCE = 50;
1024
+ const _vertex = new Vector3();
1025
+ const _start = new Vector3();
1026
+ const _end = new Vector3();
1027
+ const _line = new Line3();
1028
+ const _center = new Vector3();
1029
+ const _projection = new Vector3();
1030
+ class Snapper {
1031
+ constructor(camera, renderer, canvas) {
1032
+ this.camera = camera;
1033
+ this.renderer = renderer;
1034
+ this.canvas = canvas;
1035
+ this.threshold = 0.0001;
1036
+ this.raycaster = new Raycaster();
1037
+ this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
1038
+ this.edgesCache = new WeakMap();
1039
+ }
1040
+ isMobile() {
1041
+ if (typeof navigator === "undefined")
1042
+ return false;
1043
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
1044
+ }
1045
+ getMousePosition(event, target) {
1046
+ return target.set(event.clientX, event.clientY);
1047
+ }
1048
+ getPointerIntersects(mouse, objects) {
1049
+ const rect = this.canvas.getBoundingClientRect();
1050
+ const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
1051
+ const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
1052
+ const coords = new Vector2(x, y);
1053
+ this.raycaster.setFromCamera(coords, this.camera);
1054
+ this.raycaster.params = {
1055
+ Mesh: {},
1056
+ Line: { threshold: this.threshold },
1057
+ Line2: { threshold: this.threshold },
1058
+ LOD: {},
1059
+ Points: { threshold: this.threshold },
1060
+ Sprite: {},
1061
+ };
1062
+ let intersects = this.raycaster.intersectObjects(objects, false);
1063
+ (this.renderer.clippingPlanes || []).forEach((plane) => {
1064
+ intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
1065
+ });
1066
+ return intersects;
1067
+ }
1068
+ getDetectRadius(point) {
1069
+ const camera = this.camera;
1070
+ if (camera.isOrthographicCamera) {
1071
+ const fieldHeight = (camera.top - camera.bottom) / camera.zoom;
1072
+ const canvasHeight = this.canvas.height;
1073
+ const worldUnitsPerPixel = fieldHeight / canvasHeight;
1074
+ return this.detectRadiusInPixels * worldUnitsPerPixel;
1075
+ }
1076
+ if (camera.isPerspectiveCamera) {
1077
+ const distance = camera.position.distanceTo(point);
1078
+ const fieldHeight = 2 * Math.tan(MathUtils.degToRad(camera.fov * 0.5)) * distance;
1079
+ const canvasHeight = this.canvas.height;
1080
+ const worldUnitsPerPixel = fieldHeight / canvasHeight;
1081
+ return this.detectRadiusInPixels * worldUnitsPerPixel;
1082
+ }
1083
+ return 0.1;
1084
+ }
1085
+ getSnapPoint(mouse, objects) {
1086
+ const intersections = this.getPointerIntersects(mouse, objects);
1087
+ if (intersections.length === 0)
1088
+ return undefined;
1089
+ const object = intersections[0].object;
1090
+ const intersectionPoint = intersections[0].point;
1091
+ const localPoint = object.worldToLocal(intersectionPoint.clone());
1092
+ let snapPoint;
1093
+ let snapDistance = this.getDetectRadius(intersectionPoint);
1094
+ const geometry = object.geometry;
1095
+ const positions = geometry.attributes.position.array;
1096
+ for (let i = 0; i < positions.length; i += 3) {
1097
+ _vertex.set(positions[i], positions[i + 1], positions[i + 2]);
1098
+ const distance = _vertex.distanceTo(localPoint);
1099
+ if (distance < snapDistance) {
1100
+ snapDistance = distance;
1101
+ snapPoint = _vertex.clone();
1102
+ }
1103
+ }
1104
+ if (snapPoint)
1105
+ return object.localToWorld(snapPoint);
1106
+ let edges = this.edgesCache.get(geometry);
1107
+ if (!edges) {
1108
+ edges = new EdgesGeometry(geometry);
1109
+ this.edgesCache.set(geometry, edges);
1110
+ }
1111
+ const edgePositions = edges.attributes.position.array;
1112
+ for (let i = 0; i < edgePositions.length; i += 6) {
1113
+ _start.set(edgePositions[i], edgePositions[i + 1], edgePositions[i + 2]);
1114
+ _end.set(edgePositions[i + 3], edgePositions[i + 4], edgePositions[i + 5]);
1115
+ _line.set(_start, _end);
1116
+ _line.getCenter(_center);
1117
+ const centerDistance = _center.distanceTo(localPoint);
1118
+ if (centerDistance < snapDistance) {
1119
+ snapDistance = centerDistance;
1120
+ snapPoint = _center.clone();
1121
+ continue;
1122
+ }
1123
+ _line.closestPointToPoint(localPoint, true, _projection);
1124
+ const lineDistance = _projection.distanceTo(localPoint);
1125
+ if (lineDistance < snapDistance) {
1126
+ snapDistance = lineDistance;
1127
+ snapPoint = _projection.clone();
1128
+ }
1129
+ }
1130
+ if (snapPoint)
1131
+ return object.localToWorld(snapPoint);
1132
+ return intersectionPoint.clone();
1133
+ }
1134
+ }
1135
+
1279
1136
  const _downPoint = new Vector2();
1280
1137
  class MeasureLineDragger extends OrbitDragger {
1281
1138
  constructor(viewer) {
@@ -1687,12 +1544,6 @@ class WalkControls extends Controls {
1687
1544
  this.movementSpeed = 0.1;
1688
1545
  this.multiplier = 3;
1689
1546
  this.groundFollowingSkippedFrames = 0;
1690
- this.GROUND_BOX_HALF_SIZE = 20;
1691
- this.GROUND_BOX_REFRESH_THRESHOLD = 0.3;
1692
- this._groundObjectBoxes = new Map();
1693
- this._activeGroundObjects = [];
1694
- this._groundBox = new Box3();
1695
- this._groundBoxCenter = new Vector3();
1696
1547
  this.moveWheel = 0;
1697
1548
  this.mouseDragOn = false;
1698
1549
  this._up = new Vector3();
@@ -1759,20 +1610,11 @@ class WalkControls extends Controls {
1759
1610
  }
1760
1611
  };
1761
1612
  this.onKeyUp = (event) => {
1762
- if (this.moveKeys.delete(event.code)) {
1763
- if (this.moveKeys.size === 0) {
1764
- this._rebuildGroundBox(this.object.position);
1765
- }
1613
+ if (this.moveKeys.delete(event.code))
1766
1614
  this.update();
1767
- }
1768
1615
  };
1769
1616
  this.camera = camera;
1770
1617
  this.groundObjects = groundObjects;
1771
- for (const obj of groundObjects) {
1772
- this._groundObjectBoxes.set(obj, new Box3().setFromObject(obj));
1773
- }
1774
- const pos = this.object.position;
1775
- this._rebuildGroundBox(pos);
1776
1618
  this.raycaster = new Raycaster();
1777
1619
  this.raycaster.near = 0;
1778
1620
  this.raycaster.far = this.EYE_HEIGHT + this.FAILING_DISTANCE;
@@ -1807,29 +1649,10 @@ class WalkControls extends Controls {
1807
1649
  window.removeEventListener("keyup", this.onKeyUp);
1808
1650
  super.dispose();
1809
1651
  }
1810
- _rebuildGroundBox(center) {
1811
- const h = this.GROUND_BOX_HALF_SIZE;
1812
- this._groundBoxCenter.copy(center);
1813
- 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));
1814
- this._activeGroundObjects = this.groundObjects.filter((obj) => {
1815
- const objectBox = this._groundObjectBoxes.get(obj);
1816
- return objectBox !== undefined && this._groundBox.intersectsBox(objectBox);
1817
- });
1818
- }
1819
- _needsGroundBoxRebuild(pos) {
1820
- if (this._activeGroundObjects.length === 0 && this.groundObjects.length > 0)
1821
- return true;
1822
- const threshold = this.GROUND_BOX_HALF_SIZE * this.GROUND_BOX_REFRESH_THRESHOLD;
1823
- return (Math.abs(pos.x - this._groundBoxCenter.x) > threshold || Math.abs(pos.z - this._groundBoxCenter.z) > threshold);
1824
- }
1825
1652
  updateGroundFollowing() {
1826
- const pos = this.object.position;
1827
- if (this._needsGroundBoxRebuild(pos)) {
1828
- this._rebuildGroundBox(pos);
1829
- }
1830
1653
  this._up.copy(this.camera.up).negate();
1831
- this.raycaster.set(pos, this._up);
1832
- const intersects = this.raycaster.intersectObjects(this._activeGroundObjects, false);
1654
+ this.raycaster.set(this.object.position, this._up);
1655
+ const intersects = this.raycaster.intersectObjects(this.groundObjects, false);
1833
1656
  if (intersects.length > 0) {
1834
1657
  const groundY = intersects[0].point.y;
1835
1658
  const targetY = groundY + this.EYE_HEIGHT;
@@ -2381,7 +2204,6 @@ draggers.registerDragger("Pan", (viewer) => new PanDragger(viewer));
2381
2204
  draggers.registerDragger("Orbit", (viewer) => new OrbitDragger(viewer));
2382
2205
  draggers.registerDragger("Zoom", (viewer) => new ZoomDragger(viewer));
2383
2206
  draggers.registerDragger("MeasureLine", (viewer) => new MeasureLineDragger(viewer));
2384
- draggers.registerDragger("CuttingPlane", (viewer) => new CuttingPlaneDragger(viewer));
2385
2207
  draggers.registerDragger("CuttingPlaneXAxis", (viewer) => new CuttingPlaneXAxisDragger(viewer));
2386
2208
  draggers.registerDragger("CuttingPlaneYAxis", (viewer) => new CuttingPlaneYAxisDragger(viewer));
2387
2209
  draggers.registerDragger("CuttingPlaneZAxis", (viewer) => new CuttingPlaneZAxisDragger(viewer));
@@ -3277,7 +3099,7 @@ class SelectionComponent {
3277
3099
  this.getMousePosition(event, this.downPosition);
3278
3100
  };
3279
3101
  this.onPointerUp = (event) => {
3280
- if (!event.isPrimary || event.button !== 0)
3102
+ if (!event.isPrimary)
3281
3103
  return;
3282
3104
  const upPosition = this.getMousePosition(event, new Vector2());
3283
3105
  if (upPosition.distanceTo(this.downPosition) !== 0)
@@ -4591,81 +4413,6 @@ class DynamicGltfLoader {
4591
4413
  this.mergedObjectMap = new Map();
4592
4414
  this.mergedGeometryVisibility = new Map();
4593
4415
  this._webglInfoCache = null;
4594
- this.transformTextureSize = 1024;
4595
- this.transformTexture = this.createDummyTexture();
4596
- this.transformData = null;
4597
- this.identityTransformData = null;
4598
- this.visibilityMaterials = new Set();
4599
- }
4600
- createDummyTexture() {
4601
- const data = new Float32Array(16);
4602
- const identity = new Matrix4();
4603
- identity.toArray(data);
4604
- const dummyData = new Float32Array(16);
4605
- identity.toArray(dummyData);
4606
- const dummyTexture = new DataTexture(dummyData, 4, 1, RGBAFormat, FloatType);
4607
- dummyTexture.minFilter = NearestFilter;
4608
- dummyTexture.magFilter = NearestFilter;
4609
- dummyTexture.needsUpdate = true;
4610
- return dummyTexture;
4611
- }
4612
- initTransformTexture() {
4613
- if (this.transformTexture) {
4614
- this.transformTexture.dispose();
4615
- }
4616
- const maxInstanceCount = this.maxObjectId + 1;
4617
- let size = Math.sqrt(maxInstanceCount * 4);
4618
- size = Math.ceil(size / 4) * 4;
4619
- size = Math.max(size, 4);
4620
- this.transformTextureSize = size;
4621
- const arraySize = size * size * 4;
4622
- this.transformData = new Float32Array(arraySize);
4623
- this.identityTransformData = new Float32Array(arraySize);
4624
- for (let i = 0; i <= this.maxObjectId; i++) {
4625
- const base = i * 16;
4626
- if (base + 15 < arraySize) {
4627
- this.identityTransformData[base + 0] = 1;
4628
- this.identityTransformData[base + 5] = 1;
4629
- this.identityTransformData[base + 10] = 1;
4630
- this.identityTransformData[base + 15] = 1;
4631
- }
4632
- }
4633
- this._resetTransformData(false);
4634
- this.transformTexture = new DataTexture(this.transformData, size, size, RGBAFormat, FloatType);
4635
- this.transformTexture.needsUpdate = true;
4636
- this.transformTexture.generateMipmaps = false;
4637
- console.log(`Initialized transform texture: ${size}x${size} for ${maxInstanceCount} objects`);
4638
- this.updateMaterialUniforms();
4639
- this.visibilityMaterials.forEach((material) => {
4640
- material.needsUpdate = true;
4641
- });
4642
- }
4643
- _resetTransformData(updateTexture = true) {
4644
- if (!this.transformData || !this.identityTransformData) return;
4645
- this.transformData.set(this.identityTransformData);
4646
- if (updateTexture) {
4647
- this.updateTransformTexture();
4648
- }
4649
- }
4650
- updateMaterialUniforms() {
4651
- if (
4652
- this._lastTransformTexture === this.transformTexture &&
4653
- this._lastTransformTextureSize === this.transformTextureSize
4654
- ) {
4655
- return;
4656
- }
4657
- this._lastTransformTexture = this.transformTexture;
4658
- this._lastTransformTextureSize = this.transformTextureSize;
4659
- this.visibilityMaterials.forEach((material) => {
4660
- if (material.userData && material.userData.visibilityUniforms) {
4661
- material.userData.visibilityUniforms.transformTexture.value = this.transformTexture;
4662
- material.userData.visibilityUniforms.transformTextureSize.value = this.transformTextureSize;
4663
- }
4664
- });
4665
- }
4666
- updateTransformTexture() {
4667
- if (!this.transformTexture) return;
4668
- this.transformTexture.needsUpdate = true;
4669
4416
  }
4670
4417
  setVisibleEdges(visible) {
4671
4418
  this.visibleEdges = visible;
@@ -5555,82 +5302,36 @@ class DynamicGltfLoader {
5555
5302
  }
5556
5303
  }
5557
5304
  createVisibilityMaterial(material) {
5558
- this.visibilityMaterials.add(material);
5559
- const uniforms = {
5560
- transformTexture: { value: this.transformTexture },
5561
- transformTextureSize: { value: this.transformTextureSize },
5562
- };
5563
- material.userData.visibilityUniforms = uniforms;
5564
5305
  material.onBeforeCompile = (shader) => {
5565
- shader.uniforms.transformTexture = uniforms.transformTexture;
5566
- shader.uniforms.transformTextureSize = uniforms.transformTextureSize;
5567
5306
  shader.vertexShader = shader.vertexShader.replace(
5568
5307
  "#include <common>",
5569
5308
  `
5570
5309
  #include <common>
5571
-
5572
5310
  attribute float visibility;
5573
- attribute float objectId;
5574
5311
  varying float vVisibility;
5575
- uniform highp sampler2D transformTexture;
5576
- uniform float transformTextureSize;
5577
-
5578
- mat4 getTransformMatrix(float instanceId) {
5579
- int size = int(transformTextureSize);
5580
- int index = int(instanceId) * 4;
5581
-
5582
- int x0 = index % size;
5583
- int y0 = index / size;
5584
-
5585
- vec4 row0 = texelFetch(transformTexture, ivec2(x0, y0), 0);
5586
- vec4 row1 = texelFetch(transformTexture, ivec2(x0 + 1, y0), 0);
5587
- vec4 row2 = texelFetch(transformTexture, ivec2(x0 + 2, y0), 0);
5588
- vec4 row3 = texelFetch(transformTexture, ivec2(x0 + 3, y0), 0);
5589
-
5590
- return mat4(row0, row1, row2, row3);
5591
- }
5592
5312
  `
5593
5313
  );
5594
- shader.vertexShader = shader.vertexShader.replace(
5595
- "void main() {",
5314
+ shader.fragmentShader = shader.fragmentShader.replace(
5315
+ "#include <common>",
5596
5316
  `
5597
- void main() {
5598
- mat4 batchingMatrix = getTransformMatrix(objectId);
5599
- vVisibility = visibility;
5317
+ #include <common>
5318
+ varying float vVisibility;
5600
5319
  `
5601
5320
  );
5602
- if (shader.vertexShader.includes("#include <beginnormal_vertex>")) {
5603
- shader.vertexShader = shader.vertexShader.replace(
5604
- "#include <beginnormal_vertex>",
5605
- `
5606
- vec3 objectNormal = vec3( normal );
5607
- mat3 bm = mat3( batchingMatrix );
5608
- objectNormal = bm * objectNormal;
5609
- `
5610
- );
5611
- }
5612
5321
  shader.vertexShader = shader.vertexShader.replace(
5613
- "#include <begin_vertex>",
5322
+ "void main() {",
5614
5323
  `
5615
- vec3 transformed = vec3( position );
5616
- transformed = ( batchingMatrix * vec4( transformed, 1.0 ) ).xyz;
5324
+ void main() {
5325
+ vVisibility = visibility;
5617
5326
  `
5618
5327
  );
5619
- shader.fragmentShader = shader.fragmentShader
5620
- .replace(
5621
- "#include <common>",
5622
- `
5623
- #include <common>
5624
- varying float vVisibility;
5328
+ shader.fragmentShader = shader.fragmentShader.replace(
5329
+ "void main() {",
5625
5330
  `
5626
- )
5627
- .replace(
5628
- "void main() {",
5629
- `
5630
5331
  void main() {
5631
5332
  if (vVisibility < 0.5) discard;
5632
5333
  `
5633
- );
5334
+ );
5634
5335
  };
5635
5336
  material.needsUpdate = true;
5636
5337
  return material;
@@ -5745,8 +5446,6 @@ class DynamicGltfLoader {
5745
5446
  this.objectIdToIndex.clear();
5746
5447
  this.maxObjectId = 0;
5747
5448
  this.objectVisibility = new Float32Array();
5748
- this.meshToNodeMap = null;
5749
- this.visibilityMaterials.clear();
5750
5449
  }
5751
5450
  setStructureTransform(structureId, matrix) {
5752
5451
  const rootGroup = this.structureRoots.get(structureId);
@@ -5862,15 +5561,12 @@ class DynamicGltfLoader {
5862
5561
  });
5863
5562
  this.originalObjects.clear();
5864
5563
  this.originalObjectsToSelection.clear();
5865
- this.objectIdToIndex.clear();
5866
- this.maxObjectId = 0;
5867
5564
  const structureGroups = new Map();
5868
5565
  this.dispatchEvent("optimizationprogress", {
5869
5566
  phase: "collecting",
5870
5567
  progress: 5,
5871
5568
  message: "Collecting scene objects...",
5872
5569
  });
5873
- let totalObjectsToMerge = 0;
5874
5570
  this.scene.traverse((object) => {
5875
5571
  if (object.userData.structureId) {
5876
5572
  const structureId = object.userData.structureId;
@@ -5888,32 +5584,17 @@ class DynamicGltfLoader {
5888
5584
  });
5889
5585
  }
5890
5586
  const group = structureGroups.get(structureId);
5891
- let added = false;
5892
5587
  if (object instanceof Mesh) {
5893
5588
  this.addToMaterialGroup(object, group.mapMeshes, group.meshes);
5894
- added = true;
5895
5589
  } else if (object instanceof LineSegments) {
5896
5590
  this.addToMaterialGroup(object, group.mapLineSegments, group.lineSegments);
5897
- added = true;
5898
5591
  } else if (object instanceof Line) {
5899
5592
  this.addToMaterialGroup(object, group.mapLines, group.lines);
5900
- added = true;
5901
5593
  } else if (object instanceof Points) {
5902
5594
  this.addToMaterialGroup(object, group.mapPoints, group.points);
5903
- added = true;
5904
- }
5905
- if (added) {
5906
- totalObjectsToMerge++;
5907
5595
  }
5908
5596
  }
5909
5597
  });
5910
- if (totalObjectsToMerge > 0) {
5911
- console.log(`Pre-allocating transform texture for ${totalObjectsToMerge} objects`);
5912
- this.maxObjectId = totalObjectsToMerge;
5913
- this.initTransformTexture();
5914
- this.initializeObjectVisibility();
5915
- this.maxObjectId = 0;
5916
- }
5917
5598
  let processedGroups = 0;
5918
5599
  const totalGroups = structureGroups.size;
5919
5600
  this.dispatchEvent("optimizationprogress", {
@@ -5958,6 +5639,7 @@ class DynamicGltfLoader {
5958
5639
  this.originalObjectsToSelection.add(obj);
5959
5640
  }
5960
5641
  });
5642
+ this.initializeObjectVisibility();
5961
5643
  console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
5962
5644
  this.dispatchEvent("optimizationprogress", {
5963
5645
  phase: "complete",
@@ -6026,7 +5708,6 @@ class DynamicGltfLoader {
6026
5708
  }
6027
5709
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
6028
5710
  const mergedMesh = new Mesh(mergedGeometry, visibilityMaterial);
6029
- mergedMesh.frustumCulled = false;
6030
5711
  mergedMesh.userData.isOptimized = true;
6031
5712
  rootGroup.add(mergedMesh);
6032
5713
  this.mergedMesh.add(mergedMesh);
@@ -6143,7 +5824,6 @@ class DynamicGltfLoader {
6143
5824
  geometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
6144
5825
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
6145
5826
  const mergedLine = new LineSegments(geometry, visibilityMaterial);
6146
- mergedLine.frustumCulled = false;
6147
5827
  mergedLine.userData.isEdge = isEdge;
6148
5828
  mergedLine.userData.isOptimized = true;
6149
5829
  const mergedObjects = [mergedLine];
@@ -6232,7 +5912,6 @@ class DynamicGltfLoader {
6232
5912
  mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
6233
5913
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
6234
5914
  const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
6235
- mergedLine.frustumCulled = false;
6236
5915
  mergedLine.userData.isEdge = isEdge;
6237
5916
  mergedLine.userData.isOptimized = true;
6238
5917
  if (this.useVAO) {
@@ -6302,27 +5981,7 @@ class DynamicGltfLoader {
6302
5981
  const mergedObjects = [];
6303
5982
  if (geometries.length > 0) {
6304
5983
  const mergedGeometry = mergeGeometries(geometries, false);
6305
- const totalVertices = mergedGeometry.attributes.position.count;
6306
- const objectIds = new Float32Array(totalVertices);
6307
- let vertexOffset = 0;
6308
- group.objects.forEach((points) => {
6309
- const handle = points.userData.handle;
6310
- if (!this.objectIdToIndex.has(handle)) {
6311
- this.objectIdToIndex.set(handle, this.maxObjectId++);
6312
- }
6313
- const objectId = this.objectIdToIndex.get(handle);
6314
- const count = points.geometry.attributes.position.count;
6315
- for (let i = 0; i < count; i++) {
6316
- objectIds[vertexOffset++] = objectId;
6317
- }
6318
- });
6319
- mergedGeometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
6320
- const visibilityArray = new Float32Array(totalVertices);
6321
- visibilityArray.fill(1.0);
6322
- mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
6323
- const visibilityMaterial = this.createVisibilityMaterial(group.material);
6324
- const mergedPoints = new Points(mergedGeometry, visibilityMaterial);
6325
- mergedPoints.frustumCulled = false;
5984
+ const mergedPoints = new Points(mergedGeometry, group.material);
6326
5985
  mergedPoints.userData.isOptimized = true;
6327
5986
  if (this.useVAO) {
6328
5987
  this.createVAO(mergedPoints);
@@ -6391,33 +6050,13 @@ class DynamicGltfLoader {
6391
6050
  geometriesWithIndex.push(clonedGeometry);
6392
6051
  });
6393
6052
  const finalGeometry = mergeGeometries(geometriesWithIndex, false);
6394
- const totalVertices = finalGeometry.attributes.position.count;
6395
- const objectIds = new Float32Array(totalVertices);
6396
- let vertexOffset = 0;
6397
- lineSegmentsArray.forEach((segment) => {
6398
- const handle = segment.userData.handle;
6399
- if (!this.objectIdToIndex.has(handle)) {
6400
- this.objectIdToIndex.set(handle, this.maxObjectId++);
6401
- }
6402
- const objectId = this.objectIdToIndex.get(handle);
6403
- const count = segment.geometry.attributes.position.count;
6404
- for (let i = 0; i < count; i++) {
6405
- objectIds[vertexOffset++] = objectId;
6406
- }
6407
- });
6408
- finalGeometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
6409
- const visibilityArray = new Float32Array(totalVertices);
6410
- visibilityArray.fill(1.0);
6411
- finalGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
6412
6053
  const material = new LineBasicMaterial({
6413
6054
  vertexColors: true,
6414
6055
  });
6415
- const visibilityMaterial = this.createVisibilityMaterial(material);
6416
6056
  if (this.useVAO) {
6417
6057
  this.createVAO(finalGeometry);
6418
6058
  }
6419
- const mergedLine = new LineSegments(finalGeometry, visibilityMaterial);
6420
- mergedLine.frustumCulled = false;
6059
+ const mergedLine = new LineSegments(finalGeometry, material);
6421
6060
  mergedLine.userData.structureId = structureId;
6422
6061
  mergedLine.userData.isOptimized = true;
6423
6062
  rootGroup.add(mergedLine);
@@ -6536,50 +6175,18 @@ class DynamicGltfLoader {
6536
6175
  console.warn("No merged objects to transform");
6537
6176
  return;
6538
6177
  }
6539
- if (!this.transformData) {
6540
- console.warn("Transform texture not initialized");
6541
- return;
6178
+ this.objectTransforms = new Map(objectTransformMap);
6179
+ for (const mesh of this.mergedMesh) {
6180
+ this._applyTransformToMergedObject(mesh);
6542
6181
  }
6543
- this.objectTransforms = objectTransformMap;
6544
- this._resetTransformData(false);
6545
- const transformData = this.transformData;
6546
- const objectIdToIndex = this.objectIdToIndex;
6547
- let textureNeedsUpdate = false;
6548
- if (objectTransformMap instanceof Map) {
6549
- for (const [object, matrix] of objectTransformMap.entries()) {
6550
- const userData = object.userData;
6551
- if (!userData) continue;
6552
- const handle = userData.handle;
6553
- if (handle === undefined) continue;
6554
- const objectId = objectIdToIndex.get(handle);
6555
- if (objectId !== undefined) {
6556
- transformData.set(matrix.elements, objectId * 16);
6557
- textureNeedsUpdate = true;
6558
- }
6559
- }
6560
- } else {
6561
- const len = objectTransformMap.length;
6562
- for (let i = 0; i < len; i++) {
6563
- const pair = objectTransformMap[i];
6564
- const userData = pair[0].userData;
6565
- if (!userData) continue;
6566
- const handle = userData.handle;
6567
- if (handle === undefined) continue;
6568
- const objectId = objectIdToIndex.get(handle);
6569
- if (objectId !== undefined) {
6570
- transformData.set(pair[1].elements, objectId * 16);
6571
- textureNeedsUpdate = true;
6572
- }
6573
- }
6574
- }
6575
- if (textureNeedsUpdate) {
6576
- this.updateTransformTexture();
6577
- if (
6578
- this._lastTransformTexture !== this.transformTexture ||
6579
- this._lastTransformTextureSize !== this.transformTextureSize
6580
- ) {
6581
- this.updateMaterialUniforms();
6582
- }
6182
+ for (const line of this.mergedLines) {
6183
+ this._applyTransformToMergedObject(line);
6184
+ }
6185
+ for (const lineSegment of this.mergedLineSegments) {
6186
+ this._applyTransformToMergedObject(lineSegment);
6187
+ }
6188
+ for (const point of this.mergedPoints) {
6189
+ this._applyTransformToMergedObject(point);
6583
6190
  }
6584
6191
  }
6585
6192
  createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
@@ -6596,66 +6203,21 @@ class DynamicGltfLoader {
6596
6203
  ? objects
6597
6204
  : Array.from(objects)
6598
6205
  : Array.from(this.originalObjects);
6599
- const structureInverseMatrices = new Map();
6600
- if (!this.meshToNodeMap) {
6601
- this.meshToNodeMap = new Map();
6602
- for (const node of this.nodes.values()) {
6603
- if (node.object) {
6604
- this.meshToNodeMap.set(node.object, node);
6605
- }
6606
- }
6607
- }
6608
6206
  for (const obj of objectsArray) {
6609
6207
  if (!obj.geometry || !obj.geometry.attributes.position) continue;
6610
- if (!obj.userData.explodeVector) {
6611
- let center = null;
6612
- const node = this.meshToNodeMap.get(obj);
6613
- if (node && node.geometryExtents) {
6614
- const box = node.geometryExtents.clone();
6615
- box.applyMatrix4(obj.matrixWorld);
6616
- center = new Vector3();
6617
- box.getCenter(center);
6618
- }
6619
- if (!center) {
6620
- if (!obj.geometry.boundingBox) obj.geometry.computeBoundingBox();
6621
- const box = obj.geometry.boundingBox.clone();
6622
- box.applyMatrix4(obj.matrixWorld);
6623
- center = new Vector3();
6624
- box.getCenter(center);
6625
- }
6626
- const explodeVector = center.sub(explodeCenter);
6627
- obj.userData.explodeVector = explodeVector;
6628
- }
6629
- const explodeVector = obj.userData.explodeVector;
6630
- const distance = explodeVector.length();
6208
+ const boundingBox = new Box3().setFromBufferAttribute(obj.geometry.attributes.position);
6209
+ if (obj.matrixWorld) {
6210
+ boundingBox.applyMatrix4(obj.matrixWorld);
6211
+ }
6212
+ if (boundingBox.isEmpty()) continue;
6213
+ const objectCenter = new Vector3();
6214
+ boundingBox.getCenter(objectCenter);
6215
+ const direction = objectCenter.clone().sub(explodeCenter);
6216
+ const distance = direction.length();
6631
6217
  if (distance > 0) {
6632
- const offset = explodeVector.clone().multiplyScalar(explodeFactor - 1.0);
6633
- const localOffset = offset.clone();
6634
- if (obj.userData.structureId) {
6635
- const structureId = obj.userData.structureId;
6636
- let inverseMatrix = structureInverseMatrices.get(structureId);
6637
- if (!inverseMatrix) {
6638
- const rootGroup = this.structureRoots.get(structureId);
6639
- if (rootGroup) {
6640
- if (!rootGroup.userData.inverseWorldMatrix) {
6641
- rootGroup.userData.inverseWorldMatrix = new Matrix4().copy(rootGroup.matrixWorld).invert();
6642
- }
6643
- inverseMatrix = rootGroup.userData.inverseWorldMatrix;
6644
- structureInverseMatrices.set(structureId, inverseMatrix);
6645
- }
6646
- }
6647
- if (inverseMatrix) {
6648
- const zero = new Vector3(0, 0, 0).applyMatrix4(inverseMatrix);
6649
- const vec = offset.clone().applyMatrix4(inverseMatrix).sub(zero);
6650
- localOffset.copy(vec);
6651
- }
6652
- }
6653
- let matrix = obj.userData.explodeMatrix;
6654
- if (!matrix) {
6655
- matrix = new Matrix4();
6656
- obj.userData.explodeMatrix = matrix;
6657
- }
6658
- matrix.makeTranslation(localOffset.x, localOffset.y, localOffset.z);
6218
+ direction.normalize();
6219
+ const offset = direction.multiplyScalar(distance * (explodeFactor - 1.0));
6220
+ const matrix = new Matrix4().makeTranslation(offset.x, offset.y, offset.z);
6659
6221
  transformMap.set(obj, matrix);
6660
6222
  }
6661
6223
  }
@@ -6663,11 +6225,116 @@ class DynamicGltfLoader {
6663
6225
  }
6664
6226
  clearTransforms() {
6665
6227
  this.objectTransforms.clear();
6666
- this._resetTransformData(true);
6228
+ for (const mesh of this.mergedMesh) {
6229
+ this._restoreOriginalGeometry(mesh);
6230
+ }
6231
+ for (const line of this.mergedLines) {
6232
+ this._restoreOriginalGeometry(line);
6233
+ }
6234
+ for (const lineSegment of this.mergedLineSegments) {
6235
+ this._restoreOriginalGeometry(lineSegment);
6236
+ }
6237
+ for (const point of this.mergedPoints) {
6238
+ this._restoreOriginalGeometry(point);
6239
+ }
6667
6240
  }
6668
6241
  clearHandleTransforms() {
6669
6242
  this.clearTransforms();
6670
6243
  }
6244
+ _applyTransformToMergedObject(mergedObject) {
6245
+ const objectData = this.mergedObjectMap.get(mergedObject.uuid);
6246
+ if (!objectData || !objectData.objectMapping) return;
6247
+ const geometry = mergedObject.geometry;
6248
+ if (!geometry || !geometry.attributes.position) return;
6249
+ const positionAttr = geometry.attributes.position;
6250
+ const positions = positionAttr.array;
6251
+ if (!this.transformedGeometries.has(mergedObject.uuid)) {
6252
+ this.transformedGeometries.set(mergedObject.uuid, new Float32Array(positions));
6253
+ }
6254
+ const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
6255
+ const tempVector = new Vector3();
6256
+ for (const [originalMesh, mappingData] of objectData.objectMapping) {
6257
+ const transform = this.objectTransforms.get(originalMesh);
6258
+ if (!transform) {
6259
+ const startIdx = mappingData.startVertexIndex * 3;
6260
+ const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
6261
+ for (let i = startIdx; i < endIdx; i++) {
6262
+ positions[i] = originalPositions[i];
6263
+ }
6264
+ continue;
6265
+ }
6266
+ const startVertex = mappingData.startVertexIndex;
6267
+ const vertexCount = mappingData.vertexCount;
6268
+ for (let i = 0; i < vertexCount; i++) {
6269
+ const idx = (startVertex + i) * 3;
6270
+ tempVector.set(originalPositions[idx], originalPositions[idx + 1], originalPositions[idx + 2]);
6271
+ tempVector.applyMatrix4(transform);
6272
+ positions[idx] = tempVector.x;
6273
+ positions[idx + 1] = tempVector.y;
6274
+ positions[idx + 2] = tempVector.z;
6275
+ }
6276
+ }
6277
+ if (geometry.attributes.normal) {
6278
+ this._updateNormalsForTransform(geometry, objectData, originalPositions);
6279
+ }
6280
+ positionAttr.needsUpdate = true;
6281
+ geometry.computeBoundingSphere();
6282
+ geometry.computeBoundingBox();
6283
+ }
6284
+ _updateNormalsForTransform(geometry, objectData, originalPositions) {
6285
+ const normalAttr = geometry.attributes.normal;
6286
+ if (!normalAttr) return;
6287
+ const normals = normalAttr.array;
6288
+ const tempVector = new Vector3();
6289
+ const normalMatrix = new Matrix4();
6290
+ const normalsKey = `${geometry.uuid}_normals`;
6291
+ if (!this.transformedGeometries.has(normalsKey)) {
6292
+ this.transformedGeometries.set(normalsKey, new Float32Array(normals));
6293
+ }
6294
+ const originalNormals = this.transformedGeometries.get(normalsKey);
6295
+ for (const [originalMesh, mappingData] of objectData.objectMapping) {
6296
+ const transform = this.objectTransforms.get(originalMesh);
6297
+ if (!transform) {
6298
+ const startIdx = mappingData.startVertexIndex * 3;
6299
+ const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
6300
+ for (let i = startIdx; i < endIdx; i++) {
6301
+ normals[i] = originalNormals[i];
6302
+ }
6303
+ continue;
6304
+ }
6305
+ normalMatrix.copy(transform).invert().transpose();
6306
+ const startVertex = mappingData.startVertexIndex;
6307
+ const vertexCount = mappingData.vertexCount;
6308
+ for (let i = 0; i < vertexCount; i++) {
6309
+ const idx = (startVertex + i) * 3;
6310
+ tempVector.set(originalNormals[idx], originalNormals[idx + 1], originalNormals[idx + 2]);
6311
+ tempVector.applyMatrix4(normalMatrix).normalize();
6312
+ normals[idx] = tempVector.x;
6313
+ normals[idx + 1] = tempVector.y;
6314
+ normals[idx + 2] = tempVector.z;
6315
+ }
6316
+ }
6317
+ normalAttr.needsUpdate = true;
6318
+ }
6319
+ _restoreOriginalGeometry(mergedObject) {
6320
+ const geometry = mergedObject.geometry;
6321
+ if (!geometry || !geometry.attributes.position) return;
6322
+ const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
6323
+ if (originalPositions) {
6324
+ const positions = geometry.attributes.position.array;
6325
+ positions.set(originalPositions);
6326
+ geometry.attributes.position.needsUpdate = true;
6327
+ }
6328
+ const normalsKey = `${geometry.uuid}_normals`;
6329
+ const originalNormals = this.transformedGeometries.get(normalsKey);
6330
+ if (originalNormals && geometry.attributes.normal) {
6331
+ const normals = geometry.attributes.normal.array;
6332
+ normals.set(originalNormals);
6333
+ geometry.attributes.normal.needsUpdate = true;
6334
+ }
6335
+ geometry.computeBoundingSphere();
6336
+ geometry.computeBoundingBox();
6337
+ }
6671
6338
  syncHiddenObjects() {
6672
6339
  if (this.mergedObjectMap.size === 0) {
6673
6340
  console.log("No merged objects to sync");