@inweb/viewer-three 27.2.2 → 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 +622 -317
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +624 -319
- 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 +413 -189
- package/src/Viewer/measurement/Snapper.ts +13 -5
|
@@ -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)
|
|
@@ -4441,6 +4591,81 @@ class DynamicGltfLoader {
|
|
|
4441
4591
|
this.mergedObjectMap = new Map();
|
|
4442
4592
|
this.mergedGeometryVisibility = new Map();
|
|
4443
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;
|
|
4444
4669
|
}
|
|
4445
4670
|
setVisibleEdges(visible) {
|
|
4446
4671
|
this.visibleEdges = visible;
|
|
@@ -5330,36 +5555,82 @@ class DynamicGltfLoader {
|
|
|
5330
5555
|
}
|
|
5331
5556
|
}
|
|
5332
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;
|
|
5333
5564
|
material.onBeforeCompile = (shader) => {
|
|
5565
|
+
shader.uniforms.transformTexture = uniforms.transformTexture;
|
|
5566
|
+
shader.uniforms.transformTextureSize = uniforms.transformTextureSize;
|
|
5334
5567
|
shader.vertexShader = shader.vertexShader.replace(
|
|
5335
5568
|
"#include <common>",
|
|
5336
5569
|
`
|
|
5337
5570
|
#include <common>
|
|
5571
|
+
|
|
5338
5572
|
attribute float visibility;
|
|
5573
|
+
attribute float objectId;
|
|
5339
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
|
+
}
|
|
5340
5592
|
`
|
|
5341
5593
|
);
|
|
5342
|
-
shader.
|
|
5343
|
-
"
|
|
5594
|
+
shader.vertexShader = shader.vertexShader.replace(
|
|
5595
|
+
"void main() {",
|
|
5344
5596
|
`
|
|
5345
|
-
|
|
5346
|
-
|
|
5597
|
+
void main() {
|
|
5598
|
+
mat4 batchingMatrix = getTransformMatrix(objectId);
|
|
5599
|
+
vVisibility = visibility;
|
|
5347
5600
|
`
|
|
5348
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
|
+
}
|
|
5349
5612
|
shader.vertexShader = shader.vertexShader.replace(
|
|
5350
|
-
"
|
|
5613
|
+
"#include <begin_vertex>",
|
|
5351
5614
|
`
|
|
5352
|
-
|
|
5353
|
-
|
|
5615
|
+
vec3 transformed = vec3( position );
|
|
5616
|
+
transformed = ( batchingMatrix * vec4( transformed, 1.0 ) ).xyz;
|
|
5354
5617
|
`
|
|
5355
5618
|
);
|
|
5356
|
-
shader.fragmentShader = shader.fragmentShader
|
|
5357
|
-
|
|
5619
|
+
shader.fragmentShader = shader.fragmentShader
|
|
5620
|
+
.replace(
|
|
5621
|
+
"#include <common>",
|
|
5622
|
+
`
|
|
5623
|
+
#include <common>
|
|
5624
|
+
varying float vVisibility;
|
|
5358
5625
|
`
|
|
5626
|
+
)
|
|
5627
|
+
.replace(
|
|
5628
|
+
"void main() {",
|
|
5629
|
+
`
|
|
5359
5630
|
void main() {
|
|
5360
5631
|
if (vVisibility < 0.5) discard;
|
|
5361
5632
|
`
|
|
5362
|
-
|
|
5633
|
+
);
|
|
5363
5634
|
};
|
|
5364
5635
|
material.needsUpdate = true;
|
|
5365
5636
|
return material;
|
|
@@ -5474,6 +5745,8 @@ class DynamicGltfLoader {
|
|
|
5474
5745
|
this.objectIdToIndex.clear();
|
|
5475
5746
|
this.maxObjectId = 0;
|
|
5476
5747
|
this.objectVisibility = new Float32Array();
|
|
5748
|
+
this.meshToNodeMap = null;
|
|
5749
|
+
this.visibilityMaterials.clear();
|
|
5477
5750
|
}
|
|
5478
5751
|
setStructureTransform(structureId, matrix) {
|
|
5479
5752
|
const rootGroup = this.structureRoots.get(structureId);
|
|
@@ -5589,12 +5862,15 @@ class DynamicGltfLoader {
|
|
|
5589
5862
|
});
|
|
5590
5863
|
this.originalObjects.clear();
|
|
5591
5864
|
this.originalObjectsToSelection.clear();
|
|
5865
|
+
this.objectIdToIndex.clear();
|
|
5866
|
+
this.maxObjectId = 0;
|
|
5592
5867
|
const structureGroups = new Map();
|
|
5593
5868
|
this.dispatchEvent("optimizationprogress", {
|
|
5594
5869
|
phase: "collecting",
|
|
5595
5870
|
progress: 5,
|
|
5596
5871
|
message: "Collecting scene objects...",
|
|
5597
5872
|
});
|
|
5873
|
+
let totalObjectsToMerge = 0;
|
|
5598
5874
|
this.scene.traverse((object) => {
|
|
5599
5875
|
if (object.userData.structureId) {
|
|
5600
5876
|
const structureId = object.userData.structureId;
|
|
@@ -5612,17 +5888,32 @@ class DynamicGltfLoader {
|
|
|
5612
5888
|
});
|
|
5613
5889
|
}
|
|
5614
5890
|
const group = structureGroups.get(structureId);
|
|
5891
|
+
let added = false;
|
|
5615
5892
|
if (object instanceof Mesh) {
|
|
5616
5893
|
this.addToMaterialGroup(object, group.mapMeshes, group.meshes);
|
|
5894
|
+
added = true;
|
|
5617
5895
|
} else if (object instanceof LineSegments) {
|
|
5618
5896
|
this.addToMaterialGroup(object, group.mapLineSegments, group.lineSegments);
|
|
5897
|
+
added = true;
|
|
5619
5898
|
} else if (object instanceof Line) {
|
|
5620
5899
|
this.addToMaterialGroup(object, group.mapLines, group.lines);
|
|
5900
|
+
added = true;
|
|
5621
5901
|
} else if (object instanceof Points) {
|
|
5622
5902
|
this.addToMaterialGroup(object, group.mapPoints, group.points);
|
|
5903
|
+
added = true;
|
|
5904
|
+
}
|
|
5905
|
+
if (added) {
|
|
5906
|
+
totalObjectsToMerge++;
|
|
5623
5907
|
}
|
|
5624
5908
|
}
|
|
5625
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
|
+
}
|
|
5626
5917
|
let processedGroups = 0;
|
|
5627
5918
|
const totalGroups = structureGroups.size;
|
|
5628
5919
|
this.dispatchEvent("optimizationprogress", {
|
|
@@ -5667,7 +5958,6 @@ class DynamicGltfLoader {
|
|
|
5667
5958
|
this.originalObjectsToSelection.add(obj);
|
|
5668
5959
|
}
|
|
5669
5960
|
});
|
|
5670
|
-
this.initializeObjectVisibility();
|
|
5671
5961
|
console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
|
|
5672
5962
|
this.dispatchEvent("optimizationprogress", {
|
|
5673
5963
|
phase: "complete",
|
|
@@ -5736,6 +6026,7 @@ class DynamicGltfLoader {
|
|
|
5736
6026
|
}
|
|
5737
6027
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
5738
6028
|
const mergedMesh = new Mesh(mergedGeometry, visibilityMaterial);
|
|
6029
|
+
mergedMesh.frustumCulled = false;
|
|
5739
6030
|
mergedMesh.userData.isOptimized = true;
|
|
5740
6031
|
rootGroup.add(mergedMesh);
|
|
5741
6032
|
this.mergedMesh.add(mergedMesh);
|
|
@@ -5852,6 +6143,7 @@ class DynamicGltfLoader {
|
|
|
5852
6143
|
geometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
5853
6144
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
5854
6145
|
const mergedLine = new LineSegments(geometry, visibilityMaterial);
|
|
6146
|
+
mergedLine.frustumCulled = false;
|
|
5855
6147
|
mergedLine.userData.isEdge = isEdge;
|
|
5856
6148
|
mergedLine.userData.isOptimized = true;
|
|
5857
6149
|
const mergedObjects = [mergedLine];
|
|
@@ -5940,6 +6232,7 @@ class DynamicGltfLoader {
|
|
|
5940
6232
|
mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
5941
6233
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
5942
6234
|
const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
|
|
6235
|
+
mergedLine.frustumCulled = false;
|
|
5943
6236
|
mergedLine.userData.isEdge = isEdge;
|
|
5944
6237
|
mergedLine.userData.isOptimized = true;
|
|
5945
6238
|
if (this.useVAO) {
|
|
@@ -6009,7 +6302,27 @@ class DynamicGltfLoader {
|
|
|
6009
6302
|
const mergedObjects = [];
|
|
6010
6303
|
if (geometries.length > 0) {
|
|
6011
6304
|
const mergedGeometry = mergeGeometries(geometries, false);
|
|
6012
|
-
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;
|
|
6013
6326
|
mergedPoints.userData.isOptimized = true;
|
|
6014
6327
|
if (this.useVAO) {
|
|
6015
6328
|
this.createVAO(mergedPoints);
|
|
@@ -6078,13 +6391,33 @@ class DynamicGltfLoader {
|
|
|
6078
6391
|
geometriesWithIndex.push(clonedGeometry);
|
|
6079
6392
|
});
|
|
6080
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));
|
|
6081
6412
|
const material = new LineBasicMaterial({
|
|
6082
6413
|
vertexColors: true,
|
|
6083
6414
|
});
|
|
6415
|
+
const visibilityMaterial = this.createVisibilityMaterial(material);
|
|
6084
6416
|
if (this.useVAO) {
|
|
6085
6417
|
this.createVAO(finalGeometry);
|
|
6086
6418
|
}
|
|
6087
|
-
const mergedLine = new LineSegments(finalGeometry,
|
|
6419
|
+
const mergedLine = new LineSegments(finalGeometry, visibilityMaterial);
|
|
6420
|
+
mergedLine.frustumCulled = false;
|
|
6088
6421
|
mergedLine.userData.structureId = structureId;
|
|
6089
6422
|
mergedLine.userData.isOptimized = true;
|
|
6090
6423
|
rootGroup.add(mergedLine);
|
|
@@ -6203,18 +6536,50 @@ class DynamicGltfLoader {
|
|
|
6203
6536
|
console.warn("No merged objects to transform");
|
|
6204
6537
|
return;
|
|
6205
6538
|
}
|
|
6206
|
-
this.
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
}
|
|
6210
|
-
for (const line of this.mergedLines) {
|
|
6211
|
-
this._applyTransformToMergedObject(line);
|
|
6212
|
-
}
|
|
6213
|
-
for (const lineSegment of this.mergedLineSegments) {
|
|
6214
|
-
this._applyTransformToMergedObject(lineSegment);
|
|
6539
|
+
if (!this.transformData) {
|
|
6540
|
+
console.warn("Transform texture not initialized");
|
|
6541
|
+
return;
|
|
6215
6542
|
}
|
|
6216
|
-
|
|
6217
|
-
|
|
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
|
+
}
|
|
6218
6583
|
}
|
|
6219
6584
|
}
|
|
6220
6585
|
createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
|
|
@@ -6231,21 +6596,66 @@ class DynamicGltfLoader {
|
|
|
6231
6596
|
? objects
|
|
6232
6597
|
: Array.from(objects)
|
|
6233
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
|
+
}
|
|
6234
6608
|
for (const obj of objectsArray) {
|
|
6235
6609
|
if (!obj.geometry || !obj.geometry.attributes.position) continue;
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
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();
|
|
6245
6631
|
if (distance > 0) {
|
|
6246
|
-
|
|
6247
|
-
const
|
|
6248
|
-
|
|
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);
|
|
6249
6659
|
transformMap.set(obj, matrix);
|
|
6250
6660
|
}
|
|
6251
6661
|
}
|
|
@@ -6253,116 +6663,11 @@ class DynamicGltfLoader {
|
|
|
6253
6663
|
}
|
|
6254
6664
|
clearTransforms() {
|
|
6255
6665
|
this.objectTransforms.clear();
|
|
6256
|
-
|
|
6257
|
-
this._restoreOriginalGeometry(mesh);
|
|
6258
|
-
}
|
|
6259
|
-
for (const line of this.mergedLines) {
|
|
6260
|
-
this._restoreOriginalGeometry(line);
|
|
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
|
-
}
|
|
6666
|
+
this._resetTransformData(true);
|
|
6268
6667
|
}
|
|
6269
6668
|
clearHandleTransforms() {
|
|
6270
6669
|
this.clearTransforms();
|
|
6271
6670
|
}
|
|
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];
|
|
6330
|
-
}
|
|
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
|
-
}
|
|
6344
|
-
}
|
|
6345
|
-
normalAttr.needsUpdate = true;
|
|
6346
|
-
}
|
|
6347
|
-
_restoreOriginalGeometry(mergedObject) {
|
|
6348
|
-
const geometry = mergedObject.geometry;
|
|
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();
|
|
6365
|
-
}
|
|
6366
6671
|
syncHiddenObjects() {
|
|
6367
6672
|
if (this.mergedObjectMap.size === 0) {
|
|
6368
6673
|
console.log("No merged objects to sync");
|