@inweb/viewer-three 26.6.6 → 26.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/plugins/components/AxesHelperComponent.js +5 -5
- 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 +5 -5
- package/dist/plugins/components/AxesHelperComponent.module.js.map +1 -1
- package/dist/plugins/components/ExtentsHelperComponent.js +15 -7
- 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 +15 -7
- package/dist/plugins/components/ExtentsHelperComponent.module.js.map +1 -1
- package/dist/plugins/components/LightHelperComponent.js +5 -5
- package/dist/plugins/components/LightHelperComponent.js.map +1 -1
- package/dist/plugins/components/LightHelperComponent.min.js +1 -1
- package/dist/plugins/components/LightHelperComponent.module.js +5 -5
- package/dist/plugins/components/LightHelperComponent.module.js.map +1 -1
- package/dist/plugins/loaders/GLTFCloudLoader.js +4840 -0
- package/dist/plugins/loaders/GLTFCloudLoader.js.map +1 -0
- package/dist/plugins/loaders/GLTFCloudLoader.min.js +1 -0
- package/dist/plugins/loaders/GLTFCloudLoader.module.js +49 -0
- package/dist/plugins/loaders/GLTFCloudLoader.module.js.map +1 -0
- package/dist/plugins/loaders/IFCXLoader.js +12 -6
- 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 +13 -7
- package/dist/plugins/loaders/IFCXLoader.module.js.map +1 -1
- package/dist/viewer-three.js +3131 -459
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +2 -2
- package/dist/viewer-three.module.js +2326 -264
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/Viewer.d.ts +6 -5
- package/lib/Viewer/components/HighlighterComponent.d.ts +4 -3
- package/lib/Viewer/components/SelectionComponent.d.ts +8 -5
- package/lib/Viewer/draggers/CuttingPlaneDragger.d.ts +1 -1
- package/lib/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.d.ts +20 -0
- package/lib/Viewer/loaders/GLTFCloudDynamicLoader.d.ts +15 -0
- package/lib/Viewer/model/IModelImpl.d.ts +27 -0
- package/lib/Viewer/model/ModelImpl.d.ts +30 -0
- package/lib/Viewer/model/index.d.ts +2 -0
- package/lib/index.d.ts +1 -0
- package/package.json +11 -7
- package/plugins/components/AxesHelperComponent.ts +5 -5
- package/plugins/components/ExtentsHelperComponent.ts +15 -7
- package/plugins/components/LightHelperComponent.ts +5 -5
- package/{src/Viewer/loaders/GLTFCloudModelLoader.ts → plugins/loaders/GLTFCloudLoader.ts} +15 -12
- package/plugins/loaders/{IFCXCloudFileLoader.ts → IFCXCloudLoader.ts} +8 -4
- package/plugins/loaders/IFCXFileLoader.ts +7 -3
- package/plugins/loaders/IFCXLoader.ts +2 -2
- package/src/Viewer/Viewer.ts +32 -36
- package/src/Viewer/commands/ClearSelected.ts +2 -3
- package/src/Viewer/commands/Explode.ts +1 -47
- package/src/Viewer/commands/GetModels.ts +1 -1
- package/src/Viewer/commands/GetSelected.ts +3 -1
- package/src/Viewer/commands/HideSelected.ts +3 -4
- package/src/Viewer/commands/IsolateSelected.ts +1 -7
- package/src/Viewer/commands/SelectModel.ts +9 -1
- package/src/Viewer/commands/SetSelected.ts +8 -10
- package/src/Viewer/commands/ShowAll.ts +1 -1
- package/src/Viewer/components/BackgroundComponent.ts +1 -0
- package/src/Viewer/components/ExtentsComponent.ts +5 -3
- package/src/Viewer/components/HighlighterComponent.ts +79 -48
- package/src/Viewer/components/SelectionComponent.ts +67 -21
- package/src/Viewer/draggers/CuttingPlaneDragger.ts +7 -3
- package/src/Viewer/draggers/MeasureLineDragger.ts +2 -0
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +1628 -0
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +102 -0
- package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +450 -0
- package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +145 -0
- package/src/Viewer/loaders/GLTFFileLoader.ts +7 -2
- package/src/Viewer/loaders/index.ts +2 -2
- package/src/Viewer/model/IModelImpl.ts +67 -0
- package/src/Viewer/model/ModelImpl.ts +215 -0
- package/src/Viewer/model/index.ts +25 -0
- package/src/index.ts +1 -0
- package/lib/Viewer/loaders/GLTFCloudModelLoader.d.ts +0 -8
package/dist/viewer-three.js
CHANGED
|
@@ -58447,7 +58447,7 @@ void main() {
|
|
|
58447
58447
|
this.transformDrag = (event) => {
|
|
58448
58448
|
this.orbit.enabled = !event.value;
|
|
58449
58449
|
};
|
|
58450
|
-
this.
|
|
58450
|
+
this.updatePlaneSize = () => {
|
|
58451
58451
|
this.planeHelper.size = this.viewer.extents.getSize(new Vector3()).length();
|
|
58452
58452
|
this.viewer.update();
|
|
58453
58453
|
};
|
|
@@ -58476,12 +58476,16 @@ void main() {
|
|
|
58476
58476
|
this.transform.addEventListener("change", this.transformChange);
|
|
58477
58477
|
this.transform.addEventListener("dragging-changed", this.transformDrag);
|
|
58478
58478
|
this.viewer.helpers.add(this.transform.getHelper());
|
|
58479
|
-
this.viewer.on("explode", this.
|
|
58479
|
+
this.viewer.on("explode", this.updatePlaneSize);
|
|
58480
|
+
this.viewer.on("show", this.updatePlaneSize);
|
|
58481
|
+
this.viewer.on("showall", this.updatePlaneSize);
|
|
58480
58482
|
this.viewer.canvas.addEventListener("dblclick", this.onDoubleClick, true);
|
|
58481
58483
|
this.viewer.update();
|
|
58482
58484
|
}
|
|
58483
58485
|
dispose() {
|
|
58484
|
-
this.viewer.off("explode", this.
|
|
58486
|
+
this.viewer.off("explode", this.updatePlaneSize);
|
|
58487
|
+
this.viewer.off("show", this.updatePlaneSize);
|
|
58488
|
+
this.viewer.off("showAll", this.updatePlaneSize);
|
|
58485
58489
|
this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
|
|
58486
58490
|
this.transform.removeEventListener("change", this.transformChange);
|
|
58487
58491
|
this.transform.removeEventListener("dragging-changed", this.transformDrag);
|
|
@@ -58663,6 +58667,7 @@ void main() {
|
|
|
58663
58667
|
this.viewer.addEventListener("render", this.renderOverlay);
|
|
58664
58668
|
this.viewer.addEventListener("hide", this.updateSnapper);
|
|
58665
58669
|
this.viewer.addEventListener("isolate", this.updateSnapper);
|
|
58670
|
+
this.viewer.addEventListener("show", this.updateSnapper);
|
|
58666
58671
|
this.viewer.addEventListener("showall", this.updateSnapper);
|
|
58667
58672
|
}
|
|
58668
58673
|
dispose() {
|
|
@@ -58674,6 +58679,7 @@ void main() {
|
|
|
58674
58679
|
this.viewer.removeEventListener("render", this.renderOverlay);
|
|
58675
58680
|
this.viewer.removeEventListener("hide", this.updateSnapper);
|
|
58676
58681
|
this.viewer.removeEventListener("isolate", this.updateSnapper);
|
|
58682
|
+
this.viewer.removeEventListener("show", this.updateSnapper);
|
|
58677
58683
|
this.viewer.removeEventListener("showall", this.updateSnapper);
|
|
58678
58684
|
this.overlay.detach();
|
|
58679
58685
|
this.overlay.dispose();
|
|
@@ -59384,8 +59390,6 @@ void main() {
|
|
|
59384
59390
|
///////////////////////////////////////////////////////////////////////////////
|
|
59385
59391
|
function clearSelected(viewer) {
|
|
59386
59392
|
const selection = viewer.getComponent("SelectionComponent");
|
|
59387
|
-
if (!selection)
|
|
59388
|
-
return;
|
|
59389
59393
|
selection.clearSelection();
|
|
59390
59394
|
viewer.update();
|
|
59391
59395
|
viewer.emitEvent({ type: "select", data: undefined, handles: [] });
|
|
@@ -59466,44 +59470,8 @@ void main() {
|
|
|
59466
59470
|
// By use of this software, its documentation or related materials, you
|
|
59467
59471
|
// acknowledge and accept the above terms.
|
|
59468
59472
|
///////////////////////////////////////////////////////////////////////////////
|
|
59469
|
-
function calcExplodeDepth(object, depth) {
|
|
59470
|
-
let res = depth;
|
|
59471
|
-
object.children.forEach((x) => {
|
|
59472
|
-
const objectDepth = calcExplodeDepth(x, depth + 1);
|
|
59473
|
-
if (res < objectDepth)
|
|
59474
|
-
res = objectDepth;
|
|
59475
|
-
});
|
|
59476
|
-
object.originalPosition = object.position.clone();
|
|
59477
|
-
object.originalCenter = new Box3().setFromObject(object).getCenter(new Vector3());
|
|
59478
|
-
object.isExplodeLocked = depth > 2 && object.children.length === 0;
|
|
59479
|
-
return res;
|
|
59480
|
-
}
|
|
59481
|
-
function explodeModel(scene, scale = 0, coeff = 4) {
|
|
59482
|
-
scale /= 100;
|
|
59483
|
-
if (!scene.explodeDepth)
|
|
59484
|
-
scene.explodeDepth = calcExplodeDepth(scene, 1);
|
|
59485
|
-
const maxDepth = scene.explodeDepth;
|
|
59486
|
-
const scaledExplodeDepth = scale * maxDepth + 1;
|
|
59487
|
-
const explodeDepth = 0 | scaledExplodeDepth;
|
|
59488
|
-
const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
|
|
59489
|
-
function explodeObject(object, depth) {
|
|
59490
|
-
object.position.copy(object.originalPosition);
|
|
59491
|
-
if (depth > 0 && depth <= explodeDepth && !object.isExplodeLocked) {
|
|
59492
|
-
let objectScale = scale * coeff;
|
|
59493
|
-
if (depth === explodeDepth)
|
|
59494
|
-
objectScale *= currentSegmentFraction;
|
|
59495
|
-
const parentCenter = object.parent.originalCenter;
|
|
59496
|
-
const objectCenter = object.originalCenter;
|
|
59497
|
-
const objectOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
|
|
59498
|
-
object.position.add(objectOffset);
|
|
59499
|
-
}
|
|
59500
|
-
object.children.forEach((x) => explodeObject(x, depth + 1));
|
|
59501
|
-
}
|
|
59502
|
-
explodeObject(scene, 0);
|
|
59503
|
-
}
|
|
59504
59473
|
function explode(viewer, index = 0) {
|
|
59505
|
-
viewer.models.forEach((model) =>
|
|
59506
|
-
viewer.scene.updateMatrixWorld();
|
|
59474
|
+
viewer.models.forEach((model) => model.explode(index));
|
|
59507
59475
|
viewer.update();
|
|
59508
59476
|
viewer.emitEvent({ type: "explode", data: index });
|
|
59509
59477
|
}
|
|
@@ -59664,7 +59632,7 @@ void main() {
|
|
|
59664
59632
|
// acknowledge and accept the above terms.
|
|
59665
59633
|
///////////////////////////////////////////////////////////////////////////////
|
|
59666
59634
|
function getModels(viewer) {
|
|
59667
|
-
return viewer.models.map((model) =>
|
|
59635
|
+
return viewer.models.map((model) => model.handle);
|
|
59668
59636
|
}
|
|
59669
59637
|
|
|
59670
59638
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -59690,108 +59658,9 @@ void main() {
|
|
|
59690
59658
|
// acknowledge and accept the above terms.
|
|
59691
59659
|
///////////////////////////////////////////////////////////////////////////////
|
|
59692
59660
|
function getSelected(viewer) {
|
|
59693
|
-
|
|
59694
|
-
|
|
59695
|
-
|
|
59696
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
59697
|
-
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
|
|
59698
|
-
// All rights reserved.
|
|
59699
|
-
//
|
|
59700
|
-
// This software and its documentation and related materials are owned by
|
|
59701
|
-
// the Alliance. The software may only be incorporated into application
|
|
59702
|
-
// programs owned by members of the Alliance, subject to a signed
|
|
59703
|
-
// Membership Agreement and Supplemental Software License Agreement with the
|
|
59704
|
-
// Alliance. The structure and organization of this software are the valuable
|
|
59705
|
-
// trade secrets of the Alliance and its suppliers. The software is also
|
|
59706
|
-
// protected by copyright law and international treaty provisions. Application
|
|
59707
|
-
// programs incorporating this software must include the following statement
|
|
59708
|
-
// with their copyright notices:
|
|
59709
|
-
//
|
|
59710
|
-
// This application incorporates Open Design Alliance software pursuant to a
|
|
59711
|
-
// license agreement with Open Design Alliance.
|
|
59712
|
-
// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
|
|
59713
|
-
// All rights reserved.
|
|
59714
|
-
//
|
|
59715
|
-
// By use of this software, its documentation or related materials, you
|
|
59716
|
-
// acknowledge and accept the above terms.
|
|
59717
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
59718
|
-
class SelectionComponent {
|
|
59719
|
-
constructor(viewer) {
|
|
59720
|
-
this.onPointerDown = (event) => {
|
|
59721
|
-
if (!event.isPrimary || event.button !== 0)
|
|
59722
|
-
return;
|
|
59723
|
-
this.getMousePosition(event, this.downPosition);
|
|
59724
|
-
};
|
|
59725
|
-
this.onPointerUp = (event) => {
|
|
59726
|
-
if (!event.isPrimary)
|
|
59727
|
-
return;
|
|
59728
|
-
const upPosition = this.getMousePosition(event, new Vector2());
|
|
59729
|
-
if (this.downPosition.distanceTo(upPosition) !== 0)
|
|
59730
|
-
return;
|
|
59731
|
-
const intersects = this.getPointerIntersects(upPosition);
|
|
59732
|
-
this.clearSelection();
|
|
59733
|
-
if (intersects.length > 0)
|
|
59734
|
-
this.select(intersects[0].object);
|
|
59735
|
-
this.viewer.update();
|
|
59736
|
-
this.viewer.emitEvent({ type: "select", data: undefined, handles: this.viewer.getSelected() });
|
|
59737
|
-
};
|
|
59738
|
-
this.onDoubleClick = (event) => {
|
|
59739
|
-
if (event.button !== 0)
|
|
59740
|
-
return;
|
|
59741
|
-
this.viewer.executeCommand("zoomToSelected");
|
|
59742
|
-
};
|
|
59743
|
-
this.initHighlighter = () => {
|
|
59744
|
-
this.highlighter = this.viewer.getComponent("HighlighterComponent");
|
|
59745
|
-
};
|
|
59746
|
-
this.viewer = viewer;
|
|
59747
|
-
this.raycaster = new Raycaster();
|
|
59748
|
-
this.downPosition = new Vector2();
|
|
59749
|
-
this.viewer.addEventListener("pointerdown", this.onPointerDown);
|
|
59750
|
-
this.viewer.addEventListener("pointerup", this.onPointerUp);
|
|
59751
|
-
this.viewer.addEventListener("dblclick", this.onDoubleClick);
|
|
59752
|
-
this.viewer.addEventListener("initialize", this.initHighlighter);
|
|
59753
|
-
}
|
|
59754
|
-
dispose() {
|
|
59755
|
-
this.viewer.removeEventListener("pointerdown", this.onPointerDown);
|
|
59756
|
-
this.viewer.removeEventListener("pointerup", this.onPointerUp);
|
|
59757
|
-
this.viewer.removeEventListener("dblclick", this.onDoubleClick);
|
|
59758
|
-
this.viewer.removeEventListener("initialize", this.initHighlighter);
|
|
59759
|
-
}
|
|
59760
|
-
getMousePosition(event, target) {
|
|
59761
|
-
return target.set(event.clientX, event.clientY);
|
|
59762
|
-
}
|
|
59763
|
-
getPointerIntersects(mouse) {
|
|
59764
|
-
const rect = this.viewer.canvas.getBoundingClientRect();
|
|
59765
|
-
const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
|
|
59766
|
-
const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
|
|
59767
|
-
const coords = new Vector2(x, y);
|
|
59768
|
-
this.raycaster.setFromCamera(coords, this.viewer.camera);
|
|
59769
|
-
const objects = [];
|
|
59770
|
-
this.viewer.scene.traverseVisible((child) => objects.push(child));
|
|
59771
|
-
this.raycaster.params = this.raycaster.params = {
|
|
59772
|
-
Mesh: {},
|
|
59773
|
-
Line: { threshold: 0.25 },
|
|
59774
|
-
Line2: { threshold: 0.25 },
|
|
59775
|
-
LOD: {},
|
|
59776
|
-
Points: { threshold: 0.1 },
|
|
59777
|
-
Sprite: {},
|
|
59778
|
-
};
|
|
59779
|
-
return this.raycaster.intersectObjects(objects, false);
|
|
59780
|
-
}
|
|
59781
|
-
select(object) {
|
|
59782
|
-
if (object.isSelected)
|
|
59783
|
-
return;
|
|
59784
|
-
object.isSelected = true;
|
|
59785
|
-
this.highlighter.highlight(object);
|
|
59786
|
-
this.viewer.selected.push(object);
|
|
59787
|
-
}
|
|
59788
|
-
clearSelection() {
|
|
59789
|
-
this.viewer.selected.forEach((object) => {
|
|
59790
|
-
object.isSelected = false;
|
|
59791
|
-
this.highlighter.unhighlight(object);
|
|
59792
|
-
});
|
|
59793
|
-
this.viewer.selected.length = 0;
|
|
59794
|
-
}
|
|
59661
|
+
const handles = [];
|
|
59662
|
+
viewer.models.forEach((model) => handles.push(...model.getHandlesByObjects(viewer.selected)));
|
|
59663
|
+
return handles;
|
|
59795
59664
|
}
|
|
59796
59665
|
|
|
59797
59666
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -59817,10 +59686,9 @@ void main() {
|
|
|
59817
59686
|
// acknowledge and accept the above terms.
|
|
59818
59687
|
///////////////////////////////////////////////////////////////////////////////
|
|
59819
59688
|
function hideSelected(viewer) {
|
|
59820
|
-
viewer.
|
|
59821
|
-
const selection =
|
|
59689
|
+
viewer.models.forEach((model) => model.hideObjects(viewer.selected));
|
|
59690
|
+
const selection = viewer.getComponent("SelectionComponent");
|
|
59822
59691
|
selection.clearSelection();
|
|
59823
|
-
selection.dispose();
|
|
59824
59692
|
viewer.update();
|
|
59825
59693
|
viewer.emitEvent({ type: "hide" });
|
|
59826
59694
|
viewer.emitEvent({ type: "select", data: undefined, handles: [] });
|
|
@@ -59849,12 +59717,7 @@ void main() {
|
|
|
59849
59717
|
// acknowledge and accept the above terms.
|
|
59850
59718
|
///////////////////////////////////////////////////////////////////////////////
|
|
59851
59719
|
function isolateSelected(viewer) {
|
|
59852
|
-
|
|
59853
|
-
viewer.selected.forEach((object) => {
|
|
59854
|
-
visibleSet.add(object);
|
|
59855
|
-
object.traverseAncestors((object2) => visibleSet.add(object2));
|
|
59856
|
-
});
|
|
59857
|
-
viewer.scene.traverse((object) => (object.visible = visibleSet.has(object)));
|
|
59720
|
+
viewer.models.forEach((model) => model.isolateObjects(viewer.selected));
|
|
59858
59721
|
viewer.update();
|
|
59859
59722
|
viewer.emitEvent({ type: "isolate" });
|
|
59860
59723
|
}
|
|
@@ -59943,7 +59806,12 @@ void main() {
|
|
|
59943
59806
|
// acknowledge and accept the above terms.
|
|
59944
59807
|
///////////////////////////////////////////////////////////////////////////////
|
|
59945
59808
|
function selectModel(viewer, handle) {
|
|
59946
|
-
|
|
59809
|
+
const selection = viewer.getComponent("SelectionComponent");
|
|
59810
|
+
selection.clearSelection();
|
|
59811
|
+
viewer.models
|
|
59812
|
+
.filter((model) => model.handle === handle)
|
|
59813
|
+
.forEach((model) => selection.select(model.getObjects(), model));
|
|
59814
|
+
viewer.update();
|
|
59947
59815
|
viewer.emit({ type: "select", data: [] });
|
|
59948
59816
|
}
|
|
59949
59817
|
|
|
@@ -60022,19 +59890,14 @@ void main() {
|
|
|
60022
59890
|
// acknowledge and accept the above terms.
|
|
60023
59891
|
///////////////////////////////////////////////////////////////////////////////
|
|
60024
59892
|
function setSelected(viewer, handles = []) {
|
|
60025
|
-
const handleSet = new Set(handles);
|
|
60026
|
-
const objects = [];
|
|
60027
|
-
viewer.scene.traverseVisible((child) => {
|
|
60028
|
-
var _a;
|
|
60029
|
-
if (handleSet.has((_a = child.userData) === null || _a === undefined ? undefined : _a.handle))
|
|
60030
|
-
objects.push(child);
|
|
60031
|
-
});
|
|
60032
59893
|
const selection = viewer.getComponent("SelectionComponent");
|
|
60033
|
-
if (!selection)
|
|
60034
|
-
return;
|
|
60035
59894
|
selection.clearSelection();
|
|
60036
|
-
|
|
59895
|
+
viewer.models.forEach((model) => {
|
|
59896
|
+
const objects = model.getObjectsByHandles(handles);
|
|
59897
|
+
selection.select(objects, model);
|
|
59898
|
+
});
|
|
60037
59899
|
viewer.update();
|
|
59900
|
+
viewer.emitEvent({ type: "show" });
|
|
60038
59901
|
viewer.emitEvent({ type: "select", data: undefined, handles });
|
|
60039
59902
|
}
|
|
60040
59903
|
|
|
@@ -60061,7 +59924,7 @@ void main() {
|
|
|
60061
59924
|
// acknowledge and accept the above terms.
|
|
60062
59925
|
///////////////////////////////////////////////////////////////////////////////
|
|
60063
59926
|
function showAll(viewer) {
|
|
60064
|
-
viewer.
|
|
59927
|
+
viewer.models.forEach((model) => model.showAllObjects());
|
|
60065
59928
|
viewer.update();
|
|
60066
59929
|
viewer.emitEvent({ type: "showall" });
|
|
60067
59930
|
}
|
|
@@ -60280,6 +60143,7 @@ void main() {
|
|
|
60280
60143
|
constructor(viewer) {
|
|
60281
60144
|
this.syncOptions = () => {
|
|
60282
60145
|
this.backgroundColor.setHex(0xffffff);
|
|
60146
|
+
this.viewer.renderer.setClearColor(this.backgroundColor);
|
|
60283
60147
|
};
|
|
60284
60148
|
this.viewer = viewer;
|
|
60285
60149
|
this.backgroundColor = new Color(0xffffff);
|
|
@@ -60389,23 +60253,25 @@ void main() {
|
|
|
60389
60253
|
constructor(viewer) {
|
|
60390
60254
|
this.syncExtents = () => {
|
|
60391
60255
|
const extents = new Box3();
|
|
60392
|
-
this.viewer.
|
|
60256
|
+
this.viewer.models.forEach((model) => model.getExtents(extents));
|
|
60393
60257
|
this.viewer.extents.copy(extents);
|
|
60394
60258
|
};
|
|
60395
60259
|
this.viewer = viewer;
|
|
60396
60260
|
this.viewer.addEventListener("databasechunk", this.syncExtents);
|
|
60397
60261
|
this.viewer.addEventListener("clear", this.syncExtents);
|
|
60398
60262
|
this.viewer.on("explode", this.syncExtents);
|
|
60399
|
-
this.viewer.on("isolate", this.syncExtents);
|
|
60400
60263
|
this.viewer.on("hide", this.syncExtents);
|
|
60264
|
+
this.viewer.on("isolate", this.syncExtents);
|
|
60265
|
+
this.viewer.on("show", this.syncExtents);
|
|
60401
60266
|
this.viewer.on("showall", this.syncExtents);
|
|
60402
60267
|
}
|
|
60403
60268
|
dispose() {
|
|
60404
60269
|
this.viewer.removeEventListener("databasechunk", this.syncExtents);
|
|
60405
60270
|
this.viewer.removeEventListener("clear", this.syncExtents);
|
|
60406
60271
|
this.viewer.off("explode", this.syncExtents);
|
|
60407
|
-
this.viewer.off("isolate", this.syncExtents);
|
|
60408
60272
|
this.viewer.off("hide", this.syncExtents);
|
|
60273
|
+
this.viewer.off("isolate", this.syncExtents);
|
|
60274
|
+
this.viewer.off("show", this.syncExtents);
|
|
60409
60275
|
this.viewer.off("showall", this.syncExtents);
|
|
60410
60276
|
}
|
|
60411
60277
|
}
|
|
@@ -61605,6 +61471,16 @@ void main() {
|
|
|
61605
61471
|
this.viewer.update();
|
|
61606
61472
|
};
|
|
61607
61473
|
this.viewer = viewer;
|
|
61474
|
+
const gl2 = viewer.canvas.getContext("webgl2");
|
|
61475
|
+
if (gl2) {
|
|
61476
|
+
const size = viewer.renderer.getSize(new Vector2());
|
|
61477
|
+
this.renderTarget = new WebGLRenderTarget(size.x, size.y, {
|
|
61478
|
+
format: RGBAFormat,
|
|
61479
|
+
stencilBuffer: false,
|
|
61480
|
+
samples: 4,
|
|
61481
|
+
type: UnsignedByteType,
|
|
61482
|
+
});
|
|
61483
|
+
}
|
|
61608
61484
|
this.viewer.addEventListener("databasechunk", this.geometryEnd);
|
|
61609
61485
|
this.viewer.addEventListener("optionschange", this.optionsChange);
|
|
61610
61486
|
this.viewer.addEventListener("resize", this.viewerResize);
|
|
@@ -61615,52 +61491,210 @@ void main() {
|
|
|
61615
61491
|
this.viewer.removeEventListener("optionschange", this.optionsChange);
|
|
61616
61492
|
this.viewer.removeEventListener("resize", this.viewerResize);
|
|
61617
61493
|
}
|
|
61618
|
-
highlight(
|
|
61619
|
-
if (
|
|
61494
|
+
highlight(objects) {
|
|
61495
|
+
if (!Array.isArray(objects))
|
|
61496
|
+
objects = [objects];
|
|
61497
|
+
if (!objects.length)
|
|
61498
|
+
return;
|
|
61499
|
+
objects.forEach((object) => {
|
|
61500
|
+
if (object.isHighlighted)
|
|
61501
|
+
return;
|
|
61502
|
+
if (object.isLine || object.isLineSegments) {
|
|
61503
|
+
const positions = object.geometry.attributes.position.array;
|
|
61504
|
+
const indices = object.geometry.index ? object.geometry.index.array : null;
|
|
61505
|
+
const lineGeometry = indices
|
|
61506
|
+
? HighlighterUtils.fromIndexedLine(positions, indices)
|
|
61507
|
+
: HighlighterUtils.fromNonIndexedLine(positions, object.isLineSegments);
|
|
61508
|
+
const wireframe = new Wireframe(lineGeometry, this.highlightLineGlowMaterial);
|
|
61509
|
+
wireframe.position.copy(object.position);
|
|
61510
|
+
wireframe.rotation.copy(object.rotation);
|
|
61511
|
+
wireframe.scale.copy(object.scale);
|
|
61512
|
+
object.parent.add(wireframe);
|
|
61513
|
+
object.userData.highlightwireframe = wireframe;
|
|
61514
|
+
object.userData.originalMaterial = object.material;
|
|
61515
|
+
object.material = this.highlightLineMaterial;
|
|
61516
|
+
object.isHighlighted = true;
|
|
61517
|
+
}
|
|
61518
|
+
else if (object.isMesh) {
|
|
61519
|
+
const edgesGeometry = new EdgesGeometry(object.geometry, 30);
|
|
61520
|
+
const lineGeometry = new LineSegmentsGeometry().fromEdgesGeometry(edgesGeometry);
|
|
61521
|
+
const wireframe = new Wireframe(lineGeometry, this.outlineMaterial);
|
|
61522
|
+
wireframe.position.copy(object.position);
|
|
61523
|
+
wireframe.rotation.copy(object.rotation);
|
|
61524
|
+
wireframe.scale.copy(object.scale);
|
|
61525
|
+
object.parent.add(wireframe);
|
|
61526
|
+
object.userData.highlightwireframe = wireframe;
|
|
61527
|
+
object.userData.originalMaterial = object.material;
|
|
61528
|
+
object.material = this.highlightMaterial;
|
|
61529
|
+
object.isHighlighted = true;
|
|
61530
|
+
}
|
|
61531
|
+
});
|
|
61532
|
+
}
|
|
61533
|
+
unhighlight(objects) {
|
|
61534
|
+
if (!Array.isArray(objects))
|
|
61535
|
+
objects = [objects];
|
|
61536
|
+
if (!objects.length)
|
|
61537
|
+
return;
|
|
61538
|
+
objects.forEach((object) => {
|
|
61539
|
+
if (!object.isHighlighted)
|
|
61540
|
+
return;
|
|
61541
|
+
object.isHighlighted = false;
|
|
61542
|
+
object.material = object.userData.originalMaterial;
|
|
61543
|
+
object.userData.highlightwireframe.removeFromParent();
|
|
61544
|
+
delete object.userData.originalMaterial;
|
|
61545
|
+
delete object.userData.highlightwireframe;
|
|
61546
|
+
});
|
|
61547
|
+
}
|
|
61548
|
+
viewerResize(event) {
|
|
61549
|
+
var _a, _b;
|
|
61550
|
+
(_a = this.renderTarget) === null || _a === undefined ? undefined : _a.setSize(event.width, event.height);
|
|
61551
|
+
(_b = this.outlineMaterial) === null || _b === undefined ? undefined : _b.resolution.set(event.width, event.height);
|
|
61552
|
+
}
|
|
61553
|
+
}
|
|
61554
|
+
|
|
61555
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
61556
|
+
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
|
|
61557
|
+
// All rights reserved.
|
|
61558
|
+
//
|
|
61559
|
+
// This software and its documentation and related materials are owned by
|
|
61560
|
+
// the Alliance. The software may only be incorporated into application
|
|
61561
|
+
// programs owned by members of the Alliance, subject to a signed
|
|
61562
|
+
// Membership Agreement and Supplemental Software License Agreement with the
|
|
61563
|
+
// Alliance. The structure and organization of this software are the valuable
|
|
61564
|
+
// trade secrets of the Alliance and its suppliers. The software is also
|
|
61565
|
+
// protected by copyright law and international treaty provisions. Application
|
|
61566
|
+
// programs incorporating this software must include the following statement
|
|
61567
|
+
// with their copyright notices:
|
|
61568
|
+
//
|
|
61569
|
+
// This application incorporates Open Design Alliance software pursuant to a
|
|
61570
|
+
// license agreement with Open Design Alliance.
|
|
61571
|
+
// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
|
|
61572
|
+
// All rights reserved.
|
|
61573
|
+
//
|
|
61574
|
+
// By use of this software, its documentation or related materials, you
|
|
61575
|
+
// acknowledge and accept the above terms.
|
|
61576
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
61577
|
+
class SelectionComponent {
|
|
61578
|
+
constructor(viewer) {
|
|
61579
|
+
this.onPointerDown = (event) => {
|
|
61580
|
+
if (!event.isPrimary || event.button !== 0)
|
|
61581
|
+
return;
|
|
61582
|
+
this.getMousePosition(event, this.downPosition);
|
|
61583
|
+
};
|
|
61584
|
+
this.onPointerUp = (event) => {
|
|
61585
|
+
if (!event.isPrimary)
|
|
61586
|
+
return;
|
|
61587
|
+
const upPosition = this.getMousePosition(event, new Vector2());
|
|
61588
|
+
if (upPosition.distanceTo(this.downPosition) !== 0)
|
|
61589
|
+
return;
|
|
61590
|
+
let intersections = [];
|
|
61591
|
+
this.viewer.models.forEach((model) => {
|
|
61592
|
+
const objects = model.getVisibleObjects();
|
|
61593
|
+
const intersects = this.getPointerIntersects(upPosition, objects);
|
|
61594
|
+
intersections.push(...intersects.map((x) => ({ ...x, model })));
|
|
61595
|
+
});
|
|
61596
|
+
intersections = intersections.sort((a, b) => a.distance - b.distance);
|
|
61597
|
+
if (!event.shiftKey)
|
|
61598
|
+
this.clearSelection();
|
|
61599
|
+
if (intersections.length > 0) {
|
|
61600
|
+
const model = intersections[0].model;
|
|
61601
|
+
const handles = model.getHandlesByObjects(intersections[0].object);
|
|
61602
|
+
const objects = model.getObjectsByHandles(handles);
|
|
61603
|
+
if (!event.shiftKey)
|
|
61604
|
+
this.select(objects, model);
|
|
61605
|
+
else
|
|
61606
|
+
this.toggleSelection(objects, model);
|
|
61607
|
+
}
|
|
61608
|
+
this.viewer.update();
|
|
61609
|
+
this.viewer.emitEvent({ type: "select", data: undefined, handles: this.viewer.getSelected() });
|
|
61610
|
+
};
|
|
61611
|
+
this.onDoubleClick = (event) => {
|
|
61612
|
+
if (event.button !== 0)
|
|
61613
|
+
return;
|
|
61614
|
+
this.viewer.executeCommand("zoomToSelected");
|
|
61615
|
+
};
|
|
61616
|
+
this.initHighlighter = () => {
|
|
61617
|
+
this.highlighter = this.viewer.getComponent("HighlighterComponent");
|
|
61618
|
+
};
|
|
61619
|
+
this.viewer = viewer;
|
|
61620
|
+
this.raycaster = new Raycaster();
|
|
61621
|
+
this.downPosition = new Vector2();
|
|
61622
|
+
this.viewer.addEventListener("pointerdown", this.onPointerDown);
|
|
61623
|
+
this.viewer.addEventListener("pointerup", this.onPointerUp);
|
|
61624
|
+
this.viewer.addEventListener("dblclick", this.onDoubleClick);
|
|
61625
|
+
this.viewer.addEventListener("initialize", this.initHighlighter);
|
|
61626
|
+
}
|
|
61627
|
+
dispose() {
|
|
61628
|
+
this.viewer.removeEventListener("pointerdown", this.onPointerDown);
|
|
61629
|
+
this.viewer.removeEventListener("pointerup", this.onPointerUp);
|
|
61630
|
+
this.viewer.removeEventListener("dblclick", this.onDoubleClick);
|
|
61631
|
+
this.viewer.removeEventListener("initialize", this.initHighlighter);
|
|
61632
|
+
}
|
|
61633
|
+
getMousePosition(event, target) {
|
|
61634
|
+
return target.set(event.clientX, event.clientY);
|
|
61635
|
+
}
|
|
61636
|
+
getPointerIntersects(mouse, objects) {
|
|
61637
|
+
const rect = this.viewer.canvas.getBoundingClientRect();
|
|
61638
|
+
const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
|
|
61639
|
+
const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
|
|
61640
|
+
const coords = new Vector2(x, y);
|
|
61641
|
+
this.raycaster.setFromCamera(coords, this.viewer.camera);
|
|
61642
|
+
this.raycaster.params = this.raycaster.params = {
|
|
61643
|
+
Mesh: {},
|
|
61644
|
+
Line: { threshold: 0.25 },
|
|
61645
|
+
Line2: { threshold: 0.25 },
|
|
61646
|
+
LOD: {},
|
|
61647
|
+
Points: { threshold: 0.1 },
|
|
61648
|
+
Sprite: {},
|
|
61649
|
+
};
|
|
61650
|
+
return this.raycaster.intersectObjects(objects, false);
|
|
61651
|
+
}
|
|
61652
|
+
select(objects, model) {
|
|
61653
|
+
if (!model) {
|
|
61654
|
+
this.viewer.models.forEach((model) => this.select(objects, model));
|
|
61620
61655
|
return;
|
|
61621
|
-
if (object.isLine || object.isLineSegments) {
|
|
61622
|
-
const positions = object.geometry.attributes.position.array;
|
|
61623
|
-
const indices = object.geometry.index ? object.geometry.index.array : null;
|
|
61624
|
-
const lineGeometry = indices
|
|
61625
|
-
? HighlighterUtils.fromIndexedLine(positions, indices)
|
|
61626
|
-
: HighlighterUtils.fromNonIndexedLine(positions, object.isLineSegments);
|
|
61627
|
-
const wireframe = new Wireframe(lineGeometry, this.highlightLineGlowMaterial);
|
|
61628
|
-
wireframe.position.copy(object.position);
|
|
61629
|
-
wireframe.rotation.copy(object.rotation);
|
|
61630
|
-
wireframe.scale.copy(object.scale);
|
|
61631
|
-
object.parent.add(wireframe);
|
|
61632
|
-
object.userData.highlightwireframe = wireframe;
|
|
61633
|
-
object.userData.originalMaterial = object.material;
|
|
61634
|
-
object.material = this.highlightLineMaterial;
|
|
61635
|
-
object.isHighlighted = true;
|
|
61636
61656
|
}
|
|
61637
|
-
|
|
61638
|
-
|
|
61639
|
-
|
|
61640
|
-
|
|
61641
|
-
|
|
61642
|
-
|
|
61643
|
-
|
|
61644
|
-
|
|
61645
|
-
|
|
61646
|
-
|
|
61647
|
-
|
|
61648
|
-
|
|
61657
|
+
if (!Array.isArray(objects))
|
|
61658
|
+
objects = [objects];
|
|
61659
|
+
if (!objects.length)
|
|
61660
|
+
return;
|
|
61661
|
+
model.showObjects(objects);
|
|
61662
|
+
model.showOriginalObjects(objects);
|
|
61663
|
+
this.highlighter.highlight(objects);
|
|
61664
|
+
objects.forEach((object) => this.viewer.selected.push(object));
|
|
61665
|
+
objects.forEach((object) => (object.isSelected = true));
|
|
61666
|
+
}
|
|
61667
|
+
deselect(objects, model) {
|
|
61668
|
+
if (!model) {
|
|
61669
|
+
this.viewer.models.forEach((model) => this.select(objects, model));
|
|
61670
|
+
return;
|
|
61649
61671
|
}
|
|
61672
|
+
if (!Array.isArray(objects))
|
|
61673
|
+
objects = [objects];
|
|
61674
|
+
if (!objects.length)
|
|
61675
|
+
return;
|
|
61676
|
+
this.highlighter.unhighlight(objects);
|
|
61677
|
+
model.hideOriginalObjects(objects);
|
|
61678
|
+
this.viewer.selected = this.viewer.selected.filter((x) => !objects.includes(x));
|
|
61679
|
+
objects.forEach((object) => (object.isSelected = false));
|
|
61650
61680
|
}
|
|
61651
|
-
|
|
61652
|
-
if (!
|
|
61681
|
+
toggleSelection(objects, model) {
|
|
61682
|
+
if (!Array.isArray(objects))
|
|
61683
|
+
objects = [objects];
|
|
61684
|
+
if (!objects.length)
|
|
61653
61685
|
return;
|
|
61654
|
-
|
|
61655
|
-
|
|
61656
|
-
|
|
61657
|
-
|
|
61658
|
-
delete object.userData.highlightwireframe;
|
|
61686
|
+
if (objects[0].isSelected)
|
|
61687
|
+
this.deselect(objects, model);
|
|
61688
|
+
else
|
|
61689
|
+
this.select(objects, model);
|
|
61659
61690
|
}
|
|
61660
|
-
|
|
61661
|
-
if (!this.
|
|
61691
|
+
clearSelection() {
|
|
61692
|
+
if (!this.viewer.selected.length)
|
|
61662
61693
|
return;
|
|
61663
|
-
this.
|
|
61694
|
+
this.highlighter.unhighlight(this.viewer.selected);
|
|
61695
|
+
this.viewer.models.forEach((model) => model.hideOriginalObjects(this.viewer.selected));
|
|
61696
|
+
this.viewer.selected.forEach((object) => (object.isSelected = false));
|
|
61697
|
+
this.viewer.selected.length = 0;
|
|
61664
61698
|
}
|
|
61665
61699
|
}
|
|
61666
61700
|
|
|
@@ -61869,73 +61903,362 @@ void main() {
|
|
|
61869
61903
|
components.registerComponent("WCSHelperComponent", (viewer) => new WCSHelperComponent(viewer));
|
|
61870
61904
|
|
|
61871
61905
|
/**
|
|
61872
|
-
* @param
|
|
61873
|
-
* @param
|
|
61906
|
+
* @param {Array<BufferGeometry>} geometries
|
|
61907
|
+
* @param {Boolean} useGroups
|
|
61874
61908
|
* @return {BufferGeometry}
|
|
61875
61909
|
*/
|
|
61876
|
-
function
|
|
61910
|
+
function mergeGeometries( geometries, useGroups = false ) {
|
|
61877
61911
|
|
|
61878
|
-
|
|
61912
|
+
const isIndexed = geometries[ 0 ].index !== null;
|
|
61879
61913
|
|
|
61880
|
-
|
|
61881
|
-
|
|
61914
|
+
const attributesUsed = new Set( Object.keys( geometries[ 0 ].attributes ) );
|
|
61915
|
+
const morphAttributesUsed = new Set( Object.keys( geometries[ 0 ].morphAttributes ) );
|
|
61882
61916
|
|
|
61883
|
-
}
|
|
61917
|
+
const attributes = {};
|
|
61918
|
+
const morphAttributes = {};
|
|
61884
61919
|
|
|
61885
|
-
|
|
61920
|
+
const morphTargetsRelative = geometries[ 0 ].morphTargetsRelative;
|
|
61886
61921
|
|
|
61887
|
-
|
|
61922
|
+
const mergedGeometry = new BufferGeometry();
|
|
61888
61923
|
|
|
61889
|
-
|
|
61924
|
+
let offset = 0;
|
|
61890
61925
|
|
|
61891
|
-
|
|
61926
|
+
for ( let i = 0; i < geometries.length; ++ i ) {
|
|
61892
61927
|
|
|
61893
|
-
|
|
61928
|
+
const geometry = geometries[ i ];
|
|
61929
|
+
let attributesCount = 0;
|
|
61894
61930
|
|
|
61895
|
-
|
|
61931
|
+
// ensure that all geometries are indexed, or none
|
|
61896
61932
|
|
|
61897
|
-
|
|
61933
|
+
if ( isIndexed !== ( geometry.index !== null ) ) {
|
|
61898
61934
|
|
|
61899
|
-
|
|
61935
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.' );
|
|
61936
|
+
return null;
|
|
61900
61937
|
|
|
61901
|
-
|
|
61938
|
+
}
|
|
61902
61939
|
|
|
61903
|
-
|
|
61940
|
+
// gather attributes, exit early if they're different
|
|
61904
61941
|
|
|
61905
|
-
|
|
61906
|
-
index = geometry.getIndex();
|
|
61942
|
+
for ( const name in geometry.attributes ) {
|
|
61907
61943
|
|
|
61908
|
-
|
|
61944
|
+
if ( ! attributesUsed.has( name ) ) {
|
|
61909
61945
|
|
|
61910
|
-
console.error( 'THREE.BufferGeometryUtils.
|
|
61911
|
-
return
|
|
61946
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure "' + name + '" attribute exists among all geometries, or in none of them.' );
|
|
61947
|
+
return null;
|
|
61912
61948
|
|
|
61913
61949
|
}
|
|
61914
61950
|
|
|
61915
|
-
|
|
61951
|
+
if ( attributes[ name ] === undefined ) attributes[ name ] = [];
|
|
61916
61952
|
|
|
61917
|
-
|
|
61953
|
+
attributes[ name ].push( geometry.attributes[ name ] );
|
|
61918
61954
|
|
|
61919
|
-
|
|
61920
|
-
const newIndices = [];
|
|
61955
|
+
attributesCount ++;
|
|
61921
61956
|
|
|
61922
|
-
|
|
61957
|
+
}
|
|
61923
61958
|
|
|
61924
|
-
|
|
61959
|
+
// ensure geometries have the same number of attributes
|
|
61925
61960
|
|
|
61926
|
-
|
|
61961
|
+
if ( attributesCount !== attributesUsed.size ) {
|
|
61927
61962
|
|
|
61928
|
-
|
|
61929
|
-
|
|
61930
|
-
newIndices.push( index.getX( i + 1 ) );
|
|
61963
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. Make sure all geometries have the same number of attributes.' );
|
|
61964
|
+
return null;
|
|
61931
61965
|
|
|
61932
|
-
|
|
61966
|
+
}
|
|
61933
61967
|
|
|
61934
|
-
|
|
61968
|
+
// gather morph attributes, exit early if they're different
|
|
61935
61969
|
|
|
61936
|
-
|
|
61970
|
+
if ( morphTargetsRelative !== geometry.morphTargetsRelative ) {
|
|
61937
61971
|
|
|
61938
|
-
|
|
61972
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. .morphTargetsRelative must be consistent throughout all geometries.' );
|
|
61973
|
+
return null;
|
|
61974
|
+
|
|
61975
|
+
}
|
|
61976
|
+
|
|
61977
|
+
for ( const name in geometry.morphAttributes ) {
|
|
61978
|
+
|
|
61979
|
+
if ( ! morphAttributesUsed.has( name ) ) {
|
|
61980
|
+
|
|
61981
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. .morphAttributes must be consistent throughout all geometries.' );
|
|
61982
|
+
return null;
|
|
61983
|
+
|
|
61984
|
+
}
|
|
61985
|
+
|
|
61986
|
+
if ( morphAttributes[ name ] === undefined ) morphAttributes[ name ] = [];
|
|
61987
|
+
|
|
61988
|
+
morphAttributes[ name ].push( geometry.morphAttributes[ name ] );
|
|
61989
|
+
|
|
61990
|
+
}
|
|
61991
|
+
|
|
61992
|
+
if ( useGroups ) {
|
|
61993
|
+
|
|
61994
|
+
let count;
|
|
61995
|
+
|
|
61996
|
+
if ( isIndexed ) {
|
|
61997
|
+
|
|
61998
|
+
count = geometry.index.count;
|
|
61999
|
+
|
|
62000
|
+
} else if ( geometry.attributes.position !== undefined ) {
|
|
62001
|
+
|
|
62002
|
+
count = geometry.attributes.position.count;
|
|
62003
|
+
|
|
62004
|
+
} else {
|
|
62005
|
+
|
|
62006
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. The geometry must have either an index or a position attribute' );
|
|
62007
|
+
return null;
|
|
62008
|
+
|
|
62009
|
+
}
|
|
62010
|
+
|
|
62011
|
+
mergedGeometry.addGroup( offset, count, i );
|
|
62012
|
+
|
|
62013
|
+
offset += count;
|
|
62014
|
+
|
|
62015
|
+
}
|
|
62016
|
+
|
|
62017
|
+
}
|
|
62018
|
+
|
|
62019
|
+
// merge indices
|
|
62020
|
+
|
|
62021
|
+
if ( isIndexed ) {
|
|
62022
|
+
|
|
62023
|
+
let indexOffset = 0;
|
|
62024
|
+
const mergedIndex = [];
|
|
62025
|
+
|
|
62026
|
+
for ( let i = 0; i < geometries.length; ++ i ) {
|
|
62027
|
+
|
|
62028
|
+
const index = geometries[ i ].index;
|
|
62029
|
+
|
|
62030
|
+
for ( let j = 0; j < index.count; ++ j ) {
|
|
62031
|
+
|
|
62032
|
+
mergedIndex.push( index.getX( j ) + indexOffset );
|
|
62033
|
+
|
|
62034
|
+
}
|
|
62035
|
+
|
|
62036
|
+
indexOffset += geometries[ i ].attributes.position.count;
|
|
62037
|
+
|
|
62038
|
+
}
|
|
62039
|
+
|
|
62040
|
+
mergedGeometry.setIndex( mergedIndex );
|
|
62041
|
+
|
|
62042
|
+
}
|
|
62043
|
+
|
|
62044
|
+
// merge attributes
|
|
62045
|
+
|
|
62046
|
+
for ( const name in attributes ) {
|
|
62047
|
+
|
|
62048
|
+
const mergedAttribute = mergeAttributes( attributes[ name ] );
|
|
62049
|
+
|
|
62050
|
+
if ( ! mergedAttribute ) {
|
|
62051
|
+
|
|
62052
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the ' + name + ' attribute.' );
|
|
62053
|
+
return null;
|
|
62054
|
+
|
|
62055
|
+
}
|
|
62056
|
+
|
|
62057
|
+
mergedGeometry.setAttribute( name, mergedAttribute );
|
|
62058
|
+
|
|
62059
|
+
}
|
|
62060
|
+
|
|
62061
|
+
// merge morph attributes
|
|
62062
|
+
|
|
62063
|
+
for ( const name in morphAttributes ) {
|
|
62064
|
+
|
|
62065
|
+
const numMorphTargets = morphAttributes[ name ][ 0 ].length;
|
|
62066
|
+
|
|
62067
|
+
if ( numMorphTargets === 0 ) break;
|
|
62068
|
+
|
|
62069
|
+
mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {};
|
|
62070
|
+
mergedGeometry.morphAttributes[ name ] = [];
|
|
62071
|
+
|
|
62072
|
+
for ( let i = 0; i < numMorphTargets; ++ i ) {
|
|
62073
|
+
|
|
62074
|
+
const morphAttributesToMerge = [];
|
|
62075
|
+
|
|
62076
|
+
for ( let j = 0; j < morphAttributes[ name ].length; ++ j ) {
|
|
62077
|
+
|
|
62078
|
+
morphAttributesToMerge.push( morphAttributes[ name ][ j ][ i ] );
|
|
62079
|
+
|
|
62080
|
+
}
|
|
62081
|
+
|
|
62082
|
+
const mergedMorphAttribute = mergeAttributes( morphAttributesToMerge );
|
|
62083
|
+
|
|
62084
|
+
if ( ! mergedMorphAttribute ) {
|
|
62085
|
+
|
|
62086
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the ' + name + ' morphAttribute.' );
|
|
62087
|
+
return null;
|
|
62088
|
+
|
|
62089
|
+
}
|
|
62090
|
+
|
|
62091
|
+
mergedGeometry.morphAttributes[ name ].push( mergedMorphAttribute );
|
|
62092
|
+
|
|
62093
|
+
}
|
|
62094
|
+
|
|
62095
|
+
}
|
|
62096
|
+
|
|
62097
|
+
return mergedGeometry;
|
|
62098
|
+
|
|
62099
|
+
}
|
|
62100
|
+
|
|
62101
|
+
/**
|
|
62102
|
+
* @param {Array<BufferAttribute>} attributes
|
|
62103
|
+
* @return {BufferAttribute}
|
|
62104
|
+
*/
|
|
62105
|
+
function mergeAttributes( attributes ) {
|
|
62106
|
+
|
|
62107
|
+
let TypedArray;
|
|
62108
|
+
let itemSize;
|
|
62109
|
+
let normalized;
|
|
62110
|
+
let gpuType = -1;
|
|
62111
|
+
let arrayLength = 0;
|
|
62112
|
+
|
|
62113
|
+
for ( let i = 0; i < attributes.length; ++ i ) {
|
|
62114
|
+
|
|
62115
|
+
const attribute = attributes[ i ];
|
|
62116
|
+
|
|
62117
|
+
if ( TypedArray === undefined ) TypedArray = attribute.array.constructor;
|
|
62118
|
+
if ( TypedArray !== attribute.array.constructor ) {
|
|
62119
|
+
|
|
62120
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.array must be of consistent array types across matching attributes.' );
|
|
62121
|
+
return null;
|
|
62122
|
+
|
|
62123
|
+
}
|
|
62124
|
+
|
|
62125
|
+
if ( itemSize === undefined ) itemSize = attribute.itemSize;
|
|
62126
|
+
if ( itemSize !== attribute.itemSize ) {
|
|
62127
|
+
|
|
62128
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.itemSize must be consistent across matching attributes.' );
|
|
62129
|
+
return null;
|
|
62130
|
+
|
|
62131
|
+
}
|
|
62132
|
+
|
|
62133
|
+
if ( normalized === undefined ) normalized = attribute.normalized;
|
|
62134
|
+
if ( normalized !== attribute.normalized ) {
|
|
62135
|
+
|
|
62136
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.normalized must be consistent across matching attributes.' );
|
|
62137
|
+
return null;
|
|
62138
|
+
|
|
62139
|
+
}
|
|
62140
|
+
|
|
62141
|
+
if ( gpuType === -1 ) gpuType = attribute.gpuType;
|
|
62142
|
+
if ( gpuType !== attribute.gpuType ) {
|
|
62143
|
+
|
|
62144
|
+
console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.gpuType must be consistent across matching attributes.' );
|
|
62145
|
+
return null;
|
|
62146
|
+
|
|
62147
|
+
}
|
|
62148
|
+
|
|
62149
|
+
arrayLength += attribute.count * itemSize;
|
|
62150
|
+
|
|
62151
|
+
}
|
|
62152
|
+
|
|
62153
|
+
const array = new TypedArray( arrayLength );
|
|
62154
|
+
const result = new BufferAttribute( array, itemSize, normalized );
|
|
62155
|
+
let offset = 0;
|
|
62156
|
+
|
|
62157
|
+
for ( let i = 0; i < attributes.length; ++ i ) {
|
|
62158
|
+
|
|
62159
|
+
const attribute = attributes[ i ];
|
|
62160
|
+
if ( attribute.isInterleavedBufferAttribute ) {
|
|
62161
|
+
|
|
62162
|
+
const tupleOffset = offset / itemSize;
|
|
62163
|
+
for ( let j = 0, l = attribute.count; j < l; j ++ ) {
|
|
62164
|
+
|
|
62165
|
+
for ( let c = 0; c < itemSize; c ++ ) {
|
|
62166
|
+
|
|
62167
|
+
const value = attribute.getComponent( j, c );
|
|
62168
|
+
result.setComponent( j + tupleOffset, c, value );
|
|
62169
|
+
|
|
62170
|
+
}
|
|
62171
|
+
|
|
62172
|
+
}
|
|
62173
|
+
|
|
62174
|
+
} else {
|
|
62175
|
+
|
|
62176
|
+
array.set( attribute.array, offset );
|
|
62177
|
+
|
|
62178
|
+
}
|
|
62179
|
+
|
|
62180
|
+
offset += attribute.count * itemSize;
|
|
62181
|
+
|
|
62182
|
+
}
|
|
62183
|
+
|
|
62184
|
+
if ( gpuType !== undefined ) {
|
|
62185
|
+
|
|
62186
|
+
result.gpuType = gpuType;
|
|
62187
|
+
|
|
62188
|
+
}
|
|
62189
|
+
|
|
62190
|
+
return result;
|
|
62191
|
+
|
|
62192
|
+
}
|
|
62193
|
+
|
|
62194
|
+
/**
|
|
62195
|
+
* @param {BufferGeometry} geometry
|
|
62196
|
+
* @param {number} drawMode
|
|
62197
|
+
* @return {BufferGeometry}
|
|
62198
|
+
*/
|
|
62199
|
+
function toTrianglesDrawMode( geometry, drawMode ) {
|
|
62200
|
+
|
|
62201
|
+
if ( drawMode === TrianglesDrawMode ) {
|
|
62202
|
+
|
|
62203
|
+
console.warn( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.' );
|
|
62204
|
+
return geometry;
|
|
62205
|
+
|
|
62206
|
+
}
|
|
62207
|
+
|
|
62208
|
+
if ( drawMode === TriangleFanDrawMode || drawMode === TriangleStripDrawMode ) {
|
|
62209
|
+
|
|
62210
|
+
let index = geometry.getIndex();
|
|
62211
|
+
|
|
62212
|
+
// generate index if not present
|
|
62213
|
+
|
|
62214
|
+
if ( index === null ) {
|
|
62215
|
+
|
|
62216
|
+
const indices = [];
|
|
62217
|
+
|
|
62218
|
+
const position = geometry.getAttribute( 'position' );
|
|
62219
|
+
|
|
62220
|
+
if ( position !== undefined ) {
|
|
62221
|
+
|
|
62222
|
+
for ( let i = 0; i < position.count; i ++ ) {
|
|
62223
|
+
|
|
62224
|
+
indices.push( i );
|
|
62225
|
+
|
|
62226
|
+
}
|
|
62227
|
+
|
|
62228
|
+
geometry.setIndex( indices );
|
|
62229
|
+
index = geometry.getIndex();
|
|
62230
|
+
|
|
62231
|
+
} else {
|
|
62232
|
+
|
|
62233
|
+
console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' );
|
|
62234
|
+
return geometry;
|
|
62235
|
+
|
|
62236
|
+
}
|
|
62237
|
+
|
|
62238
|
+
}
|
|
62239
|
+
|
|
62240
|
+
//
|
|
62241
|
+
|
|
62242
|
+
const numberOfTriangles = index.count - 2;
|
|
62243
|
+
const newIndices = [];
|
|
62244
|
+
|
|
62245
|
+
if ( drawMode === TriangleFanDrawMode ) {
|
|
62246
|
+
|
|
62247
|
+
// gl.TRIANGLE_FAN
|
|
62248
|
+
|
|
62249
|
+
for ( let i = 1; i <= numberOfTriangles; i ++ ) {
|
|
62250
|
+
|
|
62251
|
+
newIndices.push( index.getX( 0 ) );
|
|
62252
|
+
newIndices.push( index.getX( i ) );
|
|
62253
|
+
newIndices.push( index.getX( i + 1 ) );
|
|
62254
|
+
|
|
62255
|
+
}
|
|
62256
|
+
|
|
62257
|
+
} else {
|
|
62258
|
+
|
|
62259
|
+
// gl.TRIANGLE_STRIP
|
|
62260
|
+
|
|
62261
|
+
for ( let i = 0; i < numberOfTriangles; i ++ ) {
|
|
61939
62262
|
|
|
61940
62263
|
if ( i % 2 === 0 ) {
|
|
61941
62264
|
|
|
@@ -66554,151 +66877,2514 @@ void main() {
|
|
|
66554
66877
|
|
|
66555
66878
|
}
|
|
66556
66879
|
|
|
66557
|
-
}
|
|
66880
|
+
}
|
|
66881
|
+
|
|
66882
|
+
}
|
|
66883
|
+
|
|
66884
|
+
// As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets.
|
|
66885
|
+
box.expandByVector( maxDisplacement );
|
|
66886
|
+
|
|
66887
|
+
}
|
|
66888
|
+
|
|
66889
|
+
geometry.boundingBox = box;
|
|
66890
|
+
|
|
66891
|
+
const sphere = new Sphere();
|
|
66892
|
+
|
|
66893
|
+
box.getCenter( sphere.center );
|
|
66894
|
+
sphere.radius = box.min.distanceTo( box.max ) / 2;
|
|
66895
|
+
|
|
66896
|
+
geometry.boundingSphere = sphere;
|
|
66897
|
+
|
|
66898
|
+
}
|
|
66899
|
+
|
|
66900
|
+
/**
|
|
66901
|
+
* @param {BufferGeometry} geometry
|
|
66902
|
+
* @param {GLTF.Primitive} primitiveDef
|
|
66903
|
+
* @param {GLTFParser} parser
|
|
66904
|
+
* @return {Promise<BufferGeometry>}
|
|
66905
|
+
*/
|
|
66906
|
+
function addPrimitiveAttributes( geometry, primitiveDef, parser ) {
|
|
66907
|
+
|
|
66908
|
+
const attributes = primitiveDef.attributes;
|
|
66909
|
+
|
|
66910
|
+
const pending = [];
|
|
66911
|
+
|
|
66912
|
+
function assignAttributeAccessor( accessorIndex, attributeName ) {
|
|
66913
|
+
|
|
66914
|
+
return parser.getDependency( 'accessor', accessorIndex )
|
|
66915
|
+
.then( function ( accessor ) {
|
|
66916
|
+
|
|
66917
|
+
geometry.setAttribute( attributeName, accessor );
|
|
66918
|
+
|
|
66919
|
+
} );
|
|
66920
|
+
|
|
66921
|
+
}
|
|
66922
|
+
|
|
66923
|
+
for ( const gltfAttributeName in attributes ) {
|
|
66924
|
+
|
|
66925
|
+
const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase();
|
|
66926
|
+
|
|
66927
|
+
// Skip attributes already provided by e.g. Draco extension.
|
|
66928
|
+
if ( threeAttributeName in geometry.attributes ) continue;
|
|
66929
|
+
|
|
66930
|
+
pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) );
|
|
66931
|
+
|
|
66932
|
+
}
|
|
66933
|
+
|
|
66934
|
+
if ( primitiveDef.indices !== undefined && ! geometry.index ) {
|
|
66935
|
+
|
|
66936
|
+
const accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) {
|
|
66937
|
+
|
|
66938
|
+
geometry.setIndex( accessor );
|
|
66939
|
+
|
|
66940
|
+
} );
|
|
66941
|
+
|
|
66942
|
+
pending.push( accessor );
|
|
66943
|
+
|
|
66944
|
+
}
|
|
66945
|
+
|
|
66946
|
+
if ( ColorManagement.workingColorSpace !== LinearSRGBColorSpace && 'COLOR_0' in attributes ) {
|
|
66947
|
+
|
|
66948
|
+
console.warn( `THREE.GLTFLoader: Converting vertex colors from "srgb-linear" to "${ColorManagement.workingColorSpace}" not supported.` );
|
|
66949
|
+
|
|
66950
|
+
}
|
|
66951
|
+
|
|
66952
|
+
assignExtrasToUserData( geometry, primitiveDef );
|
|
66953
|
+
|
|
66954
|
+
computeBounds( geometry, primitiveDef, parser );
|
|
66955
|
+
|
|
66956
|
+
return Promise.all( pending ).then( function () {
|
|
66957
|
+
|
|
66958
|
+
return primitiveDef.targets !== undefined
|
|
66959
|
+
? addMorphTargets( geometry, primitiveDef.targets, parser )
|
|
66960
|
+
: geometry;
|
|
66961
|
+
|
|
66962
|
+
} );
|
|
66963
|
+
|
|
66964
|
+
}
|
|
66965
|
+
|
|
66966
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
66967
|
+
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
|
|
66968
|
+
// All rights reserved.
|
|
66969
|
+
//
|
|
66970
|
+
// This software and its documentation and related materials are owned by
|
|
66971
|
+
// the Alliance. The software may only be incorporated into application
|
|
66972
|
+
// programs owned by members of the Alliance, subject to a signed
|
|
66973
|
+
// Membership Agreement and Supplemental Software License Agreement with the
|
|
66974
|
+
// Alliance. The structure and organization of this software are the valuable
|
|
66975
|
+
// trade secrets of the Alliance and its suppliers. The software is also
|
|
66976
|
+
// protected by copyright law and international treaty provisions. Application
|
|
66977
|
+
// programs incorporating this software must include the following statement
|
|
66978
|
+
// with their copyright notices:
|
|
66979
|
+
//
|
|
66980
|
+
// This application incorporates Open Design Alliance software pursuant to a
|
|
66981
|
+
// license agreement with Open Design Alliance.
|
|
66982
|
+
// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
|
|
66983
|
+
// All rights reserved.
|
|
66984
|
+
//
|
|
66985
|
+
// By use of this software, its documentation or related materials, you
|
|
66986
|
+
// acknowledge and accept the above terms.
|
|
66987
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
66988
|
+
class GLTFLoadingManager extends LoadingManager {
|
|
66989
|
+
constructor(file, params = {}) {
|
|
66990
|
+
super();
|
|
66991
|
+
this.path = "";
|
|
66992
|
+
this.resourcePath = "";
|
|
66993
|
+
this.fileURL = "";
|
|
66994
|
+
this.dataURLs = new Map();
|
|
66995
|
+
this.path = params.path || "";
|
|
66996
|
+
const externalFiles = params.externalFiles || new Map();
|
|
66997
|
+
if (typeof file === "string") {
|
|
66998
|
+
this.fileURL = file;
|
|
66999
|
+
this.resourcePath = LoaderUtils.extractUrlBase(file);
|
|
67000
|
+
}
|
|
67001
|
+
else {
|
|
67002
|
+
externalFiles.forEach((value, key) => (this.fileURL = value === file ? key : this.fileURL));
|
|
67003
|
+
externalFiles.set(this.fileURL, file);
|
|
67004
|
+
}
|
|
67005
|
+
externalFiles.forEach((value, key) => {
|
|
67006
|
+
let dataURL;
|
|
67007
|
+
if (typeof value === "string")
|
|
67008
|
+
dataURL = value;
|
|
67009
|
+
else
|
|
67010
|
+
dataURL = URL.createObjectURL(new Blob([value]));
|
|
67011
|
+
this.dataURLs.set(key, dataURL);
|
|
67012
|
+
});
|
|
67013
|
+
this.setURLModifier((url) => {
|
|
67014
|
+
const key = decodeURI(url)
|
|
67015
|
+
.replace(this.path, "")
|
|
67016
|
+
.replace(this.resourcePath, "")
|
|
67017
|
+
.replace(/^(\.?\/)/, "");
|
|
67018
|
+
const dataURL = this.dataURLs.get(key);
|
|
67019
|
+
return dataURL !== null && dataURL !== undefined ? dataURL : url;
|
|
67020
|
+
});
|
|
67021
|
+
}
|
|
67022
|
+
dispose() {
|
|
67023
|
+
this.dataURLs.forEach(URL.revokeObjectURL);
|
|
67024
|
+
}
|
|
67025
|
+
}
|
|
67026
|
+
|
|
67027
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
67028
|
+
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
|
|
67029
|
+
// All rights reserved.
|
|
67030
|
+
//
|
|
67031
|
+
// This software and its documentation and related materials are owned by
|
|
67032
|
+
// the Alliance. The software may only be incorporated into application
|
|
67033
|
+
// programs owned by members of the Alliance, subject to a signed
|
|
67034
|
+
// Membership Agreement and Supplemental Software License Agreement with the
|
|
67035
|
+
// Alliance. The structure and organization of this software are the valuable
|
|
67036
|
+
// trade secrets of the Alliance and its suppliers. The software is also
|
|
67037
|
+
// protected by copyright law and international treaty provisions. Application
|
|
67038
|
+
// programs incorporating this software must include the following statement
|
|
67039
|
+
// with their copyright notices:
|
|
67040
|
+
//
|
|
67041
|
+
// This application incorporates Open Design Alliance software pursuant to a
|
|
67042
|
+
// license agreement with Open Design Alliance.
|
|
67043
|
+
// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
|
|
67044
|
+
// All rights reserved.
|
|
67045
|
+
//
|
|
67046
|
+
// By use of this software, its documentation or related materials, you
|
|
67047
|
+
// acknowledge and accept the above terms.
|
|
67048
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
67049
|
+
/**
|
|
67050
|
+
* Basic model implementation.
|
|
67051
|
+
*/
|
|
67052
|
+
class ModelImpl {
|
|
67053
|
+
constructor(scene) {
|
|
67054
|
+
this.handle = "1";
|
|
67055
|
+
this.scene = scene;
|
|
67056
|
+
}
|
|
67057
|
+
dispose() {
|
|
67058
|
+
function disposeMaterial(material) {
|
|
67059
|
+
// if (material.alphaMap) material.alphaMap.dispose();
|
|
67060
|
+
// if (material.anisotropyMap) material.anisotropyMap.dispose();
|
|
67061
|
+
// if (material.aoMap) material.aoMap.dispose();
|
|
67062
|
+
// if (material.bumpMap) material.bumpMap.dispose();
|
|
67063
|
+
// if (material.clearcoatMap) material.clearcoatMap.dispose();
|
|
67064
|
+
// if (material.clearcoatNormalMap) material.clearcoatNormalMap.dispose();
|
|
67065
|
+
// if (material.clearcoatRoughnessMap) material.clearcoatRoughnessMap.dispose();
|
|
67066
|
+
// if (material.displacementMap) material.displacementMap.dispose();
|
|
67067
|
+
// if (material.emissiveMap) material.emissiveMap.dispose();
|
|
67068
|
+
// if (material.gradientMap) material.gradientMap.dispose();
|
|
67069
|
+
// if (material.envMap) material.envMap.dispose();
|
|
67070
|
+
// if (material.iridescenceMap) material.iridescenceMap.dispose();
|
|
67071
|
+
// if (material.lightMap) material.lightMap.dispose();
|
|
67072
|
+
// if (material.map) material.map.dispose();
|
|
67073
|
+
// if (material.metalnessMap) material.metalnessMap.dispose();
|
|
67074
|
+
// if (material.normalMap) material.normalMap.dispose();
|
|
67075
|
+
// if (material.roughnessMap) material.roughnessMap.dispose();
|
|
67076
|
+
// if (material.sheenColorMap) material.sheenColorMap.dispose();
|
|
67077
|
+
// if (material.sheenRoughnessMap) material.sheenRoughnessMap.dispose();
|
|
67078
|
+
// if (material.specularMap) material.specularMap.dispose();
|
|
67079
|
+
// if (material.thicknessMap) material.thicknessMap.dispose();
|
|
67080
|
+
// if (material.transmissionMap) material.transmissionMap.dispose();
|
|
67081
|
+
material.dispose();
|
|
67082
|
+
}
|
|
67083
|
+
function disposeMaterials(material) {
|
|
67084
|
+
const materials = Array.isArray(material) ? material : [material];
|
|
67085
|
+
materials.forEach((material) => disposeMaterial(material));
|
|
67086
|
+
}
|
|
67087
|
+
function disposeObject(object) {
|
|
67088
|
+
if (object.geometry)
|
|
67089
|
+
object.geometry.dispose();
|
|
67090
|
+
if (object.material)
|
|
67091
|
+
disposeMaterials(object.material);
|
|
67092
|
+
}
|
|
67093
|
+
this.scene.traverse(disposeObject);
|
|
67094
|
+
this.scene.clear();
|
|
67095
|
+
}
|
|
67096
|
+
getExtents(target) {
|
|
67097
|
+
this.scene.traverseVisible((object) => !object.children.length && target.expandByObject(object));
|
|
67098
|
+
return target;
|
|
67099
|
+
}
|
|
67100
|
+
getObjects() {
|
|
67101
|
+
const objects = [];
|
|
67102
|
+
this.scene.traverse((object) => objects.push(object));
|
|
67103
|
+
return objects;
|
|
67104
|
+
}
|
|
67105
|
+
getVisibleObjects() {
|
|
67106
|
+
const objects = [];
|
|
67107
|
+
this.scene.traverseVisible((object) => objects.push(object));
|
|
67108
|
+
return objects;
|
|
67109
|
+
}
|
|
67110
|
+
hasObject(object) {
|
|
67111
|
+
while (object) {
|
|
67112
|
+
if (object === this.scene)
|
|
67113
|
+
return true;
|
|
67114
|
+
object = object.parent;
|
|
67115
|
+
}
|
|
67116
|
+
return false;
|
|
67117
|
+
}
|
|
67118
|
+
getOwnObjects(objects) {
|
|
67119
|
+
if (!Array.isArray(objects))
|
|
67120
|
+
objects = [objects];
|
|
67121
|
+
return objects.filter((object) => this.hasObject(object));
|
|
67122
|
+
}
|
|
67123
|
+
getObjectsByHandles(handles) {
|
|
67124
|
+
const handleSet = new Set(handles);
|
|
67125
|
+
const objects = [];
|
|
67126
|
+
this.scene.traverse((object) => handleSet.has(object.userData.handle) && objects.push(object));
|
|
67127
|
+
return objects;
|
|
67128
|
+
}
|
|
67129
|
+
getHandlesByObjects(objects) {
|
|
67130
|
+
if (!Array.isArray(objects))
|
|
67131
|
+
objects = [objects];
|
|
67132
|
+
const handlesSet = new Set();
|
|
67133
|
+
this.getOwnObjects(objects).forEach((object) => handlesSet.add(object.userData.handle));
|
|
67134
|
+
return Array.from(handlesSet);
|
|
67135
|
+
}
|
|
67136
|
+
hideObjects(objects) {
|
|
67137
|
+
if (!Array.isArray(objects))
|
|
67138
|
+
objects = [objects];
|
|
67139
|
+
this.getOwnObjects(objects).forEach((object) => (object.visible = false));
|
|
67140
|
+
return this;
|
|
67141
|
+
}
|
|
67142
|
+
hideAllObjects() {
|
|
67143
|
+
return this.isolateObjects([]);
|
|
67144
|
+
}
|
|
67145
|
+
isolateObjects(objects) {
|
|
67146
|
+
if (!Array.isArray(objects))
|
|
67147
|
+
objects = [objects];
|
|
67148
|
+
const visibleSet = new Set(objects);
|
|
67149
|
+
this.getOwnObjects(objects).forEach((object) => object.traverseAncestors((parent) => visibleSet.add(parent)));
|
|
67150
|
+
this.scene.traverse((object) => (object.visible = visibleSet.has(object)));
|
|
67151
|
+
return this;
|
|
67152
|
+
}
|
|
67153
|
+
showObjects(objects) {
|
|
67154
|
+
if (!Array.isArray(objects))
|
|
67155
|
+
objects = [objects];
|
|
67156
|
+
this.getOwnObjects(objects).forEach((object) => {
|
|
67157
|
+
object.visible = true;
|
|
67158
|
+
object.traverseAncestors((parent) => (parent.visible = true));
|
|
67159
|
+
});
|
|
67160
|
+
return this;
|
|
67161
|
+
}
|
|
67162
|
+
showAllObjects() {
|
|
67163
|
+
this.scene.traverse((object) => (object.visible = true));
|
|
67164
|
+
return this;
|
|
67165
|
+
}
|
|
67166
|
+
showOriginalObjects(objects) {
|
|
67167
|
+
return this;
|
|
67168
|
+
}
|
|
67169
|
+
hideOriginalObjects(objects) {
|
|
67170
|
+
return this;
|
|
67171
|
+
}
|
|
67172
|
+
explode(scale = 0, coeff = 4) {
|
|
67173
|
+
function calcExplodeDepth(object, depth) {
|
|
67174
|
+
let res = depth;
|
|
67175
|
+
object.children.forEach((x) => {
|
|
67176
|
+
const objectDepth = calcExplodeDepth(x, depth + 1);
|
|
67177
|
+
if (res < objectDepth)
|
|
67178
|
+
res = objectDepth;
|
|
67179
|
+
});
|
|
67180
|
+
object.userData.originalPosition = object.position.clone();
|
|
67181
|
+
object.userData.originalCenter = new Box3().setFromObject(object).getCenter(new Vector3());
|
|
67182
|
+
object.userData.isExplodeLocked = depth > 2 && object.children.length === 0;
|
|
67183
|
+
return res;
|
|
67184
|
+
}
|
|
67185
|
+
scale /= 100;
|
|
67186
|
+
if (!this.scene.userData.explodeDepth)
|
|
67187
|
+
this.scene.userData.explodeDepth = calcExplodeDepth(this.scene, 1);
|
|
67188
|
+
const maxDepth = this.scene.userData.explodeDepth;
|
|
67189
|
+
const scaledExplodeDepth = scale * maxDepth + 1;
|
|
67190
|
+
const explodeDepth = 0 | scaledExplodeDepth;
|
|
67191
|
+
const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
|
|
67192
|
+
function explodeObject(object, depth) {
|
|
67193
|
+
object.position.copy(object.userData.originalPosition);
|
|
67194
|
+
if (depth > 0 && depth <= explodeDepth && !object.userData.isExplodeLocked) {
|
|
67195
|
+
let objectScale = scale * coeff;
|
|
67196
|
+
if (depth === explodeDepth)
|
|
67197
|
+
objectScale *= currentSegmentFraction;
|
|
67198
|
+
const parentCenter = object.parent.userData.originalCenter;
|
|
67199
|
+
const objectCenter = object.userData.originalCenter;
|
|
67200
|
+
const objectOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
|
|
67201
|
+
object.position.add(objectOffset);
|
|
67202
|
+
}
|
|
67203
|
+
object.children.forEach((x) => explodeObject(x, depth + 1));
|
|
67204
|
+
}
|
|
67205
|
+
explodeObject(this.scene, 0);
|
|
67206
|
+
this.scene.updateMatrixWorld();
|
|
67207
|
+
return this;
|
|
67208
|
+
}
|
|
67209
|
+
}
|
|
67210
|
+
|
|
67211
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
67212
|
+
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
|
|
67213
|
+
// All rights reserved.
|
|
67214
|
+
//
|
|
67215
|
+
// This software and its documentation and related materials are owned by
|
|
67216
|
+
// the Alliance. The software may only be incorporated into application
|
|
67217
|
+
// programs owned by members of the Alliance, subject to a signed
|
|
67218
|
+
// Membership Agreement and Supplemental Software License Agreement with the
|
|
67219
|
+
// Alliance. The structure and organization of this software are the valuable
|
|
67220
|
+
// trade secrets of the Alliance and its suppliers. The software is also
|
|
67221
|
+
// protected by copyright law and international treaty provisions. Application
|
|
67222
|
+
// programs incorporating this software must include the following statement
|
|
67223
|
+
// with their copyright notices:
|
|
67224
|
+
//
|
|
67225
|
+
// This application incorporates Open Design Alliance software pursuant to a
|
|
67226
|
+
// license agreement with Open Design Alliance.
|
|
67227
|
+
// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
|
|
67228
|
+
// All rights reserved.
|
|
67229
|
+
//
|
|
67230
|
+
// By use of this software, its documentation or related materials, you
|
|
67231
|
+
// acknowledge and accept the above terms.
|
|
67232
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
67233
|
+
class GLTFFileLoader extends Loader$1 {
|
|
67234
|
+
constructor(viewer) {
|
|
67235
|
+
super();
|
|
67236
|
+
this.viewer = viewer;
|
|
67237
|
+
}
|
|
67238
|
+
isSupport(file, format) {
|
|
67239
|
+
return ((typeof file === "string" || file instanceof globalThis.File || file instanceof ArrayBuffer) &&
|
|
67240
|
+
/(gltf|glb)$/i.test(format));
|
|
67241
|
+
}
|
|
67242
|
+
async load(file, format, params) {
|
|
67243
|
+
const manager = new GLTFLoadingManager(file, params);
|
|
67244
|
+
const loader = new GLTFLoader(manager);
|
|
67245
|
+
loader.setPath(manager.path);
|
|
67246
|
+
loader.setCrossOrigin(params.crossOrigin || loader.crossOrigin);
|
|
67247
|
+
loader.setWithCredentials(params.withCredentials || loader.withCredentials);
|
|
67248
|
+
const progress = (event) => {
|
|
67249
|
+
const { lengthComputable, loaded, total } = event;
|
|
67250
|
+
const progress = lengthComputable ? loaded / total : 1;
|
|
67251
|
+
this.viewer.emitEvent({ type: "geometryprogress", data: progress, file });
|
|
67252
|
+
};
|
|
67253
|
+
const gltf = await loader.loadAsync(manager.fileURL, progress);
|
|
67254
|
+
if (!this.viewer.scene)
|
|
67255
|
+
return this;
|
|
67256
|
+
const modelImpl = new ModelImpl(gltf.scene);
|
|
67257
|
+
modelImpl.loader = this;
|
|
67258
|
+
modelImpl.viewer = this.viewer;
|
|
67259
|
+
this.viewer.scene.add(modelImpl.scene);
|
|
67260
|
+
this.viewer.models.push(modelImpl);
|
|
67261
|
+
this.viewer.syncOptions();
|
|
67262
|
+
this.viewer.syncOverlay();
|
|
67263
|
+
this.viewer.update();
|
|
67264
|
+
this.viewer.emitEvent({ type: "databasechunk", data: gltf.scene, file });
|
|
67265
|
+
return this;
|
|
67266
|
+
}
|
|
67267
|
+
}
|
|
67268
|
+
|
|
67269
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
67270
|
+
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
|
|
67271
|
+
// All rights reserved.
|
|
67272
|
+
//
|
|
67273
|
+
// This software and its documentation and related materials are owned by
|
|
67274
|
+
// the Alliance. The software may only be incorporated into application
|
|
67275
|
+
// programs owned by members of the Alliance, subject to a signed
|
|
67276
|
+
// Membership Agreement and Supplemental Software License Agreement with the
|
|
67277
|
+
// Alliance. The structure and organization of this software are the valuable
|
|
67278
|
+
// trade secrets of the Alliance and its suppliers. The software is also
|
|
67279
|
+
// protected by copyright law and international treaty provisions. Application
|
|
67280
|
+
// programs incorporating this software must include the following statement
|
|
67281
|
+
// with their copyright notices:
|
|
67282
|
+
//
|
|
67283
|
+
// This application incorporates Open Design Alliance software pursuant to a
|
|
67284
|
+
// license agreement with Open Design Alliance.
|
|
67285
|
+
// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
|
|
67286
|
+
// All rights reserved.
|
|
67287
|
+
//
|
|
67288
|
+
// By use of this software, its documentation or related materials, you
|
|
67289
|
+
// acknowledge and accept the above terms.
|
|
67290
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
67291
|
+
/**
|
|
67292
|
+
* Dynamic model implementation.
|
|
67293
|
+
*/
|
|
67294
|
+
class DynamicModelImpl extends ModelImpl {
|
|
67295
|
+
getExtents(target) {
|
|
67296
|
+
return target.union(this.gltfLoader.getTotalGeometryExtent());
|
|
67297
|
+
}
|
|
67298
|
+
getObjects() {
|
|
67299
|
+
const objects = [];
|
|
67300
|
+
this.gltfLoader.originalObjects.forEach((object) => {
|
|
67301
|
+
objects.push(object);
|
|
67302
|
+
});
|
|
67303
|
+
return objects;
|
|
67304
|
+
}
|
|
67305
|
+
getVisibleObjects() {
|
|
67306
|
+
return this.gltfLoader.getOriginalObjectForSelect();
|
|
67307
|
+
}
|
|
67308
|
+
hasObject(object) {
|
|
67309
|
+
return this.gltfLoader.originalObjects.has(object);
|
|
67310
|
+
}
|
|
67311
|
+
getObjectsByHandles(handles) {
|
|
67312
|
+
const handlesSet = new Set(handles);
|
|
67313
|
+
const objects = [];
|
|
67314
|
+
handlesSet.forEach((handle) => {
|
|
67315
|
+
const handles = this.gltfLoader.handleToObjects.get(handle) || [];
|
|
67316
|
+
objects.push(...Array.from(handles));
|
|
67317
|
+
});
|
|
67318
|
+
return objects;
|
|
67319
|
+
}
|
|
67320
|
+
hideObjects(objects) {
|
|
67321
|
+
this.getOwnObjects(objects)
|
|
67322
|
+
.map((object) => object.userData.handle)
|
|
67323
|
+
.forEach((handle) => this.gltfLoader.hiddenHandles.add(handle));
|
|
67324
|
+
this.gltfLoader.syncHiddenObjects();
|
|
67325
|
+
return this;
|
|
67326
|
+
}
|
|
67327
|
+
isolateObjects(objects) {
|
|
67328
|
+
const handles = this.getHandlesByObjects(objects);
|
|
67329
|
+
this.gltfLoader.isolateObjects(new Set(handles));
|
|
67330
|
+
return this;
|
|
67331
|
+
}
|
|
67332
|
+
showObjects(objects) {
|
|
67333
|
+
this.getOwnObjects(objects)
|
|
67334
|
+
.map((object) => object.userData.handle)
|
|
67335
|
+
.forEach((handle) => this.gltfLoader.hiddenHandles.delete(handle));
|
|
67336
|
+
this.gltfLoader.syncHiddenObjects();
|
|
67337
|
+
return this;
|
|
67338
|
+
}
|
|
67339
|
+
showAllObjects() {
|
|
67340
|
+
this.gltfLoader.hiddenHandles.clear();
|
|
67341
|
+
this.gltfLoader.syncHiddenObjects();
|
|
67342
|
+
return this;
|
|
67343
|
+
}
|
|
67344
|
+
showOriginalObjects(objects) {
|
|
67345
|
+
this.getOwnObjects(objects).forEach((object) => (object.visible = true));
|
|
67346
|
+
return this;
|
|
67347
|
+
}
|
|
67348
|
+
hideOriginalObjects(objects) {
|
|
67349
|
+
this.getOwnObjects(objects).forEach((object) => (object.visible = false));
|
|
67350
|
+
return this;
|
|
67351
|
+
}
|
|
67352
|
+
}
|
|
67353
|
+
|
|
67354
|
+
const GL_COMPONENT_TYPES = {
|
|
67355
|
+
5120: Int8Array,
|
|
67356
|
+
5121: Uint8Array,
|
|
67357
|
+
5122: Int16Array,
|
|
67358
|
+
5123: Uint16Array,
|
|
67359
|
+
5125: Uint32Array,
|
|
67360
|
+
5126: Float32Array,
|
|
67361
|
+
};
|
|
67362
|
+
|
|
67363
|
+
const GL_CONSTANTS = {
|
|
67364
|
+
POINTS: 0,
|
|
67365
|
+
LINES: 1,
|
|
67366
|
+
LINE_LOOP: 2,
|
|
67367
|
+
LINE_STRIP: 3,
|
|
67368
|
+
TRIANGLES: 4,
|
|
67369
|
+
TRIANGLE_STRIP: 5,
|
|
67370
|
+
TRIANGLE_FAN: 6};
|
|
67371
|
+
|
|
67372
|
+
class GltfStructure {
|
|
67373
|
+
constructor(id) {
|
|
67374
|
+
this.id = `${id}`;
|
|
67375
|
+
this.json = null;
|
|
67376
|
+
this.baseUrl = "";
|
|
67377
|
+
|
|
67378
|
+
// Binary manager properties
|
|
67379
|
+
this.loadController = null;
|
|
67380
|
+
// Request batching parameters
|
|
67381
|
+
this.batchDelay = 10;
|
|
67382
|
+
this.maxBatchSize = 5 * 1024 * 1024;
|
|
67383
|
+
this.maxRangesPerRequest = 512;
|
|
67384
|
+
|
|
67385
|
+
// Request queue
|
|
67386
|
+
this.pendingRequests = [];
|
|
67387
|
+
this.batchTimeout = null;
|
|
67388
|
+
|
|
67389
|
+
// Material and texture properties
|
|
67390
|
+
this.textureLoader = new TextureLoader();
|
|
67391
|
+
this.materials = new Map();
|
|
67392
|
+
this.textureCache = new Map();
|
|
67393
|
+
}
|
|
67394
|
+
|
|
67395
|
+
async initialize(loadController) {
|
|
67396
|
+
this.json = await loadController.loadJson();
|
|
67397
|
+
this.baseUrl = await loadController.baseUrl();
|
|
67398
|
+
this.loadController = loadController;
|
|
67399
|
+
}
|
|
67400
|
+
|
|
67401
|
+
clear() {
|
|
67402
|
+
this.json = null;
|
|
67403
|
+
this.baseUrl = "";
|
|
67404
|
+
this.loadController = null;
|
|
67405
|
+
this.pendingRequests = [];
|
|
67406
|
+
if (this.batchTimeout) {
|
|
67407
|
+
clearTimeout(this.batchTimeout);
|
|
67408
|
+
this.batchTimeout = null;
|
|
67409
|
+
}
|
|
67410
|
+
|
|
67411
|
+
// Clear materials and textures
|
|
67412
|
+
this.disposeMaterials();
|
|
67413
|
+
this.textureCache.clear();
|
|
67414
|
+
this.materials.clear();
|
|
67415
|
+
}
|
|
67416
|
+
|
|
67417
|
+
getJson() {
|
|
67418
|
+
return this.json;
|
|
67419
|
+
}
|
|
67420
|
+
|
|
67421
|
+
// Schedule a request for processing
|
|
67422
|
+
scheduleRequest(request) {
|
|
67423
|
+
this.pendingRequests.push(request);
|
|
67424
|
+
|
|
67425
|
+
// Clear existing timeout
|
|
67426
|
+
if (this.batchTimeout) {
|
|
67427
|
+
clearTimeout(this.batchTimeout);
|
|
67428
|
+
}
|
|
67429
|
+
|
|
67430
|
+
// Set new timeout for batch processing
|
|
67431
|
+
this.batchTimeout = setTimeout(() => this.processBatch(), this.batchDelay);
|
|
67432
|
+
|
|
67433
|
+
// Return a promise that will resolve when the data is available
|
|
67434
|
+
return new Promise((resolve, reject) => {
|
|
67435
|
+
request.resolve = resolve;
|
|
67436
|
+
request.reject = reject;
|
|
67437
|
+
});
|
|
67438
|
+
}
|
|
67439
|
+
|
|
67440
|
+
async processBatch() {
|
|
67441
|
+
if (this.pendingRequests.length === 0) return;
|
|
67442
|
+
|
|
67443
|
+
// Take current batch of requests and clear timeout
|
|
67444
|
+
const currentBatch = [...this.pendingRequests];
|
|
67445
|
+
this.pendingRequests = [];
|
|
67446
|
+
|
|
67447
|
+
if (this.batchTimeout) {
|
|
67448
|
+
clearTimeout(this.batchTimeout);
|
|
67449
|
+
this.batchTimeout = null;
|
|
67450
|
+
}
|
|
67451
|
+
try {
|
|
67452
|
+
// Split requests into smaller groups
|
|
67453
|
+
for (let i = 0; i < currentBatch.length; i += this.maxRangesPerRequest) {
|
|
67454
|
+
const batchRequests = currentBatch.slice(i, i + this.maxRangesPerRequest);
|
|
67455
|
+
const buffer = await this.loadController.loadBinaryData(batchRequests);
|
|
67456
|
+
|
|
67457
|
+
let currentOffset = 0;
|
|
67458
|
+
batchRequests.forEach((request) => {
|
|
67459
|
+
const view = this.createTypedArray(buffer, currentOffset, request.length, request.componentType);
|
|
67460
|
+
request.resolve(view);
|
|
67461
|
+
currentOffset += request.length;
|
|
67462
|
+
});
|
|
67463
|
+
}
|
|
67464
|
+
} catch (error) {
|
|
67465
|
+
console.error("Error processing batch:", error);
|
|
67466
|
+
currentBatch.forEach((request) => request.reject(error));
|
|
67467
|
+
}
|
|
67468
|
+
|
|
67469
|
+
if (this.pendingRequests.length > 0) {
|
|
67470
|
+
this.batchTimeout = setTimeout(() => this.processBatch(), this.batchDelay);
|
|
67471
|
+
}
|
|
67472
|
+
}
|
|
67473
|
+
|
|
67474
|
+
getBufferView(byteOffset, byteLength, componentType) {
|
|
67475
|
+
return this.scheduleRequest({
|
|
67476
|
+
offset: byteOffset,
|
|
67477
|
+
length: byteLength,
|
|
67478
|
+
componentType,
|
|
67479
|
+
});
|
|
67480
|
+
}
|
|
67481
|
+
|
|
67482
|
+
createTypedArray(buffer, offset, length, componentType) {
|
|
67483
|
+
try {
|
|
67484
|
+
// Validate parameters
|
|
67485
|
+
if (!buffer || !(buffer instanceof ArrayBuffer)) {
|
|
67486
|
+
throw new Error("Invalid buffer");
|
|
67487
|
+
}
|
|
67488
|
+
|
|
67489
|
+
// Calculate element size for given type
|
|
67490
|
+
let elementSize;
|
|
67491
|
+
switch (componentType) {
|
|
67492
|
+
case 5120:
|
|
67493
|
+
case 5121:
|
|
67494
|
+
elementSize = 1;
|
|
67495
|
+
break; // BYTE, UNSIGNED_BYTE
|
|
67496
|
+
case 5122:
|
|
67497
|
+
case 5123:
|
|
67498
|
+
elementSize = 2;
|
|
67499
|
+
break; // SHORT, UNSIGNED_SHORT
|
|
67500
|
+
case 5125:
|
|
67501
|
+
case 5126:
|
|
67502
|
+
elementSize = 4;
|
|
67503
|
+
break; // UNSIGNED_INT, FLOAT
|
|
67504
|
+
default:
|
|
67505
|
+
throw new Error(`Unsupported component type: ${componentType}`);
|
|
67506
|
+
}
|
|
67507
|
+
|
|
67508
|
+
// Check if requested length is correct
|
|
67509
|
+
const numElements = length / elementSize;
|
|
67510
|
+
if (!Number.isInteger(numElements)) {
|
|
67511
|
+
throw new Error(`Invalid length ${length} for component type ${componentType}`);
|
|
67512
|
+
}
|
|
67513
|
+
|
|
67514
|
+
// Check if buffer is large enough
|
|
67515
|
+
if (length > buffer.byteLength) {
|
|
67516
|
+
throw new Error(`Buffer too small: need ${length} bytes, but buffer is ${buffer.byteLength} bytes`);
|
|
67517
|
+
}
|
|
67518
|
+
|
|
67519
|
+
// Create appropriate typed array
|
|
67520
|
+
const ArrayType = GL_COMPONENT_TYPES[componentType];
|
|
67521
|
+
return new ArrayType(buffer, offset, numElements);
|
|
67522
|
+
} catch (error) {
|
|
67523
|
+
if (error.name !== "AbortError") {
|
|
67524
|
+
console.error("Error creating typed array:", {
|
|
67525
|
+
bufferSize: buffer?.byteLength,
|
|
67526
|
+
offset,
|
|
67527
|
+
length,
|
|
67528
|
+
componentType,
|
|
67529
|
+
error,
|
|
67530
|
+
});
|
|
67531
|
+
}
|
|
67532
|
+
throw error;
|
|
67533
|
+
}
|
|
67534
|
+
}
|
|
67535
|
+
|
|
67536
|
+
async createBufferAttribute(accessorIndex) {
|
|
67537
|
+
if (!this.json) {
|
|
67538
|
+
throw new Error("No GLTF structure loaded");
|
|
67539
|
+
}
|
|
67540
|
+
|
|
67541
|
+
const gltf = this.json;
|
|
67542
|
+
const accessor = gltf.accessors[accessorIndex];
|
|
67543
|
+
const bufferView = gltf.bufferViews[accessor.bufferView];
|
|
67544
|
+
|
|
67545
|
+
try {
|
|
67546
|
+
const byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0);
|
|
67547
|
+
const components = this.getNumComponents(accessor.type);
|
|
67548
|
+
const count = accessor.count;
|
|
67549
|
+
const byteLength = count * components * this.getComponentSize(accessor.componentType);
|
|
67550
|
+
|
|
67551
|
+
const array = await this.getBufferView(byteOffset, byteLength, accessor.componentType);
|
|
67552
|
+
|
|
67553
|
+
const attribute = new BufferAttribute(array, components);
|
|
67554
|
+
|
|
67555
|
+
if (accessor.min !== undefined) attribute.min = accessor.min;
|
|
67556
|
+
if (accessor.max !== undefined) attribute.max = accessor.max;
|
|
67557
|
+
|
|
67558
|
+
return attribute;
|
|
67559
|
+
} catch (error) {
|
|
67560
|
+
if (error.name !== "AbortError") {
|
|
67561
|
+
console.error("Error creating buffer attribute:", {
|
|
67562
|
+
error,
|
|
67563
|
+
accessor,
|
|
67564
|
+
bufferView,
|
|
67565
|
+
});
|
|
67566
|
+
}
|
|
67567
|
+
|
|
67568
|
+
throw error;
|
|
67569
|
+
}
|
|
67570
|
+
}
|
|
67571
|
+
|
|
67572
|
+
getComponentSize(componentType) {
|
|
67573
|
+
switch (componentType) {
|
|
67574
|
+
case 5120: // BYTE
|
|
67575
|
+
case 5121: // UNSIGNED_BYTE
|
|
67576
|
+
return 1;
|
|
67577
|
+
case 5122: // SHORT
|
|
67578
|
+
case 5123: // UNSIGNED_SHORT
|
|
67579
|
+
return 2;
|
|
67580
|
+
case 5125: // UNSIGNED_INT
|
|
67581
|
+
case 5126: // FLOAT
|
|
67582
|
+
return 4;
|
|
67583
|
+
default:
|
|
67584
|
+
throw new Error(`Unknown component type: ${componentType}`);
|
|
67585
|
+
}
|
|
67586
|
+
}
|
|
67587
|
+
|
|
67588
|
+
getNumComponents(type) {
|
|
67589
|
+
switch (type) {
|
|
67590
|
+
case "SCALAR":
|
|
67591
|
+
return 1;
|
|
67592
|
+
case "VEC2":
|
|
67593
|
+
return 2;
|
|
67594
|
+
case "VEC3":
|
|
67595
|
+
return 3;
|
|
67596
|
+
case "VEC4":
|
|
67597
|
+
return 4;
|
|
67598
|
+
case "MAT2":
|
|
67599
|
+
return 4;
|
|
67600
|
+
case "MAT3":
|
|
67601
|
+
return 9;
|
|
67602
|
+
case "MAT4":
|
|
67603
|
+
return 16;
|
|
67604
|
+
default:
|
|
67605
|
+
throw new Error(`Unknown type: ${type}`);
|
|
67606
|
+
}
|
|
67607
|
+
}
|
|
67608
|
+
|
|
67609
|
+
async loadTextures() {
|
|
67610
|
+
if (!this.json.textures) return;
|
|
67611
|
+
|
|
67612
|
+
const loadTexture = async (imageIndex) => {
|
|
67613
|
+
const image = this.json.images[imageIndex];
|
|
67614
|
+
|
|
67615
|
+
if (image.uri) {
|
|
67616
|
+
// Handle base64 or URL
|
|
67617
|
+
if (image.uri.startsWith("data:")) {
|
|
67618
|
+
return await this.textureLoader.loadAsync(image.uri);
|
|
67619
|
+
} else {
|
|
67620
|
+
const fullUrl = this.baseUrl + image.uri;
|
|
67621
|
+
return await this.textureLoader.loadAsync(fullUrl);
|
|
67622
|
+
}
|
|
67623
|
+
} else if (image.bufferView !== undefined) {
|
|
67624
|
+
// Handle embedded binary data
|
|
67625
|
+
const bufferView = this.json.bufferViews[image.bufferView];
|
|
67626
|
+
const array = await this.getBufferView(
|
|
67627
|
+
bufferView.byteOffset || 0,
|
|
67628
|
+
bufferView.byteLength,
|
|
67629
|
+
5121 // UNSIGNED_BYTE
|
|
67630
|
+
);
|
|
67631
|
+
const blob = new Blob([array], { type: image.mimeType });
|
|
67632
|
+
const url = URL.createObjectURL(blob);
|
|
67633
|
+
const texture = await this.textureLoader.loadAsync(url);
|
|
67634
|
+
URL.revokeObjectURL(url);
|
|
67635
|
+
texture.flipY = false; // GLTF standard
|
|
67636
|
+
return texture;
|
|
67637
|
+
}
|
|
67638
|
+
};
|
|
67639
|
+
|
|
67640
|
+
// Load all textures
|
|
67641
|
+
const texturePromises = [];
|
|
67642
|
+
for (let i = 0; i < this.json.textures.length; i++) {
|
|
67643
|
+
texturePromises.push(
|
|
67644
|
+
loadTexture(this.json.textures[i].source).then((texture) => this.textureCache.set(i, texture))
|
|
67645
|
+
);
|
|
67646
|
+
}
|
|
67647
|
+
await Promise.all(texturePromises);
|
|
67648
|
+
}
|
|
67649
|
+
|
|
67650
|
+
loadMaterials() {
|
|
67651
|
+
if (!this.json.materials) return this.materials;
|
|
67652
|
+
|
|
67653
|
+
for (let i = 0; i < this.json.materials.length; i++) {
|
|
67654
|
+
const materialDef = this.json.materials[i];
|
|
67655
|
+
const material = this.createMaterial(materialDef);
|
|
67656
|
+
this.materials.set(i, material);
|
|
67657
|
+
}
|
|
67658
|
+
return this.materials;
|
|
67659
|
+
}
|
|
67660
|
+
|
|
67661
|
+
createMaterial(materialDef) {
|
|
67662
|
+
const material = new MeshStandardMaterial();
|
|
67663
|
+
|
|
67664
|
+
// Base color
|
|
67665
|
+
if (materialDef.pbrMetallicRoughness) {
|
|
67666
|
+
const pbr = materialDef.pbrMetallicRoughness;
|
|
67667
|
+
|
|
67668
|
+
if (pbr.baseColorFactor) {
|
|
67669
|
+
material.color.fromArray(pbr.baseColorFactor);
|
|
67670
|
+
material.opacity = pbr.baseColorFactor[3];
|
|
67671
|
+
}
|
|
67672
|
+
|
|
67673
|
+
if (pbr.baseColorTexture) {
|
|
67674
|
+
material.map = this.textureCache.get(pbr.baseColorTexture.index);
|
|
67675
|
+
}
|
|
67676
|
+
|
|
67677
|
+
// Metallic and roughness
|
|
67678
|
+
if (pbr.metallicFactor !== undefined) {
|
|
67679
|
+
material.metalness = pbr.metallicFactor;
|
|
67680
|
+
}
|
|
67681
|
+
if (pbr.roughnessFactor !== undefined) {
|
|
67682
|
+
material.roughness = pbr.roughnessFactor;
|
|
67683
|
+
}
|
|
67684
|
+
if (pbr.metallicRoughnessTexture) {
|
|
67685
|
+
material.metalnessMap = this.textureCache.get(pbr.metallicRoughnessTexture.index);
|
|
67686
|
+
material.roughnessMap = material.metalnessMap;
|
|
67687
|
+
}
|
|
67688
|
+
}
|
|
67689
|
+
|
|
67690
|
+
// Normal map
|
|
67691
|
+
if (materialDef.normalTexture) {
|
|
67692
|
+
material.normalMap = this.textureCache.get(materialDef.normalTexture.index);
|
|
67693
|
+
if (materialDef.normalTexture.scale !== undefined) {
|
|
67694
|
+
material.normalScale.set(materialDef.normalTexture.scale, materialDef.normalTexture.scale);
|
|
67695
|
+
}
|
|
67696
|
+
}
|
|
67697
|
+
|
|
67698
|
+
// Emissive
|
|
67699
|
+
if (materialDef.emissiveFactor) {
|
|
67700
|
+
material.emissive.fromArray(materialDef.emissiveFactor);
|
|
67701
|
+
}
|
|
67702
|
+
if (materialDef.emissiveTexture) {
|
|
67703
|
+
material.emissiveMap = this.textureCache.get(materialDef.emissiveTexture.index);
|
|
67704
|
+
}
|
|
67705
|
+
|
|
67706
|
+
// Occlusion
|
|
67707
|
+
if (materialDef.occlusionTexture) {
|
|
67708
|
+
material.aoMap = this.textureCache.get(materialDef.occlusionTexture.index);
|
|
67709
|
+
if (materialDef.occlusionTexture.strength !== undefined) {
|
|
67710
|
+
material.aoMapIntensity = materialDef.occlusionTexture.strength;
|
|
67711
|
+
}
|
|
67712
|
+
}
|
|
67713
|
+
|
|
67714
|
+
// Alpha mode
|
|
67715
|
+
if (materialDef.alphaMode === "BLEND") {
|
|
67716
|
+
material.transparent = true;
|
|
67717
|
+
} else if (materialDef.alphaMode === "MASK") {
|
|
67718
|
+
material.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5;
|
|
67719
|
+
}
|
|
67720
|
+
|
|
67721
|
+
// Double sided
|
|
67722
|
+
material.side = materialDef.doubleSided ? DoubleSide : FrontSide;
|
|
67723
|
+
|
|
67724
|
+
material.name = materialDef.name;
|
|
67725
|
+
|
|
67726
|
+
return material;
|
|
67727
|
+
}
|
|
67728
|
+
|
|
67729
|
+
disposeMaterials() {
|
|
67730
|
+
// Dispose all textures
|
|
67731
|
+
this.textureCache.forEach((texture) => texture.dispose());
|
|
67732
|
+
this.textureCache.clear();
|
|
67733
|
+
|
|
67734
|
+
// Dispose all materials
|
|
67735
|
+
this.materials.forEach((material) => {
|
|
67736
|
+
if (material.map) material.map.dispose();
|
|
67737
|
+
if (material.lightMap) material.lightMap.dispose();
|
|
67738
|
+
if (material.bumpMap) material.bumpMap.dispose();
|
|
67739
|
+
if (material.normalMap) material.normalMap.dispose();
|
|
67740
|
+
if (material.specularMap) material.specularMap.dispose();
|
|
67741
|
+
if (material.envMap) material.envMap.dispose();
|
|
67742
|
+
if (material.aoMap) material.aoMap.dispose();
|
|
67743
|
+
if (material.metalnessMap) material.metalnessMap.dispose();
|
|
67744
|
+
if (material.roughnessMap) material.roughnessMap.dispose();
|
|
67745
|
+
if (material.emissiveMap) material.emissiveMap.dispose();
|
|
67746
|
+
material.dispose();
|
|
67747
|
+
});
|
|
67748
|
+
this.materials.clear();
|
|
67749
|
+
}
|
|
67750
|
+
|
|
67751
|
+
estimateNodeSize(meshIndex) {
|
|
67752
|
+
if (!this.json.meshes) return 0;
|
|
67753
|
+
|
|
67754
|
+
const meshDef = this.json.meshes[meshIndex];
|
|
67755
|
+
if (!meshDef || !meshDef.primitives) return 0;
|
|
67756
|
+
|
|
67757
|
+
let totalSize = 0;
|
|
67758
|
+
|
|
67759
|
+
// Estimate size for each primitive
|
|
67760
|
+
for (const primitive of meshDef.primitives) {
|
|
67761
|
+
// Check for attributes
|
|
67762
|
+
if (primitive.attributes) {
|
|
67763
|
+
// Calculate attributes size
|
|
67764
|
+
for (const [, accessorIndex] of Object.entries(primitive.attributes)) {
|
|
67765
|
+
if (accessorIndex === undefined) continue;
|
|
67766
|
+
|
|
67767
|
+
const accessor = this.json.accessors[accessorIndex];
|
|
67768
|
+
if (!accessor) continue;
|
|
67769
|
+
|
|
67770
|
+
const numComponents = this.getNumComponents(accessor.type);
|
|
67771
|
+
const bytesPerComponent = this.getComponentSize(accessor.componentType);
|
|
67772
|
+
totalSize += accessor.count * numComponents * bytesPerComponent;
|
|
67773
|
+
}
|
|
67774
|
+
}
|
|
67775
|
+
|
|
67776
|
+
// Calculate indices size if present
|
|
67777
|
+
if (primitive.indices !== undefined) {
|
|
67778
|
+
const accessor = this.json.accessors[primitive.indices];
|
|
67779
|
+
if (accessor) {
|
|
67780
|
+
const bytesPerComponent = this.getComponentSize(accessor.componentType);
|
|
67781
|
+
totalSize += accessor.count * bytesPerComponent;
|
|
67782
|
+
}
|
|
67783
|
+
}
|
|
67784
|
+
}
|
|
67785
|
+
|
|
67786
|
+
return totalSize;
|
|
67787
|
+
}
|
|
67788
|
+
}
|
|
67789
|
+
|
|
67790
|
+
class DynamicGltfLoader {
|
|
67791
|
+
constructor(camera, scene, renderer) {
|
|
67792
|
+
this.camera = camera;
|
|
67793
|
+
this.scene = scene;
|
|
67794
|
+
this.renderer = renderer;
|
|
67795
|
+
|
|
67796
|
+
this.eventHandlers = {
|
|
67797
|
+
geometryprogress: [],
|
|
67798
|
+
databasechunk: [],
|
|
67799
|
+
geometryend: [],
|
|
67800
|
+
geometryerror: [],
|
|
67801
|
+
update: [],
|
|
67802
|
+
geometrymemory: [],
|
|
67803
|
+
};
|
|
67804
|
+
|
|
67805
|
+
this.loadDistance = 100;
|
|
67806
|
+
this.unloadDistance = 150;
|
|
67807
|
+
this.checkInterval = 1000;
|
|
67808
|
+
|
|
67809
|
+
this.nodes = new Map();
|
|
67810
|
+
this.loadedMeshes = new Map();
|
|
67811
|
+
this.nodesToLoad = [];
|
|
67812
|
+
this.edgeNodes = [];
|
|
67813
|
+
this.structures = [];
|
|
67814
|
+
this.structureRoots = new Map();
|
|
67815
|
+
|
|
67816
|
+
this.memoryLimit = this.getAvailableMemory();
|
|
67817
|
+
this.loadedGeometrySize = 0;
|
|
67818
|
+
this.geometryCache = new Map();
|
|
67819
|
+
this.materialCache = new Map();
|
|
67820
|
+
this.textureCache = new Map();
|
|
67821
|
+
this.currentMemoryUsage = 0;
|
|
67822
|
+
|
|
67823
|
+
this.updateMemoryIndicator();
|
|
67824
|
+
|
|
67825
|
+
this.loadedMaterials = new Map();
|
|
67826
|
+
this.abortController = new AbortController();
|
|
67827
|
+
|
|
67828
|
+
this.batchSize = 10000;
|
|
67829
|
+
this.frameDelay = 0;
|
|
67830
|
+
|
|
67831
|
+
this.graphicsObjectLimit = 10000;
|
|
67832
|
+
this.totalLoadedObjects = 0;
|
|
67833
|
+
|
|
67834
|
+
this.lastUpdateTime = 0;
|
|
67835
|
+
this.updateInterval = 1000;
|
|
67836
|
+
|
|
67837
|
+
this.handleToObjects = new Map();
|
|
67838
|
+
|
|
67839
|
+
this.originalObjects = new Set();
|
|
67840
|
+
this.originalObjectsToSelection = new Set();
|
|
67841
|
+
|
|
67842
|
+
this.optimizedOriginalMap = new Map();
|
|
67843
|
+
this.mergedMesh = new Set();
|
|
67844
|
+
this.mergedLines = new Set();
|
|
67845
|
+
this.mergedLineSegments = new Set();
|
|
67846
|
+
this.mergedPoints = new Set();
|
|
67847
|
+
|
|
67848
|
+
this.isolatedObjects = [];
|
|
67849
|
+
this.useVAO = !!window.WebGL2RenderingContext && this.renderer.getContext() instanceof WebGL2RenderingContext;
|
|
67850
|
+
|
|
67851
|
+
this.handleToOptimizedObjects = new Map();
|
|
67852
|
+
|
|
67853
|
+
this.hiddenHandles = new Set();
|
|
67854
|
+
this.newOptimizedObjects = new Set();
|
|
67855
|
+
this.oldOptimizeObjects = new Set();
|
|
67856
|
+
}
|
|
67857
|
+
|
|
67858
|
+
getAvailableMemory() {
|
|
67859
|
+
let memoryLimit = 6 * 1024 * 1024 * 1024;
|
|
67860
|
+
try {
|
|
67861
|
+
if (navigator.deviceMemory) {
|
|
67862
|
+
memoryLimit = navigator.deviceMemory * 1024 * 1024 * 1024;
|
|
67863
|
+
} else if (performance.memory) {
|
|
67864
|
+
const jsHeapSizeLimit = performance.memory.jsHeapSizeLimit;
|
|
67865
|
+
if (jsHeapSizeLimit) {
|
|
67866
|
+
memoryLimit = Math.min(memoryLimit, jsHeapSizeLimit);
|
|
67867
|
+
}
|
|
67868
|
+
}
|
|
67869
|
+
|
|
67870
|
+
memoryLimit = Math.min(memoryLimit, 16 * 1024 * 1024 * 1024);
|
|
67871
|
+
memoryLimit = Math.max(memoryLimit, 2 * 1024 * 1024 * 1024);
|
|
67872
|
+
|
|
67873
|
+
console.log(`Available memory set to ${Math.round(memoryLimit / (1024 * 1024 * 1024))}GB`);
|
|
67874
|
+
} catch (error) {
|
|
67875
|
+
console.warn("Error detecting available memory:", error);
|
|
67876
|
+
}
|
|
67877
|
+
|
|
67878
|
+
return memoryLimit;
|
|
67879
|
+
}
|
|
67880
|
+
|
|
67881
|
+
getAbortController() {
|
|
67882
|
+
return this.abortController;
|
|
67883
|
+
}
|
|
67884
|
+
|
|
67885
|
+
abortLoading() {
|
|
67886
|
+
this.abortController.abort();
|
|
67887
|
+
}
|
|
67888
|
+
|
|
67889
|
+
updateMemoryIndicator() {
|
|
67890
|
+
this.dispatchEvent("geometrymemory", {
|
|
67891
|
+
currentUsage: this.currentMemoryUsage,
|
|
67892
|
+
limit: this.memoryLimit,
|
|
67893
|
+
});
|
|
67894
|
+
}
|
|
67895
|
+
|
|
67896
|
+
setMemoryLimit(bytesLimit) {
|
|
67897
|
+
// this.memoryLimit = bytesLimit;
|
|
67898
|
+
//this.updateMemoryIndicator();
|
|
67899
|
+
// console.log(`Memory limit set to ${Math.round(bytesLimit / (1024 * 1024))}MB`);
|
|
67900
|
+
}
|
|
67901
|
+
|
|
67902
|
+
estimateGeometrySize(nodeGroup) {
|
|
67903
|
+
let totalSize = 0;
|
|
67904
|
+
nodeGroup.traverse((child) => {
|
|
67905
|
+
if (child.geometry) {
|
|
67906
|
+
if (this.abortController.signal.aborted) {
|
|
67907
|
+
throw new DOMException("Loading aborted", "AbortError");
|
|
67908
|
+
}
|
|
67909
|
+
const geometry = child.geometry;
|
|
67910
|
+
|
|
67911
|
+
if (geometry.attributes) {
|
|
67912
|
+
Object.values(geometry.attributes).forEach((attribute) => {
|
|
67913
|
+
if (attribute && attribute.array) {
|
|
67914
|
+
totalSize += attribute.array.byteLength;
|
|
67915
|
+
}
|
|
67916
|
+
});
|
|
67917
|
+
}
|
|
67918
|
+
|
|
67919
|
+
if (geometry.index && geometry.index.array) {
|
|
67920
|
+
totalSize += geometry.index.array.byteLength;
|
|
67921
|
+
}
|
|
67922
|
+
}
|
|
67923
|
+
});
|
|
67924
|
+
|
|
67925
|
+
return totalSize;
|
|
67926
|
+
}
|
|
67927
|
+
|
|
67928
|
+
recalculateScene() {
|
|
67929
|
+
const geometries = [];
|
|
67930
|
+
this.scene.traverse((object) => {
|
|
67931
|
+
if (this.abortController.signal.aborted) {
|
|
67932
|
+
throw new DOMException("Loading aborted", "AbortError");
|
|
67933
|
+
}
|
|
67934
|
+
|
|
67935
|
+
if (object.geometry && !this.geometryCache.has(object.geometry.uuid)) {
|
|
67936
|
+
const size = this.estimateGeometrySize(object);
|
|
67937
|
+
this.geometryCache.set(object.geometry.uuid, size);
|
|
67938
|
+
geometries.push({
|
|
67939
|
+
object,
|
|
67940
|
+
size,
|
|
67941
|
+
distance: object.position.distanceTo(this.camera.position),
|
|
67942
|
+
});
|
|
67943
|
+
}
|
|
67944
|
+
});
|
|
67945
|
+
|
|
67946
|
+
if (this.abortController.signal.aborted) {
|
|
67947
|
+
throw new DOMException("Loading aborted", "AbortError");
|
|
67948
|
+
}
|
|
67949
|
+
|
|
67950
|
+
geometries.sort((a, b) => b.distance - a.distance);
|
|
67951
|
+
|
|
67952
|
+
let currentMemoryUsage = 0;
|
|
67953
|
+
for (const geo of geometries) {
|
|
67954
|
+
currentMemoryUsage += geo.size;
|
|
67955
|
+
}
|
|
67956
|
+
|
|
67957
|
+
if (currentMemoryUsage > this.memoryLimit) {
|
|
67958
|
+
console.log(`Memory usage (${Math.round(currentMemoryUsage / (1024 * 1024))}MB) exceeds limit`);
|
|
67959
|
+
|
|
67960
|
+
for (const geo of geometries) {
|
|
67961
|
+
if (currentMemoryUsage <= this.memoryLimit) break;
|
|
67962
|
+
|
|
67963
|
+
if (this.abortController.signal.aborted) {
|
|
67964
|
+
throw new DOMException("Loading aborted", "AbortError");
|
|
67965
|
+
}
|
|
67966
|
+
|
|
67967
|
+
const object = geo.object;
|
|
67968
|
+
if (object.geometry) {
|
|
67969
|
+
currentMemoryUsage -= geo.size;
|
|
67970
|
+
this.geometryCache.delete(object.geometry.uuid);
|
|
67971
|
+
object.geometry.dispose();
|
|
67972
|
+
object.visible = false;
|
|
67973
|
+
}
|
|
67974
|
+
}
|
|
67975
|
+
}
|
|
67976
|
+
|
|
67977
|
+
this.currentMemoryUsage = currentMemoryUsage;
|
|
67978
|
+
this.updateMemoryIndicator();
|
|
67979
|
+
|
|
67980
|
+
console.log(`Final memory usage: ${Math.round(currentMemoryUsage / (1024 * 1024))}MB`);
|
|
67981
|
+
}
|
|
67982
|
+
|
|
67983
|
+
async loadNode(nodeId) {
|
|
67984
|
+
const node = this.nodes.get(nodeId);
|
|
67985
|
+
if (!node || node.loaded || node.loading) return;
|
|
67986
|
+
|
|
67987
|
+
node.loading = true;
|
|
67988
|
+
const meshDef = node.structure.getJson().meshes[node.meshIndex];
|
|
67989
|
+
|
|
67990
|
+
try {
|
|
67991
|
+
for (const primitive of meshDef.primitives) {
|
|
67992
|
+
const positionAccessor = primitive.attributes.POSITION;
|
|
67993
|
+
const geometry = new BufferGeometry();
|
|
67994
|
+
const attributes = new Map();
|
|
67995
|
+
|
|
67996
|
+
attributes.set("position", node.structure.createBufferAttribute(positionAccessor));
|
|
67997
|
+
|
|
67998
|
+
if (primitive.attributes.NORMAL !== undefined) {
|
|
67999
|
+
attributes.set("normal", node.structure.createBufferAttribute(primitive.attributes.NORMAL));
|
|
68000
|
+
}
|
|
68001
|
+
|
|
68002
|
+
if (primitive.attributes.TEXCOORD_0 !== undefined) {
|
|
68003
|
+
attributes.set("uv", node.structure.createBufferAttribute(primitive.attributes.TEXCOORD_0));
|
|
68004
|
+
}
|
|
68005
|
+
|
|
68006
|
+
const loadedAttributes = await Promise.all(
|
|
68007
|
+
[...attributes.entries()].map(async ([name, promise]) => {
|
|
68008
|
+
const attribute = await promise;
|
|
68009
|
+
return [name, attribute];
|
|
68010
|
+
})
|
|
68011
|
+
);
|
|
68012
|
+
|
|
68013
|
+
loadedAttributes.forEach(([name, attribute]) => {
|
|
68014
|
+
geometry.setAttribute(name, attribute);
|
|
68015
|
+
});
|
|
68016
|
+
|
|
68017
|
+
if (primitive.indices !== undefined) {
|
|
68018
|
+
const indexAttribute = await node.structure.createBufferAttribute(primitive.indices);
|
|
68019
|
+
geometry.setIndex(indexAttribute);
|
|
68020
|
+
}
|
|
68021
|
+
|
|
68022
|
+
this.currentPrimitiveMode = primitive.mode;
|
|
68023
|
+
|
|
68024
|
+
let material;
|
|
68025
|
+
if (primitive.material !== undefined) {
|
|
68026
|
+
material = node.structure.materials.get(primitive.material) || this.createDefaultMaterial();
|
|
68027
|
+
} else {
|
|
68028
|
+
material = this.createDefaultMaterial();
|
|
68029
|
+
}
|
|
68030
|
+
|
|
68031
|
+
let mesh;
|
|
68032
|
+
if (primitive.mode === GL_CONSTANTS.POINTS) {
|
|
68033
|
+
const pointsMaterial = new PointsMaterial();
|
|
68034
|
+
Material.prototype.copy.call(pointsMaterial, material);
|
|
68035
|
+
pointsMaterial.color.copy(material.color);
|
|
68036
|
+
pointsMaterial.map = material.map;
|
|
68037
|
+
pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px
|
|
68038
|
+
|
|
68039
|
+
mesh = new Points(geometry, pointsMaterial);
|
|
68040
|
+
} else if (
|
|
68041
|
+
primitive.mode === GL_CONSTANTS.TRIANGLES ||
|
|
68042
|
+
primitive.mode === GL_CONSTANTS.TRIANGLE_STRIP ||
|
|
68043
|
+
primitive.mode === GL_CONSTANTS.TRIANGLE_FAN ||
|
|
68044
|
+
primitive.mode === undefined
|
|
68045
|
+
) {
|
|
68046
|
+
mesh = new Mesh(geometry, material);
|
|
68047
|
+
|
|
68048
|
+
if (primitive.mode === GL_CONSTANTS.TRIANGLE_STRIP) {
|
|
68049
|
+
mesh.drawMode = TriangleStripDrawMode;
|
|
68050
|
+
} else if (primitive.mode === GL_CONSTANTS.TRIANGLE_FAN) {
|
|
68051
|
+
mesh.drawMode = TriangleFanDrawMode;
|
|
68052
|
+
}
|
|
68053
|
+
} else if (primitive.mode === GL_CONSTANTS.LINES) {
|
|
68054
|
+
mesh = new LineSegments(geometry, material);
|
|
68055
|
+
} else if (primitive.mode === GL_CONSTANTS.LINE_STRIP) {
|
|
68056
|
+
mesh = new Line$1(geometry, material);
|
|
68057
|
+
} else if (primitive.mode === GL_CONSTANTS.LINE_LOOP) {
|
|
68058
|
+
mesh = new LineLoop(geometry, material);
|
|
68059
|
+
}
|
|
68060
|
+
|
|
68061
|
+
if (node.extras) {
|
|
68062
|
+
mesh.userData = { ...mesh.userData, ...node.extras };
|
|
68063
|
+
}
|
|
68064
|
+
|
|
68065
|
+
if (meshDef.extras) {
|
|
68066
|
+
mesh.userData = { ...mesh.userData, ...meshDef.extras };
|
|
68067
|
+
}
|
|
68068
|
+
|
|
68069
|
+
if (primitive.extras) {
|
|
68070
|
+
mesh.userData = { ...mesh.userData, ...primitive.extras };
|
|
68071
|
+
}
|
|
68072
|
+
|
|
68073
|
+
if (node.handle) {
|
|
68074
|
+
mesh.userData.handle = node.handle;
|
|
68075
|
+
} else {
|
|
68076
|
+
mesh.userData.handle = `${node.structure.id}_${mesh.userData.handle}`;
|
|
68077
|
+
}
|
|
68078
|
+
|
|
68079
|
+
if (mesh.material.name === "edges") {
|
|
68080
|
+
mesh.userData.isEdge = true;
|
|
68081
|
+
} else {
|
|
68082
|
+
mesh.userData.isEdge = false;
|
|
68083
|
+
}
|
|
68084
|
+
|
|
68085
|
+
this.registerObjectWithHandle(mesh, mesh.userData.handle);
|
|
68086
|
+
|
|
68087
|
+
mesh.position.copy(node.position);
|
|
68088
|
+
|
|
68089
|
+
if (!geometry.attributes.normal) {
|
|
68090
|
+
geometry.computeVertexNormals();
|
|
68091
|
+
}
|
|
68092
|
+
|
|
68093
|
+
if (material.aoMap && geometry.attributes.uv) {
|
|
68094
|
+
geometry.setAttribute("uv2", geometry.attributes.uv);
|
|
68095
|
+
}
|
|
68096
|
+
if (node.group) {
|
|
68097
|
+
node.group.add(mesh);
|
|
68098
|
+
} else {
|
|
68099
|
+
this.scene.add(mesh);
|
|
68100
|
+
}
|
|
68101
|
+
node.object = mesh;
|
|
68102
|
+
|
|
68103
|
+
this.totalLoadedObjects++;
|
|
68104
|
+
mesh.visible = this.totalLoadedObjects < this.graphicsObjectLimit;
|
|
68105
|
+
}
|
|
68106
|
+
|
|
68107
|
+
node.loaded = true;
|
|
68108
|
+
node.loading = false;
|
|
68109
|
+
|
|
68110
|
+
const geometrySize = this.estimateGeometrySize(node.object);
|
|
68111
|
+
this.geometryCache.set(node.object.uuid, geometrySize);
|
|
68112
|
+
this.currentMemoryUsage += geometrySize;
|
|
68113
|
+
} catch (error) {
|
|
68114
|
+
if (error.name !== "AbortError") {
|
|
68115
|
+
console.error(`Error loading node ${nodeId}:`, error);
|
|
68116
|
+
}
|
|
68117
|
+
node.loading = false;
|
|
68118
|
+
}
|
|
68119
|
+
}
|
|
68120
|
+
|
|
68121
|
+
unloadNode(nodeId) {
|
|
68122
|
+
const node = this.nodes.get(nodeId);
|
|
68123
|
+
if (!node || !node.loaded) return;
|
|
68124
|
+
|
|
68125
|
+
if (node.object) {
|
|
68126
|
+
if (node.object.parent) {
|
|
68127
|
+
node.object.parent.remove(node.object);
|
|
68128
|
+
} else {
|
|
68129
|
+
this.scene.remove(node.object);
|
|
68130
|
+
}
|
|
68131
|
+
|
|
68132
|
+
node.object.traverse((child) => {
|
|
68133
|
+
if (child.geometry) {
|
|
68134
|
+
const geometrySize = this.geometryCache.get(child.geometry.uuid) || 0;
|
|
68135
|
+
this.currentMemoryUsage -= geometrySize;
|
|
68136
|
+
this.geometryCache.delete(child.geometry.uuid);
|
|
68137
|
+
child.geometry.dispose();
|
|
68138
|
+
}
|
|
68139
|
+
});
|
|
68140
|
+
|
|
68141
|
+
node.object = null;
|
|
68142
|
+
node.loaded = false;
|
|
68143
|
+
this.updateMemoryIndicator();
|
|
68144
|
+
console.log(`Unloaded node: ${nodeId}`);
|
|
68145
|
+
}
|
|
68146
|
+
}
|
|
68147
|
+
|
|
68148
|
+
checkDistances() {
|
|
68149
|
+
const cameraPosition = this.camera.position;
|
|
68150
|
+
|
|
68151
|
+
this.nodes.forEach((node, nodeId) => {
|
|
68152
|
+
const distance = cameraPosition.distanceTo(node.position);
|
|
68153
|
+
|
|
68154
|
+
if (node.loaded) {
|
|
68155
|
+
if (distance > this.unloadDistance) {
|
|
68156
|
+
this.unloadNode(nodeId);
|
|
68157
|
+
}
|
|
68158
|
+
} else if (!node.loading) {
|
|
68159
|
+
if (distance < this.loadDistance) {
|
|
68160
|
+
this.loadNode(nodeId);
|
|
68161
|
+
}
|
|
68162
|
+
}
|
|
68163
|
+
});
|
|
68164
|
+
}
|
|
68165
|
+
|
|
68166
|
+
async loadStructure(structures) {
|
|
68167
|
+
this.clear();
|
|
68168
|
+
|
|
68169
|
+
const structureArray = Array.isArray(structures) ? structures : [structures];
|
|
68170
|
+
|
|
68171
|
+
for (const structure of structureArray) {
|
|
68172
|
+
this.structures.push(structure);
|
|
68173
|
+
}
|
|
68174
|
+
|
|
68175
|
+
for (const structure of this.structures) {
|
|
68176
|
+
try {
|
|
68177
|
+
await structure.loadTextures();
|
|
68178
|
+
await structure.loadMaterials();
|
|
68179
|
+
} catch (error) {
|
|
68180
|
+
console.error("Error loading materials:", error);
|
|
68181
|
+
throw error;
|
|
68182
|
+
}
|
|
68183
|
+
}
|
|
68184
|
+
|
|
68185
|
+
await this.processSceneHierarchy();
|
|
68186
|
+
}
|
|
68187
|
+
|
|
68188
|
+
async processSceneHierarchy() {
|
|
68189
|
+
if (this.structures.length === 0) {
|
|
68190
|
+
throw new Error("No GLTF structures loaded");
|
|
68191
|
+
}
|
|
68192
|
+
|
|
68193
|
+
this.nodesToLoad = [];
|
|
68194
|
+
|
|
68195
|
+
let estimatedSize = 0;
|
|
68196
|
+
|
|
68197
|
+
for (const structure of this.structures) {
|
|
68198
|
+
const gltf = structure.getJson();
|
|
68199
|
+
|
|
68200
|
+
if (!gltf.scenes || !gltf.scenes.length) {
|
|
68201
|
+
console.warn("No scenes found in GLTF structure");
|
|
68202
|
+
continue;
|
|
68203
|
+
}
|
|
68204
|
+
|
|
68205
|
+
estimatedSize += gltf.buffers[0].byteLength;
|
|
68206
|
+
|
|
68207
|
+
const rootGroup = new Group$1();
|
|
68208
|
+
rootGroup.name = `structure_${structure.id}_root`;
|
|
68209
|
+
this.scene.add(rootGroup);
|
|
68210
|
+
this.structureRoots.set(structure.id, rootGroup);
|
|
68211
|
+
|
|
68212
|
+
const scene = gltf.scenes[gltf.scene || 0];
|
|
68213
|
+
|
|
68214
|
+
for (const nodeIndex of scene.nodes) {
|
|
68215
|
+
await this.processNodeHierarchy(structure, nodeIndex, rootGroup);
|
|
68216
|
+
}
|
|
68217
|
+
}
|
|
68218
|
+
|
|
68219
|
+
const ignoreEdges = estimatedSize * 2 > this.memoryLimit;
|
|
68220
|
+
|
|
68221
|
+
this.nodesToLoad.sort((a, b) => {
|
|
68222
|
+
const nodeA = this.nodes.get(a);
|
|
68223
|
+
const nodeB = this.nodes.get(b);
|
|
68224
|
+
|
|
68225
|
+
if (!nodeA?.geometryExtents || !nodeB?.geometryExtents) {
|
|
68226
|
+
return 0;
|
|
68227
|
+
}
|
|
68228
|
+
|
|
68229
|
+
const sizeA = nodeA.geometryExtents.getSize(new Vector3());
|
|
68230
|
+
const sizeB = nodeB.geometryExtents.getSize(new Vector3());
|
|
68231
|
+
const volumeA = sizeA.x * sizeA.y * sizeA.z;
|
|
68232
|
+
const volumeB = sizeB.x * sizeB.y * sizeB.z;
|
|
68233
|
+
|
|
68234
|
+
return volumeB - volumeA;
|
|
68235
|
+
});
|
|
68236
|
+
|
|
68237
|
+
if (!ignoreEdges) {
|
|
68238
|
+
this.nodesToLoad.push(...this.edgeNodes);
|
|
68239
|
+
}
|
|
68240
|
+
|
|
68241
|
+
this.dispatchEvent("databasechunk", {
|
|
68242
|
+
totalNodes: this.nodesToLoad.length,
|
|
68243
|
+
structures: this.structures.map((s) => ({
|
|
68244
|
+
id: s.id,
|
|
68245
|
+
nodeCount: this.nodesToLoad.filter((nodeId) => nodeId.startsWith(s.id)).length,
|
|
68246
|
+
})),
|
|
68247
|
+
});
|
|
68248
|
+
}
|
|
68249
|
+
|
|
68250
|
+
async processNodeHierarchy(structure, nodeId, parentGroup) {
|
|
68251
|
+
const nodeDef = structure.json.nodes[nodeId];
|
|
68252
|
+
let nodeGroup = null;
|
|
68253
|
+
|
|
68254
|
+
let handle = null;
|
|
68255
|
+
if (nodeDef.extras?.handle) {
|
|
68256
|
+
handle = `${structure.id}_${nodeDef.extras.handle}`;
|
|
68257
|
+
}
|
|
68258
|
+
|
|
68259
|
+
if (nodeDef.camera !== undefined) {
|
|
68260
|
+
const camera = this.loadCamera(structure, nodeDef.camera, nodeDef);
|
|
68261
|
+
if (nodeDef.extras) {
|
|
68262
|
+
camera.userData = { ...camera.userData, ...nodeDef.extras };
|
|
68263
|
+
}
|
|
68264
|
+
this.scene.add(camera);
|
|
68265
|
+
return;
|
|
68266
|
+
}
|
|
68267
|
+
|
|
68268
|
+
const needsGroup = this.needsGroupForNode(structure, nodeDef);
|
|
68269
|
+
|
|
68270
|
+
if (needsGroup) {
|
|
68271
|
+
nodeGroup = new Group$1();
|
|
68272
|
+
nodeGroup.name = nodeDef.name || `node_${nodeId}`;
|
|
68273
|
+
|
|
68274
|
+
if (nodeDef.extras) {
|
|
68275
|
+
nodeGroup.userData = { ...nodeDef.extras };
|
|
68276
|
+
if (nodeGroup.userData.handle) {
|
|
68277
|
+
nodeGroup.userData.handle = `${structure.id}_${nodeGroup.userData.handle}`;
|
|
68278
|
+
}
|
|
68279
|
+
}
|
|
68280
|
+
|
|
68281
|
+
if (nodeDef.matrix) {
|
|
68282
|
+
nodeGroup.matrix.fromArray(nodeDef.matrix);
|
|
68283
|
+
nodeGroup.matrixAutoUpdate = false;
|
|
68284
|
+
} else if (nodeDef.translation || nodeDef.rotation || nodeDef.scale) {
|
|
68285
|
+
const position = nodeDef.translation ? new Vector3().fromArray(nodeDef.translation) : new Vector3();
|
|
68286
|
+
const quaternion = nodeDef.rotation ? new Quaternion().fromArray(nodeDef.rotation) : new Quaternion();
|
|
68287
|
+
const scale = nodeDef.scale ? new Vector3().fromArray(nodeDef.scale) : new Vector3(1, 1, 1);
|
|
68288
|
+
nodeGroup.matrix.compose(position, quaternion, scale);
|
|
68289
|
+
nodeGroup.matrixAutoUpdate = false;
|
|
68290
|
+
}
|
|
68291
|
+
|
|
68292
|
+
if (parentGroup) {
|
|
68293
|
+
parentGroup.add(nodeGroup);
|
|
68294
|
+
}
|
|
68295
|
+
}
|
|
68296
|
+
|
|
68297
|
+
if (nodeDef.mesh !== undefined) {
|
|
68298
|
+
const nodeMatrix = new Matrix4();
|
|
68299
|
+
const uniqueNodeId = `${structure.id}_${nodeId}`;
|
|
68300
|
+
const meshDef = structure.json.meshes[nodeDef.mesh];
|
|
68301
|
+
const geometryExtents = new Box3();
|
|
68302
|
+
|
|
68303
|
+
for (const primitive of meshDef.primitives) {
|
|
68304
|
+
const positionAccessor = structure.json.accessors[primitive.attributes.POSITION];
|
|
68305
|
+
if (positionAccessor && positionAccessor.min && positionAccessor.max) {
|
|
68306
|
+
const primitiveBox = new Box3(
|
|
68307
|
+
new Vector3().fromArray(positionAccessor.min),
|
|
68308
|
+
new Vector3().fromArray(positionAccessor.max)
|
|
68309
|
+
);
|
|
68310
|
+
geometryExtents.union(primitiveBox);
|
|
68311
|
+
}
|
|
68312
|
+
}
|
|
68313
|
+
|
|
68314
|
+
let isEdge = false;
|
|
68315
|
+
if (meshDef.primitives[0].material !== undefined) {
|
|
68316
|
+
const material = structure.json.materials[meshDef.primitives[0].material];
|
|
68317
|
+
if (material?.name === "edges") {
|
|
68318
|
+
isEdge = true;
|
|
68319
|
+
}
|
|
68320
|
+
}
|
|
68321
|
+
|
|
68322
|
+
if (!isEdge) {
|
|
68323
|
+
this.nodesToLoad.push(uniqueNodeId);
|
|
68324
|
+
} else {
|
|
68325
|
+
this.edgeNodes.push(uniqueNodeId);
|
|
68326
|
+
}
|
|
68327
|
+
|
|
68328
|
+
this.nodes.set(uniqueNodeId, {
|
|
68329
|
+
position: nodeGroup ? nodeGroup.position.clone() : new Vector3().setFromMatrixPosition(nodeMatrix),
|
|
68330
|
+
nodeIndex: nodeId,
|
|
68331
|
+
meshIndex: nodeDef.mesh,
|
|
68332
|
+
loaded: false,
|
|
68333
|
+
loading: false,
|
|
68334
|
+
object: null,
|
|
68335
|
+
group: nodeGroup || parentGroup,
|
|
68336
|
+
structure,
|
|
68337
|
+
extras: nodeDef.extras,
|
|
68338
|
+
geometryExtents,
|
|
68339
|
+
handle,
|
|
68340
|
+
});
|
|
68341
|
+
}
|
|
68342
|
+
|
|
68343
|
+
if (nodeDef.children) {
|
|
68344
|
+
for (const childId of nodeDef.children) {
|
|
68345
|
+
await this.processNodeHierarchy(structure, childId, nodeGroup || parentGroup);
|
|
68346
|
+
}
|
|
68347
|
+
}
|
|
68348
|
+
|
|
68349
|
+
return nodeGroup;
|
|
68350
|
+
}
|
|
68351
|
+
|
|
68352
|
+
needsGroupForNode(structure, nodeDef) {
|
|
68353
|
+
const hasTransforms = nodeDef.matrix || nodeDef.translation || nodeDef.rotation || nodeDef.scale;
|
|
68354
|
+
|
|
68355
|
+
const hasMultiplePrimitives =
|
|
68356
|
+
nodeDef.mesh !== undefined && structure.json.meshes[nodeDef.mesh].primitives.length > 1;
|
|
68357
|
+
|
|
68358
|
+
return hasTransforms !== undefined || hasMultiplePrimitives;
|
|
68359
|
+
}
|
|
68360
|
+
|
|
68361
|
+
async processNodes() {
|
|
68362
|
+
const nodesToLoad = this.nodesToLoad;
|
|
68363
|
+
let loadedCount = 0;
|
|
68364
|
+
const totalNodes = nodesToLoad.length;
|
|
68365
|
+
|
|
68366
|
+
try {
|
|
68367
|
+
while (loadedCount < totalNodes) {
|
|
68368
|
+
const batch = nodesToLoad.slice(loadedCount, loadedCount + this.batchSize);
|
|
68369
|
+
const batchPromises = [];
|
|
68370
|
+
|
|
68371
|
+
for (const nodeId of batch) {
|
|
68372
|
+
if (this.abortController.signal.aborted) {
|
|
68373
|
+
throw new DOMException("Loading aborted", "AbortError");
|
|
68374
|
+
}
|
|
68375
|
+
|
|
68376
|
+
const estimatedSize = await this.estimateNodeSize(nodeId);
|
|
68377
|
+
|
|
68378
|
+
if (this.currentMemoryUsage + estimatedSize > this.memoryLimit) {
|
|
68379
|
+
console.log(`Memory limit reached after loading ${loadedCount} nodes`);
|
|
68380
|
+
this.dispatchEvent("geometryerror", { message: "Memory limit reached" });
|
|
68381
|
+
this.dispatchEvent("update");
|
|
68382
|
+
return loadedCount;
|
|
68383
|
+
}
|
|
68384
|
+
|
|
68385
|
+
batchPromises.push(this.loadNode(nodeId));
|
|
68386
|
+
}
|
|
68387
|
+
|
|
68388
|
+
await Promise.all(batchPromises);
|
|
68389
|
+
loadedCount += batch.length;
|
|
68390
|
+
|
|
68391
|
+
this.updateMemoryIndicator();
|
|
68392
|
+
this.dispatchEvent("geometryprogress", {
|
|
68393
|
+
percentage: Math.round((loadedCount / totalNodes) * 100),
|
|
68394
|
+
loaded: loadedCount,
|
|
68395
|
+
total: totalNodes,
|
|
68396
|
+
});
|
|
68397
|
+
|
|
68398
|
+
const currentTime = Date.now();
|
|
68399
|
+
if (currentTime - this.lastUpdateTime >= this.updateInterval) {
|
|
68400
|
+
this.dispatchEvent("update");
|
|
68401
|
+
this.lastUpdateTime = currentTime;
|
|
68402
|
+
}
|
|
68403
|
+
|
|
68404
|
+
await new Promise((resolve) => {
|
|
68405
|
+
setTimeout(resolve, 0);
|
|
68406
|
+
});
|
|
68407
|
+
}
|
|
68408
|
+
|
|
68409
|
+
this.dispatchEvent("geometryend", {
|
|
68410
|
+
totalLoaded: loadedCount,
|
|
68411
|
+
totalNodes,
|
|
68412
|
+
});
|
|
68413
|
+
|
|
68414
|
+
return loadedCount;
|
|
68415
|
+
} catch (error) {
|
|
68416
|
+
this.dispatchEvent("geometryerror", { error });
|
|
68417
|
+
throw error;
|
|
68418
|
+
}
|
|
68419
|
+
}
|
|
68420
|
+
|
|
68421
|
+
async loadNodes() {
|
|
68422
|
+
console.time("process nodes");
|
|
68423
|
+
await this.processNodes();
|
|
68424
|
+
console.timeEnd("process nodes");
|
|
68425
|
+
|
|
68426
|
+
console.time("optimize scene");
|
|
68427
|
+
await this.optimizeScene();
|
|
68428
|
+
console.timeEnd("optimize scene");
|
|
68429
|
+
}
|
|
68430
|
+
|
|
68431
|
+
cleanupPartialLoad() {
|
|
68432
|
+
this.nodesToLoad.forEach((nodeId) => {
|
|
68433
|
+
const node = this.nodes.get(nodeId);
|
|
68434
|
+
if (node && node.loading) {
|
|
68435
|
+
this.unloadNode(nodeId);
|
|
68436
|
+
}
|
|
68437
|
+
});
|
|
68438
|
+
}
|
|
68439
|
+
|
|
68440
|
+
createDefaultMaterial() {
|
|
68441
|
+
if (this.currentPrimitiveMode === GL_CONSTANTS.POINTS) {
|
|
68442
|
+
return new PointsMaterial({
|
|
68443
|
+
color: new Color(0x808080),
|
|
68444
|
+
size: 0.05,
|
|
68445
|
+
sizeAttenuation: true,
|
|
68446
|
+
alphaTest: 0.5,
|
|
68447
|
+
transparent: true,
|
|
68448
|
+
vertexColors: false,
|
|
68449
|
+
blending: NormalBlending,
|
|
68450
|
+
depthWrite: false,
|
|
68451
|
+
depthTest: true,
|
|
68452
|
+
});
|
|
68453
|
+
} else {
|
|
68454
|
+
return new MeshStandardMaterial({
|
|
68455
|
+
color: 0x808080,
|
|
68456
|
+
metalness: 0.0,
|
|
68457
|
+
roughness: 1.0,
|
|
68458
|
+
side: DoubleSide,
|
|
68459
|
+
});
|
|
68460
|
+
}
|
|
68461
|
+
}
|
|
68462
|
+
|
|
68463
|
+
async estimateNodeSize(nodeId) {
|
|
68464
|
+
const node = this.nodes.get(nodeId);
|
|
68465
|
+
if (!node) return 0;
|
|
68466
|
+
return await node.structure.estimateNodeSize(node.meshIndex);
|
|
68467
|
+
}
|
|
68468
|
+
|
|
68469
|
+
getTotalGeometryExtent() {
|
|
68470
|
+
const totalExtent = new Box3();
|
|
68471
|
+
|
|
68472
|
+
for (const node of this.nodes.values()) {
|
|
68473
|
+
if (!node.geometryExtents) continue;
|
|
68474
|
+
|
|
68475
|
+
if (node.object && this.hiddenHandles.has(node.object.userData.handle)) continue;
|
|
68476
|
+
|
|
68477
|
+
const transformedBox = node.geometryExtents.clone();
|
|
68478
|
+
|
|
68479
|
+
if (node.group && node.group.matrix) {
|
|
68480
|
+
transformedBox.applyMatrix4(node.group.matrix);
|
|
68481
|
+
|
|
68482
|
+
if (node.group.parent && node.group.parent.matrix) {
|
|
68483
|
+
transformedBox.applyMatrix4(node.group.parent.matrix);
|
|
68484
|
+
}
|
|
68485
|
+
}
|
|
68486
|
+
totalExtent.union(transformedBox);
|
|
68487
|
+
}
|
|
68488
|
+
|
|
68489
|
+
return totalExtent;
|
|
68490
|
+
}
|
|
68491
|
+
|
|
68492
|
+
loadCamera(structure, cameraIndex, nodeDef) {
|
|
68493
|
+
const cameraDef = structure.getJson().cameras[cameraIndex];
|
|
68494
|
+
const params = cameraDef[cameraDef.type];
|
|
68495
|
+
|
|
68496
|
+
let camera;
|
|
68497
|
+
if (cameraDef.type === "perspective") {
|
|
68498
|
+
camera = new PerspectiveCamera(
|
|
68499
|
+
MathUtils.radToDeg(params.yfov),
|
|
68500
|
+
params.aspectRatio || 1,
|
|
68501
|
+
params.znear || 1,
|
|
68502
|
+
params.zfar || 2e6
|
|
68503
|
+
);
|
|
68504
|
+
} else if (cameraDef.type === "orthographic") {
|
|
68505
|
+
camera = new OrthographicCamera(
|
|
68506
|
+
params.xmag / -2,
|
|
68507
|
+
params.xmag / 2,
|
|
68508
|
+
params.ymag / 2,
|
|
68509
|
+
params.ymag / -2,
|
|
68510
|
+
params.znear,
|
|
68511
|
+
params.zfar
|
|
68512
|
+
);
|
|
68513
|
+
}
|
|
68514
|
+
|
|
68515
|
+
if (nodeDef.matrix) {
|
|
68516
|
+
camera.matrix.fromArray(nodeDef.matrix);
|
|
68517
|
+
camera.matrix.decompose(camera.position, camera.quaternion, camera.scale);
|
|
68518
|
+
} else {
|
|
68519
|
+
if (nodeDef.translation) {
|
|
68520
|
+
camera.position.fromArray(nodeDef.translation);
|
|
68521
|
+
}
|
|
68522
|
+
if (nodeDef.rotation) {
|
|
68523
|
+
camera.quaternion.fromArray(nodeDef.rotation);
|
|
68524
|
+
}
|
|
68525
|
+
if (nodeDef.scale) {
|
|
68526
|
+
camera.scale.fromArray(nodeDef.scale);
|
|
68527
|
+
}
|
|
68528
|
+
}
|
|
68529
|
+
|
|
68530
|
+
return camera;
|
|
68531
|
+
}
|
|
68532
|
+
|
|
68533
|
+
clearNodesToLoad() {
|
|
68534
|
+
this.nodesToLoad = [];
|
|
68535
|
+
}
|
|
68536
|
+
|
|
68537
|
+
async addStructure(loadController) {
|
|
68538
|
+
const structure = new GltfStructure();
|
|
68539
|
+
await structure.initialize(loadController);
|
|
68540
|
+
this.structures.push(structure);
|
|
68541
|
+
return structure;
|
|
68542
|
+
}
|
|
68543
|
+
|
|
68544
|
+
removeOptimization() {
|
|
68545
|
+
this.originalObjects.forEach((obj) => (obj.visible = true));
|
|
68546
|
+
|
|
68547
|
+
const disposeMerged = (obj) => {
|
|
68548
|
+
if (obj.parent) {
|
|
68549
|
+
obj.parent.remove(obj);
|
|
68550
|
+
}
|
|
68551
|
+
if (obj.geometry) {
|
|
68552
|
+
obj.geometry.dispose();
|
|
68553
|
+
}
|
|
68554
|
+
};
|
|
68555
|
+
|
|
68556
|
+
if (this.structureGroups) {
|
|
68557
|
+
for (const group of this.structureGroups.values()) {
|
|
68558
|
+
group.meshes.forEach(disposeMerged);
|
|
68559
|
+
group.lines.forEach(disposeMerged);
|
|
68560
|
+
group.lineSegments.forEach(disposeMerged);
|
|
68561
|
+
group.meshes.clear();
|
|
68562
|
+
group.lines.clear();
|
|
68563
|
+
group.lineSegments.clear();
|
|
68564
|
+
}
|
|
68565
|
+
}
|
|
68566
|
+
this.optimizedOriginalMap.clear();
|
|
68567
|
+
this.mergedMesh.clear();
|
|
68568
|
+
this.mergedLines.clear();
|
|
68569
|
+
this.mergedLineSegments.clear();
|
|
68570
|
+
this.originalObjects.clear();
|
|
68571
|
+
this.originalObjectsToSelection.clear();
|
|
68572
|
+
}
|
|
68573
|
+
|
|
68574
|
+
clear() {
|
|
68575
|
+
// Clear all structures
|
|
68576
|
+
this.structures.forEach((structure) => {
|
|
68577
|
+
if (structure) {
|
|
68578
|
+
structure.clear();
|
|
68579
|
+
}
|
|
68580
|
+
});
|
|
68581
|
+
this.structures = [];
|
|
68582
|
+
|
|
68583
|
+
// Clear all nodes and unload their objects
|
|
68584
|
+
this.nodes.forEach((node) => {
|
|
68585
|
+
if (node.object) {
|
|
68586
|
+
if (node.object.parent) {
|
|
68587
|
+
node.object.parent.remove(node.object);
|
|
68588
|
+
}
|
|
68589
|
+
if (node.object.geometry) {
|
|
68590
|
+
node.object.geometry.dispose();
|
|
68591
|
+
}
|
|
68592
|
+
if (node.object.material) {
|
|
68593
|
+
if (Array.isArray(node.object.material)) {
|
|
68594
|
+
node.object.material.forEach((material) => material.dispose());
|
|
68595
|
+
} else {
|
|
68596
|
+
node.object.material.dispose();
|
|
68597
|
+
}
|
|
68598
|
+
}
|
|
68599
|
+
}
|
|
68600
|
+
});
|
|
68601
|
+
this.nodes.clear();
|
|
68602
|
+
|
|
68603
|
+
// Clear all loaded meshes
|
|
68604
|
+
this.loadedMeshes.forEach((mesh) => {
|
|
68605
|
+
if (mesh.geometry) mesh.geometry.dispose();
|
|
68606
|
+
if (mesh.material) {
|
|
68607
|
+
if (Array.isArray(mesh.material)) {
|
|
68608
|
+
mesh.material.forEach((material) => material.dispose());
|
|
68609
|
+
} else {
|
|
68610
|
+
mesh.material.dispose();
|
|
68611
|
+
}
|
|
68612
|
+
}
|
|
68613
|
+
});
|
|
68614
|
+
this.loadedMeshes.clear();
|
|
68615
|
+
|
|
68616
|
+
// Clear all structure roots and their children
|
|
68617
|
+
this.structureRoots.forEach((rootGroup) => {
|
|
68618
|
+
if (rootGroup) {
|
|
68619
|
+
rootGroup.traverse((child) => {
|
|
68620
|
+
if (child.geometry) child.geometry.dispose();
|
|
68621
|
+
if (child.material) {
|
|
68622
|
+
if (Array.isArray(child.material)) {
|
|
68623
|
+
child.material.forEach((material) => material.dispose());
|
|
68624
|
+
} else {
|
|
68625
|
+
child.material.dispose();
|
|
68626
|
+
}
|
|
68627
|
+
}
|
|
68628
|
+
});
|
|
68629
|
+
if (rootGroup.parent) {
|
|
68630
|
+
rootGroup.parent.remove(rootGroup);
|
|
68631
|
+
}
|
|
68632
|
+
}
|
|
68633
|
+
});
|
|
68634
|
+
this.structureRoots.clear();
|
|
68635
|
+
|
|
68636
|
+
// Clear all optimized objects
|
|
68637
|
+
this.mergedMesh.forEach((mesh) => {
|
|
68638
|
+
if (mesh.geometry) mesh.geometry.dispose();
|
|
68639
|
+
if (mesh.material) {
|
|
68640
|
+
if (Array.isArray(mesh.material)) {
|
|
68641
|
+
mesh.material.forEach((material) => material.dispose());
|
|
68642
|
+
} else {
|
|
68643
|
+
mesh.material.dispose();
|
|
68644
|
+
}
|
|
68645
|
+
}
|
|
68646
|
+
if (mesh.parent) mesh.parent.remove(mesh);
|
|
68647
|
+
});
|
|
68648
|
+
this.mergedMesh.clear();
|
|
68649
|
+
|
|
68650
|
+
this.mergedLines.forEach((line) => {
|
|
68651
|
+
if (line.geometry) line.geometry.dispose();
|
|
68652
|
+
if (line.material) line.material.dispose();
|
|
68653
|
+
if (line.parent) line.parent.remove(line);
|
|
68654
|
+
});
|
|
68655
|
+
this.mergedLines.clear();
|
|
68656
|
+
|
|
68657
|
+
this.mergedLineSegments.forEach((lineSegment) => {
|
|
68658
|
+
if (lineSegment.geometry) lineSegment.geometry.dispose();
|
|
68659
|
+
if (lineSegment.material) lineSegment.material.dispose();
|
|
68660
|
+
if (lineSegment.parent) lineSegment.parent.remove(lineSegment);
|
|
68661
|
+
});
|
|
68662
|
+
this.mergedLineSegments.clear();
|
|
68663
|
+
|
|
68664
|
+
this.mergedPoints.forEach((points) => {
|
|
68665
|
+
if (points.geometry) points.geometry.dispose();
|
|
68666
|
+
if (points.material) points.material.dispose();
|
|
68667
|
+
if (points.parent) points.parent.remove(points);
|
|
68668
|
+
});
|
|
68669
|
+
this.mergedPoints.clear();
|
|
68670
|
+
|
|
68671
|
+
// Clear all caches
|
|
68672
|
+
this.geometryCache.clear();
|
|
68673
|
+
this.materialCache.clear();
|
|
68674
|
+
this.textureCache.clear();
|
|
68675
|
+
this.loadedMaterials.clear();
|
|
68676
|
+
|
|
68677
|
+
// Clear all maps and sets
|
|
68678
|
+
this.nodesToLoad = [];
|
|
68679
|
+
this.handleToObjects.clear();
|
|
68680
|
+
this.originalObjects.clear();
|
|
68681
|
+
this.originalObjectsToSelection.clear();
|
|
68682
|
+
this.optimizedOriginalMap.clear();
|
|
68683
|
+
this.handleToOptimizedObjects.clear();
|
|
68684
|
+
this.hiddenHandles.clear();
|
|
68685
|
+
this.newOptimizedObjects.clear();
|
|
68686
|
+
this.oldOptimizeObjects.clear();
|
|
68687
|
+
this.isolatedObjects = [];
|
|
68688
|
+
|
|
68689
|
+
// Reset counters and state
|
|
68690
|
+
this.totalLoadedObjects = 0;
|
|
68691
|
+
this.lastUpdateTime = 0;
|
|
68692
|
+
this.currentMemoryUsage = 0;
|
|
68693
|
+
this.loadedGeometrySize = 0;
|
|
68694
|
+
|
|
68695
|
+
this.abortController = new AbortController();
|
|
68696
|
+
this.updateMemoryIndicator();
|
|
68697
|
+
}
|
|
68698
|
+
|
|
68699
|
+
setStructureTransform(structureId, matrix) {
|
|
68700
|
+
const rootGroup = this.structureRoots.get(structureId);
|
|
68701
|
+
if (rootGroup) {
|
|
68702
|
+
rootGroup.matrix.copy(matrix);
|
|
68703
|
+
rootGroup.matrix.decompose(rootGroup.position, rootGroup.quaternion, rootGroup.scale);
|
|
68704
|
+
return true;
|
|
68705
|
+
}
|
|
68706
|
+
return false;
|
|
68707
|
+
}
|
|
68708
|
+
|
|
68709
|
+
getStructureRootGroup(structureId) {
|
|
68710
|
+
return this.structureRoots.get(structureId);
|
|
68711
|
+
}
|
|
68712
|
+
|
|
68713
|
+
addEventListener(event, handler) {
|
|
68714
|
+
if (this.eventHandlers[event]) {
|
|
68715
|
+
this.eventHandlers[event].push(handler);
|
|
68716
|
+
}
|
|
68717
|
+
}
|
|
68718
|
+
|
|
68719
|
+
removeEventListener(event, handler) {
|
|
68720
|
+
if (this.eventHandlers[event]) {
|
|
68721
|
+
this.eventHandlers[event] = this.eventHandlers[event].filter((h) => h !== handler);
|
|
68722
|
+
}
|
|
68723
|
+
}
|
|
68724
|
+
|
|
68725
|
+
dispatchEvent(event, data) {
|
|
68726
|
+
if (this.eventHandlers[event]) {
|
|
68727
|
+
this.eventHandlers[event].forEach((handler) => handler(data));
|
|
68728
|
+
}
|
|
68729
|
+
}
|
|
68730
|
+
|
|
68731
|
+
registerObjectWithHandle(object, handle) {
|
|
68732
|
+
if (!handle) return;
|
|
68733
|
+
|
|
68734
|
+
const fullHandle = object.userData.handle;
|
|
68735
|
+
if (!this.handleToObjects.has(fullHandle)) {
|
|
68736
|
+
this.handleToObjects.set(fullHandle, new Set());
|
|
68737
|
+
}
|
|
68738
|
+
this.handleToObjects.get(fullHandle).add(object);
|
|
68739
|
+
|
|
68740
|
+
object.userData.structureId = object.userData.handle.split("_")[0];
|
|
68741
|
+
}
|
|
68742
|
+
|
|
68743
|
+
getObjectsByHandle(handle) {
|
|
68744
|
+
if (!handle) return [];
|
|
68745
|
+
return Array.from(this.handleToObjects.get(handle) || []);
|
|
68746
|
+
}
|
|
68747
|
+
|
|
68748
|
+
getHandlesByObjects(objects) {
|
|
68749
|
+
if (!objects.length) return [];
|
|
68750
|
+
const handles = new Set();
|
|
68751
|
+
objects.forEach((obj) => {
|
|
68752
|
+
if (this.originalObjects.has(obj)) handles.add(obj.userData.handle);
|
|
68753
|
+
});
|
|
68754
|
+
return Array.from(handles);
|
|
68755
|
+
}
|
|
68756
|
+
|
|
68757
|
+
getMaterialId(material, index) {
|
|
68758
|
+
const props = {
|
|
68759
|
+
type: material.type,
|
|
68760
|
+
color: material.color?.getHex(),
|
|
68761
|
+
map: material.map?.uuid,
|
|
68762
|
+
transparent: material.transparent,
|
|
68763
|
+
opacity: material.opacity,
|
|
68764
|
+
side: material.side,
|
|
68765
|
+
index: index ? 1 : 0,
|
|
68766
|
+
};
|
|
68767
|
+
return JSON.stringify(props);
|
|
68768
|
+
}
|
|
68769
|
+
|
|
68770
|
+
addToMaterialGroup(object, groupsMap, optimizeGroupList) {
|
|
68771
|
+
const VERTEX_LIMIT = 100_000;
|
|
68772
|
+
const INDEX_LIMIT = 100_000;
|
|
68773
|
+
|
|
68774
|
+
const objectGeometryVertexCount = object.geometry.attributes.position.count;
|
|
68775
|
+
const objectGeometryIndexCount = object.geometry.index ? object.geometry.index.count : 0;
|
|
68776
|
+
|
|
68777
|
+
const material = object.material;
|
|
68778
|
+
let materialId = this.getMaterialId(material, object.geometry.index !== null);
|
|
68779
|
+
|
|
68780
|
+
let group;
|
|
68781
|
+
if (!groupsMap.has(materialId)) {
|
|
68782
|
+
group = {
|
|
68783
|
+
material,
|
|
68784
|
+
objects: [object],
|
|
68785
|
+
totalVertices: objectGeometryVertexCount,
|
|
68786
|
+
totalIndices: objectGeometryIndexCount,
|
|
68787
|
+
};
|
|
68788
|
+
groupsMap.set(materialId, group);
|
|
68789
|
+
optimizeGroupList.push(group);
|
|
68790
|
+
} else {
|
|
68791
|
+
group = groupsMap.get(materialId);
|
|
68792
|
+
if (
|
|
68793
|
+
group.totalVertices + objectGeometryVertexCount > VERTEX_LIMIT ||
|
|
68794
|
+
group.totalIndices + objectGeometryIndexCount > INDEX_LIMIT
|
|
68795
|
+
) {
|
|
68796
|
+
const newGroup = {
|
|
68797
|
+
material,
|
|
68798
|
+
objects: [object],
|
|
68799
|
+
totalVertices: objectGeometryVertexCount,
|
|
68800
|
+
totalIndices: objectGeometryIndexCount,
|
|
68801
|
+
};
|
|
68802
|
+
materialId = this.getMaterialId(material, object.geometry.index !== null);
|
|
68803
|
+
groupsMap.set(materialId, newGroup);
|
|
68804
|
+
optimizeGroupList.push(newGroup);
|
|
68805
|
+
} else {
|
|
68806
|
+
group.objects.push(object);
|
|
68807
|
+
group.totalVertices += objectGeometryVertexCount;
|
|
68808
|
+
group.totalIndices += objectGeometryIndexCount;
|
|
68809
|
+
}
|
|
68810
|
+
}
|
|
68811
|
+
|
|
68812
|
+
this.originalObjects.add(object);
|
|
68813
|
+
}
|
|
68814
|
+
|
|
68815
|
+
optimizeScene() {
|
|
68816
|
+
this.originalObjects.clear();
|
|
68817
|
+
this.originalObjectsToSelection.clear();
|
|
68818
|
+
const structureGroups = new Map();
|
|
68819
|
+
|
|
68820
|
+
this.scene.traverse((object) => {
|
|
68821
|
+
if (object.userData.structureId) {
|
|
68822
|
+
const structureId = object.userData.structureId;
|
|
68823
|
+
if (!structureGroups.has(structureId)) {
|
|
68824
|
+
structureGroups.set(structureId, {
|
|
68825
|
+
mapMeshes: new Map(),
|
|
68826
|
+
mapLines: new Map(),
|
|
68827
|
+
mapLineSegments: new Map(),
|
|
68828
|
+
mapPoints: new Map(),
|
|
68829
|
+
|
|
68830
|
+
meshes: [],
|
|
68831
|
+
lines: [],
|
|
68832
|
+
lineSegments: [],
|
|
68833
|
+
points: [],
|
|
68834
|
+
rootGroup: this.structureRoots.get(structureId),
|
|
68835
|
+
});
|
|
68836
|
+
}
|
|
68837
|
+
|
|
68838
|
+
const group = structureGroups.get(structureId);
|
|
68839
|
+
|
|
68840
|
+
if (object instanceof Mesh) {
|
|
68841
|
+
this.addToMaterialGroup(object, group.mapMeshes, group.meshes);
|
|
68842
|
+
} else if (object instanceof LineSegments) {
|
|
68843
|
+
this.addToMaterialGroup(object, group.mapLineSegments, group.lineSegments);
|
|
68844
|
+
} else if (object instanceof Line$1) {
|
|
68845
|
+
this.addToMaterialGroup(object, group.mapLines, group.lines);
|
|
68846
|
+
} else if (object instanceof Points) {
|
|
68847
|
+
this.addToMaterialGroup(object, group.mapPoints, group.points);
|
|
68848
|
+
}
|
|
68849
|
+
}
|
|
68850
|
+
});
|
|
68851
|
+
|
|
68852
|
+
for (const group of structureGroups.values()) {
|
|
68853
|
+
group.mapMeshes.clear();
|
|
68854
|
+
group.mapLines.clear();
|
|
68855
|
+
group.mapLineSegments.clear();
|
|
68856
|
+
group.mapPoints.clear();
|
|
68857
|
+
|
|
68858
|
+
this.mergeMeshGroups(group.meshes, group.rootGroup);
|
|
68859
|
+
this.mergeLineGroups(group.lines, group.rootGroup);
|
|
68860
|
+
this.mergeLineSegmentGroups(group.lineSegments, group.rootGroup);
|
|
68861
|
+
this.mergePointsGroups(group.points, group.rootGroup);
|
|
68862
|
+
}
|
|
68863
|
+
|
|
68864
|
+
this.originalObjects.forEach((obj) => {
|
|
68865
|
+
obj.visible = false;
|
|
68866
|
+
if (!(obj instanceof Points) && !obj.userData.isEdge) {
|
|
68867
|
+
this.originalObjectsToSelection.add(obj);
|
|
68868
|
+
}
|
|
68869
|
+
});
|
|
68870
|
+
|
|
68871
|
+
this.dispatchEvent("update");
|
|
68872
|
+
}
|
|
68873
|
+
|
|
68874
|
+
mergeMeshGroups(materialGroups, rootGroup) {
|
|
68875
|
+
for (const group of materialGroups) {
|
|
68876
|
+
try {
|
|
68877
|
+
const geometries = [];
|
|
68878
|
+
const handles = new Set();
|
|
68879
|
+
const optimizedObjects = [];
|
|
68880
|
+
|
|
68881
|
+
for (const mesh of group.objects) {
|
|
68882
|
+
const geometry = mesh.geometry.clone();
|
|
68883
|
+
mesh.updateWorldMatrix(true, false);
|
|
68884
|
+
geometry.applyMatrix4(mesh.matrixWorld);
|
|
68885
|
+
geometries.push(geometry);
|
|
68886
|
+
|
|
68887
|
+
optimizedObjects.push(mesh);
|
|
68888
|
+
handles.add(mesh.userData.handle);
|
|
68889
|
+
}
|
|
68890
|
+
|
|
68891
|
+
const mergedObjects = [];
|
|
68892
|
+
|
|
68893
|
+
if (geometries.length > 0) {
|
|
68894
|
+
const mergedGeometry = mergeGeometries(geometries);
|
|
68895
|
+
if (this.useVAO) {
|
|
68896
|
+
this.createVAO(mergedGeometry);
|
|
68897
|
+
}
|
|
68898
|
+
|
|
68899
|
+
const mergedMesh = new Mesh(mergedGeometry, group.material);
|
|
68900
|
+
rootGroup.add(mergedMesh);
|
|
68901
|
+
|
|
68902
|
+
this.mergedMesh.add(mergedMesh);
|
|
68903
|
+
this.optimizedOriginalMap.set(mergedMesh, optimizedObjects);
|
|
68904
|
+
|
|
68905
|
+
mergedObjects.push(mergedMesh);
|
|
68906
|
+
|
|
68907
|
+
geometries.forEach((geometry) => {
|
|
68908
|
+
geometry.dispose();
|
|
68909
|
+
});
|
|
68910
|
+
}
|
|
68911
|
+
|
|
68912
|
+
handles.forEach((handle) => {
|
|
68913
|
+
if (this.handleToOptimizedObjects.has(handle)) {
|
|
68914
|
+
const existingObjects = this.handleToOptimizedObjects.get(handle);
|
|
68915
|
+
existingObjects.push(...mergedObjects);
|
|
68916
|
+
this.handleToOptimizedObjects.set(handle, existingObjects);
|
|
68917
|
+
} else {
|
|
68918
|
+
this.handleToOptimizedObjects.set(handle, mergedObjects);
|
|
68919
|
+
}
|
|
68920
|
+
});
|
|
68921
|
+
} catch (error) {
|
|
68922
|
+
console.error("Failed to merge meshes for material:", error);
|
|
68923
|
+
group.objects.forEach((mesh) => {
|
|
68924
|
+
mesh.visible = true;
|
|
68925
|
+
});
|
|
68926
|
+
}
|
|
68927
|
+
}
|
|
68928
|
+
}
|
|
68929
|
+
|
|
68930
|
+
mergeLineGroups(materialGroups, rootGroup) {
|
|
68931
|
+
for (const group of materialGroups) {
|
|
68932
|
+
if (group.objects.length === 0) continue;
|
|
68933
|
+
|
|
68934
|
+
const handles = new Set();
|
|
68935
|
+
let totalVertices = 0;
|
|
68936
|
+
group.objects.map((line) => {
|
|
68937
|
+
handles.add(line.userData.handle);
|
|
68938
|
+
totalVertices += line.geometry.attributes.position.count;
|
|
68939
|
+
});
|
|
68940
|
+
|
|
68941
|
+
const positions = new Float32Array(totalVertices * 3);
|
|
68942
|
+
let posOffset = 0;
|
|
68943
|
+
|
|
68944
|
+
const indices = [];
|
|
68945
|
+
let vertexOffset = 0;
|
|
68946
|
+
|
|
68947
|
+
group.objects.forEach((line) => {
|
|
68948
|
+
const geometry = line.geometry;
|
|
68949
|
+
const positionAttr = geometry.attributes.position;
|
|
68950
|
+
const vertexCount = positionAttr.count;
|
|
68951
|
+
|
|
68952
|
+
line.updateWorldMatrix(true, false);
|
|
68953
|
+
const matrix = line.matrixWorld;
|
|
68954
|
+
const vector = new Vector3();
|
|
68955
|
+
|
|
68956
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
68957
|
+
vector.fromBufferAttribute(positionAttr, i);
|
|
68958
|
+
vector.applyMatrix4(matrix);
|
|
68959
|
+
positions[posOffset++] = vector.x;
|
|
68960
|
+
positions[posOffset++] = vector.y;
|
|
68961
|
+
positions[posOffset++] = vector.z;
|
|
68962
|
+
}
|
|
68963
|
+
|
|
68964
|
+
for (let i = 0; i < vertexCount - 1; i++) {
|
|
68965
|
+
indices.push(vertexOffset + i, vertexOffset + i + 1);
|
|
68966
|
+
}
|
|
68967
|
+
|
|
68968
|
+
vertexOffset += vertexCount;
|
|
68969
|
+
});
|
|
68970
|
+
|
|
68971
|
+
const geometry = new BufferGeometry();
|
|
68972
|
+
geometry.setAttribute("position", new BufferAttribute(positions, 3));
|
|
68973
|
+
geometry.setIndex(indices);
|
|
68974
|
+
geometry.computeBoundingSphere();
|
|
68975
|
+
geometry.computeBoundingBox();
|
|
68976
|
+
|
|
68977
|
+
const mergedLine = new LineSegments(geometry, group.material);
|
|
68978
|
+
const mergedObjects = [mergedLine];
|
|
68979
|
+
if (this.useVAO) {
|
|
68980
|
+
this.createVAO(mergedLine);
|
|
68981
|
+
}
|
|
68982
|
+
rootGroup.add(mergedLine);
|
|
68983
|
+
this.mergedLines.add(mergedLine);
|
|
68984
|
+
this.optimizedOriginalMap.set(mergedLine, group.objects);
|
|
68985
|
+
|
|
68986
|
+
handles.forEach((handle) => {
|
|
68987
|
+
if (this.handleToOptimizedObjects.has(handle)) {
|
|
68988
|
+
const existingObjects = this.handleToOptimizedObjects.get(handle);
|
|
68989
|
+
existingObjects.push(...mergedObjects);
|
|
68990
|
+
this.handleToOptimizedObjects.set(handle, existingObjects);
|
|
68991
|
+
} else {
|
|
68992
|
+
this.handleToOptimizedObjects.set(handle, mergedObjects);
|
|
68993
|
+
}
|
|
68994
|
+
});
|
|
68995
|
+
}
|
|
68996
|
+
}
|
|
68997
|
+
|
|
68998
|
+
mergeLineSegmentGroups(materialGroups, rootGroup) {
|
|
68999
|
+
for (const group of materialGroups) {
|
|
69000
|
+
try {
|
|
69001
|
+
const geometries = [];
|
|
69002
|
+
const optimizedObjects = [];
|
|
69003
|
+
const handles = new Set();
|
|
69004
|
+
|
|
69005
|
+
for (const line of group.objects) {
|
|
69006
|
+
const geometry = line.geometry.clone();
|
|
69007
|
+
line.updateWorldMatrix(true, false);
|
|
69008
|
+
geometry.applyMatrix4(line.matrixWorld);
|
|
69009
|
+
geometries.push(geometry);
|
|
69010
|
+
optimizedObjects.push(line);
|
|
69011
|
+
handles.add(line.userData.handle);
|
|
69012
|
+
}
|
|
69013
|
+
|
|
69014
|
+
const mergedObjects = [];
|
|
69015
|
+
|
|
69016
|
+
if (geometries.length > 0) {
|
|
69017
|
+
const mergedGeometry = mergeGeometries(geometries, false);
|
|
69018
|
+
const mergedLine = new LineSegments(mergedGeometry, group.material);
|
|
69019
|
+
|
|
69020
|
+
if (this.useVAO) {
|
|
69021
|
+
this.createVAO(mergedLine);
|
|
69022
|
+
}
|
|
69023
|
+
|
|
69024
|
+
rootGroup.add(mergedLine);
|
|
69025
|
+
this.mergedLineSegments.add(mergedLine);
|
|
69026
|
+
this.optimizedOriginalMap.set(mergedLine, optimizedObjects);
|
|
69027
|
+
mergedObjects.push(mergedLine);
|
|
69028
|
+
|
|
69029
|
+
geometries.forEach((geometry) => {
|
|
69030
|
+
geometry.dispose();
|
|
69031
|
+
});
|
|
69032
|
+
}
|
|
69033
|
+
|
|
69034
|
+
handles.forEach((handle) => {
|
|
69035
|
+
if (this.handleToOptimizedObjects.has(handle)) {
|
|
69036
|
+
const existingObjects = this.handleToOptimizedObjects.get(handle);
|
|
69037
|
+
existingObjects.push(...mergedObjects);
|
|
69038
|
+
this.handleToOptimizedObjects.set(handle, existingObjects);
|
|
69039
|
+
} else {
|
|
69040
|
+
this.handleToOptimizedObjects.set(handle, mergedObjects);
|
|
69041
|
+
}
|
|
69042
|
+
});
|
|
69043
|
+
} catch (error) {
|
|
69044
|
+
console.warn("Failed to merge line segments for material:", error);
|
|
69045
|
+
group.objects.forEach((line) => {
|
|
69046
|
+
line.visible = true;
|
|
69047
|
+
});
|
|
69048
|
+
}
|
|
69049
|
+
}
|
|
69050
|
+
}
|
|
69051
|
+
|
|
69052
|
+
mergePointsGroups(materialGroups, rootGroup) {
|
|
69053
|
+
for (const group of materialGroups) {
|
|
69054
|
+
try {
|
|
69055
|
+
const geometries = [];
|
|
69056
|
+
const optimizedObjects = [];
|
|
69057
|
+
const handles = new Set();
|
|
69058
|
+
|
|
69059
|
+
for (const points of group.objects) {
|
|
69060
|
+
const geometry = points.geometry.clone();
|
|
69061
|
+
points.updateWorldMatrix(true, false);
|
|
69062
|
+
geometry.applyMatrix4(points.matrixWorld);
|
|
69063
|
+
geometries.push(geometry);
|
|
69064
|
+
optimizedObjects.push(points);
|
|
69065
|
+
handles.add(points.userData.handle);
|
|
69066
|
+
}
|
|
69067
|
+
|
|
69068
|
+
const mergedObjects = [];
|
|
69069
|
+
|
|
69070
|
+
if (geometries.length > 0) {
|
|
69071
|
+
const mergedGeometry = mergeGeometries(geometries, false);
|
|
69072
|
+
const mergedPoints = new Points(mergedGeometry, group.material);
|
|
69073
|
+
|
|
69074
|
+
if (this.useVAO) {
|
|
69075
|
+
this.createVAO(mergedPoints);
|
|
69076
|
+
}
|
|
69077
|
+
|
|
69078
|
+
rootGroup.add(mergedPoints);
|
|
69079
|
+
|
|
69080
|
+
this.mergedPoints.add(mergedPoints);
|
|
69081
|
+
this.optimizedOriginalMap.set(mergedPoints, optimizedObjects);
|
|
69082
|
+
mergedObjects.push(mergedPoints);
|
|
69083
|
+
|
|
69084
|
+
geometries.forEach((geometry) => {
|
|
69085
|
+
geometry.dispose();
|
|
69086
|
+
});
|
|
69087
|
+
}
|
|
69088
|
+
|
|
69089
|
+
handles.forEach((handle) => {
|
|
69090
|
+
if (this.handleToOptimizedObjects.has(handle)) {
|
|
69091
|
+
const existingObjects = this.handleToOptimizedObjects.get(handle);
|
|
69092
|
+
existingObjects.push(...mergedObjects);
|
|
69093
|
+
this.handleToOptimizedObjects.set(handle, existingObjects);
|
|
69094
|
+
} else {
|
|
69095
|
+
this.handleToOptimizedObjects.set(handle, mergedObjects);
|
|
69096
|
+
}
|
|
69097
|
+
});
|
|
69098
|
+
} catch (error) {
|
|
69099
|
+
console.warn("Failed to merge points for material:", error);
|
|
69100
|
+
group.objects.forEach((points) => {
|
|
69101
|
+
points.visible = true;
|
|
69102
|
+
});
|
|
69103
|
+
}
|
|
69104
|
+
}
|
|
69105
|
+
}
|
|
69106
|
+
|
|
69107
|
+
mergeInSingleSegment(structureId, rootGroup) {
|
|
69108
|
+
const lineSegmentsArray = [...this.mergedLineSegments, ...this.mergedLines].filter(
|
|
69109
|
+
(obj) => obj.userData.structureId === structureId
|
|
69110
|
+
);
|
|
69111
|
+
|
|
69112
|
+
if (lineSegmentsArray.length === 0) return;
|
|
69113
|
+
|
|
69114
|
+
try {
|
|
69115
|
+
const geometriesWithIndex = [];
|
|
69116
|
+
const hasNormals = lineSegmentsArray.some((segment) => segment.geometry.attributes.normal !== undefined);
|
|
69117
|
+
|
|
69118
|
+
lineSegmentsArray.forEach((segment) => {
|
|
69119
|
+
const clonedGeometry = segment.geometry.clone();
|
|
69120
|
+
segment.updateWorldMatrix(true, false);
|
|
69121
|
+
clonedGeometry.applyMatrix4(segment.matrixWorld);
|
|
69122
|
+
|
|
69123
|
+
if (hasNormals && !clonedGeometry.attributes.normal) {
|
|
69124
|
+
clonedGeometry.computeVertexNormals();
|
|
69125
|
+
}
|
|
69126
|
+
if (!hasNormals && clonedGeometry.attributes.normal) {
|
|
69127
|
+
clonedGeometry.deleteAttribute("normal");
|
|
69128
|
+
}
|
|
69129
|
+
|
|
69130
|
+
const colorArray = new Float32Array(clonedGeometry.attributes.position.count * 3);
|
|
69131
|
+
for (let i = 0; i < colorArray.length; i += 3) {
|
|
69132
|
+
colorArray[i] = segment.material.color.r;
|
|
69133
|
+
colorArray[i + 1] = segment.material.color.g;
|
|
69134
|
+
colorArray[i + 2] = segment.material.color.b;
|
|
69135
|
+
}
|
|
69136
|
+
clonedGeometry.setAttribute("color", new BufferAttribute(colorArray, 3));
|
|
69137
|
+
|
|
69138
|
+
if (!clonedGeometry.index) {
|
|
69139
|
+
const indices = [];
|
|
69140
|
+
const posCount = clonedGeometry.attributes.position.count;
|
|
69141
|
+
for (let i = 0; i < posCount - 1; i += 2) {
|
|
69142
|
+
indices.push(i, i + 1);
|
|
69143
|
+
}
|
|
69144
|
+
clonedGeometry.setIndex(indices);
|
|
69145
|
+
}
|
|
69146
|
+
|
|
69147
|
+
geometriesWithIndex.push(clonedGeometry);
|
|
69148
|
+
});
|
|
69149
|
+
|
|
69150
|
+
const finalGeometry = mergeGeometries(geometriesWithIndex, false);
|
|
69151
|
+
const material = new LineBasicMaterial({
|
|
69152
|
+
vertexColors: true,
|
|
69153
|
+
});
|
|
69154
|
+
|
|
69155
|
+
if (this.useVAO) {
|
|
69156
|
+
this.createVAO(finalGeometry);
|
|
69157
|
+
}
|
|
69158
|
+
|
|
69159
|
+
const mergedLine = new LineSegments(finalGeometry, material);
|
|
69160
|
+
mergedLine.userData.structureId = structureId;
|
|
69161
|
+
rootGroup.add(mergedLine);
|
|
69162
|
+
this.mergedLineSegments.add(mergedLine);
|
|
69163
|
+
|
|
69164
|
+
lineSegmentsArray.forEach((obj) => {
|
|
69165
|
+
if (obj.parent) {
|
|
69166
|
+
obj.parent.remove(obj);
|
|
69167
|
+
}
|
|
69168
|
+
obj.geometry.dispose();
|
|
69169
|
+
});
|
|
69170
|
+
} catch (error) {
|
|
69171
|
+
console.error("Failed to merge geometries:", error);
|
|
69172
|
+
lineSegmentsArray.forEach((obj) => {
|
|
69173
|
+
obj.visible = true;
|
|
69174
|
+
rootGroup.add(obj);
|
|
69175
|
+
});
|
|
69176
|
+
}
|
|
69177
|
+
}
|
|
69178
|
+
|
|
69179
|
+
showOriginalObjects(objects) {
|
|
69180
|
+
objects.forEach((obj) => {
|
|
69181
|
+
if (this.originalObjects.has(obj)) {
|
|
69182
|
+
obj.visible = true;
|
|
69183
|
+
}
|
|
69184
|
+
});
|
|
69185
|
+
}
|
|
69186
|
+
|
|
69187
|
+
hideOriginalObjects(objects) {
|
|
69188
|
+
objects.forEach((obj) => {
|
|
69189
|
+
if (this.originalObjects.has(obj)) {
|
|
69190
|
+
obj.visible = false;
|
|
69191
|
+
}
|
|
69192
|
+
});
|
|
69193
|
+
}
|
|
69194
|
+
|
|
69195
|
+
createVAO(geometry) {
|
|
69196
|
+
if (!this.useVAO) {
|
|
69197
|
+
return;
|
|
69198
|
+
}
|
|
66558
69199
|
|
|
66559
|
-
|
|
69200
|
+
if (geometry.attributes?.position?.count < 1000) {
|
|
69201
|
+
return;
|
|
69202
|
+
}
|
|
66560
69203
|
|
|
66561
|
-
|
|
66562
|
-
|
|
69204
|
+
const gl = this.renderer.getContext();
|
|
69205
|
+
const vao = gl.createVertexArray();
|
|
69206
|
+
gl.bindVertexArray(vao);
|
|
66563
69207
|
|
|
66564
|
-
|
|
69208
|
+
for (const name in geometry.attributes) {
|
|
69209
|
+
const attribute = geometry.attributes[name];
|
|
69210
|
+
const buffer = this.renderer.properties.get(attribute).buffer;
|
|
66565
69211
|
|
|
66566
|
-
|
|
69212
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
69213
|
+
gl.enableVertexAttribArray(attribute.itemSize);
|
|
69214
|
+
gl.vertexAttribPointer(attribute.itemSize, attribute.itemSize, gl.FLOAT, false, 0, 0);
|
|
69215
|
+
}
|
|
66567
69216
|
|
|
66568
|
-
|
|
69217
|
+
if (geometry.index) {
|
|
69218
|
+
const indexBuffer = this.renderer.properties.get(geometry.index).buffer;
|
|
69219
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
|
69220
|
+
}
|
|
66569
69221
|
|
|
66570
|
-
|
|
66571
|
-
|
|
69222
|
+
gl.bindVertexArray(null);
|
|
69223
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
69224
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
|
|
66572
69225
|
|
|
66573
|
-
|
|
69226
|
+
geometry.vao = vao;
|
|
69227
|
+
}
|
|
66574
69228
|
|
|
66575
|
-
|
|
69229
|
+
getOriginalObjectForSelect() {
|
|
69230
|
+
const optimizedOriginals = [];
|
|
66576
69231
|
|
|
66577
|
-
|
|
66578
|
-
|
|
66579
|
-
|
|
66580
|
-
|
|
66581
|
-
* @return {Promise<BufferGeometry>}
|
|
66582
|
-
*/
|
|
66583
|
-
function addPrimitiveAttributes( geometry, primitiveDef, parser ) {
|
|
69232
|
+
for (const obj of this.originalObjectsToSelection) {
|
|
69233
|
+
if (this.hiddenHandles.has(obj.userData.handle)) {
|
|
69234
|
+
continue;
|
|
69235
|
+
}
|
|
66584
69236
|
|
|
66585
|
-
|
|
69237
|
+
optimizedOriginals.push(obj);
|
|
69238
|
+
}
|
|
66586
69239
|
|
|
66587
|
-
|
|
69240
|
+
return optimizedOriginals;
|
|
69241
|
+
}
|
|
66588
69242
|
|
|
66589
|
-
|
|
69243
|
+
isolateObjects(handles) {
|
|
69244
|
+
if (this.hiddenHandles.size !== 0) {
|
|
69245
|
+
this.hiddenHandles.clear();
|
|
69246
|
+
this.syncHiddenObjects();
|
|
69247
|
+
}
|
|
66590
69248
|
|
|
66591
|
-
|
|
66592
|
-
|
|
69249
|
+
for (const handle of this.handleToOptimizedObjects.keys()) {
|
|
69250
|
+
if (!handles.has(handle)) {
|
|
69251
|
+
this.hiddenHandles.add(handle);
|
|
69252
|
+
}
|
|
69253
|
+
}
|
|
66593
69254
|
|
|
66594
|
-
|
|
69255
|
+
this.syncHiddenObjects();
|
|
69256
|
+
}
|
|
66595
69257
|
|
|
66596
|
-
|
|
69258
|
+
showAllHiddenObjects() {
|
|
69259
|
+
this.hiddenHandles.clear();
|
|
69260
|
+
this.syncHiddenObjects();
|
|
69261
|
+
}
|
|
66597
69262
|
|
|
66598
|
-
|
|
69263
|
+
hideObjects(handles) {
|
|
69264
|
+
handles.forEach((handle) => {
|
|
69265
|
+
this.hiddenHandles.add(handle);
|
|
69266
|
+
});
|
|
69267
|
+
this.syncHiddenObjects();
|
|
69268
|
+
}
|
|
66599
69269
|
|
|
66600
|
-
|
|
69270
|
+
showObjects(handles) {
|
|
69271
|
+
handles.forEach((handle) => {
|
|
69272
|
+
this.hiddenHandles.delete(handle);
|
|
69273
|
+
});
|
|
69274
|
+
this.syncHiddenObjects();
|
|
69275
|
+
}
|
|
66601
69276
|
|
|
66602
|
-
|
|
69277
|
+
syncHiddenObjects() {
|
|
69278
|
+
if (this.oldOptimizeObjects.size !== 0) {
|
|
69279
|
+
for (const obj of this.oldOptimizeObjects) {
|
|
69280
|
+
obj.visible = true;
|
|
69281
|
+
}
|
|
69282
|
+
this.oldOptimizeObjects.clear();
|
|
69283
|
+
}
|
|
66603
69284
|
|
|
66604
|
-
|
|
66605
|
-
|
|
69285
|
+
if (this.newOptimizedObjects.size !== 0) {
|
|
69286
|
+
for (const obj of this.newOptimizedObjects) {
|
|
69287
|
+
obj.visible = false;
|
|
69288
|
+
obj.geometry.dispose();
|
|
69289
|
+
obj.parent.remove(obj);
|
|
69290
|
+
}
|
|
69291
|
+
this.newOptimizedObjects.clear();
|
|
69292
|
+
}
|
|
66606
69293
|
|
|
66607
|
-
|
|
69294
|
+
if (this.hiddenHandles.size === 0) {
|
|
69295
|
+
return;
|
|
69296
|
+
}
|
|
66608
69297
|
|
|
66609
|
-
|
|
69298
|
+
this.hiddenHandles.forEach((handle) => {
|
|
69299
|
+
const objects = this.handleToOptimizedObjects.get(handle);
|
|
69300
|
+
if (objects) {
|
|
69301
|
+
objects.forEach((x) => this.oldOptimizeObjects.add(x));
|
|
69302
|
+
}
|
|
69303
|
+
});
|
|
66610
69304
|
|
|
66611
|
-
|
|
69305
|
+
this.oldOptimizeObjects.forEach((optimizedObject) => {
|
|
69306
|
+
optimizedObject.visible = false;
|
|
66612
69307
|
|
|
66613
|
-
|
|
69308
|
+
const originObjects = this.optimizedOriginalMap.get(optimizedObject);
|
|
69309
|
+
const updateListToOptimize = [];
|
|
69310
|
+
originObjects.forEach((obj) => {
|
|
69311
|
+
if (!this.hiddenHandles.has(obj.userData.handle)) {
|
|
69312
|
+
updateListToOptimize.push(obj);
|
|
69313
|
+
}
|
|
69314
|
+
});
|
|
66614
69315
|
|
|
66615
|
-
|
|
69316
|
+
const firstObject = updateListToOptimize[0];
|
|
66616
69317
|
|
|
66617
|
-
|
|
69318
|
+
if (firstObject instanceof Mesh || firstObject instanceof LineSegments) {
|
|
69319
|
+
const geometries = updateListToOptimize.map((obj) => {
|
|
69320
|
+
const geometry = obj.geometry.clone();
|
|
69321
|
+
obj.updateWorldMatrix(true, false);
|
|
69322
|
+
geometry.applyMatrix4(obj.matrixWorld);
|
|
69323
|
+
return geometry;
|
|
69324
|
+
});
|
|
66618
69325
|
|
|
66619
|
-
|
|
69326
|
+
const newMergedGeometry = mergeGeometries(geometries);
|
|
69327
|
+
const mergedObject =
|
|
69328
|
+
firstObject instanceof Mesh
|
|
69329
|
+
? new Mesh(newMergedGeometry, optimizedObject.material)
|
|
69330
|
+
: new LineSegments(newMergedGeometry, optimizedObject.material);
|
|
66620
69331
|
|
|
66621
|
-
|
|
69332
|
+
mergedObject.visible = true;
|
|
69333
|
+
optimizedObject.parent.add(mergedObject);
|
|
69334
|
+
this.newOptimizedObjects.add(mergedObject);
|
|
66622
69335
|
|
|
66623
|
-
|
|
69336
|
+
geometries.forEach((geometry) => {
|
|
69337
|
+
geometry.dispose();
|
|
69338
|
+
});
|
|
69339
|
+
} else if (firstObject instanceof Line$1) {
|
|
69340
|
+
let totalVertices = 0;
|
|
69341
|
+
updateListToOptimize.map((line) => {
|
|
69342
|
+
totalVertices += line.geometry.attributes.position.count;
|
|
69343
|
+
});
|
|
66624
69344
|
|
|
66625
|
-
|
|
69345
|
+
const positions = new Float32Array(totalVertices * 3);
|
|
69346
|
+
let posOffset = 0;
|
|
66626
69347
|
|
|
66627
|
-
|
|
69348
|
+
const indices = [];
|
|
69349
|
+
let vertexOffset = 0;
|
|
66628
69350
|
|
|
66629
|
-
|
|
69351
|
+
updateListToOptimize.forEach((line) => {
|
|
69352
|
+
const geometry = line.geometry;
|
|
69353
|
+
const positionAttr = geometry.attributes.position;
|
|
69354
|
+
const vertexCount = positionAttr.count;
|
|
66630
69355
|
|
|
66631
|
-
|
|
69356
|
+
line.updateWorldMatrix(true, false);
|
|
69357
|
+
const matrix = line.matrixWorld;
|
|
69358
|
+
const vector = new Vector3();
|
|
66632
69359
|
|
|
66633
|
-
|
|
69360
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
69361
|
+
vector.fromBufferAttribute(positionAttr, i);
|
|
69362
|
+
vector.applyMatrix4(matrix);
|
|
69363
|
+
positions[posOffset++] = vector.x;
|
|
69364
|
+
positions[posOffset++] = vector.y;
|
|
69365
|
+
positions[posOffset++] = vector.z;
|
|
69366
|
+
}
|
|
66634
69367
|
|
|
66635
|
-
|
|
66636
|
-
|
|
66637
|
-
|
|
69368
|
+
for (let i = 0; i < vertexCount - 1; i++) {
|
|
69369
|
+
indices.push(vertexOffset + i, vertexOffset + i + 1);
|
|
69370
|
+
}
|
|
66638
69371
|
|
|
66639
|
-
|
|
69372
|
+
vertexOffset += vertexCount;
|
|
69373
|
+
});
|
|
66640
69374
|
|
|
66641
|
-
|
|
69375
|
+
const geometry = new BufferGeometry();
|
|
69376
|
+
geometry.setAttribute("position", new BufferAttribute(positions, 3));
|
|
69377
|
+
geometry.setIndex(indices);
|
|
69378
|
+
geometry.computeBoundingSphere();
|
|
69379
|
+
geometry.computeBoundingBox();
|
|
66642
69380
|
|
|
66643
|
-
|
|
66644
|
-
|
|
66645
|
-
|
|
66646
|
-
|
|
66647
|
-
|
|
66648
|
-
|
|
66649
|
-
|
|
66650
|
-
// Membership Agreement and Supplemental Software License Agreement with the
|
|
66651
|
-
// Alliance. The structure and organization of this software are the valuable
|
|
66652
|
-
// trade secrets of the Alliance and its suppliers. The software is also
|
|
66653
|
-
// protected by copyright law and international treaty provisions. Application
|
|
66654
|
-
// programs incorporating this software must include the following statement
|
|
66655
|
-
// with their copyright notices:
|
|
66656
|
-
//
|
|
66657
|
-
// This application incorporates Open Design Alliance software pursuant to a
|
|
66658
|
-
// license agreement with Open Design Alliance.
|
|
66659
|
-
// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
|
|
66660
|
-
// All rights reserved.
|
|
66661
|
-
//
|
|
66662
|
-
// By use of this software, its documentation or related materials, you
|
|
66663
|
-
// acknowledge and accept the above terms.
|
|
66664
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
66665
|
-
class GLTFLoadingManager extends LoadingManager {
|
|
66666
|
-
constructor(file, params = {}) {
|
|
66667
|
-
super();
|
|
66668
|
-
this.path = "";
|
|
66669
|
-
this.resourcePath = "";
|
|
66670
|
-
this.fileURL = "";
|
|
66671
|
-
this.dataURLs = new Map();
|
|
66672
|
-
this.path = params.path || "";
|
|
66673
|
-
const externalFiles = params.externalFiles || new Map();
|
|
66674
|
-
if (typeof file === "string") {
|
|
66675
|
-
this.fileURL = file;
|
|
66676
|
-
this.resourcePath = LoaderUtils.extractUrlBase(file);
|
|
66677
|
-
}
|
|
66678
|
-
else {
|
|
66679
|
-
externalFiles.forEach((value, key) => (this.fileURL = value === file ? key : this.fileURL));
|
|
66680
|
-
externalFiles.set(this.fileURL, file);
|
|
66681
|
-
}
|
|
66682
|
-
externalFiles.forEach((value, key) => {
|
|
66683
|
-
let dataURL;
|
|
66684
|
-
if (typeof value === "string")
|
|
66685
|
-
dataURL = value;
|
|
66686
|
-
else
|
|
66687
|
-
dataURL = URL.createObjectURL(new Blob([value]));
|
|
66688
|
-
this.dataURLs.set(key, dataURL);
|
|
66689
|
-
});
|
|
66690
|
-
this.setURLModifier((url) => {
|
|
66691
|
-
const key = decodeURI(url)
|
|
66692
|
-
.replace(this.path, "")
|
|
66693
|
-
.replace(this.resourcePath, "")
|
|
66694
|
-
.replace(/^(\.?\/)/, "");
|
|
66695
|
-
const dataURL = this.dataURLs.get(key);
|
|
66696
|
-
return dataURL !== null && dataURL !== undefined ? dataURL : url;
|
|
66697
|
-
});
|
|
66698
|
-
}
|
|
66699
|
-
dispose() {
|
|
66700
|
-
this.dataURLs.forEach(URL.revokeObjectURL);
|
|
66701
|
-
}
|
|
69381
|
+
const mergedLine = new LineSegments(geometry, optimizedObject.material);
|
|
69382
|
+
mergedLine.visible = true;
|
|
69383
|
+
optimizedObject.parent.add(mergedLine);
|
|
69384
|
+
this.newOptimizedObjects.add(mergedLine);
|
|
69385
|
+
}
|
|
69386
|
+
});
|
|
69387
|
+
}
|
|
66702
69388
|
}
|
|
66703
69389
|
|
|
66704
69390
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -66723,93 +69409,82 @@ void main() {
|
|
|
66723
69409
|
// By use of this software, its documentation or related materials, you
|
|
66724
69410
|
// acknowledge and accept the above terms.
|
|
66725
69411
|
///////////////////////////////////////////////////////////////////////////////
|
|
66726
|
-
class
|
|
69412
|
+
class GLTFCloudDynamicLoader {
|
|
66727
69413
|
constructor(viewer) {
|
|
66728
|
-
|
|
69414
|
+
this.requestId = 0;
|
|
66729
69415
|
this.viewer = viewer;
|
|
69416
|
+
this.scene = new Group$1();
|
|
66730
69417
|
}
|
|
66731
|
-
|
|
66732
|
-
|
|
66733
|
-
|
|
66734
|
-
}
|
|
66735
|
-
async load(file, format, params) {
|
|
66736
|
-
const manager = new GLTFLoadingManager(file, params);
|
|
66737
|
-
const loader = new GLTFLoader(manager);
|
|
66738
|
-
loader.setPath(manager.path);
|
|
66739
|
-
loader.setCrossOrigin(params.crossOrigin || loader.crossOrigin);
|
|
66740
|
-
loader.setWithCredentials(params.withCredentials || loader.withCredentials);
|
|
66741
|
-
const progress = (event) => {
|
|
66742
|
-
const { lengthComputable, loaded, total } = event;
|
|
66743
|
-
const progress = lengthComputable ? loaded / total : 1;
|
|
66744
|
-
this.viewer.emitEvent({ type: "geometryprogress", data: progress, file });
|
|
66745
|
-
};
|
|
66746
|
-
const gltf = await loader.loadAsync(manager.fileURL, progress);
|
|
66747
|
-
if (!this.viewer.scene)
|
|
66748
|
-
return this;
|
|
66749
|
-
this.viewer.scene.add(gltf.scene);
|
|
66750
|
-
this.viewer.models.push(gltf.scene);
|
|
66751
|
-
this.viewer.syncOptions();
|
|
66752
|
-
this.viewer.syncOverlay();
|
|
66753
|
-
this.viewer.update();
|
|
66754
|
-
this.viewer.emitEvent({ type: "databasechunk", data: gltf.scene, file });
|
|
66755
|
-
return this;
|
|
66756
|
-
}
|
|
66757
|
-
}
|
|
66758
|
-
|
|
66759
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
66760
|
-
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
|
|
66761
|
-
// All rights reserved.
|
|
66762
|
-
//
|
|
66763
|
-
// This software and its documentation and related materials are owned by
|
|
66764
|
-
// the Alliance. The software may only be incorporated into application
|
|
66765
|
-
// programs owned by members of the Alliance, subject to a signed
|
|
66766
|
-
// Membership Agreement and Supplemental Software License Agreement with the
|
|
66767
|
-
// Alliance. The structure and organization of this software are the valuable
|
|
66768
|
-
// trade secrets of the Alliance and its suppliers. The software is also
|
|
66769
|
-
// protected by copyright law and international treaty provisions. Application
|
|
66770
|
-
// programs incorporating this software must include the following statement
|
|
66771
|
-
// with their copyright notices:
|
|
66772
|
-
//
|
|
66773
|
-
// This application incorporates Open Design Alliance software pursuant to a
|
|
66774
|
-
// license agreement with Open Design Alliance.
|
|
66775
|
-
// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
|
|
66776
|
-
// All rights reserved.
|
|
66777
|
-
//
|
|
66778
|
-
// By use of this software, its documentation or related materials, you
|
|
66779
|
-
// acknowledge and accept the above terms.
|
|
66780
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
66781
|
-
class GLTFCloudModelLoader extends Loader$1 {
|
|
66782
|
-
constructor(viewer) {
|
|
66783
|
-
super();
|
|
66784
|
-
this.viewer = viewer;
|
|
69418
|
+
dispose() {
|
|
69419
|
+
if (this.gltfLoader)
|
|
69420
|
+
this.gltfLoader.clear();
|
|
66785
69421
|
}
|
|
66786
|
-
isSupport(
|
|
66787
|
-
return (typeof
|
|
66788
|
-
typeof
|
|
66789
|
-
typeof
|
|
66790
|
-
|
|
69422
|
+
isSupport(file) {
|
|
69423
|
+
return (typeof file === "object" &&
|
|
69424
|
+
typeof file.database === "string" &&
|
|
69425
|
+
typeof file.downloadResource === "function" &&
|
|
69426
|
+
typeof file.downloadResourceRange === "function" &&
|
|
69427
|
+
/.gltf$/i.test(file.database));
|
|
66791
69428
|
}
|
|
66792
|
-
async load(model) {
|
|
66793
|
-
|
|
66794
|
-
|
|
66795
|
-
|
|
66796
|
-
|
|
66797
|
-
|
|
66798
|
-
|
|
66799
|
-
|
|
69429
|
+
async load(model, format, params) {
|
|
69430
|
+
this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, this.viewer.scene, this.viewer.renderer);
|
|
69431
|
+
this.gltfLoader.memoryLimit = this.viewer.options.memoryLimit;
|
|
69432
|
+
this.gltfLoader.addEventListener("databasechunk", (data) => {
|
|
69433
|
+
const modelImpl = new DynamicModelImpl(this.scene);
|
|
69434
|
+
modelImpl.loader = this;
|
|
69435
|
+
modelImpl.gltfLoader = this.gltfLoader;
|
|
69436
|
+
modelImpl.viewer = this.viewer;
|
|
69437
|
+
this.viewer.scene.add(this.scene);
|
|
69438
|
+
this.viewer.models.push(modelImpl);
|
|
69439
|
+
this.viewer.syncOptions();
|
|
69440
|
+
this.viewer.syncOverlay();
|
|
69441
|
+
this.viewer.update();
|
|
69442
|
+
this.viewer.emitEvent({ type: "databasechunk", data, file: model.file, model });
|
|
69443
|
+
});
|
|
69444
|
+
this.gltfLoader.addEventListener("geometryprogress", (data) => {
|
|
69445
|
+
const progress = data.loaded / data.total;
|
|
66800
69446
|
this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model.file, model });
|
|
69447
|
+
});
|
|
69448
|
+
this.gltfLoader.addEventListener("geometrymemory", (data) => {
|
|
69449
|
+
this.viewer.emit({ type: "geometryprogress", data });
|
|
69450
|
+
});
|
|
69451
|
+
this.gltfLoader.addEventListener("geometryerror", (data) => {
|
|
69452
|
+
this.viewer.emitEvent({ type: "geometryerror", data, file: model.file, model });
|
|
69453
|
+
});
|
|
69454
|
+
this.gltfLoader.addEventListener("update", (data) => {
|
|
69455
|
+
this.viewer.update();
|
|
69456
|
+
});
|
|
69457
|
+
const loadController = {
|
|
69458
|
+
loadJson: async () => {
|
|
69459
|
+
const progress = (progress) => {
|
|
69460
|
+
this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model });
|
|
69461
|
+
};
|
|
69462
|
+
const arrayBuffer = await model.downloadResource(model.database, progress, this.gltfLoader.getAbortController().signal);
|
|
69463
|
+
const text = new TextDecoder().decode(arrayBuffer);
|
|
69464
|
+
const json = JSON.parse(text);
|
|
69465
|
+
return json;
|
|
69466
|
+
},
|
|
69467
|
+
loadBinaryData: (requests) => {
|
|
69468
|
+
const ranges = requests.map((request) => ({
|
|
69469
|
+
begin: request.offset,
|
|
69470
|
+
end: request.offset + request.length - 1,
|
|
69471
|
+
requestId: this.requestId++,
|
|
69472
|
+
}));
|
|
69473
|
+
return model.downloadResourceRange(model.geometry[0], undefined, ranges, undefined, this.gltfLoader.getAbortController().signal);
|
|
69474
|
+
},
|
|
69475
|
+
baseUrl: () => Promise.resolve(`${model.httpClient.serverUrl}${model.path}/`),
|
|
66801
69476
|
};
|
|
66802
|
-
const
|
|
66803
|
-
|
|
66804
|
-
|
|
66805
|
-
this.
|
|
66806
|
-
this.viewer.models.push(gltf.scene);
|
|
66807
|
-
this.viewer.syncOptions();
|
|
66808
|
-
this.viewer.syncOverlay();
|
|
66809
|
-
this.viewer.update();
|
|
66810
|
-
this.viewer.emitEvent({ type: "databasechunk", data: gltf.scene, file: model.file, model });
|
|
69477
|
+
const structure = new GltfStructure(1);
|
|
69478
|
+
await structure.initialize(loadController);
|
|
69479
|
+
await this.gltfLoader.loadStructure(structure);
|
|
69480
|
+
await this.gltfLoader.loadNodes();
|
|
66811
69481
|
return this;
|
|
66812
69482
|
}
|
|
69483
|
+
cancel() {
|
|
69484
|
+
if (this.gltfLoader) {
|
|
69485
|
+
this.gltfLoader.abortLoading();
|
|
69486
|
+
}
|
|
69487
|
+
}
|
|
66813
69488
|
}
|
|
66814
69489
|
|
|
66815
69490
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -66902,7 +69577,7 @@ void main() {
|
|
|
66902
69577
|
const loaders = loadersRegistry("threejs");
|
|
66903
69578
|
// build-in loaders
|
|
66904
69579
|
loaders.registerLoader("gltf-file", (viewer) => new GLTFFileLoader(viewer));
|
|
66905
|
-
loaders.registerLoader("gltf-cloud
|
|
69580
|
+
loaders.registerLoader("gltf-cloud", (viewer) => new GLTFCloudDynamicLoader(viewer));
|
|
66906
69581
|
|
|
66907
69582
|
class EventEmitter2 {
|
|
66908
69583
|
constructor() {
|
|
@@ -81357,8 +84032,8 @@ void main() {
|
|
|
81357
84032
|
this.client = client;
|
|
81358
84033
|
this.canvasEvents = CANVAS_EVENTS;
|
|
81359
84034
|
this.canvaseventlistener = (event) => this.emit(event);
|
|
81360
|
-
this.models = [];
|
|
81361
84035
|
this.loaders = [];
|
|
84036
|
+
this.models = [];
|
|
81362
84037
|
this.selected = [];
|
|
81363
84038
|
this.extents = new Box3();
|
|
81364
84039
|
this.target = new Vector3();
|
|
@@ -81390,14 +84065,22 @@ void main() {
|
|
|
81390
84065
|
this.addEventListener("optionschange", (event) => this.syncOptions(event.data));
|
|
81391
84066
|
this.scene = new Scene();
|
|
81392
84067
|
this.helpers = new Scene();
|
|
84068
|
+
const pixelRatio = window.devicePixelRatio;
|
|
81393
84069
|
const rect = canvas.parentElement.getBoundingClientRect();
|
|
81394
84070
|
const width = rect.width || 1;
|
|
81395
84071
|
const height = rect.height || 1;
|
|
81396
84072
|
const aspect = width / height;
|
|
81397
84073
|
this.camera = new PerspectiveCamera(45, aspect, 0.01, 1000);
|
|
81398
84074
|
this.camera.up.set(0, 0, 1);
|
|
81399
|
-
this.renderer = new WebGLRenderer({
|
|
81400
|
-
|
|
84075
|
+
this.renderer = new WebGLRenderer({
|
|
84076
|
+
canvas,
|
|
84077
|
+
antialias: true,
|
|
84078
|
+
alpha: true,
|
|
84079
|
+
preserveDrawingBuffer: true,
|
|
84080
|
+
powerPreference: "high-performance",
|
|
84081
|
+
logarithmicDepthBuffer: false,
|
|
84082
|
+
});
|
|
84083
|
+
this.renderer.setPixelRatio(pixelRatio);
|
|
81401
84084
|
this.renderer.setSize(width, height);
|
|
81402
84085
|
this.renderer.toneMapping = LinearToneMapping;
|
|
81403
84086
|
this.canvas = canvas;
|
|
@@ -81440,6 +84123,12 @@ void main() {
|
|
|
81440
84123
|
isInitialized() {
|
|
81441
84124
|
return !!this.renderer;
|
|
81442
84125
|
}
|
|
84126
|
+
update(force = false) {
|
|
84127
|
+
this.renderNeeded = true;
|
|
84128
|
+
if (force)
|
|
84129
|
+
this.render(performance.now());
|
|
84130
|
+
this.emitEvent({ type: "update", data: force });
|
|
84131
|
+
}
|
|
81443
84132
|
render(time) {
|
|
81444
84133
|
var _a, _b;
|
|
81445
84134
|
if (!this.renderNeeded)
|
|
@@ -81460,15 +84149,6 @@ void main() {
|
|
|
81460
84149
|
this.renderTime = time;
|
|
81461
84150
|
this.emitEvent({ type: "render", time, deltaTime });
|
|
81462
84151
|
}
|
|
81463
|
-
update(force = false) {
|
|
81464
|
-
this.renderNeeded = true;
|
|
81465
|
-
if (force)
|
|
81466
|
-
this.render(performance.now());
|
|
81467
|
-
this.emitEvent({ type: "update", data: force });
|
|
81468
|
-
}
|
|
81469
|
-
syncOptions(options = this.options) {
|
|
81470
|
-
// this.update();
|
|
81471
|
-
}
|
|
81472
84152
|
loadReferences(model) {
|
|
81473
84153
|
// todo: load reference as text fonts
|
|
81474
84154
|
return Promise.resolve(this);
|
|
@@ -81601,34 +84281,25 @@ void main() {
|
|
|
81601
84281
|
clear() {
|
|
81602
84282
|
if (!this.renderer)
|
|
81603
84283
|
return this;
|
|
81604
|
-
function disposeMaterial(material) {
|
|
81605
|
-
const materials = Array.isArray(material) ? material : [material];
|
|
81606
|
-
materials.forEach((material) => material.dispose());
|
|
81607
|
-
}
|
|
81608
|
-
function disposeObject(object) {
|
|
81609
|
-
if (object.geometry)
|
|
81610
|
-
object.geometry.dispose();
|
|
81611
|
-
if (object.material)
|
|
81612
|
-
disposeMaterial(object.material);
|
|
81613
|
-
}
|
|
81614
84284
|
this.setActiveDragger();
|
|
81615
84285
|
this.clearSlices();
|
|
81616
84286
|
this.clearOverlay();
|
|
81617
84287
|
this.clearSelected();
|
|
81618
|
-
this.helpers.traverse(disposeObject);
|
|
81619
|
-
this.helpers.clear();
|
|
81620
|
-
this.models.forEach((model) => model.traverse(disposeObject));
|
|
81621
|
-
this.models.forEach((model) => model.removeFromParent());
|
|
81622
|
-
this.models = [];
|
|
81623
|
-
this.scene.clear();
|
|
81624
84288
|
this.loaders.forEach((loader) => loader.dispose());
|
|
81625
84289
|
this.loaders = [];
|
|
84290
|
+
this.models.forEach((model) => model.dispose());
|
|
84291
|
+
this.models = [];
|
|
84292
|
+
this.helpers.clear();
|
|
84293
|
+
this.scene.clear();
|
|
81626
84294
|
this.syncOptions();
|
|
81627
84295
|
this.syncOverlay();
|
|
81628
84296
|
this.update(true);
|
|
81629
84297
|
this.emitEvent({ type: "clear" });
|
|
81630
84298
|
return this;
|
|
81631
84299
|
}
|
|
84300
|
+
syncOptions(options = this.options) {
|
|
84301
|
+
// this.update();
|
|
84302
|
+
}
|
|
81632
84303
|
syncOverlay() {
|
|
81633
84304
|
if (!this.renderer)
|
|
81634
84305
|
return;
|
|
@@ -81898,6 +84569,7 @@ void main() {
|
|
|
81898
84569
|
exports.GLTFLoadingManager = GLTFLoadingManager;
|
|
81899
84570
|
exports.Loader = Loader$1;
|
|
81900
84571
|
exports.Markup = KonvaMarkup;
|
|
84572
|
+
exports.ModelImpl = ModelImpl;
|
|
81901
84573
|
exports.Options = Options;
|
|
81902
84574
|
exports.Viewer = Viewer;
|
|
81903
84575
|
exports.commands = commands;
|