@inweb/viewer-three 27.4.7 → 27.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/components/AxesHelperComponent.js +3 -0
- package/dist/extensions/components/AxesHelperComponent.js.map +1 -1
- package/dist/extensions/components/AxesHelperComponent.min.js +1 -1
- package/dist/extensions/components/AxesHelperComponent.module.js +3 -0
- package/dist/extensions/components/AxesHelperComponent.module.js.map +1 -1
- package/dist/extensions/components/ExtentsHelperComponent.js +6 -2
- package/dist/extensions/components/ExtentsHelperComponent.js.map +1 -1
- package/dist/extensions/components/ExtentsHelperComponent.min.js +1 -1
- package/dist/extensions/components/ExtentsHelperComponent.module.js +6 -2
- package/dist/extensions/components/ExtentsHelperComponent.module.js.map +1 -1
- package/dist/extensions/components/GridHelperComponent.js +1 -0
- package/dist/extensions/components/GridHelperComponent.js.map +1 -1
- package/dist/extensions/components/GridHelperComponent.min.js +1 -1
- package/dist/extensions/components/GridHelperComponent.module.js +1 -0
- package/dist/extensions/components/GridHelperComponent.module.js.map +1 -1
- package/dist/extensions/components/LightHelperComponent.js +2 -1
- package/dist/extensions/components/LightHelperComponent.js.map +1 -1
- package/dist/extensions/components/LightHelperComponent.min.js +1 -1
- package/dist/extensions/components/LightHelperComponent.module.js +2 -1
- package/dist/extensions/components/LightHelperComponent.module.js.map +1 -1
- package/dist/extensions/components/StatsPanelComponent.js +0 -1
- package/dist/extensions/components/StatsPanelComponent.js.map +1 -1
- package/dist/extensions/components/StatsPanelComponent.min.js +1 -1
- package/dist/extensions/components/StatsPanelComponent.module.js +0 -1
- package/dist/extensions/components/StatsPanelComponent.module.js.map +1 -1
- package/dist/extensions/loaders/GLTFCloudLoader.js +7 -2
- package/dist/extensions/loaders/GLTFCloudLoader.js.map +1 -1
- package/dist/extensions/loaders/GLTFCloudLoader.min.js +1 -1
- package/dist/extensions/loaders/GLTFCloudLoader.module.js +7 -2
- package/dist/extensions/loaders/GLTFCloudLoader.module.js.map +1 -1
- package/dist/extensions/loaders/GLTFFileLoader.js +2 -1
- package/dist/extensions/loaders/GLTFFileLoader.js.map +1 -1
- package/dist/extensions/loaders/GLTFFileLoader.min.js +1 -1
- package/dist/extensions/loaders/GLTFFileLoader.module.js +2 -1
- package/dist/extensions/loaders/GLTFFileLoader.module.js.map +1 -1
- package/dist/extensions/loaders/IFCXLoader.js +10 -5
- package/dist/extensions/loaders/IFCXLoader.js.map +1 -1
- package/dist/extensions/loaders/IFCXLoader.min.js +1 -1
- package/dist/extensions/loaders/IFCXLoader.module.js +10 -5
- package/dist/extensions/loaders/IFCXLoader.module.js.map +1 -1
- package/dist/viewer-three.js +1901 -569
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +4 -4
- package/dist/viewer-three.module.js +1366 -451
- package/dist/viewer-three.module.js.map +1 -1
- package/extensions/components/AxesHelperComponent.ts +3 -0
- package/extensions/components/ExtentsHelperComponent.ts +5 -2
- package/extensions/components/GridHelperComponent.ts +1 -0
- package/extensions/components/LightHelperComponent.ts +2 -1
- package/extensions/components/StatsPanelComponent.ts +0 -1
- package/extensions/loaders/GLTFCloudLoader.ts +8 -2
- package/extensions/loaders/GLTFFileLoader.ts +3 -2
- package/extensions/loaders/IFCX/IFCXFileLoader.ts +11 -5
- package/lib/Viewer/Viewer.d.ts +6 -8
- package/lib/Viewer/components/CameraComponent.d.ts +1 -1
- package/lib/Viewer/components/ClippingPlaneComponent.d.ts +8 -0
- package/lib/Viewer/components/HighlighterComponent.d.ts +2 -2
- package/lib/Viewer/components/InfoComponent.d.ts +1 -1
- package/lib/Viewer/components/SectionsComponent.d.ts +15 -0
- package/lib/Viewer/components/WCSHelperComponent.d.ts +2 -2
- package/lib/Viewer/draggers/CuttingPlaneDragger.d.ts +6 -6
- package/lib/Viewer/draggers/OrbitDragger.d.ts +1 -1
- package/lib/Viewer/measurement/Snapper.d.ts +4 -4
- package/package.json +5 -5
- package/src/Viewer/Viewer.ts +59 -48
- package/src/Viewer/commands/GetSelected2.ts +1 -1
- package/src/Viewer/commands/SetSelected.ts +1 -1
- package/src/Viewer/commands/index.ts +1 -1
- package/src/Viewer/components/BackgroundComponent.ts +2 -1
- package/src/Viewer/components/CameraComponent.ts +6 -7
- package/src/Viewer/components/CanvasRemoveComponent.ts +0 -1
- package/src/Viewer/{scenes/Helpers.ts → components/ClippingPlaneComponent.ts} +22 -12
- package/src/Viewer/components/HighlighterComponent.ts +9 -5
- package/src/Viewer/components/HighlighterUtils.ts +2 -2
- package/src/Viewer/components/InfoComponent.ts +4 -4
- package/src/Viewer/components/SectionsComponent.ts +119 -0
- package/src/Viewer/components/SelectionComponent.ts +5 -3
- package/src/Viewer/components/WCSHelperComponent.ts +8 -6
- package/src/Viewer/components/index.ts +4 -0
- package/src/Viewer/draggers/CuttingPlaneDragger.ts +57 -34
- package/src/Viewer/draggers/MeasureLineDragger.ts +1 -1
- package/src/Viewer/draggers/OrbitDragger.ts +3 -3
- package/src/Viewer/helpers/SectionsHelper.js +1061 -0
- package/src/Viewer/helpers/WCSHelper.ts +31 -5
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +417 -92
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +19 -14
- package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +76 -9
- package/src/Viewer/loaders/GLTFBinaryParser.ts +2 -2
- package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +3 -2
- package/src/Viewer/loaders/GLTFFileDynamicLoader.ts +6 -4
- package/src/Viewer/measurement/Snapper.ts +6 -7
- package/src/Viewer/models/ModelImpl.ts +65 -28
- package/lib/Viewer/scenes/Helpers.d.ts +0 -7
- package/src/Viewer/postprocessing/SSAARenderPass.js +0 -245
|
@@ -23,19 +23,19 @@
|
|
|
23
23
|
|
|
24
24
|
import { draggersRegistry, commandsRegistry, Options, componentsRegistry, Info, Loader, loadersRegistry, CANVAS_EVENTS } from '@inweb/viewer-core';
|
|
25
25
|
export * from '@inweb/viewer-core';
|
|
26
|
-
import { EventDispatcher, Vector3, MOUSE, TOUCH, Spherical, Quaternion, Vector2, Object3D, BufferGeometry, Float32BufferAttribute, Line, LineBasicMaterial, Mesh, MeshBasicMaterial, DoubleSide, Line3, Raycaster, MathUtils, EdgesGeometry, Plane, Matrix4, Vector4, Controls, Box3, Clock, Color, PerspectiveCamera, OrthographicCamera, AmbientLight, DirectionalLight, HemisphereLight, REVISION, MeshPhongMaterial, WebGLRenderTarget, UnsignedByteType, RGBAFormat, CylinderGeometry, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial, TextureLoader, BufferAttribute, LinearMipmapLinearFilter, NearestMipmapLinearFilter, LinearMipmapNearestFilter, NearestMipmapNearestFilter, LinearFilter, NearestFilter, RepeatWrapping, MirroredRepeatWrapping, ClampToEdgeWrapping,
|
|
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, ShaderMaterial, PointsMaterial, Points, Sphere, ShapeUtils, CylinderGeometry, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial, TextureLoader, BufferAttribute, LinearMipmapLinearFilter, NearestMipmapLinearFilter, LinearMipmapNearestFilter, NearestMipmapNearestFilter, LinearFilter, NearestFilter, RepeatWrapping, MirroredRepeatWrapping, ClampToEdgeWrapping, MeshStandardMaterial, DataTexture, FloatType, TriangleStripDrawMode, TriangleFanDrawMode, LineSegments, LineLoop, Group, NormalBlending, LoadingManager, LoaderUtils, FileLoader, 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';
|
|
30
30
|
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';
|
|
31
|
+
import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2.js';
|
|
31
32
|
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
32
33
|
import { JSONParser } from '@streamparser/json';
|
|
33
34
|
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
|
|
34
35
|
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
|
|
35
36
|
import { FXAAPass } from 'three/examples/jsm/postprocessing/FXAAPass.js';
|
|
36
37
|
import { SMAAPass } from 'three/examples/jsm/postprocessing/SMAAPass.js';
|
|
37
|
-
import {
|
|
38
|
-
import { CopyShader } from 'three/examples/jsm/shaders/CopyShader.js';
|
|
38
|
+
import { SSAARenderPass } from 'three/examples/jsm/postprocessing/SSAARenderPass.js';
|
|
39
39
|
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js';
|
|
40
40
|
import { EventEmitter2 } from '@inweb/eventemitter2';
|
|
41
41
|
import { Markup } from '@inweb/markup';
|
|
@@ -751,9 +751,9 @@ const _line = new Line3();
|
|
|
751
751
|
const _center = new Vector3();
|
|
752
752
|
const _projection = new Vector3();
|
|
753
753
|
class Snapper {
|
|
754
|
-
constructor(camera,
|
|
754
|
+
constructor(camera, clippingPlanes, canvas) {
|
|
755
755
|
this.camera = camera;
|
|
756
|
-
this.
|
|
756
|
+
this.clippingPlanes = clippingPlanes;
|
|
757
757
|
this.canvas = canvas;
|
|
758
758
|
this.threshold = 0.0001;
|
|
759
759
|
this.raycaster = new Raycaster();
|
|
@@ -784,7 +784,7 @@ class Snapper {
|
|
|
784
784
|
};
|
|
785
785
|
let intersects = this.raycaster.intersectObjects(objects, recursive);
|
|
786
786
|
if (clip) {
|
|
787
|
-
const clippingPlanes = this.
|
|
787
|
+
const clippingPlanes = this.clippingPlanes;
|
|
788
788
|
clippingPlanes.forEach((plane) => {
|
|
789
789
|
intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
|
|
790
790
|
});
|
|
@@ -815,7 +815,7 @@ class Snapper {
|
|
|
815
815
|
const object = intersections[0].object;
|
|
816
816
|
const intersectionPoint = intersections[0].point;
|
|
817
817
|
const localPoint = object.worldToLocal(intersectionPoint.clone());
|
|
818
|
-
let snapPoint;
|
|
818
|
+
let snapPoint = undefined;
|
|
819
819
|
let snapDistance = this.getDetectRadius(intersectionPoint);
|
|
820
820
|
const geometry = object.geometry;
|
|
821
821
|
const positions = geometry.attributes.position.array;
|
|
@@ -871,7 +871,7 @@ class OrbitDragger {
|
|
|
871
871
|
this.orbit.object = this.viewer.camera;
|
|
872
872
|
this.orbit.update();
|
|
873
873
|
};
|
|
874
|
-
this.
|
|
874
|
+
this.updateZoomSpeed = ({ data: options }) => {
|
|
875
875
|
this.orbit.zoomSpeed = Math.abs(this.orbit.zoomSpeed) * (options.reverseZoomWheel ? -1 : 1);
|
|
876
876
|
};
|
|
877
877
|
this.controlsStart = () => {
|
|
@@ -930,7 +930,7 @@ class OrbitDragger {
|
|
|
930
930
|
this.viewer.addEventListener("zoom", this.updateControls);
|
|
931
931
|
this.viewer.addEventListener("drawviewpoint", this.updateControls);
|
|
932
932
|
this.viewer.addEventListener("changecameramode", this.updateControlsCamera);
|
|
933
|
-
this.viewer.addEventListener("optionschange", this.
|
|
933
|
+
this.viewer.addEventListener("optionschange", this.updateZoomSpeed);
|
|
934
934
|
this.viewer.addEventListener("contextmenu", this.stopContextMenu);
|
|
935
935
|
this.updateControls();
|
|
936
936
|
this.updateControlsCamera();
|
|
@@ -942,7 +942,7 @@ class OrbitDragger {
|
|
|
942
942
|
this.viewer.removeEventListener("zoom", this.updateControls);
|
|
943
943
|
this.viewer.removeEventListener("drawviewpoint", this.updateControls);
|
|
944
944
|
this.viewer.removeEventListener("changecameramode", this.updateControlsCamera);
|
|
945
|
-
this.viewer.removeEventListener("optionschange", this.
|
|
945
|
+
this.viewer.removeEventListener("optionschange", this.updateZoomSpeed);
|
|
946
946
|
this.viewer.removeEventListener("contextmenu", this.stopContextMenu);
|
|
947
947
|
this.orbit.removeEventListener("start", this.controlsStart);
|
|
948
948
|
this.orbit.removeEventListener("change", this.controlsChange);
|
|
@@ -954,13 +954,17 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
954
954
|
constructor(viewer) {
|
|
955
955
|
super(viewer);
|
|
956
956
|
this.helpers = [];
|
|
957
|
-
this.activeHelper =
|
|
957
|
+
this.activeHelper = undefined;
|
|
958
|
+
this.transformUpdate = () => {
|
|
959
|
+
this.viewer.update();
|
|
960
|
+
};
|
|
958
961
|
this.transformChange = () => {
|
|
959
962
|
if (!this.activeHelper)
|
|
960
963
|
return;
|
|
961
964
|
const plane = this.activeHelper.plane;
|
|
962
965
|
plane.normal.copy(new Vector3(0, 0, -1)).applyQuaternion(this.activeHelper.quaternion);
|
|
963
966
|
plane.constant = -this.activeHelper.position.dot(plane.normal);
|
|
967
|
+
this.viewer.emitEvent({ type: "changecuttingplanes" });
|
|
964
968
|
this.viewer.update();
|
|
965
969
|
this.changed = true;
|
|
966
970
|
};
|
|
@@ -972,25 +976,29 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
972
976
|
this.orbit.enabled = !event.value;
|
|
973
977
|
this.translate.enabled = !event.value;
|
|
974
978
|
};
|
|
975
|
-
this.
|
|
979
|
+
this.syncHelpers = () => {
|
|
976
980
|
const extentsSize = this.viewer.extents.getSize(new Vector3()).length() || 1;
|
|
977
|
-
this.
|
|
981
|
+
const extentsCenter = this.viewer.extents.getCenter(new Vector3());
|
|
982
|
+
this.helpers.forEach((planeHelper) => {
|
|
983
|
+
planeHelper.size = extentsSize;
|
|
984
|
+
planeHelper.position.copy(planeHelper.plane.projectPoint(extentsCenter, new Vector3()));
|
|
985
|
+
});
|
|
978
986
|
this.viewer.update();
|
|
979
987
|
};
|
|
980
|
-
this.updateTransformCamera = () => {
|
|
981
|
-
this.translate.camera = this.viewer.camera;
|
|
982
|
-
this.rotate.camera = this.viewer.camera;
|
|
983
|
-
this.snapper.camera = this.viewer.camera;
|
|
984
|
-
};
|
|
985
988
|
this.clearHelpers = () => {
|
|
986
989
|
this.setActiveHelper();
|
|
987
990
|
this.helpers.forEach((helper) => {
|
|
988
991
|
helper.removeFromParent();
|
|
989
992
|
helper.dispose();
|
|
990
993
|
});
|
|
991
|
-
this.helpers =
|
|
994
|
+
this.helpers.length = 0;
|
|
992
995
|
this.viewer.update();
|
|
993
996
|
};
|
|
997
|
+
this.updateTransformCamera = () => {
|
|
998
|
+
this.translate.camera = this.viewer.camera;
|
|
999
|
+
this.rotate.camera = this.viewer.camera;
|
|
1000
|
+
this.snapper.camera = this.viewer.camera;
|
|
1001
|
+
};
|
|
994
1002
|
this.onKeyDown = (event) => {
|
|
995
1003
|
if (event.key === "Shift")
|
|
996
1004
|
this.rotate.setRotationSnap(Math.PI / 4);
|
|
@@ -1034,12 +1042,9 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
1034
1042
|
this.transformChange();
|
|
1035
1043
|
event.stopPropagation();
|
|
1036
1044
|
};
|
|
1037
|
-
|
|
1038
|
-
viewer.renderer.clippingPlanes = [];
|
|
1039
|
-
this.clippingPlanes = viewer.renderer.clippingPlanes;
|
|
1040
|
-
this.clippingPlanes.forEach((plane) => this.addHelper(plane));
|
|
1045
|
+
viewer.clippingPlanes.forEach((plane) => this.addHelper(plane));
|
|
1041
1046
|
const extentsSize = viewer.extents.getSize(new Vector3()).length() || 1;
|
|
1042
|
-
this.snapper = new Snapper(viewer.camera, viewer.
|
|
1047
|
+
this.snapper = new Snapper(viewer.camera, viewer.clippingPlanes, viewer.canvas);
|
|
1043
1048
|
this.snapper.threshold = extentsSize / 10000;
|
|
1044
1049
|
this.downPosition = new Vector2();
|
|
1045
1050
|
this.position0 = new Vector3();
|
|
@@ -1050,7 +1055,8 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
1050
1055
|
this.translate.showX = false;
|
|
1051
1056
|
this.translate.showY = false;
|
|
1052
1057
|
this.translate.showZ = true;
|
|
1053
|
-
this.translate.addEventListener("change", this.
|
|
1058
|
+
this.translate.addEventListener("change", this.transformUpdate);
|
|
1059
|
+
this.translate.addEventListener("objectChange", this.transformChange);
|
|
1054
1060
|
this.translate.addEventListener("dragging-changed", this.translateDrag);
|
|
1055
1061
|
this.viewer.helpers.add(this.translate.getHelper());
|
|
1056
1062
|
this.rotate = new TransformControls(viewer.camera, viewer.canvas);
|
|
@@ -1059,15 +1065,18 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
1059
1065
|
this.rotate.showX = true;
|
|
1060
1066
|
this.rotate.showY = true;
|
|
1061
1067
|
this.rotate.showZ = false;
|
|
1062
|
-
this.rotate.addEventListener("change", this.
|
|
1068
|
+
this.rotate.addEventListener("change", this.transformUpdate);
|
|
1069
|
+
this.rotate.addEventListener("objectChange", this.transformChange);
|
|
1063
1070
|
this.rotate.addEventListener("dragging-changed", this.rotateDrag);
|
|
1064
1071
|
this.viewer.helpers.add(this.rotate.getHelper());
|
|
1065
1072
|
this.setActiveHelper(this.helpers[this.helpers.length - 1]);
|
|
1066
|
-
this.viewer.addEventListener("explode", this.
|
|
1067
|
-
this.viewer.addEventListener("
|
|
1068
|
-
this.viewer.addEventListener("
|
|
1069
|
-
this.viewer.addEventListener("
|
|
1073
|
+
this.viewer.addEventListener("explode", this.syncHelpers);
|
|
1074
|
+
this.viewer.addEventListener("hide", this.syncHelpers);
|
|
1075
|
+
this.viewer.addEventListener("isolate", this.syncHelpers);
|
|
1076
|
+
this.viewer.addEventListener("show", this.syncHelpers);
|
|
1077
|
+
this.viewer.addEventListener("showall", this.syncHelpers);
|
|
1070
1078
|
this.viewer.addEventListener("clearslices", this.clearHelpers);
|
|
1079
|
+
this.viewer.addEventListener("changecameramode", this.updateTransformCamera);
|
|
1071
1080
|
this.viewer.canvas.addEventListener("pointerdown", this.onPointerDown, true);
|
|
1072
1081
|
this.viewer.canvas.addEventListener("pointerup", this.onPointerUp, true);
|
|
1073
1082
|
this.viewer.canvas.addEventListener("pointercancel", this.onPointerCancel, true);
|
|
@@ -1077,23 +1086,27 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
1077
1086
|
this.viewer.update();
|
|
1078
1087
|
}
|
|
1079
1088
|
dispose() {
|
|
1080
|
-
this.viewer.removeEventListener("explode", this.
|
|
1081
|
-
this.viewer.removeEventListener("
|
|
1082
|
-
this.viewer.removeEventListener("
|
|
1083
|
-
this.viewer.removeEventListener("
|
|
1089
|
+
this.viewer.removeEventListener("explode", this.syncHelpers);
|
|
1090
|
+
this.viewer.removeEventListener("hide", this.syncHelpers);
|
|
1091
|
+
this.viewer.removeEventListener("isolate", this.syncHelpers);
|
|
1092
|
+
this.viewer.removeEventListener("show", this.syncHelpers);
|
|
1093
|
+
this.viewer.removeEventListener("showall", this.syncHelpers);
|
|
1084
1094
|
this.viewer.removeEventListener("clearslices", this.clearHelpers);
|
|
1095
|
+
this.viewer.removeEventListener("changecameramode", this.updateTransformCamera);
|
|
1085
1096
|
this.viewer.canvas.removeEventListener("pointerdown", this.onPointerDown, true);
|
|
1086
1097
|
this.viewer.canvas.removeEventListener("pointerup", this.onPointerUp, true);
|
|
1087
1098
|
this.viewer.canvas.removeEventListener("pointercancel", this.onPointerCancel, true);
|
|
1088
1099
|
this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
|
|
1089
1100
|
window.removeEventListener("keydown", this.onKeyDown);
|
|
1090
1101
|
window.removeEventListener("keyup", this.onKeyUp);
|
|
1091
|
-
this.translate.removeEventListener("change", this.
|
|
1102
|
+
this.translate.removeEventListener("change", this.transformUpdate);
|
|
1103
|
+
this.translate.removeEventListener("objectChange", this.transformChange);
|
|
1092
1104
|
this.translate.removeEventListener("dragging-changed", this.translateDrag);
|
|
1093
1105
|
this.translate.getHelper().removeFromParent();
|
|
1094
1106
|
this.translate.detach();
|
|
1095
1107
|
this.translate.dispose();
|
|
1096
|
-
this.rotate.removeEventListener("change", this.
|
|
1108
|
+
this.rotate.removeEventListener("change", this.transformUpdate);
|
|
1109
|
+
this.rotate.removeEventListener("objectChange", this.transformChange);
|
|
1097
1110
|
this.rotate.removeEventListener("dragging-changed", this.rotateDrag);
|
|
1098
1111
|
this.rotate.getHelper().removeFromParent();
|
|
1099
1112
|
this.rotate.detach();
|
|
@@ -1102,8 +1115,8 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
1102
1115
|
helper.removeFromParent();
|
|
1103
1116
|
helper.dispose();
|
|
1104
1117
|
});
|
|
1105
|
-
this.helpers =
|
|
1106
|
-
this.activeHelper =
|
|
1118
|
+
this.helpers.length = 0;
|
|
1119
|
+
this.activeHelper = undefined;
|
|
1107
1120
|
super.dispose();
|
|
1108
1121
|
}
|
|
1109
1122
|
addHelper(plane) {
|
|
@@ -1155,9 +1168,10 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
1155
1168
|
const extentsCenter = this.viewer.extents.getCenter(new Vector3());
|
|
1156
1169
|
const constant = -extentsCenter.dot(normal);
|
|
1157
1170
|
const plane = new Plane(normal, constant);
|
|
1158
|
-
this.clippingPlanes.push(plane);
|
|
1171
|
+
this.viewer.clippingPlanes.push(plane);
|
|
1159
1172
|
const helper = this.addHelper(plane);
|
|
1160
1173
|
this.setActiveHelper(helper);
|
|
1174
|
+
this.viewer.emitEvent({ type: "changecuttingplanes" });
|
|
1161
1175
|
}
|
|
1162
1176
|
addPlaneX() {
|
|
1163
1177
|
this.addPlane(new Vector3(-1, 0, 0));
|
|
@@ -1172,13 +1186,14 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
1172
1186
|
if (!this.activeHelper)
|
|
1173
1187
|
return;
|
|
1174
1188
|
const helper = this.activeHelper;
|
|
1175
|
-
const index = this.clippingPlanes.indexOf(helper.plane);
|
|
1189
|
+
const index = this.viewer.clippingPlanes.indexOf(helper.plane);
|
|
1176
1190
|
if (index !== -1)
|
|
1177
|
-
this.clippingPlanes.splice(index, 1);
|
|
1191
|
+
this.viewer.clippingPlanes.splice(index, 1);
|
|
1178
1192
|
this.helpers = this.helpers.filter((x) => x !== helper);
|
|
1179
1193
|
helper.removeFromParent();
|
|
1180
1194
|
helper.dispose();
|
|
1181
1195
|
this.setActiveHelper(this.helpers[this.helpers.length - 1]);
|
|
1196
|
+
this.viewer.emitEvent({ type: "changecuttingplanes" });
|
|
1182
1197
|
}
|
|
1183
1198
|
}
|
|
1184
1199
|
|
|
@@ -1372,7 +1387,7 @@ class MeasureLineDragger extends OrbitDragger {
|
|
|
1372
1387
|
this.line = new MeasureLine(this.overlay, this.scale, this.units, this.precision);
|
|
1373
1388
|
this.overlay.addLine(this.line);
|
|
1374
1389
|
const extentsSize = this.viewer.extents.getSize(new Vector3()).length() || 1;
|
|
1375
|
-
this.snapper = new Snapper(viewer.camera, viewer.
|
|
1390
|
+
this.snapper = new Snapper(viewer.camera, viewer.clippingPlanes, viewer.canvas);
|
|
1376
1391
|
this.snapper.threshold = extentsSize / 10000;
|
|
1377
1392
|
this.objects = [];
|
|
1378
1393
|
this.updateObjects();
|
|
@@ -2651,8 +2666,8 @@ commands.registerCommand("left", (viewer) => setDefaultViewPosition(viewer, "lef
|
|
|
2651
2666
|
commands.registerCommand("right", (viewer) => setDefaultViewPosition(viewer, "right"));
|
|
2652
2667
|
commands.registerCommand("front", (viewer) => setDefaultViewPosition(viewer, "front"));
|
|
2653
2668
|
commands.registerCommand("back", (viewer) => setDefaultViewPosition(viewer, "back"));
|
|
2654
|
-
commands.registerCommand("sw", (viewer) => setDefaultViewPosition(viewer, "sw"));
|
|
2655
2669
|
commands.registerCommand("se", (viewer) => setDefaultViewPosition(viewer, "se"));
|
|
2670
|
+
commands.registerCommand("sw", (viewer) => setDefaultViewPosition(viewer, "sw"));
|
|
2656
2671
|
commands.registerCommand("ne", (viewer) => setDefaultViewPosition(viewer, "ne"));
|
|
2657
2672
|
commands.registerCommand("nw", (viewer) => setDefaultViewPosition(viewer, "nw"));
|
|
2658
2673
|
commands.registerCommandAlias("clearMarkup", "clearOverlay");
|
|
@@ -2675,6 +2690,7 @@ class BackgroundComponent {
|
|
|
2675
2690
|
this.syncOptions = () => {
|
|
2676
2691
|
this.backgroundColor.setHex(0xffffff);
|
|
2677
2692
|
this.viewer.renderer.setClearColor(this.backgroundColor);
|
|
2693
|
+
this.viewer.update();
|
|
2678
2694
|
};
|
|
2679
2695
|
this.viewer = viewer;
|
|
2680
2696
|
this.backgroundColor = new Color(0xffffff);
|
|
@@ -2684,13 +2700,13 @@ class BackgroundComponent {
|
|
|
2684
2700
|
}
|
|
2685
2701
|
dispose() {
|
|
2686
2702
|
this.viewer.removeEventListener("optionschange", this.syncOptions);
|
|
2687
|
-
this.viewer.scene.background =
|
|
2703
|
+
this.viewer.scene.background = null;
|
|
2688
2704
|
}
|
|
2689
2705
|
}
|
|
2690
2706
|
|
|
2691
2707
|
class CameraComponent {
|
|
2692
2708
|
constructor(viewer) {
|
|
2693
|
-
this.
|
|
2709
|
+
this.syncOptions = () => {
|
|
2694
2710
|
this.switchCameraMode(this.viewer.options.cameraMode);
|
|
2695
2711
|
};
|
|
2696
2712
|
this.geometryEnd = () => {
|
|
@@ -2721,13 +2737,13 @@ class CameraComponent {
|
|
|
2721
2737
|
};
|
|
2722
2738
|
this.viewer = viewer;
|
|
2723
2739
|
this.viewer.addEventListener("databasechunk", this.geometryEnd);
|
|
2724
|
-
this.viewer.addEventListener("optionschange", this.
|
|
2725
|
-
this.viewer.addEventListener("initialize", this.
|
|
2740
|
+
this.viewer.addEventListener("optionschange", this.syncOptions);
|
|
2741
|
+
this.viewer.addEventListener("initialize", this.syncOptions);
|
|
2726
2742
|
}
|
|
2727
2743
|
dispose() {
|
|
2728
2744
|
this.viewer.removeEventListener("databasechunk", this.geometryEnd);
|
|
2729
|
-
this.viewer.removeEventListener("optionschange", this.
|
|
2730
|
-
this.viewer.removeEventListener("initialize", this.
|
|
2745
|
+
this.viewer.removeEventListener("optionschange", this.syncOptions);
|
|
2746
|
+
this.viewer.removeEventListener("initialize", this.syncOptions);
|
|
2731
2747
|
}
|
|
2732
2748
|
getCameraMode(camera) {
|
|
2733
2749
|
return camera.isOrthographicCamera ? "orthographic" : "perspective";
|
|
@@ -2750,7 +2766,6 @@ class CameraComponent {
|
|
|
2750
2766
|
camera.updateProjectionMatrix();
|
|
2751
2767
|
this.viewer.camera = camera;
|
|
2752
2768
|
this.viewer.renderPass.camera = camera;
|
|
2753
|
-
this.viewer.helpersPass.camera = camera;
|
|
2754
2769
|
this.viewer.ssaaRenderPass.camera = camera;
|
|
2755
2770
|
this.viewer.update();
|
|
2756
2771
|
}
|
|
@@ -2761,7 +2776,7 @@ class CameraComponent {
|
|
|
2761
2776
|
if (mode === this.getCameraMode(currentCamera))
|
|
2762
2777
|
return;
|
|
2763
2778
|
const target = this.viewer.target.clone();
|
|
2764
|
-
let camera;
|
|
2779
|
+
let camera = null;
|
|
2765
2780
|
if (currentCamera.isOrthographicCamera) {
|
|
2766
2781
|
const fov = currentCamera.userData.fov || 45;
|
|
2767
2782
|
const fieldHeight = (currentCamera.top - currentCamera.bottom) / currentCamera.zoom;
|
|
@@ -2894,7 +2909,7 @@ class InfoComponent {
|
|
|
2894
2909
|
console.log("WebGL Renderer:", this.viewer.info.system.webglRenderer);
|
|
2895
2910
|
console.log("WebGL Vendor:", this.viewer.info.system.webglVendor);
|
|
2896
2911
|
this.resize();
|
|
2897
|
-
this.
|
|
2912
|
+
this.syncOptions({ data: this.viewer.options });
|
|
2898
2913
|
};
|
|
2899
2914
|
this.clear = () => {
|
|
2900
2915
|
this.viewer.info.performance.timeToFirstRender = 0;
|
|
@@ -2918,7 +2933,7 @@ class InfoComponent {
|
|
|
2918
2933
|
this.viewer.info.memory.totalEstimatedGpuBytes = 0;
|
|
2919
2934
|
this.viewer.info.memory.usedJSHeapSize = 0;
|
|
2920
2935
|
};
|
|
2921
|
-
this.
|
|
2936
|
+
this.syncOptions = ({ data: options }) => {
|
|
2922
2937
|
if (options.antialiasing === false)
|
|
2923
2938
|
this.viewer.info.render.antialiasing = "";
|
|
2924
2939
|
else if (options.antialiasing === true)
|
|
@@ -2993,7 +3008,7 @@ class InfoComponent {
|
|
|
2993
3008
|
this.frames = 0;
|
|
2994
3009
|
this.viewer.addEventListener("initialize", this.initialize);
|
|
2995
3010
|
this.viewer.addEventListener("clear", this.clear);
|
|
2996
|
-
this.viewer.addEventListener("optionschange", this.
|
|
3011
|
+
this.viewer.addEventListener("optionschange", this.syncOptions);
|
|
2997
3012
|
this.viewer.addEventListener("geometrystart", this.geometryStart);
|
|
2998
3013
|
this.viewer.addEventListener("databasechunk", this.databaseChunk);
|
|
2999
3014
|
this.viewer.addEventListener("geometryend", this.geometryEnd);
|
|
@@ -3004,7 +3019,7 @@ class InfoComponent {
|
|
|
3004
3019
|
dispose() {
|
|
3005
3020
|
this.viewer.removeEventListener("initialize", this.initialize);
|
|
3006
3021
|
this.viewer.removeEventListener("clear", this.clear);
|
|
3007
|
-
this.viewer.removeEventListener("optionschange", this.
|
|
3022
|
+
this.viewer.removeEventListener("optionschange", this.syncOptions);
|
|
3008
3023
|
this.viewer.removeEventListener("geometrystart", this.geometryStart);
|
|
3009
3024
|
this.viewer.removeEventListener("databasechunk", this.databaseChunk);
|
|
3010
3025
|
this.viewer.removeEventListener("geometryend", this.geometryEnd);
|
|
@@ -3052,7 +3067,6 @@ class CanvasRemoveComponent {
|
|
|
3052
3067
|
}
|
|
3053
3068
|
dispose() {
|
|
3054
3069
|
this.mutationObserver.disconnect();
|
|
3055
|
-
this.mutationObserver = undefined;
|
|
3056
3070
|
}
|
|
3057
3071
|
}
|
|
3058
3072
|
|
|
@@ -3142,28 +3156,38 @@ class HighlighterComponent {
|
|
|
3142
3156
|
polygonOffset: true,
|
|
3143
3157
|
polygonOffsetFactor: 1,
|
|
3144
3158
|
polygonOffsetUnits: 1,
|
|
3159
|
+
clippingPlanes: this.viewer.clippingPlanes,
|
|
3145
3160
|
});
|
|
3146
3161
|
this.edgesMaterial = new LineMaterial({
|
|
3147
3162
|
linewidth: 1.5,
|
|
3148
3163
|
resolution: new Vector2(window.innerWidth, window.innerHeight),
|
|
3164
|
+
clippingPlanes: this.viewer.clippingPlanes,
|
|
3149
3165
|
});
|
|
3150
3166
|
this.lineMaterial = new LineBasicMaterial({
|
|
3151
3167
|
transparent: true,
|
|
3152
3168
|
depthTest: true,
|
|
3153
3169
|
depthWrite: true,
|
|
3170
|
+
clippingPlanes: this.viewer.clippingPlanes,
|
|
3154
3171
|
});
|
|
3155
3172
|
this.lineGlowMaterial = new LineMaterial({
|
|
3156
3173
|
linewidth: 1.5,
|
|
3157
3174
|
transparent: true,
|
|
3158
3175
|
opacity: 0.8,
|
|
3159
3176
|
resolution: new Vector2(window.innerWidth, window.innerHeight),
|
|
3177
|
+
clippingPlanes: this.viewer.clippingPlanes,
|
|
3160
3178
|
});
|
|
3161
3179
|
this.syncHighlightColors();
|
|
3162
3180
|
};
|
|
3163
|
-
this.
|
|
3181
|
+
this.syncOptions = () => {
|
|
3164
3182
|
this.syncHighlightColors();
|
|
3165
3183
|
this.viewer.update();
|
|
3166
3184
|
};
|
|
3185
|
+
this.viewerResize = (event) => {
|
|
3186
|
+
var _a, _b, _c;
|
|
3187
|
+
(_a = this.renderTarget) === null || _a === void 0 ? void 0 : _a.setSize(event.width, event.height);
|
|
3188
|
+
(_b = this.edgesMaterial) === null || _b === void 0 ? void 0 : _b.resolution.set(event.width, event.height);
|
|
3189
|
+
(_c = this.lineGlowMaterial) === null || _c === void 0 ? void 0 : _c.resolution.set(event.width, event.height);
|
|
3190
|
+
};
|
|
3167
3191
|
this.viewer = viewer;
|
|
3168
3192
|
const gl2 = viewer.canvas.getContext("webgl2");
|
|
3169
3193
|
if (gl2) {
|
|
@@ -3176,13 +3200,13 @@ class HighlighterComponent {
|
|
|
3176
3200
|
});
|
|
3177
3201
|
}
|
|
3178
3202
|
this.viewer.addEventListener("databasechunk", this.geometryEnd);
|
|
3179
|
-
this.viewer.addEventListener("optionschange", this.
|
|
3203
|
+
this.viewer.addEventListener("optionschange", this.syncOptions);
|
|
3180
3204
|
this.viewer.addEventListener("resize", this.viewerResize);
|
|
3181
3205
|
this.geometryEnd();
|
|
3182
3206
|
}
|
|
3183
3207
|
dispose() {
|
|
3184
3208
|
this.viewer.removeEventListener("databasechunk", this.geometryEnd);
|
|
3185
|
-
this.viewer.removeEventListener("optionschange", this.
|
|
3209
|
+
this.viewer.removeEventListener("optionschange", this.syncOptions);
|
|
3186
3210
|
this.viewer.removeEventListener("resize", this.viewerResize);
|
|
3187
3211
|
}
|
|
3188
3212
|
highlight(objects) {
|
|
@@ -3261,12 +3285,6 @@ class HighlighterComponent {
|
|
|
3261
3285
|
wireframe.visible = edgesVisibility;
|
|
3262
3286
|
});
|
|
3263
3287
|
}
|
|
3264
|
-
viewerResize(event) {
|
|
3265
|
-
var _a, _b, _c;
|
|
3266
|
-
(_a = this.renderTarget) === null || _a === void 0 ? void 0 : _a.setSize(event.width, event.height);
|
|
3267
|
-
(_b = this.edgesMaterial) === null || _b === void 0 ? void 0 : _b.resolution.set(event.width, event.height);
|
|
3268
|
-
(_c = this.lineGlowMaterial) === null || _c === void 0 ? void 0 : _c.resolution.set(event.width, event.height);
|
|
3269
|
-
}
|
|
3270
3288
|
}
|
|
3271
3289
|
|
|
3272
3290
|
class SelectionComponent {
|
|
@@ -3283,7 +3301,7 @@ class SelectionComponent {
|
|
|
3283
3301
|
if (upPosition.distanceTo(this.downPosition) !== 0)
|
|
3284
3302
|
return;
|
|
3285
3303
|
const extentsSize = this.viewer.extents.getSize(new Vector3()).length() || 1;
|
|
3286
|
-
const snapper = new Snapper(this.viewer.camera, this.viewer.
|
|
3304
|
+
const snapper = new Snapper(this.viewer.camera, this.viewer.clippingPlanes, this.viewer.canvas);
|
|
3287
3305
|
snapper.threshold = extentsSize / 10000;
|
|
3288
3306
|
let intersections = [];
|
|
3289
3307
|
this.viewer.models.forEach((model) => {
|
|
@@ -3380,9 +3398,824 @@ class SelectionComponent {
|
|
|
3380
3398
|
}
|
|
3381
3399
|
}
|
|
3382
3400
|
|
|
3401
|
+
class ClippingPlaneComponent {
|
|
3402
|
+
constructor(viewer) {
|
|
3403
|
+
this.applyClippingPlanes = () => {
|
|
3404
|
+
this.viewer.models.forEach((model) => {
|
|
3405
|
+
model.scene.traverse((object) => {
|
|
3406
|
+
if (object.material) {
|
|
3407
|
+
const materials = Array.isArray(object.material) ? object.material : [object.material];
|
|
3408
|
+
materials.forEach((material) => (material.clippingPlanes = this.viewer.clippingPlanes));
|
|
3409
|
+
}
|
|
3410
|
+
});
|
|
3411
|
+
});
|
|
3412
|
+
};
|
|
3413
|
+
this.viewer = viewer;
|
|
3414
|
+
this.viewer.addEventListener("geometryend", this.applyClippingPlanes);
|
|
3415
|
+
this.viewer.addEventListener("changecuttingplanes", this.applyClippingPlanes);
|
|
3416
|
+
}
|
|
3417
|
+
dispose() {
|
|
3418
|
+
this.viewer.removeEventListener("geometryend", this.applyClippingPlanes);
|
|
3419
|
+
this.viewer.removeEventListener("changecuttingplanes", this.applyClippingPlanes);
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
|
|
3423
|
+
class PointHashGrid {
|
|
3424
|
+
constructor(tolerance = 1e-5) {
|
|
3425
|
+
this.tolerance = tolerance;
|
|
3426
|
+
this.points = [];
|
|
3427
|
+
this.grid = new Map();
|
|
3428
|
+
}
|
|
3429
|
+
_hash(hx, hy, hz) {
|
|
3430
|
+
return `${hx},${hy},${hz}`;
|
|
3431
|
+
}
|
|
3432
|
+
add(v) {
|
|
3433
|
+
const hx = Math.round(v.x / this.tolerance);
|
|
3434
|
+
const hy = Math.round(v.y / this.tolerance);
|
|
3435
|
+
const hz = Math.round(v.z / this.tolerance);
|
|
3436
|
+
for (let i = -1; i <= 1; i++) {
|
|
3437
|
+
for (let j = -1; j <= 1; j++) {
|
|
3438
|
+
for (let k = -1; k <= 1; k++) {
|
|
3439
|
+
const hash = this._hash(hx + i, hy + j, hz + k);
|
|
3440
|
+
const cell = this.grid.get(hash);
|
|
3441
|
+
if (cell) {
|
|
3442
|
+
for (const id of cell) {
|
|
3443
|
+
if (this.points[id].distanceTo(v) <= this.tolerance) return id;
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
const id = this.points.length;
|
|
3450
|
+
this.points.push(v.clone());
|
|
3451
|
+
const centerHash = this._hash(hx, hy, hz);
|
|
3452
|
+
if (!this.grid.has(centerHash)) this.grid.set(centerHash, []);
|
|
3453
|
+
this.grid.get(centerHash).push(id);
|
|
3454
|
+
return id;
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
class SectionsHelper extends Object3D {
|
|
3458
|
+
constructor() {
|
|
3459
|
+
super();
|
|
3460
|
+
this.type = "SectionsHelper";
|
|
3461
|
+
this.flags = {
|
|
3462
|
+
fillEnabled: true,
|
|
3463
|
+
fillColor: "#fffde7",
|
|
3464
|
+
hatchEnabled: true,
|
|
3465
|
+
hatchColor: "#000000",
|
|
3466
|
+
hatchScale: 8.0,
|
|
3467
|
+
outlineEnabled: true,
|
|
3468
|
+
outlineColor: "#000000",
|
|
3469
|
+
outlineWidth: 2,
|
|
3470
|
+
boundaryOnly: true,
|
|
3471
|
+
showDebugSeams: false,
|
|
3472
|
+
showDebugPoints: false,
|
|
3473
|
+
showDebugSegments: false,
|
|
3474
|
+
showDebugGaps: false,
|
|
3475
|
+
showDebugInfo: false,
|
|
3476
|
+
useObjFillColor: false,
|
|
3477
|
+
useObjOutlineColor: false,
|
|
3478
|
+
};
|
|
3479
|
+
this._caps = [];
|
|
3480
|
+
this._outlines = [];
|
|
3481
|
+
this._debugPoints = [];
|
|
3482
|
+
this._debugSegments = [];
|
|
3483
|
+
this._debugGaps = [];
|
|
3484
|
+
this._vA = new Vector3();
|
|
3485
|
+
this._vB = new Vector3();
|
|
3486
|
+
this._vC = new Vector3();
|
|
3487
|
+
this._worldBox = new Box3();
|
|
3488
|
+
}
|
|
3489
|
+
dispose() {
|
|
3490
|
+
const disposeMesh = (item) => {
|
|
3491
|
+
if (item.geometry) item.geometry.dispose();
|
|
3492
|
+
if (item.material) item.material.dispose();
|
|
3493
|
+
this.remove(item);
|
|
3494
|
+
};
|
|
3495
|
+
this._caps.forEach(disposeMesh);
|
|
3496
|
+
this._outlines.forEach(disposeMesh);
|
|
3497
|
+
this._debugPoints.forEach(disposeMesh);
|
|
3498
|
+
this._debugSegments.forEach(disposeMesh);
|
|
3499
|
+
this._debugGaps.forEach(disposeMesh);
|
|
3500
|
+
this._caps.length = 0;
|
|
3501
|
+
this._outlines.length = 0;
|
|
3502
|
+
this._debugPoints.length = 0;
|
|
3503
|
+
this._debugSegments.length = 0;
|
|
3504
|
+
this._debugGaps.length = 0;
|
|
3505
|
+
}
|
|
3506
|
+
setSize(width, height) {
|
|
3507
|
+
this._outlines.forEach((o) => {
|
|
3508
|
+
if (o.material.resolution) o.material.resolution.set(width, height);
|
|
3509
|
+
});
|
|
3510
|
+
this._debugSegments.forEach((s) => {
|
|
3511
|
+
if (s.material.resolution) s.material.resolution.set(width, height);
|
|
3512
|
+
});
|
|
3513
|
+
}
|
|
3514
|
+
_ensureHelpersCount(count) {
|
|
3515
|
+
const hatchVertexShader = `
|
|
3516
|
+
#include <common>
|
|
3517
|
+
#include <logdepthbuf_pars_vertex>
|
|
3518
|
+
#include <clipping_planes_pars_vertex>
|
|
3519
|
+
|
|
3520
|
+
attribute float aHatchDir;
|
|
3521
|
+
attribute vec3 aFillColor;
|
|
3522
|
+
|
|
3523
|
+
varying vec3 vWP;
|
|
3524
|
+
varying float vHatchDir;
|
|
3525
|
+
varying vec3 vFillColor;
|
|
3526
|
+
|
|
3527
|
+
void main() {
|
|
3528
|
+
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
|
3529
|
+
#include <clipping_planes_vertex>
|
|
3530
|
+
|
|
3531
|
+
vWP = (modelMatrix * vec4(position, 1.0)).xyz;
|
|
3532
|
+
vHatchDir = aHatchDir;
|
|
3533
|
+
vFillColor = aFillColor;
|
|
3534
|
+
|
|
3535
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
3536
|
+
#include <logdepthbuf_vertex>
|
|
3537
|
+
}
|
|
3538
|
+
`;
|
|
3539
|
+
const hatchFragmentShader = `
|
|
3540
|
+
#include <common>
|
|
3541
|
+
#include <logdepthbuf_pars_fragment>
|
|
3542
|
+
#include <clipping_planes_pars_fragment>
|
|
3543
|
+
|
|
3544
|
+
uniform vec3 lineColor;
|
|
3545
|
+
uniform float uHatchScale;
|
|
3546
|
+
uniform float uHatchEnabled;
|
|
3547
|
+
|
|
3548
|
+
varying vec3 vWP;
|
|
3549
|
+
varying float vHatchDir;
|
|
3550
|
+
varying vec3 vFillColor;
|
|
3551
|
+
|
|
3552
|
+
void main() {
|
|
3553
|
+
#include <clipping_planes_fragment>
|
|
3554
|
+
#include <logdepthbuf_fragment>
|
|
3555
|
+
|
|
3556
|
+
float v1 = mod(gl_FragCoord.x + gl_FragCoord.y, uHatchScale);
|
|
3557
|
+
float v2 = mod(gl_FragCoord.x - gl_FragCoord.y, uHatchScale);
|
|
3558
|
+
float v = mix(v1, v2, vHatchDir);
|
|
3559
|
+
|
|
3560
|
+
float hatchMask = step(v, 1.5) * uHatchEnabled;
|
|
3561
|
+
gl_FragColor = vec4(mix(vFillColor, lineColor, hatchMask), 1.0);
|
|
3562
|
+
}
|
|
3563
|
+
`;
|
|
3564
|
+
while (this._caps.length < count) {
|
|
3565
|
+
const capMat = new ShaderMaterial({
|
|
3566
|
+
uniforms: {
|
|
3567
|
+
lineColor: { value: new Color() },
|
|
3568
|
+
uHatchScale: { value: 10.0 },
|
|
3569
|
+
uHatchEnabled: { value: 1.0 },
|
|
3570
|
+
},
|
|
3571
|
+
vertexShader: hatchVertexShader,
|
|
3572
|
+
fragmentShader: hatchFragmentShader,
|
|
3573
|
+
side: DoubleSide,
|
|
3574
|
+
clipping: true,
|
|
3575
|
+
depthTest: true,
|
|
3576
|
+
depthWrite: false,
|
|
3577
|
+
});
|
|
3578
|
+
const capMesh = new Mesh(new BufferGeometry(), capMat);
|
|
3579
|
+
capMesh.renderOrder = 5;
|
|
3580
|
+
this.add(capMesh);
|
|
3581
|
+
this._caps.push(capMesh);
|
|
3582
|
+
const lineMat = new LineMaterial({
|
|
3583
|
+
color: 0xffffff,
|
|
3584
|
+
linewidth: 2,
|
|
3585
|
+
resolution: new Vector2(window.innerWidth, window.innerHeight),
|
|
3586
|
+
depthTest: true,
|
|
3587
|
+
clipping: true,
|
|
3588
|
+
vertexColors: true,
|
|
3589
|
+
});
|
|
3590
|
+
const lineObj = new LineSegments2(new LineSegmentsGeometry(), lineMat);
|
|
3591
|
+
lineObj.renderOrder = 100;
|
|
3592
|
+
this.add(lineObj);
|
|
3593
|
+
this._outlines.push(lineObj);
|
|
3594
|
+
const ptsMat = new PointsMaterial({
|
|
3595
|
+
color: 0x00aaff,
|
|
3596
|
+
size: 6,
|
|
3597
|
+
sizeAttenuation: false,
|
|
3598
|
+
depthTest: false,
|
|
3599
|
+
transparent: true,
|
|
3600
|
+
depthWrite: false,
|
|
3601
|
+
});
|
|
3602
|
+
const pointsObj = new Points(new BufferGeometry(), ptsMat);
|
|
3603
|
+
pointsObj.renderOrder = 200;
|
|
3604
|
+
this.add(pointsObj);
|
|
3605
|
+
this._debugPoints.push(pointsObj);
|
|
3606
|
+
const debugSegMat = new LineMaterial({
|
|
3607
|
+
color: 0x00ff00,
|
|
3608
|
+
linewidth: 4,
|
|
3609
|
+
resolution: new Vector2(window.innerWidth, window.innerHeight),
|
|
3610
|
+
depthTest: false,
|
|
3611
|
+
transparent: true,
|
|
3612
|
+
depthWrite: false,
|
|
3613
|
+
clipping: true,
|
|
3614
|
+
});
|
|
3615
|
+
const debugSegObj = new LineSegments2(new LineSegmentsGeometry(), debugSegMat);
|
|
3616
|
+
debugSegObj.renderOrder = 150;
|
|
3617
|
+
this.add(debugSegObj);
|
|
3618
|
+
this._debugSegments.push(debugSegObj);
|
|
3619
|
+
const gapPtsMat = new PointsMaterial({
|
|
3620
|
+
size: 6,
|
|
3621
|
+
sizeAttenuation: false,
|
|
3622
|
+
depthTest: false,
|
|
3623
|
+
transparent: true,
|
|
3624
|
+
depthWrite: false,
|
|
3625
|
+
vertexColors: true,
|
|
3626
|
+
});
|
|
3627
|
+
const gapsObj = new Points(new BufferGeometry(), gapPtsMat);
|
|
3628
|
+
gapsObj.renderOrder = 250;
|
|
3629
|
+
this.add(gapsObj);
|
|
3630
|
+
this._debugGaps.push(gapsObj);
|
|
3631
|
+
}
|
|
3632
|
+
for (let i = count; i < this._caps.length; i++) {
|
|
3633
|
+
this._caps[i].visible = false;
|
|
3634
|
+
this._outlines[i].visible = false;
|
|
3635
|
+
this._debugPoints[i].visible = false;
|
|
3636
|
+
this._debugSegments[i].visible = false;
|
|
3637
|
+
this._debugGaps[i].visible = false;
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
update(objects, extents, planes) {
|
|
3641
|
+
const t0 = performance.now();
|
|
3642
|
+
this._ensureHelpersCount(planes.length);
|
|
3643
|
+
if (planes.length === 0) return;
|
|
3644
|
+
const sphere = extents.getBoundingSphere(new Sphere());
|
|
3645
|
+
const globalRadius = Math.max(sphere.radius, 1e-3);
|
|
3646
|
+
const clippingBias = globalRadius * 1e-4;
|
|
3647
|
+
const biasedPlanes = planes.map((p) => {
|
|
3648
|
+
const bp = p.clone();
|
|
3649
|
+
bp.constant += clippingBias;
|
|
3650
|
+
return bp;
|
|
3651
|
+
});
|
|
3652
|
+
const targetMeshes = [];
|
|
3653
|
+
objects.forEach((obj) => {
|
|
3654
|
+
if (obj.isMesh && obj.material) {
|
|
3655
|
+
const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
|
|
3656
|
+
if (mats.some((m) => m.clippingPlanes)) targetMeshes.push(obj);
|
|
3657
|
+
}
|
|
3658
|
+
});
|
|
3659
|
+
planes.forEach((plane, pIdx) => {
|
|
3660
|
+
const capMesh = this._caps[pIdx];
|
|
3661
|
+
const outlineMesh = this._outlines[pIdx];
|
|
3662
|
+
const debugPtsMesh = this._debugPoints[pIdx];
|
|
3663
|
+
const debugSegsMesh = this._debugSegments[pIdx];
|
|
3664
|
+
const debugGapsMesh = this._debugGaps[pIdx];
|
|
3665
|
+
const hatchColor = new Color(this.flags.hatchColor);
|
|
3666
|
+
hatchColor.convertLinearToSRGB();
|
|
3667
|
+
capMesh.material.uniforms.lineColor.value.set(hatchColor);
|
|
3668
|
+
capMesh.material.uniforms.uHatchScale.value = this.flags.hatchScale;
|
|
3669
|
+
capMesh.material.uniforms.uHatchEnabled.value = this.flags.hatchEnabled ? 1.0 : 0.0;
|
|
3670
|
+
outlineMesh.material.linewidth = this.flags.outlineWidth;
|
|
3671
|
+
const otherBiasedPlanes = biasedPlanes.filter((_, i) => i !== pIdx);
|
|
3672
|
+
capMesh.material.clippingPlanes = otherBiasedPlanes;
|
|
3673
|
+
outlineMesh.material.clippingPlanes = otherBiasedPlanes;
|
|
3674
|
+
debugSegsMesh.material.clippingPlanes = otherBiasedPlanes;
|
|
3675
|
+
const n = plane.normal;
|
|
3676
|
+
const planeOrigin = n.clone().multiplyScalar(-plane.constant);
|
|
3677
|
+
const up = new Vector3(0, 1, 0);
|
|
3678
|
+
if (Math.abs(n.dot(up)) > 0.999) up.set(1, 0, 0);
|
|
3679
|
+
const uAxis = new Vector3().crossVectors(up, n).normalize();
|
|
3680
|
+
const vAxis = new Vector3().crossVectors(n, uAxis).normalize();
|
|
3681
|
+
const positions = [];
|
|
3682
|
+
const indices = [];
|
|
3683
|
+
const hatchDirs = [];
|
|
3684
|
+
const fillColors = [];
|
|
3685
|
+
const combinedOutlinePoints = [];
|
|
3686
|
+
const combinedOutlineColors = [];
|
|
3687
|
+
const rawPts = [];
|
|
3688
|
+
const rawSegs = [];
|
|
3689
|
+
const rawGaps = [];
|
|
3690
|
+
const rawGapColors = [];
|
|
3691
|
+
targetMeshes.forEach((mesh, meshIndex) => {
|
|
3692
|
+
if (!mesh.geometry.boundingBox) mesh.geometry.computeBoundingBox();
|
|
3693
|
+
if (!mesh.geometry.boundingSphere) mesh.geometry.computeBoundingSphere();
|
|
3694
|
+
this._worldBox.copy(mesh.geometry.boundingBox).applyMatrix4(mesh.matrixWorld);
|
|
3695
|
+
if (!plane.intersectsBox(this._worldBox)) return;
|
|
3696
|
+
const localScale = new Vector3().setFromMatrixScale(mesh.matrixWorld);
|
|
3697
|
+
const maxScale = Math.max(localScale.x, localScale.y, localScale.z);
|
|
3698
|
+
const localRadius = Math.max(mesh.geometry.boundingSphere.radius * maxScale, 1e-3);
|
|
3699
|
+
const localHashTolerance = Math.max(localRadius * 1e-4, 1e-6);
|
|
3700
|
+
const localEps = Math.max(localRadius * 1e-5, 1e-7);
|
|
3701
|
+
const baseColor = new Color(0xffffff);
|
|
3702
|
+
const om = mesh.userData.originalMaterial;
|
|
3703
|
+
const mm = om ?? (Array.isArray(mesh.material) ? mesh.material[0] : mesh.material);
|
|
3704
|
+
if (mm.color) baseColor.copy(mm.color);
|
|
3705
|
+
const objFillColor = baseColor.clone().lerp(new Color(0x000000), 0.2);
|
|
3706
|
+
const objOutlineColor = baseColor.clone().lerp(new Color(0x000000), 0.85);
|
|
3707
|
+
const hue = ((meshIndex * 137.5) % 360) / 360;
|
|
3708
|
+
const meshGapColor = new Color().setHSL(hue, 1.0, 0.5);
|
|
3709
|
+
const currentHatchDir = meshIndex % 2 === 0 ? 0.0 : 1.0;
|
|
3710
|
+
const fillColor = this.flags.useObjFillColor ? objFillColor : new Color(this.flags.fillColor);
|
|
3711
|
+
const outlineColor = this.flags.useObjOutlineColor ? objOutlineColor : new Color(this.flags.outlineColor);
|
|
3712
|
+
meshGapColor.convertLinearToSRGB();
|
|
3713
|
+
fillColor.convertLinearToSRGB();
|
|
3714
|
+
outlineColor.convertLinearToSRGB();
|
|
3715
|
+
const localEdgeStats = new Map();
|
|
3716
|
+
const localPointGrid = new PointHashGrid(localHashTolerance);
|
|
3717
|
+
this._calculateMeshSegmentsUndirected(mesh, plane, localEdgeStats, localPointGrid, localEps);
|
|
3718
|
+
if (localEdgeStats.size > 0) {
|
|
3719
|
+
const boundaryEdges = [];
|
|
3720
|
+
for (const [key, stat] of localEdgeStats.entries()) {
|
|
3721
|
+
const isBoundary = stat.count % 2 !== 0;
|
|
3722
|
+
const ids = key.split("-");
|
|
3723
|
+
const id0 = Number(ids[0]);
|
|
3724
|
+
const id1 = Number(ids[1]);
|
|
3725
|
+
const p1 = localPointGrid.points[id0];
|
|
3726
|
+
const p2 = localPointGrid.points[id1];
|
|
3727
|
+
if (this.flags.showDebugSeams || (this.flags.boundaryOnly ? isBoundary : true)) {
|
|
3728
|
+
combinedOutlinePoints.push(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
|
|
3729
|
+
combinedOutlineColors.push(outlineColor.r, outlineColor.g, outlineColor.b);
|
|
3730
|
+
combinedOutlineColors.push(outlineColor.r, outlineColor.g, outlineColor.b);
|
|
3731
|
+
}
|
|
3732
|
+
if (isBoundary) boundaryEdges.push([id0, id1]);
|
|
3733
|
+
if (this.flags.showDebugSegments) rawSegs.push(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
|
|
3734
|
+
}
|
|
3735
|
+
if (this.flags.showDebugPoints) {
|
|
3736
|
+
for (const p of localPointGrid.points) rawPts.push(p.x, p.y, p.z);
|
|
3737
|
+
}
|
|
3738
|
+
if (this.flags.fillEnabled && boundaryEdges.length >= 3) {
|
|
3739
|
+
const currentAdj = new Map();
|
|
3740
|
+
for (const edge of boundaryEdges) {
|
|
3741
|
+
const a = edge[0];
|
|
3742
|
+
const b = edge[1];
|
|
3743
|
+
if (!currentAdj.has(a)) currentAdj.set(a, []);
|
|
3744
|
+
if (!currentAdj.has(b)) currentAdj.set(b, []);
|
|
3745
|
+
currentAdj.get(a).push(b);
|
|
3746
|
+
currentAdj.get(b).push(a);
|
|
3747
|
+
}
|
|
3748
|
+
const degree1 = [];
|
|
3749
|
+
for (const [node, neighbors] of currentAdj.entries()) {
|
|
3750
|
+
if (neighbors.length === 1) degree1.push(node);
|
|
3751
|
+
}
|
|
3752
|
+
const stitchTol = Math.max(localRadius * 0.05, 1e-4);
|
|
3753
|
+
for (let i = 0; i < degree1.length; i++) {
|
|
3754
|
+
const n1 = degree1[i];
|
|
3755
|
+
if (currentAdj.get(n1).length !== 1) continue;
|
|
3756
|
+
const p1 = localPointGrid.points[n1];
|
|
3757
|
+
let bestEdgeIdx = -1;
|
|
3758
|
+
let bestProj = null;
|
|
3759
|
+
let bestT = 0;
|
|
3760
|
+
let minDist = stitchTol;
|
|
3761
|
+
const edgeCount = boundaryEdges.length;
|
|
3762
|
+
for (let eIdx = 0; eIdx < edgeCount; eIdx++) {
|
|
3763
|
+
const edge = boundaryEdges[eIdx];
|
|
3764
|
+
const eA = edge[0];
|
|
3765
|
+
const eB = edge[1];
|
|
3766
|
+
if (eA === n1 || eB === n1) continue;
|
|
3767
|
+
const pA = localPointGrid.points[eA];
|
|
3768
|
+
const pB = localPointGrid.points[eB];
|
|
3769
|
+
const lineVec = new Vector3().subVectors(pB, pA);
|
|
3770
|
+
const lineLenSq = lineVec.lengthSq();
|
|
3771
|
+
let proj;
|
|
3772
|
+
let t;
|
|
3773
|
+
if (lineLenSq < 1e-12) {
|
|
3774
|
+
proj = pA.clone();
|
|
3775
|
+
t = 0;
|
|
3776
|
+
} else {
|
|
3777
|
+
const ptVec = new Vector3().subVectors(p1, pA);
|
|
3778
|
+
t = ptVec.dot(lineVec) / lineLenSq;
|
|
3779
|
+
t = Math.max(0, Math.min(1, t));
|
|
3780
|
+
proj = new Vector3().copy(pA).addScaledVector(lineVec, t);
|
|
3781
|
+
}
|
|
3782
|
+
const dist = p1.distanceTo(proj);
|
|
3783
|
+
if (dist < minDist) {
|
|
3784
|
+
minDist = dist;
|
|
3785
|
+
bestEdgeIdx = eIdx;
|
|
3786
|
+
bestProj = proj;
|
|
3787
|
+
bestT = t;
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
if (bestEdgeIdx !== -1) {
|
|
3791
|
+
const edge = boundaryEdges[bestEdgeIdx];
|
|
3792
|
+
const eA = edge[0];
|
|
3793
|
+
const eB = edge[1];
|
|
3794
|
+
if (bestT < 0.001) {
|
|
3795
|
+
boundaryEdges.push([n1, eA]);
|
|
3796
|
+
currentAdj.get(n1).push(eA);
|
|
3797
|
+
currentAdj.get(eA).push(n1);
|
|
3798
|
+
p1.copy(localPointGrid.points[eA]);
|
|
3799
|
+
} else if (bestT > 0.999) {
|
|
3800
|
+
boundaryEdges.push([n1, eB]);
|
|
3801
|
+
currentAdj.get(n1).push(eB);
|
|
3802
|
+
currentAdj.get(eB).push(n1);
|
|
3803
|
+
p1.copy(localPointGrid.points[eB]);
|
|
3804
|
+
} else {
|
|
3805
|
+
const newNodeId = localPointGrid.add(bestProj);
|
|
3806
|
+
edge[1] = newNodeId;
|
|
3807
|
+
boundaryEdges.push([newNodeId, eB]);
|
|
3808
|
+
boundaryEdges.push([n1, newNodeId]);
|
|
3809
|
+
const neighborsA = currentAdj.get(eA);
|
|
3810
|
+
neighborsA[neighborsA.indexOf(eB)] = newNodeId;
|
|
3811
|
+
const neighborsB = currentAdj.get(eB);
|
|
3812
|
+
neighborsB[neighborsB.indexOf(eA)] = newNodeId;
|
|
3813
|
+
if (!currentAdj.has(newNodeId)) currentAdj.set(newNodeId, []);
|
|
3814
|
+
currentAdj.get(newNodeId).push(eA, eB, n1);
|
|
3815
|
+
currentAdj.get(n1).push(newNodeId);
|
|
3816
|
+
p1.copy(bestProj);
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
if (this.flags.showDebugGaps) {
|
|
3821
|
+
for (const [node, neighbors] of currentAdj.entries()) {
|
|
3822
|
+
if (neighbors.length !== 2) {
|
|
3823
|
+
const p = localPointGrid.points[node];
|
|
3824
|
+
rawGaps.push(p.x, p.y, p.z);
|
|
3825
|
+
rawGapColors.push(meshGapColor.r, meshGapColor.g, meshGapColor.b);
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
const loops = this._assembleLoopsUndirected(boundaryEdges, localPointGrid, uAxis, vAxis);
|
|
3830
|
+
if (loops.length > 0) {
|
|
3831
|
+
this._triangulateTreeOptimized(
|
|
3832
|
+
loops,
|
|
3833
|
+
planeOrigin,
|
|
3834
|
+
uAxis,
|
|
3835
|
+
vAxis,
|
|
3836
|
+
positions,
|
|
3837
|
+
indices,
|
|
3838
|
+
localRadius,
|
|
3839
|
+
fillColor,
|
|
3840
|
+
currentHatchDir,
|
|
3841
|
+
hatchDirs,
|
|
3842
|
+
fillColors
|
|
3843
|
+
);
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
});
|
|
3848
|
+
if (indices.length > 0) {
|
|
3849
|
+
capMesh.geometry.dispose();
|
|
3850
|
+
capMesh.geometry = new BufferGeometry();
|
|
3851
|
+
capMesh.geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
|
|
3852
|
+
capMesh.geometry.setAttribute("aHatchDir", new Float32BufferAttribute(hatchDirs, 1));
|
|
3853
|
+
capMesh.geometry.setAttribute("aFillColor", new Float32BufferAttribute(fillColors, 3));
|
|
3854
|
+
capMesh.geometry.setIndex(indices);
|
|
3855
|
+
capMesh.geometry.computeVertexNormals();
|
|
3856
|
+
capMesh.visible = this.flags.fillEnabled;
|
|
3857
|
+
} else {
|
|
3858
|
+
capMesh.visible = false;
|
|
3859
|
+
}
|
|
3860
|
+
if (outlineMesh.geometry) outlineMesh.geometry.dispose();
|
|
3861
|
+
outlineMesh.geometry = new LineSegmentsGeometry();
|
|
3862
|
+
if (this.flags.outlineEnabled && combinedOutlinePoints.length >= 6) {
|
|
3863
|
+
outlineMesh.geometry.setPositions(new Float32Array(combinedOutlinePoints));
|
|
3864
|
+
outlineMesh.geometry.setColors(new Float32Array(combinedOutlineColors));
|
|
3865
|
+
outlineMesh.visible = true;
|
|
3866
|
+
} else {
|
|
3867
|
+
outlineMesh.visible = false;
|
|
3868
|
+
}
|
|
3869
|
+
if (this.flags.showDebugPoints && rawPts.length > 0) {
|
|
3870
|
+
debugPtsMesh.geometry.setAttribute("position", new Float32BufferAttribute(rawPts, 3));
|
|
3871
|
+
debugPtsMesh.visible = true;
|
|
3872
|
+
} else {
|
|
3873
|
+
debugPtsMesh.visible = false;
|
|
3874
|
+
}
|
|
3875
|
+
if (debugSegsMesh.geometry) debugSegsMesh.geometry.dispose();
|
|
3876
|
+
debugSegsMesh.geometry = new LineSegmentsGeometry();
|
|
3877
|
+
if (this.flags.showDebugSegments && rawSegs.length >= 6) {
|
|
3878
|
+
debugSegsMesh.geometry.setPositions(new Float32Array(rawSegs));
|
|
3879
|
+
debugSegsMesh.visible = true;
|
|
3880
|
+
} else {
|
|
3881
|
+
debugSegsMesh.visible = false;
|
|
3882
|
+
}
|
|
3883
|
+
if (debugGapsMesh.geometry) debugGapsMesh.geometry.dispose();
|
|
3884
|
+
debugGapsMesh.geometry = new BufferGeometry();
|
|
3885
|
+
if (this.flags.showDebugGaps && rawGaps.length > 0) {
|
|
3886
|
+
debugGapsMesh.geometry.setAttribute("position", new Float32BufferAttribute(rawGaps, 3));
|
|
3887
|
+
debugGapsMesh.geometry.setAttribute("color", new Float32BufferAttribute(rawGapColors, 3));
|
|
3888
|
+
debugGapsMesh.visible = true;
|
|
3889
|
+
} else {
|
|
3890
|
+
debugGapsMesh.visible = false;
|
|
3891
|
+
}
|
|
3892
|
+
});
|
|
3893
|
+
if (this.flags.showDebugInfo) {
|
|
3894
|
+
console.log(`[SectionsHelper] v7.00 Updated in ${(performance.now() - t0).toFixed(2)} ms`);
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
_assembleLoopsUndirected(edges, pointGrid, uAxis, vAxis) {
|
|
3898
|
+
const adj = new Map();
|
|
3899
|
+
for (const edge of edges) {
|
|
3900
|
+
const a = edge[0];
|
|
3901
|
+
const b = edge[1];
|
|
3902
|
+
if (!adj.has(a)) adj.set(a, []);
|
|
3903
|
+
if (!adj.has(b)) adj.set(b, []);
|
|
3904
|
+
adj.get(a).push(b);
|
|
3905
|
+
adj.get(b).push(a);
|
|
3906
|
+
}
|
|
3907
|
+
const loops = [];
|
|
3908
|
+
while (adj.size > 0) {
|
|
3909
|
+
let startNode = -1;
|
|
3910
|
+
for (const key of adj.keys()) {
|
|
3911
|
+
if (adj.get(key).length > 0) {
|
|
3912
|
+
startNode = key;
|
|
3913
|
+
break;
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
if (startNode === -1) break;
|
|
3917
|
+
let current = startNode;
|
|
3918
|
+
let prev = -1;
|
|
3919
|
+
const path = [];
|
|
3920
|
+
const pathIndices = new Map();
|
|
3921
|
+
while (true) {
|
|
3922
|
+
path.push(current);
|
|
3923
|
+
pathIndices.set(current, path.length - 1);
|
|
3924
|
+
const neighbors = adj.get(current);
|
|
3925
|
+
if (!neighbors || neighbors.length === 0) break;
|
|
3926
|
+
let nextIdx = 0;
|
|
3927
|
+
if (neighbors.length > 1 && prev !== -1) {
|
|
3928
|
+
const pPrev = pointGrid.points[prev];
|
|
3929
|
+
const pCurr = pointGrid.points[current];
|
|
3930
|
+
const vIn = new Vector3().subVectors(pCurr, pPrev);
|
|
3931
|
+
const in2d = new Vector2(vIn.dot(uAxis), vIn.dot(vAxis));
|
|
3932
|
+
if (in2d.lengthSq() > 1e-10) {
|
|
3933
|
+
in2d.normalize();
|
|
3934
|
+
let minAngle = Infinity;
|
|
3935
|
+
for (let i = 0; i < neighbors.length; i++) {
|
|
3936
|
+
const pNext = pointGrid.points[neighbors[i]];
|
|
3937
|
+
const vOut = new Vector3().subVectors(pNext, pCurr);
|
|
3938
|
+
const out2d = new Vector2(vOut.dot(uAxis), vOut.dot(vAxis));
|
|
3939
|
+
if (out2d.lengthSq() > 1e-10) {
|
|
3940
|
+
out2d.normalize();
|
|
3941
|
+
const angle = Math.atan2(in2d.cross(out2d), in2d.dot(out2d));
|
|
3942
|
+
if (angle < minAngle) {
|
|
3943
|
+
minAngle = angle;
|
|
3944
|
+
nextIdx = i;
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
const next = neighbors[nextIdx];
|
|
3951
|
+
neighbors.splice(nextIdx, 1);
|
|
3952
|
+
const nextNeighbors = adj.get(next);
|
|
3953
|
+
if (nextNeighbors) {
|
|
3954
|
+
const revIdx = nextNeighbors.indexOf(current);
|
|
3955
|
+
if (revIdx !== -1) nextNeighbors.splice(revIdx, 1);
|
|
3956
|
+
}
|
|
3957
|
+
prev = current;
|
|
3958
|
+
current = next;
|
|
3959
|
+
if (pathIndices.has(current)) {
|
|
3960
|
+
const loopStartIdx = pathIndices.get(current);
|
|
3961
|
+
const loopNodes = path.slice(loopStartIdx);
|
|
3962
|
+
if (loopNodes.length >= 3) loops.push(loopNodes.map((id) => pointGrid.points[id]));
|
|
3963
|
+
for (let i = loopStartIdx; i < path.length; i++) pathIndices.delete(path[i]);
|
|
3964
|
+
path.length = loopStartIdx;
|
|
3965
|
+
prev = path.length > 1 ? path[path.length - 2] : -1;
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
for (const key of adj.keys()) {
|
|
3969
|
+
if (adj.get(key).length === 0) adj.delete(key);
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
return loops;
|
|
3973
|
+
}
|
|
3974
|
+
_triangulateTreeOptimized(
|
|
3975
|
+
loops,
|
|
3976
|
+
planeOrigin,
|
|
3977
|
+
uAxis,
|
|
3978
|
+
vAxis,
|
|
3979
|
+
positionsBuffer,
|
|
3980
|
+
indicesBuffer,
|
|
3981
|
+
localRadius,
|
|
3982
|
+
fillColor,
|
|
3983
|
+
hatchDir,
|
|
3984
|
+
hatchDirsBuffer,
|
|
3985
|
+
fillColorsBuffer
|
|
3986
|
+
) {
|
|
3987
|
+
const shapesData = [];
|
|
3988
|
+
const minArea = localRadius * 1e-5 * (localRadius * 1e-5);
|
|
3989
|
+
loops.forEach((loop) => {
|
|
3990
|
+
const pts2d = loop.map((p) => {
|
|
3991
|
+
const pv = p.clone().sub(planeOrigin);
|
|
3992
|
+
return new Vector2(pv.dot(uAxis), pv.dot(vAxis));
|
|
3993
|
+
});
|
|
3994
|
+
const cleaned = [];
|
|
3995
|
+
for (let k = 0; k < pts2d.length; k++) {
|
|
3996
|
+
const prev = k === 0 ? pts2d[pts2d.length - 1] : pts2d[k - 1];
|
|
3997
|
+
if (pts2d[k].distanceTo(prev) > 1e-5) cleaned.push(pts2d[k]);
|
|
3998
|
+
}
|
|
3999
|
+
if (cleaned.length < 3) return;
|
|
4000
|
+
const area = ShapeUtils.area(cleaned);
|
|
4001
|
+
if (Math.abs(area) > minArea) {
|
|
4002
|
+
shapesData.push({ pts2d: cleaned, absArea: Math.abs(area), depth: 0, parent: -1, holes: [] });
|
|
4003
|
+
}
|
|
4004
|
+
});
|
|
4005
|
+
shapesData.sort((a, b) => b.absArea - a.absArea);
|
|
4006
|
+
for (let i = 0; i < shapesData.length; i++) {
|
|
4007
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
4008
|
+
if (shapesData[i].absArea > shapesData[j].absArea * 0.98) continue;
|
|
4009
|
+
if (this._isLoopInside(shapesData[i].pts2d, shapesData[j].pts2d, localRadius)) {
|
|
4010
|
+
shapesData[i].parent = j;
|
|
4011
|
+
shapesData[i].depth = shapesData[j].depth + 1;
|
|
4012
|
+
break;
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
for (let i = 0; i < shapesData.length; i++) {
|
|
4017
|
+
const shape = shapesData[i];
|
|
4018
|
+
if (shape.depth % 2 === 1 && shape.parent !== -1) {
|
|
4019
|
+
shapesData[shape.parent].holes.push(shape.pts2d);
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
for (let i = 0; i < shapesData.length; i++) {
|
|
4023
|
+
const shapeData = shapesData[i];
|
|
4024
|
+
if (shapeData.depth % 2 !== 0) continue;
|
|
4025
|
+
if (ShapeUtils.area(shapeData.pts2d) < 0) {
|
|
4026
|
+
shapeData.pts2d.reverse();
|
|
4027
|
+
}
|
|
4028
|
+
shapeData.holes.forEach((h) => {
|
|
4029
|
+
if (ShapeUtils.area(h) > 0) h.reverse();
|
|
4030
|
+
});
|
|
4031
|
+
const allPoints = [...shapeData.pts2d];
|
|
4032
|
+
shapeData.holes.forEach((h) => allPoints.push(...h));
|
|
4033
|
+
const faces = ShapeUtils.triangulateShape(shapeData.pts2d, shapeData.holes);
|
|
4034
|
+
const vertexOffset = positionsBuffer.length / 3;
|
|
4035
|
+
for (const pt of allPoints) {
|
|
4036
|
+
const p3d = planeOrigin.clone().addScaledVector(uAxis, pt.x).addScaledVector(vAxis, pt.y);
|
|
4037
|
+
positionsBuffer.push(p3d.x, p3d.y, p3d.z);
|
|
4038
|
+
hatchDirsBuffer.push(hatchDir);
|
|
4039
|
+
fillColorsBuffer.push(fillColor.r, fillColor.g, fillColor.b);
|
|
4040
|
+
}
|
|
4041
|
+
for (let f = 0; f < faces.length; f++) {
|
|
4042
|
+
indicesBuffer.push(vertexOffset + faces[f][0], vertexOffset + faces[f][1], vertexOffset + faces[f][2]);
|
|
4043
|
+
}
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
_isPointInPoly(pt, poly) {
|
|
4047
|
+
let inside = false;
|
|
4048
|
+
const py = pt.y + 1.119e-7;
|
|
4049
|
+
const px = pt.x;
|
|
4050
|
+
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
4051
|
+
if (
|
|
4052
|
+
poly[i].y > py !== poly[j].y > py &&
|
|
4053
|
+
px < ((poly[j].x - poly[i].x) * (py - poly[i].y)) / (poly[j].y - poly[i].y) + poly[i].x
|
|
4054
|
+
) {
|
|
4055
|
+
inside = !inside;
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
return inside;
|
|
4059
|
+
}
|
|
4060
|
+
_isLoopInside(child, parent, localRadius) {
|
|
4061
|
+
let minX1 = Infinity;
|
|
4062
|
+
let maxX1 = -Infinity;
|
|
4063
|
+
let minY1 = Infinity;
|
|
4064
|
+
let maxY1 = -Infinity;
|
|
4065
|
+
for (const p of child) {
|
|
4066
|
+
if (p.x < minX1) minX1 = p.x;
|
|
4067
|
+
if (p.x > maxX1) maxX1 = p.x;
|
|
4068
|
+
if (p.y < minY1) minY1 = p.y;
|
|
4069
|
+
if (p.y > maxY1) maxY1 = p.y;
|
|
4070
|
+
}
|
|
4071
|
+
let minX2 = Infinity;
|
|
4072
|
+
let maxX2 = -Infinity;
|
|
4073
|
+
let minY2 = Infinity;
|
|
4074
|
+
let maxY2 = -Infinity;
|
|
4075
|
+
for (const p of parent) {
|
|
4076
|
+
if (p.x < minX2) minX2 = p.x;
|
|
4077
|
+
if (p.x > maxX2) maxX2 = p.x;
|
|
4078
|
+
if (p.y < minY2) minY2 = p.y;
|
|
4079
|
+
if (p.y > maxY2) maxY2 = p.y;
|
|
4080
|
+
}
|
|
4081
|
+
const margin = Math.max(localRadius * 1e-4, 1e-5);
|
|
4082
|
+
if (minX1 < minX2 - margin || maxX1 > maxX2 + margin || minY1 < minY2 - margin || maxY1 > maxY2 + margin) {
|
|
4083
|
+
return false;
|
|
4084
|
+
}
|
|
4085
|
+
let insideCount = 0;
|
|
4086
|
+
for (let i = 0; i < child.length; i++) {
|
|
4087
|
+
if (this._isPointInPoly(child[i], parent)) {
|
|
4088
|
+
insideCount++;
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
return insideCount >= child.length * 0.85;
|
|
4092
|
+
}
|
|
4093
|
+
_calculateMeshSegmentsUndirected(mesh, plane, edgeStats, grid, eps) {
|
|
4094
|
+
const geom = mesh.geometry;
|
|
4095
|
+
const pos = geom.attributes.position;
|
|
4096
|
+
const index = geom.index;
|
|
4097
|
+
const world = mesh.matrixWorld;
|
|
4098
|
+
const count = index ? index.count : pos.count;
|
|
4099
|
+
for (let i = 0; i < count; i += 3) {
|
|
4100
|
+
const i1 = index ? index.getX(i) : i;
|
|
4101
|
+
const i2 = index ? index.getX(i + 1) : i + 1;
|
|
4102
|
+
const i3 = index ? index.getX(i + 2) : i + 2;
|
|
4103
|
+
const v1 = this._vA.fromBufferAttribute(pos, i1).applyMatrix4(world);
|
|
4104
|
+
const v2 = this._vB.fromBufferAttribute(pos, i2).applyMatrix4(world);
|
|
4105
|
+
const v3 = this._vC.fromBufferAttribute(pos, i3).applyMatrix4(world);
|
|
4106
|
+
let d1 = plane.distanceToPoint(v1);
|
|
4107
|
+
let d2 = plane.distanceToPoint(v2);
|
|
4108
|
+
let d3 = plane.distanceToPoint(v3);
|
|
4109
|
+
if (Math.abs(d1) <= eps) d1 = eps;
|
|
4110
|
+
if (Math.abs(d2) <= eps) d2 = eps;
|
|
4111
|
+
if (Math.abs(d3) <= eps) d3 = eps;
|
|
4112
|
+
const s1 = d1 > 0 ? 1 : -1;
|
|
4113
|
+
const s2 = d2 > 0 ? 1 : -1;
|
|
4114
|
+
const s3 = d3 > 0 ? 1 : -1;
|
|
4115
|
+
if (s1 === s2 && s2 === s3) continue;
|
|
4116
|
+
const intersections = [];
|
|
4117
|
+
if (s1 !== s2) {
|
|
4118
|
+
intersections.push(new Vector3().lerpVectors(v1, v2, Math.abs(d1) / (Math.abs(d1) + Math.abs(d2))));
|
|
4119
|
+
}
|
|
4120
|
+
if (s2 !== s3) {
|
|
4121
|
+
intersections.push(new Vector3().lerpVectors(v2, v3, Math.abs(d2) / (Math.abs(d2) + Math.abs(d3))));
|
|
4122
|
+
}
|
|
4123
|
+
if (s3 !== s1) {
|
|
4124
|
+
intersections.push(new Vector3().lerpVectors(v3, v1, Math.abs(d3) / (Math.abs(d3) + Math.abs(d1))));
|
|
4125
|
+
}
|
|
4126
|
+
if (intersections.length >= 2) {
|
|
4127
|
+
const id1 = grid.add(intersections[0]);
|
|
4128
|
+
const id2 = grid.add(intersections[1]);
|
|
4129
|
+
if (id1 !== id2) {
|
|
4130
|
+
const key = id1 < id2 ? `${id1}-${id2}` : `${id2}-${id1}`;
|
|
4131
|
+
const stat = edgeStats.get(key) || { count: 0 };
|
|
4132
|
+
stat.count++;
|
|
4133
|
+
edgeStats.set(key, stat);
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4139
|
+
|
|
4140
|
+
class SectionsComponent {
|
|
4141
|
+
constructor(viewer) {
|
|
4142
|
+
this.updateTimerId = 0;
|
|
4143
|
+
this.updateDelay = 250;
|
|
4144
|
+
this.syncOptions = () => {
|
|
4145
|
+
function rgbToHex(c) {
|
|
4146
|
+
const hex = (v = 0) => v.toString(16).padStart(2, "0");
|
|
4147
|
+
return "#" + hex(c.r) + hex(c.g) + hex(c.b);
|
|
4148
|
+
}
|
|
4149
|
+
const options = this.viewer.options;
|
|
4150
|
+
const flags = this.sectionsHelper.flags;
|
|
4151
|
+
flags.fillEnabled = options.enableSectionFill;
|
|
4152
|
+
flags.fillColor = rgbToHex(options.sectionFillColor);
|
|
4153
|
+
flags.useObjFillColor = options.sectionUseObjectColor;
|
|
4154
|
+
flags.hatchEnabled = options.enableSectionHatch;
|
|
4155
|
+
flags.hatchColor = rgbToHex(options.sectionHatchColor);
|
|
4156
|
+
flags.hatchScale = options.sectionHatchScale;
|
|
4157
|
+
flags.outlineEnabled = options.enableSectionOutline;
|
|
4158
|
+
flags.outlineColor = rgbToHex(options.sectionOutlineColor);
|
|
4159
|
+
flags.outlineWidth = options.sectionOutlineWidth;
|
|
4160
|
+
this.syncSections();
|
|
4161
|
+
};
|
|
4162
|
+
this.syncHelper = () => {
|
|
4163
|
+
this.sectionsHelper.removeFromParent();
|
|
4164
|
+
this.viewer.helpers.add(this.sectionsHelper);
|
|
4165
|
+
};
|
|
4166
|
+
this.syncSections = () => {
|
|
4167
|
+
this.sectionsHelper.visible = false;
|
|
4168
|
+
clearTimeout(this.updateTimerId);
|
|
4169
|
+
this.updateTimerId = window.setTimeout(this.updateSections, this.updateDelay);
|
|
4170
|
+
};
|
|
4171
|
+
this.viewerResize = (event) => {
|
|
4172
|
+
this.sectionsHelper.setSize(event.width, event.height);
|
|
4173
|
+
};
|
|
4174
|
+
this.updateSections = () => {
|
|
4175
|
+
const objects = [];
|
|
4176
|
+
this.viewer.models.forEach((model) => objects.push(model.getVisibleObjects()));
|
|
4177
|
+
const objects2 = objects.flat();
|
|
4178
|
+
this.sectionsHelper.update(objects2, this.viewer.extents, this.viewer.clippingPlanes);
|
|
4179
|
+
this.sectionsHelper.visible = true;
|
|
4180
|
+
this.viewer.update();
|
|
4181
|
+
};
|
|
4182
|
+
this.sectionsHelper = new SectionsHelper();
|
|
4183
|
+
this.viewer = viewer;
|
|
4184
|
+
this.viewer.addEventListener("initialize", this.syncHelper);
|
|
4185
|
+
this.viewer.addEventListener("databasechunk", this.syncHelper);
|
|
4186
|
+
this.viewer.addEventListener("drawviewpoint", this.syncHelper);
|
|
4187
|
+
this.viewer.addEventListener("changecuttingplanes", this.syncSections);
|
|
4188
|
+
this.viewer.addEventListener("explode", this.syncSections);
|
|
4189
|
+
this.viewer.addEventListener("hide", this.syncSections);
|
|
4190
|
+
this.viewer.addEventListener("isolate", this.syncSections);
|
|
4191
|
+
this.viewer.addEventListener("show", this.syncSections);
|
|
4192
|
+
this.viewer.addEventListener("showall", this.syncSections);
|
|
4193
|
+
this.viewer.addEventListener("resize", this.viewerResize);
|
|
4194
|
+
this.viewer.addEventListener("optionschange", this.syncOptions);
|
|
4195
|
+
this.syncOptions();
|
|
4196
|
+
}
|
|
4197
|
+
dispose() {
|
|
4198
|
+
clearTimeout(this.updateTimerId);
|
|
4199
|
+
this.sectionsHelper.removeFromParent();
|
|
4200
|
+
this.sectionsHelper.dispose();
|
|
4201
|
+
this.viewer.removeEventListener("initialize", this.syncHelper);
|
|
4202
|
+
this.viewer.removeEventListener("databasechunk", this.syncHelper);
|
|
4203
|
+
this.viewer.removeEventListener("drawviewpoint", this.syncHelper);
|
|
4204
|
+
this.viewer.removeEventListener("changecuttingplanes", this.syncSections);
|
|
4205
|
+
this.viewer.removeEventListener("explode", this.syncSections);
|
|
4206
|
+
this.viewer.removeEventListener("hide", this.syncSections);
|
|
4207
|
+
this.viewer.removeEventListener("isolate", this.syncSections);
|
|
4208
|
+
this.viewer.removeEventListener("show", this.syncSections);
|
|
4209
|
+
this.viewer.removeEventListener("showall", this.syncSections);
|
|
4210
|
+
this.viewer.removeEventListener("resize", this.viewerResize);
|
|
4211
|
+
this.viewer.removeEventListener("optionschange", this.syncOptions);
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
|
|
3383
4215
|
class WCSHelper extends Object3D {
|
|
3384
4216
|
constructor(camera) {
|
|
3385
4217
|
super();
|
|
4218
|
+
this.type = "WCSHelper";
|
|
3386
4219
|
this.camera = camera;
|
|
3387
4220
|
this.size = 160;
|
|
3388
4221
|
this.orthoCamera = new OrthographicCamera(-2, 2, 2, -2, 0, 4);
|
|
@@ -3442,11 +4275,13 @@ class WCSHelper extends Object3D {
|
|
|
3442
4275
|
canvas.width = 64;
|
|
3443
4276
|
canvas.height = 64;
|
|
3444
4277
|
const context = canvas.getContext("2d");
|
|
3445
|
-
context
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
4278
|
+
if (context) {
|
|
4279
|
+
context.clearRect(0, 0, 64, 64);
|
|
4280
|
+
context.font = "24px Arial";
|
|
4281
|
+
context.textAlign = "center";
|
|
4282
|
+
context.fillStyle = color.getStyle();
|
|
4283
|
+
context.fillText(text, 32, 41);
|
|
4284
|
+
}
|
|
3450
4285
|
const texture = new CanvasTexture(canvas);
|
|
3451
4286
|
texture.colorSpace = SRGBColorSpace;
|
|
3452
4287
|
return new SpriteMaterial({ map: texture, toneMapped: false });
|
|
@@ -3470,7 +4305,7 @@ class WCSHelper extends Object3D {
|
|
|
3470
4305
|
|
|
3471
4306
|
class WCSHelperComponent {
|
|
3472
4307
|
constructor(viewer) {
|
|
3473
|
-
this.
|
|
4308
|
+
this.syncHelper = () => {
|
|
3474
4309
|
this.wcsHelper.dispose();
|
|
3475
4310
|
this.wcsHelper = new WCSHelper(this.viewer.camera);
|
|
3476
4311
|
};
|
|
@@ -3484,14 +4319,14 @@ class WCSHelperComponent {
|
|
|
3484
4319
|
};
|
|
3485
4320
|
this.wcsHelper = new WCSHelper(viewer.camera);
|
|
3486
4321
|
this.viewer = viewer;
|
|
3487
|
-
this.viewer.addEventListener("databasechunk", this.
|
|
3488
|
-
this.viewer.addEventListener("drawviewpoint", this.
|
|
4322
|
+
this.viewer.addEventListener("databasechunk", this.syncHelper);
|
|
4323
|
+
this.viewer.addEventListener("drawviewpoint", this.syncHelper);
|
|
3489
4324
|
this.viewer.addEventListener("render", this.viewerRender);
|
|
3490
4325
|
this.viewer.addEventListener("changecameramode", this.updateHelperCamera);
|
|
3491
4326
|
}
|
|
3492
4327
|
dispose() {
|
|
3493
|
-
this.viewer.removeEventListener("databasechunk", this.
|
|
3494
|
-
this.viewer.removeEventListener("drawviewpoint", this.
|
|
4328
|
+
this.viewer.removeEventListener("databasechunk", this.syncHelper);
|
|
4329
|
+
this.viewer.removeEventListener("drawviewpoint", this.syncHelper);
|
|
3495
4330
|
this.viewer.removeEventListener("render", this.viewerRender);
|
|
3496
4331
|
this.viewer.removeEventListener("changecameramode", this.updateHelperCamera);
|
|
3497
4332
|
this.wcsHelper.dispose();
|
|
@@ -3537,11 +4372,14 @@ components.registerComponent("CanvasRemoveComponent", (viewer) => new CanvasRemo
|
|
|
3537
4372
|
components.registerComponent("RenderLoopComponent", (viewer) => new RenderLoopComponent(viewer));
|
|
3538
4373
|
components.registerComponent("HighlighterComponent", (viewer) => new HighlighterComponent(viewer));
|
|
3539
4374
|
components.registerComponent("SelectionComponent", (viewer) => new SelectionComponent(viewer));
|
|
4375
|
+
components.registerComponent("ClippingPlaneComponent", (viewer) => new ClippingPlaneComponent(viewer));
|
|
4376
|
+
components.registerComponent("SectionsComponent", (viewer) => new SectionsComponent(viewer));
|
|
3540
4377
|
components.registerComponent("WCSHelperComponent", (viewer) => new WCSHelperComponent(viewer));
|
|
3541
4378
|
components.registerComponent("ResetComponent", (viewer) => new ResetComponent(viewer));
|
|
3542
4379
|
|
|
3543
4380
|
class ModelImpl {
|
|
3544
4381
|
constructor(scene) {
|
|
4382
|
+
this.id = "";
|
|
3545
4383
|
this.scene = scene;
|
|
3546
4384
|
this.handleToObjects = new Map();
|
|
3547
4385
|
this.originalObjects = new Set();
|
|
@@ -3572,8 +4410,8 @@ class ModelImpl {
|
|
|
3572
4410
|
if (object.material)
|
|
3573
4411
|
disposeMaterials(object.material);
|
|
3574
4412
|
}
|
|
3575
|
-
this.handleToObjects
|
|
3576
|
-
this.originalObjects
|
|
4413
|
+
this.handleToObjects.clear();
|
|
4414
|
+
this.originalObjects.clear();
|
|
3577
4415
|
this.scene.traverse(disposeObject);
|
|
3578
4416
|
this.scene.clear();
|
|
3579
4417
|
}
|
|
@@ -3717,7 +4555,25 @@ class ModelImpl {
|
|
|
3717
4555
|
return info;
|
|
3718
4556
|
}
|
|
3719
4557
|
getExtents(target) {
|
|
3720
|
-
|
|
4558
|
+
const _box = new Box3();
|
|
4559
|
+
function expandByObject(object, target) {
|
|
4560
|
+
if (!object.geometry)
|
|
4561
|
+
return;
|
|
4562
|
+
object.updateWorldMatrix(false, false);
|
|
4563
|
+
if (object.boundingBox !== undefined) {
|
|
4564
|
+
if (object.boundingBox === null)
|
|
4565
|
+
object.computeBoundingBox();
|
|
4566
|
+
_box.copy(object.boundingBox);
|
|
4567
|
+
}
|
|
4568
|
+
else {
|
|
4569
|
+
if (object.geometry.boundingBox === null)
|
|
4570
|
+
object.geometry.computeBoundingBox();
|
|
4571
|
+
_box.copy(object.geometry.boundingBox);
|
|
4572
|
+
}
|
|
4573
|
+
_box.applyMatrix4(object.matrixWorld);
|
|
4574
|
+
target.union(_box);
|
|
4575
|
+
}
|
|
4576
|
+
this.scene.traverseVisible((object) => expandByObject(object, target));
|
|
3721
4577
|
return target;
|
|
3722
4578
|
}
|
|
3723
4579
|
getObjects() {
|
|
@@ -3725,8 +4581,8 @@ class ModelImpl {
|
|
|
3725
4581
|
}
|
|
3726
4582
|
getVisibleObjects() {
|
|
3727
4583
|
const objects = [];
|
|
3728
|
-
this.scene.traverseVisible((object) => objects.push(object));
|
|
3729
|
-
return objects
|
|
4584
|
+
this.scene.traverseVisible((object) => object.userData.handle && objects.push(object));
|
|
4585
|
+
return objects;
|
|
3730
4586
|
}
|
|
3731
4587
|
getObjectsByHandles(handles) {
|
|
3732
4588
|
if (!Array.isArray(handles))
|
|
@@ -3821,44 +4677,54 @@ class ModelImpl {
|
|
|
3821
4677
|
centersCache.set(handle, target.clone());
|
|
3822
4678
|
return target;
|
|
3823
4679
|
};
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
4680
|
+
const calcObjectOffset = (object, target) => {
|
|
4681
|
+
const parent = object.parent;
|
|
4682
|
+
if (!parent || parent.userData.originalCenter === undefined)
|
|
4683
|
+
return target;
|
|
4684
|
+
return target.subVectors(object.userData.originalCenter, parent.userData.originalCenter);
|
|
4685
|
+
};
|
|
4686
|
+
const calcObjectDepth = (object) => {
|
|
4687
|
+
if (object.userData.depth !== undefined)
|
|
4688
|
+
return object.userData.depth;
|
|
4689
|
+
const parent = object.parent;
|
|
4690
|
+
const depth = parent && object !== explodeRoot ? calcObjectDepth(parent) + 1 : 0;
|
|
4691
|
+
object.userData.depth = depth;
|
|
3831
4692
|
object.userData.originalPosition = object.position.clone();
|
|
3832
4693
|
object.userData.originalCenter = calcObjectCenter(object, new Vector3());
|
|
3833
|
-
|
|
3834
|
-
|
|
4694
|
+
object.userData.originalOffset = calcObjectOffset(object, new Vector3());
|
|
4695
|
+
return depth;
|
|
4696
|
+
};
|
|
3835
4697
|
const explodeScale = scale / 100;
|
|
3836
4698
|
const explodeRoot = this.scene;
|
|
3837
|
-
|
|
3838
|
-
|
|
4699
|
+
const explodeObjects = this.getObjects();
|
|
4700
|
+
if (explodeRoot.userData.explodeDepth === undefined) {
|
|
4701
|
+
let maxDepth = 0;
|
|
4702
|
+
explodeObjects.forEach((object) => {
|
|
4703
|
+
const depth = calcObjectDepth(object);
|
|
4704
|
+
if (depth > maxDepth)
|
|
4705
|
+
maxDepth = depth;
|
|
4706
|
+
});
|
|
4707
|
+
explodeRoot.userData.explodeDepth = maxDepth;
|
|
3839
4708
|
}
|
|
3840
4709
|
const maxDepth = explodeRoot.userData.explodeDepth;
|
|
3841
4710
|
const scaledExplodeDepth = explodeScale * maxDepth + 1;
|
|
3842
4711
|
const explodeDepth = 0 | scaledExplodeDepth;
|
|
3843
4712
|
const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
|
|
3844
|
-
|
|
4713
|
+
const explodeObject = (object) => {
|
|
3845
4714
|
if (object.isCamera)
|
|
3846
4715
|
return;
|
|
3847
|
-
if (object.userData.isHighlightWireframe)
|
|
3848
|
-
return;
|
|
3849
4716
|
object.position.copy(object.userData.originalPosition);
|
|
4717
|
+
const depth = object.userData.depth;
|
|
3850
4718
|
if (depth > 0 && depth <= explodeDepth) {
|
|
3851
4719
|
let objectScale = explodeScale * coeff;
|
|
3852
4720
|
if (depth === explodeDepth)
|
|
3853
4721
|
objectScale *= currentSegmentFraction;
|
|
3854
|
-
|
|
3855
|
-
const objectCenter = object.userData.originalCenter;
|
|
3856
|
-
const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
|
|
3857
|
-
object.position.add(localOffset);
|
|
4722
|
+
object.position.addScaledVector(object.userData.originalOffset, objectScale);
|
|
3858
4723
|
}
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
4724
|
+
};
|
|
4725
|
+
explodeObjects.forEach((object) => {
|
|
4726
|
+
explodeObject(object);
|
|
4727
|
+
});
|
|
3862
4728
|
this.scene.updateMatrixWorld();
|
|
3863
4729
|
return this;
|
|
3864
4730
|
}
|
|
@@ -3961,6 +4827,12 @@ class DynamicModelImpl extends ModelImpl {
|
|
|
3961
4827
|
centersCache.set(handle, target.clone());
|
|
3962
4828
|
return target;
|
|
3963
4829
|
};
|
|
4830
|
+
const calcObjectOffset = (object, target) => {
|
|
4831
|
+
const parent = object.parent;
|
|
4832
|
+
if (!parent || parent.userData.originalCenter === undefined)
|
|
4833
|
+
return target;
|
|
4834
|
+
return target.subVectors(object.userData.originalCenter, parent.userData.originalCenter);
|
|
4835
|
+
};
|
|
3964
4836
|
const calcObjectDepth = (object) => {
|
|
3965
4837
|
if (object.userData.depth !== undefined)
|
|
3966
4838
|
return object.userData.depth;
|
|
@@ -3969,13 +4841,15 @@ class DynamicModelImpl extends ModelImpl {
|
|
|
3969
4841
|
object.userData.depth = depth;
|
|
3970
4842
|
object.userData.originalPosition = object.position.clone();
|
|
3971
4843
|
object.userData.originalCenter = calcObjectCenter(object, new Vector3());
|
|
4844
|
+
object.userData.originalOffset = calcObjectOffset(object, new Vector3());
|
|
3972
4845
|
return depth;
|
|
3973
4846
|
};
|
|
3974
4847
|
const explodeScale = scale / 100;
|
|
3975
4848
|
const explodeRoot = this.scene.children[0];
|
|
3976
|
-
|
|
4849
|
+
const explodeObjects = this.getObjects();
|
|
4850
|
+
if (explodeRoot.userData.explodeDepth === undefined) {
|
|
3977
4851
|
let maxDepth = 0;
|
|
3978
|
-
|
|
4852
|
+
explodeObjects.forEach((object) => {
|
|
3979
4853
|
const depth = calcObjectDepth(object);
|
|
3980
4854
|
if (depth > maxDepth)
|
|
3981
4855
|
maxDepth = depth;
|
|
@@ -3987,29 +4861,26 @@ class DynamicModelImpl extends ModelImpl {
|
|
|
3987
4861
|
const explodeDepth = 0 | scaledExplodeDepth;
|
|
3988
4862
|
const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
|
|
3989
4863
|
const offsetCache = new Map();
|
|
3990
|
-
const
|
|
4864
|
+
const calcExplodeOffset = (object, target) => {
|
|
3991
4865
|
if (offsetCache.has(object))
|
|
3992
4866
|
return target.copy(offsetCache.get(object));
|
|
3993
4867
|
const parent = object.parent;
|
|
3994
4868
|
if (parent && object !== explodeRoot)
|
|
3995
|
-
|
|
4869
|
+
calcExplodeOffset(parent, target);
|
|
3996
4870
|
const depth = object.userData.depth;
|
|
3997
4871
|
if (depth > 0 && depth <= explodeDepth) {
|
|
3998
4872
|
let objectScale = explodeScale * coeff;
|
|
3999
4873
|
if (depth === explodeDepth)
|
|
4000
4874
|
objectScale *= currentSegmentFraction;
|
|
4001
|
-
|
|
4002
|
-
const objectCenter = object.userData.originalCenter;
|
|
4003
|
-
const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
|
|
4004
|
-
target.add(localOffset);
|
|
4875
|
+
target.addScaledVector(object.userData.originalOffset, objectScale);
|
|
4005
4876
|
}
|
|
4006
4877
|
offsetCache.set(object, target.clone());
|
|
4007
4878
|
return target;
|
|
4008
4879
|
};
|
|
4009
4880
|
const transformMap = new Map();
|
|
4010
|
-
|
|
4011
|
-
const
|
|
4012
|
-
transformMap.set(object, new Matrix4().makeTranslation(
|
|
4881
|
+
explodeObjects.forEach((object) => {
|
|
4882
|
+
const offset = calcExplodeOffset(object, new Vector3());
|
|
4883
|
+
transformMap.set(object, new Matrix4().makeTranslation(offset));
|
|
4013
4884
|
});
|
|
4014
4885
|
this.gltfLoader.applyObjectTransforms(transformMap);
|
|
4015
4886
|
this.scene.updateMatrixWorld();
|
|
@@ -4080,6 +4951,7 @@ class GltfStructure {
|
|
|
4080
4951
|
this._nextObjectId = 1;
|
|
4081
4952
|
this.loadingAborted = false;
|
|
4082
4953
|
this.criticalError = null;
|
|
4954
|
+
this.embeddedBinaryChunk = null;
|
|
4083
4955
|
}
|
|
4084
4956
|
async initialize(loader) {
|
|
4085
4957
|
const json = await this.loadController.loadJson();
|
|
@@ -4088,28 +4960,72 @@ class GltfStructure {
|
|
|
4088
4960
|
}
|
|
4089
4961
|
this.json = json;
|
|
4090
4962
|
this.loader = loader;
|
|
4091
|
-
|
|
4963
|
+
const bufferUri = this.json.buffers?.[0]?.uri || "";
|
|
4964
|
+
if (bufferUri.startsWith("data:")) {
|
|
4965
|
+
this.embeddedBinaryChunk = await this._decodeDataUri(bufferUri);
|
|
4966
|
+
this.uri = "";
|
|
4967
|
+
} else {
|
|
4968
|
+
this.uri = bufferUri;
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
4971
|
+
async _decodeDataUri(dataUri) {
|
|
4972
|
+
try {
|
|
4973
|
+
const response = await fetch(dataUri);
|
|
4974
|
+
return await response.arrayBuffer();
|
|
4975
|
+
} catch (e) {
|
|
4976
|
+
throw new Error(`DynamicLoader: Failed to decode embedded data URI: ${e.message}`);
|
|
4977
|
+
}
|
|
4092
4978
|
}
|
|
4093
4979
|
clear() {
|
|
4094
|
-
this.json = null;
|
|
4095
|
-
this.loadController = null;
|
|
4096
|
-
this.pendingRequests = [];
|
|
4097
4980
|
if (this.batchTimeout) {
|
|
4098
4981
|
clearTimeout(this.batchTimeout);
|
|
4099
4982
|
this.batchTimeout = null;
|
|
4100
4983
|
}
|
|
4101
|
-
this.
|
|
4102
|
-
this.
|
|
4103
|
-
|
|
4984
|
+
this._rejectPendingRequests();
|
|
4985
|
+
if (this.disposeMaterials) {
|
|
4986
|
+
try {
|
|
4987
|
+
this.disposeMaterials();
|
|
4988
|
+
} catch (e) {
|
|
4989
|
+
console.warn("DynamicLoader: error during disposeMaterials in clear():", e);
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
if (this.textureCache) this.textureCache.clear();
|
|
4993
|
+
if (this.materials) this.materials.clear();
|
|
4994
|
+
this.embeddedBinaryChunk = null;
|
|
4995
|
+
this.json = null;
|
|
4996
|
+
this.loadController = null;
|
|
4997
|
+
this.uri = "";
|
|
4104
4998
|
this.activeChunkLoads = 0;
|
|
4105
4999
|
this.chunkQueue = [];
|
|
4106
5000
|
this.loadingAborted = false;
|
|
4107
5001
|
this.criticalError = null;
|
|
4108
5002
|
}
|
|
5003
|
+
_rejectPendingRequests() {
|
|
5004
|
+
const pending = this.pendingRequests;
|
|
5005
|
+
this.pendingRequests = [];
|
|
5006
|
+
if (!pending || pending.length === 0) return;
|
|
5007
|
+
const cancelError = new Error("DynamicLoader: Structure cleared while requests pending");
|
|
5008
|
+
for (let i = 0; i < pending.length; i++) {
|
|
5009
|
+
const item = pending[i];
|
|
5010
|
+
if (item && typeof item._reject === "function") {
|
|
5011
|
+
try {
|
|
5012
|
+
item._reject(cancelError);
|
|
5013
|
+
} catch {
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
}
|
|
4109
5018
|
getJson() {
|
|
4110
5019
|
return this.json;
|
|
4111
5020
|
}
|
|
4112
5021
|
scheduleRequest(request) {
|
|
5022
|
+
if (this.embeddedBinaryChunk && !this.uri) {
|
|
5023
|
+
return Promise.resolve({
|
|
5024
|
+
buffer: this.embeddedBinaryChunk,
|
|
5025
|
+
relOffset: request.offset,
|
|
5026
|
+
length: request.length,
|
|
5027
|
+
});
|
|
5028
|
+
}
|
|
4113
5029
|
return new Promise((resolve, reject) => {
|
|
4114
5030
|
if (this.loadingAborted) {
|
|
4115
5031
|
reject(
|
|
@@ -4386,8 +5302,13 @@ class GltfStructure {
|
|
|
4386
5302
|
return await this.textureLoader.loadAsync(fullUrl);
|
|
4387
5303
|
} else if (image.bufferView !== undefined) {
|
|
4388
5304
|
const bufferView = this.json.bufferViews[image.bufferView];
|
|
4389
|
-
const
|
|
4390
|
-
|
|
5305
|
+
const { buffer, relOffset } = await this.getBufferView(
|
|
5306
|
+
bufferView.byteOffset || 0,
|
|
5307
|
+
bufferView.byteLength,
|
|
5308
|
+
5121
|
|
5309
|
+
);
|
|
5310
|
+
const imageBytes = new Uint8Array(buffer, relOffset, bufferView.byteLength);
|
|
5311
|
+
const blob = new Blob([imageBytes], { type: image.mimeType });
|
|
4391
5312
|
const url = URL.createObjectURL(blob);
|
|
4392
5313
|
const texture = await this.textureLoader.loadAsync(url);
|
|
4393
5314
|
URL.revokeObjectURL(url);
|
|
@@ -4417,6 +5338,7 @@ class GltfStructure {
|
|
|
4417
5338
|
})
|
|
4418
5339
|
);
|
|
4419
5340
|
}
|
|
5341
|
+
await this.flushBufferRequests();
|
|
4420
5342
|
await Promise.all(texturePromises);
|
|
4421
5343
|
}
|
|
4422
5344
|
loadMaterials() {
|
|
@@ -4625,6 +5547,7 @@ class GltfStructure {
|
|
|
4625
5547
|
}
|
|
4626
5548
|
}
|
|
4627
5549
|
|
|
5550
|
+
const DRACO_EXTENSION_NAME = "KHR_draco_mesh_compression";
|
|
4628
5551
|
const STRUCTURE_ID_SEPARATOR = ":";
|
|
4629
5552
|
class DynamicGltfLoader {
|
|
4630
5553
|
constructor(camera, scene, renderer) {
|
|
@@ -4699,6 +5622,189 @@ class DynamicGltfLoader {
|
|
|
4699
5622
|
this.transformData = null;
|
|
4700
5623
|
this.identityTransformData = null;
|
|
4701
5624
|
this.visibilityMaterials = new Set();
|
|
5625
|
+
this._dracoLoader = null;
|
|
5626
|
+
}
|
|
5627
|
+
setDracoLoader(loader = null) {
|
|
5628
|
+
this._dracoLoader = loader || null;
|
|
5629
|
+
}
|
|
5630
|
+
async _decodeDracoPrimitive(structure, primitive, dracoBufferData) {
|
|
5631
|
+
const dracoExt = primitive.extensions[DRACO_EXTENSION_NAME];
|
|
5632
|
+
const loader = this._dracoLoader;
|
|
5633
|
+
const attributeIDs = {};
|
|
5634
|
+
const attributeTypes = {};
|
|
5635
|
+
const gltfNameToThreeName = new Map();
|
|
5636
|
+
const threeNameToAccessor = new Map();
|
|
5637
|
+
for (const [gltfAttrName, dracoUniqueId] of Object.entries(dracoExt.attributes)) {
|
|
5638
|
+
const threeName = this._gltfAttributeNameToThreeName(gltfAttrName);
|
|
5639
|
+
attributeIDs[threeName] = dracoUniqueId;
|
|
5640
|
+
gltfNameToThreeName.set(gltfAttrName, threeName);
|
|
5641
|
+
const accessorIdx = primitive.attributes[gltfAttrName];
|
|
5642
|
+
if (accessorIdx !== undefined) {
|
|
5643
|
+
const accessor = structure.json.accessors[accessorIdx];
|
|
5644
|
+
attributeTypes[threeName] = this._gltfComponentTypeToTypedArrayName(accessor.componentType);
|
|
5645
|
+
threeNameToAccessor.set(threeName, accessor);
|
|
5646
|
+
}
|
|
5647
|
+
}
|
|
5648
|
+
const geometry = await loader.decodeGeometry(dracoBufferData, {
|
|
5649
|
+
attributeIDs,
|
|
5650
|
+
attributeTypes,
|
|
5651
|
+
useUniqueIDs: true,
|
|
5652
|
+
});
|
|
5653
|
+
for (const [threeName, accessor] of threeNameToAccessor) {
|
|
5654
|
+
const attribute = geometry.getAttribute(threeName);
|
|
5655
|
+
if (!attribute) continue;
|
|
5656
|
+
if (accessor.normalized === true) {
|
|
5657
|
+
attribute.normalized = true;
|
|
5658
|
+
}
|
|
5659
|
+
if (accessor.min) attribute.min = accessor.min;
|
|
5660
|
+
if (accessor.max) attribute.max = accessor.max;
|
|
5661
|
+
}
|
|
5662
|
+
for (const [threeName, accessor] of threeNameToAccessor) {
|
|
5663
|
+
const attribute = geometry.getAttribute(threeName);
|
|
5664
|
+
if (!attribute || !attribute.normalized) continue;
|
|
5665
|
+
const denom = this._normalizedDenominator(accessor.componentType);
|
|
5666
|
+
if (denom <= 0) continue;
|
|
5667
|
+
const src = attribute.array;
|
|
5668
|
+
const inv = 1 / denom;
|
|
5669
|
+
const isSigned = accessor.componentType === 5120 || accessor.componentType === 5122;
|
|
5670
|
+
const out = new Float32Array(src.length);
|
|
5671
|
+
for (let i = 0; i < src.length; i++) {
|
|
5672
|
+
let v = src[i] * inv;
|
|
5673
|
+
if (isSigned && v < -1) v = -1;
|
|
5674
|
+
out[i] = v;
|
|
5675
|
+
}
|
|
5676
|
+
const newAttr = new BufferAttribute(out, attribute.itemSize, false);
|
|
5677
|
+
if (accessor.min) newAttr.min = accessor.min;
|
|
5678
|
+
if (accessor.max) newAttr.max = accessor.max;
|
|
5679
|
+
geometry.setAttribute(threeName, newAttr);
|
|
5680
|
+
}
|
|
5681
|
+
return geometry;
|
|
5682
|
+
}
|
|
5683
|
+
_gltfComponentTypeToTypedArrayName(componentType) {
|
|
5684
|
+
switch (componentType) {
|
|
5685
|
+
case 5120:
|
|
5686
|
+
return "Int8Array";
|
|
5687
|
+
case 5121:
|
|
5688
|
+
return "Uint8Array";
|
|
5689
|
+
case 5122:
|
|
5690
|
+
return "Int16Array";
|
|
5691
|
+
case 5123:
|
|
5692
|
+
return "Uint16Array";
|
|
5693
|
+
case 5125:
|
|
5694
|
+
return "Uint32Array";
|
|
5695
|
+
case 5126:
|
|
5696
|
+
return "Float32Array";
|
|
5697
|
+
default:
|
|
5698
|
+
return "Float32Array";
|
|
5699
|
+
}
|
|
5700
|
+
}
|
|
5701
|
+
_normalizedDenominator(componentType) {
|
|
5702
|
+
switch (componentType) {
|
|
5703
|
+
case 5120:
|
|
5704
|
+
return 127;
|
|
5705
|
+
case 5121:
|
|
5706
|
+
return 255;
|
|
5707
|
+
case 5122:
|
|
5708
|
+
return 32767;
|
|
5709
|
+
case 5123:
|
|
5710
|
+
return 65535;
|
|
5711
|
+
default:
|
|
5712
|
+
return 0;
|
|
5713
|
+
}
|
|
5714
|
+
}
|
|
5715
|
+
_gltfAttributeNameToThreeName(name) {
|
|
5716
|
+
switch (name) {
|
|
5717
|
+
case "POSITION":
|
|
5718
|
+
return "position";
|
|
5719
|
+
case "NORMAL":
|
|
5720
|
+
return "normal";
|
|
5721
|
+
case "TANGENT":
|
|
5722
|
+
return "tangent";
|
|
5723
|
+
case "TEXCOORD_0":
|
|
5724
|
+
return "uv";
|
|
5725
|
+
case "TEXCOORD_1":
|
|
5726
|
+
return "uv2";
|
|
5727
|
+
case "COLOR_0":
|
|
5728
|
+
return "color";
|
|
5729
|
+
case "JOINTS_0":
|
|
5730
|
+
return "skinIndex";
|
|
5731
|
+
case "WEIGHTS_0":
|
|
5732
|
+
return "skinWeight";
|
|
5733
|
+
default:
|
|
5734
|
+
return name.toLowerCase();
|
|
5735
|
+
}
|
|
5736
|
+
}
|
|
5737
|
+
_buildAccessorRequest(structure, accessorIndex, type, primIdx) {
|
|
5738
|
+
const accessor = structure.json.accessors[accessorIndex];
|
|
5739
|
+
const bufferView = structure.json.bufferViews[accessor.bufferView];
|
|
5740
|
+
const components = structure.getNumComponents(accessor.type);
|
|
5741
|
+
const componentSize = structure.getComponentSize(accessor.componentType);
|
|
5742
|
+
const itemBytes = components * componentSize;
|
|
5743
|
+
const accessorByteOffset = accessor.byteOffset || 0;
|
|
5744
|
+
const bvByteOffset = bufferView.byteOffset || 0;
|
|
5745
|
+
const byteStride = bufferView.byteStride || 0;
|
|
5746
|
+
const interleaved = byteStride !== 0 && byteStride !== itemBytes;
|
|
5747
|
+
const offset = bvByteOffset + accessorByteOffset;
|
|
5748
|
+
let length;
|
|
5749
|
+
if (interleaved) {
|
|
5750
|
+
length = (accessor.count - 1) * byteStride + itemBytes;
|
|
5751
|
+
} else {
|
|
5752
|
+
length = accessor.count * itemBytes;
|
|
5753
|
+
}
|
|
5754
|
+
return {
|
|
5755
|
+
offset,
|
|
5756
|
+
length,
|
|
5757
|
+
componentType: accessor.componentType,
|
|
5758
|
+
accessorIndex,
|
|
5759
|
+
type,
|
|
5760
|
+
primIdx,
|
|
5761
|
+
_accessor: accessor,
|
|
5762
|
+
_components: components,
|
|
5763
|
+
_componentSize: componentSize,
|
|
5764
|
+
_itemBytes: itemBytes,
|
|
5765
|
+
_byteStride: byteStride,
|
|
5766
|
+
_interleaved: interleaved,
|
|
5767
|
+
};
|
|
5768
|
+
}
|
|
5769
|
+
_createGeometryAttribute(req) {
|
|
5770
|
+
const accessor = req._accessor;
|
|
5771
|
+
const components = req._components;
|
|
5772
|
+
const count = accessor.count;
|
|
5773
|
+
const stride = req._interleaved ? req._byteStride / req._componentSize : components;
|
|
5774
|
+
const normalized = accessor.normalized === true;
|
|
5775
|
+
const componentType = req.componentType;
|
|
5776
|
+
const src = req.data;
|
|
5777
|
+
if (!req._interleaved && !normalized) {
|
|
5778
|
+
return new BufferAttribute(src, components, false);
|
|
5779
|
+
}
|
|
5780
|
+
if (normalized) {
|
|
5781
|
+
const denom = this._normalizedDenominator(componentType);
|
|
5782
|
+
if (denom > 0) {
|
|
5783
|
+
const out = new Float32Array(count * components);
|
|
5784
|
+
const inv = 1 / denom;
|
|
5785
|
+
const isSignedNormalized = componentType === 5120 || componentType === 5122;
|
|
5786
|
+
for (let i = 0; i < count; i++) {
|
|
5787
|
+
const srcBase = i * stride;
|
|
5788
|
+
const dstBase = i * components;
|
|
5789
|
+
for (let c = 0; c < components; c++) {
|
|
5790
|
+
let v = src[srcBase + c] * inv;
|
|
5791
|
+
if (isSignedNormalized && v < -1) v = -1;
|
|
5792
|
+
out[dstBase + c] = v;
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
return new BufferAttribute(out, components, false);
|
|
5796
|
+
}
|
|
5797
|
+
}
|
|
5798
|
+
const TypedArrayCtor = src.constructor;
|
|
5799
|
+
const out = new TypedArrayCtor(count * components);
|
|
5800
|
+
for (let i = 0; i < count; i++) {
|
|
5801
|
+
const srcBase = i * stride;
|
|
5802
|
+
const dstBase = i * components;
|
|
5803
|
+
for (let c = 0; c < components; c++) {
|
|
5804
|
+
out[dstBase + c] = src[srcBase + c];
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
return new BufferAttribute(out, components, false);
|
|
4702
5808
|
}
|
|
4703
5809
|
createDummyTexture() {
|
|
4704
5810
|
const data = new Float32Array(16);
|
|
@@ -4770,7 +5876,7 @@ class DynamicGltfLoader {
|
|
|
4770
5876
|
if (!this.transformTexture) return;
|
|
4771
5877
|
this.transformTexture.needsUpdate = true;
|
|
4772
5878
|
}
|
|
4773
|
-
setVisibleEdges(visible) {
|
|
5879
|
+
setVisibleEdges(visible = true) {
|
|
4774
5880
|
this.visibleEdges = visible;
|
|
4775
5881
|
}
|
|
4776
5882
|
getAvailableMemory() {
|
|
@@ -5024,78 +6130,51 @@ class DynamicGltfLoader {
|
|
|
5024
6130
|
node.loading = true;
|
|
5025
6131
|
const meshDef = node.structure.getJson().meshes[node.meshIndex];
|
|
5026
6132
|
try {
|
|
6133
|
+
if (
|
|
6134
|
+
!this._dracoLoader &&
|
|
6135
|
+
meshDef.primitives &&
|
|
6136
|
+
meshDef.primitives.some((p) => p.extensions && p.extensions[DRACO_EXTENSION_NAME])
|
|
6137
|
+
) {
|
|
6138
|
+
throw new Error(
|
|
6139
|
+
"primitive uses KHR_draco_mesh_compression but no DRACOLoader is configured. " +
|
|
6140
|
+
"Inject one via dynamicLoader.setDracoLoader(new DRACOLoader()) before opening the file."
|
|
6141
|
+
);
|
|
6142
|
+
}
|
|
5027
6143
|
const bufferRequests = [];
|
|
5028
6144
|
const primitiveReqMap = new Map();
|
|
6145
|
+
const dracoPrimitives = new Map();
|
|
5029
6146
|
for (let primIdx = 0; primIdx < meshDef.primitives.length; primIdx++) {
|
|
5030
6147
|
const primitive = meshDef.primitives[primIdx];
|
|
5031
6148
|
const reqs = [];
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
const
|
|
5035
|
-
const
|
|
5036
|
-
const
|
|
5037
|
-
const
|
|
5038
|
-
const count = accessor.count;
|
|
5039
|
-
const byteLength = count * components * node.structure.getComponentSize(accessor.componentType);
|
|
5040
|
-
reqs.push({
|
|
6149
|
+
const dracoExt = primitive.extensions && primitive.extensions[DRACO_EXTENSION_NAME];
|
|
6150
|
+
if (dracoExt) {
|
|
6151
|
+
const bufferView = node.structure.json.bufferViews[dracoExt.bufferView];
|
|
6152
|
+
const byteOffset = bufferView.byteOffset || 0;
|
|
6153
|
+
const byteLength = bufferView.byteLength;
|
|
6154
|
+
const dracoReq = {
|
|
5041
6155
|
offset: byteOffset,
|
|
5042
6156
|
length: byteLength,
|
|
5043
|
-
componentType:
|
|
5044
|
-
|
|
5045
|
-
type: "position",
|
|
6157
|
+
componentType: 5121,
|
|
6158
|
+
type: "draco",
|
|
5046
6159
|
primIdx,
|
|
5047
|
-
}
|
|
6160
|
+
};
|
|
6161
|
+
reqs.push(dracoReq);
|
|
6162
|
+
dracoPrimitives.set(primIdx, { req: dracoReq, primitive });
|
|
6163
|
+
primitiveReqMap.set(primIdx, reqs);
|
|
6164
|
+
bufferRequests.push(...reqs);
|
|
6165
|
+
continue;
|
|
6166
|
+
}
|
|
6167
|
+
if (primitive.attributes.POSITION !== undefined) {
|
|
6168
|
+
reqs.push(this._buildAccessorRequest(node.structure, primitive.attributes.POSITION, "position", primIdx));
|
|
5048
6169
|
}
|
|
5049
6170
|
if (primitive.attributes.NORMAL !== undefined) {
|
|
5050
|
-
|
|
5051
|
-
const accessor = node.structure.json.accessors[accessorIndex];
|
|
5052
|
-
const bufferView = node.structure.json.bufferViews[accessor.bufferView];
|
|
5053
|
-
const byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0);
|
|
5054
|
-
const components = node.structure.getNumComponents(accessor.type);
|
|
5055
|
-
const count = accessor.count;
|
|
5056
|
-
const byteLength = count * components * node.structure.getComponentSize(accessor.componentType);
|
|
5057
|
-
reqs.push({
|
|
5058
|
-
offset: byteOffset,
|
|
5059
|
-
length: byteLength,
|
|
5060
|
-
componentType: accessor.componentType,
|
|
5061
|
-
accessorIndex,
|
|
5062
|
-
type: "normal",
|
|
5063
|
-
primIdx,
|
|
5064
|
-
});
|
|
6171
|
+
reqs.push(this._buildAccessorRequest(node.structure, primitive.attributes.NORMAL, "normal", primIdx));
|
|
5065
6172
|
}
|
|
5066
6173
|
if (primitive.attributes.TEXCOORD_0 !== undefined) {
|
|
5067
|
-
|
|
5068
|
-
const accessor = node.structure.json.accessors[accessorIndex];
|
|
5069
|
-
const bufferView = node.structure.json.bufferViews[accessor.bufferView];
|
|
5070
|
-
const byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0);
|
|
5071
|
-
const components = node.structure.getNumComponents(accessor.type);
|
|
5072
|
-
const count = accessor.count;
|
|
5073
|
-
const byteLength = count * components * node.structure.getComponentSize(accessor.componentType);
|
|
5074
|
-
reqs.push({
|
|
5075
|
-
offset: byteOffset,
|
|
5076
|
-
length: byteLength,
|
|
5077
|
-
componentType: accessor.componentType,
|
|
5078
|
-
accessorIndex,
|
|
5079
|
-
type: "uv",
|
|
5080
|
-
primIdx,
|
|
5081
|
-
});
|
|
6174
|
+
reqs.push(this._buildAccessorRequest(node.structure, primitive.attributes.TEXCOORD_0, "uv", primIdx));
|
|
5082
6175
|
}
|
|
5083
6176
|
if (primitive.indices !== undefined) {
|
|
5084
|
-
|
|
5085
|
-
const accessor = node.structure.json.accessors[accessorIndex];
|
|
5086
|
-
const bufferView = node.structure.json.bufferViews[accessor.bufferView];
|
|
5087
|
-
const byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0);
|
|
5088
|
-
const components = node.structure.getNumComponents(accessor.type);
|
|
5089
|
-
const count = accessor.count;
|
|
5090
|
-
const byteLength = count * components * node.structure.getComponentSize(accessor.componentType);
|
|
5091
|
-
reqs.push({
|
|
5092
|
-
offset: byteOffset,
|
|
5093
|
-
length: byteLength,
|
|
5094
|
-
componentType: accessor.componentType,
|
|
5095
|
-
accessorIndex,
|
|
5096
|
-
type: "index",
|
|
5097
|
-
primIdx,
|
|
5098
|
-
});
|
|
6177
|
+
reqs.push(this._buildAccessorRequest(node.structure, primitive.indices, "index", primIdx));
|
|
5099
6178
|
}
|
|
5100
6179
|
primitiveReqMap.set(primIdx, reqs);
|
|
5101
6180
|
bufferRequests.push(...reqs);
|
|
@@ -5121,29 +6200,31 @@ class DynamicGltfLoader {
|
|
|
5121
6200
|
}
|
|
5122
6201
|
for (let primIdx = 0; primIdx < meshDef.primitives.length; primIdx++) {
|
|
5123
6202
|
const primitive = meshDef.primitives[primIdx];
|
|
5124
|
-
const geometry = new BufferGeometry();
|
|
5125
6203
|
const reqs = primitiveReqMap.get(primIdx);
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
const
|
|
5129
|
-
const
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
6204
|
+
let geometry;
|
|
6205
|
+
if (dracoPrimitives.has(primIdx)) {
|
|
6206
|
+
const dracoReq = reqs.find((r) => r.type === "draco");
|
|
6207
|
+
const dracoBytes = new Uint8Array(dracoReq.data.buffer, dracoReq.data.byteOffset, dracoReq.data.byteLength);
|
|
6208
|
+
const dracoBuffer = dracoBytes.slice().buffer;
|
|
6209
|
+
geometry = await this._decodeDracoPrimitive(node.structure, primitive, dracoBuffer);
|
|
6210
|
+
} else {
|
|
6211
|
+
geometry = new BufferGeometry();
|
|
6212
|
+
if (primitive.attributes.POSITION !== undefined) {
|
|
6213
|
+
const req = reqs.find((r) => r.type === "position" && r.accessorIndex === primitive.attributes.POSITION);
|
|
6214
|
+
geometry.setAttribute("position", this._createGeometryAttribute(req));
|
|
6215
|
+
}
|
|
6216
|
+
if (primitive.attributes.NORMAL !== undefined) {
|
|
6217
|
+
const req = reqs.find((r) => r.type === "normal" && r.accessorIndex === primitive.attributes.NORMAL);
|
|
6218
|
+
geometry.setAttribute("normal", this._createGeometryAttribute(req));
|
|
6219
|
+
}
|
|
6220
|
+
if (primitive.attributes.TEXCOORD_0 !== undefined) {
|
|
6221
|
+
const req = reqs.find((r) => r.type === "uv" && r.accessorIndex === primitive.attributes.TEXCOORD_0);
|
|
6222
|
+
geometry.setAttribute("uv", this._createGeometryAttribute(req));
|
|
6223
|
+
}
|
|
6224
|
+
if (primitive.indices !== undefined) {
|
|
6225
|
+
const req = reqs.find((r) => r.type === "index" && r.accessorIndex === primitive.indices);
|
|
6226
|
+
geometry.setIndex(this._createGeometryAttribute(req));
|
|
6227
|
+
}
|
|
5147
6228
|
}
|
|
5148
6229
|
let material;
|
|
5149
6230
|
if (primitive.material !== undefined) {
|
|
@@ -5382,20 +6463,43 @@ class DynamicGltfLoader {
|
|
|
5382
6463
|
const nodeMatrix = new Matrix4();
|
|
5383
6464
|
const uniqueNodeId = `${structure.id}_${nodeId}`;
|
|
5384
6465
|
const meshDef = structure.json.meshes[nodeDef.mesh];
|
|
6466
|
+
if (!meshDef || !meshDef.primitives || meshDef.primitives.length === 0) {
|
|
6467
|
+
if (nodeDef.children) {
|
|
6468
|
+
for (const childId of nodeDef.children) {
|
|
6469
|
+
await this.processNodeHierarchy(structure, childId, nodeGroup || parentGroup);
|
|
6470
|
+
}
|
|
6471
|
+
}
|
|
6472
|
+
return nodeGroup;
|
|
6473
|
+
}
|
|
5385
6474
|
const geometryExtents = new Box3();
|
|
5386
6475
|
for (const primitive of meshDef.primitives) {
|
|
6476
|
+
if (!primitive.attributes) continue;
|
|
5387
6477
|
const positionAccessor = structure.json.accessors[primitive.attributes.POSITION];
|
|
5388
6478
|
if (positionAccessor && positionAccessor.min && positionAccessor.max) {
|
|
5389
|
-
const
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
6479
|
+
const minVec = new Vector3().fromArray(positionAccessor.min);
|
|
6480
|
+
const maxVec = new Vector3().fromArray(positionAccessor.max);
|
|
6481
|
+
if (positionAccessor.normalized === true) {
|
|
6482
|
+
const denom = this._normalizedDenominator(positionAccessor.componentType);
|
|
6483
|
+
if (denom > 0) {
|
|
6484
|
+
minVec.divideScalar(denom);
|
|
6485
|
+
maxVec.divideScalar(denom);
|
|
6486
|
+
if (positionAccessor.componentType === 5120 || positionAccessor.componentType === 5122) {
|
|
6487
|
+
minVec.x = Math.max(minVec.x, -1);
|
|
6488
|
+
minVec.y = Math.max(minVec.y, -1);
|
|
6489
|
+
minVec.z = Math.max(minVec.z, -1);
|
|
6490
|
+
maxVec.x = Math.max(maxVec.x, -1);
|
|
6491
|
+
maxVec.y = Math.max(maxVec.y, -1);
|
|
6492
|
+
maxVec.z = Math.max(maxVec.z, -1);
|
|
6493
|
+
}
|
|
6494
|
+
}
|
|
6495
|
+
}
|
|
6496
|
+
geometryExtents.union(new Box3(minVec, maxVec));
|
|
5394
6497
|
}
|
|
5395
6498
|
}
|
|
5396
6499
|
let isEdge = false;
|
|
5397
|
-
|
|
5398
|
-
|
|
6500
|
+
const firstPrimitive = meshDef.primitives[0];
|
|
6501
|
+
if (firstPrimitive && firstPrimitive.material !== undefined) {
|
|
6502
|
+
const material = structure.json.materials[firstPrimitive.material];
|
|
5399
6503
|
if (material?.name === "edges") {
|
|
5400
6504
|
isEdge = true;
|
|
5401
6505
|
}
|
|
@@ -5708,6 +6812,10 @@ class DynamicGltfLoader {
|
|
|
5708
6812
|
vec3 objectNormal = vec3( normal );
|
|
5709
6813
|
mat3 bm = mat3( batchingMatrix );
|
|
5710
6814
|
objectNormal = bm * objectNormal;
|
|
6815
|
+
#ifdef USE_TANGENT
|
|
6816
|
+
vec3 objectTangent = vec3( tangent.xyz );
|
|
6817
|
+
objectTangent = bm * objectTangent;
|
|
6818
|
+
#endif
|
|
5711
6819
|
`
|
|
5712
6820
|
);
|
|
5713
6821
|
}
|
|
@@ -6880,7 +7988,7 @@ class DynamicGltfLoader {
|
|
|
6880
7988
|
}
|
|
6881
7989
|
return extent;
|
|
6882
7990
|
}
|
|
6883
|
-
setMaxConcurrentChunks(maxChunks) {
|
|
7991
|
+
setMaxConcurrentChunks(maxChunks = 6) {
|
|
6884
7992
|
if (maxChunks < 1) {
|
|
6885
7993
|
console.warn("Max concurrent chunks must be at least 1");
|
|
6886
7994
|
return;
|
|
@@ -6982,10 +8090,10 @@ class GLTFBinaryParser {
|
|
|
6982
8090
|
}
|
|
6983
8091
|
offset += chunkLength;
|
|
6984
8092
|
}
|
|
6985
|
-
if (
|
|
8093
|
+
if (this.content === undefined) {
|
|
6986
8094
|
throw new Error("GLTFBinaryParser: JSON content not found.");
|
|
6987
8095
|
}
|
|
6988
|
-
if (
|
|
8096
|
+
if (this.body === undefined) {
|
|
6989
8097
|
throw new Error("GLTFBinaryParser: Binary buffer chunk not found or type not supported.");
|
|
6990
8098
|
}
|
|
6991
8099
|
}
|
|
@@ -7068,16 +8176,18 @@ class GLTFFileDynamicLoader extends Loader {
|
|
|
7068
8176
|
if (this.manager)
|
|
7069
8177
|
this.manager.dispose();
|
|
7070
8178
|
}
|
|
7071
|
-
isSupport(file, format) {
|
|
8179
|
+
isSupport(file, format = "") {
|
|
7072
8180
|
return ((typeof file === "string" || file instanceof globalThis.File || file instanceof ArrayBuffer) &&
|
|
7073
8181
|
/(gltf|glb)$/i.test(format));
|
|
7074
8182
|
}
|
|
7075
|
-
async load(file, format, params) {
|
|
8183
|
+
async load(file, format, params = {}) {
|
|
7076
8184
|
this.manager = new GLTFLoadingManager(file, params);
|
|
7077
8185
|
const scene = new Group();
|
|
7078
8186
|
this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, scene, this.viewer.renderer);
|
|
7079
|
-
this.gltfLoader.
|
|
7080
|
-
this.gltfLoader.
|
|
8187
|
+
this.gltfLoader.setMemoryLimit(this.viewer.options.memoryLimit);
|
|
8188
|
+
this.gltfLoader.setVisibleEdges(this.viewer.options.edgeModel);
|
|
8189
|
+
this.gltfLoader.setMaxConcurrentChunks(params.maxConcurrentChunks);
|
|
8190
|
+
this.gltfLoader.setDracoLoader(params.dracoLoader);
|
|
7081
8191
|
const modelImpl = new DynamicModelImpl(scene);
|
|
7082
8192
|
modelImpl.id = params.modelId || this.extractFileName(file);
|
|
7083
8193
|
modelImpl.gltfLoader = this.gltfLoader;
|
|
@@ -7164,8 +8274,10 @@ class GLTFCloudDynamicLoader extends Loader {
|
|
|
7164
8274
|
async load(model, format, params = {}) {
|
|
7165
8275
|
const scene = new Group();
|
|
7166
8276
|
this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, scene, this.viewer.renderer);
|
|
7167
|
-
this.gltfLoader.
|
|
8277
|
+
this.gltfLoader.setMemoryLimit(this.viewer.options.memoryLimit);
|
|
7168
8278
|
this.gltfLoader.setVisibleEdges(this.viewer.options.edgeModel);
|
|
8279
|
+
this.gltfLoader.setMaxConcurrentChunks(params.maxConcurrentChunks);
|
|
8280
|
+
this.gltfLoader.setDracoLoader(params.dracoLoader);
|
|
7169
8281
|
const modelImpl = new DynamicModelImpl(scene);
|
|
7170
8282
|
modelImpl.id = model.file.id;
|
|
7171
8283
|
modelImpl.gltfLoader = this.gltfLoader;
|
|
@@ -7225,207 +8337,6 @@ const loaders = loadersRegistry("threejs");
|
|
|
7225
8337
|
loaders.registerLoader("gltf-file", (viewer) => new GLTFFileDynamicLoader(viewer));
|
|
7226
8338
|
loaders.registerLoader("gltf-cloud", (viewer) => new GLTFCloudDynamicLoader(viewer));
|
|
7227
8339
|
|
|
7228
|
-
class SSAARenderPass extends Pass {
|
|
7229
|
-
constructor(scenes, camera, clearColor = 0x000000, clearAlpha = 0) {
|
|
7230
|
-
super();
|
|
7231
|
-
this.scenes = Array.isArray(scenes) ? scenes : [scenes];
|
|
7232
|
-
this.camera = camera;
|
|
7233
|
-
this.sampleLevel = 2;
|
|
7234
|
-
this.unbiased = true;
|
|
7235
|
-
this.stencilBuffer = false;
|
|
7236
|
-
this.clearColor = clearColor;
|
|
7237
|
-
this.clearAlpha = clearAlpha;
|
|
7238
|
-
this._sampleRenderTarget = null;
|
|
7239
|
-
this._oldClearColor = new Color();
|
|
7240
|
-
this._copyUniforms = UniformsUtils.clone(CopyShader.uniforms);
|
|
7241
|
-
this._copyMaterial = new ShaderMaterial({
|
|
7242
|
-
uniforms: this._copyUniforms,
|
|
7243
|
-
vertexShader: CopyShader.vertexShader,
|
|
7244
|
-
fragmentShader: CopyShader.fragmentShader,
|
|
7245
|
-
transparent: true,
|
|
7246
|
-
depthTest: false,
|
|
7247
|
-
depthWrite: false,
|
|
7248
|
-
premultipliedAlpha: true,
|
|
7249
|
-
blending: AdditiveBlending,
|
|
7250
|
-
});
|
|
7251
|
-
this._fsQuad = new FullScreenQuad(this._copyMaterial);
|
|
7252
|
-
}
|
|
7253
|
-
dispose() {
|
|
7254
|
-
if (this._sampleRenderTarget) {
|
|
7255
|
-
this._sampleRenderTarget.dispose();
|
|
7256
|
-
this._sampleRenderTarget = null;
|
|
7257
|
-
}
|
|
7258
|
-
this._copyMaterial.dispose();
|
|
7259
|
-
this._fsQuad.dispose();
|
|
7260
|
-
}
|
|
7261
|
-
setSize(width, height) {
|
|
7262
|
-
if (this._sampleRenderTarget) this._sampleRenderTarget.setSize(width, height);
|
|
7263
|
-
}
|
|
7264
|
-
render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) {
|
|
7265
|
-
if (!this._sampleRenderTarget) {
|
|
7266
|
-
this._sampleRenderTarget = new WebGLRenderTarget(readBuffer.width, readBuffer.height, {
|
|
7267
|
-
type: HalfFloatType,
|
|
7268
|
-
stencilBuffer: this.stencilBuffer,
|
|
7269
|
-
});
|
|
7270
|
-
this._sampleRenderTarget.texture.name = "SSAAMultiRenderPass.sample";
|
|
7271
|
-
}
|
|
7272
|
-
const jitterOffsets = _JitterVectors[Math.max(0, Math.min(this.sampleLevel, 5))];
|
|
7273
|
-
const autoClear = renderer.autoClear;
|
|
7274
|
-
renderer.autoClear = false;
|
|
7275
|
-
renderer.getClearColor(this._oldClearColor);
|
|
7276
|
-
const oldClearAlpha = renderer.getClearAlpha();
|
|
7277
|
-
const baseSampleWeight = 1.0 / jitterOffsets.length;
|
|
7278
|
-
const roundingRange = 1 / 32;
|
|
7279
|
-
this._copyUniforms["tDiffuse"].value = this._sampleRenderTarget.texture;
|
|
7280
|
-
const viewOffset = {
|
|
7281
|
-
fullWidth: readBuffer.width,
|
|
7282
|
-
fullHeight: readBuffer.height,
|
|
7283
|
-
offsetX: 0,
|
|
7284
|
-
offsetY: 0,
|
|
7285
|
-
width: readBuffer.width,
|
|
7286
|
-
height: readBuffer.height,
|
|
7287
|
-
};
|
|
7288
|
-
const originalViewOffset = Object.assign({}, this.camera.view);
|
|
7289
|
-
if (originalViewOffset.enabled) Object.assign(viewOffset, originalViewOffset);
|
|
7290
|
-
for (let i = 0; i < jitterOffsets.length; i++) {
|
|
7291
|
-
const jitterOffset = jitterOffsets[i];
|
|
7292
|
-
if (this.camera.setViewOffset) {
|
|
7293
|
-
this.camera.setViewOffset(
|
|
7294
|
-
viewOffset.fullWidth,
|
|
7295
|
-
viewOffset.fullHeight,
|
|
7296
|
-
viewOffset.offsetX + jitterOffset[0] * 0.0625,
|
|
7297
|
-
viewOffset.offsetY + jitterOffset[1] * 0.0625,
|
|
7298
|
-
viewOffset.width,
|
|
7299
|
-
viewOffset.height
|
|
7300
|
-
);
|
|
7301
|
-
}
|
|
7302
|
-
let sampleWeight = baseSampleWeight;
|
|
7303
|
-
if (this.unbiased) {
|
|
7304
|
-
const uniformCenteredDistribution = -0.5 + (i + 0.5) / jitterOffsets.length;
|
|
7305
|
-
sampleWeight += roundingRange * uniformCenteredDistribution;
|
|
7306
|
-
}
|
|
7307
|
-
this._copyUniforms["opacity"].value = sampleWeight;
|
|
7308
|
-
renderer.setClearColor(this.clearColor, this.clearAlpha);
|
|
7309
|
-
renderer.setRenderTarget(this._sampleRenderTarget);
|
|
7310
|
-
renderer.clear();
|
|
7311
|
-
this.scenes.forEach((scene) => renderer.render(scene, this.camera));
|
|
7312
|
-
renderer.setRenderTarget(this.renderToScreen ? null : writeBuffer);
|
|
7313
|
-
if (i === 0) {
|
|
7314
|
-
renderer.setClearColor(0x000000, 0.0);
|
|
7315
|
-
renderer.clear();
|
|
7316
|
-
}
|
|
7317
|
-
this._fsQuad.render(renderer);
|
|
7318
|
-
}
|
|
7319
|
-
if (this.camera.setViewOffset && originalViewOffset.enabled) {
|
|
7320
|
-
this.camera.setViewOffset(
|
|
7321
|
-
originalViewOffset.fullWidth,
|
|
7322
|
-
originalViewOffset.fullHeight,
|
|
7323
|
-
originalViewOffset.offsetX,
|
|
7324
|
-
originalViewOffset.offsetY,
|
|
7325
|
-
originalViewOffset.width,
|
|
7326
|
-
originalViewOffset.height
|
|
7327
|
-
);
|
|
7328
|
-
} else if (this.camera.clearViewOffset) {
|
|
7329
|
-
this.camera.clearViewOffset();
|
|
7330
|
-
}
|
|
7331
|
-
renderer.autoClear = autoClear;
|
|
7332
|
-
renderer.setClearColor(this._oldClearColor, oldClearAlpha);
|
|
7333
|
-
}
|
|
7334
|
-
}
|
|
7335
|
-
const _JitterVectors = [
|
|
7336
|
-
[[0, 0]],
|
|
7337
|
-
[
|
|
7338
|
-
[4, 4],
|
|
7339
|
-
[-4, -4],
|
|
7340
|
-
],
|
|
7341
|
-
[
|
|
7342
|
-
[-2, -6],
|
|
7343
|
-
[6, -2],
|
|
7344
|
-
[-6, 2],
|
|
7345
|
-
[2, 6],
|
|
7346
|
-
],
|
|
7347
|
-
[
|
|
7348
|
-
[1, -3],
|
|
7349
|
-
[-1, 3],
|
|
7350
|
-
[5, 1],
|
|
7351
|
-
[-3, -5],
|
|
7352
|
-
[-5, 5],
|
|
7353
|
-
[-7, -1],
|
|
7354
|
-
[3, 7],
|
|
7355
|
-
[7, -7],
|
|
7356
|
-
],
|
|
7357
|
-
[
|
|
7358
|
-
[1, 1],
|
|
7359
|
-
[-1, -3],
|
|
7360
|
-
[-3, 2],
|
|
7361
|
-
[4, -1],
|
|
7362
|
-
[-5, -2],
|
|
7363
|
-
[2, 5],
|
|
7364
|
-
[5, 3],
|
|
7365
|
-
[3, -5],
|
|
7366
|
-
[-2, 6],
|
|
7367
|
-
[0, -7],
|
|
7368
|
-
[-4, -6],
|
|
7369
|
-
[-6, 4],
|
|
7370
|
-
[-8, 0],
|
|
7371
|
-
[7, -4],
|
|
7372
|
-
[6, 7],
|
|
7373
|
-
[-7, -8],
|
|
7374
|
-
],
|
|
7375
|
-
[
|
|
7376
|
-
[-4, -7],
|
|
7377
|
-
[-7, -5],
|
|
7378
|
-
[-3, -5],
|
|
7379
|
-
[-5, -4],
|
|
7380
|
-
[-1, -4],
|
|
7381
|
-
[-2, -2],
|
|
7382
|
-
[-6, -1],
|
|
7383
|
-
[-4, 0],
|
|
7384
|
-
[-7, 1],
|
|
7385
|
-
[-1, 2],
|
|
7386
|
-
[-6, 3],
|
|
7387
|
-
[-3, 3],
|
|
7388
|
-
[-7, 6],
|
|
7389
|
-
[-3, 6],
|
|
7390
|
-
[-5, 7],
|
|
7391
|
-
[-1, 7],
|
|
7392
|
-
[5, -7],
|
|
7393
|
-
[1, -6],
|
|
7394
|
-
[6, -5],
|
|
7395
|
-
[4, -4],
|
|
7396
|
-
[2, -3],
|
|
7397
|
-
[7, -2],
|
|
7398
|
-
[1, -1],
|
|
7399
|
-
[4, -1],
|
|
7400
|
-
[2, 1],
|
|
7401
|
-
[6, 2],
|
|
7402
|
-
[0, 4],
|
|
7403
|
-
[4, 4],
|
|
7404
|
-
[2, 5],
|
|
7405
|
-
[7, 5],
|
|
7406
|
-
[5, 6],
|
|
7407
|
-
[3, 7],
|
|
7408
|
-
],
|
|
7409
|
-
];
|
|
7410
|
-
|
|
7411
|
-
class Helpers extends Scene {
|
|
7412
|
-
constructor() {
|
|
7413
|
-
super(...arguments);
|
|
7414
|
-
this.oldAutoClear = false;
|
|
7415
|
-
this.oldClippingPlanes = [];
|
|
7416
|
-
}
|
|
7417
|
-
onBeforeRender(renderer) {
|
|
7418
|
-
this.oldAutoClear = renderer.autoClear;
|
|
7419
|
-
this.oldClippingPlanes = renderer.clippingPlanes;
|
|
7420
|
-
renderer.autoClear = false;
|
|
7421
|
-
renderer.clippingPlanes = [];
|
|
7422
|
-
}
|
|
7423
|
-
onAfterRender(renderer) {
|
|
7424
|
-
renderer.clippingPlanes = this.oldClippingPlanes;
|
|
7425
|
-
renderer.autoClear = this.oldAutoClear;
|
|
7426
|
-
}
|
|
7427
|
-
}
|
|
7428
|
-
|
|
7429
8340
|
class Viewer extends EventEmitter2 {
|
|
7430
8341
|
constructor(client) {
|
|
7431
8342
|
super();
|
|
@@ -7439,9 +8350,9 @@ class Viewer extends EventEmitter2 {
|
|
|
7439
8350
|
this.selected = [];
|
|
7440
8351
|
this.extents = new Box3();
|
|
7441
8352
|
this.target = new Vector3(0, 0, 0);
|
|
8353
|
+
this.clippingPlanes = [];
|
|
7442
8354
|
this._activeDragger = null;
|
|
7443
8355
|
this._components = [];
|
|
7444
|
-
this._updateDelay = 1000;
|
|
7445
8356
|
this._renderNeeded = false;
|
|
7446
8357
|
this._renderTime = 0;
|
|
7447
8358
|
this.render = this.render.bind(this);
|
|
@@ -7460,7 +8371,9 @@ class Viewer extends EventEmitter2 {
|
|
|
7460
8371
|
initialize(canvas, onProgress) {
|
|
7461
8372
|
this.addEventListener("optionschange", (event) => this.syncOptions(event.data));
|
|
7462
8373
|
this.scene = new Scene();
|
|
7463
|
-
this.helpers = new
|
|
8374
|
+
this.helpers = new Group();
|
|
8375
|
+
this.helpers.name = "Helpers";
|
|
8376
|
+
this.scene.add(this.helpers);
|
|
7464
8377
|
const pixelRatio = window.devicePixelRatio;
|
|
7465
8378
|
const rect = canvas.parentElement.getBoundingClientRect();
|
|
7466
8379
|
const width = rect.width || 1;
|
|
@@ -7482,17 +8395,17 @@ class Viewer extends EventEmitter2 {
|
|
|
7482
8395
|
this.renderer.setPixelRatio(pixelRatio);
|
|
7483
8396
|
this.renderer.setSize(width, height);
|
|
7484
8397
|
this.renderer.outputColorSpace = LinearSRGBColorSpace;
|
|
8398
|
+
this.renderer.localClippingEnabled = true;
|
|
7485
8399
|
this.renderPass = new RenderPass(this.scene, this.camera);
|
|
7486
|
-
this.helpersPass = new RenderPass(this.helpers, this.camera);
|
|
7487
|
-
this.helpersPass.clear = false;
|
|
7488
8400
|
this.fxaaPass = new FXAAPass();
|
|
7489
8401
|
this.smaaPass = new SMAAPass();
|
|
7490
|
-
this.ssaaRenderPass = new SSAARenderPass(
|
|
8402
|
+
this.ssaaRenderPass = new SSAARenderPass(this.scene, this.camera);
|
|
7491
8403
|
this.ssaaRenderPass.unbiased = true;
|
|
7492
8404
|
this.outputPass = new OutputPass();
|
|
7493
|
-
|
|
8405
|
+
const renderTarget = new WebGLRenderTarget(1, 1, { samples: 4 });
|
|
8406
|
+
renderTarget.texture.name = "EffectComposer.rt1";
|
|
8407
|
+
this.composer = new EffectComposer(this.renderer, renderTarget);
|
|
7494
8408
|
this.composer.addPass(this.renderPass);
|
|
7495
|
-
this.composer.addPass(this.helpersPass);
|
|
7496
8409
|
this.composer.addPass(this.smaaPass);
|
|
7497
8410
|
this.composer.addPass(this.fxaaPass);
|
|
7498
8411
|
this.composer.addPass(this.ssaaRenderPass);
|
|
@@ -7502,7 +8415,9 @@ class Viewer extends EventEmitter2 {
|
|
|
7502
8415
|
this.canvasEvents.forEach((x) => canvas.addEventListener(x, this.canvaseventlistener));
|
|
7503
8416
|
this._markup.initialize(this.canvas, this.canvasEvents, this, this);
|
|
7504
8417
|
for (const name of components.getComponents().keys()) {
|
|
7505
|
-
|
|
8418
|
+
const component = components.createComponent(name, this);
|
|
8419
|
+
if (component)
|
|
8420
|
+
this._components.push(component);
|
|
7506
8421
|
}
|
|
7507
8422
|
this.syncOptions();
|
|
7508
8423
|
this.syncOverlay();
|
|
@@ -7523,7 +8438,7 @@ class Viewer extends EventEmitter2 {
|
|
|
7523
8438
|
this.removeAllListeners();
|
|
7524
8439
|
this.setActiveDragger();
|
|
7525
8440
|
this._components.forEach((component) => component.dispose());
|
|
7526
|
-
this._components =
|
|
8441
|
+
this._components.length = 0;
|
|
7527
8442
|
this._markup.dispose();
|
|
7528
8443
|
if (this.canvas) {
|
|
7529
8444
|
this.canvasEvents.forEach((x) => this.canvas.removeEventListener(x, this.canvaseventlistener));
|
|
@@ -7533,8 +8448,6 @@ class Viewer extends EventEmitter2 {
|
|
|
7533
8448
|
this.composer.dispose();
|
|
7534
8449
|
if (this.renderPass)
|
|
7535
8450
|
this.renderPass.dispose();
|
|
7536
|
-
if (this.helpersPass)
|
|
7537
|
-
this.helpersPass.dispose();
|
|
7538
8451
|
if (this.fxaaPass)
|
|
7539
8452
|
this.fxaaPass.dispose();
|
|
7540
8453
|
if (this.smaaPass)
|
|
@@ -7550,7 +8463,6 @@ class Viewer extends EventEmitter2 {
|
|
|
7550
8463
|
this.camera = undefined;
|
|
7551
8464
|
this.renderer = undefined;
|
|
7552
8465
|
this.renderPass = undefined;
|
|
7553
|
-
this.helpersPass = undefined;
|
|
7554
8466
|
this.fxaaPass = undefined;
|
|
7555
8467
|
this.smaaPass = undefined;
|
|
7556
8468
|
this.ssaaRenderPass = undefined;
|
|
@@ -7582,11 +8494,12 @@ class Viewer extends EventEmitter2 {
|
|
|
7582
8494
|
}
|
|
7583
8495
|
update(force = false) {
|
|
7584
8496
|
const time = performance.now();
|
|
7585
|
-
|
|
8497
|
+
if (typeof force === "number" && time - this._renderTime >= force)
|
|
8498
|
+
force = true;
|
|
7586
8499
|
this._renderNeeded = true;
|
|
7587
8500
|
if (force)
|
|
7588
8501
|
this.render(time);
|
|
7589
|
-
this.emitEvent({ type: "update", force });
|
|
8502
|
+
this.emitEvent({ type: "update", force: !!force });
|
|
7590
8503
|
}
|
|
7591
8504
|
render(time, force = false) {
|
|
7592
8505
|
if (!this.renderer)
|
|
@@ -7600,13 +8513,7 @@ class Viewer extends EventEmitter2 {
|
|
|
7600
8513
|
this._renderNeeded = false;
|
|
7601
8514
|
this.renderer.info.autoReset = false;
|
|
7602
8515
|
this.renderer.info.reset();
|
|
7603
|
-
|
|
7604
|
-
this.renderer.render(this.scene, this.camera);
|
|
7605
|
-
this.renderer.render(this.helpers, this.camera);
|
|
7606
|
-
}
|
|
7607
|
-
else {
|
|
7608
|
-
this.composer.render(deltaTime);
|
|
7609
|
-
}
|
|
8516
|
+
this.composer.render(deltaTime);
|
|
7610
8517
|
this.emitEvent({ type: "render", time, deltaTime });
|
|
7611
8518
|
}
|
|
7612
8519
|
loadReferences(model) {
|
|
@@ -7666,11 +8573,12 @@ class Viewer extends EventEmitter2 {
|
|
|
7666
8573
|
this.clearOverlay();
|
|
7667
8574
|
this.clearSelected();
|
|
7668
8575
|
this.loaders.forEach((loader) => loader.dispose());
|
|
7669
|
-
this.loaders =
|
|
8576
|
+
this.loaders.length = 0;
|
|
7670
8577
|
this.models.forEach((model) => model.dispose());
|
|
7671
|
-
this.models =
|
|
7672
|
-
this.scene.clear();
|
|
8578
|
+
this.models.length = 0;
|
|
7673
8579
|
this.helpers.clear();
|
|
8580
|
+
this.scene.clear();
|
|
8581
|
+
this.scene.add(this.helpers);
|
|
7674
8582
|
this.extents.makeEmpty();
|
|
7675
8583
|
this.syncOptions();
|
|
7676
8584
|
this.syncOverlay();
|
|
@@ -7687,8 +8595,15 @@ class Viewer extends EventEmitter2 {
|
|
|
7687
8595
|
this.fxaaPass.enabled = options.antialiasing === "fxaa";
|
|
7688
8596
|
this.smaaPass.enabled = options.antialiasing === "smaa";
|
|
7689
8597
|
this.ssaaRenderPass.enabled = options.antialiasing === "ssaa";
|
|
7690
|
-
this.renderPass.enabled =
|
|
7691
|
-
|
|
8598
|
+
this.renderPass.enabled = options.antialiasing !== "ssaa";
|
|
8599
|
+
const samples = options.antialiasing === true || options.antialiasing === "msaa" ? 4 : 0;
|
|
8600
|
+
if (this.composer.renderTarget1.samples !== samples) {
|
|
8601
|
+
const size = this.renderer.getSize(new Vector2());
|
|
8602
|
+
const newRenderTarget = new WebGLRenderTarget(1, 1, { samples });
|
|
8603
|
+
newRenderTarget.texture.name = "EffectComposer.rt1";
|
|
8604
|
+
this.composer.reset(newRenderTarget);
|
|
8605
|
+
this.composer.setSize(size.x, size.y);
|
|
8606
|
+
}
|
|
7692
8607
|
this.update();
|
|
7693
8608
|
}
|
|
7694
8609
|
syncOverlay() {
|
|
@@ -7707,7 +8622,8 @@ class Viewer extends EventEmitter2 {
|
|
|
7707
8622
|
clearSlices() {
|
|
7708
8623
|
if (!this.renderer)
|
|
7709
8624
|
return;
|
|
7710
|
-
this.
|
|
8625
|
+
this.clippingPlanes.length = 0;
|
|
8626
|
+
this.emitEvent({ type: "changecuttingplanes" });
|
|
7711
8627
|
this.emitEvent({ type: "clearslices" });
|
|
7712
8628
|
this.update();
|
|
7713
8629
|
}
|
|
@@ -7776,7 +8692,7 @@ class Viewer extends EventEmitter2 {
|
|
|
7776
8692
|
}
|
|
7777
8693
|
}
|
|
7778
8694
|
getComponent(name) {
|
|
7779
|
-
return this._components.find((component) => component.name === name);
|
|
8695
|
+
return this._components.find((component) => component.name === name) || null;
|
|
7780
8696
|
}
|
|
7781
8697
|
drawViewpoint(viewpoint) {
|
|
7782
8698
|
var _a, _b, _c, _d;
|
|
@@ -7803,7 +8719,6 @@ class Viewer extends EventEmitter2 {
|
|
|
7803
8719
|
camera.updateMatrixWorld();
|
|
7804
8720
|
this.camera = camera;
|
|
7805
8721
|
this.renderPass.camera = camera;
|
|
7806
|
-
this.helpersPass.camera = camera;
|
|
7807
8722
|
this.ssaaRenderPass.camera = camera;
|
|
7808
8723
|
this.options.cameraMode = "orthographic";
|
|
7809
8724
|
this.emitEvent({ type: "changecameramode", mode: "orthographic" });
|
|
@@ -7826,7 +8741,6 @@ class Viewer extends EventEmitter2 {
|
|
|
7826
8741
|
camera.updateMatrixWorld();
|
|
7827
8742
|
this.camera = camera;
|
|
7828
8743
|
this.renderPass.camera = camera;
|
|
7829
|
-
this.helpersPass.camera = camera;
|
|
7830
8744
|
this.ssaaRenderPass.camera = camera;
|
|
7831
8745
|
this.options.cameraMode = "perspective";
|
|
7832
8746
|
this.emitEvent({ type: "changecameramode", mode: "perspective" });
|
|
@@ -7837,8 +8751,9 @@ class Viewer extends EventEmitter2 {
|
|
|
7837
8751
|
clipping_planes.forEach((clipping_plane) => {
|
|
7838
8752
|
const plane = new Plane();
|
|
7839
8753
|
plane.setFromNormalAndCoplanarPoint(getVector3FromPoint3d(clipping_plane.direction), getVector3FromPoint3d(clipping_plane.location));
|
|
7840
|
-
this.
|
|
8754
|
+
this.clippingPlanes.push(plane);
|
|
7841
8755
|
});
|
|
8756
|
+
this.emitEvent({ type: "changecuttingplanes" });
|
|
7842
8757
|
}
|
|
7843
8758
|
};
|
|
7844
8759
|
const setSelection = (selection) => {
|
|
@@ -7893,7 +8808,7 @@ class Viewer extends EventEmitter2 {
|
|
|
7893
8808
|
};
|
|
7894
8809
|
const getClippingPlanes = () => {
|
|
7895
8810
|
const clipping_planes = [];
|
|
7896
|
-
this.
|
|
8811
|
+
this.clippingPlanes.forEach((plane) => {
|
|
7897
8812
|
const clipping_plane = {
|
|
7898
8813
|
location: getPoint3dFromVector3(plane.coplanarPoint(new Vector3())),
|
|
7899
8814
|
direction: getPoint3dFromVector3(plane.normal),
|