@inweb/viewer-three 27.2.0 → 27.2.2
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 +171 -60
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +168 -58
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/controls/WalkControls.d.ts +8 -0
- package/lib/Viewer/draggers/CuttingPlaneDragger.d.ts +9 -5
- package/lib/Viewer/helpers/PlaneHelper2.d.ts +8 -0
- package/package.json +5 -5
- package/src/Viewer/Viewer.ts +4 -0
- package/src/Viewer/components/CameraComponent.ts +1 -1
- package/src/Viewer/components/InfoComponent.ts +5 -1
- package/src/Viewer/controls/WalkControls.ts +41 -3
- package/src/Viewer/draggers/CuttingPlaneDragger.ts +65 -24
- package/src/Viewer/draggers/CuttingPlaneXAxis.ts +1 -1
- package/src/Viewer/draggers/CuttingPlaneYAxis.ts +1 -1
- package/src/Viewer/draggers/CuttingPlaneZAxis.ts +1 -1
- package/src/Viewer/helpers/{PlaneHelper.ts → PlaneHelper2.ts} +6 -23
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +68 -21
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +1 -0
- package/src/Viewer/models/ModelImpl.ts +1 -0
- package/lib/Viewer/helpers/PlaneHelper.d.ts +0 -11
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
import { draggersRegistry, commandsRegistry, Options, componentsRegistry, Info, Loader, loadersRegistry, CANVAS_EVENTS } from '@inweb/viewer-core';
|
|
25
25
|
export * from '@inweb/viewer-core';
|
|
26
|
-
import { Line,
|
|
26
|
+
import { Line, BufferGeometry, Float32BufferAttribute, LineBasicMaterial, Mesh, MeshBasicMaterial, DoubleSide, EventDispatcher, Vector3, MOUSE, TOUCH, Spherical, Quaternion, Vector2, Plane, Object3D, Line3, Raycaster, MathUtils, EdgesGeometry, Matrix4, Vector4, Controls, Box3, Clock, Color, PerspectiveCamera, OrthographicCamera, AmbientLight, DirectionalLight, HemisphereLight, REVISION, 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';
|
|
@@ -41,17 +41,15 @@ import { EventEmitter2 } from '@inweb/eventemitter2';
|
|
|
41
41
|
import { Markup } from '@inweb/markup';
|
|
42
42
|
export * from '@inweb/markup';
|
|
43
43
|
|
|
44
|
-
class
|
|
45
|
-
constructor(
|
|
44
|
+
class PlaneHelper2 extends Line {
|
|
45
|
+
constructor(size = 1, color = 0xc0c0c0) {
|
|
46
46
|
const positions = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0];
|
|
47
47
|
const geometry = new BufferGeometry();
|
|
48
48
|
geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
|
|
49
49
|
geometry.computeBoundingSphere();
|
|
50
50
|
super(geometry, new LineBasicMaterial({ color, toneMapped: false }));
|
|
51
|
-
this.type = "
|
|
52
|
-
this.plane = plane;
|
|
51
|
+
this.type = "PlaneHelper2";
|
|
53
52
|
this.size = size;
|
|
54
|
-
this.offset = offset;
|
|
55
53
|
const positions2 = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0];
|
|
56
54
|
const geometry2 = new BufferGeometry();
|
|
57
55
|
geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3));
|
|
@@ -69,14 +67,10 @@ class PlaneHelper extends Line {
|
|
|
69
67
|
dispose() {
|
|
70
68
|
this.geometry.dispose();
|
|
71
69
|
this.material.dispose();
|
|
72
|
-
this.
|
|
73
|
-
this.
|
|
70
|
+
this.helper.geometry.dispose();
|
|
71
|
+
this.helper.material.dispose();
|
|
74
72
|
}
|
|
75
73
|
updateMatrixWorld(force) {
|
|
76
|
-
this.position.set(0, 0, 0);
|
|
77
|
-
this.lookAt(this.plane.normal);
|
|
78
|
-
this.position.copy(this.offset);
|
|
79
|
-
this.translateZ(-(this.offset.dot(this.plane.normal) + this.plane.constant));
|
|
80
74
|
this.scale.set(0.5 * this.size, 0.5 * this.size, 1);
|
|
81
75
|
super.updateMatrixWorld(force);
|
|
82
76
|
}
|
|
@@ -832,26 +826,41 @@ class OrbitDragger {
|
|
|
832
826
|
}
|
|
833
827
|
|
|
834
828
|
class CuttingPlaneDragger extends OrbitDragger {
|
|
835
|
-
constructor(viewer, normal
|
|
829
|
+
constructor(viewer, normal) {
|
|
836
830
|
super(viewer);
|
|
837
831
|
this.transformChange = () => {
|
|
832
|
+
this.plane.normal.copy(new Vector3(0, 0, -1)).applyQuaternion(this.planeCenter.quaternion);
|
|
838
833
|
this.plane.constant = -this.planeCenter.position.dot(this.plane.normal);
|
|
839
834
|
this.viewer.update();
|
|
840
835
|
};
|
|
841
|
-
this.
|
|
836
|
+
this.translateDrag = (event) => {
|
|
842
837
|
this.orbit.enabled = !event.value;
|
|
838
|
+
this.rotate.enabled = !event.value;
|
|
839
|
+
};
|
|
840
|
+
this.rotateDrag = (event) => {
|
|
841
|
+
this.orbit.enabled = !event.value;
|
|
842
|
+
this.translate.enabled = !event.value;
|
|
843
843
|
};
|
|
844
844
|
this.updatePlaneSize = () => {
|
|
845
845
|
this.planeHelper.size = this.viewer.extents.getSize(new Vector3()).length() || 1;
|
|
846
846
|
this.viewer.update();
|
|
847
847
|
};
|
|
848
848
|
this.updateTransformCamera = () => {
|
|
849
|
-
this.
|
|
849
|
+
this.translate.camera = this.viewer.camera;
|
|
850
|
+
this.rotate.camera = this.viewer.camera;
|
|
851
|
+
};
|
|
852
|
+
this.onKeyDown = (event) => {
|
|
853
|
+
if (event.key === "Shift")
|
|
854
|
+
this.rotate.setRotationSnap(Math.PI / 4);
|
|
855
|
+
};
|
|
856
|
+
this.onKeyUp = (event) => {
|
|
857
|
+
if (event.key === "Shift")
|
|
858
|
+
this.rotate.setRotationSnap(null);
|
|
850
859
|
};
|
|
851
860
|
this.onDoubleClick = (event) => {
|
|
852
861
|
event.stopPropagation();
|
|
853
|
-
this.
|
|
854
|
-
this.
|
|
862
|
+
this.planeCenter.rotateOnAxis(new Vector3(0, 1, 0), Math.PI);
|
|
863
|
+
this.transformChange();
|
|
855
864
|
};
|
|
856
865
|
const extentsSize = viewer.extents.getSize(new Vector3()).length() || 1;
|
|
857
866
|
const extentsCenter = viewer.extents.getCenter(new Vector3());
|
|
@@ -860,24 +869,38 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
860
869
|
if (!viewer.renderer.clippingPlanes)
|
|
861
870
|
viewer.renderer.clippingPlanes = [];
|
|
862
871
|
viewer.renderer.clippingPlanes.push(this.plane);
|
|
863
|
-
this.planeHelper = new PlaneHelper(this.plane, extentsSize, color, extentsCenter);
|
|
864
|
-
this.viewer.helpers.add(this.planeHelper);
|
|
865
872
|
this.planeCenter = new Object3D();
|
|
866
873
|
this.planeCenter.position.copy(extentsCenter);
|
|
874
|
+
this.planeCenter.quaternion.setFromUnitVectors(new Vector3(0, 0, -1), normal);
|
|
867
875
|
this.viewer.helpers.add(this.planeCenter);
|
|
868
|
-
this.
|
|
869
|
-
this.
|
|
870
|
-
this.
|
|
871
|
-
this.
|
|
872
|
-
this.
|
|
873
|
-
this.
|
|
874
|
-
this.
|
|
875
|
-
this.
|
|
876
|
+
this.planeHelper = new PlaneHelper2(extentsSize);
|
|
877
|
+
this.planeCenter.add(this.planeHelper);
|
|
878
|
+
this.translate = new TransformControls(viewer.camera, viewer.canvas);
|
|
879
|
+
this.translate.setSpace("local");
|
|
880
|
+
this.translate.showX = false;
|
|
881
|
+
this.translate.showY = false;
|
|
882
|
+
this.translate.showZ = true;
|
|
883
|
+
this.translate.attach(this.planeCenter);
|
|
884
|
+
this.translate.addEventListener("change", this.transformChange);
|
|
885
|
+
this.translate.addEventListener("dragging-changed", this.translateDrag);
|
|
886
|
+
this.viewer.helpers.add(this.translate.getHelper());
|
|
887
|
+
this.rotate = new TransformControls(viewer.camera, viewer.canvas);
|
|
888
|
+
this.rotate.setMode("rotate");
|
|
889
|
+
this.rotate.setSpace("local");
|
|
890
|
+
this.rotate.showX = true;
|
|
891
|
+
this.rotate.showY = true;
|
|
892
|
+
this.rotate.showZ = false;
|
|
893
|
+
this.rotate.attach(this.planeCenter);
|
|
894
|
+
this.rotate.addEventListener("change", this.transformChange);
|
|
895
|
+
this.rotate.addEventListener("dragging-changed", this.rotateDrag);
|
|
896
|
+
this.viewer.helpers.add(this.rotate.getHelper());
|
|
876
897
|
this.viewer.addEventListener("explode", this.updatePlaneSize);
|
|
877
898
|
this.viewer.addEventListener("show", this.updatePlaneSize);
|
|
878
899
|
this.viewer.addEventListener("showall", this.updatePlaneSize);
|
|
879
900
|
this.viewer.addEventListener("changecameramode", this.updateTransformCamera);
|
|
880
901
|
this.viewer.canvas.addEventListener("dblclick", this.onDoubleClick, true);
|
|
902
|
+
window.addEventListener("keydown", this.onKeyDown);
|
|
903
|
+
window.addEventListener("keyup", this.onKeyUp);
|
|
881
904
|
this.viewer.update();
|
|
882
905
|
}
|
|
883
906
|
dispose() {
|
|
@@ -886,11 +909,18 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
886
909
|
this.viewer.removeEventListener("showall", this.updatePlaneSize);
|
|
887
910
|
this.viewer.removeEventListener("changecameramode", this.updateTransformCamera);
|
|
888
911
|
this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
this.
|
|
892
|
-
this.
|
|
893
|
-
this.
|
|
912
|
+
window.removeEventListener("keydown", this.onKeyDown);
|
|
913
|
+
window.removeEventListener("keyup", this.onKeyUp);
|
|
914
|
+
this.translate.removeEventListener("change", this.transformChange);
|
|
915
|
+
this.translate.removeEventListener("dragging-changed", this.translateDrag);
|
|
916
|
+
this.translate.getHelper().removeFromParent();
|
|
917
|
+
this.translate.detach();
|
|
918
|
+
this.translate.dispose();
|
|
919
|
+
this.rotate.removeEventListener("change", this.transformChange);
|
|
920
|
+
this.rotate.removeEventListener("dragging-changed", this.rotateDrag);
|
|
921
|
+
this.rotate.getHelper().removeFromParent();
|
|
922
|
+
this.rotate.detach();
|
|
923
|
+
this.rotate.dispose();
|
|
894
924
|
this.planeHelper.removeFromParent();
|
|
895
925
|
this.planeHelper.dispose();
|
|
896
926
|
this.planeCenter.removeFromParent();
|
|
@@ -900,19 +930,19 @@ class CuttingPlaneDragger extends OrbitDragger {
|
|
|
900
930
|
|
|
901
931
|
class CuttingPlaneXAxisDragger extends CuttingPlaneDragger {
|
|
902
932
|
constructor(viewer) {
|
|
903
|
-
super(viewer, new Vector3(1, 0, 0)
|
|
933
|
+
super(viewer, new Vector3(-1, 0, 0));
|
|
904
934
|
}
|
|
905
935
|
}
|
|
906
936
|
|
|
907
937
|
class CuttingPlaneYAxisDragger extends CuttingPlaneDragger {
|
|
908
938
|
constructor(viewer) {
|
|
909
|
-
super(viewer, new Vector3(0, 1, 0)
|
|
939
|
+
super(viewer, new Vector3(0, -1, 0));
|
|
910
940
|
}
|
|
911
941
|
}
|
|
912
942
|
|
|
913
943
|
class CuttingPlaneZAxisDragger extends CuttingPlaneDragger {
|
|
914
944
|
constructor(viewer) {
|
|
915
|
-
super(viewer, new Vector3(0, 0, 1)
|
|
945
|
+
super(viewer, new Vector3(0, 0, -1));
|
|
916
946
|
}
|
|
917
947
|
}
|
|
918
948
|
|
|
@@ -1514,6 +1544,12 @@ class WalkControls extends Controls {
|
|
|
1514
1544
|
this.movementSpeed = 0.1;
|
|
1515
1545
|
this.multiplier = 3;
|
|
1516
1546
|
this.groundFollowingSkippedFrames = 0;
|
|
1547
|
+
this.GROUND_BOX_HALF_SIZE = 20;
|
|
1548
|
+
this.GROUND_BOX_REFRESH_THRESHOLD = 0.3;
|
|
1549
|
+
this._groundObjectBoxes = new Map();
|
|
1550
|
+
this._activeGroundObjects = [];
|
|
1551
|
+
this._groundBox = new Box3();
|
|
1552
|
+
this._groundBoxCenter = new Vector3();
|
|
1517
1553
|
this.moveWheel = 0;
|
|
1518
1554
|
this.mouseDragOn = false;
|
|
1519
1555
|
this._up = new Vector3();
|
|
@@ -1585,6 +1621,9 @@ class WalkControls extends Controls {
|
|
|
1585
1621
|
};
|
|
1586
1622
|
this.camera = camera;
|
|
1587
1623
|
this.groundObjects = groundObjects;
|
|
1624
|
+
for (const obj of groundObjects) {
|
|
1625
|
+
this._groundObjectBoxes.set(obj, new Box3().setFromObject(obj));
|
|
1626
|
+
}
|
|
1588
1627
|
this.raycaster = new Raycaster();
|
|
1589
1628
|
this.raycaster.near = 0;
|
|
1590
1629
|
this.raycaster.far = this.EYE_HEIGHT + this.FAILING_DISTANCE;
|
|
@@ -1619,10 +1658,29 @@ class WalkControls extends Controls {
|
|
|
1619
1658
|
window.removeEventListener("keyup", this.onKeyUp);
|
|
1620
1659
|
super.dispose();
|
|
1621
1660
|
}
|
|
1661
|
+
_rebuildGroundBox(center) {
|
|
1662
|
+
const h = this.GROUND_BOX_HALF_SIZE;
|
|
1663
|
+
this._groundBoxCenter.copy(center);
|
|
1664
|
+
this._groundBox.set(new Vector3(center.x - h, center.y - h * 4, center.z - h), new Vector3(center.x + h, center.y + h * 4, center.z + h));
|
|
1665
|
+
this._activeGroundObjects = this.groundObjects.filter((obj) => {
|
|
1666
|
+
const objectBox = this._groundObjectBoxes.get(obj);
|
|
1667
|
+
return objectBox !== undefined && this._groundBox.intersectsBox(objectBox);
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1670
|
+
_needsGroundBoxRebuild(pos) {
|
|
1671
|
+
if (this._activeGroundObjects.length === 0 && this.groundObjects.length > 0)
|
|
1672
|
+
return true;
|
|
1673
|
+
const threshold = this.GROUND_BOX_HALF_SIZE * this.GROUND_BOX_REFRESH_THRESHOLD;
|
|
1674
|
+
return (Math.abs(pos.x - this._groundBoxCenter.x) > threshold || Math.abs(pos.z - this._groundBoxCenter.z) > threshold);
|
|
1675
|
+
}
|
|
1622
1676
|
updateGroundFollowing() {
|
|
1677
|
+
const pos = this.object.position;
|
|
1678
|
+
if (this._needsGroundBoxRebuild(pos)) {
|
|
1679
|
+
this._rebuildGroundBox(pos);
|
|
1680
|
+
}
|
|
1623
1681
|
this._up.copy(this.camera.up).negate();
|
|
1624
|
-
this.raycaster.set(
|
|
1625
|
-
const intersects = this.raycaster.intersectObjects(this.
|
|
1682
|
+
this.raycaster.set(pos, this._up);
|
|
1683
|
+
const intersects = this.raycaster.intersectObjects(this._activeGroundObjects, false);
|
|
1626
1684
|
if (intersects.length > 0) {
|
|
1627
1685
|
const groundY = intersects[0].point.y;
|
|
1628
1686
|
const targetY = groundY + this.EYE_HEIGHT;
|
|
@@ -2499,7 +2557,7 @@ class CameraComponent {
|
|
|
2499
2557
|
camera = object;
|
|
2500
2558
|
});
|
|
2501
2559
|
if (camera) {
|
|
2502
|
-
camera.isDefaultCamera = true;
|
|
2560
|
+
camera.userData.isDefaultCamera = true;
|
|
2503
2561
|
camera.scale.set(1, 1, 1);
|
|
2504
2562
|
this.switchCamera(camera);
|
|
2505
2563
|
const mode = this.getCameraMode(camera);
|
|
@@ -2703,6 +2761,7 @@ class InfoComponent {
|
|
|
2703
2761
|
this.viewer.info.optimizedScene.edges = 0;
|
|
2704
2762
|
this.viewer.info.memory.geometries = 0;
|
|
2705
2763
|
this.viewer.info.memory.geometryBytes = 0;
|
|
2764
|
+
this.viewer.info.memory.optimizedGeometryBytes = 0;
|
|
2706
2765
|
this.viewer.info.memory.textures = 0;
|
|
2707
2766
|
this.viewer.info.memory.textureBytes = 0;
|
|
2708
2767
|
this.viewer.info.memory.materials = 0;
|
|
@@ -2739,6 +2798,7 @@ class InfoComponent {
|
|
|
2739
2798
|
this.viewer.info.optimizedScene.edges += info.optimizedScene.edges;
|
|
2740
2799
|
this.viewer.info.memory.geometries += info.memory.geometries;
|
|
2741
2800
|
this.viewer.info.memory.geometryBytes += info.memory.geometryBytes;
|
|
2801
|
+
this.viewer.info.memory.optimizedGeometryBytes += info.memory.optimizedGeometryBytes;
|
|
2742
2802
|
this.viewer.info.memory.textures += info.memory.textures;
|
|
2743
2803
|
this.viewer.info.memory.textureBytes += info.memory.textureBytes;
|
|
2744
2804
|
this.viewer.info.memory.materials += info.memory.materials;
|
|
@@ -2749,7 +2809,9 @@ class InfoComponent {
|
|
|
2749
2809
|
this.viewer.info.performance.loadTime += performance.now() - this.startTime;
|
|
2750
2810
|
console.log("Number of objects:", info.scene.objects);
|
|
2751
2811
|
console.log("Number of objects after optimization:", info.optimizedScene.objects);
|
|
2752
|
-
console.log("
|
|
2812
|
+
console.log("Geometry size:", info.memory.geometryBytes / (1024 * 1024), "MB");
|
|
2813
|
+
console.log("Optimized geometry size:", info.memory.optimizedGeometryBytes / (1024 * 1024), "MB");
|
|
2814
|
+
console.log("Estimated GPU used:", info.memory.totalEstimatedGpuBytes / (1024 * 1024), "MB");
|
|
2753
2815
|
console.log("File load time:", this.viewer.info.performance.loadTime, "ms");
|
|
2754
2816
|
};
|
|
2755
2817
|
this.resize = () => {
|
|
@@ -3492,6 +3554,7 @@ class ModelImpl {
|
|
|
3492
3554
|
info.scene.edges = Math.floor(totalEdges);
|
|
3493
3555
|
info.memory.geometries = geometries.size;
|
|
3494
3556
|
info.memory.geometryBytes = geometryBytes;
|
|
3557
|
+
info.memory.optimizedGeometryBytes = 0;
|
|
3495
3558
|
info.memory.textures = textures.size;
|
|
3496
3559
|
info.memory.textureBytes = Math.floor(textureBytes);
|
|
3497
3560
|
info.memory.materials = materials.size;
|
|
@@ -3664,6 +3727,7 @@ class DynamicModelImpl extends ModelImpl {
|
|
|
3664
3727
|
info.optimizedScene.edges = stats.scene.afterOptimization.edges;
|
|
3665
3728
|
info.memory.geometries = stats.memory.geometries.count;
|
|
3666
3729
|
info.memory.geometryBytes = stats.memory.geometries.bytes;
|
|
3730
|
+
info.memory.optimizedGeometryBytes = stats.memory.geometries.optimizedBytes;
|
|
3667
3731
|
info.memory.textures = stats.memory.textures.count;
|
|
3668
3732
|
info.memory.materials = stats.memory.materials.count;
|
|
3669
3733
|
info.memory.totalEstimatedGpuBytes = stats.memory.totalEstimatedGpuBytes;
|
|
@@ -4335,11 +4399,14 @@ class DynamicGltfLoader {
|
|
|
4335
4399
|
this.structures = [];
|
|
4336
4400
|
this.structureRoots = new Map();
|
|
4337
4401
|
this.memoryLimit = this.getAvailableMemory();
|
|
4402
|
+
this.optimizationMemoryMultiplier = 5;
|
|
4403
|
+
this.memoryEstimationFactor = 1.7;
|
|
4338
4404
|
this.loadedGeometrySize = 0;
|
|
4339
4405
|
this.geometryCache = new Map();
|
|
4340
4406
|
this.materialCache = new Map();
|
|
4341
4407
|
this.textureCache = new Map();
|
|
4342
4408
|
this.currentMemoryUsage = 0;
|
|
4409
|
+
this.pendingMemoryUsage = 0;
|
|
4343
4410
|
this.updateMemoryIndicator();
|
|
4344
4411
|
this.loadedMaterials = new Map();
|
|
4345
4412
|
this.abortController = new AbortController();
|
|
@@ -4395,7 +4462,7 @@ class DynamicGltfLoader {
|
|
|
4395
4462
|
} catch (error) {
|
|
4396
4463
|
console.warn("Error detecting available memory:", error);
|
|
4397
4464
|
}
|
|
4398
|
-
return memoryLimit
|
|
4465
|
+
return memoryLimit;
|
|
4399
4466
|
}
|
|
4400
4467
|
getAbortController() {
|
|
4401
4468
|
return this.abortController;
|
|
@@ -4403,9 +4470,26 @@ class DynamicGltfLoader {
|
|
|
4403
4470
|
abortLoading() {
|
|
4404
4471
|
this.abortController.abort();
|
|
4405
4472
|
}
|
|
4473
|
+
getOptimizedGeometrySize() {
|
|
4474
|
+
let total = 0;
|
|
4475
|
+
const addSize = (obj) => {
|
|
4476
|
+
if (obj && obj.geometry) total += this.estimateGeometrySize(obj);
|
|
4477
|
+
};
|
|
4478
|
+
this.mergedMesh?.forEach(addSize);
|
|
4479
|
+
this.mergedLines?.forEach(addSize);
|
|
4480
|
+
this.mergedLineSegments?.forEach(addSize);
|
|
4481
|
+
this.mergedPoints?.forEach(addSize);
|
|
4482
|
+
return total;
|
|
4483
|
+
}
|
|
4406
4484
|
updateMemoryIndicator() {
|
|
4485
|
+
const optimizedUsage = this.getOptimizedGeometrySize();
|
|
4486
|
+
const totalUsage = this.currentMemoryUsage + optimizedUsage;
|
|
4487
|
+
const totalUsageEstimate = Math.round(totalUsage * this.memoryEstimationFactor);
|
|
4407
4488
|
this.dispatchEvent("geometrymemory", {
|
|
4408
4489
|
currentUsage: this.currentMemoryUsage,
|
|
4490
|
+
optimizedUsage,
|
|
4491
|
+
totalUsage,
|
|
4492
|
+
totalUsageEstimate,
|
|
4409
4493
|
limit: this.memoryLimit,
|
|
4410
4494
|
});
|
|
4411
4495
|
}
|
|
@@ -4457,10 +4541,13 @@ class DynamicGltfLoader {
|
|
|
4457
4541
|
for (const geo of geometries) {
|
|
4458
4542
|
currentMemoryUsage += geo.size;
|
|
4459
4543
|
}
|
|
4460
|
-
|
|
4461
|
-
|
|
4544
|
+
const effectiveLimitForEviction = this.memoryLimit / this.memoryEstimationFactor;
|
|
4545
|
+
if (currentMemoryUsage > effectiveLimitForEviction) {
|
|
4546
|
+
console.log(
|
|
4547
|
+
`Memory usage (${Math.round((currentMemoryUsage * this.memoryEstimationFactor) / (1024 * 1024))}MB est.) exceeds limit`
|
|
4548
|
+
);
|
|
4462
4549
|
for (const geo of geometries) {
|
|
4463
|
-
if (currentMemoryUsage <=
|
|
4550
|
+
if (currentMemoryUsage <= effectiveLimitForEviction) break;
|
|
4464
4551
|
if (this.abortController.signal.aborted) {
|
|
4465
4552
|
throw new DOMException("Loading aborted", "AbortError");
|
|
4466
4553
|
}
|
|
@@ -4544,7 +4631,9 @@ class DynamicGltfLoader {
|
|
|
4544
4631
|
}
|
|
4545
4632
|
const materialCount = uniqueMaterialIds.size;
|
|
4546
4633
|
const textureCount = uniqueTextureIds.size;
|
|
4547
|
-
const
|
|
4634
|
+
const optimizedUsageBytes = this.getOptimizedGeometrySize();
|
|
4635
|
+
const totalUsageBytes = geometryMemoryBytes + optimizedUsageBytes;
|
|
4636
|
+
const estimatedGpuMemoryBytes = Math.round(totalUsageBytes * this.memoryEstimationFactor);
|
|
4548
4637
|
if (!this._webglInfoCache) {
|
|
4549
4638
|
try {
|
|
4550
4639
|
const gl = this.renderer.getContext();
|
|
@@ -4581,7 +4670,12 @@ class DynamicGltfLoader {
|
|
|
4581
4670
|
},
|
|
4582
4671
|
},
|
|
4583
4672
|
memory: {
|
|
4584
|
-
geometries: {
|
|
4673
|
+
geometries: {
|
|
4674
|
+
count: geometryCount,
|
|
4675
|
+
bytes: geometryMemoryBytes,
|
|
4676
|
+
optimizedBytes: optimizedUsageBytes,
|
|
4677
|
+
totalRawBytes: totalUsageBytes,
|
|
4678
|
+
},
|
|
4585
4679
|
textures: { count: textureCount },
|
|
4586
4680
|
materials: { count: materialCount },
|
|
4587
4681
|
totalEstimatedGpuBytes: estimatedGpuMemoryBytes,
|
|
@@ -4593,9 +4687,12 @@ class DynamicGltfLoader {
|
|
|
4593
4687
|
},
|
|
4594
4688
|
};
|
|
4595
4689
|
}
|
|
4596
|
-
async loadNode(nodeId, onLoadFinishCb) {
|
|
4690
|
+
async loadNode(nodeId, onLoadFinishCb, reservedEstimatedSize = 0) {
|
|
4597
4691
|
const node = this.nodes.get(nodeId);
|
|
4598
|
-
if (!node || node.loaded || node.loading)
|
|
4692
|
+
if (!node || node.loaded || node.loading) {
|
|
4693
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
4694
|
+
return;
|
|
4695
|
+
}
|
|
4599
4696
|
node.loading = true;
|
|
4600
4697
|
const meshDef = node.structure.getJson().meshes[node.meshIndex];
|
|
4601
4698
|
try {
|
|
@@ -4678,6 +4775,7 @@ class DynamicGltfLoader {
|
|
|
4678
4775
|
if (bufferRequests.length === 0) {
|
|
4679
4776
|
node.loaded = true;
|
|
4680
4777
|
node.loading = false;
|
|
4778
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
4681
4779
|
return;
|
|
4682
4780
|
}
|
|
4683
4781
|
bufferRequests.sort((a, b) => a.offset - b.offset);
|
|
@@ -4789,6 +4887,7 @@ class DynamicGltfLoader {
|
|
|
4789
4887
|
}
|
|
4790
4888
|
node.loaded = true;
|
|
4791
4889
|
node.loading = false;
|
|
4890
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
4792
4891
|
const geometrySize = this.estimateGeometrySize(node.object);
|
|
4793
4892
|
this.geometryCache.set(node.object.uuid, geometrySize);
|
|
4794
4893
|
this.currentMemoryUsage += geometrySize;
|
|
@@ -4797,6 +4896,7 @@ class DynamicGltfLoader {
|
|
|
4797
4896
|
}
|
|
4798
4897
|
} catch (error) {
|
|
4799
4898
|
node.loading = false;
|
|
4899
|
+
this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
|
|
4800
4900
|
if (error.name === "AbortError") {
|
|
4801
4901
|
return;
|
|
4802
4902
|
}
|
|
@@ -5012,15 +5112,18 @@ class DynamicGltfLoader {
|
|
|
5012
5112
|
let loadedCount = 0;
|
|
5013
5113
|
let lastLoadedCount = 0;
|
|
5014
5114
|
const totalNodes = nodesToLoad.length;
|
|
5115
|
+
const progressTotal = { value: totalNodes };
|
|
5015
5116
|
const loadProgress = async () => {
|
|
5016
5117
|
loadedCount++;
|
|
5118
|
+
const total = progressTotal.value;
|
|
5119
|
+
const percentage = total > 0 ? Math.min(100, Math.round((loadedCount / total) * 100)) : 0;
|
|
5017
5120
|
if (loadedCount - lastLoadedCount > 1000) {
|
|
5018
5121
|
lastLoadedCount = loadedCount;
|
|
5019
5122
|
this.updateMemoryIndicator();
|
|
5020
5123
|
this.dispatchEvent("geometryprogress", {
|
|
5021
|
-
percentage
|
|
5124
|
+
percentage,
|
|
5022
5125
|
loaded: loadedCount,
|
|
5023
|
-
total
|
|
5126
|
+
total,
|
|
5024
5127
|
});
|
|
5025
5128
|
this.dispatchEvent("update");
|
|
5026
5129
|
await new Promise((resolve) => {
|
|
@@ -5030,20 +5133,22 @@ class DynamicGltfLoader {
|
|
|
5030
5133
|
};
|
|
5031
5134
|
try {
|
|
5032
5135
|
const loadOperations = [];
|
|
5136
|
+
let memoryLimitReached = false;
|
|
5033
5137
|
for (const nodeId of nodesToLoad) {
|
|
5034
5138
|
if (this.abortController.signal.aborted) {
|
|
5035
5139
|
throw new DOMException("Loading aborted", "AbortError");
|
|
5036
5140
|
}
|
|
5037
5141
|
const estimatedSize = await this.estimateNodeSize(nodeId);
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5142
|
+
const estimated = Number(estimatedSize) || 0;
|
|
5143
|
+
const effectiveLimit = this.memoryLimit / this.optimizationMemoryMultiplier / this.memoryEstimationFactor;
|
|
5144
|
+
if (this.currentMemoryUsage + this.pendingMemoryUsage + estimated > effectiveLimit) {
|
|
5145
|
+
memoryLimitReached = true;
|
|
5146
|
+
progressTotal.value = loadOperations.length;
|
|
5147
|
+
console.log(`Memory limit reached after scheduling ${loadOperations.length} nodes`);
|
|
5148
|
+
break;
|
|
5045
5149
|
}
|
|
5046
|
-
|
|
5150
|
+
this.pendingMemoryUsage += estimated;
|
|
5151
|
+
loadOperations.push(this.loadNode(nodeId, loadProgress, estimated));
|
|
5047
5152
|
}
|
|
5048
5153
|
for (const structure of this.structures) {
|
|
5049
5154
|
loadOperations.push(structure.flushBufferRequests());
|
|
@@ -5051,7 +5156,8 @@ class DynamicGltfLoader {
|
|
|
5051
5156
|
await Promise.all(loadOperations);
|
|
5052
5157
|
this.dispatchEvent("geometryend", {
|
|
5053
5158
|
totalLoaded: loadedCount,
|
|
5054
|
-
totalNodes,
|
|
5159
|
+
totalNodes: progressTotal.value,
|
|
5160
|
+
memoryLimitReached,
|
|
5055
5161
|
});
|
|
5056
5162
|
return loadedCount;
|
|
5057
5163
|
} catch (error) {
|
|
@@ -5361,6 +5467,7 @@ class DynamicGltfLoader {
|
|
|
5361
5467
|
this.transformedGeometries.clear();
|
|
5362
5468
|
this.totalLoadedObjects = 0;
|
|
5363
5469
|
this.currentMemoryUsage = 0;
|
|
5470
|
+
this.pendingMemoryUsage = 0;
|
|
5364
5471
|
this.loadedGeometrySize = 0;
|
|
5365
5472
|
this.abortController = new AbortController();
|
|
5366
5473
|
this.updateMemoryIndicator();
|
|
@@ -5567,6 +5674,7 @@ class DynamicGltfLoader {
|
|
|
5567
5674
|
progress: 100,
|
|
5568
5675
|
message: `Optimization complete! ${this.maxObjectId} objects processed.`,
|
|
5569
5676
|
});
|
|
5677
|
+
this.updateMemoryIndicator();
|
|
5570
5678
|
this.dispatchEvent("update");
|
|
5571
5679
|
}
|
|
5572
5680
|
async mergeMeshGroups(materialGroups, rootGroup) {
|
|
@@ -7136,12 +7244,14 @@ class Viewer extends EventEmitter2 {
|
|
|
7136
7244
|
if (!this.renderer)
|
|
7137
7245
|
return;
|
|
7138
7246
|
this._markup.clearOverlay();
|
|
7247
|
+
this.emitEvent({ type: "clearoverlay" });
|
|
7139
7248
|
this.update();
|
|
7140
7249
|
}
|
|
7141
7250
|
clearSlices() {
|
|
7142
7251
|
if (!this.renderer)
|
|
7143
7252
|
return;
|
|
7144
7253
|
this.renderer.clippingPlanes = [];
|
|
7254
|
+
this.emitEvent({ type: "clearslices" });
|
|
7145
7255
|
this.update();
|
|
7146
7256
|
}
|
|
7147
7257
|
getSelected() {
|