@inweb/viewer-three 26.9.6 → 26.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/viewer-three.js +372 -126
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +357 -111
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/components/ResetComponent.d.ts +10 -0
- package/package.json +5 -5
- package/src/Viewer/commands/ResetView.ts +5 -1
- package/src/Viewer/commands/ZoomTo.ts +1 -1
- package/src/Viewer/components/ResetComponent.ts +64 -0
- package/src/Viewer/components/SelectionComponent.ts +4 -4
- package/src/Viewer/components/index.ts +2 -0
- package/src/Viewer/draggers/MeasureLineDragger.ts +175 -17
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +272 -105
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
import { draggersRegistry, commandsRegistry, componentsRegistry, Loader, loadersRegistry, Options, CANVAS_EVENTS } from '@inweb/viewer-core';
|
|
25
25
|
export * from '@inweb/viewer-core';
|
|
26
|
-
import { Line, Vector3, BufferGeometry, Float32BufferAttribute, LineBasicMaterial, Mesh, MeshBasicMaterial, DoubleSide, EventDispatcher, MOUSE, TOUCH, Spherical, Quaternion, Vector2, Plane, Object3D, Matrix4, Vector4, Raycaster, Controls, Clock,
|
|
26
|
+
import { Line, Vector3, BufferGeometry, Float32BufferAttribute, LineBasicMaterial, Mesh, MeshBasicMaterial, DoubleSide, EventDispatcher, MOUSE, TOUCH, Spherical, Quaternion, Vector2, Plane, Object3D, Line3, Matrix4, Vector4, MathUtils, Raycaster, EdgesGeometry, Controls, Clock, Sphere, Box3, Color, AmbientLight, DirectionalLight, HemisphereLight, MeshPhongMaterial, WebGLRenderTarget, UnsignedByteType, RGBAFormat, OrthographicCamera, CylinderGeometry, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial, LoadingManager, LoaderUtils, TextureLoader, BufferAttribute, PointsMaterial, Points, TriangleStripDrawMode, TriangleFanDrawMode, LineSegments, LineLoop, Group, NormalBlending, PerspectiveCamera, UniformsUtils, ShaderMaterial, AdditiveBlending, HalfFloatType, Scene, WebGLRenderer, LinearSRGBColorSpace } from 'three';
|
|
27
27
|
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
|
|
28
28
|
import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js';
|
|
29
29
|
import { Wireframe } from 'three/examples/jsm/lines/Wireframe.js';
|
|
@@ -900,6 +900,8 @@ class CuttingPlaneZAxisDragger extends CuttingPlaneDragger {
|
|
|
900
900
|
}
|
|
901
901
|
|
|
902
902
|
const PRECISION = 0.01;
|
|
903
|
+
const DESKTOP_SNAP_DISTANCE = 10;
|
|
904
|
+
const MOBILE_SNAP_DISTANCE = 50;
|
|
903
905
|
class MeasureLineDragger extends OrbitDragger {
|
|
904
906
|
constructor(viewer) {
|
|
905
907
|
super(viewer);
|
|
@@ -914,7 +916,10 @@ class MeasureLineDragger extends OrbitDragger {
|
|
|
914
916
|
this.onPointerMove = (event) => {
|
|
915
917
|
if (this.orbit.enabled && this.orbit.state !== -1)
|
|
916
918
|
return;
|
|
917
|
-
|
|
919
|
+
const snapPoint = this.snapper.getSnapPoint(event);
|
|
920
|
+
if (snapPoint && this.line.endPoint && snapPoint.equals(this.line.endPoint))
|
|
921
|
+
return;
|
|
922
|
+
this.line.endPoint = snapPoint;
|
|
918
923
|
this.line.render();
|
|
919
924
|
if (this.line.startPoint)
|
|
920
925
|
this.changed = true;
|
|
@@ -943,14 +948,14 @@ class MeasureLineDragger extends OrbitDragger {
|
|
|
943
948
|
this.overlay.render();
|
|
944
949
|
};
|
|
945
950
|
this.updateSnapper = () => {
|
|
946
|
-
this.snapper.update(this.viewer
|
|
951
|
+
this.snapper.update(this.viewer);
|
|
947
952
|
};
|
|
948
953
|
this.overlay = new MeasureOverlay(viewer.camera, viewer.canvas);
|
|
949
954
|
this.overlay.attach();
|
|
950
955
|
this.line = new MeasureLine(this.overlay);
|
|
951
956
|
this.overlay.addLine(this.line);
|
|
952
957
|
this.snapper = new MeasureSnapper(viewer.camera, viewer.canvas);
|
|
953
|
-
this.
|
|
958
|
+
this.updateSnapper();
|
|
954
959
|
this.viewer.canvas.addEventListener("pointerdown", this.onPointerDown);
|
|
955
960
|
this.viewer.canvas.addEventListener("pointermove", this.onPointerMove);
|
|
956
961
|
this.viewer.canvas.addEventListener("pointerup", this.onPointerUp);
|
|
@@ -973,20 +978,39 @@ class MeasureLineDragger extends OrbitDragger {
|
|
|
973
978
|
this.viewer.removeEventListener("isolate", this.updateSnapper);
|
|
974
979
|
this.viewer.removeEventListener("show", this.updateSnapper);
|
|
975
980
|
this.viewer.removeEventListener("showall", this.updateSnapper);
|
|
981
|
+
this.snapper.dispose();
|
|
976
982
|
this.overlay.detach();
|
|
977
983
|
this.overlay.dispose();
|
|
978
984
|
super.dispose();
|
|
979
985
|
}
|
|
980
986
|
}
|
|
987
|
+
const _vertex = new Vector3();
|
|
988
|
+
const _start = new Vector3();
|
|
989
|
+
const _end = new Vector3();
|
|
990
|
+
const _line = new Line3();
|
|
991
|
+
const _center = new Vector3();
|
|
992
|
+
const _projection = new Vector3();
|
|
981
993
|
class MeasureSnapper {
|
|
982
994
|
constructor(camera, canvas) {
|
|
983
|
-
this.objects = [];
|
|
984
995
|
this.camera = camera;
|
|
985
996
|
this.canvas = canvas;
|
|
997
|
+
this.objects = [];
|
|
986
998
|
this.raycaster = new Raycaster();
|
|
999
|
+
this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
|
|
1000
|
+
this.edgesCache = new WeakMap();
|
|
987
1001
|
}
|
|
988
|
-
|
|
989
|
-
|
|
1002
|
+
dispose() {
|
|
1003
|
+
this.objects = [];
|
|
1004
|
+
}
|
|
1005
|
+
isMobile() {
|
|
1006
|
+
if (typeof navigator === "undefined")
|
|
1007
|
+
return false;
|
|
1008
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
|
|
1009
|
+
}
|
|
1010
|
+
getMousePosition(event, target) {
|
|
1011
|
+
return target.set(event.clientX, event.clientY);
|
|
1012
|
+
}
|
|
1013
|
+
getPointerIntersects(mouse, objects) {
|
|
990
1014
|
const rect = this.canvas.getBoundingClientRect();
|
|
991
1015
|
const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
|
|
992
1016
|
const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
|
|
@@ -994,20 +1018,86 @@ class MeasureSnapper {
|
|
|
994
1018
|
this.raycaster.setFromCamera(coords, this.camera);
|
|
995
1019
|
this.raycaster.params = {
|
|
996
1020
|
Mesh: {},
|
|
997
|
-
Line: { threshold: 0.
|
|
998
|
-
Line2: { threshold: 0.
|
|
1021
|
+
Line: { threshold: 0.05 },
|
|
1022
|
+
Line2: { threshold: 0.05 },
|
|
999
1023
|
LOD: {},
|
|
1000
|
-
Points: { threshold: 0.
|
|
1024
|
+
Points: { threshold: 0.01 },
|
|
1001
1025
|
Sprite: {},
|
|
1002
1026
|
};
|
|
1003
|
-
|
|
1004
|
-
if (intersects.length === 0)
|
|
1005
|
-
return undefined;
|
|
1006
|
-
return intersects[0].point;
|
|
1027
|
+
return this.raycaster.intersectObjects(objects, false);
|
|
1007
1028
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1029
|
+
getDetectRadius(point) {
|
|
1030
|
+
const camera = this.camera;
|
|
1031
|
+
if (camera.isOrthographicCamera) {
|
|
1032
|
+
const worldHeight = camera.top - camera.bottom;
|
|
1033
|
+
const canvasHeight = this.canvas.height;
|
|
1034
|
+
const worldUnitsPerPixel = worldHeight / canvasHeight;
|
|
1035
|
+
return this.detectRadiusInPixels * worldUnitsPerPixel;
|
|
1036
|
+
}
|
|
1037
|
+
if (camera.isPerspectiveCamera) {
|
|
1038
|
+
const distance = camera.position.distanceTo(point);
|
|
1039
|
+
const worldHeight = 2 * Math.tan(MathUtils.degToRad(camera.fov * 0.5)) * distance;
|
|
1040
|
+
const canvasHeight = this.canvas.height;
|
|
1041
|
+
const worldUnitsPerPixel = worldHeight / canvasHeight;
|
|
1042
|
+
return this.detectRadiusInPixels * worldUnitsPerPixel;
|
|
1043
|
+
}
|
|
1044
|
+
return 0.1;
|
|
1045
|
+
}
|
|
1046
|
+
getSnapPoint(event) {
|
|
1047
|
+
const mouse = this.getMousePosition(event, new Vector2());
|
|
1048
|
+
const intersections = this.getPointerIntersects(mouse, this.objects);
|
|
1049
|
+
if (intersections.length === 0)
|
|
1050
|
+
return undefined;
|
|
1051
|
+
const object = intersections[0].object;
|
|
1052
|
+
const intersectionPoint = intersections[0].point;
|
|
1053
|
+
const localPoint = object.worldToLocal(intersectionPoint.clone());
|
|
1054
|
+
let snapPoint;
|
|
1055
|
+
let snapDistance = this.getDetectRadius(intersectionPoint);
|
|
1056
|
+
const geometry = object.geometry;
|
|
1057
|
+
const positions = geometry.attributes.position.array;
|
|
1058
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
1059
|
+
_vertex.set(positions[i], positions[i + 1], positions[i + 2]);
|
|
1060
|
+
const distance = _vertex.distanceTo(localPoint);
|
|
1061
|
+
if (distance < snapDistance) {
|
|
1062
|
+
snapDistance = distance;
|
|
1063
|
+
snapPoint = _vertex.clone();
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
if (snapPoint)
|
|
1067
|
+
return object.localToWorld(snapPoint);
|
|
1068
|
+
let edges = this.edgesCache.get(geometry);
|
|
1069
|
+
if (!edges) {
|
|
1070
|
+
edges = new EdgesGeometry(geometry);
|
|
1071
|
+
this.edgesCache.set(geometry, edges);
|
|
1072
|
+
}
|
|
1073
|
+
const edgePositions = edges.attributes.position.array;
|
|
1074
|
+
for (let i = 0; i < edgePositions.length; i += 6) {
|
|
1075
|
+
_start.set(edgePositions[i], edgePositions[i + 1], edgePositions[i + 2]);
|
|
1076
|
+
_end.set(edgePositions[i + 3], edgePositions[i + 4], edgePositions[i + 5]);
|
|
1077
|
+
_line.set(_start, _end);
|
|
1078
|
+
_line.getCenter(_center);
|
|
1079
|
+
const centerDistance = _center.distanceTo(localPoint);
|
|
1080
|
+
if (centerDistance < snapDistance) {
|
|
1081
|
+
snapDistance = centerDistance;
|
|
1082
|
+
snapPoint = _center.clone();
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
1085
|
+
_line.closestPointToPoint(localPoint, true, _projection);
|
|
1086
|
+
const lineDistance = _projection.distanceTo(localPoint);
|
|
1087
|
+
if (lineDistance < snapDistance) {
|
|
1088
|
+
snapDistance = lineDistance;
|
|
1089
|
+
snapPoint = _projection.clone();
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (snapPoint)
|
|
1093
|
+
return object.localToWorld(snapPoint);
|
|
1094
|
+
return intersectionPoint.clone();
|
|
1095
|
+
}
|
|
1096
|
+
update(viewer) {
|
|
1097
|
+
this.objects.length = 0;
|
|
1098
|
+
viewer.models.forEach((model) => {
|
|
1099
|
+
model.getVisibleObjects().forEach((object) => this.objects.push(object));
|
|
1100
|
+
});
|
|
1011
1101
|
}
|
|
1012
1102
|
}
|
|
1013
1103
|
class MeasureOverlay {
|
|
@@ -1029,6 +1119,8 @@ class MeasureOverlay {
|
|
|
1029
1119
|
this.container.style.outline = "none";
|
|
1030
1120
|
this.container.style.pointerEvents = "none";
|
|
1031
1121
|
this.container.style.overflow = "hidden";
|
|
1122
|
+
if (!this.canvas.parentElement)
|
|
1123
|
+
return;
|
|
1032
1124
|
this.canvas.parentElement.appendChild(this.container);
|
|
1033
1125
|
}
|
|
1034
1126
|
dispose() {
|
|
@@ -1059,7 +1151,7 @@ class MeasureOverlay {
|
|
|
1059
1151
|
const _middlePoint = new Vector3();
|
|
1060
1152
|
class MeasureLine {
|
|
1061
1153
|
constructor(overlay) {
|
|
1062
|
-
this.id =
|
|
1154
|
+
this.id = MathUtils.generateUUID();
|
|
1063
1155
|
this.unit = "";
|
|
1064
1156
|
this.scale = 1.0;
|
|
1065
1157
|
this.size = 10.0;
|
|
@@ -1083,6 +1175,10 @@ class MeasureLine {
|
|
|
1083
1175
|
this.elementEndPoint.remove();
|
|
1084
1176
|
this.elementLine.remove();
|
|
1085
1177
|
this.elementLabel.remove();
|
|
1178
|
+
this.elementStartPoint = undefined;
|
|
1179
|
+
this.elementEndPoint = undefined;
|
|
1180
|
+
this.elementLine = undefined;
|
|
1181
|
+
this.elementLabel = undefined;
|
|
1086
1182
|
}
|
|
1087
1183
|
render() {
|
|
1088
1184
|
const projector = this.overlay.projector;
|
|
@@ -1739,7 +1835,7 @@ function zoomTo(viewer, box) {
|
|
|
1739
1835
|
if (camera.isPerspectiveCamera) {
|
|
1740
1836
|
const offset = new Vector3(0, 0, 1)
|
|
1741
1837
|
.applyQuaternion(camera.quaternion)
|
|
1742
|
-
.multiplyScalar(boxSize / Math.tan(MathUtils.
|
|
1838
|
+
.multiplyScalar(boxSize / Math.tan(MathUtils.degToRad(camera.fov * 0.5)));
|
|
1743
1839
|
camera.position.copy(offset).add(boxCenter);
|
|
1744
1840
|
camera.updateMatrixWorld();
|
|
1745
1841
|
}
|
|
@@ -1825,6 +1921,7 @@ function regenerateAll(viewer) {
|
|
|
1825
1921
|
}
|
|
1826
1922
|
|
|
1827
1923
|
function resetView(viewer) {
|
|
1924
|
+
const reset = viewer.getComponent("ResetComponent");
|
|
1828
1925
|
viewer.executeCommand("setActiveDragger");
|
|
1829
1926
|
viewer.executeCommand("clearSlices");
|
|
1830
1927
|
viewer.executeCommand("clearOverlay");
|
|
@@ -1833,7 +1930,7 @@ function resetView(viewer) {
|
|
|
1833
1930
|
viewer.executeCommand("showAll");
|
|
1834
1931
|
viewer.executeCommand("explode", 0);
|
|
1835
1932
|
viewer.executeCommand("zoomToExtents", true);
|
|
1836
|
-
|
|
1933
|
+
reset.resetCameraPosition();
|
|
1837
1934
|
viewer.emit({ type: "resetview" });
|
|
1838
1935
|
}
|
|
1839
1936
|
|
|
@@ -2400,12 +2497,12 @@ class SelectionComponent {
|
|
|
2400
2497
|
const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
|
|
2401
2498
|
const coords = new Vector2(x, y);
|
|
2402
2499
|
this.raycaster.setFromCamera(coords, this.viewer.camera);
|
|
2403
|
-
this.raycaster.params =
|
|
2500
|
+
this.raycaster.params = {
|
|
2404
2501
|
Mesh: {},
|
|
2405
|
-
Line: { threshold: 0.
|
|
2406
|
-
Line2: { threshold: 0.
|
|
2502
|
+
Line: { threshold: 0.05 },
|
|
2503
|
+
Line2: { threshold: 0.05 },
|
|
2407
2504
|
LOD: {},
|
|
2408
|
-
Points: { threshold: 0.
|
|
2505
|
+
Points: { threshold: 0.01 },
|
|
2409
2506
|
Sprite: {},
|
|
2410
2507
|
};
|
|
2411
2508
|
return this.raycaster.intersectObjects(objects, false);
|
|
@@ -2571,6 +2668,31 @@ class WCSHelperComponent {
|
|
|
2571
2668
|
}
|
|
2572
2669
|
}
|
|
2573
2670
|
|
|
2671
|
+
class ResetComponent {
|
|
2672
|
+
constructor(viewer) {
|
|
2673
|
+
this.savedCameraPosition = null;
|
|
2674
|
+
this.onDatabaseChunk = () => {
|
|
2675
|
+
this.savedCameraPosition = {
|
|
2676
|
+
position: this.viewer.camera.position.clone(),
|
|
2677
|
+
up: this.viewer.camera.up.clone(),
|
|
2678
|
+
direction: this.viewer.camera.getWorldDirection(new Vector3()),
|
|
2679
|
+
};
|
|
2680
|
+
};
|
|
2681
|
+
this.viewer = viewer;
|
|
2682
|
+
this.viewer.addEventListener("databasechunk", this.onDatabaseChunk);
|
|
2683
|
+
}
|
|
2684
|
+
dispose() {
|
|
2685
|
+
this.viewer.removeEventListener("databasechunk", this.onDatabaseChunk);
|
|
2686
|
+
}
|
|
2687
|
+
resetCameraPosition() {
|
|
2688
|
+
if (this.savedCameraPosition) {
|
|
2689
|
+
this.viewer.camera.position.copy(this.savedCameraPosition.position);
|
|
2690
|
+
this.viewer.camera.up.copy(this.savedCameraPosition.up);
|
|
2691
|
+
this.viewer.camera.lookAt(this.savedCameraPosition.position.clone().add(this.savedCameraPosition.direction));
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2574
2696
|
const components = componentsRegistry("threejs");
|
|
2575
2697
|
components.registerComponent("ExtentsComponent", (viewer) => new ExtentsComponent(viewer));
|
|
2576
2698
|
components.registerComponent("CameraComponent", (viewer) => new CameraComponent(viewer));
|
|
@@ -2581,6 +2703,7 @@ components.registerComponent("RenderLoopComponent", (viewer) => new RenderLoopCo
|
|
|
2581
2703
|
components.registerComponent("HighlighterComponent", (viewer) => new HighlighterComponent(viewer));
|
|
2582
2704
|
components.registerComponent("SelectionComponent", (viewer) => new SelectionComponent(viewer));
|
|
2583
2705
|
components.registerComponent("WCSHelperComponent", (viewer) => new WCSHelperComponent(viewer));
|
|
2706
|
+
components.registerComponent("ResetComponent", (viewer) => new ResetComponent(viewer));
|
|
2584
2707
|
|
|
2585
2708
|
class GLTFLoadingManager extends LoadingManager {
|
|
2586
2709
|
constructor(file, params = {}) {
|
|
@@ -3371,6 +3494,12 @@ class DynamicGltfLoader {
|
|
|
3371
3494
|
this.maxConcurrentChunks = 8;
|
|
3372
3495
|
this.activeChunkLoads = 0;
|
|
3373
3496
|
this.chunkQueue = [];
|
|
3497
|
+
this.objectIdToIndex = new Map();
|
|
3498
|
+
this.maxObjectId = 0;
|
|
3499
|
+
this.objectVisibility = new Float32Array();
|
|
3500
|
+
this.maxConcurrentChunks = 6;
|
|
3501
|
+
this.mergedObjectMap = new Map();
|
|
3502
|
+
this.mergedGeometryVisibility = new Map();
|
|
3374
3503
|
}
|
|
3375
3504
|
setVisibleEdges(visible) {
|
|
3376
3505
|
this.visibleEdges = visible;
|
|
@@ -4077,6 +4206,50 @@ class DynamicGltfLoader {
|
|
|
4077
4206
|
this.originalObjects.clear();
|
|
4078
4207
|
this.originalObjectsToSelection.clear();
|
|
4079
4208
|
}
|
|
4209
|
+
initializeObjectVisibility() {
|
|
4210
|
+
if (this.maxObjectId > 0) {
|
|
4211
|
+
this.objectVisibility = new Float32Array(this.maxObjectId);
|
|
4212
|
+
for (let i = 0; i < this.maxObjectId; i++) {
|
|
4213
|
+
this.objectVisibility[i] = 1.0;
|
|
4214
|
+
}
|
|
4215
|
+
console.log(`Initialized object visibility array: ${this.maxObjectId} objects`);
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
createVisibilityMaterial(material) {
|
|
4219
|
+
material.onBeforeCompile = (shader) => {
|
|
4220
|
+
shader.vertexShader = shader.vertexShader.replace(
|
|
4221
|
+
"#include <common>",
|
|
4222
|
+
`
|
|
4223
|
+
#include <common>
|
|
4224
|
+
attribute float visibility;
|
|
4225
|
+
varying float vVisibility;
|
|
4226
|
+
`
|
|
4227
|
+
);
|
|
4228
|
+
shader.fragmentShader = shader.fragmentShader.replace(
|
|
4229
|
+
"#include <common>",
|
|
4230
|
+
`
|
|
4231
|
+
#include <common>
|
|
4232
|
+
varying float vVisibility;
|
|
4233
|
+
`
|
|
4234
|
+
);
|
|
4235
|
+
shader.vertexShader = shader.vertexShader.replace(
|
|
4236
|
+
"void main() {",
|
|
4237
|
+
`
|
|
4238
|
+
void main() {
|
|
4239
|
+
vVisibility = visibility;
|
|
4240
|
+
`
|
|
4241
|
+
);
|
|
4242
|
+
shader.fragmentShader = shader.fragmentShader.replace(
|
|
4243
|
+
"void main() {",
|
|
4244
|
+
`
|
|
4245
|
+
void main() {
|
|
4246
|
+
if (vVisibility < 0.5) discard;
|
|
4247
|
+
`
|
|
4248
|
+
);
|
|
4249
|
+
};
|
|
4250
|
+
material.needsUpdate = true;
|
|
4251
|
+
return material;
|
|
4252
|
+
}
|
|
4080
4253
|
clear() {
|
|
4081
4254
|
this.chunkQueue = [];
|
|
4082
4255
|
this.structures.forEach((structure) => {
|
|
@@ -4182,6 +4355,9 @@ class DynamicGltfLoader {
|
|
|
4182
4355
|
this.loadedGeometrySize = 0;
|
|
4183
4356
|
this.abortController = new AbortController();
|
|
4184
4357
|
this.updateMemoryIndicator();
|
|
4358
|
+
this.objectIdToIndex.clear();
|
|
4359
|
+
this.maxObjectId = 0;
|
|
4360
|
+
this.objectVisibility = new Float32Array();
|
|
4185
4361
|
}
|
|
4186
4362
|
setStructureTransform(structureId, matrix) {
|
|
4187
4363
|
const rootGroup = this.structureRoots.get(structureId);
|
|
@@ -4331,18 +4507,43 @@ class DynamicGltfLoader {
|
|
|
4331
4507
|
this.originalObjectsToSelection.add(obj);
|
|
4332
4508
|
}
|
|
4333
4509
|
});
|
|
4510
|
+
this.initializeObjectVisibility();
|
|
4511
|
+
console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
|
|
4334
4512
|
this.dispatchEvent("update");
|
|
4335
4513
|
}
|
|
4336
4514
|
mergeMeshGroups(materialGroups, rootGroup) {
|
|
4337
4515
|
for (const group of materialGroups) {
|
|
4516
|
+
if (!group.material) {
|
|
4517
|
+
console.warn("Skipping mesh group with null material");
|
|
4518
|
+
continue;
|
|
4519
|
+
}
|
|
4338
4520
|
try {
|
|
4339
4521
|
const geometries = [];
|
|
4340
4522
|
const handles = new Set();
|
|
4341
4523
|
const optimizedObjects = [];
|
|
4524
|
+
const objectMapping = new Map();
|
|
4525
|
+
let currentVertexOffset = 0;
|
|
4342
4526
|
for (const mesh of group.objects) {
|
|
4343
4527
|
const geometry = mesh.geometry.clone();
|
|
4344
4528
|
mesh.updateWorldMatrix(true, false);
|
|
4345
4529
|
geometry.applyMatrix4(mesh.matrixWorld);
|
|
4530
|
+
const handle = mesh.userData.handle;
|
|
4531
|
+
if (!this.objectIdToIndex.has(handle)) {
|
|
4532
|
+
this.objectIdToIndex.set(handle, this.maxObjectId++);
|
|
4533
|
+
}
|
|
4534
|
+
const objectId = this.objectIdToIndex.get(handle);
|
|
4535
|
+
const vertexCount = geometry.attributes.position.count;
|
|
4536
|
+
const objectIds = new Float32Array(vertexCount);
|
|
4537
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
4538
|
+
objectIds[i] = objectId;
|
|
4539
|
+
}
|
|
4540
|
+
geometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
|
|
4541
|
+
objectMapping.set(mesh, {
|
|
4542
|
+
geometry,
|
|
4543
|
+
startVertexIndex: currentVertexOffset,
|
|
4544
|
+
vertexCount: geometry.attributes.position.count,
|
|
4545
|
+
});
|
|
4546
|
+
currentVertexOffset += geometry.attributes.position.count;
|
|
4346
4547
|
geometries.push(geometry);
|
|
4347
4548
|
optimizedObjects.push(mesh);
|
|
4348
4549
|
handles.add(mesh.userData.handle);
|
|
@@ -4350,13 +4551,26 @@ class DynamicGltfLoader {
|
|
|
4350
4551
|
const mergedObjects = [];
|
|
4351
4552
|
if (geometries.length > 0) {
|
|
4352
4553
|
const mergedGeometry = mergeGeometries(geometries);
|
|
4554
|
+
const totalVertices = mergedGeometry.attributes.position.count;
|
|
4555
|
+
const visibilityArray = new Float32Array(totalVertices);
|
|
4556
|
+
for (let i = 0; i < totalVertices; i++) {
|
|
4557
|
+
visibilityArray[i] = 1.0;
|
|
4558
|
+
}
|
|
4559
|
+
mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
4353
4560
|
if (this.useVAO) {
|
|
4354
4561
|
this.createVAO(mergedGeometry);
|
|
4355
4562
|
}
|
|
4356
|
-
const
|
|
4563
|
+
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
4564
|
+
const mergedMesh = new Mesh(mergedGeometry, visibilityMaterial);
|
|
4357
4565
|
rootGroup.add(mergedMesh);
|
|
4358
4566
|
this.mergedMesh.add(mergedMesh);
|
|
4359
4567
|
this.optimizedOriginalMap.set(mergedMesh, optimizedObjects);
|
|
4568
|
+
this.mergedObjectMap.set(mergedMesh.uuid, {
|
|
4569
|
+
objectMapping,
|
|
4570
|
+
visibilityArray,
|
|
4571
|
+
totalVertices,
|
|
4572
|
+
});
|
|
4573
|
+
this.mergedGeometryVisibility.set(mergedMesh, visibilityArray);
|
|
4360
4574
|
mergedObjects.push(mergedMesh);
|
|
4361
4575
|
geometries.forEach((geometry) => {
|
|
4362
4576
|
geometry.dispose();
|
|
@@ -4382,8 +4596,14 @@ class DynamicGltfLoader {
|
|
|
4382
4596
|
mergeLineGroups(materialGroups, rootGroup) {
|
|
4383
4597
|
for (const group of materialGroups) {
|
|
4384
4598
|
if (group.objects.length === 0) continue;
|
|
4599
|
+
if (!group.material) {
|
|
4600
|
+
console.warn("Skipping line group with null material");
|
|
4601
|
+
continue;
|
|
4602
|
+
}
|
|
4385
4603
|
const handles = new Set();
|
|
4386
4604
|
let totalVertices = 0;
|
|
4605
|
+
const objectMapping = new Map();
|
|
4606
|
+
let currentVertexOffset = 0;
|
|
4387
4607
|
group.objects.map((line) => {
|
|
4388
4608
|
handles.add(line.userData.handle);
|
|
4389
4609
|
totalVertices += line.geometry.attributes.position.count;
|
|
@@ -4396,6 +4616,15 @@ class DynamicGltfLoader {
|
|
|
4396
4616
|
const geometry = line.geometry;
|
|
4397
4617
|
const positionAttr = geometry.attributes.position;
|
|
4398
4618
|
const vertexCount = positionAttr.count;
|
|
4619
|
+
const handle = line.userData.handle;
|
|
4620
|
+
if (!this.objectIdToIndex.has(handle)) {
|
|
4621
|
+
this.objectIdToIndex.set(handle, this.maxObjectId++);
|
|
4622
|
+
}
|
|
4623
|
+
objectMapping.set(line, {
|
|
4624
|
+
startVertexIndex: currentVertexOffset,
|
|
4625
|
+
vertexCount,
|
|
4626
|
+
});
|
|
4627
|
+
currentVertexOffset += vertexCount;
|
|
4399
4628
|
line.updateWorldMatrix(true, false);
|
|
4400
4629
|
const matrix = line.matrixWorld;
|
|
4401
4630
|
const vector = new Vector3();
|
|
@@ -4416,7 +4645,24 @@ class DynamicGltfLoader {
|
|
|
4416
4645
|
geometry.setIndex(indices);
|
|
4417
4646
|
geometry.computeBoundingSphere();
|
|
4418
4647
|
geometry.computeBoundingBox();
|
|
4419
|
-
const
|
|
4648
|
+
const objectIds = new Float32Array(totalVertices);
|
|
4649
|
+
let vertexIndex = 0;
|
|
4650
|
+
group.objects.forEach((line) => {
|
|
4651
|
+
const vertexCount = line.geometry.attributes.position.count;
|
|
4652
|
+
const handle = line.userData.handle;
|
|
4653
|
+
const objectId = this.objectIdToIndex.get(handle);
|
|
4654
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
4655
|
+
objectIds[vertexIndex++] = objectId;
|
|
4656
|
+
}
|
|
4657
|
+
});
|
|
4658
|
+
geometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
|
|
4659
|
+
const visibilityArray = new Float32Array(totalVertices);
|
|
4660
|
+
for (let i = 0; i < totalVertices; i++) {
|
|
4661
|
+
visibilityArray[i] = 1.0;
|
|
4662
|
+
}
|
|
4663
|
+
geometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
4664
|
+
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
4665
|
+
const mergedLine = new LineSegments(geometry, visibilityMaterial);
|
|
4420
4666
|
const mergedObjects = [mergedLine];
|
|
4421
4667
|
if (this.useVAO) {
|
|
4422
4668
|
this.createVAO(mergedLine);
|
|
@@ -4424,6 +4670,12 @@ class DynamicGltfLoader {
|
|
|
4424
4670
|
rootGroup.add(mergedLine);
|
|
4425
4671
|
this.mergedLines.add(mergedLine);
|
|
4426
4672
|
this.optimizedOriginalMap.set(mergedLine, group.objects);
|
|
4673
|
+
this.mergedObjectMap.set(mergedLine.uuid, {
|
|
4674
|
+
objectMapping,
|
|
4675
|
+
visibilityArray,
|
|
4676
|
+
totalVertices,
|
|
4677
|
+
});
|
|
4678
|
+
this.mergedGeometryVisibility.set(mergedLine, visibilityArray);
|
|
4427
4679
|
handles.forEach((handle) => {
|
|
4428
4680
|
if (this.handleToOptimizedObjects.has(handle)) {
|
|
4429
4681
|
const existingObjects = this.handleToOptimizedObjects.get(handle);
|
|
@@ -4437,14 +4689,37 @@ class DynamicGltfLoader {
|
|
|
4437
4689
|
}
|
|
4438
4690
|
mergeLineSegmentGroups(materialGroups, rootGroup) {
|
|
4439
4691
|
for (const group of materialGroups) {
|
|
4692
|
+
if (!group.material) {
|
|
4693
|
+
console.warn("Skipping line segment group with null material");
|
|
4694
|
+
continue;
|
|
4695
|
+
}
|
|
4440
4696
|
try {
|
|
4441
4697
|
const geometries = [];
|
|
4442
4698
|
const optimizedObjects = [];
|
|
4443
4699
|
const handles = new Set();
|
|
4700
|
+
const objectMapping = new Map();
|
|
4701
|
+
let currentVertexOffset = 0;
|
|
4444
4702
|
for (const line of group.objects) {
|
|
4445
4703
|
const geometry = line.geometry.clone();
|
|
4446
4704
|
line.updateWorldMatrix(true, false);
|
|
4447
4705
|
geometry.applyMatrix4(line.matrixWorld);
|
|
4706
|
+
const handle = line.userData.handle;
|
|
4707
|
+
if (!this.objectIdToIndex.has(handle)) {
|
|
4708
|
+
this.objectIdToIndex.set(handle, this.maxObjectId++);
|
|
4709
|
+
}
|
|
4710
|
+
const objectId = this.objectIdToIndex.get(handle);
|
|
4711
|
+
const vertexCount = geometry.attributes.position.count;
|
|
4712
|
+
const objectIds = new Float32Array(vertexCount);
|
|
4713
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
4714
|
+
objectIds[i] = objectId;
|
|
4715
|
+
}
|
|
4716
|
+
geometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
|
|
4717
|
+
objectMapping.set(line, {
|
|
4718
|
+
geometry,
|
|
4719
|
+
startVertexIndex: currentVertexOffset,
|
|
4720
|
+
vertexCount: geometry.attributes.position.count,
|
|
4721
|
+
});
|
|
4722
|
+
currentVertexOffset += geometry.attributes.position.count;
|
|
4448
4723
|
geometries.push(geometry);
|
|
4449
4724
|
optimizedObjects.push(line);
|
|
4450
4725
|
handles.add(line.userData.handle);
|
|
@@ -4452,13 +4727,26 @@ class DynamicGltfLoader {
|
|
|
4452
4727
|
const mergedObjects = [];
|
|
4453
4728
|
if (geometries.length > 0) {
|
|
4454
4729
|
const mergedGeometry = mergeGeometries(geometries, false);
|
|
4455
|
-
const
|
|
4730
|
+
const totalVertices = mergedGeometry.attributes.position.count;
|
|
4731
|
+
const visibilityArray = new Float32Array(totalVertices);
|
|
4732
|
+
for (let i = 0; i < totalVertices; i++) {
|
|
4733
|
+
visibilityArray[i] = 1.0;
|
|
4734
|
+
}
|
|
4735
|
+
mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
4736
|
+
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
4737
|
+
const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
|
|
4456
4738
|
if (this.useVAO) {
|
|
4457
4739
|
this.createVAO(mergedLine);
|
|
4458
4740
|
}
|
|
4459
4741
|
rootGroup.add(mergedLine);
|
|
4460
4742
|
this.mergedLineSegments.add(mergedLine);
|
|
4461
4743
|
this.optimizedOriginalMap.set(mergedLine, optimizedObjects);
|
|
4744
|
+
this.mergedObjectMap.set(mergedLine.uuid, {
|
|
4745
|
+
objectMapping,
|
|
4746
|
+
visibilityArray,
|
|
4747
|
+
totalVertices,
|
|
4748
|
+
});
|
|
4749
|
+
this.mergedGeometryVisibility.set(mergedLine, visibilityArray);
|
|
4462
4750
|
mergedObjects.push(mergedLine);
|
|
4463
4751
|
geometries.forEach((geometry) => {
|
|
4464
4752
|
geometry.dispose();
|
|
@@ -4483,6 +4771,10 @@ class DynamicGltfLoader {
|
|
|
4483
4771
|
}
|
|
4484
4772
|
mergePointsGroups(materialGroups, rootGroup) {
|
|
4485
4773
|
for (const group of materialGroups) {
|
|
4774
|
+
if (!group.material) {
|
|
4775
|
+
console.warn("Skipping points group with null material");
|
|
4776
|
+
continue;
|
|
4777
|
+
}
|
|
4486
4778
|
try {
|
|
4487
4779
|
const geometries = [];
|
|
4488
4780
|
const optimizedObjects = [];
|
|
@@ -4665,97 +4957,51 @@ class DynamicGltfLoader {
|
|
|
4665
4957
|
});
|
|
4666
4958
|
this.syncHiddenObjects();
|
|
4667
4959
|
}
|
|
4668
|
-
|
|
4669
|
-
if (
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
for (
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4960
|
+
_updateVisibilityAttribute(mergedObject) {
|
|
4961
|
+
if (
|
|
4962
|
+
mergedObject.geometry &&
|
|
4963
|
+
mergedObject.geometry.attributes.visibility &&
|
|
4964
|
+
mergedObject.geometry.attributes.objectId
|
|
4965
|
+
) {
|
|
4966
|
+
const visibilityArray = mergedObject.geometry.attributes.visibility.array;
|
|
4967
|
+
const objectIdArray = mergedObject.geometry.attributes.objectId.array;
|
|
4968
|
+
for (let i = 0; i < visibilityArray.length; i++) {
|
|
4969
|
+
const objectId = objectIdArray[i];
|
|
4970
|
+
if (objectId < this.objectVisibility.length) {
|
|
4971
|
+
visibilityArray[i] = this.objectVisibility[objectId];
|
|
4972
|
+
}
|
|
4680
4973
|
}
|
|
4681
|
-
|
|
4974
|
+
mergedObject.geometry.attributes.visibility.needsUpdate = true;
|
|
4682
4975
|
}
|
|
4683
|
-
|
|
4976
|
+
}
|
|
4977
|
+
syncHiddenObjects() {
|
|
4978
|
+
if (this.mergedObjectMap.size === 0) {
|
|
4979
|
+
console.log("No merged objects to sync");
|
|
4684
4980
|
return;
|
|
4685
4981
|
}
|
|
4686
|
-
this.
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
objects.forEach((x) => this.oldOptimizeObjects.add(x));
|
|
4982
|
+
if (this.objectVisibility.length > 0) {
|
|
4983
|
+
for (let i = 0; i < this.objectVisibility.length; i++) {
|
|
4984
|
+
this.objectVisibility[i] = 1.0;
|
|
4690
4985
|
}
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
const updateListToOptimize = [];
|
|
4696
|
-
originObjects.forEach((obj) => {
|
|
4697
|
-
if (!this.hiddenHandles.has(obj.userData.handle)) {
|
|
4698
|
-
updateListToOptimize.push(obj);
|
|
4986
|
+
this.hiddenHandles.forEach((handle) => {
|
|
4987
|
+
const index = this.objectIdToIndex.get(handle);
|
|
4988
|
+
if (index !== undefined && index < this.objectVisibility.length) {
|
|
4989
|
+
this.objectVisibility[index] = 0.0;
|
|
4699
4990
|
}
|
|
4700
4991
|
});
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
mergedObject.visible = true;
|
|
4715
|
-
optimizedObject.parent.add(mergedObject);
|
|
4716
|
-
this.newOptimizedObjects.add(mergedObject);
|
|
4717
|
-
geometries.forEach((geometry) => {
|
|
4718
|
-
geometry.dispose();
|
|
4719
|
-
});
|
|
4720
|
-
} else if (firstObject instanceof Line) {
|
|
4721
|
-
let totalVertices = 0;
|
|
4722
|
-
updateListToOptimize.map((line) => {
|
|
4723
|
-
totalVertices += line.geometry.attributes.position.count;
|
|
4724
|
-
});
|
|
4725
|
-
const positions = new Float32Array(totalVertices * 3);
|
|
4726
|
-
let posOffset = 0;
|
|
4727
|
-
const indices = [];
|
|
4728
|
-
let vertexOffset = 0;
|
|
4729
|
-
updateListToOptimize.forEach((line) => {
|
|
4730
|
-
const geometry = line.geometry;
|
|
4731
|
-
const positionAttr = geometry.attributes.position;
|
|
4732
|
-
const vertexCount = positionAttr.count;
|
|
4733
|
-
line.updateWorldMatrix(true, false);
|
|
4734
|
-
const matrix = line.matrixWorld;
|
|
4735
|
-
const vector = new Vector3();
|
|
4736
|
-
for (let i = 0; i < vertexCount; i++) {
|
|
4737
|
-
vector.fromBufferAttribute(positionAttr, i);
|
|
4738
|
-
vector.applyMatrix4(matrix);
|
|
4739
|
-
positions[posOffset++] = vector.x;
|
|
4740
|
-
positions[posOffset++] = vector.y;
|
|
4741
|
-
positions[posOffset++] = vector.z;
|
|
4742
|
-
}
|
|
4743
|
-
for (let i = 0; i < vertexCount - 1; i++) {
|
|
4744
|
-
indices.push(vertexOffset + i, vertexOffset + i + 1);
|
|
4745
|
-
}
|
|
4746
|
-
vertexOffset += vertexCount;
|
|
4747
|
-
});
|
|
4748
|
-
const geometry = new BufferGeometry();
|
|
4749
|
-
geometry.setAttribute("position", new BufferAttribute(positions, 3));
|
|
4750
|
-
geometry.setIndex(indices);
|
|
4751
|
-
geometry.computeBoundingSphere();
|
|
4752
|
-
geometry.computeBoundingBox();
|
|
4753
|
-
const mergedLine = new LineSegments(geometry, optimizedObject.material);
|
|
4754
|
-
mergedLine.visible = true;
|
|
4755
|
-
optimizedObject.parent.add(mergedLine);
|
|
4756
|
-
this.newOptimizedObjects.add(mergedLine);
|
|
4757
|
-
}
|
|
4758
|
-
});
|
|
4992
|
+
}
|
|
4993
|
+
for (const mesh of this.mergedMesh) {
|
|
4994
|
+
this._updateVisibilityAttribute(mesh);
|
|
4995
|
+
}
|
|
4996
|
+
for (const line of this.mergedLines) {
|
|
4997
|
+
this._updateVisibilityAttribute(line);
|
|
4998
|
+
}
|
|
4999
|
+
for (const lineSegment of this.mergedLineSegments) {
|
|
5000
|
+
this._updateVisibilityAttribute(lineSegment);
|
|
5001
|
+
}
|
|
5002
|
+
for (const point of this.mergedPoints) {
|
|
5003
|
+
this._updateVisibilityAttribute(point);
|
|
5004
|
+
}
|
|
4759
5005
|
}
|
|
4760
5006
|
getStructureGeometryExtent(structureId) {
|
|
4761
5007
|
const extent = new Box3();
|