@inweb/viewer-three 27.2.0 → 27.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -572,6 +572,7 @@
572
572
  this.memory = {
573
573
  geometries: 0,
574
574
  geometryBytes: 0,
575
+ optimizedGeometryBytes: 0,
575
576
  textures: 0,
576
577
  textureBytes: 0,
577
578
  materials: 0,
@@ -21254,7 +21255,7 @@
21254
21255
  this.material.dispose();
21255
21256
  }
21256
21257
  }
21257
- let PlaneHelper$1 = class PlaneHelper extends Line$1 {
21258
+ class PlaneHelper extends Line$1 {
21258
21259
  constructor( plane, size = 1, hex = 0xffff00 ) {
21259
21260
  const color = hex;
21260
21261
  const positions = [ 1, -1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0 ];
@@ -21284,7 +21285,7 @@
21284
21285
  this.children[ 0 ].geometry.dispose();
21285
21286
  this.children[ 0 ].material.dispose();
21286
21287
  }
21287
- };
21288
+ }
21288
21289
  const _axis = new Vector3();
21289
21290
  let _lineGeometry, _coneGeometry;
21290
21291
  class ArrowHelper extends Object3D {
@@ -32454,7 +32455,7 @@ void main() {
32454
32455
  PerspectiveCamera: PerspectiveCamera,
32455
32456
  Plane: Plane,
32456
32457
  PlaneGeometry: PlaneGeometry,
32457
- PlaneHelper: PlaneHelper$1,
32458
+ PlaneHelper: PlaneHelper,
32458
32459
  PointLight: PointLight,
32459
32460
  PointLightHelper: PointLightHelper,
32460
32461
  Points: Points,
@@ -33643,17 +33644,15 @@ void main() {
33643
33644
  }
33644
33645
  }
33645
33646
 
33646
- class PlaneHelper extends Line$1 {
33647
- constructor(plane, size = 1, color = 0xffff00, offset = new Vector3()) {
33647
+ class PlaneHelper2 extends Line$1 {
33648
+ constructor(size = 1, color = 0xc0c0c0) {
33648
33649
  const positions = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0];
33649
33650
  const geometry = new BufferGeometry();
33650
33651
  geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
33651
33652
  geometry.computeBoundingSphere();
33652
33653
  super(geometry, new LineBasicMaterial({ color, toneMapped: false }));
33653
- this.type = "PlaneHelper";
33654
- this.plane = plane;
33654
+ this.type = "PlaneHelper2";
33655
33655
  this.size = size;
33656
- this.offset = offset;
33657
33656
  const positions2 = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0];
33658
33657
  const geometry2 = new BufferGeometry();
33659
33658
  geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3));
@@ -33671,14 +33670,10 @@ void main() {
33671
33670
  dispose() {
33672
33671
  this.geometry.dispose();
33673
33672
  this.material.dispose();
33674
- this.children[0].geometry.dispose();
33675
- this.children[0].material.dispose();
33673
+ this.helper.geometry.dispose();
33674
+ this.helper.material.dispose();
33676
33675
  }
33677
33676
  updateMatrixWorld(force) {
33678
- this.position.set(0, 0, 0);
33679
- this.lookAt(this.plane.normal);
33680
- this.position.copy(this.offset);
33681
- this.translateZ(-(this.offset.dot(this.plane.normal) + this.plane.constant));
33682
33677
  this.scale.set(0.5 * this.size, 0.5 * this.size, 1);
33683
33678
  super.updateMatrixWorld(force);
33684
33679
  }
@@ -34434,26 +34429,41 @@ void main() {
34434
34429
  }
34435
34430
 
34436
34431
  class CuttingPlaneDragger extends OrbitDragger {
34437
- constructor(viewer, normal, color) {
34432
+ constructor(viewer, normal) {
34438
34433
  super(viewer);
34439
34434
  this.transformChange = () => {
34435
+ this.plane.normal.copy(new Vector3(0, 0, -1)).applyQuaternion(this.planeCenter.quaternion);
34440
34436
  this.plane.constant = -this.planeCenter.position.dot(this.plane.normal);
34441
34437
  this.viewer.update();
34442
34438
  };
34443
- this.transformDrag = (event) => {
34439
+ this.translateDrag = (event) => {
34444
34440
  this.orbit.enabled = !event.value;
34441
+ this.rotate.enabled = !event.value;
34442
+ };
34443
+ this.rotateDrag = (event) => {
34444
+ this.orbit.enabled = !event.value;
34445
+ this.translate.enabled = !event.value;
34445
34446
  };
34446
34447
  this.updatePlaneSize = () => {
34447
34448
  this.planeHelper.size = this.viewer.extents.getSize(new Vector3()).length() || 1;
34448
34449
  this.viewer.update();
34449
34450
  };
34450
34451
  this.updateTransformCamera = () => {
34451
- this.transform.camera = this.viewer.camera;
34452
+ this.translate.camera = this.viewer.camera;
34453
+ this.rotate.camera = this.viewer.camera;
34454
+ };
34455
+ this.onKeyDown = (event) => {
34456
+ if (event.key === "Shift")
34457
+ this.rotate.setRotationSnap(Math.PI / 4);
34458
+ };
34459
+ this.onKeyUp = (event) => {
34460
+ if (event.key === "Shift")
34461
+ this.rotate.setRotationSnap(null);
34452
34462
  };
34453
34463
  this.onDoubleClick = (event) => {
34454
34464
  event.stopPropagation();
34455
- this.plane.negate();
34456
- this.viewer.update();
34465
+ this.planeCenter.rotateOnAxis(new Vector3(0, 1, 0), Math.PI);
34466
+ this.transformChange();
34457
34467
  };
34458
34468
  const extentsSize = viewer.extents.getSize(new Vector3()).length() || 1;
34459
34469
  const extentsCenter = viewer.extents.getCenter(new Vector3());
@@ -34462,24 +34472,38 @@ void main() {
34462
34472
  if (!viewer.renderer.clippingPlanes)
34463
34473
  viewer.renderer.clippingPlanes = [];
34464
34474
  viewer.renderer.clippingPlanes.push(this.plane);
34465
- this.planeHelper = new PlaneHelper(this.plane, extentsSize, color, extentsCenter);
34466
- this.viewer.helpers.add(this.planeHelper);
34467
34475
  this.planeCenter = new Object3D();
34468
34476
  this.planeCenter.position.copy(extentsCenter);
34477
+ this.planeCenter.quaternion.setFromUnitVectors(new Vector3(0, 0, -1), normal);
34469
34478
  this.viewer.helpers.add(this.planeCenter);
34470
- this.transform = new TransformControls(viewer.camera, viewer.canvas);
34471
- this.transform.showX = !!normal.x;
34472
- this.transform.showY = !!normal.y;
34473
- this.transform.showZ = !!normal.z;
34474
- this.transform.attach(this.planeCenter);
34475
- this.transform.addEventListener("change", this.transformChange);
34476
- this.transform.addEventListener("dragging-changed", this.transformDrag);
34477
- this.viewer.helpers.add(this.transform.getHelper());
34479
+ this.planeHelper = new PlaneHelper2(extentsSize);
34480
+ this.planeCenter.add(this.planeHelper);
34481
+ this.translate = new TransformControls(viewer.camera, viewer.canvas);
34482
+ this.translate.setSpace("local");
34483
+ this.translate.showX = false;
34484
+ this.translate.showY = false;
34485
+ this.translate.showZ = true;
34486
+ this.translate.attach(this.planeCenter);
34487
+ this.translate.addEventListener("change", this.transformChange);
34488
+ this.translate.addEventListener("dragging-changed", this.translateDrag);
34489
+ this.viewer.helpers.add(this.translate.getHelper());
34490
+ this.rotate = new TransformControls(viewer.camera, viewer.canvas);
34491
+ this.rotate.setMode("rotate");
34492
+ this.rotate.setSpace("local");
34493
+ this.rotate.showX = true;
34494
+ this.rotate.showY = true;
34495
+ this.rotate.showZ = false;
34496
+ this.rotate.attach(this.planeCenter);
34497
+ this.rotate.addEventListener("change", this.transformChange);
34498
+ this.rotate.addEventListener("dragging-changed", this.rotateDrag);
34499
+ this.viewer.helpers.add(this.rotate.getHelper());
34478
34500
  this.viewer.addEventListener("explode", this.updatePlaneSize);
34479
34501
  this.viewer.addEventListener("show", this.updatePlaneSize);
34480
34502
  this.viewer.addEventListener("showall", this.updatePlaneSize);
34481
34503
  this.viewer.addEventListener("changecameramode", this.updateTransformCamera);
34482
34504
  this.viewer.canvas.addEventListener("dblclick", this.onDoubleClick, true);
34505
+ window.addEventListener("keydown", this.onKeyDown);
34506
+ window.addEventListener("keyup", this.onKeyUp);
34483
34507
  this.viewer.update();
34484
34508
  }
34485
34509
  dispose() {
@@ -34488,11 +34512,18 @@ void main() {
34488
34512
  this.viewer.removeEventListener("showall", this.updatePlaneSize);
34489
34513
  this.viewer.removeEventListener("changecameramode", this.updateTransformCamera);
34490
34514
  this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
34491
- this.transform.removeEventListener("change", this.transformChange);
34492
- this.transform.removeEventListener("dragging-changed", this.transformDrag);
34493
- this.transform.getHelper().removeFromParent();
34494
- this.transform.detach();
34495
- this.transform.dispose();
34515
+ window.removeEventListener("keydown", this.onKeyDown);
34516
+ window.removeEventListener("keyup", this.onKeyUp);
34517
+ this.translate.removeEventListener("change", this.transformChange);
34518
+ this.translate.removeEventListener("dragging-changed", this.translateDrag);
34519
+ this.translate.getHelper().removeFromParent();
34520
+ this.translate.detach();
34521
+ this.translate.dispose();
34522
+ this.rotate.removeEventListener("change", this.transformChange);
34523
+ this.rotate.removeEventListener("dragging-changed", this.rotateDrag);
34524
+ this.rotate.getHelper().removeFromParent();
34525
+ this.rotate.detach();
34526
+ this.rotate.dispose();
34496
34527
  this.planeHelper.removeFromParent();
34497
34528
  this.planeHelper.dispose();
34498
34529
  this.planeCenter.removeFromParent();
@@ -34502,19 +34533,19 @@ void main() {
34502
34533
 
34503
34534
  class CuttingPlaneXAxisDragger extends CuttingPlaneDragger {
34504
34535
  constructor(viewer) {
34505
- super(viewer, new Vector3(1, 0, 0), 0xff0000);
34536
+ super(viewer, new Vector3(-1, 0, 0));
34506
34537
  }
34507
34538
  }
34508
34539
 
34509
34540
  class CuttingPlaneYAxisDragger extends CuttingPlaneDragger {
34510
34541
  constructor(viewer) {
34511
- super(viewer, new Vector3(0, 1, 0), 0x00ff00);
34542
+ super(viewer, new Vector3(0, -1, 0));
34512
34543
  }
34513
34544
  }
34514
34545
 
34515
34546
  class CuttingPlaneZAxisDragger extends CuttingPlaneDragger {
34516
34547
  constructor(viewer) {
34517
- super(viewer, new Vector3(0, 0, 1), 0x0000ff);
34548
+ super(viewer, new Vector3(0, 0, -1));
34518
34549
  }
34519
34550
  }
34520
34551
 
@@ -35116,6 +35147,12 @@ void main() {
35116
35147
  this.movementSpeed = 0.1;
35117
35148
  this.multiplier = 3;
35118
35149
  this.groundFollowingSkippedFrames = 0;
35150
+ this.GROUND_BOX_HALF_SIZE = 20;
35151
+ this.GROUND_BOX_REFRESH_THRESHOLD = 0.3;
35152
+ this._groundObjectBoxes = new Map();
35153
+ this._activeGroundObjects = [];
35154
+ this._groundBox = new Box3();
35155
+ this._groundBoxCenter = new Vector3();
35119
35156
  this.moveWheel = 0;
35120
35157
  this.mouseDragOn = false;
35121
35158
  this._up = new Vector3();
@@ -35187,6 +35224,9 @@ void main() {
35187
35224
  };
35188
35225
  this.camera = camera;
35189
35226
  this.groundObjects = groundObjects;
35227
+ for (const obj of groundObjects) {
35228
+ this._groundObjectBoxes.set(obj, new Box3().setFromObject(obj));
35229
+ }
35190
35230
  this.raycaster = new Raycaster();
35191
35231
  this.raycaster.near = 0;
35192
35232
  this.raycaster.far = this.EYE_HEIGHT + this.FAILING_DISTANCE;
@@ -35221,10 +35261,29 @@ void main() {
35221
35261
  window.removeEventListener("keyup", this.onKeyUp);
35222
35262
  super.dispose();
35223
35263
  }
35264
+ _rebuildGroundBox(center) {
35265
+ const h = this.GROUND_BOX_HALF_SIZE;
35266
+ this._groundBoxCenter.copy(center);
35267
+ this._groundBox.set(new Vector3(center.x - h, center.y - h * 4, center.z - h), new Vector3(center.x + h, center.y + h * 4, center.z + h));
35268
+ this._activeGroundObjects = this.groundObjects.filter((obj) => {
35269
+ const objectBox = this._groundObjectBoxes.get(obj);
35270
+ return objectBox !== undefined && this._groundBox.intersectsBox(objectBox);
35271
+ });
35272
+ }
35273
+ _needsGroundBoxRebuild(pos) {
35274
+ if (this._activeGroundObjects.length === 0 && this.groundObjects.length > 0)
35275
+ return true;
35276
+ const threshold = this.GROUND_BOX_HALF_SIZE * this.GROUND_BOX_REFRESH_THRESHOLD;
35277
+ return (Math.abs(pos.x - this._groundBoxCenter.x) > threshold || Math.abs(pos.z - this._groundBoxCenter.z) > threshold);
35278
+ }
35224
35279
  updateGroundFollowing() {
35280
+ const pos = this.object.position;
35281
+ if (this._needsGroundBoxRebuild(pos)) {
35282
+ this._rebuildGroundBox(pos);
35283
+ }
35225
35284
  this._up.copy(this.camera.up).negate();
35226
- this.raycaster.set(this.object.position, this._up);
35227
- const intersects = this.raycaster.intersectObjects(this.groundObjects, false);
35285
+ this.raycaster.set(pos, this._up);
35286
+ const intersects = this.raycaster.intersectObjects(this._activeGroundObjects, false);
35228
35287
  if (intersects.length > 0) {
35229
35288
  const groundY = intersects[0].point.y;
35230
35289
  const targetY = groundY + this.EYE_HEIGHT;
@@ -36101,7 +36160,7 @@ void main() {
36101
36160
  camera = object;
36102
36161
  });
36103
36162
  if (camera) {
36104
- camera.isDefaultCamera = true;
36163
+ camera.userData.isDefaultCamera = true;
36105
36164
  camera.scale.set(1, 1, 1);
36106
36165
  this.switchCamera(camera);
36107
36166
  const mode = this.getCameraMode(camera);
@@ -36305,6 +36364,7 @@ void main() {
36305
36364
  this.viewer.info.optimizedScene.edges = 0;
36306
36365
  this.viewer.info.memory.geometries = 0;
36307
36366
  this.viewer.info.memory.geometryBytes = 0;
36367
+ this.viewer.info.memory.optimizedGeometryBytes = 0;
36308
36368
  this.viewer.info.memory.textures = 0;
36309
36369
  this.viewer.info.memory.textureBytes = 0;
36310
36370
  this.viewer.info.memory.materials = 0;
@@ -36341,6 +36401,7 @@ void main() {
36341
36401
  this.viewer.info.optimizedScene.edges += info.optimizedScene.edges;
36342
36402
  this.viewer.info.memory.geometries += info.memory.geometries;
36343
36403
  this.viewer.info.memory.geometryBytes += info.memory.geometryBytes;
36404
+ this.viewer.info.memory.optimizedGeometryBytes += info.memory.optimizedGeometryBytes;
36344
36405
  this.viewer.info.memory.textures += info.memory.textures;
36345
36406
  this.viewer.info.memory.textureBytes += info.memory.textureBytes;
36346
36407
  this.viewer.info.memory.materials += info.memory.materials;
@@ -36351,7 +36412,9 @@ void main() {
36351
36412
  this.viewer.info.performance.loadTime += performance.now() - this.startTime;
36352
36413
  console.log("Number of objects:", info.scene.objects);
36353
36414
  console.log("Number of objects after optimization:", info.optimizedScene.objects);
36354
- console.log("Total geometry size:", info.memory.totalEstimatedGpuBytes / (1024 * 1024), "MB");
36415
+ console.log("Geometry size:", info.memory.geometryBytes / (1024 * 1024), "MB");
36416
+ console.log("Optimized geometry size:", info.memory.optimizedGeometryBytes / (1024 * 1024), "MB");
36417
+ console.log("Estimated GPU used:", info.memory.totalEstimatedGpuBytes / (1024 * 1024), "MB");
36355
36418
  console.log("File load time:", this.viewer.info.performance.loadTime, "ms");
36356
36419
  };
36357
36420
  this.resize = () => {
@@ -37733,6 +37796,7 @@ void main() {
37733
37796
  info.scene.edges = Math.floor(totalEdges);
37734
37797
  info.memory.geometries = geometries.size;
37735
37798
  info.memory.geometryBytes = geometryBytes;
37799
+ info.memory.optimizedGeometryBytes = 0;
37736
37800
  info.memory.textures = textures.size;
37737
37801
  info.memory.textureBytes = Math.floor(textureBytes);
37738
37802
  info.memory.materials = materials.size;
@@ -37905,6 +37969,7 @@ void main() {
37905
37969
  info.optimizedScene.edges = stats.scene.afterOptimization.edges;
37906
37970
  info.memory.geometries = stats.memory.geometries.count;
37907
37971
  info.memory.geometryBytes = stats.memory.geometries.bytes;
37972
+ info.memory.optimizedGeometryBytes = stats.memory.geometries.optimizedBytes;
37908
37973
  info.memory.textures = stats.memory.textures.count;
37909
37974
  info.memory.materials = stats.memory.materials.count;
37910
37975
  info.memory.totalEstimatedGpuBytes = stats.memory.totalEstimatedGpuBytes;
@@ -38725,11 +38790,14 @@ void main() {
38725
38790
  this.structures = [];
38726
38791
  this.structureRoots = new Map();
38727
38792
  this.memoryLimit = this.getAvailableMemory();
38793
+ this.optimizationMemoryMultiplier = 5;
38794
+ this.memoryEstimationFactor = 1.7;
38728
38795
  this.loadedGeometrySize = 0;
38729
38796
  this.geometryCache = new Map();
38730
38797
  this.materialCache = new Map();
38731
38798
  this.textureCache = new Map();
38732
38799
  this.currentMemoryUsage = 0;
38800
+ this.pendingMemoryUsage = 0;
38733
38801
  this.updateMemoryIndicator();
38734
38802
  this.loadedMaterials = new Map();
38735
38803
  this.abortController = new AbortController();
@@ -38785,7 +38853,7 @@ void main() {
38785
38853
  } catch (error) {
38786
38854
  console.warn("Error detecting available memory:", error);
38787
38855
  }
38788
- return memoryLimit / 3;
38856
+ return memoryLimit;
38789
38857
  }
38790
38858
  getAbortController() {
38791
38859
  return this.abortController;
@@ -38793,9 +38861,26 @@ void main() {
38793
38861
  abortLoading() {
38794
38862
  this.abortController.abort();
38795
38863
  }
38864
+ getOptimizedGeometrySize() {
38865
+ let total = 0;
38866
+ const addSize = (obj) => {
38867
+ if (obj && obj.geometry) total += this.estimateGeometrySize(obj);
38868
+ };
38869
+ this.mergedMesh?.forEach(addSize);
38870
+ this.mergedLines?.forEach(addSize);
38871
+ this.mergedLineSegments?.forEach(addSize);
38872
+ this.mergedPoints?.forEach(addSize);
38873
+ return total;
38874
+ }
38796
38875
  updateMemoryIndicator() {
38876
+ const optimizedUsage = this.getOptimizedGeometrySize();
38877
+ const totalUsage = this.currentMemoryUsage + optimizedUsage;
38878
+ const totalUsageEstimate = Math.round(totalUsage * this.memoryEstimationFactor);
38797
38879
  this.dispatchEvent("geometrymemory", {
38798
38880
  currentUsage: this.currentMemoryUsage,
38881
+ optimizedUsage,
38882
+ totalUsage,
38883
+ totalUsageEstimate,
38799
38884
  limit: this.memoryLimit,
38800
38885
  });
38801
38886
  }
@@ -38847,10 +38932,13 @@ void main() {
38847
38932
  for (const geo of geometries) {
38848
38933
  currentMemoryUsage += geo.size;
38849
38934
  }
38850
- if (currentMemoryUsage > this.memoryLimit) {
38851
- console.log(`Memory usage (${Math.round(currentMemoryUsage / (1024 * 1024))}MB) exceeds limit`);
38935
+ const effectiveLimitForEviction = this.memoryLimit / this.memoryEstimationFactor;
38936
+ if (currentMemoryUsage > effectiveLimitForEviction) {
38937
+ console.log(
38938
+ `Memory usage (${Math.round((currentMemoryUsage * this.memoryEstimationFactor) / (1024 * 1024))}MB est.) exceeds limit`
38939
+ );
38852
38940
  for (const geo of geometries) {
38853
- if (currentMemoryUsage <= this.memoryLimit) break;
38941
+ if (currentMemoryUsage <= effectiveLimitForEviction) break;
38854
38942
  if (this.abortController.signal.aborted) {
38855
38943
  throw new DOMException("Loading aborted", "AbortError");
38856
38944
  }
@@ -38934,7 +39022,9 @@ void main() {
38934
39022
  }
38935
39023
  const materialCount = uniqueMaterialIds.size;
38936
39024
  const textureCount = uniqueTextureIds.size;
38937
- const estimatedGpuMemoryBytes = geometryMemoryBytes;
39025
+ const optimizedUsageBytes = this.getOptimizedGeometrySize();
39026
+ const totalUsageBytes = geometryMemoryBytes + optimizedUsageBytes;
39027
+ const estimatedGpuMemoryBytes = Math.round(totalUsageBytes * this.memoryEstimationFactor);
38938
39028
  if (!this._webglInfoCache) {
38939
39029
  try {
38940
39030
  const gl = this.renderer.getContext();
@@ -38971,7 +39061,12 @@ void main() {
38971
39061
  },
38972
39062
  },
38973
39063
  memory: {
38974
- geometries: { count: geometryCount, bytes: geometryMemoryBytes },
39064
+ geometries: {
39065
+ count: geometryCount,
39066
+ bytes: geometryMemoryBytes,
39067
+ optimizedBytes: optimizedUsageBytes,
39068
+ totalRawBytes: totalUsageBytes,
39069
+ },
38975
39070
  textures: { count: textureCount },
38976
39071
  materials: { count: materialCount },
38977
39072
  totalEstimatedGpuBytes: estimatedGpuMemoryBytes,
@@ -38983,9 +39078,12 @@ void main() {
38983
39078
  },
38984
39079
  };
38985
39080
  }
38986
- async loadNode(nodeId, onLoadFinishCb) {
39081
+ async loadNode(nodeId, onLoadFinishCb, reservedEstimatedSize = 0) {
38987
39082
  const node = this.nodes.get(nodeId);
38988
- if (!node || node.loaded || node.loading) return;
39083
+ if (!node || node.loaded || node.loading) {
39084
+ this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
39085
+ return;
39086
+ }
38989
39087
  node.loading = true;
38990
39088
  const meshDef = node.structure.getJson().meshes[node.meshIndex];
38991
39089
  try {
@@ -39068,6 +39166,7 @@ void main() {
39068
39166
  if (bufferRequests.length === 0) {
39069
39167
  node.loaded = true;
39070
39168
  node.loading = false;
39169
+ this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
39071
39170
  return;
39072
39171
  }
39073
39172
  bufferRequests.sort((a, b) => a.offset - b.offset);
@@ -39179,6 +39278,7 @@ void main() {
39179
39278
  }
39180
39279
  node.loaded = true;
39181
39280
  node.loading = false;
39281
+ this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
39182
39282
  const geometrySize = this.estimateGeometrySize(node.object);
39183
39283
  this.geometryCache.set(node.object.uuid, geometrySize);
39184
39284
  this.currentMemoryUsage += geometrySize;
@@ -39187,6 +39287,7 @@ void main() {
39187
39287
  }
39188
39288
  } catch (error) {
39189
39289
  node.loading = false;
39290
+ this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
39190
39291
  if (error.name === "AbortError") {
39191
39292
  return;
39192
39293
  }
@@ -39402,15 +39503,18 @@ void main() {
39402
39503
  let loadedCount = 0;
39403
39504
  let lastLoadedCount = 0;
39404
39505
  const totalNodes = nodesToLoad.length;
39506
+ const progressTotal = { value: totalNodes };
39405
39507
  const loadProgress = async () => {
39406
39508
  loadedCount++;
39509
+ const total = progressTotal.value;
39510
+ const percentage = total > 0 ? Math.min(100, Math.round((loadedCount / total) * 100)) : 0;
39407
39511
  if (loadedCount - lastLoadedCount > 1000) {
39408
39512
  lastLoadedCount = loadedCount;
39409
39513
  this.updateMemoryIndicator();
39410
39514
  this.dispatchEvent("geometryprogress", {
39411
- percentage: Math.round((loadedCount / totalNodes) * 100),
39515
+ percentage,
39412
39516
  loaded: loadedCount,
39413
- total: totalNodes,
39517
+ total,
39414
39518
  });
39415
39519
  this.dispatchEvent("update");
39416
39520
  await new Promise((resolve) => {
@@ -39420,20 +39524,22 @@ void main() {
39420
39524
  };
39421
39525
  try {
39422
39526
  const loadOperations = [];
39527
+ let memoryLimitReached = false;
39423
39528
  for (const nodeId of nodesToLoad) {
39424
39529
  if (this.abortController.signal.aborted) {
39425
39530
  throw new DOMException("Loading aborted", "AbortError");
39426
39531
  }
39427
39532
  const estimatedSize = await this.estimateNodeSize(nodeId);
39428
- if (this.currentMemoryUsage + estimatedSize > this.memoryLimit) {
39429
- console.log(`Memory limit reached after loading ${loadedCount} nodes`);
39430
- this.dispatchEvent("geometryerror", {
39431
- message: "Memory limit reached",
39432
- });
39433
- this.dispatchEvent("update");
39434
- return loadedCount;
39533
+ const estimated = Number(estimatedSize) || 0;
39534
+ const effectiveLimit = this.memoryLimit / this.optimizationMemoryMultiplier / this.memoryEstimationFactor;
39535
+ if (this.currentMemoryUsage + this.pendingMemoryUsage + estimated > effectiveLimit) {
39536
+ memoryLimitReached = true;
39537
+ progressTotal.value = loadOperations.length;
39538
+ console.log(`Memory limit reached after scheduling ${loadOperations.length} nodes`);
39539
+ break;
39435
39540
  }
39436
- loadOperations.push(this.loadNode(nodeId, loadProgress));
39541
+ this.pendingMemoryUsage += estimated;
39542
+ loadOperations.push(this.loadNode(nodeId, loadProgress, estimated));
39437
39543
  }
39438
39544
  for (const structure of this.structures) {
39439
39545
  loadOperations.push(structure.flushBufferRequests());
@@ -39441,7 +39547,8 @@ void main() {
39441
39547
  await Promise.all(loadOperations);
39442
39548
  this.dispatchEvent("geometryend", {
39443
39549
  totalLoaded: loadedCount,
39444
- totalNodes,
39550
+ totalNodes: progressTotal.value,
39551
+ memoryLimitReached,
39445
39552
  });
39446
39553
  return loadedCount;
39447
39554
  } catch (error) {
@@ -39751,6 +39858,7 @@ void main() {
39751
39858
  this.transformedGeometries.clear();
39752
39859
  this.totalLoadedObjects = 0;
39753
39860
  this.currentMemoryUsage = 0;
39861
+ this.pendingMemoryUsage = 0;
39754
39862
  this.loadedGeometrySize = 0;
39755
39863
  this.abortController = new AbortController();
39756
39864
  this.updateMemoryIndicator();
@@ -39957,6 +40065,7 @@ void main() {
39957
40065
  progress: 100,
39958
40066
  message: `Optimization complete! ${this.maxObjectId} objects processed.`,
39959
40067
  });
40068
+ this.updateMemoryIndicator();
39960
40069
  this.dispatchEvent("update");
39961
40070
  }
39962
40071
  async mergeMeshGroups(materialGroups, rootGroup) {
@@ -57695,12 +57804,14 @@ js: import "konva/skia-backend";
57695
57804
  if (!this.renderer)
57696
57805
  return;
57697
57806
  this._markup.clearOverlay();
57807
+ this.emitEvent({ type: "clearoverlay" });
57698
57808
  this.update();
57699
57809
  }
57700
57810
  clearSlices() {
57701
57811
  if (!this.renderer)
57702
57812
  return;
57703
57813
  this.renderer.clippingPlanes = [];
57814
+ this.emitEvent({ type: "clearslices" });
57704
57815
  this.update();
57705
57816
  }
57706
57817
  getSelected() {