@inweb/viewer-three 27.2.1 → 27.2.3
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 +704 -339
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +703 -338
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/controls/WalkControls.d.ts +8 -0
- package/lib/Viewer/draggers/CuttingPlaneDragger.d.ts +30 -8
- package/lib/Viewer/helpers/PlaneHelper2.d.ts +12 -0
- package/lib/Viewer/measurement/Snapper.d.ts +1 -1
- package/package.json +5 -5
- package/src/Viewer/Viewer.ts +4 -0
- package/src/Viewer/components/SelectionComponent.ts +1 -1
- package/src/Viewer/controls/WalkControls.ts +50 -4
- package/src/Viewer/draggers/CuttingPlaneDragger.ts +243 -42
- 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/{PlaneHelper.ts → PlaneHelper2.ts} +27 -31
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +413 -189
- package/src/Viewer/measurement/Snapper.ts +13 -5
- package/lib/Viewer/helpers/PlaneHelper.d.ts +0 -11
|
@@ -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,47 +41,6 @@ import { EventEmitter2 } from '@inweb/eventemitter2';
|
|
|
41
41
|
import { Markup } from '@inweb/markup';
|
|
42
42
|
export * from '@inweb/markup';
|
|
43
43
|
|
|
44
|
-
class PlaneHelper extends Line {
|
|
45
|
-
constructor(plane, size = 1, color = 0xffff00, offset = new Vector3()) {
|
|
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 = "PlaneHelper";
|
|
52
|
-
this.plane = plane;
|
|
53
|
-
this.size = size;
|
|
54
|
-
this.offset = offset;
|
|
55
|
-
const positions2 = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0];
|
|
56
|
-
const geometry2 = new BufferGeometry();
|
|
57
|
-
geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3));
|
|
58
|
-
geometry2.computeBoundingSphere();
|
|
59
|
-
this.helper = new Mesh(geometry2, new MeshBasicMaterial({
|
|
60
|
-
color,
|
|
61
|
-
opacity: 0.2,
|
|
62
|
-
transparent: true,
|
|
63
|
-
depthWrite: false,
|
|
64
|
-
toneMapped: false,
|
|
65
|
-
side: DoubleSide,
|
|
66
|
-
}));
|
|
67
|
-
this.add(this.helper);
|
|
68
|
-
}
|
|
69
|
-
dispose() {
|
|
70
|
-
this.geometry.dispose();
|
|
71
|
-
this.material.dispose();
|
|
72
|
-
this.children[0].geometry.dispose();
|
|
73
|
-
this.children[0].material.dispose();
|
|
74
|
-
}
|
|
75
|
-
updateMatrixWorld(force) {
|
|
76
|
-
this.position.set(0, 0, 0);
|
|
77
|
-
this.lookAt(this.plane.normal);
|
|
78
|
-
this.position.copy(this.offset);
|
|
79
|
-
this.translateZ(-(this.offset.dot(this.plane.normal) + this.plane.constant));
|
|
80
|
-
this.scale.set(0.5 * this.size, 0.5 * this.size, 1);
|
|
81
|
-
super.updateMatrixWorld(force);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
44
|
const _changeEvent = { type: "change" };
|
|
86
45
|
const _startEvent = { type: "start" };
|
|
87
46
|
const _endEvent = { type: "end" };
|
|
@@ -740,6 +699,166 @@ class OrbitControls extends EventDispatcher {
|
|
|
740
699
|
}
|
|
741
700
|
}
|
|
742
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
|
+
|
|
743
862
|
class OrbitDragger {
|
|
744
863
|
constructor(viewer) {
|
|
745
864
|
this.updateControls = () => {
|
|
@@ -832,52 +951,129 @@ class OrbitDragger {
|
|
|
832
951
|
}
|
|
833
952
|
|
|
834
953
|
class CuttingPlaneDragger extends OrbitDragger {
|
|
835
|
-
constructor(viewer
|
|
954
|
+
constructor(viewer) {
|
|
836
955
|
super(viewer);
|
|
956
|
+
this.helpers = [];
|
|
957
|
+
this.activeHelper = null;
|
|
837
958
|
this.transformChange = () => {
|
|
838
|
-
|
|
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);
|
|
839
964
|
this.viewer.update();
|
|
965
|
+
this.changed = true;
|
|
966
|
+
};
|
|
967
|
+
this.translateDrag = (event) => {
|
|
968
|
+
this.orbit.enabled = !event.value;
|
|
969
|
+
this.rotate.enabled = !event.value;
|
|
840
970
|
};
|
|
841
|
-
this.
|
|
971
|
+
this.rotateDrag = (event) => {
|
|
842
972
|
this.orbit.enabled = !event.value;
|
|
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
|
-
this.
|
|
981
|
+
this.translate.camera = this.viewer.camera;
|
|
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();
|
|
993
|
+
};
|
|
994
|
+
this.onKeyDown = (event) => {
|
|
995
|
+
if (event.key === "Shift")
|
|
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();
|
|
1001
|
+
};
|
|
1002
|
+
this.onKeyUp = (event) => {
|
|
1003
|
+
if (event.key === "Shift")
|
|
1004
|
+
this.rotate.setRotationSnap(null);
|
|
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));
|
|
850
1025
|
};
|
|
851
1026
|
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();
|
|
852
1035
|
event.stopPropagation();
|
|
853
|
-
this.plane.negate();
|
|
854
|
-
this.viewer.update();
|
|
855
1036
|
};
|
|
856
|
-
const extentsSize = viewer.extents.getSize(new Vector3()).length() || 1;
|
|
857
|
-
const extentsCenter = viewer.extents.getCenter(new Vector3());
|
|
858
|
-
const constant = -extentsCenter.dot(normal);
|
|
859
|
-
this.plane = new Plane(normal, constant);
|
|
860
1037
|
if (!viewer.renderer.clippingPlanes)
|
|
861
1038
|
viewer.renderer.clippingPlanes = [];
|
|
862
|
-
viewer.renderer.clippingPlanes
|
|
863
|
-
this.
|
|
864
|
-
|
|
865
|
-
this.
|
|
866
|
-
this.
|
|
867
|
-
this.
|
|
868
|
-
this.
|
|
869
|
-
this.
|
|
870
|
-
this.
|
|
871
|
-
this.
|
|
872
|
-
this.
|
|
873
|
-
this.
|
|
874
|
-
this.
|
|
875
|
-
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();
|
|
1047
|
+
this.translate = new TransformControls(viewer.camera, viewer.canvas);
|
|
1048
|
+
this.translate.setMode("translate");
|
|
1049
|
+
this.translate.setSpace("local");
|
|
1050
|
+
this.translate.showX = false;
|
|
1051
|
+
this.translate.showY = false;
|
|
1052
|
+
this.translate.showZ = true;
|
|
1053
|
+
this.translate.addEventListener("change", this.transformChange);
|
|
1054
|
+
this.translate.addEventListener("dragging-changed", this.translateDrag);
|
|
1055
|
+
this.viewer.helpers.add(this.translate.getHelper());
|
|
1056
|
+
this.rotate = new TransformControls(viewer.camera, viewer.canvas);
|
|
1057
|
+
this.rotate.setMode("rotate");
|
|
1058
|
+
this.rotate.setSpace("local");
|
|
1059
|
+
this.rotate.showX = true;
|
|
1060
|
+
this.rotate.showY = true;
|
|
1061
|
+
this.rotate.showZ = false;
|
|
1062
|
+
this.rotate.addEventListener("change", this.transformChange);
|
|
1063
|
+
this.rotate.addEventListener("dragging-changed", this.rotateDrag);
|
|
1064
|
+
this.viewer.helpers.add(this.rotate.getHelper());
|
|
1065
|
+
this.setActiveHelper(this.helpers[this.helpers.length - 1]);
|
|
876
1066
|
this.viewer.addEventListener("explode", this.updatePlaneSize);
|
|
877
1067
|
this.viewer.addEventListener("show", this.updatePlaneSize);
|
|
878
1068
|
this.viewer.addEventListener("showall", this.updatePlaneSize);
|
|
879
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);
|
|
880
1074
|
this.viewer.canvas.addEventListener("dblclick", this.onDoubleClick, true);
|
|
1075
|
+
window.addEventListener("keydown", this.onKeyDown);
|
|
1076
|
+
window.addEventListener("keyup", this.onKeyUp);
|
|
881
1077
|
this.viewer.update();
|
|
882
1078
|
}
|
|
883
1079
|
dispose() {
|
|
@@ -885,34 +1081,125 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
885
1081
|
this.viewer.removeEventListener("show", this.updatePlaneSize);
|
|
886
1082
|
this.viewer.removeEventListener("showall", this.updatePlaneSize);
|
|
887
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);
|
|
888
1088
|
this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
this.
|
|
892
|
-
this.
|
|
893
|
-
this.
|
|
894
|
-
this.
|
|
895
|
-
this.
|
|
896
|
-
this.
|
|
1089
|
+
window.removeEventListener("keydown", this.onKeyDown);
|
|
1090
|
+
window.removeEventListener("keyup", this.onKeyUp);
|
|
1091
|
+
this.translate.removeEventListener("change", this.transformChange);
|
|
1092
|
+
this.translate.removeEventListener("dragging-changed", this.translateDrag);
|
|
1093
|
+
this.translate.getHelper().removeFromParent();
|
|
1094
|
+
this.translate.detach();
|
|
1095
|
+
this.translate.dispose();
|
|
1096
|
+
this.rotate.removeEventListener("change", this.transformChange);
|
|
1097
|
+
this.rotate.removeEventListener("dragging-changed", this.rotateDrag);
|
|
1098
|
+
this.rotate.getHelper().removeFromParent();
|
|
1099
|
+
this.rotate.detach();
|
|
1100
|
+
this.rotate.dispose();
|
|
1101
|
+
this.helpers.forEach((helper) => {
|
|
1102
|
+
helper.removeFromParent();
|
|
1103
|
+
helper.dispose();
|
|
1104
|
+
});
|
|
1105
|
+
this.helpers = [];
|
|
1106
|
+
this.activeHelper = null;
|
|
897
1107
|
super.dispose();
|
|
898
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
|
+
}
|
|
899
1183
|
}
|
|
900
1184
|
|
|
901
1185
|
class CuttingPlaneXAxisDragger extends CuttingPlaneDragger {
|
|
902
1186
|
constructor(viewer) {
|
|
903
|
-
super(viewer
|
|
1187
|
+
super(viewer);
|
|
1188
|
+
this.addPlaneX();
|
|
904
1189
|
}
|
|
905
1190
|
}
|
|
906
1191
|
|
|
907
1192
|
class CuttingPlaneYAxisDragger extends CuttingPlaneDragger {
|
|
908
1193
|
constructor(viewer) {
|
|
909
|
-
super(viewer
|
|
1194
|
+
super(viewer);
|
|
1195
|
+
this.addPlaneY();
|
|
910
1196
|
}
|
|
911
1197
|
}
|
|
912
1198
|
|
|
913
1199
|
class CuttingPlaneZAxisDragger extends CuttingPlaneDragger {
|
|
914
1200
|
constructor(viewer) {
|
|
915
|
-
super(viewer
|
|
1201
|
+
super(viewer);
|
|
1202
|
+
this.addPlaneZ();
|
|
916
1203
|
}
|
|
917
1204
|
}
|
|
918
1205
|
|
|
@@ -989,120 +1276,6 @@ function formatDistance(distance, units, precision = 2) {
|
|
|
989
1276
|
}
|
|
990
1277
|
}
|
|
991
1278
|
|
|
992
|
-
const DESKTOP_SNAP_DISTANCE = 10;
|
|
993
|
-
const MOBILE_SNAP_DISTANCE = 50;
|
|
994
|
-
const _vertex = new Vector3();
|
|
995
|
-
const _start = new Vector3();
|
|
996
|
-
const _end = new Vector3();
|
|
997
|
-
const _line = new Line3();
|
|
998
|
-
const _center = new Vector3();
|
|
999
|
-
const _projection = new Vector3();
|
|
1000
|
-
class Snapper {
|
|
1001
|
-
constructor(camera, renderer, canvas) {
|
|
1002
|
-
this.camera = camera;
|
|
1003
|
-
this.renderer = renderer;
|
|
1004
|
-
this.canvas = canvas;
|
|
1005
|
-
this.threshold = 0.0001;
|
|
1006
|
-
this.raycaster = new Raycaster();
|
|
1007
|
-
this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
|
|
1008
|
-
this.edgesCache = new WeakMap();
|
|
1009
|
-
}
|
|
1010
|
-
isMobile() {
|
|
1011
|
-
if (typeof navigator === "undefined")
|
|
1012
|
-
return false;
|
|
1013
|
-
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
|
|
1014
|
-
}
|
|
1015
|
-
getMousePosition(event, target) {
|
|
1016
|
-
return target.set(event.clientX, event.clientY);
|
|
1017
|
-
}
|
|
1018
|
-
getPointerIntersects(mouse, objects) {
|
|
1019
|
-
const rect = this.canvas.getBoundingClientRect();
|
|
1020
|
-
const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
|
|
1021
|
-
const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
|
|
1022
|
-
const coords = new Vector2(x, y);
|
|
1023
|
-
this.raycaster.setFromCamera(coords, this.camera);
|
|
1024
|
-
this.raycaster.params = {
|
|
1025
|
-
Mesh: {},
|
|
1026
|
-
Line: { threshold: this.threshold },
|
|
1027
|
-
Line2: { threshold: this.threshold },
|
|
1028
|
-
LOD: {},
|
|
1029
|
-
Points: { threshold: this.threshold },
|
|
1030
|
-
Sprite: {},
|
|
1031
|
-
};
|
|
1032
|
-
let intersects = this.raycaster.intersectObjects(objects, false);
|
|
1033
|
-
(this.renderer.clippingPlanes || []).forEach((plane) => {
|
|
1034
|
-
intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
|
|
1035
|
-
});
|
|
1036
|
-
return intersects;
|
|
1037
|
-
}
|
|
1038
|
-
getDetectRadius(point) {
|
|
1039
|
-
const camera = this.camera;
|
|
1040
|
-
if (camera.isOrthographicCamera) {
|
|
1041
|
-
const fieldHeight = (camera.top - camera.bottom) / camera.zoom;
|
|
1042
|
-
const canvasHeight = this.canvas.height;
|
|
1043
|
-
const worldUnitsPerPixel = fieldHeight / canvasHeight;
|
|
1044
|
-
return this.detectRadiusInPixels * worldUnitsPerPixel;
|
|
1045
|
-
}
|
|
1046
|
-
if (camera.isPerspectiveCamera) {
|
|
1047
|
-
const distance = camera.position.distanceTo(point);
|
|
1048
|
-
const fieldHeight = 2 * Math.tan(MathUtils.degToRad(camera.fov * 0.5)) * distance;
|
|
1049
|
-
const canvasHeight = this.canvas.height;
|
|
1050
|
-
const worldUnitsPerPixel = fieldHeight / canvasHeight;
|
|
1051
|
-
return this.detectRadiusInPixels * worldUnitsPerPixel;
|
|
1052
|
-
}
|
|
1053
|
-
return 0.1;
|
|
1054
|
-
}
|
|
1055
|
-
getSnapPoint(mouse, objects) {
|
|
1056
|
-
const intersections = this.getPointerIntersects(mouse, objects);
|
|
1057
|
-
if (intersections.length === 0)
|
|
1058
|
-
return undefined;
|
|
1059
|
-
const object = intersections[0].object;
|
|
1060
|
-
const intersectionPoint = intersections[0].point;
|
|
1061
|
-
const localPoint = object.worldToLocal(intersectionPoint.clone());
|
|
1062
|
-
let snapPoint;
|
|
1063
|
-
let snapDistance = this.getDetectRadius(intersectionPoint);
|
|
1064
|
-
const geometry = object.geometry;
|
|
1065
|
-
const positions = geometry.attributes.position.array;
|
|
1066
|
-
for (let i = 0; i < positions.length; i += 3) {
|
|
1067
|
-
_vertex.set(positions[i], positions[i + 1], positions[i + 2]);
|
|
1068
|
-
const distance = _vertex.distanceTo(localPoint);
|
|
1069
|
-
if (distance < snapDistance) {
|
|
1070
|
-
snapDistance = distance;
|
|
1071
|
-
snapPoint = _vertex.clone();
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
if (snapPoint)
|
|
1075
|
-
return object.localToWorld(snapPoint);
|
|
1076
|
-
let edges = this.edgesCache.get(geometry);
|
|
1077
|
-
if (!edges) {
|
|
1078
|
-
edges = new EdgesGeometry(geometry);
|
|
1079
|
-
this.edgesCache.set(geometry, edges);
|
|
1080
|
-
}
|
|
1081
|
-
const edgePositions = edges.attributes.position.array;
|
|
1082
|
-
for (let i = 0; i < edgePositions.length; i += 6) {
|
|
1083
|
-
_start.set(edgePositions[i], edgePositions[i + 1], edgePositions[i + 2]);
|
|
1084
|
-
_end.set(edgePositions[i + 3], edgePositions[i + 4], edgePositions[i + 5]);
|
|
1085
|
-
_line.set(_start, _end);
|
|
1086
|
-
_line.getCenter(_center);
|
|
1087
|
-
const centerDistance = _center.distanceTo(localPoint);
|
|
1088
|
-
if (centerDistance < snapDistance) {
|
|
1089
|
-
snapDistance = centerDistance;
|
|
1090
|
-
snapPoint = _center.clone();
|
|
1091
|
-
continue;
|
|
1092
|
-
}
|
|
1093
|
-
_line.closestPointToPoint(localPoint, true, _projection);
|
|
1094
|
-
const lineDistance = _projection.distanceTo(localPoint);
|
|
1095
|
-
if (lineDistance < snapDistance) {
|
|
1096
|
-
snapDistance = lineDistance;
|
|
1097
|
-
snapPoint = _projection.clone();
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
if (snapPoint)
|
|
1101
|
-
return object.localToWorld(snapPoint);
|
|
1102
|
-
return intersectionPoint.clone();
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
1279
|
const _downPoint = new Vector2();
|
|
1107
1280
|
class MeasureLineDragger extends OrbitDragger {
|
|
1108
1281
|
constructor(viewer) {
|
|
@@ -1514,6 +1687,12 @@ class WalkControls extends Controls {
|
|
|
1514
1687
|
this.movementSpeed = 0.1;
|
|
1515
1688
|
this.multiplier = 3;
|
|
1516
1689
|
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();
|
|
1517
1696
|
this.moveWheel = 0;
|
|
1518
1697
|
this.mouseDragOn = false;
|
|
1519
1698
|
this._up = new Vector3();
|
|
@@ -1580,11 +1759,20 @@ class WalkControls extends Controls {
|
|
|
1580
1759
|
}
|
|
1581
1760
|
};
|
|
1582
1761
|
this.onKeyUp = (event) => {
|
|
1583
|
-
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
|
+
}
|
|
1584
1766
|
this.update();
|
|
1767
|
+
}
|
|
1585
1768
|
};
|
|
1586
1769
|
this.camera = camera;
|
|
1587
1770
|
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);
|
|
1588
1776
|
this.raycaster = new Raycaster();
|
|
1589
1777
|
this.raycaster.near = 0;
|
|
1590
1778
|
this.raycaster.far = this.EYE_HEIGHT + this.FAILING_DISTANCE;
|
|
@@ -1619,10 +1807,29 @@ class WalkControls extends Controls {
|
|
|
1619
1807
|
window.removeEventListener("keyup", this.onKeyUp);
|
|
1620
1808
|
super.dispose();
|
|
1621
1809
|
}
|
|
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
|
+
}
|
|
1622
1825
|
updateGroundFollowing() {
|
|
1826
|
+
const pos = this.object.position;
|
|
1827
|
+
if (this._needsGroundBoxRebuild(pos)) {
|
|
1828
|
+
this._rebuildGroundBox(pos);
|
|
1829
|
+
}
|
|
1623
1830
|
this._up.copy(this.camera.up).negate();
|
|
1624
|
-
this.raycaster.set(
|
|
1625
|
-
const intersects = this.raycaster.intersectObjects(this.
|
|
1831
|
+
this.raycaster.set(pos, this._up);
|
|
1832
|
+
const intersects = this.raycaster.intersectObjects(this._activeGroundObjects, false);
|
|
1626
1833
|
if (intersects.length > 0) {
|
|
1627
1834
|
const groundY = intersects[0].point.y;
|
|
1628
1835
|
const targetY = groundY + this.EYE_HEIGHT;
|
|
@@ -2174,6 +2381,7 @@ draggers.registerDragger("Pan", (viewer) => new PanDragger(viewer));
|
|
|
2174
2381
|
draggers.registerDragger("Orbit", (viewer) => new OrbitDragger(viewer));
|
|
2175
2382
|
draggers.registerDragger("Zoom", (viewer) => new ZoomDragger(viewer));
|
|
2176
2383
|
draggers.registerDragger("MeasureLine", (viewer) => new MeasureLineDragger(viewer));
|
|
2384
|
+
draggers.registerDragger("CuttingPlane", (viewer) => new CuttingPlaneDragger(viewer));
|
|
2177
2385
|
draggers.registerDragger("CuttingPlaneXAxis", (viewer) => new CuttingPlaneXAxisDragger(viewer));
|
|
2178
2386
|
draggers.registerDragger("CuttingPlaneYAxis", (viewer) => new CuttingPlaneYAxisDragger(viewer));
|
|
2179
2387
|
draggers.registerDragger("CuttingPlaneZAxis", (viewer) => new CuttingPlaneZAxisDragger(viewer));
|
|
@@ -3069,7 +3277,7 @@ class SelectionComponent {
|
|
|
3069
3277
|
this.getMousePosition(event, this.downPosition);
|
|
3070
3278
|
};
|
|
3071
3279
|
this.onPointerUp = (event) => {
|
|
3072
|
-
if (!event.isPrimary)
|
|
3280
|
+
if (!event.isPrimary || event.button !== 0)
|
|
3073
3281
|
return;
|
|
3074
3282
|
const upPosition = this.getMousePosition(event, new Vector2());
|
|
3075
3283
|
if (upPosition.distanceTo(this.downPosition) !== 0)
|
|
@@ -4383,6 +4591,81 @@ class DynamicGltfLoader {
|
|
|
4383
4591
|
this.mergedObjectMap = new Map();
|
|
4384
4592
|
this.mergedGeometryVisibility = new Map();
|
|
4385
4593
|
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;
|
|
4386
4669
|
}
|
|
4387
4670
|
setVisibleEdges(visible) {
|
|
4388
4671
|
this.visibleEdges = visible;
|
|
@@ -5272,36 +5555,82 @@ class DynamicGltfLoader {
|
|
|
5272
5555
|
}
|
|
5273
5556
|
}
|
|
5274
5557
|
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;
|
|
5275
5564
|
material.onBeforeCompile = (shader) => {
|
|
5565
|
+
shader.uniforms.transformTexture = uniforms.transformTexture;
|
|
5566
|
+
shader.uniforms.transformTextureSize = uniforms.transformTextureSize;
|
|
5276
5567
|
shader.vertexShader = shader.vertexShader.replace(
|
|
5277
5568
|
"#include <common>",
|
|
5278
5569
|
`
|
|
5279
5570
|
#include <common>
|
|
5571
|
+
|
|
5280
5572
|
attribute float visibility;
|
|
5573
|
+
attribute float objectId;
|
|
5281
5574
|
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
|
+
}
|
|
5282
5592
|
`
|
|
5283
5593
|
);
|
|
5284
|
-
shader.
|
|
5285
|
-
"
|
|
5594
|
+
shader.vertexShader = shader.vertexShader.replace(
|
|
5595
|
+
"void main() {",
|
|
5286
5596
|
`
|
|
5287
|
-
|
|
5288
|
-
|
|
5597
|
+
void main() {
|
|
5598
|
+
mat4 batchingMatrix = getTransformMatrix(objectId);
|
|
5599
|
+
vVisibility = visibility;
|
|
5289
5600
|
`
|
|
5290
5601
|
);
|
|
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
|
+
}
|
|
5291
5612
|
shader.vertexShader = shader.vertexShader.replace(
|
|
5292
|
-
"
|
|
5613
|
+
"#include <begin_vertex>",
|
|
5293
5614
|
`
|
|
5294
|
-
|
|
5295
|
-
|
|
5615
|
+
vec3 transformed = vec3( position );
|
|
5616
|
+
transformed = ( batchingMatrix * vec4( transformed, 1.0 ) ).xyz;
|
|
5296
5617
|
`
|
|
5297
5618
|
);
|
|
5298
|
-
shader.fragmentShader = shader.fragmentShader
|
|
5299
|
-
|
|
5619
|
+
shader.fragmentShader = shader.fragmentShader
|
|
5620
|
+
.replace(
|
|
5621
|
+
"#include <common>",
|
|
5622
|
+
`
|
|
5623
|
+
#include <common>
|
|
5624
|
+
varying float vVisibility;
|
|
5300
5625
|
`
|
|
5626
|
+
)
|
|
5627
|
+
.replace(
|
|
5628
|
+
"void main() {",
|
|
5629
|
+
`
|
|
5301
5630
|
void main() {
|
|
5302
5631
|
if (vVisibility < 0.5) discard;
|
|
5303
5632
|
`
|
|
5304
|
-
|
|
5633
|
+
);
|
|
5305
5634
|
};
|
|
5306
5635
|
material.needsUpdate = true;
|
|
5307
5636
|
return material;
|
|
@@ -5416,6 +5745,8 @@ class DynamicGltfLoader {
|
|
|
5416
5745
|
this.objectIdToIndex.clear();
|
|
5417
5746
|
this.maxObjectId = 0;
|
|
5418
5747
|
this.objectVisibility = new Float32Array();
|
|
5748
|
+
this.meshToNodeMap = null;
|
|
5749
|
+
this.visibilityMaterials.clear();
|
|
5419
5750
|
}
|
|
5420
5751
|
setStructureTransform(structureId, matrix) {
|
|
5421
5752
|
const rootGroup = this.structureRoots.get(structureId);
|
|
@@ -5531,12 +5862,15 @@ class DynamicGltfLoader {
|
|
|
5531
5862
|
});
|
|
5532
5863
|
this.originalObjects.clear();
|
|
5533
5864
|
this.originalObjectsToSelection.clear();
|
|
5865
|
+
this.objectIdToIndex.clear();
|
|
5866
|
+
this.maxObjectId = 0;
|
|
5534
5867
|
const structureGroups = new Map();
|
|
5535
5868
|
this.dispatchEvent("optimizationprogress", {
|
|
5536
5869
|
phase: "collecting",
|
|
5537
5870
|
progress: 5,
|
|
5538
5871
|
message: "Collecting scene objects...",
|
|
5539
5872
|
});
|
|
5873
|
+
let totalObjectsToMerge = 0;
|
|
5540
5874
|
this.scene.traverse((object) => {
|
|
5541
5875
|
if (object.userData.structureId) {
|
|
5542
5876
|
const structureId = object.userData.structureId;
|
|
@@ -5554,17 +5888,32 @@ class DynamicGltfLoader {
|
|
|
5554
5888
|
});
|
|
5555
5889
|
}
|
|
5556
5890
|
const group = structureGroups.get(structureId);
|
|
5891
|
+
let added = false;
|
|
5557
5892
|
if (object instanceof Mesh) {
|
|
5558
5893
|
this.addToMaterialGroup(object, group.mapMeshes, group.meshes);
|
|
5894
|
+
added = true;
|
|
5559
5895
|
} else if (object instanceof LineSegments) {
|
|
5560
5896
|
this.addToMaterialGroup(object, group.mapLineSegments, group.lineSegments);
|
|
5897
|
+
added = true;
|
|
5561
5898
|
} else if (object instanceof Line) {
|
|
5562
5899
|
this.addToMaterialGroup(object, group.mapLines, group.lines);
|
|
5900
|
+
added = true;
|
|
5563
5901
|
} else if (object instanceof Points) {
|
|
5564
5902
|
this.addToMaterialGroup(object, group.mapPoints, group.points);
|
|
5903
|
+
added = true;
|
|
5904
|
+
}
|
|
5905
|
+
if (added) {
|
|
5906
|
+
totalObjectsToMerge++;
|
|
5565
5907
|
}
|
|
5566
5908
|
}
|
|
5567
5909
|
});
|
|
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
|
+
}
|
|
5568
5917
|
let processedGroups = 0;
|
|
5569
5918
|
const totalGroups = structureGroups.size;
|
|
5570
5919
|
this.dispatchEvent("optimizationprogress", {
|
|
@@ -5609,7 +5958,6 @@ class DynamicGltfLoader {
|
|
|
5609
5958
|
this.originalObjectsToSelection.add(obj);
|
|
5610
5959
|
}
|
|
5611
5960
|
});
|
|
5612
|
-
this.initializeObjectVisibility();
|
|
5613
5961
|
console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
|
|
5614
5962
|
this.dispatchEvent("optimizationprogress", {
|
|
5615
5963
|
phase: "complete",
|
|
@@ -5678,6 +6026,7 @@ class DynamicGltfLoader {
|
|
|
5678
6026
|
}
|
|
5679
6027
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
5680
6028
|
const mergedMesh = new Mesh(mergedGeometry, visibilityMaterial);
|
|
6029
|
+
mergedMesh.frustumCulled = false;
|
|
5681
6030
|
mergedMesh.userData.isOptimized = true;
|
|
5682
6031
|
rootGroup.add(mergedMesh);
|
|
5683
6032
|
this.mergedMesh.add(mergedMesh);
|
|
@@ -5794,6 +6143,7 @@ class DynamicGltfLoader {
|
|
|
5794
6143
|
geometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
5795
6144
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
5796
6145
|
const mergedLine = new LineSegments(geometry, visibilityMaterial);
|
|
6146
|
+
mergedLine.frustumCulled = false;
|
|
5797
6147
|
mergedLine.userData.isEdge = isEdge;
|
|
5798
6148
|
mergedLine.userData.isOptimized = true;
|
|
5799
6149
|
const mergedObjects = [mergedLine];
|
|
@@ -5882,6 +6232,7 @@ class DynamicGltfLoader {
|
|
|
5882
6232
|
mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
5883
6233
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
5884
6234
|
const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
|
|
6235
|
+
mergedLine.frustumCulled = false;
|
|
5885
6236
|
mergedLine.userData.isEdge = isEdge;
|
|
5886
6237
|
mergedLine.userData.isOptimized = true;
|
|
5887
6238
|
if (this.useVAO) {
|
|
@@ -5951,7 +6302,27 @@ class DynamicGltfLoader {
|
|
|
5951
6302
|
const mergedObjects = [];
|
|
5952
6303
|
if (geometries.length > 0) {
|
|
5953
6304
|
const mergedGeometry = mergeGeometries(geometries, false);
|
|
5954
|
-
const
|
|
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;
|
|
5955
6326
|
mergedPoints.userData.isOptimized = true;
|
|
5956
6327
|
if (this.useVAO) {
|
|
5957
6328
|
this.createVAO(mergedPoints);
|
|
@@ -6020,13 +6391,33 @@ class DynamicGltfLoader {
|
|
|
6020
6391
|
geometriesWithIndex.push(clonedGeometry);
|
|
6021
6392
|
});
|
|
6022
6393
|
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));
|
|
6023
6412
|
const material = new LineBasicMaterial({
|
|
6024
6413
|
vertexColors: true,
|
|
6025
6414
|
});
|
|
6415
|
+
const visibilityMaterial = this.createVisibilityMaterial(material);
|
|
6026
6416
|
if (this.useVAO) {
|
|
6027
6417
|
this.createVAO(finalGeometry);
|
|
6028
6418
|
}
|
|
6029
|
-
const mergedLine = new LineSegments(finalGeometry,
|
|
6419
|
+
const mergedLine = new LineSegments(finalGeometry, visibilityMaterial);
|
|
6420
|
+
mergedLine.frustumCulled = false;
|
|
6030
6421
|
mergedLine.userData.structureId = structureId;
|
|
6031
6422
|
mergedLine.userData.isOptimized = true;
|
|
6032
6423
|
rootGroup.add(mergedLine);
|
|
@@ -6145,18 +6536,50 @@ class DynamicGltfLoader {
|
|
|
6145
6536
|
console.warn("No merged objects to transform");
|
|
6146
6537
|
return;
|
|
6147
6538
|
}
|
|
6148
|
-
this.
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
}
|
|
6152
|
-
for (const line of this.mergedLines) {
|
|
6153
|
-
this._applyTransformToMergedObject(line);
|
|
6154
|
-
}
|
|
6155
|
-
for (const lineSegment of this.mergedLineSegments) {
|
|
6156
|
-
this._applyTransformToMergedObject(lineSegment);
|
|
6539
|
+
if (!this.transformData) {
|
|
6540
|
+
console.warn("Transform texture not initialized");
|
|
6541
|
+
return;
|
|
6157
6542
|
}
|
|
6158
|
-
|
|
6159
|
-
|
|
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
|
+
}
|
|
6160
6583
|
}
|
|
6161
6584
|
}
|
|
6162
6585
|
createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
|
|
@@ -6173,21 +6596,66 @@ class DynamicGltfLoader {
|
|
|
6173
6596
|
? objects
|
|
6174
6597
|
: Array.from(objects)
|
|
6175
6598
|
: 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
|
+
}
|
|
6176
6608
|
for (const obj of objectsArray) {
|
|
6177
6609
|
if (!obj.geometry || !obj.geometry.attributes.position) continue;
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
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();
|
|
6187
6631
|
if (distance > 0) {
|
|
6188
|
-
|
|
6189
|
-
const
|
|
6190
|
-
|
|
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);
|
|
6191
6659
|
transformMap.set(obj, matrix);
|
|
6192
6660
|
}
|
|
6193
6661
|
}
|
|
@@ -6195,116 +6663,11 @@ class DynamicGltfLoader {
|
|
|
6195
6663
|
}
|
|
6196
6664
|
clearTransforms() {
|
|
6197
6665
|
this.objectTransforms.clear();
|
|
6198
|
-
|
|
6199
|
-
this._restoreOriginalGeometry(mesh);
|
|
6200
|
-
}
|
|
6201
|
-
for (const line of this.mergedLines) {
|
|
6202
|
-
this._restoreOriginalGeometry(line);
|
|
6203
|
-
}
|
|
6204
|
-
for (const lineSegment of this.mergedLineSegments) {
|
|
6205
|
-
this._restoreOriginalGeometry(lineSegment);
|
|
6206
|
-
}
|
|
6207
|
-
for (const point of this.mergedPoints) {
|
|
6208
|
-
this._restoreOriginalGeometry(point);
|
|
6209
|
-
}
|
|
6666
|
+
this._resetTransformData(true);
|
|
6210
6667
|
}
|
|
6211
6668
|
clearHandleTransforms() {
|
|
6212
6669
|
this.clearTransforms();
|
|
6213
6670
|
}
|
|
6214
|
-
_applyTransformToMergedObject(mergedObject) {
|
|
6215
|
-
const objectData = this.mergedObjectMap.get(mergedObject.uuid);
|
|
6216
|
-
if (!objectData || !objectData.objectMapping) return;
|
|
6217
|
-
const geometry = mergedObject.geometry;
|
|
6218
|
-
if (!geometry || !geometry.attributes.position) return;
|
|
6219
|
-
const positionAttr = geometry.attributes.position;
|
|
6220
|
-
const positions = positionAttr.array;
|
|
6221
|
-
if (!this.transformedGeometries.has(mergedObject.uuid)) {
|
|
6222
|
-
this.transformedGeometries.set(mergedObject.uuid, new Float32Array(positions));
|
|
6223
|
-
}
|
|
6224
|
-
const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
|
|
6225
|
-
const tempVector = new Vector3();
|
|
6226
|
-
for (const [originalMesh, mappingData] of objectData.objectMapping) {
|
|
6227
|
-
const transform = this.objectTransforms.get(originalMesh);
|
|
6228
|
-
if (!transform) {
|
|
6229
|
-
const startIdx = mappingData.startVertexIndex * 3;
|
|
6230
|
-
const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
|
|
6231
|
-
for (let i = startIdx; i < endIdx; i++) {
|
|
6232
|
-
positions[i] = originalPositions[i];
|
|
6233
|
-
}
|
|
6234
|
-
continue;
|
|
6235
|
-
}
|
|
6236
|
-
const startVertex = mappingData.startVertexIndex;
|
|
6237
|
-
const vertexCount = mappingData.vertexCount;
|
|
6238
|
-
for (let i = 0; i < vertexCount; i++) {
|
|
6239
|
-
const idx = (startVertex + i) * 3;
|
|
6240
|
-
tempVector.set(originalPositions[idx], originalPositions[idx + 1], originalPositions[idx + 2]);
|
|
6241
|
-
tempVector.applyMatrix4(transform);
|
|
6242
|
-
positions[idx] = tempVector.x;
|
|
6243
|
-
positions[idx + 1] = tempVector.y;
|
|
6244
|
-
positions[idx + 2] = tempVector.z;
|
|
6245
|
-
}
|
|
6246
|
-
}
|
|
6247
|
-
if (geometry.attributes.normal) {
|
|
6248
|
-
this._updateNormalsForTransform(geometry, objectData, originalPositions);
|
|
6249
|
-
}
|
|
6250
|
-
positionAttr.needsUpdate = true;
|
|
6251
|
-
geometry.computeBoundingSphere();
|
|
6252
|
-
geometry.computeBoundingBox();
|
|
6253
|
-
}
|
|
6254
|
-
_updateNormalsForTransform(geometry, objectData, originalPositions) {
|
|
6255
|
-
const normalAttr = geometry.attributes.normal;
|
|
6256
|
-
if (!normalAttr) return;
|
|
6257
|
-
const normals = normalAttr.array;
|
|
6258
|
-
const tempVector = new Vector3();
|
|
6259
|
-
const normalMatrix = new Matrix4();
|
|
6260
|
-
const normalsKey = `${geometry.uuid}_normals`;
|
|
6261
|
-
if (!this.transformedGeometries.has(normalsKey)) {
|
|
6262
|
-
this.transformedGeometries.set(normalsKey, new Float32Array(normals));
|
|
6263
|
-
}
|
|
6264
|
-
const originalNormals = this.transformedGeometries.get(normalsKey);
|
|
6265
|
-
for (const [originalMesh, mappingData] of objectData.objectMapping) {
|
|
6266
|
-
const transform = this.objectTransforms.get(originalMesh);
|
|
6267
|
-
if (!transform) {
|
|
6268
|
-
const startIdx = mappingData.startVertexIndex * 3;
|
|
6269
|
-
const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
|
|
6270
|
-
for (let i = startIdx; i < endIdx; i++) {
|
|
6271
|
-
normals[i] = originalNormals[i];
|
|
6272
|
-
}
|
|
6273
|
-
continue;
|
|
6274
|
-
}
|
|
6275
|
-
normalMatrix.copy(transform).invert().transpose();
|
|
6276
|
-
const startVertex = mappingData.startVertexIndex;
|
|
6277
|
-
const vertexCount = mappingData.vertexCount;
|
|
6278
|
-
for (let i = 0; i < vertexCount; i++) {
|
|
6279
|
-
const idx = (startVertex + i) * 3;
|
|
6280
|
-
tempVector.set(originalNormals[idx], originalNormals[idx + 1], originalNormals[idx + 2]);
|
|
6281
|
-
tempVector.applyMatrix4(normalMatrix).normalize();
|
|
6282
|
-
normals[idx] = tempVector.x;
|
|
6283
|
-
normals[idx + 1] = tempVector.y;
|
|
6284
|
-
normals[idx + 2] = tempVector.z;
|
|
6285
|
-
}
|
|
6286
|
-
}
|
|
6287
|
-
normalAttr.needsUpdate = true;
|
|
6288
|
-
}
|
|
6289
|
-
_restoreOriginalGeometry(mergedObject) {
|
|
6290
|
-
const geometry = mergedObject.geometry;
|
|
6291
|
-
if (!geometry || !geometry.attributes.position) return;
|
|
6292
|
-
const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
|
|
6293
|
-
if (originalPositions) {
|
|
6294
|
-
const positions = geometry.attributes.position.array;
|
|
6295
|
-
positions.set(originalPositions);
|
|
6296
|
-
geometry.attributes.position.needsUpdate = true;
|
|
6297
|
-
}
|
|
6298
|
-
const normalsKey = `${geometry.uuid}_normals`;
|
|
6299
|
-
const originalNormals = this.transformedGeometries.get(normalsKey);
|
|
6300
|
-
if (originalNormals && geometry.attributes.normal) {
|
|
6301
|
-
const normals = geometry.attributes.normal.array;
|
|
6302
|
-
normals.set(originalNormals);
|
|
6303
|
-
geometry.attributes.normal.needsUpdate = true;
|
|
6304
|
-
}
|
|
6305
|
-
geometry.computeBoundingSphere();
|
|
6306
|
-
geometry.computeBoundingBox();
|
|
6307
|
-
}
|
|
6308
6671
|
syncHiddenObjects() {
|
|
6309
6672
|
if (this.mergedObjectMap.size === 0) {
|
|
6310
6673
|
console.log("No merged objects to sync");
|
|
@@ -7186,12 +7549,14 @@ class Viewer extends EventEmitter2 {
|
|
|
7186
7549
|
if (!this.renderer)
|
|
7187
7550
|
return;
|
|
7188
7551
|
this._markup.clearOverlay();
|
|
7552
|
+
this.emitEvent({ type: "clearoverlay" });
|
|
7189
7553
|
this.update();
|
|
7190
7554
|
}
|
|
7191
7555
|
clearSlices() {
|
|
7192
7556
|
if (!this.renderer)
|
|
7193
7557
|
return;
|
|
7194
7558
|
this.renderer.clippingPlanes = [];
|
|
7559
|
+
this.emitEvent({ type: "clearslices" });
|
|
7195
7560
|
this.update();
|
|
7196
7561
|
}
|
|
7197
7562
|
getSelected() {
|