@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.
- package/dist/viewer-three.js +723 -358
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +724 -359
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/draggers/CuttingPlaneDragger.d.ts +23 -5
- package/lib/Viewer/helpers/PlaneHelper2.d.ts +8 -4
- package/lib/Viewer/measurement/Snapper.d.ts +1 -1
- package/package.json +5 -5
- package/src/Viewer/components/SelectionComponent.ts +1 -1
- package/src/Viewer/controls/WalkControls.ts +10 -2
- package/src/Viewer/draggers/CuttingPlaneDragger.ts +191 -31
- package/src/Viewer/draggers/CuttingPlaneXAxis.ts +2 -3
- package/src/Viewer/draggers/CuttingPlaneYAxis.ts +2 -3
- package/src/Viewer/draggers/CuttingPlaneZAxis.ts +2 -3
- package/src/Viewer/draggers/index.ts +2 -0
- package/src/Viewer/helpers/PlaneHelper2.ts +30 -17
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +495 -182
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +44 -33
- package/src/Viewer/measurement/Snapper.ts +13 -5
- package/src/Viewer/models/ModelImpl.ts +13 -10
|
@@ -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 {
|
|
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';
|
|
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,41 +41,6 @@ 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
|
-
|
|
79
44
|
const _changeEvent = { type: "change" };
|
|
80
45
|
const _startEvent = { type: "start" };
|
|
81
46
|
const _endEvent = { type: "end" };
|
|
@@ -734,6 +699,166 @@ class OrbitControls extends EventDispatcher {
|
|
|
734
699
|
}
|
|
735
700
|
}
|
|
736
701
|
|
|
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
|
+
|
|
737
862
|
class OrbitDragger {
|
|
738
863
|
constructor(viewer) {
|
|
739
864
|
this.updateControls = () => {
|
|
@@ -826,12 +951,18 @@ class OrbitDragger {
|
|
|
826
951
|
}
|
|
827
952
|
|
|
828
953
|
class CuttingPlaneDragger extends OrbitDragger {
|
|
829
|
-
constructor(viewer
|
|
954
|
+
constructor(viewer) {
|
|
830
955
|
super(viewer);
|
|
956
|
+
this.helpers = [];
|
|
957
|
+
this.activeHelper = null;
|
|
831
958
|
this.transformChange = () => {
|
|
832
|
-
|
|
833
|
-
|
|
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);
|
|
834
964
|
this.viewer.update();
|
|
965
|
+
this.changed = true;
|
|
835
966
|
};
|
|
836
967
|
this.translateDrag = (event) => {
|
|
837
968
|
this.orbit.enabled = !event.value;
|
|
@@ -842,45 +973,83 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
842
973
|
this.translate.enabled = !event.value;
|
|
843
974
|
};
|
|
844
975
|
this.updatePlaneSize = () => {
|
|
845
|
-
|
|
976
|
+
const extentsSize = this.viewer.extents.getSize(new Vector3()).length() || 1;
|
|
977
|
+
this.helpers.forEach((planeHelper) => (planeHelper.size = extentsSize));
|
|
846
978
|
this.viewer.update();
|
|
847
979
|
};
|
|
848
980
|
this.updateTransformCamera = () => {
|
|
849
981
|
this.translate.camera = this.viewer.camera;
|
|
850
982
|
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();
|
|
851
993
|
};
|
|
852
994
|
this.onKeyDown = (event) => {
|
|
853
995
|
if (event.key === "Shift")
|
|
854
996
|
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();
|
|
855
1001
|
};
|
|
856
1002
|
this.onKeyUp = (event) => {
|
|
857
1003
|
if (event.key === "Shift")
|
|
858
1004
|
this.rotate.setRotationSnap(null);
|
|
859
1005
|
};
|
|
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
|
+
};
|
|
860
1026
|
this.onDoubleClick = (event) => {
|
|
861
|
-
|
|
862
|
-
|
|
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);
|
|
863
1034
|
this.transformChange();
|
|
1035
|
+
event.stopPropagation();
|
|
864
1036
|
};
|
|
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);
|
|
869
1037
|
if (!viewer.renderer.clippingPlanes)
|
|
870
1038
|
viewer.renderer.clippingPlanes = [];
|
|
871
|
-
viewer.renderer.clippingPlanes
|
|
872
|
-
this.
|
|
873
|
-
|
|
874
|
-
this.
|
|
875
|
-
this.
|
|
876
|
-
this.
|
|
877
|
-
this.
|
|
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();
|
|
878
1047
|
this.translate = new TransformControls(viewer.camera, viewer.canvas);
|
|
1048
|
+
this.translate.setMode("translate");
|
|
879
1049
|
this.translate.setSpace("local");
|
|
880
1050
|
this.translate.showX = false;
|
|
881
1051
|
this.translate.showY = false;
|
|
882
1052
|
this.translate.showZ = true;
|
|
883
|
-
this.translate.attach(this.planeCenter);
|
|
884
1053
|
this.translate.addEventListener("change", this.transformChange);
|
|
885
1054
|
this.translate.addEventListener("dragging-changed", this.translateDrag);
|
|
886
1055
|
this.viewer.helpers.add(this.translate.getHelper());
|
|
@@ -890,14 +1059,18 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
890
1059
|
this.rotate.showX = true;
|
|
891
1060
|
this.rotate.showY = true;
|
|
892
1061
|
this.rotate.showZ = false;
|
|
893
|
-
this.rotate.attach(this.planeCenter);
|
|
894
1062
|
this.rotate.addEventListener("change", this.transformChange);
|
|
895
1063
|
this.rotate.addEventListener("dragging-changed", this.rotateDrag);
|
|
896
1064
|
this.viewer.helpers.add(this.rotate.getHelper());
|
|
1065
|
+
this.setActiveHelper(this.helpers[this.helpers.length - 1]);
|
|
897
1066
|
this.viewer.addEventListener("explode", this.updatePlaneSize);
|
|
898
1067
|
this.viewer.addEventListener("show", this.updatePlaneSize);
|
|
899
1068
|
this.viewer.addEventListener("showall", this.updatePlaneSize);
|
|
900
1069
|
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);
|
|
901
1074
|
this.viewer.canvas.addEventListener("dblclick", this.onDoubleClick, true);
|
|
902
1075
|
window.addEventListener("keydown", this.onKeyDown);
|
|
903
1076
|
window.addEventListener("keyup", this.onKeyUp);
|
|
@@ -908,6 +1081,10 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
908
1081
|
this.viewer.removeEventListener("show", this.updatePlaneSize);
|
|
909
1082
|
this.viewer.removeEventListener("showall", this.updatePlaneSize);
|
|
910
1083
|
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);
|
|
911
1088
|
this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
|
|
912
1089
|
window.removeEventListener("keydown", this.onKeyDown);
|
|
913
1090
|
window.removeEventListener("keyup", this.onKeyUp);
|
|
@@ -921,28 +1098,108 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
921
1098
|
this.rotate.getHelper().removeFromParent();
|
|
922
1099
|
this.rotate.detach();
|
|
923
1100
|
this.rotate.dispose();
|
|
924
|
-
this.
|
|
925
|
-
|
|
926
|
-
|
|
1101
|
+
this.helpers.forEach((helper) => {
|
|
1102
|
+
helper.removeFromParent();
|
|
1103
|
+
helper.dispose();
|
|
1104
|
+
});
|
|
1105
|
+
this.helpers = [];
|
|
1106
|
+
this.activeHelper = null;
|
|
927
1107
|
super.dispose();
|
|
928
1108
|
}
|
|
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
|
+
}
|
|
929
1183
|
}
|
|
930
1184
|
|
|
931
1185
|
class CuttingPlaneXAxisDragger extends CuttingPlaneDragger {
|
|
932
1186
|
constructor(viewer) {
|
|
933
|
-
super(viewer
|
|
1187
|
+
super(viewer);
|
|
1188
|
+
this.addPlaneX();
|
|
934
1189
|
}
|
|
935
1190
|
}
|
|
936
1191
|
|
|
937
1192
|
class CuttingPlaneYAxisDragger extends CuttingPlaneDragger {
|
|
938
1193
|
constructor(viewer) {
|
|
939
|
-
super(viewer
|
|
1194
|
+
super(viewer);
|
|
1195
|
+
this.addPlaneY();
|
|
940
1196
|
}
|
|
941
1197
|
}
|
|
942
1198
|
|
|
943
1199
|
class CuttingPlaneZAxisDragger extends CuttingPlaneDragger {
|
|
944
1200
|
constructor(viewer) {
|
|
945
|
-
super(viewer
|
|
1201
|
+
super(viewer);
|
|
1202
|
+
this.addPlaneZ();
|
|
946
1203
|
}
|
|
947
1204
|
}
|
|
948
1205
|
|
|
@@ -1019,120 +1276,6 @@ function formatDistance(distance, units, precision = 2) {
|
|
|
1019
1276
|
}
|
|
1020
1277
|
}
|
|
1021
1278
|
|
|
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
|
-
|
|
1136
1279
|
const _downPoint = new Vector2();
|
|
1137
1280
|
class MeasureLineDragger extends OrbitDragger {
|
|
1138
1281
|
constructor(viewer) {
|
|
@@ -1616,14 +1759,20 @@ class WalkControls extends Controls {
|
|
|
1616
1759
|
}
|
|
1617
1760
|
};
|
|
1618
1761
|
this.onKeyUp = (event) => {
|
|
1619
|
-
if (this.moveKeys.delete(event.code))
|
|
1762
|
+
if (this.moveKeys.delete(event.code)) {
|
|
1763
|
+
if (this.moveKeys.size === 0) {
|
|
1764
|
+
this._rebuildGroundBox(this.object.position);
|
|
1765
|
+
}
|
|
1620
1766
|
this.update();
|
|
1767
|
+
}
|
|
1621
1768
|
};
|
|
1622
1769
|
this.camera = camera;
|
|
1623
1770
|
this.groundObjects = groundObjects;
|
|
1624
1771
|
for (const obj of groundObjects) {
|
|
1625
1772
|
this._groundObjectBoxes.set(obj, new Box3().setFromObject(obj));
|
|
1626
1773
|
}
|
|
1774
|
+
const pos = this.object.position;
|
|
1775
|
+
this._rebuildGroundBox(pos);
|
|
1627
1776
|
this.raycaster = new Raycaster();
|
|
1628
1777
|
this.raycaster.near = 0;
|
|
1629
1778
|
this.raycaster.far = this.EYE_HEIGHT + this.FAILING_DISTANCE;
|
|
@@ -2232,6 +2381,7 @@ draggers.registerDragger("Pan", (viewer) => new PanDragger(viewer));
|
|
|
2232
2381
|
draggers.registerDragger("Orbit", (viewer) => new OrbitDragger(viewer));
|
|
2233
2382
|
draggers.registerDragger("Zoom", (viewer) => new ZoomDragger(viewer));
|
|
2234
2383
|
draggers.registerDragger("MeasureLine", (viewer) => new MeasureLineDragger(viewer));
|
|
2384
|
+
draggers.registerDragger("CuttingPlane", (viewer) => new CuttingPlaneDragger(viewer));
|
|
2235
2385
|
draggers.registerDragger("CuttingPlaneXAxis", (viewer) => new CuttingPlaneXAxisDragger(viewer));
|
|
2236
2386
|
draggers.registerDragger("CuttingPlaneYAxis", (viewer) => new CuttingPlaneYAxisDragger(viewer));
|
|
2237
2387
|
draggers.registerDragger("CuttingPlaneZAxis", (viewer) => new CuttingPlaneZAxisDragger(viewer));
|
|
@@ -3127,7 +3277,7 @@ class SelectionComponent {
|
|
|
3127
3277
|
this.getMousePosition(event, this.downPosition);
|
|
3128
3278
|
};
|
|
3129
3279
|
this.onPointerUp = (event) => {
|
|
3130
|
-
if (!event.isPrimary)
|
|
3280
|
+
if (!event.isPrimary || event.button !== 0)
|
|
3131
3281
|
return;
|
|
3132
3282
|
const upPosition = this.getMousePosition(event, new Vector2());
|
|
3133
3283
|
if (upPosition.distanceTo(this.downPosition) !== 0)
|
|
@@ -3656,19 +3806,19 @@ class ModelImpl {
|
|
|
3656
3806
|
return this;
|
|
3657
3807
|
}
|
|
3658
3808
|
explode(scale = 0, coeff = 4) {
|
|
3659
|
-
const
|
|
3660
|
-
const
|
|
3809
|
+
const centersCache = new Map();
|
|
3810
|
+
const calcObjectCenter = (object, target) => {
|
|
3661
3811
|
const extents = new Box3().setFromObject(object);
|
|
3662
3812
|
const handle = object.userData.handle;
|
|
3663
3813
|
if (!handle)
|
|
3664
3814
|
return extents.getCenter(target);
|
|
3665
|
-
const center =
|
|
3815
|
+
const center = centersCache.get(handle);
|
|
3666
3816
|
if (center)
|
|
3667
3817
|
return target.copy(center);
|
|
3668
3818
|
const objects = this.getObjectsByHandles(handle);
|
|
3669
3819
|
objects.forEach((x) => extents.expandByObject(x));
|
|
3670
3820
|
extents.getCenter(target);
|
|
3671
|
-
|
|
3821
|
+
centersCache.set(handle, target.clone());
|
|
3672
3822
|
return target;
|
|
3673
3823
|
};
|
|
3674
3824
|
function calcExplodeDepth(object, depth) {
|
|
@@ -3679,13 +3829,14 @@ class ModelImpl {
|
|
|
3679
3829
|
result = objectDepth;
|
|
3680
3830
|
});
|
|
3681
3831
|
object.userData.originalPosition = object.position.clone();
|
|
3682
|
-
object.userData.originalCenter =
|
|
3832
|
+
object.userData.originalCenter = calcObjectCenter(object, new Vector3());
|
|
3683
3833
|
return result;
|
|
3684
3834
|
}
|
|
3685
3835
|
const explodeScale = scale / 100;
|
|
3686
3836
|
const explodeRoot = this.scene;
|
|
3687
|
-
if (!explodeRoot.userData.explodeDepth)
|
|
3837
|
+
if (!explodeRoot.userData.explodeDepth) {
|
|
3688
3838
|
explodeRoot.userData.explodeDepth = calcExplodeDepth(explodeRoot, 1);
|
|
3839
|
+
}
|
|
3689
3840
|
const maxDepth = explodeRoot.userData.explodeDepth;
|
|
3690
3841
|
const scaledExplodeDepth = explodeScale * maxDepth + 1;
|
|
3691
3842
|
const explodeDepth = 0 | scaledExplodeDepth;
|
|
@@ -3702,8 +3853,8 @@ class ModelImpl {
|
|
|
3702
3853
|
objectScale *= currentSegmentFraction;
|
|
3703
3854
|
const parentCenter = object.parent.userData.originalCenter;
|
|
3704
3855
|
const objectCenter = object.userData.originalCenter;
|
|
3705
|
-
const
|
|
3706
|
-
object.position.add(
|
|
3856
|
+
const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
|
|
3857
|
+
object.position.add(localOffset);
|
|
3707
3858
|
}
|
|
3708
3859
|
object.children.forEach((x) => explodeObject(x, depth + 1));
|
|
3709
3860
|
}
|
|
@@ -3795,67 +3946,73 @@ class DynamicModelImpl extends ModelImpl {
|
|
|
3795
3946
|
return this;
|
|
3796
3947
|
}
|
|
3797
3948
|
explode(scale = 0, coeff = 4) {
|
|
3798
|
-
const
|
|
3949
|
+
const centersCache = new Map();
|
|
3799
3950
|
const calcObjectCenter = (object, target) => {
|
|
3800
3951
|
const extents = new Box3().setFromObject(object);
|
|
3801
3952
|
const handle = object.userData.handle;
|
|
3802
3953
|
if (!handle)
|
|
3803
3954
|
return extents.getCenter(target);
|
|
3804
|
-
const center =
|
|
3955
|
+
const center = centersCache.get(handle);
|
|
3805
3956
|
if (center)
|
|
3806
3957
|
return target.copy(center);
|
|
3807
3958
|
const objects = this.getObjectsByHandles(handle);
|
|
3808
3959
|
objects.forEach((x) => extents.expandByObject(x));
|
|
3809
3960
|
extents.getCenter(target);
|
|
3810
|
-
|
|
3961
|
+
centersCache.set(handle, target.clone());
|
|
3811
3962
|
return target;
|
|
3812
3963
|
};
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
if (result < objectDepth)
|
|
3820
|
-
result = objectDepth;
|
|
3821
|
-
});
|
|
3964
|
+
const calcObjectDepth = (object) => {
|
|
3965
|
+
if (object.userData.depth !== undefined)
|
|
3966
|
+
return object.userData.depth;
|
|
3967
|
+
const parent = object.parent;
|
|
3968
|
+
const depth = parent && object !== explodeRoot ? calcObjectDepth(parent) + 1 : 0;
|
|
3969
|
+
object.userData.depth = depth;
|
|
3822
3970
|
object.userData.originalPosition = object.position.clone();
|
|
3823
3971
|
object.userData.originalCenter = calcObjectCenter(object, new Vector3());
|
|
3824
|
-
return
|
|
3825
|
-
}
|
|
3972
|
+
return depth;
|
|
3973
|
+
};
|
|
3826
3974
|
const explodeScale = scale / 100;
|
|
3827
3975
|
const explodeRoot = this.scene.children[0];
|
|
3828
|
-
if (!explodeRoot.userData.explodeDepth)
|
|
3829
|
-
|
|
3976
|
+
if (!explodeRoot.userData.explodeDepth) {
|
|
3977
|
+
let maxDepth = 0;
|
|
3978
|
+
this.gltfLoader.originalObjects.forEach((object) => {
|
|
3979
|
+
const depth = calcObjectDepth(object);
|
|
3980
|
+
if (depth > maxDepth)
|
|
3981
|
+
maxDepth = depth;
|
|
3982
|
+
});
|
|
3983
|
+
explodeRoot.userData.explodeDepth = maxDepth;
|
|
3984
|
+
}
|
|
3830
3985
|
const maxDepth = explodeRoot.userData.explodeDepth;
|
|
3831
3986
|
const scaledExplodeDepth = explodeScale * maxDepth + 1;
|
|
3832
3987
|
const explodeDepth = 0 | scaledExplodeDepth;
|
|
3833
3988
|
const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
|
|
3834
|
-
const
|
|
3835
|
-
|
|
3836
|
-
if (object
|
|
3837
|
-
return;
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3989
|
+
const offsetCache = new Map();
|
|
3990
|
+
const calcObjectOffset = (object, target) => {
|
|
3991
|
+
if (offsetCache.has(object))
|
|
3992
|
+
return target.copy(offsetCache.get(object));
|
|
3993
|
+
const parent = object.parent;
|
|
3994
|
+
if (parent && object !== explodeRoot)
|
|
3995
|
+
calcObjectOffset(parent, target);
|
|
3996
|
+
const depth = object.userData.depth;
|
|
3997
|
+
if (depth > 0 && depth <= explodeDepth) {
|
|
3842
3998
|
let objectScale = explodeScale * coeff;
|
|
3843
3999
|
if (depth === explodeDepth)
|
|
3844
4000
|
objectScale *= currentSegmentFraction;
|
|
3845
|
-
const parentCenter =
|
|
4001
|
+
const parentCenter = parent.userData.originalCenter;
|
|
3846
4002
|
const objectCenter = object.userData.originalCenter;
|
|
3847
|
-
const
|
|
3848
|
-
|
|
3849
|
-
const matrix = new Matrix4().makeTranslation(objectOffset.x, objectOffset.y, objectOffset.z);
|
|
3850
|
-
transformMap.set(object, matrix);
|
|
4003
|
+
const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
|
|
4004
|
+
target.add(localOffset);
|
|
3851
4005
|
}
|
|
3852
|
-
object.
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
4006
|
+
offsetCache.set(object, target.clone());
|
|
4007
|
+
return target;
|
|
4008
|
+
};
|
|
4009
|
+
const transformMap = new Map();
|
|
4010
|
+
this.gltfLoader.originalObjects.forEach((object) => {
|
|
4011
|
+
const globalOffset = calcObjectOffset(object, new Vector3());
|
|
4012
|
+
transformMap.set(object, new Matrix4().makeTranslation(globalOffset));
|
|
4013
|
+
});
|
|
3858
4014
|
this.gltfLoader.applyObjectTransforms(transformMap);
|
|
4015
|
+
this.scene.updateMatrixWorld();
|
|
3859
4016
|
return this;
|
|
3860
4017
|
}
|
|
3861
4018
|
}
|
|
@@ -4432,6 +4589,8 @@ class DynamicGltfLoader {
|
|
|
4432
4589
|
this.oldOptimizeObjects = new Set();
|
|
4433
4590
|
this.objectTransforms = new Map();
|
|
4434
4591
|
this.transformedGeometries = new Map();
|
|
4592
|
+
this.syncTransformsToOriginalObjects = true;
|
|
4593
|
+
this._originalObjectMatrices = new Map();
|
|
4435
4594
|
this.activeChunkLoads = 0;
|
|
4436
4595
|
this.chunkQueue = [];
|
|
4437
4596
|
this.objectIdToIndex = new Map();
|
|
@@ -4441,6 +4600,81 @@ class DynamicGltfLoader {
|
|
|
4441
4600
|
this.mergedObjectMap = new Map();
|
|
4442
4601
|
this.mergedGeometryVisibility = new Map();
|
|
4443
4602
|
this._webglInfoCache = null;
|
|
4603
|
+
this.transformTextureSize = 1024;
|
|
4604
|
+
this.transformTexture = this.createDummyTexture();
|
|
4605
|
+
this.transformData = null;
|
|
4606
|
+
this.identityTransformData = null;
|
|
4607
|
+
this.visibilityMaterials = new Set();
|
|
4608
|
+
}
|
|
4609
|
+
createDummyTexture() {
|
|
4610
|
+
const data = new Float32Array(16);
|
|
4611
|
+
const identity = new Matrix4();
|
|
4612
|
+
identity.toArray(data);
|
|
4613
|
+
const dummyData = new Float32Array(16);
|
|
4614
|
+
identity.toArray(dummyData);
|
|
4615
|
+
const dummyTexture = new DataTexture(dummyData, 4, 1, RGBAFormat, FloatType);
|
|
4616
|
+
dummyTexture.minFilter = NearestFilter;
|
|
4617
|
+
dummyTexture.magFilter = NearestFilter;
|
|
4618
|
+
dummyTexture.needsUpdate = true;
|
|
4619
|
+
return dummyTexture;
|
|
4620
|
+
}
|
|
4621
|
+
initTransformTexture() {
|
|
4622
|
+
if (this.transformTexture) {
|
|
4623
|
+
this.transformTexture.dispose();
|
|
4624
|
+
}
|
|
4625
|
+
const maxInstanceCount = this.maxObjectId + 1;
|
|
4626
|
+
let size = Math.sqrt(maxInstanceCount * 4);
|
|
4627
|
+
size = Math.ceil(size / 4) * 4;
|
|
4628
|
+
size = Math.max(size, 4);
|
|
4629
|
+
this.transformTextureSize = size;
|
|
4630
|
+
const arraySize = size * size * 4;
|
|
4631
|
+
this.transformData = new Float32Array(arraySize);
|
|
4632
|
+
this.identityTransformData = new Float32Array(arraySize);
|
|
4633
|
+
for (let i = 0; i <= this.maxObjectId; i++) {
|
|
4634
|
+
const base = i * 16;
|
|
4635
|
+
if (base + 15 < arraySize) {
|
|
4636
|
+
this.identityTransformData[base + 0] = 1;
|
|
4637
|
+
this.identityTransformData[base + 5] = 1;
|
|
4638
|
+
this.identityTransformData[base + 10] = 1;
|
|
4639
|
+
this.identityTransformData[base + 15] = 1;
|
|
4640
|
+
}
|
|
4641
|
+
}
|
|
4642
|
+
this._resetTransformData(false);
|
|
4643
|
+
this.transformTexture = new DataTexture(this.transformData, size, size, RGBAFormat, FloatType);
|
|
4644
|
+
this.transformTexture.needsUpdate = true;
|
|
4645
|
+
this.transformTexture.generateMipmaps = false;
|
|
4646
|
+
console.log(`Initialized transform texture: ${size}x${size} for ${maxInstanceCount} objects`);
|
|
4647
|
+
this.updateMaterialUniforms();
|
|
4648
|
+
this.visibilityMaterials.forEach((material) => {
|
|
4649
|
+
material.needsUpdate = true;
|
|
4650
|
+
});
|
|
4651
|
+
}
|
|
4652
|
+
_resetTransformData(updateTexture = true) {
|
|
4653
|
+
if (!this.transformData || !this.identityTransformData) return;
|
|
4654
|
+
this.transformData.set(this.identityTransformData);
|
|
4655
|
+
if (updateTexture) {
|
|
4656
|
+
this.updateTransformTexture();
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
updateMaterialUniforms() {
|
|
4660
|
+
if (
|
|
4661
|
+
this._lastTransformTexture === this.transformTexture &&
|
|
4662
|
+
this._lastTransformTextureSize === this.transformTextureSize
|
|
4663
|
+
) {
|
|
4664
|
+
return;
|
|
4665
|
+
}
|
|
4666
|
+
this._lastTransformTexture = this.transformTexture;
|
|
4667
|
+
this._lastTransformTextureSize = this.transformTextureSize;
|
|
4668
|
+
this.visibilityMaterials.forEach((material) => {
|
|
4669
|
+
if (material.userData && material.userData.visibilityUniforms) {
|
|
4670
|
+
material.userData.visibilityUniforms.transformTexture.value = this.transformTexture;
|
|
4671
|
+
material.userData.visibilityUniforms.transformTextureSize.value = this.transformTextureSize;
|
|
4672
|
+
}
|
|
4673
|
+
});
|
|
4674
|
+
}
|
|
4675
|
+
updateTransformTexture() {
|
|
4676
|
+
if (!this.transformTexture) return;
|
|
4677
|
+
this.transformTexture.needsUpdate = true;
|
|
4444
4678
|
}
|
|
4445
4679
|
setVisibleEdges(visible) {
|
|
4446
4680
|
this.visibleEdges = visible;
|
|
@@ -5330,36 +5564,82 @@ class DynamicGltfLoader {
|
|
|
5330
5564
|
}
|
|
5331
5565
|
}
|
|
5332
5566
|
createVisibilityMaterial(material) {
|
|
5567
|
+
this.visibilityMaterials.add(material);
|
|
5568
|
+
const uniforms = {
|
|
5569
|
+
transformTexture: { value: this.transformTexture },
|
|
5570
|
+
transformTextureSize: { value: this.transformTextureSize },
|
|
5571
|
+
};
|
|
5572
|
+
material.userData.visibilityUniforms = uniforms;
|
|
5333
5573
|
material.onBeforeCompile = (shader) => {
|
|
5574
|
+
shader.uniforms.transformTexture = uniforms.transformTexture;
|
|
5575
|
+
shader.uniforms.transformTextureSize = uniforms.transformTextureSize;
|
|
5334
5576
|
shader.vertexShader = shader.vertexShader.replace(
|
|
5335
5577
|
"#include <common>",
|
|
5336
5578
|
`
|
|
5337
5579
|
#include <common>
|
|
5580
|
+
|
|
5338
5581
|
attribute float visibility;
|
|
5582
|
+
attribute float objectId;
|
|
5339
5583
|
varying float vVisibility;
|
|
5584
|
+
uniform highp sampler2D transformTexture;
|
|
5585
|
+
uniform float transformTextureSize;
|
|
5586
|
+
|
|
5587
|
+
mat4 getTransformMatrix(float instanceId) {
|
|
5588
|
+
int size = int(transformTextureSize);
|
|
5589
|
+
int index = int(instanceId) * 4;
|
|
5590
|
+
|
|
5591
|
+
int x0 = index % size;
|
|
5592
|
+
int y0 = index / size;
|
|
5593
|
+
|
|
5594
|
+
vec4 row0 = texelFetch(transformTexture, ivec2(x0, y0), 0);
|
|
5595
|
+
vec4 row1 = texelFetch(transformTexture, ivec2(x0 + 1, y0), 0);
|
|
5596
|
+
vec4 row2 = texelFetch(transformTexture, ivec2(x0 + 2, y0), 0);
|
|
5597
|
+
vec4 row3 = texelFetch(transformTexture, ivec2(x0 + 3, y0), 0);
|
|
5598
|
+
|
|
5599
|
+
return mat4(row0, row1, row2, row3);
|
|
5600
|
+
}
|
|
5340
5601
|
`
|
|
5341
5602
|
);
|
|
5342
|
-
shader.
|
|
5343
|
-
"
|
|
5603
|
+
shader.vertexShader = shader.vertexShader.replace(
|
|
5604
|
+
"void main() {",
|
|
5344
5605
|
`
|
|
5345
|
-
|
|
5346
|
-
|
|
5606
|
+
void main() {
|
|
5607
|
+
mat4 batchingMatrix = getTransformMatrix(objectId);
|
|
5608
|
+
vVisibility = visibility;
|
|
5347
5609
|
`
|
|
5348
5610
|
);
|
|
5611
|
+
if (shader.vertexShader.includes("#include <beginnormal_vertex>")) {
|
|
5612
|
+
shader.vertexShader = shader.vertexShader.replace(
|
|
5613
|
+
"#include <beginnormal_vertex>",
|
|
5614
|
+
`
|
|
5615
|
+
vec3 objectNormal = vec3( normal );
|
|
5616
|
+
mat3 bm = mat3( batchingMatrix );
|
|
5617
|
+
objectNormal = bm * objectNormal;
|
|
5618
|
+
`
|
|
5619
|
+
);
|
|
5620
|
+
}
|
|
5349
5621
|
shader.vertexShader = shader.vertexShader.replace(
|
|
5350
|
-
"
|
|
5622
|
+
"#include <begin_vertex>",
|
|
5351
5623
|
`
|
|
5352
|
-
|
|
5353
|
-
|
|
5624
|
+
vec3 transformed = vec3( position );
|
|
5625
|
+
transformed = ( batchingMatrix * vec4( transformed, 1.0 ) ).xyz;
|
|
5354
5626
|
`
|
|
5355
5627
|
);
|
|
5356
|
-
shader.fragmentShader = shader.fragmentShader
|
|
5357
|
-
|
|
5628
|
+
shader.fragmentShader = shader.fragmentShader
|
|
5629
|
+
.replace(
|
|
5630
|
+
"#include <common>",
|
|
5631
|
+
`
|
|
5632
|
+
#include <common>
|
|
5633
|
+
varying float vVisibility;
|
|
5358
5634
|
`
|
|
5635
|
+
)
|
|
5636
|
+
.replace(
|
|
5637
|
+
"void main() {",
|
|
5638
|
+
`
|
|
5359
5639
|
void main() {
|
|
5360
5640
|
if (vVisibility < 0.5) discard;
|
|
5361
5641
|
`
|
|
5362
|
-
|
|
5642
|
+
);
|
|
5363
5643
|
};
|
|
5364
5644
|
material.needsUpdate = true;
|
|
5365
5645
|
return material;
|
|
@@ -5465,6 +5745,7 @@ class DynamicGltfLoader {
|
|
|
5465
5745
|
this.isolatedObjects = [];
|
|
5466
5746
|
this.objectTransforms.clear();
|
|
5467
5747
|
this.transformedGeometries.clear();
|
|
5748
|
+
this._originalObjectMatrices.clear();
|
|
5468
5749
|
this.totalLoadedObjects = 0;
|
|
5469
5750
|
this.currentMemoryUsage = 0;
|
|
5470
5751
|
this.pendingMemoryUsage = 0;
|
|
@@ -5474,6 +5755,8 @@ class DynamicGltfLoader {
|
|
|
5474
5755
|
this.objectIdToIndex.clear();
|
|
5475
5756
|
this.maxObjectId = 0;
|
|
5476
5757
|
this.objectVisibility = new Float32Array();
|
|
5758
|
+
this.meshToNodeMap = null;
|
|
5759
|
+
this.visibilityMaterials.clear();
|
|
5477
5760
|
}
|
|
5478
5761
|
setStructureTransform(structureId, matrix) {
|
|
5479
5762
|
const rootGroup = this.structureRoots.get(structureId);
|
|
@@ -5589,12 +5872,15 @@ class DynamicGltfLoader {
|
|
|
5589
5872
|
});
|
|
5590
5873
|
this.originalObjects.clear();
|
|
5591
5874
|
this.originalObjectsToSelection.clear();
|
|
5875
|
+
this.objectIdToIndex.clear();
|
|
5876
|
+
this.maxObjectId = 0;
|
|
5592
5877
|
const structureGroups = new Map();
|
|
5593
5878
|
this.dispatchEvent("optimizationprogress", {
|
|
5594
5879
|
phase: "collecting",
|
|
5595
5880
|
progress: 5,
|
|
5596
5881
|
message: "Collecting scene objects...",
|
|
5597
5882
|
});
|
|
5883
|
+
let totalObjectsToMerge = 0;
|
|
5598
5884
|
this.scene.traverse((object) => {
|
|
5599
5885
|
if (object.userData.structureId) {
|
|
5600
5886
|
const structureId = object.userData.structureId;
|
|
@@ -5612,17 +5898,32 @@ class DynamicGltfLoader {
|
|
|
5612
5898
|
});
|
|
5613
5899
|
}
|
|
5614
5900
|
const group = structureGroups.get(structureId);
|
|
5901
|
+
let added = false;
|
|
5615
5902
|
if (object instanceof Mesh) {
|
|
5616
5903
|
this.addToMaterialGroup(object, group.mapMeshes, group.meshes);
|
|
5904
|
+
added = true;
|
|
5617
5905
|
} else if (object instanceof LineSegments) {
|
|
5618
5906
|
this.addToMaterialGroup(object, group.mapLineSegments, group.lineSegments);
|
|
5907
|
+
added = true;
|
|
5619
5908
|
} else if (object instanceof Line) {
|
|
5620
5909
|
this.addToMaterialGroup(object, group.mapLines, group.lines);
|
|
5910
|
+
added = true;
|
|
5621
5911
|
} else if (object instanceof Points) {
|
|
5622
5912
|
this.addToMaterialGroup(object, group.mapPoints, group.points);
|
|
5913
|
+
added = true;
|
|
5914
|
+
}
|
|
5915
|
+
if (added) {
|
|
5916
|
+
totalObjectsToMerge++;
|
|
5623
5917
|
}
|
|
5624
5918
|
}
|
|
5625
5919
|
});
|
|
5920
|
+
if (totalObjectsToMerge > 0) {
|
|
5921
|
+
console.log(`Pre-allocating transform texture for ${totalObjectsToMerge} objects`);
|
|
5922
|
+
this.maxObjectId = totalObjectsToMerge;
|
|
5923
|
+
this.initTransformTexture();
|
|
5924
|
+
this.initializeObjectVisibility();
|
|
5925
|
+
this.maxObjectId = 0;
|
|
5926
|
+
}
|
|
5626
5927
|
let processedGroups = 0;
|
|
5627
5928
|
const totalGroups = structureGroups.size;
|
|
5628
5929
|
this.dispatchEvent("optimizationprogress", {
|
|
@@ -5667,7 +5968,6 @@ class DynamicGltfLoader {
|
|
|
5667
5968
|
this.originalObjectsToSelection.add(obj);
|
|
5668
5969
|
}
|
|
5669
5970
|
});
|
|
5670
|
-
this.initializeObjectVisibility();
|
|
5671
5971
|
console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
|
|
5672
5972
|
this.dispatchEvent("optimizationprogress", {
|
|
5673
5973
|
phase: "complete",
|
|
@@ -5736,6 +6036,7 @@ class DynamicGltfLoader {
|
|
|
5736
6036
|
}
|
|
5737
6037
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
5738
6038
|
const mergedMesh = new Mesh(mergedGeometry, visibilityMaterial);
|
|
6039
|
+
mergedMesh.frustumCulled = false;
|
|
5739
6040
|
mergedMesh.userData.isOptimized = true;
|
|
5740
6041
|
rootGroup.add(mergedMesh);
|
|
5741
6042
|
this.mergedMesh.add(mergedMesh);
|
|
@@ -5852,6 +6153,7 @@ class DynamicGltfLoader {
|
|
|
5852
6153
|
geometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
5853
6154
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
5854
6155
|
const mergedLine = new LineSegments(geometry, visibilityMaterial);
|
|
6156
|
+
mergedLine.frustumCulled = false;
|
|
5855
6157
|
mergedLine.userData.isEdge = isEdge;
|
|
5856
6158
|
mergedLine.userData.isOptimized = true;
|
|
5857
6159
|
const mergedObjects = [mergedLine];
|
|
@@ -5940,6 +6242,7 @@ class DynamicGltfLoader {
|
|
|
5940
6242
|
mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
5941
6243
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
5942
6244
|
const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
|
|
6245
|
+
mergedLine.frustumCulled = false;
|
|
5943
6246
|
mergedLine.userData.isEdge = isEdge;
|
|
5944
6247
|
mergedLine.userData.isOptimized = true;
|
|
5945
6248
|
if (this.useVAO) {
|
|
@@ -6009,7 +6312,27 @@ class DynamicGltfLoader {
|
|
|
6009
6312
|
const mergedObjects = [];
|
|
6010
6313
|
if (geometries.length > 0) {
|
|
6011
6314
|
const mergedGeometry = mergeGeometries(geometries, false);
|
|
6012
|
-
const
|
|
6315
|
+
const totalVertices = mergedGeometry.attributes.position.count;
|
|
6316
|
+
const objectIds = new Float32Array(totalVertices);
|
|
6317
|
+
let vertexOffset = 0;
|
|
6318
|
+
group.objects.forEach((points) => {
|
|
6319
|
+
const handle = points.userData.handle;
|
|
6320
|
+
if (!this.objectIdToIndex.has(handle)) {
|
|
6321
|
+
this.objectIdToIndex.set(handle, this.maxObjectId++);
|
|
6322
|
+
}
|
|
6323
|
+
const objectId = this.objectIdToIndex.get(handle);
|
|
6324
|
+
const count = points.geometry.attributes.position.count;
|
|
6325
|
+
for (let i = 0; i < count; i++) {
|
|
6326
|
+
objectIds[vertexOffset++] = objectId;
|
|
6327
|
+
}
|
|
6328
|
+
});
|
|
6329
|
+
mergedGeometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
|
|
6330
|
+
const visibilityArray = new Float32Array(totalVertices);
|
|
6331
|
+
visibilityArray.fill(1.0);
|
|
6332
|
+
mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
6333
|
+
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
6334
|
+
const mergedPoints = new Points(mergedGeometry, visibilityMaterial);
|
|
6335
|
+
mergedPoints.frustumCulled = false;
|
|
6013
6336
|
mergedPoints.userData.isOptimized = true;
|
|
6014
6337
|
if (this.useVAO) {
|
|
6015
6338
|
this.createVAO(mergedPoints);
|
|
@@ -6078,13 +6401,33 @@ class DynamicGltfLoader {
|
|
|
6078
6401
|
geometriesWithIndex.push(clonedGeometry);
|
|
6079
6402
|
});
|
|
6080
6403
|
const finalGeometry = mergeGeometries(geometriesWithIndex, false);
|
|
6404
|
+
const totalVertices = finalGeometry.attributes.position.count;
|
|
6405
|
+
const objectIds = new Float32Array(totalVertices);
|
|
6406
|
+
let vertexOffset = 0;
|
|
6407
|
+
lineSegmentsArray.forEach((segment) => {
|
|
6408
|
+
const handle = segment.userData.handle;
|
|
6409
|
+
if (!this.objectIdToIndex.has(handle)) {
|
|
6410
|
+
this.objectIdToIndex.set(handle, this.maxObjectId++);
|
|
6411
|
+
}
|
|
6412
|
+
const objectId = this.objectIdToIndex.get(handle);
|
|
6413
|
+
const count = segment.geometry.attributes.position.count;
|
|
6414
|
+
for (let i = 0; i < count; i++) {
|
|
6415
|
+
objectIds[vertexOffset++] = objectId;
|
|
6416
|
+
}
|
|
6417
|
+
});
|
|
6418
|
+
finalGeometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
|
|
6419
|
+
const visibilityArray = new Float32Array(totalVertices);
|
|
6420
|
+
visibilityArray.fill(1.0);
|
|
6421
|
+
finalGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
6081
6422
|
const material = new LineBasicMaterial({
|
|
6082
6423
|
vertexColors: true,
|
|
6083
6424
|
});
|
|
6425
|
+
const visibilityMaterial = this.createVisibilityMaterial(material);
|
|
6084
6426
|
if (this.useVAO) {
|
|
6085
6427
|
this.createVAO(finalGeometry);
|
|
6086
6428
|
}
|
|
6087
|
-
const mergedLine = new LineSegments(finalGeometry,
|
|
6429
|
+
const mergedLine = new LineSegments(finalGeometry, visibilityMaterial);
|
|
6430
|
+
mergedLine.frustumCulled = false;
|
|
6088
6431
|
mergedLine.userData.structureId = structureId;
|
|
6089
6432
|
mergedLine.userData.isOptimized = true;
|
|
6090
6433
|
rootGroup.add(mergedLine);
|
|
@@ -6203,18 +6546,91 @@ class DynamicGltfLoader {
|
|
|
6203
6546
|
console.warn("No merged objects to transform");
|
|
6204
6547
|
return;
|
|
6205
6548
|
}
|
|
6206
|
-
this.
|
|
6207
|
-
|
|
6208
|
-
|
|
6549
|
+
if (!this.transformData) {
|
|
6550
|
+
console.warn("Transform texture not initialized");
|
|
6551
|
+
return;
|
|
6209
6552
|
}
|
|
6210
|
-
|
|
6211
|
-
|
|
6553
|
+
this.objectTransforms = objectTransformMap;
|
|
6554
|
+
this._resetTransformData(false);
|
|
6555
|
+
const transformData = this.transformData;
|
|
6556
|
+
const objectIdToIndex = this.objectIdToIndex;
|
|
6557
|
+
let textureNeedsUpdate = false;
|
|
6558
|
+
if (objectTransformMap instanceof Map) {
|
|
6559
|
+
for (const [object, matrix] of objectTransformMap.entries()) {
|
|
6560
|
+
const userData = object.userData;
|
|
6561
|
+
if (!userData) continue;
|
|
6562
|
+
const handle = userData.handle;
|
|
6563
|
+
if (handle === undefined) continue;
|
|
6564
|
+
const objectId = objectIdToIndex.get(handle);
|
|
6565
|
+
if (objectId !== undefined) {
|
|
6566
|
+
transformData.set(matrix.elements, objectId * 16);
|
|
6567
|
+
textureNeedsUpdate = true;
|
|
6568
|
+
}
|
|
6569
|
+
}
|
|
6570
|
+
} else {
|
|
6571
|
+
const len = objectTransformMap.length;
|
|
6572
|
+
for (let i = 0; i < len; i++) {
|
|
6573
|
+
const pair = objectTransformMap[i];
|
|
6574
|
+
const userData = pair[0].userData;
|
|
6575
|
+
if (!userData) continue;
|
|
6576
|
+
const handle = userData.handle;
|
|
6577
|
+
if (handle === undefined) continue;
|
|
6578
|
+
const objectId = objectIdToIndex.get(handle);
|
|
6579
|
+
if (objectId !== undefined) {
|
|
6580
|
+
transformData.set(pair[1].elements, objectId * 16);
|
|
6581
|
+
textureNeedsUpdate = true;
|
|
6582
|
+
}
|
|
6583
|
+
}
|
|
6584
|
+
}
|
|
6585
|
+
if (textureNeedsUpdate) {
|
|
6586
|
+
this.updateTransformTexture();
|
|
6587
|
+
if (
|
|
6588
|
+
this._lastTransformTexture !== this.transformTexture ||
|
|
6589
|
+
this._lastTransformTextureSize !== this.transformTextureSize
|
|
6590
|
+
) {
|
|
6591
|
+
this.updateMaterialUniforms();
|
|
6592
|
+
}
|
|
6212
6593
|
}
|
|
6213
|
-
|
|
6214
|
-
this.
|
|
6594
|
+
if (this.syncTransformsToOriginalObjects) {
|
|
6595
|
+
this._syncOriginalObjectTransforms(objectTransformMap);
|
|
6215
6596
|
}
|
|
6216
|
-
|
|
6217
|
-
|
|
6597
|
+
}
|
|
6598
|
+
_syncOriginalObjectTransforms(objectTransformMap) {
|
|
6599
|
+
for (const [obj, savedPos] of this._originalObjectMatrices) {
|
|
6600
|
+
obj.position.copy(savedPos);
|
|
6601
|
+
if (obj.userData.highlight) {
|
|
6602
|
+
obj.userData.highlight.position.copy(savedPos);
|
|
6603
|
+
}
|
|
6604
|
+
}
|
|
6605
|
+
this._originalObjectMatrices.clear();
|
|
6606
|
+
const _offset = new Vector3();
|
|
6607
|
+
const _parentInverse = new Matrix4();
|
|
6608
|
+
if (objectTransformMap instanceof Map) {
|
|
6609
|
+
for (const [object, matrix] of objectTransformMap.entries()) {
|
|
6610
|
+
if (!object.userData?.handle) continue;
|
|
6611
|
+
if (!this._originalObjectMatrices.has(object)) {
|
|
6612
|
+
this._originalObjectMatrices.set(object, object.position.clone());
|
|
6613
|
+
}
|
|
6614
|
+
_offset.setFromMatrixPosition(matrix);
|
|
6615
|
+
if (object.userData.structureId) {
|
|
6616
|
+
const rootGroup = this.structureRoots.get(object.userData.structureId);
|
|
6617
|
+
if (rootGroup && object.parent && object.parent !== rootGroup) {
|
|
6618
|
+
const origin = new Vector3(0, 0, 0);
|
|
6619
|
+
origin.applyMatrix4(rootGroup.matrixWorld);
|
|
6620
|
+
_offset.applyMatrix4(rootGroup.matrixWorld);
|
|
6621
|
+
_offset.sub(origin);
|
|
6622
|
+
const parentOrigin = new Vector3(0, 0, 0);
|
|
6623
|
+
_parentInverse.copy(object.parent.matrixWorld).invert();
|
|
6624
|
+
parentOrigin.applyMatrix4(_parentInverse);
|
|
6625
|
+
_offset.applyMatrix4(_parentInverse);
|
|
6626
|
+
_offset.sub(parentOrigin);
|
|
6627
|
+
}
|
|
6628
|
+
}
|
|
6629
|
+
object.position.add(_offset);
|
|
6630
|
+
if (object.userData.highlight) {
|
|
6631
|
+
object.userData.highlight.position.copy(object.position);
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6218
6634
|
}
|
|
6219
6635
|
}
|
|
6220
6636
|
createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
|
|
@@ -6231,21 +6647,66 @@ class DynamicGltfLoader {
|
|
|
6231
6647
|
? objects
|
|
6232
6648
|
: Array.from(objects)
|
|
6233
6649
|
: Array.from(this.originalObjects);
|
|
6650
|
+
const structureInverseMatrices = new Map();
|
|
6651
|
+
if (!this.meshToNodeMap) {
|
|
6652
|
+
this.meshToNodeMap = new Map();
|
|
6653
|
+
for (const node of this.nodes.values()) {
|
|
6654
|
+
if (node.object) {
|
|
6655
|
+
this.meshToNodeMap.set(node.object, node);
|
|
6656
|
+
}
|
|
6657
|
+
}
|
|
6658
|
+
}
|
|
6234
6659
|
for (const obj of objectsArray) {
|
|
6235
6660
|
if (!obj.geometry || !obj.geometry.attributes.position) continue;
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6661
|
+
if (!obj.userData.explodeVector) {
|
|
6662
|
+
let center = null;
|
|
6663
|
+
const node = this.meshToNodeMap.get(obj);
|
|
6664
|
+
if (node && node.geometryExtents) {
|
|
6665
|
+
const box = node.geometryExtents.clone();
|
|
6666
|
+
box.applyMatrix4(obj.matrixWorld);
|
|
6667
|
+
center = new Vector3();
|
|
6668
|
+
box.getCenter(center);
|
|
6669
|
+
}
|
|
6670
|
+
if (!center) {
|
|
6671
|
+
if (!obj.geometry.boundingBox) obj.geometry.computeBoundingBox();
|
|
6672
|
+
const box = obj.geometry.boundingBox.clone();
|
|
6673
|
+
box.applyMatrix4(obj.matrixWorld);
|
|
6674
|
+
center = new Vector3();
|
|
6675
|
+
box.getCenter(center);
|
|
6676
|
+
}
|
|
6677
|
+
const explodeVector = center.sub(explodeCenter);
|
|
6678
|
+
obj.userData.explodeVector = explodeVector;
|
|
6679
|
+
}
|
|
6680
|
+
const explodeVector = obj.userData.explodeVector;
|
|
6681
|
+
const distance = explodeVector.length();
|
|
6245
6682
|
if (distance > 0) {
|
|
6246
|
-
|
|
6247
|
-
const
|
|
6248
|
-
|
|
6683
|
+
const offset = explodeVector.clone().multiplyScalar(explodeFactor - 1.0);
|
|
6684
|
+
const localOffset = offset.clone();
|
|
6685
|
+
if (obj.userData.structureId) {
|
|
6686
|
+
const structureId = obj.userData.structureId;
|
|
6687
|
+
let inverseMatrix = structureInverseMatrices.get(structureId);
|
|
6688
|
+
if (!inverseMatrix) {
|
|
6689
|
+
const rootGroup = this.structureRoots.get(structureId);
|
|
6690
|
+
if (rootGroup) {
|
|
6691
|
+
if (!rootGroup.userData.inverseWorldMatrix) {
|
|
6692
|
+
rootGroup.userData.inverseWorldMatrix = new Matrix4().copy(rootGroup.matrixWorld).invert();
|
|
6693
|
+
}
|
|
6694
|
+
inverseMatrix = rootGroup.userData.inverseWorldMatrix;
|
|
6695
|
+
structureInverseMatrices.set(structureId, inverseMatrix);
|
|
6696
|
+
}
|
|
6697
|
+
}
|
|
6698
|
+
if (inverseMatrix) {
|
|
6699
|
+
const zero = new Vector3(0, 0, 0).applyMatrix4(inverseMatrix);
|
|
6700
|
+
const vec = offset.clone().applyMatrix4(inverseMatrix).sub(zero);
|
|
6701
|
+
localOffset.copy(vec);
|
|
6702
|
+
}
|
|
6703
|
+
}
|
|
6704
|
+
let matrix = obj.userData.explodeMatrix;
|
|
6705
|
+
if (!matrix) {
|
|
6706
|
+
matrix = new Matrix4();
|
|
6707
|
+
obj.userData.explodeMatrix = matrix;
|
|
6708
|
+
}
|
|
6709
|
+
matrix.makeTranslation(localOffset.x, localOffset.y, localOffset.z);
|
|
6249
6710
|
transformMap.set(obj, matrix);
|
|
6250
6711
|
}
|
|
6251
6712
|
}
|
|
@@ -6253,115 +6714,19 @@ class DynamicGltfLoader {
|
|
|
6253
6714
|
}
|
|
6254
6715
|
clearTransforms() {
|
|
6255
6716
|
this.objectTransforms.clear();
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
for (const lineSegment of this.mergedLineSegments) {
|
|
6263
|
-
this._restoreOriginalGeometry(lineSegment);
|
|
6264
|
-
}
|
|
6265
|
-
for (const point of this.mergedPoints) {
|
|
6266
|
-
this._restoreOriginalGeometry(point);
|
|
6267
|
-
}
|
|
6268
|
-
}
|
|
6269
|
-
clearHandleTransforms() {
|
|
6270
|
-
this.clearTransforms();
|
|
6271
|
-
}
|
|
6272
|
-
_applyTransformToMergedObject(mergedObject) {
|
|
6273
|
-
const objectData = this.mergedObjectMap.get(mergedObject.uuid);
|
|
6274
|
-
if (!objectData || !objectData.objectMapping) return;
|
|
6275
|
-
const geometry = mergedObject.geometry;
|
|
6276
|
-
if (!geometry || !geometry.attributes.position) return;
|
|
6277
|
-
const positionAttr = geometry.attributes.position;
|
|
6278
|
-
const positions = positionAttr.array;
|
|
6279
|
-
if (!this.transformedGeometries.has(mergedObject.uuid)) {
|
|
6280
|
-
this.transformedGeometries.set(mergedObject.uuid, new Float32Array(positions));
|
|
6281
|
-
}
|
|
6282
|
-
const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
|
|
6283
|
-
const tempVector = new Vector3();
|
|
6284
|
-
for (const [originalMesh, mappingData] of objectData.objectMapping) {
|
|
6285
|
-
const transform = this.objectTransforms.get(originalMesh);
|
|
6286
|
-
if (!transform) {
|
|
6287
|
-
const startIdx = mappingData.startVertexIndex * 3;
|
|
6288
|
-
const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
|
|
6289
|
-
for (let i = startIdx; i < endIdx; i++) {
|
|
6290
|
-
positions[i] = originalPositions[i];
|
|
6291
|
-
}
|
|
6292
|
-
continue;
|
|
6293
|
-
}
|
|
6294
|
-
const startVertex = mappingData.startVertexIndex;
|
|
6295
|
-
const vertexCount = mappingData.vertexCount;
|
|
6296
|
-
for (let i = 0; i < vertexCount; i++) {
|
|
6297
|
-
const idx = (startVertex + i) * 3;
|
|
6298
|
-
tempVector.set(originalPositions[idx], originalPositions[idx + 1], originalPositions[idx + 2]);
|
|
6299
|
-
tempVector.applyMatrix4(transform);
|
|
6300
|
-
positions[idx] = tempVector.x;
|
|
6301
|
-
positions[idx + 1] = tempVector.y;
|
|
6302
|
-
positions[idx + 2] = tempVector.z;
|
|
6303
|
-
}
|
|
6304
|
-
}
|
|
6305
|
-
if (geometry.attributes.normal) {
|
|
6306
|
-
this._updateNormalsForTransform(geometry, objectData, originalPositions);
|
|
6307
|
-
}
|
|
6308
|
-
positionAttr.needsUpdate = true;
|
|
6309
|
-
geometry.computeBoundingSphere();
|
|
6310
|
-
geometry.computeBoundingBox();
|
|
6311
|
-
}
|
|
6312
|
-
_updateNormalsForTransform(geometry, objectData, originalPositions) {
|
|
6313
|
-
const normalAttr = geometry.attributes.normal;
|
|
6314
|
-
if (!normalAttr) return;
|
|
6315
|
-
const normals = normalAttr.array;
|
|
6316
|
-
const tempVector = new Vector3();
|
|
6317
|
-
const normalMatrix = new Matrix4();
|
|
6318
|
-
const normalsKey = `${geometry.uuid}_normals`;
|
|
6319
|
-
if (!this.transformedGeometries.has(normalsKey)) {
|
|
6320
|
-
this.transformedGeometries.set(normalsKey, new Float32Array(normals));
|
|
6321
|
-
}
|
|
6322
|
-
const originalNormals = this.transformedGeometries.get(normalsKey);
|
|
6323
|
-
for (const [originalMesh, mappingData] of objectData.objectMapping) {
|
|
6324
|
-
const transform = this.objectTransforms.get(originalMesh);
|
|
6325
|
-
if (!transform) {
|
|
6326
|
-
const startIdx = mappingData.startVertexIndex * 3;
|
|
6327
|
-
const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
|
|
6328
|
-
for (let i = startIdx; i < endIdx; i++) {
|
|
6329
|
-
normals[i] = originalNormals[i];
|
|
6717
|
+
this._resetTransformData(true);
|
|
6718
|
+
if (this.syncTransformsToOriginalObjects) {
|
|
6719
|
+
for (const [obj, savedPos] of this._originalObjectMatrices) {
|
|
6720
|
+
obj.position.copy(savedPos);
|
|
6721
|
+
if (obj.userData.highlight) {
|
|
6722
|
+
obj.userData.highlight.position.copy(savedPos);
|
|
6330
6723
|
}
|
|
6331
|
-
continue;
|
|
6332
|
-
}
|
|
6333
|
-
normalMatrix.copy(transform).invert().transpose();
|
|
6334
|
-
const startVertex = mappingData.startVertexIndex;
|
|
6335
|
-
const vertexCount = mappingData.vertexCount;
|
|
6336
|
-
for (let i = 0; i < vertexCount; i++) {
|
|
6337
|
-
const idx = (startVertex + i) * 3;
|
|
6338
|
-
tempVector.set(originalNormals[idx], originalNormals[idx + 1], originalNormals[idx + 2]);
|
|
6339
|
-
tempVector.applyMatrix4(normalMatrix).normalize();
|
|
6340
|
-
normals[idx] = tempVector.x;
|
|
6341
|
-
normals[idx + 1] = tempVector.y;
|
|
6342
|
-
normals[idx + 2] = tempVector.z;
|
|
6343
6724
|
}
|
|
6725
|
+
this._originalObjectMatrices.clear();
|
|
6344
6726
|
}
|
|
6345
|
-
normalAttr.needsUpdate = true;
|
|
6346
6727
|
}
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
if (!geometry || !geometry.attributes.position) return;
|
|
6350
|
-
const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
|
|
6351
|
-
if (originalPositions) {
|
|
6352
|
-
const positions = geometry.attributes.position.array;
|
|
6353
|
-
positions.set(originalPositions);
|
|
6354
|
-
geometry.attributes.position.needsUpdate = true;
|
|
6355
|
-
}
|
|
6356
|
-
const normalsKey = `${geometry.uuid}_normals`;
|
|
6357
|
-
const originalNormals = this.transformedGeometries.get(normalsKey);
|
|
6358
|
-
if (originalNormals && geometry.attributes.normal) {
|
|
6359
|
-
const normals = geometry.attributes.normal.array;
|
|
6360
|
-
normals.set(originalNormals);
|
|
6361
|
-
geometry.attributes.normal.needsUpdate = true;
|
|
6362
|
-
}
|
|
6363
|
-
geometry.computeBoundingSphere();
|
|
6364
|
-
geometry.computeBoundingBox();
|
|
6728
|
+
clearHandleTransforms() {
|
|
6729
|
+
this.clearTransforms();
|
|
6365
6730
|
}
|
|
6366
6731
|
syncHiddenObjects() {
|
|
6367
6732
|
if (this.mergedObjectMap.size === 0) {
|