@inweb/viewer-three 26.10.6 → 26.11.1
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/plugins/components/AxesHelperComponent.js +23 -1
- package/dist/plugins/components/AxesHelperComponent.js.map +1 -1
- package/dist/plugins/components/AxesHelperComponent.min.js +1 -1
- package/dist/plugins/components/AxesHelperComponent.module.js +24 -2
- package/dist/plugins/components/AxesHelperComponent.module.js.map +1 -1
- package/dist/plugins/components/ExtentsHelperComponent.js +18 -0
- package/dist/plugins/components/ExtentsHelperComponent.js.map +1 -1
- package/dist/plugins/components/ExtentsHelperComponent.min.js +1 -1
- package/dist/plugins/components/ExtentsHelperComponent.module.js +19 -1
- package/dist/plugins/components/ExtentsHelperComponent.module.js.map +1 -1
- package/dist/plugins/loaders/GLTFCloudLoader.js +2 -3
- package/dist/plugins/loaders/GLTFCloudLoader.js.map +1 -1
- package/dist/plugins/loaders/GLTFCloudLoader.min.js +1 -1
- package/dist/plugins/loaders/GLTFCloudLoader.module.js +2 -3
- package/dist/plugins/loaders/GLTFCloudLoader.module.js.map +1 -1
- package/dist/plugins/loaders/GLTFFileLoader.js +2499 -0
- package/dist/plugins/loaders/GLTFFileLoader.js.map +1 -0
- package/dist/plugins/loaders/GLTFFileLoader.min.js +24 -0
- package/dist/plugins/loaders/GLTFFileLoader.module.js +74 -0
- package/dist/plugins/loaders/GLTFFileLoader.module.js.map +1 -0
- package/dist/plugins/loaders/IFCXLoader.js +5 -7
- package/dist/plugins/loaders/IFCXLoader.js.map +1 -1
- package/dist/plugins/loaders/IFCXLoader.min.js +1 -1
- package/dist/plugins/loaders/IFCXLoader.module.js +5 -7
- package/dist/plugins/loaders/IFCXLoader.module.js.map +1 -1
- package/dist/plugins/loaders/PotreeLoader.js +1 -2
- package/dist/plugins/loaders/PotreeLoader.js.map +1 -1
- package/dist/plugins/loaders/PotreeLoader.min.js +1 -1
- package/dist/plugins/loaders/PotreeLoader.module.js +1 -2
- package/dist/plugins/loaders/PotreeLoader.module.js.map +1 -1
- package/dist/viewer-three.js +770 -2777
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +609 -206
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/Viewer.d.ts +27 -20
- package/lib/Viewer/commands/GetSelected2.d.ts +2 -0
- package/lib/Viewer/commands/SelectModel.d.ts +1 -1
- package/lib/Viewer/commands/SetSelected2.d.ts +2 -0
- package/lib/Viewer/components/index.d.ts +6 -6
- package/lib/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.d.ts +0 -1
- package/lib/Viewer/loaders/GLTFBinaryExtension.d.ts +5 -0
- package/lib/Viewer/loaders/GLTFCloudDynamicLoader.d.ts +2 -2
- package/lib/Viewer/loaders/{GLTFFileLoader.d.ts → GLTFFileDynamicLoader.d.ts} +7 -1
- package/lib/Viewer/loaders/GLTFLoadingManager.d.ts +4 -3
- package/lib/Viewer/loaders/RangesLoader.d.ts +15 -0
- package/lib/Viewer/loaders/index.d.ts +22 -14
- package/lib/Viewer/models/IModelImpl.d.ts +6 -8
- package/lib/Viewer/models/ModelImpl.d.ts +3 -5
- package/package.json +5 -5
- package/plugins/components/AxesHelperComponent.ts +31 -2
- package/plugins/components/ExtentsHelperComponent.ts +25 -0
- package/plugins/loaders/GLTFCloudLoader.ts +2 -3
- package/{src/Viewer → plugins}/loaders/GLTFFileLoader.ts +21 -12
- package/plugins/loaders/IFCX/IFCXCloudLoader.ts +5 -5
- package/plugins/loaders/IFCX/IFCXFileLoader.ts +3 -4
- package/plugins/loaders/Potree/PotreeFileLoader.ts +3 -4
- package/src/Viewer/Viewer.ts +120 -88
- package/src/Viewer/commands/ClearSelected.ts +3 -1
- package/src/Viewer/commands/GetModels.ts +1 -1
- package/src/Viewer/commands/GetSelected.ts +2 -2
- package/src/Viewer/commands/GetSelected2.ts +34 -0
- package/src/Viewer/commands/HideSelected.ts +3 -1
- package/src/Viewer/commands/SelectModel.ts +5 -5
- package/src/Viewer/commands/SetSelected.ts +9 -10
- package/src/Viewer/commands/SetSelected2.ts +42 -0
- package/src/Viewer/commands/ZoomToObjects.ts +5 -6
- package/src/Viewer/commands/ZoomToSelected.ts +3 -1
- package/src/Viewer/commands/index.ts +4 -0
- package/src/Viewer/components/CameraComponent.ts +6 -1
- package/src/Viewer/components/ExtentsComponent.ts +4 -1
- package/src/Viewer/components/SelectionComponent.ts +2 -0
- package/src/Viewer/components/index.ts +6 -6
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +253 -22
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +20 -10
- package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +3 -1
- package/src/Viewer/loaders/GLTFBinaryExtension.ts +91 -0
- package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +13 -19
- package/src/Viewer/loaders/GLTFFileDynamicLoader.ts +145 -0
- package/src/Viewer/loaders/GLTFLoadingManager.ts +5 -4
- package/src/Viewer/loaders/RangesLoader.ts +95 -0
- package/src/Viewer/loaders/index.ts +24 -16
- package/src/Viewer/models/IModelImpl.ts +8 -9
- package/src/Viewer/models/ModelImpl.ts +30 -17
|
@@ -23,12 +23,11 @@
|
|
|
23
23
|
|
|
24
24
|
import { draggersRegistry, commandsRegistry, Options, componentsRegistry, Loader, loadersRegistry, 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, Line3, Matrix4, Vector4, MathUtils, Raycaster, EdgesGeometry, Controls, Clock, Sphere, Box3, Color, PerspectiveCamera, OrthographicCamera, AmbientLight, DirectionalLight, HemisphereLight, MeshPhongMaterial, WebGLRenderTarget, UnsignedByteType, RGBAFormat, CylinderGeometry, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial,
|
|
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, PerspectiveCamera, OrthographicCamera, AmbientLight, DirectionalLight, HemisphereLight, MeshPhongMaterial, WebGLRenderTarget, UnsignedByteType, RGBAFormat, CylinderGeometry, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial, TextureLoader, BufferAttribute, PointsMaterial, Points, TriangleStripDrawMode, TriangleFanDrawMode, LineSegments, LineLoop, Group, NormalBlending, LoadingManager, LoaderUtils, FileLoader, UniformsUtils, ShaderMaterial, AdditiveBlending, HalfFloatType, Scene, WebGLRenderer, LinearSRGBColorSpace } from 'three';
|
|
27
27
|
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
|
|
28
28
|
import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js';
|
|
29
29
|
import { Wireframe } from 'three/examples/jsm/lines/Wireframe.js';
|
|
30
30
|
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';
|
|
31
|
-
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
32
31
|
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
33
32
|
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
|
|
34
33
|
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
|
|
@@ -2060,7 +2059,8 @@ function clearSelected(viewer) {
|
|
|
2060
2059
|
const selection = viewer.getComponent("SelectionComponent");
|
|
2061
2060
|
selection.clearSelection();
|
|
2062
2061
|
viewer.update();
|
|
2063
|
-
viewer.emitEvent({ type: "select",
|
|
2062
|
+
viewer.emitEvent({ type: "select", handles: [] });
|
|
2063
|
+
viewer.emitEvent({ type: "select2", handles: [] });
|
|
2064
2064
|
}
|
|
2065
2065
|
|
|
2066
2066
|
function clearSlices(viewer) {
|
|
@@ -2149,22 +2149,31 @@ function getDefaultViewPositions() {
|
|
|
2149
2149
|
}
|
|
2150
2150
|
|
|
2151
2151
|
function getModels(viewer) {
|
|
2152
|
-
return viewer.models.map((model) => model.
|
|
2152
|
+
return viewer.models.map((model) => model.id);
|
|
2153
2153
|
}
|
|
2154
2154
|
|
|
2155
2155
|
function getSelected(viewer) {
|
|
2156
|
-
const
|
|
2157
|
-
|
|
2156
|
+
const handles2 = viewer.executeCommand("getSelected2");
|
|
2157
|
+
const handles = handles2.map((handle) => handle.slice(handle.indexOf(":") + 1));
|
|
2158
2158
|
return handles;
|
|
2159
2159
|
}
|
|
2160
2160
|
|
|
2161
|
+
function getSelected2(viewer) {
|
|
2162
|
+
const handles2 = [];
|
|
2163
|
+
viewer.models.forEach((model) => {
|
|
2164
|
+
handles2.push(...model.getHandlesByObjects(viewer.selected));
|
|
2165
|
+
});
|
|
2166
|
+
return handles2;
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2161
2169
|
function hideSelected(viewer) {
|
|
2162
2170
|
viewer.models.forEach((model) => model.hideObjects(viewer.selected));
|
|
2163
2171
|
const selection = viewer.getComponent("SelectionComponent");
|
|
2164
2172
|
selection.clearSelection();
|
|
2165
2173
|
viewer.update();
|
|
2166
2174
|
viewer.emitEvent({ type: "hide" });
|
|
2167
|
-
viewer.emitEvent({ type: "select",
|
|
2175
|
+
viewer.emitEvent({ type: "select", handles: [] });
|
|
2176
|
+
viewer.emitEvent({ type: "select2", handles: [] });
|
|
2168
2177
|
}
|
|
2169
2178
|
|
|
2170
2179
|
function isolateSelected(viewer) {
|
|
@@ -2191,14 +2200,13 @@ function resetView(viewer) {
|
|
|
2191
2200
|
viewer.emit({ type: "resetview" });
|
|
2192
2201
|
}
|
|
2193
2202
|
|
|
2194
|
-
function selectModel(viewer,
|
|
2203
|
+
function selectModel(viewer, id) {
|
|
2195
2204
|
const selection = viewer.getComponent("SelectionComponent");
|
|
2196
2205
|
selection.clearSelection();
|
|
2197
|
-
viewer.models
|
|
2198
|
-
.filter((model) => model.handle === handle)
|
|
2199
|
-
.forEach((model) => selection.select(model.getObjects(), model));
|
|
2206
|
+
viewer.models.filter((model) => model.id === id).forEach((model) => selection.select(model.getObjects(), model));
|
|
2200
2207
|
viewer.update();
|
|
2201
|
-
viewer.
|
|
2208
|
+
viewer.emitEvent({ type: "select", handles: viewer.getSelected() });
|
|
2209
|
+
viewer.emitEvent({ type: "select2", handles: viewer.getSelected2() });
|
|
2202
2210
|
}
|
|
2203
2211
|
|
|
2204
2212
|
function setActiveDragger(viewer, dragger = "") {
|
|
@@ -2210,16 +2218,31 @@ function setMarkupColor(viewer, r = 255, g = 0, b = 0) {
|
|
|
2210
2218
|
}
|
|
2211
2219
|
|
|
2212
2220
|
function setSelected(viewer, handles = []) {
|
|
2213
|
-
const
|
|
2214
|
-
|
|
2221
|
+
const handles2 = [];
|
|
2222
|
+
handles.forEach((handle) => {
|
|
2223
|
+
if (handle.includes(":")) {
|
|
2224
|
+
handles2.push(handle);
|
|
2225
|
+
}
|
|
2226
|
+
else
|
|
2227
|
+
viewer.models.forEach((model) => {
|
|
2228
|
+
handles2.push(`${model.id}:${handle}`);
|
|
2229
|
+
});
|
|
2230
|
+
});
|
|
2231
|
+
viewer.executeCommand("setSelected2", handles2);
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
function setSelected2(viewer, handles = []) {
|
|
2235
|
+
const selectionComponent = viewer.getComponent("SelectionComponent");
|
|
2236
|
+
selectionComponent.clearSelection();
|
|
2215
2237
|
viewer.models.forEach((model) => {
|
|
2216
2238
|
const objects = model.getObjectsByHandles(handles);
|
|
2217
2239
|
model.showObjects(objects);
|
|
2218
|
-
|
|
2240
|
+
selectionComponent.select(objects, model);
|
|
2219
2241
|
});
|
|
2220
2242
|
viewer.update();
|
|
2221
2243
|
viewer.emitEvent({ type: "show" });
|
|
2222
|
-
viewer.emitEvent({ type: "select", data: undefined, handles });
|
|
2244
|
+
viewer.emitEvent({ type: "select", data: undefined, handles: viewer.getSelected() });
|
|
2245
|
+
viewer.emitEvent({ type: "select2", data: undefined, handles });
|
|
2223
2246
|
}
|
|
2224
2247
|
|
|
2225
2248
|
function showAll(viewer) {
|
|
@@ -2233,21 +2256,19 @@ function zoomToExtents(viewer) {
|
|
|
2233
2256
|
}
|
|
2234
2257
|
|
|
2235
2258
|
function zoomToObjects(viewer, handles = []) {
|
|
2236
|
-
const
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
if (handleSet.has((_a = child.userData) === null || _a === void 0 ? void 0 : _a.handle))
|
|
2241
|
-
objects.push(child);
|
|
2259
|
+
const extents = new Box3();
|
|
2260
|
+
viewer.models.forEach((model) => {
|
|
2261
|
+
const objects = model.getObjectsByHandles(handles);
|
|
2262
|
+
objects.forEach((object) => extents.expandByObject(object));
|
|
2242
2263
|
});
|
|
2243
|
-
const extents = objects.reduce((result, object) => result.expandByObject(object), new Box3());
|
|
2244
2264
|
if (extents.isEmpty())
|
|
2245
2265
|
extents.copy(viewer.extents);
|
|
2246
2266
|
zoomTo(viewer, extents);
|
|
2247
2267
|
}
|
|
2248
2268
|
|
|
2249
2269
|
function zoomToSelected(viewer) {
|
|
2250
|
-
const extents =
|
|
2270
|
+
const extents = new Box3();
|
|
2271
|
+
viewer.selected.forEach((object) => extents.expandByObject(object));
|
|
2251
2272
|
if (extents.isEmpty())
|
|
2252
2273
|
extents.copy(viewer.extents);
|
|
2253
2274
|
zoomTo(viewer, extents);
|
|
@@ -2264,6 +2285,7 @@ commands.registerCommand("explode", explode);
|
|
|
2264
2285
|
commands.registerCommand("getDefaultViewPositions", getDefaultViewPositions);
|
|
2265
2286
|
commands.registerCommand("getModels", getModels);
|
|
2266
2287
|
commands.registerCommand("getSelected", getSelected);
|
|
2288
|
+
commands.registerCommand("getSelected2", getSelected2);
|
|
2267
2289
|
commands.registerCommand("hideSelected", hideSelected);
|
|
2268
2290
|
commands.registerCommand("isolateSelected", isolateSelected);
|
|
2269
2291
|
commands.registerCommand("regenerateAll", regenerateAll);
|
|
@@ -2273,6 +2295,7 @@ commands.registerCommand("setActiveDragger", setActiveDragger);
|
|
|
2273
2295
|
commands.registerCommand("setDefaultViewPosition", setDefaultViewPosition);
|
|
2274
2296
|
commands.registerCommand("setMarkupColor", setMarkupColor);
|
|
2275
2297
|
commands.registerCommand("setSelected", setSelected);
|
|
2298
|
+
commands.registerCommand("setSelected2", setSelected2);
|
|
2276
2299
|
commands.registerCommand("showAll", showAll);
|
|
2277
2300
|
commands.registerCommand("zoomToExtents", zoomToExtents);
|
|
2278
2301
|
commands.registerCommand("zoomToObjects", zoomToObjects);
|
|
@@ -2325,6 +2348,10 @@ class CameraComponent {
|
|
|
2325
2348
|
this.switchCameraMode(this.viewer.options.cameraMode);
|
|
2326
2349
|
};
|
|
2327
2350
|
this.geometryEnd = () => {
|
|
2351
|
+
if (this.viewer.models.length > 1) {
|
|
2352
|
+
this.switchCamera(this.viewer.camera);
|
|
2353
|
+
return;
|
|
2354
|
+
}
|
|
2328
2355
|
let camera;
|
|
2329
2356
|
this.viewer.scene.traverse((object) => {
|
|
2330
2357
|
if (object.isCamera)
|
|
@@ -2422,6 +2449,8 @@ class ExtentsComponent {
|
|
|
2422
2449
|
const extents = new Box3();
|
|
2423
2450
|
this.viewer.models.forEach((model) => model.getExtents(extents));
|
|
2424
2451
|
this.viewer.extents.copy(extents);
|
|
2452
|
+
if (this.viewer.models.length > 1)
|
|
2453
|
+
return;
|
|
2425
2454
|
this.viewer.extents.getCenter(this.viewer.target);
|
|
2426
2455
|
};
|
|
2427
2456
|
this.viewer = viewer;
|
|
@@ -2770,6 +2799,7 @@ class SelectionComponent {
|
|
|
2770
2799
|
}
|
|
2771
2800
|
this.viewer.update();
|
|
2772
2801
|
this.viewer.emitEvent({ type: "select", data: undefined, handles: this.viewer.getSelected() });
|
|
2802
|
+
this.viewer.emitEvent({ type: "select2", data: undefined, handles: this.viewer.getSelected2() });
|
|
2773
2803
|
};
|
|
2774
2804
|
this.onDoubleClick = (event) => {
|
|
2775
2805
|
if (event.button !== 0)
|
|
@@ -3014,48 +3044,8 @@ components.registerComponent("SelectionComponent", (viewer) => new SelectionComp
|
|
|
3014
3044
|
components.registerComponent("WCSHelperComponent", (viewer) => new WCSHelperComponent(viewer));
|
|
3015
3045
|
components.registerComponent("ResetComponent", (viewer) => new ResetComponent(viewer));
|
|
3016
3046
|
|
|
3017
|
-
class GLTFLoadingManager extends LoadingManager {
|
|
3018
|
-
constructor(file, params = {}) {
|
|
3019
|
-
super();
|
|
3020
|
-
this.path = "";
|
|
3021
|
-
this.resourcePath = "";
|
|
3022
|
-
this.fileURL = "";
|
|
3023
|
-
this.dataURLs = new Map();
|
|
3024
|
-
this.path = params.path || "";
|
|
3025
|
-
const externalFiles = params.externalFiles || new Map();
|
|
3026
|
-
if (typeof file === "string") {
|
|
3027
|
-
this.fileURL = file;
|
|
3028
|
-
this.resourcePath = LoaderUtils.extractUrlBase(file);
|
|
3029
|
-
}
|
|
3030
|
-
else {
|
|
3031
|
-
externalFiles.forEach((value, key) => (this.fileURL = value === file ? key : this.fileURL));
|
|
3032
|
-
externalFiles.set(this.fileURL, file);
|
|
3033
|
-
}
|
|
3034
|
-
externalFiles.forEach((value, key) => {
|
|
3035
|
-
let dataURL;
|
|
3036
|
-
if (typeof value === "string")
|
|
3037
|
-
dataURL = value;
|
|
3038
|
-
else
|
|
3039
|
-
dataURL = URL.createObjectURL(new Blob([value]));
|
|
3040
|
-
this.dataURLs.set(key, dataURL);
|
|
3041
|
-
});
|
|
3042
|
-
this.setURLModifier((url) => {
|
|
3043
|
-
const key = decodeURI(url)
|
|
3044
|
-
.replace(this.path, "")
|
|
3045
|
-
.replace(this.resourcePath, "")
|
|
3046
|
-
.replace(/^(\.?\/)/, "");
|
|
3047
|
-
const dataURL = this.dataURLs.get(key);
|
|
3048
|
-
return dataURL !== null && dataURL !== void 0 ? dataURL : url;
|
|
3049
|
-
});
|
|
3050
|
-
}
|
|
3051
|
-
dispose() {
|
|
3052
|
-
this.dataURLs.forEach(URL.revokeObjectURL);
|
|
3053
|
-
}
|
|
3054
|
-
}
|
|
3055
|
-
|
|
3056
3047
|
class ModelImpl {
|
|
3057
3048
|
constructor(scene) {
|
|
3058
|
-
this.handle = "1";
|
|
3059
3049
|
this.scene = scene;
|
|
3060
3050
|
}
|
|
3061
3051
|
dispose() {
|
|
@@ -3097,27 +3087,45 @@ class ModelImpl {
|
|
|
3097
3087
|
}
|
|
3098
3088
|
return false;
|
|
3099
3089
|
}
|
|
3090
|
+
hasHandle(handle) {
|
|
3091
|
+
return !handle.includes(":") || handle.split(":", 1)[0] === this.id + "";
|
|
3092
|
+
}
|
|
3100
3093
|
getOwnObjects(objects) {
|
|
3101
3094
|
if (!Array.isArray(objects))
|
|
3102
3095
|
objects = [objects];
|
|
3103
3096
|
return objects.filter((object) => this.hasObject(object));
|
|
3104
3097
|
}
|
|
3098
|
+
getOwnHandles(handles) {
|
|
3099
|
+
if (!Array.isArray(handles))
|
|
3100
|
+
handles = [handles];
|
|
3101
|
+
return handles.filter((handle) => this.hasHandle(handle));
|
|
3102
|
+
}
|
|
3105
3103
|
getObjectsByHandles(handles) {
|
|
3106
|
-
const
|
|
3104
|
+
const ownHandles = this.getOwnHandles(handles);
|
|
3105
|
+
if (ownHandles.length === 0)
|
|
3106
|
+
return [];
|
|
3107
|
+
const handleSet = new Set(ownHandles.map((handle) => handle.slice(handle.indexOf(":") + 1)));
|
|
3107
3108
|
const objects = [];
|
|
3108
|
-
this.scene.traverse((object) =>
|
|
3109
|
+
this.scene.traverse((object) => {
|
|
3110
|
+
const handle = object.userData.handle;
|
|
3111
|
+
if (handle && handleSet.has(handle))
|
|
3112
|
+
objects.push(object);
|
|
3113
|
+
});
|
|
3109
3114
|
return objects;
|
|
3110
3115
|
}
|
|
3111
3116
|
getHandlesByObjects(objects) {
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
+
const ownObjects = this.getOwnObjects(objects);
|
|
3118
|
+
if (ownObjects.length === 0)
|
|
3119
|
+
return [];
|
|
3120
|
+
const handleSet = new Set();
|
|
3121
|
+
ownObjects.forEach((object) => {
|
|
3122
|
+
const handle = object.userData.handle;
|
|
3123
|
+
if (handle)
|
|
3124
|
+
handleSet.add(`${this.id}:${handle}`);
|
|
3125
|
+
});
|
|
3126
|
+
return Array.from(handleSet);
|
|
3117
3127
|
}
|
|
3118
3128
|
hideObjects(objects) {
|
|
3119
|
-
if (!Array.isArray(objects))
|
|
3120
|
-
objects = [objects];
|
|
3121
3129
|
this.getOwnObjects(objects).forEach((object) => (object.visible = false));
|
|
3122
3130
|
return this;
|
|
3123
3131
|
}
|
|
@@ -3133,8 +3141,6 @@ class ModelImpl {
|
|
|
3133
3141
|
return this;
|
|
3134
3142
|
}
|
|
3135
3143
|
showObjects(objects) {
|
|
3136
|
-
if (!Array.isArray(objects))
|
|
3137
|
-
objects = [objects];
|
|
3138
3144
|
this.getOwnObjects(objects).forEach((object) => {
|
|
3139
3145
|
object.visible = true;
|
|
3140
3146
|
object.traverseAncestors((parent) => (parent.visible = true));
|
|
@@ -3190,42 +3196,6 @@ class ModelImpl {
|
|
|
3190
3196
|
}
|
|
3191
3197
|
}
|
|
3192
3198
|
|
|
3193
|
-
class GLTFFileLoader extends Loader {
|
|
3194
|
-
constructor(viewer) {
|
|
3195
|
-
super();
|
|
3196
|
-
this.viewer = viewer;
|
|
3197
|
-
}
|
|
3198
|
-
isSupport(file, format) {
|
|
3199
|
-
return ((typeof file === "string" || file instanceof globalThis.File || file instanceof ArrayBuffer) &&
|
|
3200
|
-
/(gltf|glb)$/i.test(format));
|
|
3201
|
-
}
|
|
3202
|
-
async load(file, format, params) {
|
|
3203
|
-
const manager = new GLTFLoadingManager(file, params);
|
|
3204
|
-
const loader = new GLTFLoader(manager);
|
|
3205
|
-
loader.setPath(manager.path);
|
|
3206
|
-
loader.setCrossOrigin(params.crossOrigin || loader.crossOrigin);
|
|
3207
|
-
loader.setWithCredentials(params.withCredentials || loader.withCredentials);
|
|
3208
|
-
const progress = (event) => {
|
|
3209
|
-
const { lengthComputable, loaded, total } = event;
|
|
3210
|
-
const progress = lengthComputable ? loaded / total : 1;
|
|
3211
|
-
this.viewer.emitEvent({ type: "geometryprogress", data: progress, file });
|
|
3212
|
-
};
|
|
3213
|
-
const gltf = await loader.loadAsync(manager.fileURL, progress);
|
|
3214
|
-
if (!this.viewer.scene)
|
|
3215
|
-
return this;
|
|
3216
|
-
const modelImpl = new ModelImpl(gltf.scene);
|
|
3217
|
-
modelImpl.loader = this;
|
|
3218
|
-
modelImpl.viewer = this.viewer;
|
|
3219
|
-
this.viewer.scene.add(gltf.scene);
|
|
3220
|
-
this.viewer.models.push(modelImpl);
|
|
3221
|
-
this.viewer.syncOptions();
|
|
3222
|
-
this.viewer.syncOverlay();
|
|
3223
|
-
this.viewer.update();
|
|
3224
|
-
this.viewer.emitEvent({ type: "databasechunk", data: gltf.scene, file });
|
|
3225
|
-
return this;
|
|
3226
|
-
}
|
|
3227
|
-
}
|
|
3228
|
-
|
|
3229
3199
|
class DynamicModelImpl extends ModelImpl {
|
|
3230
3200
|
getExtents(target) {
|
|
3231
3201
|
return target.union(this.gltfLoader.getTotalGeometryExtent());
|
|
@@ -3244,31 +3214,40 @@ class DynamicModelImpl extends ModelImpl {
|
|
|
3244
3214
|
return this.gltfLoader.originalObjects.has(object);
|
|
3245
3215
|
}
|
|
3246
3216
|
getObjectsByHandles(handles) {
|
|
3247
|
-
const
|
|
3217
|
+
const ownHandles = this.getOwnHandles(handles);
|
|
3218
|
+
if (ownHandles.length === 0)
|
|
3219
|
+
return [];
|
|
3220
|
+
const handlesSet = new Set(ownHandles);
|
|
3248
3221
|
const objects = [];
|
|
3249
3222
|
handlesSet.forEach((handle) => {
|
|
3250
|
-
|
|
3251
|
-
const handles = this.gltfLoader.handleToObjects.get(handle2) || [];
|
|
3252
|
-
objects.push(...Array.from(handles));
|
|
3223
|
+
objects.push(...this.gltfLoader.getObjectsByHandle(handle));
|
|
3253
3224
|
});
|
|
3254
3225
|
return objects;
|
|
3255
3226
|
}
|
|
3256
3227
|
getHandlesByObjects(objects) {
|
|
3257
|
-
const
|
|
3258
|
-
|
|
3228
|
+
const ownObjects = this.getOwnObjects(objects);
|
|
3229
|
+
if (ownObjects.length === 0)
|
|
3230
|
+
return [];
|
|
3231
|
+
const handleSet = new Set();
|
|
3232
|
+
ownObjects.forEach((object) => {
|
|
3233
|
+
const handle = object.userData.handle;
|
|
3234
|
+
if (handle)
|
|
3235
|
+
handleSet.add(handle);
|
|
3236
|
+
});
|
|
3237
|
+
return Array.from(handleSet);
|
|
3259
3238
|
}
|
|
3260
3239
|
hideObjects(objects) {
|
|
3261
|
-
const handles =
|
|
3240
|
+
const handles = this.getHandlesByObjects(objects);
|
|
3262
3241
|
this.gltfLoader.hideObjects(handles);
|
|
3263
3242
|
return this;
|
|
3264
3243
|
}
|
|
3265
3244
|
isolateObjects(objects) {
|
|
3266
|
-
const handles =
|
|
3245
|
+
const handles = this.getHandlesByObjects(objects);
|
|
3267
3246
|
this.gltfLoader.isolateObjects(new Set(handles));
|
|
3268
3247
|
return this;
|
|
3269
3248
|
}
|
|
3270
3249
|
showObjects(objects) {
|
|
3271
|
-
const handles =
|
|
3250
|
+
const handles = this.getHandlesByObjects(objects);
|
|
3272
3251
|
this.gltfLoader.showObjects(handles);
|
|
3273
3252
|
return this;
|
|
3274
3253
|
}
|
|
@@ -3332,11 +3311,13 @@ class GltfStructure {
|
|
|
3332
3311
|
this.materials = new Map();
|
|
3333
3312
|
this.textureCache = new Map();
|
|
3334
3313
|
this.materialCache = new Map();
|
|
3314
|
+
this.uri = "";
|
|
3335
3315
|
}
|
|
3336
3316
|
async initialize(loader) {
|
|
3337
3317
|
this.json = await this.loadController.loadJson();
|
|
3338
3318
|
this.baseUrl = await this.loadController.baseUrl();
|
|
3339
3319
|
this.loader = loader;
|
|
3320
|
+
this.uri = this.json.buffers[0].uri || "";
|
|
3340
3321
|
}
|
|
3341
3322
|
clear() {
|
|
3342
3323
|
this.json = null;
|
|
@@ -3430,7 +3411,7 @@ class GltfStructure {
|
|
|
3430
3411
|
await this.loader.waitForChunkSlot();
|
|
3431
3412
|
try {
|
|
3432
3413
|
const length = range.end - range.start;
|
|
3433
|
-
const buffer = await this.loadController.loadBinaryData([{ offset: range.start, length }]);
|
|
3414
|
+
const buffer = await this.loadController.loadBinaryData([{ offset: range.start, length }], this.uri);
|
|
3434
3415
|
for (const req of range.requests) {
|
|
3435
3416
|
const relOffset = req.offset - range.start;
|
|
3436
3417
|
try {
|
|
@@ -3747,6 +3728,7 @@ class GltfStructure {
|
|
|
3747
3728
|
}
|
|
3748
3729
|
}
|
|
3749
3730
|
|
|
3731
|
+
const STRUCTURE_ID_SEPARATOR = ":";
|
|
3750
3732
|
class DynamicGltfLoader {
|
|
3751
3733
|
constructor(camera, scene, renderer) {
|
|
3752
3734
|
this.camera = camera;
|
|
@@ -3759,6 +3741,7 @@ class DynamicGltfLoader {
|
|
|
3759
3741
|
geometryerror: [],
|
|
3760
3742
|
update: [],
|
|
3761
3743
|
geometrymemory: [],
|
|
3744
|
+
optimizationprogress: [],
|
|
3762
3745
|
};
|
|
3763
3746
|
this.loadDistance = 100;
|
|
3764
3747
|
this.unloadDistance = 150;
|
|
@@ -3800,7 +3783,6 @@ class DynamicGltfLoader {
|
|
|
3800
3783
|
this.hiddenHandles = new Set();
|
|
3801
3784
|
this.newOptimizedObjects = new Set();
|
|
3802
3785
|
this.oldOptimizeObjects = new Set();
|
|
3803
|
-
this.maxConcurrentChunks = 8;
|
|
3804
3786
|
this.activeChunkLoads = 0;
|
|
3805
3787
|
this.chunkQueue = [];
|
|
3806
3788
|
this.objectIdToIndex = new Map();
|
|
@@ -3809,6 +3791,7 @@ class DynamicGltfLoader {
|
|
|
3809
3791
|
this.maxConcurrentChunks = 6;
|
|
3810
3792
|
this.mergedObjectMap = new Map();
|
|
3811
3793
|
this.mergedGeometryVisibility = new Map();
|
|
3794
|
+
this._webglInfoCache = null;
|
|
3812
3795
|
}
|
|
3813
3796
|
setVisibleEdges(visible) {
|
|
3814
3797
|
this.visibleEdges = visible;
|
|
@@ -3912,6 +3895,123 @@ class DynamicGltfLoader {
|
|
|
3912
3895
|
this.updateMemoryIndicator();
|
|
3913
3896
|
console.log(`Final memory usage: ${Math.round(currentMemoryUsage / (1024 * 1024))}MB`);
|
|
3914
3897
|
}
|
|
3898
|
+
getStats() {
|
|
3899
|
+
let totalObjects = 0;
|
|
3900
|
+
let renderedObjects = 0;
|
|
3901
|
+
let totalTriangles = 0;
|
|
3902
|
+
let renderedTriangles = 0;
|
|
3903
|
+
let totalLines = 0;
|
|
3904
|
+
let renderedLines = 0;
|
|
3905
|
+
let totalEdges = 0;
|
|
3906
|
+
let renderedEdges = 0;
|
|
3907
|
+
this.scene.traverse((object) => {
|
|
3908
|
+
totalObjects++;
|
|
3909
|
+
const geometry = object.geometry;
|
|
3910
|
+
if (!geometry) return;
|
|
3911
|
+
let triCount = 0;
|
|
3912
|
+
if (geometry.index) {
|
|
3913
|
+
triCount = Math.floor(geometry.index.count / 3);
|
|
3914
|
+
} else if (geometry.attributes && geometry.attributes.position) {
|
|
3915
|
+
triCount = Math.floor(geometry.attributes.position.count / 3);
|
|
3916
|
+
}
|
|
3917
|
+
totalTriangles += triCount;
|
|
3918
|
+
let lineCount = 0;
|
|
3919
|
+
if (geometry.index) {
|
|
3920
|
+
lineCount = Math.floor(geometry.index.count / 2);
|
|
3921
|
+
} else if (geometry.attributes && geometry.attributes.position) {
|
|
3922
|
+
lineCount = Math.floor(geometry.attributes.position.count / 2);
|
|
3923
|
+
}
|
|
3924
|
+
if (object.type === "Line" || object.type === "LineSegments" || object.type === "LineLoop") {
|
|
3925
|
+
if (object.userData.isEdge) {
|
|
3926
|
+
totalEdges += lineCount;
|
|
3927
|
+
} else {
|
|
3928
|
+
totalLines += lineCount;
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
if (object.visible !== false) {
|
|
3932
|
+
if (object.isMesh || object.isLine || object.isPoints) {
|
|
3933
|
+
renderedObjects++;
|
|
3934
|
+
if (object.isMesh) {
|
|
3935
|
+
renderedTriangles += triCount;
|
|
3936
|
+
} else if (object.type === "Line" || object.type === "LineSegments" || object.type === "LineLoop") {
|
|
3937
|
+
if (object.userData.isEdge) {
|
|
3938
|
+
renderedEdges += lineCount;
|
|
3939
|
+
} else {
|
|
3940
|
+
renderedLines += lineCount;
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
});
|
|
3946
|
+
const geometryCount = this.geometryCache ? this.geometryCache.size : 0;
|
|
3947
|
+
const geometryMemoryBytes = Array.from(this.geometryCache?.values?.() || []).reduce((a, b) => a + b, 0);
|
|
3948
|
+
const uniqueMaterialIds = new Set();
|
|
3949
|
+
const uniqueTextureIds = new Set();
|
|
3950
|
+
if (Array.isArray(this.structures)) {
|
|
3951
|
+
for (const structure of this.structures) {
|
|
3952
|
+
console.log(structure.materialCache.values());
|
|
3953
|
+
try {
|
|
3954
|
+
for (const entry of structure.materialCache.values()) {
|
|
3955
|
+
if (entry?.mesh?.uuid) uniqueMaterialIds.add(entry.mesh.uuid);
|
|
3956
|
+
if (entry?.points?.uuid) uniqueMaterialIds.add(entry.points.uuid);
|
|
3957
|
+
if (entry?.lines?.uuid) uniqueMaterialIds.add(entry.lines.uuid);
|
|
3958
|
+
}
|
|
3959
|
+
} catch (exp) {
|
|
3960
|
+
console.error("Error adding material to uniqueMaterialIds", exp);
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
const materialCount = uniqueMaterialIds.size;
|
|
3965
|
+
const textureCount = uniqueTextureIds.size;
|
|
3966
|
+
const estimatedGpuMemoryBytes = geometryMemoryBytes;
|
|
3967
|
+
if (!this._webglInfoCache) {
|
|
3968
|
+
try {
|
|
3969
|
+
const gl = this.renderer.getContext();
|
|
3970
|
+
const dbgInfo = gl.getExtension("WEBGL_debug_renderer_info");
|
|
3971
|
+
if (dbgInfo) {
|
|
3972
|
+
const rendererStr = gl.getParameter(dbgInfo.UNMASKED_RENDERER_WEBGL);
|
|
3973
|
+
const vendorStr = gl.getParameter(dbgInfo.UNMASKED_VENDOR_WEBGL);
|
|
3974
|
+
this._webglInfoCache = { renderer: rendererStr, vendor: vendorStr };
|
|
3975
|
+
} else {
|
|
3976
|
+
this._webglInfoCache = { renderer: null, vendor: null };
|
|
3977
|
+
}
|
|
3978
|
+
} catch (e) {
|
|
3979
|
+
console.error("Error getting webgl info", e);
|
|
3980
|
+
this._webglInfoCache = { renderer: null, vendor: null };
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
const size = new Vector2();
|
|
3984
|
+
if (this.renderer && this.renderer.getSize) {
|
|
3985
|
+
this.renderer.getSize(size);
|
|
3986
|
+
}
|
|
3987
|
+
return {
|
|
3988
|
+
scene: {
|
|
3989
|
+
beforeOptimization: {
|
|
3990
|
+
objects: totalObjects - renderedObjects,
|
|
3991
|
+
triangles: totalTriangles - renderedTriangles,
|
|
3992
|
+
lines: totalLines - renderedLines,
|
|
3993
|
+
edges: totalEdges - renderedEdges,
|
|
3994
|
+
},
|
|
3995
|
+
afterOptimization: {
|
|
3996
|
+
objects: renderedObjects,
|
|
3997
|
+
triangles: renderedTriangles,
|
|
3998
|
+
lines: renderedLines,
|
|
3999
|
+
edges: renderedEdges,
|
|
4000
|
+
},
|
|
4001
|
+
},
|
|
4002
|
+
memory: {
|
|
4003
|
+
geometries: { count: geometryCount, bytes: geometryMemoryBytes },
|
|
4004
|
+
textures: { count: textureCount },
|
|
4005
|
+
materials: { count: materialCount },
|
|
4006
|
+
totalEstimatedGpuBytes: estimatedGpuMemoryBytes,
|
|
4007
|
+
},
|
|
4008
|
+
system: {
|
|
4009
|
+
webglRenderer: this._webglInfoCache?.renderer || "",
|
|
4010
|
+
webglVendor: this._webglInfoCache?.vendor || "",
|
|
4011
|
+
viewport: { width: size.x || 0, height: size.y || 0 },
|
|
4012
|
+
},
|
|
4013
|
+
};
|
|
4014
|
+
}
|
|
3915
4015
|
async loadNode(nodeId, onLoadFinishCb) {
|
|
3916
4016
|
const node = this.nodes.get(nodeId);
|
|
3917
4017
|
if (!node || node.loaded || node.loading) return;
|
|
@@ -4082,7 +4182,7 @@ class DynamicGltfLoader {
|
|
|
4082
4182
|
if (node.handle) {
|
|
4083
4183
|
mesh.userData.handle = node.handle;
|
|
4084
4184
|
} else {
|
|
4085
|
-
mesh.userData.handle = `${node.structure.id}
|
|
4185
|
+
mesh.userData.handle = `${node.structure.id}${STRUCTURE_ID_SEPARATOR}${mesh.userData.handle}`;
|
|
4086
4186
|
}
|
|
4087
4187
|
if (mesh.material.name === "edges") {
|
|
4088
4188
|
mesh.userData.isEdge = true;
|
|
@@ -4228,7 +4328,7 @@ class DynamicGltfLoader {
|
|
|
4228
4328
|
let nodeGroup = null;
|
|
4229
4329
|
let handle = null;
|
|
4230
4330
|
if (nodeDef.extras?.handle) {
|
|
4231
|
-
handle = `${structure.id}
|
|
4331
|
+
handle = `${structure.id}${STRUCTURE_ID_SEPARATOR}${nodeDef.extras.handle}`;
|
|
4232
4332
|
}
|
|
4233
4333
|
if (nodeDef.camera !== undefined) {
|
|
4234
4334
|
const camera = this.loadCamera(structure, nodeDef.camera, nodeDef);
|
|
@@ -4245,7 +4345,7 @@ class DynamicGltfLoader {
|
|
|
4245
4345
|
if (nodeDef.extras) {
|
|
4246
4346
|
nodeGroup.userData = { ...nodeDef.extras };
|
|
4247
4347
|
if (nodeGroup.userData.handle) {
|
|
4248
|
-
nodeGroup.userData.handle = `${structure.id}
|
|
4348
|
+
nodeGroup.userData.handle = `${structure.id}${STRUCTURE_ID_SEPARATOR}${nodeGroup.userData.handle}`;
|
|
4249
4349
|
}
|
|
4250
4350
|
}
|
|
4251
4351
|
if (nodeDef.matrix) {
|
|
@@ -4290,7 +4390,7 @@ class DynamicGltfLoader {
|
|
|
4290
4390
|
this.edgeNodes.push(uniqueNodeId);
|
|
4291
4391
|
}
|
|
4292
4392
|
if (meshDef.extras && meshDef.extras.handle) {
|
|
4293
|
-
handle = `${structure.id}
|
|
4393
|
+
handle = `${structure.id}${STRUCTURE_ID_SEPARATOR}${meshDef.extras.handle}`;
|
|
4294
4394
|
}
|
|
4295
4395
|
this.nodes.set(uniqueNodeId, {
|
|
4296
4396
|
position: nodeGroup ? nodeGroup.position.clone() : new Vector3().setFromMatrixPosition(nodeMatrix),
|
|
@@ -4376,12 +4476,12 @@ class DynamicGltfLoader {
|
|
|
4376
4476
|
}
|
|
4377
4477
|
}
|
|
4378
4478
|
async loadNodes() {
|
|
4379
|
-
console.time("
|
|
4479
|
+
console.time("Process nodes");
|
|
4380
4480
|
await this.processNodes();
|
|
4381
|
-
console.timeEnd("
|
|
4382
|
-
console.time("
|
|
4481
|
+
console.timeEnd("Process nodes");
|
|
4482
|
+
console.time("Optimize scene");
|
|
4383
4483
|
await this.optimizeScene();
|
|
4384
|
-
console.timeEnd("
|
|
4484
|
+
console.timeEnd("Optimize scene");
|
|
4385
4485
|
}
|
|
4386
4486
|
cleanupPartialLoad() {
|
|
4387
4487
|
this.nodesToLoad.forEach((nodeId) => {
|
|
@@ -4702,7 +4802,7 @@ class DynamicGltfLoader {
|
|
|
4702
4802
|
this.handleToObjects.set(fullHandle, new Set());
|
|
4703
4803
|
}
|
|
4704
4804
|
this.handleToObjects.get(fullHandle).add(object);
|
|
4705
|
-
object.userData.structureId = object.userData.handle.split(
|
|
4805
|
+
object.userData.structureId = object.userData.handle.split(STRUCTURE_ID_SEPARATOR)[0];
|
|
4706
4806
|
}
|
|
4707
4807
|
getObjectsByHandle(handle) {
|
|
4708
4808
|
if (!handle) return [];
|
|
@@ -4768,10 +4868,28 @@ class DynamicGltfLoader {
|
|
|
4768
4868
|
}
|
|
4769
4869
|
this.originalObjects.add(object);
|
|
4770
4870
|
}
|
|
4771
|
-
|
|
4871
|
+
yieldToUI() {
|
|
4872
|
+
return new Promise((resolve) => {
|
|
4873
|
+
requestAnimationFrame(() => {
|
|
4874
|
+
setTimeout(resolve, 0);
|
|
4875
|
+
});
|
|
4876
|
+
});
|
|
4877
|
+
}
|
|
4878
|
+
async optimizeScene() {
|
|
4879
|
+
console.log("Starting scene optimization...");
|
|
4880
|
+
this.dispatchEvent("optimizationprogress", {
|
|
4881
|
+
phase: "start",
|
|
4882
|
+
progress: 0,
|
|
4883
|
+
message: "Starting optimization...",
|
|
4884
|
+
});
|
|
4772
4885
|
this.originalObjects.clear();
|
|
4773
4886
|
this.originalObjectsToSelection.clear();
|
|
4774
4887
|
const structureGroups = new Map();
|
|
4888
|
+
this.dispatchEvent("optimizationprogress", {
|
|
4889
|
+
phase: "collecting",
|
|
4890
|
+
progress: 5,
|
|
4891
|
+
message: "Collecting scene objects...",
|
|
4892
|
+
});
|
|
4775
4893
|
this.scene.traverse((object) => {
|
|
4776
4894
|
if (object.userData.structureId) {
|
|
4777
4895
|
const structureId = object.userData.structureId;
|
|
@@ -4800,16 +4918,44 @@ class DynamicGltfLoader {
|
|
|
4800
4918
|
}
|
|
4801
4919
|
}
|
|
4802
4920
|
});
|
|
4921
|
+
let processedGroups = 0;
|
|
4922
|
+
const totalGroups = structureGroups.size;
|
|
4923
|
+
this.dispatchEvent("optimizationprogress", {
|
|
4924
|
+
phase: "merging",
|
|
4925
|
+
progress: 10,
|
|
4926
|
+
message: `Merging ${totalGroups} structure groups...`,
|
|
4927
|
+
current: 0,
|
|
4928
|
+
total: totalGroups,
|
|
4929
|
+
});
|
|
4803
4930
|
for (const group of structureGroups.values()) {
|
|
4804
4931
|
group.mapMeshes.clear();
|
|
4805
4932
|
group.mapLines.clear();
|
|
4806
4933
|
group.mapLineSegments.clear();
|
|
4807
4934
|
group.mapPoints.clear();
|
|
4808
|
-
this.mergeMeshGroups(group.meshes, group.rootGroup);
|
|
4809
|
-
this.
|
|
4810
|
-
this.
|
|
4811
|
-
this.
|
|
4935
|
+
await this.mergeMeshGroups(group.meshes, group.rootGroup);
|
|
4936
|
+
await this.yieldToUI();
|
|
4937
|
+
await this.mergeLineGroups(group.lines, group.rootGroup);
|
|
4938
|
+
await this.yieldToUI();
|
|
4939
|
+
await this.mergeLineSegmentGroups(group.lineSegments, group.rootGroup);
|
|
4940
|
+
await this.yieldToUI();
|
|
4941
|
+
await this.mergePointsGroups(group.points, group.rootGroup);
|
|
4942
|
+
processedGroups++;
|
|
4943
|
+
const progress = 10 + Math.round((processedGroups / totalGroups) * 80);
|
|
4944
|
+
this.dispatchEvent("optimizationprogress", {
|
|
4945
|
+
phase: "merging",
|
|
4946
|
+
progress,
|
|
4947
|
+
message: `Processing structure ${processedGroups}/${totalGroups}...`,
|
|
4948
|
+
current: processedGroups,
|
|
4949
|
+
total: totalGroups,
|
|
4950
|
+
});
|
|
4951
|
+
console.log(`Optimization progress: ${processedGroups}/${totalGroups} structure groups processed (${progress}%)`);
|
|
4952
|
+
await this.yieldToUI();
|
|
4812
4953
|
}
|
|
4954
|
+
this.dispatchEvent("optimizationprogress", {
|
|
4955
|
+
phase: "finalizing",
|
|
4956
|
+
progress: 95,
|
|
4957
|
+
message: "Finalizing optimization...",
|
|
4958
|
+
});
|
|
4813
4959
|
this.originalObjects.forEach((obj) => {
|
|
4814
4960
|
obj.visible = false;
|
|
4815
4961
|
if (!(obj instanceof Points) && !obj.userData.isEdge) {
|
|
@@ -4818,9 +4964,15 @@ class DynamicGltfLoader {
|
|
|
4818
4964
|
});
|
|
4819
4965
|
this.initializeObjectVisibility();
|
|
4820
4966
|
console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
|
|
4967
|
+
this.dispatchEvent("optimizationprogress", {
|
|
4968
|
+
phase: "complete",
|
|
4969
|
+
progress: 100,
|
|
4970
|
+
message: `Optimization complete! ${this.maxObjectId} objects processed.`,
|
|
4971
|
+
});
|
|
4821
4972
|
this.dispatchEvent("update");
|
|
4822
4973
|
}
|
|
4823
|
-
mergeMeshGroups(materialGroups, rootGroup) {
|
|
4974
|
+
async mergeMeshGroups(materialGroups, rootGroup) {
|
|
4975
|
+
let processedGroups = 0;
|
|
4824
4976
|
for (const group of materialGroups) {
|
|
4825
4977
|
if (!group.material) {
|
|
4826
4978
|
console.warn("Skipping mesh group with null material");
|
|
@@ -4894,6 +5046,10 @@ class DynamicGltfLoader {
|
|
|
4894
5046
|
this.handleToOptimizedObjects.set(handle, mergedObjects);
|
|
4895
5047
|
}
|
|
4896
5048
|
});
|
|
5049
|
+
processedGroups++;
|
|
5050
|
+
if (processedGroups % 5 === 0) {
|
|
5051
|
+
await this.yieldToUI();
|
|
5052
|
+
}
|
|
4897
5053
|
} catch (error) {
|
|
4898
5054
|
console.error("Failed to merge meshes for material:", error);
|
|
4899
5055
|
group.objects.forEach((mesh) => {
|
|
@@ -4902,7 +5058,8 @@ class DynamicGltfLoader {
|
|
|
4902
5058
|
}
|
|
4903
5059
|
}
|
|
4904
5060
|
}
|
|
4905
|
-
mergeLineGroups(materialGroups, rootGroup) {
|
|
5061
|
+
async mergeLineGroups(materialGroups, rootGroup) {
|
|
5062
|
+
let processedGroups = 0;
|
|
4906
5063
|
for (const group of materialGroups) {
|
|
4907
5064
|
if (group.objects.length === 0) continue;
|
|
4908
5065
|
if (!group.material) {
|
|
@@ -4921,7 +5078,9 @@ class DynamicGltfLoader {
|
|
|
4921
5078
|
let posOffset = 0;
|
|
4922
5079
|
const indices = [];
|
|
4923
5080
|
let vertexOffset = 0;
|
|
5081
|
+
let isEdge = false;
|
|
4924
5082
|
group.objects.forEach((line) => {
|
|
5083
|
+
isEdge = line.userData.isEdge;
|
|
4925
5084
|
const geometry = line.geometry;
|
|
4926
5085
|
const positionAttr = geometry.attributes.position;
|
|
4927
5086
|
const vertexCount = positionAttr.count;
|
|
@@ -4972,6 +5131,7 @@ class DynamicGltfLoader {
|
|
|
4972
5131
|
geometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
4973
5132
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
4974
5133
|
const mergedLine = new LineSegments(geometry, visibilityMaterial);
|
|
5134
|
+
mergedLine.userData.isEdge = isEdge;
|
|
4975
5135
|
const mergedObjects = [mergedLine];
|
|
4976
5136
|
if (this.useVAO) {
|
|
4977
5137
|
this.createVAO(mergedLine);
|
|
@@ -4994,9 +5154,14 @@ class DynamicGltfLoader {
|
|
|
4994
5154
|
this.handleToOptimizedObjects.set(handle, mergedObjects);
|
|
4995
5155
|
}
|
|
4996
5156
|
});
|
|
5157
|
+
processedGroups++;
|
|
5158
|
+
if (processedGroups % 5 === 0) {
|
|
5159
|
+
await this.yieldToUI();
|
|
5160
|
+
}
|
|
4997
5161
|
}
|
|
4998
5162
|
}
|
|
4999
|
-
mergeLineSegmentGroups(materialGroups, rootGroup) {
|
|
5163
|
+
async mergeLineSegmentGroups(materialGroups, rootGroup) {
|
|
5164
|
+
let processedGroups = 0;
|
|
5000
5165
|
for (const group of materialGroups) {
|
|
5001
5166
|
if (!group.material) {
|
|
5002
5167
|
console.warn("Skipping line segment group with null material");
|
|
@@ -5008,7 +5173,9 @@ class DynamicGltfLoader {
|
|
|
5008
5173
|
const handles = new Set();
|
|
5009
5174
|
const objectMapping = new Map();
|
|
5010
5175
|
let currentVertexOffset = 0;
|
|
5176
|
+
let isEdge = false;
|
|
5011
5177
|
for (const line of group.objects) {
|
|
5178
|
+
isEdge = line.userData.isEdge;
|
|
5012
5179
|
const geometry = line.geometry.clone();
|
|
5013
5180
|
line.updateWorldMatrix(true, false);
|
|
5014
5181
|
geometry.applyMatrix4(line.matrixWorld);
|
|
@@ -5044,6 +5211,7 @@ class DynamicGltfLoader {
|
|
|
5044
5211
|
mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
5045
5212
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
5046
5213
|
const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
|
|
5214
|
+
mergedLine.userData.isEdge = isEdge;
|
|
5047
5215
|
if (this.useVAO) {
|
|
5048
5216
|
this.createVAO(mergedLine);
|
|
5049
5217
|
}
|
|
@@ -5070,6 +5238,10 @@ class DynamicGltfLoader {
|
|
|
5070
5238
|
this.handleToOptimizedObjects.set(handle, mergedObjects);
|
|
5071
5239
|
}
|
|
5072
5240
|
});
|
|
5241
|
+
processedGroups++;
|
|
5242
|
+
if (processedGroups % 5 === 0) {
|
|
5243
|
+
await this.yieldToUI();
|
|
5244
|
+
}
|
|
5073
5245
|
} catch (error) {
|
|
5074
5246
|
console.warn("Failed to merge line segments for material:", error);
|
|
5075
5247
|
group.objects.forEach((line) => {
|
|
@@ -5078,7 +5250,8 @@ class DynamicGltfLoader {
|
|
|
5078
5250
|
}
|
|
5079
5251
|
}
|
|
5080
5252
|
}
|
|
5081
|
-
mergePointsGroups(materialGroups, rootGroup) {
|
|
5253
|
+
async mergePointsGroups(materialGroups, rootGroup) {
|
|
5254
|
+
let processedGroups = 0;
|
|
5082
5255
|
for (const group of materialGroups) {
|
|
5083
5256
|
if (!group.material) {
|
|
5084
5257
|
console.warn("Skipping points group with null material");
|
|
@@ -5120,6 +5293,10 @@ class DynamicGltfLoader {
|
|
|
5120
5293
|
this.handleToOptimizedObjects.set(handle, mergedObjects);
|
|
5121
5294
|
}
|
|
5122
5295
|
});
|
|
5296
|
+
processedGroups++;
|
|
5297
|
+
if (processedGroups % 5 === 0) {
|
|
5298
|
+
await this.yieldToUI();
|
|
5299
|
+
}
|
|
5123
5300
|
} catch (error) {
|
|
5124
5301
|
console.warn("Failed to merge points for material:", error);
|
|
5125
5302
|
group.objects.forEach((points) => {
|
|
@@ -5355,8 +5532,225 @@ class DynamicGltfLoader {
|
|
|
5355
5532
|
}
|
|
5356
5533
|
}
|
|
5357
5534
|
|
|
5358
|
-
class
|
|
5535
|
+
class GLTFLoadingManager extends LoadingManager {
|
|
5536
|
+
constructor(file, params = {}) {
|
|
5537
|
+
super();
|
|
5538
|
+
this.path = "";
|
|
5539
|
+
this.resourcePath = "";
|
|
5540
|
+
this.fileURL = "";
|
|
5541
|
+
this.dataURLs = new Map();
|
|
5542
|
+
this.path = params.path || "";
|
|
5543
|
+
const externalFiles = params.externalFiles || new Map();
|
|
5544
|
+
if (typeof file === "string") {
|
|
5545
|
+
this.fileURL = file;
|
|
5546
|
+
this.resourcePath = LoaderUtils.extractUrlBase(file);
|
|
5547
|
+
}
|
|
5548
|
+
else {
|
|
5549
|
+
externalFiles.forEach((value, key) => (this.fileURL = value === file ? key : this.fileURL));
|
|
5550
|
+
externalFiles.set(this.fileURL, file);
|
|
5551
|
+
}
|
|
5552
|
+
externalFiles.forEach((value, key) => {
|
|
5553
|
+
let dataURL;
|
|
5554
|
+
if (typeof value === "string")
|
|
5555
|
+
dataURL = value;
|
|
5556
|
+
else
|
|
5557
|
+
dataURL = URL.createObjectURL(new Blob([value]));
|
|
5558
|
+
this.dataURLs.set(key, dataURL);
|
|
5559
|
+
});
|
|
5560
|
+
this.setURLModifier((url) => {
|
|
5561
|
+
const key = decodeURI(url)
|
|
5562
|
+
.replace(this.path, "")
|
|
5563
|
+
.replace(this.resourcePath, "")
|
|
5564
|
+
.replace(/^(\.?\/)/, "");
|
|
5565
|
+
const dataURL = this.dataURLs.get(key);
|
|
5566
|
+
return dataURL !== null && dataURL !== void 0 ? dataURL : url;
|
|
5567
|
+
});
|
|
5568
|
+
}
|
|
5569
|
+
dispose() {
|
|
5570
|
+
this.dataURLs.forEach((dataURL) => URL.revokeObjectURL(dataURL));
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
|
|
5574
|
+
const BINARY_EXTENSION_HEADER_MAGIC = "glTF";
|
|
5575
|
+
const BINARY_EXTENSION_HEADER_LENGTH = 12;
|
|
5576
|
+
const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4e4f534a, BIN: 0x004e4042 };
|
|
5577
|
+
class GLTFBinaryExtension {
|
|
5578
|
+
constructor(data) {
|
|
5579
|
+
const headerView = new DataView(data, 0, BINARY_EXTENSION_HEADER_LENGTH);
|
|
5580
|
+
const textDecoder = new TextDecoder();
|
|
5581
|
+
const magic = textDecoder.decode(new Uint8Array(data.slice(0, 4)));
|
|
5582
|
+
if (magic !== BINARY_EXTENSION_HEADER_MAGIC) {
|
|
5583
|
+
this.content = textDecoder.decode(data);
|
|
5584
|
+
return;
|
|
5585
|
+
}
|
|
5586
|
+
const header = {
|
|
5587
|
+
magic,
|
|
5588
|
+
version: headerView.getUint32(4, true),
|
|
5589
|
+
length: headerView.getUint32(8, true),
|
|
5590
|
+
};
|
|
5591
|
+
if (header.magic !== BINARY_EXTENSION_HEADER_MAGIC) {
|
|
5592
|
+
throw new Error("Unsupported glTF-Binary header.");
|
|
5593
|
+
}
|
|
5594
|
+
if (header.version < 2.0) {
|
|
5595
|
+
throw new Error("Legacy binary file detected.");
|
|
5596
|
+
}
|
|
5597
|
+
const chunkContentsLength = header.length - BINARY_EXTENSION_HEADER_LENGTH;
|
|
5598
|
+
const chunkView = new DataView(data, BINARY_EXTENSION_HEADER_LENGTH);
|
|
5599
|
+
let chunkIndex = 0;
|
|
5600
|
+
while (chunkIndex < chunkContentsLength) {
|
|
5601
|
+
const chunkLength = chunkView.getUint32(chunkIndex, true);
|
|
5602
|
+
chunkIndex += 4;
|
|
5603
|
+
const chunkType = chunkView.getUint32(chunkIndex, true);
|
|
5604
|
+
chunkIndex += 4;
|
|
5605
|
+
if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON) {
|
|
5606
|
+
const contentArray = new Uint8Array(data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength);
|
|
5607
|
+
this.content = textDecoder.decode(contentArray);
|
|
5608
|
+
}
|
|
5609
|
+
else if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN) {
|
|
5610
|
+
const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex;
|
|
5611
|
+
this.body = data.slice(byteOffset, byteOffset + chunkLength);
|
|
5612
|
+
}
|
|
5613
|
+
chunkIndex += chunkLength;
|
|
5614
|
+
}
|
|
5615
|
+
if (typeof this.content === "undefined") {
|
|
5616
|
+
throw new Error("JSON content not found.");
|
|
5617
|
+
}
|
|
5618
|
+
}
|
|
5619
|
+
}
|
|
5620
|
+
|
|
5621
|
+
class RangesLoader {
|
|
5622
|
+
constructor() {
|
|
5623
|
+
this.requestHeader = {};
|
|
5624
|
+
this.withCredentials = false;
|
|
5625
|
+
this.abortSignal = undefined;
|
|
5626
|
+
}
|
|
5627
|
+
setRequestHeader(requestHeader) {
|
|
5628
|
+
this.requestHeader = requestHeader;
|
|
5629
|
+
}
|
|
5630
|
+
setWithCredentials(withCredentials) {
|
|
5631
|
+
this.withCredentials = withCredentials;
|
|
5632
|
+
}
|
|
5633
|
+
setAbortSignal(abortSignal) {
|
|
5634
|
+
this.abortSignal = abortSignal;
|
|
5635
|
+
}
|
|
5636
|
+
async load(url, ranges) {
|
|
5637
|
+
const init = {
|
|
5638
|
+
headers: {
|
|
5639
|
+
...this.requestHeader,
|
|
5640
|
+
Range: "bytes=" + ranges.map((x) => `${x.offset}-${x.offset + x.length - 1}`).join(","),
|
|
5641
|
+
},
|
|
5642
|
+
credentials: this.withCredentials ? "include" : "same-origin",
|
|
5643
|
+
signal: this.abortSignal,
|
|
5644
|
+
};
|
|
5645
|
+
const response = await fetch(url, init);
|
|
5646
|
+
if (!response.ok) {
|
|
5647
|
+
throw new Error(`Failed to fetch "${url}", status ${response.status}`);
|
|
5648
|
+
}
|
|
5649
|
+
if (response.status !== 206) {
|
|
5650
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
5651
|
+
return this.extractRanges(arrayBuffer, ranges);
|
|
5652
|
+
}
|
|
5653
|
+
return response.arrayBuffer();
|
|
5654
|
+
}
|
|
5655
|
+
extractRanges(arrayBuffer, ranges) {
|
|
5656
|
+
const totalLength = ranges.reduce((sum, range) => sum + range.length, 0);
|
|
5657
|
+
const result = new Uint8Array(totalLength);
|
|
5658
|
+
let offset = 0;
|
|
5659
|
+
for (const range of ranges) {
|
|
5660
|
+
const chunk = new Uint8Array(arrayBuffer, range.offset, range.length);
|
|
5661
|
+
result.set(chunk, offset);
|
|
5662
|
+
offset += range.length;
|
|
5663
|
+
}
|
|
5664
|
+
return result.buffer;
|
|
5665
|
+
}
|
|
5666
|
+
}
|
|
5667
|
+
|
|
5668
|
+
class GLTFFileDynamicLoader extends Loader {
|
|
5359
5669
|
constructor(viewer) {
|
|
5670
|
+
super();
|
|
5671
|
+
this.viewer = viewer;
|
|
5672
|
+
}
|
|
5673
|
+
dispose() {
|
|
5674
|
+
if (this.gltfLoader)
|
|
5675
|
+
this.gltfLoader.clear();
|
|
5676
|
+
if (this.manager)
|
|
5677
|
+
this.manager.dispose();
|
|
5678
|
+
}
|
|
5679
|
+
isSupport(file, format) {
|
|
5680
|
+
return ((typeof file === "string" || file instanceof globalThis.File || file instanceof ArrayBuffer) &&
|
|
5681
|
+
/(gltf|glb)$/i.test(format));
|
|
5682
|
+
}
|
|
5683
|
+
async load(file, format, params) {
|
|
5684
|
+
this.manager = new GLTFLoadingManager(file, params);
|
|
5685
|
+
const scene = new Group();
|
|
5686
|
+
this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, scene, this.viewer.renderer);
|
|
5687
|
+
this.gltfLoader.memoryLimit = this.viewer.options.memoryLimit;
|
|
5688
|
+
this.gltfLoader.visibleEdges = this.viewer.options.edgeModel;
|
|
5689
|
+
const modelImpl = new DynamicModelImpl(scene);
|
|
5690
|
+
modelImpl.id = params.modelId || this.extractFileName(file);
|
|
5691
|
+
modelImpl.gltfLoader = this.gltfLoader;
|
|
5692
|
+
this.gltfLoader.addEventListener("databasechunk", () => {
|
|
5693
|
+
this.viewer.scene.add(scene);
|
|
5694
|
+
this.viewer.models.push(modelImpl);
|
|
5695
|
+
this.viewer.syncOptions();
|
|
5696
|
+
this.viewer.syncOverlay();
|
|
5697
|
+
this.viewer.update();
|
|
5698
|
+
this.viewer.emitEvent({ type: "databasechunk", data: scene, file });
|
|
5699
|
+
});
|
|
5700
|
+
this.gltfLoader.addEventListener("geometryerror", (data) => {
|
|
5701
|
+
this.viewer.emitEvent({ type: "geometryerror", data, file });
|
|
5702
|
+
});
|
|
5703
|
+
this.gltfLoader.addEventListener("update", (data) => {
|
|
5704
|
+
this.viewer.update();
|
|
5705
|
+
});
|
|
5706
|
+
const loadController = {
|
|
5707
|
+
loadJson: async () => {
|
|
5708
|
+
const loader = new FileLoader(this.manager);
|
|
5709
|
+
loader.setPath(this.manager.path);
|
|
5710
|
+
loader.setRequestHeader(params.requestHeader || {});
|
|
5711
|
+
loader.setWithCredentials(params.withCredentials || loader.withCredentials);
|
|
5712
|
+
loader.setResponseType("arraybuffer");
|
|
5713
|
+
const progress = (event) => {
|
|
5714
|
+
const { lengthComputable, loaded, total } = event;
|
|
5715
|
+
const progress = lengthComputable ? loaded / total : 1;
|
|
5716
|
+
this.viewer.emitEvent({ type: "geometryprogress", data: progress, file });
|
|
5717
|
+
};
|
|
5718
|
+
const data = await loader.loadAsync(this.manager.fileURL, progress);
|
|
5719
|
+
const extension = new GLTFBinaryExtension(data);
|
|
5720
|
+
this.gltf = JSON.parse(extension.content);
|
|
5721
|
+
this.bin = extension.body;
|
|
5722
|
+
return this.gltf;
|
|
5723
|
+
},
|
|
5724
|
+
loadBinaryData: (ranges, uri = "") => {
|
|
5725
|
+
const loader = new RangesLoader();
|
|
5726
|
+
loader.setRequestHeader(params.requestHeader || {});
|
|
5727
|
+
loader.setWithCredentials(params.withCredentials || false);
|
|
5728
|
+
loader.setAbortSignal(this.gltfLoader.abortController.signal);
|
|
5729
|
+
if (this.bin)
|
|
5730
|
+
return loader.extractRanges(this.bin, ranges);
|
|
5731
|
+
const path = this.manager.path || this.manager.resourcePath;
|
|
5732
|
+
const url = LoaderUtils.resolveURL(uri, path);
|
|
5733
|
+
return loader.load(this.manager.resolveURL(url), ranges);
|
|
5734
|
+
},
|
|
5735
|
+
baseUrl: () => {
|
|
5736
|
+
const path = this.manager.path || this.manager.resourcePath;
|
|
5737
|
+
return Promise.resolve(path);
|
|
5738
|
+
},
|
|
5739
|
+
};
|
|
5740
|
+
const structure = new GltfStructure(modelImpl.id, loadController);
|
|
5741
|
+
await this.gltfLoader.loadStructure(structure);
|
|
5742
|
+
await this.gltfLoader.loadNodes();
|
|
5743
|
+
return this;
|
|
5744
|
+
}
|
|
5745
|
+
cancel() {
|
|
5746
|
+
if (this.gltfLoader)
|
|
5747
|
+
this.gltfLoader.abortLoading();
|
|
5748
|
+
}
|
|
5749
|
+
}
|
|
5750
|
+
|
|
5751
|
+
class GLTFCloudDynamicLoader extends Loader {
|
|
5752
|
+
constructor(viewer) {
|
|
5753
|
+
super();
|
|
5360
5754
|
this.requestId = 0;
|
|
5361
5755
|
this.viewer = viewer;
|
|
5362
5756
|
}
|
|
@@ -5371,17 +5765,15 @@ class GLTFCloudDynamicLoader {
|
|
|
5371
5765
|
typeof file.downloadResourceRange === "function" &&
|
|
5372
5766
|
/.gltf$/i.test(file.database));
|
|
5373
5767
|
}
|
|
5374
|
-
async load(model, format, params) {
|
|
5768
|
+
async load(model, format, params = {}) {
|
|
5375
5769
|
const scene = new Group();
|
|
5376
5770
|
this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, scene, this.viewer.renderer);
|
|
5377
5771
|
this.gltfLoader.memoryLimit = this.viewer.options.memoryLimit;
|
|
5378
5772
|
this.gltfLoader.setVisibleEdges(this.viewer.options.edgeModel);
|
|
5773
|
+
const modelImpl = new DynamicModelImpl(scene);
|
|
5774
|
+
modelImpl.id = model.file.id;
|
|
5775
|
+
modelImpl.gltfLoader = this.gltfLoader;
|
|
5379
5776
|
this.gltfLoader.addEventListener("databasechunk", (data) => {
|
|
5380
|
-
const modelImpl = new DynamicModelImpl(scene);
|
|
5381
|
-
modelImpl.loader = this;
|
|
5382
|
-
modelImpl.viewer = this.viewer;
|
|
5383
|
-
modelImpl.gltfLoader = this.gltfLoader;
|
|
5384
|
-
modelImpl.modelId = model.id;
|
|
5385
5777
|
this.viewer.scene.add(scene);
|
|
5386
5778
|
this.viewer.models.push(modelImpl);
|
|
5387
5779
|
this.viewer.syncOptions();
|
|
@@ -5389,10 +5781,6 @@ class GLTFCloudDynamicLoader {
|
|
|
5389
5781
|
this.viewer.update();
|
|
5390
5782
|
this.viewer.emitEvent({ type: "databasechunk", data: scene, file: model.file, model });
|
|
5391
5783
|
});
|
|
5392
|
-
this.gltfLoader.addEventListener("geometryprogress", (data) => {
|
|
5393
|
-
const progress = data.loaded / data.total;
|
|
5394
|
-
this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model.file, model });
|
|
5395
|
-
});
|
|
5396
5784
|
this.gltfLoader.addEventListener("geometryerror", (data) => {
|
|
5397
5785
|
this.viewer.emitEvent({ type: "geometryerror", data, file: model.file, model });
|
|
5398
5786
|
});
|
|
@@ -5402,7 +5790,7 @@ class GLTFCloudDynamicLoader {
|
|
|
5402
5790
|
const loadController = {
|
|
5403
5791
|
loadJson: async () => {
|
|
5404
5792
|
const progress = (progress) => {
|
|
5405
|
-
this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model });
|
|
5793
|
+
this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model.file, model });
|
|
5406
5794
|
};
|
|
5407
5795
|
const arrayBuffer = await model.downloadResource(model.database, progress, this.gltfLoader.getAbortController().signal);
|
|
5408
5796
|
const text = new TextDecoder().decode(arrayBuffer);
|
|
@@ -5419,7 +5807,7 @@ class GLTFCloudDynamicLoader {
|
|
|
5419
5807
|
},
|
|
5420
5808
|
baseUrl: () => Promise.resolve(`${model.httpClient.serverUrl}${model.path}/`),
|
|
5421
5809
|
};
|
|
5422
|
-
const structure = new GltfStructure(
|
|
5810
|
+
const structure = new GltfStructure(modelImpl.id, loadController);
|
|
5423
5811
|
await this.gltfLoader.loadStructure(structure);
|
|
5424
5812
|
await this.gltfLoader.loadNodes();
|
|
5425
5813
|
return this;
|
|
@@ -5431,7 +5819,7 @@ class GLTFCloudDynamicLoader {
|
|
|
5431
5819
|
}
|
|
5432
5820
|
|
|
5433
5821
|
const loaders = loadersRegistry("threejs");
|
|
5434
|
-
loaders.registerLoader("gltf-file", (viewer) => new
|
|
5822
|
+
loaders.registerLoader("gltf-file", (viewer) => new GLTFFileDynamicLoader(viewer));
|
|
5435
5823
|
loaders.registerLoader("gltf-cloud", (viewer) => new GLTFCloudDynamicLoader(viewer));
|
|
5436
5824
|
|
|
5437
5825
|
class SSAARenderPass extends Pass {
|
|
@@ -5638,24 +6026,25 @@ class Helpers extends Scene {
|
|
|
5638
6026
|
class Viewer extends EventEmitter2 {
|
|
5639
6027
|
constructor(client) {
|
|
5640
6028
|
super();
|
|
5641
|
-
this._options = new Options(this);
|
|
5642
6029
|
this.client = client;
|
|
5643
|
-
this.
|
|
5644
|
-
this.canvaseventlistener = (event) => this.emit(event);
|
|
6030
|
+
this.options = new Options(this);
|
|
5645
6031
|
this.loaders = [];
|
|
5646
6032
|
this.models = [];
|
|
6033
|
+
this.canvasEvents = CANVAS_EVENTS.slice();
|
|
6034
|
+
this.canvaseventlistener = (event) => this.emit(event);
|
|
5647
6035
|
this.selected = [];
|
|
5648
6036
|
this.extents = new Box3();
|
|
5649
|
-
this.target = new Vector3();
|
|
6037
|
+
this.target = new Vector3(0, 0, 0);
|
|
5650
6038
|
this._activeDragger = null;
|
|
5651
6039
|
this._components = [];
|
|
6040
|
+
this._renderNeeded = false;
|
|
5652
6041
|
this._renderTime = 0;
|
|
5653
6042
|
this.render = this.render.bind(this);
|
|
5654
6043
|
this.update = this.update.bind(this);
|
|
5655
6044
|
this._markup = new Markup();
|
|
5656
6045
|
}
|
|
5657
|
-
get
|
|
5658
|
-
return this.
|
|
6046
|
+
get markup() {
|
|
6047
|
+
return this._markup;
|
|
5659
6048
|
}
|
|
5660
6049
|
get draggers() {
|
|
5661
6050
|
return [...draggers.getDraggers().keys()];
|
|
@@ -5663,14 +6052,10 @@ class Viewer extends EventEmitter2 {
|
|
|
5663
6052
|
get components() {
|
|
5664
6053
|
return [...components.getComponents().keys()];
|
|
5665
6054
|
}
|
|
5666
|
-
get markup() {
|
|
5667
|
-
return this._markup;
|
|
5668
|
-
}
|
|
5669
6055
|
initialize(canvas, onProgress) {
|
|
5670
6056
|
this.addEventListener("optionschange", (event) => this.syncOptions(event.data));
|
|
5671
6057
|
this.scene = new Scene();
|
|
5672
6058
|
this.helpers = new Helpers();
|
|
5673
|
-
this.target = new Vector3(0, 0, 0);
|
|
5674
6059
|
const pixelRatio = window.devicePixelRatio;
|
|
5675
6060
|
const rect = canvas.parentElement.getBoundingClientRect();
|
|
5676
6061
|
const width = rect.width || 1;
|
|
@@ -5821,21 +6206,25 @@ class Viewer extends EventEmitter2 {
|
|
|
5821
6206
|
async open(file, params = {}) {
|
|
5822
6207
|
if (!this.renderer)
|
|
5823
6208
|
return this;
|
|
5824
|
-
|
|
6209
|
+
const mode = params.mode || "file";
|
|
6210
|
+
if (mode !== "assembly" && mode !== "a" && mode !== "append") {
|
|
5825
6211
|
this.cancel();
|
|
5826
6212
|
this.clear();
|
|
5827
6213
|
}
|
|
5828
|
-
this.emitEvent({ type: "open", file });
|
|
6214
|
+
this.emitEvent({ type: "open", mode, file });
|
|
5829
6215
|
let model = file;
|
|
5830
6216
|
if (model && typeof model.getModels === "function") {
|
|
5831
6217
|
const models = await model.getModels();
|
|
5832
6218
|
model = models.find((model) => model.default) || models[0] || file;
|
|
5833
6219
|
}
|
|
6220
|
+
if (model && typeof model.database === "string") {
|
|
6221
|
+
file = model.file;
|
|
6222
|
+
}
|
|
5834
6223
|
if (!model)
|
|
5835
6224
|
throw new Error(`Format not supported`);
|
|
5836
6225
|
let format = params.format;
|
|
5837
|
-
if (!format && typeof
|
|
5838
|
-
format =
|
|
6226
|
+
if (!format && typeof file["type"] === "string")
|
|
6227
|
+
format = file["type"].split(".").pop();
|
|
5839
6228
|
if (!format && typeof file === "string")
|
|
5840
6229
|
format = file.split(".").pop();
|
|
5841
6230
|
if (!format && file instanceof globalThis.File)
|
|
@@ -5862,7 +6251,7 @@ class Viewer extends EventEmitter2 {
|
|
|
5862
6251
|
}
|
|
5863
6252
|
loadGltfFile(file, externalFiles, params = {}) {
|
|
5864
6253
|
console.warn("Viewer.loadGltfFile() has been deprecated since 26.4 and will be removed in a future release, use Viewer.open() instead.");
|
|
5865
|
-
return this.open(file, { ...params, format: "gltf", externalFiles, mode: "
|
|
6254
|
+
return this.open(file, { ...params, format: "gltf", externalFiles, mode: "assembly" });
|
|
5866
6255
|
}
|
|
5867
6256
|
cancel() {
|
|
5868
6257
|
this.loaders.forEach((loader) => loader.cancel());
|
|
@@ -5882,12 +6271,17 @@ class Viewer extends EventEmitter2 {
|
|
|
5882
6271
|
this.models = [];
|
|
5883
6272
|
this.scene.clear();
|
|
5884
6273
|
this.helpers.clear();
|
|
6274
|
+
this.extents.makeEmpty();
|
|
6275
|
+
this.target.set(0, 0, 0);
|
|
5885
6276
|
this.syncOptions();
|
|
5886
6277
|
this.syncOverlay();
|
|
5887
6278
|
this.update(true);
|
|
5888
6279
|
this.emitEvent({ type: "clear" });
|
|
5889
6280
|
return this;
|
|
5890
6281
|
}
|
|
6282
|
+
is3D() {
|
|
6283
|
+
return true;
|
|
6284
|
+
}
|
|
5891
6285
|
syncOptions(options = this.options) {
|
|
5892
6286
|
if (!this.renderer)
|
|
5893
6287
|
return;
|
|
@@ -5919,9 +6313,15 @@ class Viewer extends EventEmitter2 {
|
|
|
5919
6313
|
getSelected() {
|
|
5920
6314
|
return this.executeCommand("getSelected");
|
|
5921
6315
|
}
|
|
6316
|
+
getSelected2() {
|
|
6317
|
+
return this.executeCommand("getSelected2");
|
|
6318
|
+
}
|
|
5922
6319
|
setSelected(handles) {
|
|
5923
6320
|
this.executeCommand("setSelected", handles);
|
|
5924
6321
|
}
|
|
6322
|
+
setSelected2(handles) {
|
|
6323
|
+
this.executeCommand("setSelected2", handles);
|
|
6324
|
+
}
|
|
5925
6325
|
clearSelected() {
|
|
5926
6326
|
this.executeCommand("clearSelected");
|
|
5927
6327
|
}
|
|
@@ -5981,37 +6381,8 @@ class Viewer extends EventEmitter2 {
|
|
|
5981
6381
|
getComponent(name) {
|
|
5982
6382
|
return this._components.find((component) => component.name === name);
|
|
5983
6383
|
}
|
|
5984
|
-
is3D() {
|
|
5985
|
-
return true;
|
|
5986
|
-
}
|
|
5987
|
-
screenToWorld(position) {
|
|
5988
|
-
if (!this.renderer)
|
|
5989
|
-
return { x: position.x, y: position.y, z: 0 };
|
|
5990
|
-
const rect = this.canvas.getBoundingClientRect();
|
|
5991
|
-
const x = position.x / (rect.width / 2) - 1;
|
|
5992
|
-
const y = -position.y / (rect.height / 2) + 1;
|
|
5993
|
-
const point = new Vector3(x, y, -1);
|
|
5994
|
-
point.unproject(this.camera);
|
|
5995
|
-
return { x: point.x, y: point.y, z: point.z };
|
|
5996
|
-
}
|
|
5997
|
-
worldToScreen(position) {
|
|
5998
|
-
if (!this.renderer)
|
|
5999
|
-
return { x: position.x, y: position.y };
|
|
6000
|
-
const point = new Vector3(position.x, position.y, position.z);
|
|
6001
|
-
point.project(this.camera);
|
|
6002
|
-
const rect = this.canvas.getBoundingClientRect();
|
|
6003
|
-
const x = (point.x + 1) * (rect.width / 2);
|
|
6004
|
-
const y = (-point.y + 1) * (rect.height / 2);
|
|
6005
|
-
return { x, y };
|
|
6006
|
-
}
|
|
6007
|
-
getScale() {
|
|
6008
|
-
return { x: 1, y: 1, z: 1 };
|
|
6009
|
-
}
|
|
6010
|
-
executeCommand(id, ...args) {
|
|
6011
|
-
return commands.executeCommand(id, this, ...args);
|
|
6012
|
-
}
|
|
6013
6384
|
drawViewpoint(viewpoint) {
|
|
6014
|
-
var _a, _b, _c;
|
|
6385
|
+
var _a, _b, _c, _d;
|
|
6015
6386
|
if (!this.renderer)
|
|
6016
6387
|
return;
|
|
6017
6388
|
const getVector3FromPoint3d = ({ x, y, z }) => new Vector3(x, y, z);
|
|
@@ -6063,11 +6434,13 @@ class Viewer extends EventEmitter2 {
|
|
|
6063
6434
|
}
|
|
6064
6435
|
};
|
|
6065
6436
|
const setClippingPlanes = (clipping_planes) => {
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6437
|
+
if (clipping_planes) {
|
|
6438
|
+
clipping_planes.forEach((clipping_plane) => {
|
|
6439
|
+
const plane = new Plane();
|
|
6440
|
+
plane.setFromNormalAndCoplanarPoint(getVector3FromPoint3d(clipping_plane.direction), getVector3FromPoint3d(clipping_plane.location));
|
|
6441
|
+
this.renderer.clippingPlanes.push(plane);
|
|
6442
|
+
});
|
|
6443
|
+
}
|
|
6071
6444
|
};
|
|
6072
6445
|
const setSelection = (selection) => {
|
|
6073
6446
|
if (selection)
|
|
@@ -6083,9 +6456,9 @@ class Viewer extends EventEmitter2 {
|
|
|
6083
6456
|
setOrthogonalCamera(viewpoint.orthogonal_camera);
|
|
6084
6457
|
setPerspectiveCamera(viewpoint.perspective_camera);
|
|
6085
6458
|
setClippingPlanes(viewpoint.clipping_planes);
|
|
6086
|
-
setSelection(viewpoint.selection);
|
|
6459
|
+
setSelection(((_b = viewpoint.custom_fields) === null || _b === void 0 ? void 0 : _b.selection2) || viewpoint.selection);
|
|
6087
6460
|
this._markup.setViewpoint(viewpoint);
|
|
6088
|
-
this.target
|
|
6461
|
+
this.target.copy(getVector3FromPoint3d((_d = (_c = viewpoint.custom_fields) === null || _c === void 0 ? void 0 : _c.camera_target) !== null && _d !== void 0 ? _d : this.target));
|
|
6089
6462
|
this.setActiveDragger(draggerName);
|
|
6090
6463
|
this.emitEvent({ type: "drawviewpoint", data: viewpoint });
|
|
6091
6464
|
this.update();
|
|
@@ -6132,6 +6505,9 @@ class Viewer extends EventEmitter2 {
|
|
|
6132
6505
|
const getSelection = () => {
|
|
6133
6506
|
return this.getSelected().map((handle) => ({ handle }));
|
|
6134
6507
|
};
|
|
6508
|
+
const getSelection2 = () => {
|
|
6509
|
+
return this.getSelected2().map((handle) => ({ handle }));
|
|
6510
|
+
};
|
|
6135
6511
|
const viewpoint = { custom_fields: {} };
|
|
6136
6512
|
viewpoint.orthogonal_camera = getOrthogonalCamera();
|
|
6137
6513
|
viewpoint.perspective_camera = getPerspectiveCamera();
|
|
@@ -6140,9 +6516,36 @@ class Viewer extends EventEmitter2 {
|
|
|
6140
6516
|
viewpoint.description = new Date().toDateString();
|
|
6141
6517
|
this._markup.getViewpoint(viewpoint);
|
|
6142
6518
|
viewpoint.custom_fields.camera_target = getPoint3dFromVector3(this.target);
|
|
6519
|
+
viewpoint.custom_fields.selection2 = getSelection2();
|
|
6143
6520
|
this.emitEvent({ type: "createviewpoint", data: viewpoint });
|
|
6144
6521
|
return viewpoint;
|
|
6145
6522
|
}
|
|
6523
|
+
screenToWorld(position) {
|
|
6524
|
+
if (!this.renderer)
|
|
6525
|
+
return { x: position.x, y: position.y, z: 0 };
|
|
6526
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
6527
|
+
const x = position.x / (rect.width / 2) - 1;
|
|
6528
|
+
const y = -position.y / (rect.height / 2) + 1;
|
|
6529
|
+
const point = new Vector3(x, y, -1);
|
|
6530
|
+
point.unproject(this.camera);
|
|
6531
|
+
return { x: point.x, y: point.y, z: point.z };
|
|
6532
|
+
}
|
|
6533
|
+
worldToScreen(position) {
|
|
6534
|
+
if (!this.renderer)
|
|
6535
|
+
return { x: position.x, y: position.y };
|
|
6536
|
+
const point = new Vector3(position.x, position.y, position.z);
|
|
6537
|
+
point.project(this.camera);
|
|
6538
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
6539
|
+
const x = (point.x + 1) * (rect.width / 2);
|
|
6540
|
+
const y = (-point.y + 1) * (rect.height / 2);
|
|
6541
|
+
return { x, y };
|
|
6542
|
+
}
|
|
6543
|
+
getScale() {
|
|
6544
|
+
return { x: 1, y: 1, z: 1 };
|
|
6545
|
+
}
|
|
6546
|
+
executeCommand(id, ...args) {
|
|
6547
|
+
return commands.executeCommand(id, this, ...args);
|
|
6548
|
+
}
|
|
6146
6549
|
}
|
|
6147
6550
|
|
|
6148
6551
|
export { GLTFLoadingManager, ModelImpl, Viewer, commands, components, draggers, loaders };
|