@inweb/viewer-three 27.1.9 → 27.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1506,14 +1506,19 @@ class WalkControls extends Controls {
1506
1506
  super(camera, canvas);
1507
1507
  this.EYE_HEIGHT = 1.7;
1508
1508
  this.FAILING_DISTANCE = 2;
1509
- this.GROUND_FOLLOWING_SPEED = 0.05;
1509
+ this.GROUND_FOLLOWING_SKIP_FRAMES = 3;
1510
+ this.GROUND_FOLLOWING_SPEED = 0.4;
1510
1511
  this.LOOK_SPEED = 0.1;
1511
1512
  this.WALK_SPEED_DELIMITER = 4;
1512
1513
  this.WHEEL_SPEED_DELIMITER = 15000;
1513
1514
  this.movementSpeed = 0.1;
1514
1515
  this.multiplier = 3;
1516
+ this.groundFollowingSkippedFrames = 0;
1515
1517
  this.moveWheel = 0;
1516
1518
  this.mouseDragOn = false;
1519
+ this._up = new Vector3();
1520
+ this._forward = new Vector3();
1521
+ this._sideways = new Vector3();
1517
1522
  this.onPointerDown = (event) => {
1518
1523
  if (event.button !== 0)
1519
1524
  return;
@@ -1583,6 +1588,14 @@ class WalkControls extends Controls {
1583
1588
  this.raycaster = new Raycaster();
1584
1589
  this.raycaster.near = 0;
1585
1590
  this.raycaster.far = this.EYE_HEIGHT + this.FAILING_DISTANCE;
1591
+ this.raycaster.params = {
1592
+ Mesh: {},
1593
+ Line: { threshold: 0 },
1594
+ Line2: { threshold: 0 },
1595
+ LOD: { threshold: 0 },
1596
+ Points: { threshold: 0 },
1597
+ Sprite: { threshold: 0 },
1598
+ };
1586
1599
  this.moveKeys = new Set();
1587
1600
  this.moveClock = new Clock();
1588
1601
  this.quaternion = camera.quaternion.clone();
@@ -1607,16 +1620,8 @@ class WalkControls extends Controls {
1607
1620
  super.dispose();
1608
1621
  }
1609
1622
  updateGroundFollowing() {
1610
- const up = new Vector3().copy(this.camera.up);
1611
- this.raycaster.set(this.object.position, up.negate());
1612
- this.raycaster.params = this.raycaster.params = {
1613
- Mesh: {},
1614
- Line: { threshold: 0 },
1615
- Line2: { threshold: 0 },
1616
- LOD: { threshold: 0 },
1617
- Points: { threshold: 0 },
1618
- Sprite: { threshold: 0 },
1619
- };
1623
+ this._up.copy(this.camera.up).negate();
1624
+ this.raycaster.set(this.object.position, this._up);
1620
1625
  const intersects = this.raycaster.intersectObjects(this.groundObjects, false);
1621
1626
  if (intersects.length > 0) {
1622
1627
  const groundY = intersects[0].point.y;
@@ -1627,8 +1632,8 @@ class WalkControls extends Controls {
1627
1632
  update() {
1628
1633
  let moved = false;
1629
1634
  let upgradeGroundFollowing = false;
1630
- const forward = new Vector3();
1631
- const sideways = new Vector3();
1635
+ const forward = this._forward;
1636
+ const sideways = this._sideways;
1632
1637
  if (this.moveKeys.size > 0) {
1633
1638
  upgradeGroundFollowing = true;
1634
1639
  const timeDelta = this.moveClock.getDelta();
@@ -1680,8 +1685,11 @@ class WalkControls extends Controls {
1680
1685
  this.moveWheel += -1 * Math.sign(this.moveWheel);
1681
1686
  moved = true;
1682
1687
  }
1683
- if (upgradeGroundFollowing)
1688
+ this.groundFollowingSkippedFrames++;
1689
+ if (upgradeGroundFollowing && this.groundFollowingSkippedFrames >= this.GROUND_FOLLOWING_SKIP_FRAMES) {
1690
+ this.groundFollowingSkippedFrames = 0;
1684
1691
  this.updateGroundFollowing();
1692
+ }
1685
1693
  if (moved) {
1686
1694
  this.dispatchEvent({ type: "change" });
1687
1695
  }
@@ -2491,7 +2499,7 @@ class CameraComponent {
2491
2499
  camera = object;
2492
2500
  });
2493
2501
  if (camera) {
2494
- camera.isDefaultCamera = true;
2502
+ camera.userData.isDefaultCamera = true;
2495
2503
  camera.scale.set(1, 1, 1);
2496
2504
  this.switchCamera(camera);
2497
2505
  const mode = this.getCameraMode(camera);
@@ -2695,6 +2703,7 @@ class InfoComponent {
2695
2703
  this.viewer.info.optimizedScene.edges = 0;
2696
2704
  this.viewer.info.memory.geometries = 0;
2697
2705
  this.viewer.info.memory.geometryBytes = 0;
2706
+ this.viewer.info.memory.optimizedGeometryBytes = 0;
2698
2707
  this.viewer.info.memory.textures = 0;
2699
2708
  this.viewer.info.memory.textureBytes = 0;
2700
2709
  this.viewer.info.memory.materials = 0;
@@ -2731,6 +2740,7 @@ class InfoComponent {
2731
2740
  this.viewer.info.optimizedScene.edges += info.optimizedScene.edges;
2732
2741
  this.viewer.info.memory.geometries += info.memory.geometries;
2733
2742
  this.viewer.info.memory.geometryBytes += info.memory.geometryBytes;
2743
+ this.viewer.info.memory.optimizedGeometryBytes += info.memory.optimizedGeometryBytes;
2734
2744
  this.viewer.info.memory.textures += info.memory.textures;
2735
2745
  this.viewer.info.memory.textureBytes += info.memory.textureBytes;
2736
2746
  this.viewer.info.memory.materials += info.memory.materials;
@@ -2741,7 +2751,9 @@ class InfoComponent {
2741
2751
  this.viewer.info.performance.loadTime += performance.now() - this.startTime;
2742
2752
  console.log("Number of objects:", info.scene.objects);
2743
2753
  console.log("Number of objects after optimization:", info.optimizedScene.objects);
2744
- console.log("Total geometry size:", info.memory.totalEstimatedGpuBytes / (1024 * 1024), "MB");
2754
+ console.log("Geometry size:", info.memory.geometryBytes / (1024 * 1024), "MB");
2755
+ console.log("Optimized geometry size:", info.memory.optimizedGeometryBytes / (1024 * 1024), "MB");
2756
+ console.log("Estimated GPU used:", info.memory.totalEstimatedGpuBytes / (1024 * 1024), "MB");
2745
2757
  console.log("File load time:", this.viewer.info.performance.loadTime, "ms");
2746
2758
  };
2747
2759
  this.resize = () => {
@@ -3484,6 +3496,7 @@ class ModelImpl {
3484
3496
  info.scene.edges = Math.floor(totalEdges);
3485
3497
  info.memory.geometries = geometries.size;
3486
3498
  info.memory.geometryBytes = geometryBytes;
3499
+ info.memory.optimizedGeometryBytes = 0;
3487
3500
  info.memory.textures = textures.size;
3488
3501
  info.memory.textureBytes = Math.floor(textureBytes);
3489
3502
  info.memory.materials = materials.size;
@@ -3656,6 +3669,7 @@ class DynamicModelImpl extends ModelImpl {
3656
3669
  info.optimizedScene.edges = stats.scene.afterOptimization.edges;
3657
3670
  info.memory.geometries = stats.memory.geometries.count;
3658
3671
  info.memory.geometryBytes = stats.memory.geometries.bytes;
3672
+ info.memory.optimizedGeometryBytes = stats.memory.geometries.optimizedBytes;
3659
3673
  info.memory.textures = stats.memory.textures.count;
3660
3674
  info.memory.materials = stats.memory.materials.count;
3661
3675
  info.memory.totalEstimatedGpuBytes = stats.memory.totalEstimatedGpuBytes;
@@ -4327,11 +4341,14 @@ class DynamicGltfLoader {
4327
4341
  this.structures = [];
4328
4342
  this.structureRoots = new Map();
4329
4343
  this.memoryLimit = this.getAvailableMemory();
4344
+ this.optimizationMemoryMultiplier = 5;
4345
+ this.memoryEstimationFactor = 1.7;
4330
4346
  this.loadedGeometrySize = 0;
4331
4347
  this.geometryCache = new Map();
4332
4348
  this.materialCache = new Map();
4333
4349
  this.textureCache = new Map();
4334
4350
  this.currentMemoryUsage = 0;
4351
+ this.pendingMemoryUsage = 0;
4335
4352
  this.updateMemoryIndicator();
4336
4353
  this.loadedMaterials = new Map();
4337
4354
  this.abortController = new AbortController();
@@ -4387,7 +4404,7 @@ class DynamicGltfLoader {
4387
4404
  } catch (error) {
4388
4405
  console.warn("Error detecting available memory:", error);
4389
4406
  }
4390
- return memoryLimit / 3;
4407
+ return memoryLimit;
4391
4408
  }
4392
4409
  getAbortController() {
4393
4410
  return this.abortController;
@@ -4395,9 +4412,26 @@ class DynamicGltfLoader {
4395
4412
  abortLoading() {
4396
4413
  this.abortController.abort();
4397
4414
  }
4415
+ getOptimizedGeometrySize() {
4416
+ let total = 0;
4417
+ const addSize = (obj) => {
4418
+ if (obj && obj.geometry) total += this.estimateGeometrySize(obj);
4419
+ };
4420
+ this.mergedMesh?.forEach(addSize);
4421
+ this.mergedLines?.forEach(addSize);
4422
+ this.mergedLineSegments?.forEach(addSize);
4423
+ this.mergedPoints?.forEach(addSize);
4424
+ return total;
4425
+ }
4398
4426
  updateMemoryIndicator() {
4427
+ const optimizedUsage = this.getOptimizedGeometrySize();
4428
+ const totalUsage = this.currentMemoryUsage + optimizedUsage;
4429
+ const totalUsageEstimate = Math.round(totalUsage * this.memoryEstimationFactor);
4399
4430
  this.dispatchEvent("geometrymemory", {
4400
4431
  currentUsage: this.currentMemoryUsage,
4432
+ optimizedUsage,
4433
+ totalUsage,
4434
+ totalUsageEstimate,
4401
4435
  limit: this.memoryLimit,
4402
4436
  });
4403
4437
  }
@@ -4449,10 +4483,13 @@ class DynamicGltfLoader {
4449
4483
  for (const geo of geometries) {
4450
4484
  currentMemoryUsage += geo.size;
4451
4485
  }
4452
- if (currentMemoryUsage > this.memoryLimit) {
4453
- console.log(`Memory usage (${Math.round(currentMemoryUsage / (1024 * 1024))}MB) exceeds limit`);
4486
+ const effectiveLimitForEviction = this.memoryLimit / this.memoryEstimationFactor;
4487
+ if (currentMemoryUsage > effectiveLimitForEviction) {
4488
+ console.log(
4489
+ `Memory usage (${Math.round((currentMemoryUsage * this.memoryEstimationFactor) / (1024 * 1024))}MB est.) exceeds limit`
4490
+ );
4454
4491
  for (const geo of geometries) {
4455
- if (currentMemoryUsage <= this.memoryLimit) break;
4492
+ if (currentMemoryUsage <= effectiveLimitForEviction) break;
4456
4493
  if (this.abortController.signal.aborted) {
4457
4494
  throw new DOMException("Loading aborted", "AbortError");
4458
4495
  }
@@ -4536,7 +4573,9 @@ class DynamicGltfLoader {
4536
4573
  }
4537
4574
  const materialCount = uniqueMaterialIds.size;
4538
4575
  const textureCount = uniqueTextureIds.size;
4539
- const estimatedGpuMemoryBytes = geometryMemoryBytes;
4576
+ const optimizedUsageBytes = this.getOptimizedGeometrySize();
4577
+ const totalUsageBytes = geometryMemoryBytes + optimizedUsageBytes;
4578
+ const estimatedGpuMemoryBytes = Math.round(totalUsageBytes * this.memoryEstimationFactor);
4540
4579
  if (!this._webglInfoCache) {
4541
4580
  try {
4542
4581
  const gl = this.renderer.getContext();
@@ -4573,7 +4612,12 @@ class DynamicGltfLoader {
4573
4612
  },
4574
4613
  },
4575
4614
  memory: {
4576
- geometries: { count: geometryCount, bytes: geometryMemoryBytes },
4615
+ geometries: {
4616
+ count: geometryCount,
4617
+ bytes: geometryMemoryBytes,
4618
+ optimizedBytes: optimizedUsageBytes,
4619
+ totalRawBytes: totalUsageBytes,
4620
+ },
4577
4621
  textures: { count: textureCount },
4578
4622
  materials: { count: materialCount },
4579
4623
  totalEstimatedGpuBytes: estimatedGpuMemoryBytes,
@@ -4585,9 +4629,12 @@ class DynamicGltfLoader {
4585
4629
  },
4586
4630
  };
4587
4631
  }
4588
- async loadNode(nodeId, onLoadFinishCb) {
4632
+ async loadNode(nodeId, onLoadFinishCb, reservedEstimatedSize = 0) {
4589
4633
  const node = this.nodes.get(nodeId);
4590
- if (!node || node.loaded || node.loading) return;
4634
+ if (!node || node.loaded || node.loading) {
4635
+ this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
4636
+ return;
4637
+ }
4591
4638
  node.loading = true;
4592
4639
  const meshDef = node.structure.getJson().meshes[node.meshIndex];
4593
4640
  try {
@@ -4670,6 +4717,7 @@ class DynamicGltfLoader {
4670
4717
  if (bufferRequests.length === 0) {
4671
4718
  node.loaded = true;
4672
4719
  node.loading = false;
4720
+ this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
4673
4721
  return;
4674
4722
  }
4675
4723
  bufferRequests.sort((a, b) => a.offset - b.offset);
@@ -4781,6 +4829,7 @@ class DynamicGltfLoader {
4781
4829
  }
4782
4830
  node.loaded = true;
4783
4831
  node.loading = false;
4832
+ this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
4784
4833
  const geometrySize = this.estimateGeometrySize(node.object);
4785
4834
  this.geometryCache.set(node.object.uuid, geometrySize);
4786
4835
  this.currentMemoryUsage += geometrySize;
@@ -4789,6 +4838,7 @@ class DynamicGltfLoader {
4789
4838
  }
4790
4839
  } catch (error) {
4791
4840
  node.loading = false;
4841
+ this.pendingMemoryUsage = Math.max(0, this.pendingMemoryUsage - reservedEstimatedSize);
4792
4842
  if (error.name === "AbortError") {
4793
4843
  return;
4794
4844
  }
@@ -5004,15 +5054,18 @@ class DynamicGltfLoader {
5004
5054
  let loadedCount = 0;
5005
5055
  let lastLoadedCount = 0;
5006
5056
  const totalNodes = nodesToLoad.length;
5057
+ const progressTotal = { value: totalNodes };
5007
5058
  const loadProgress = async () => {
5008
5059
  loadedCount++;
5060
+ const total = progressTotal.value;
5061
+ const percentage = total > 0 ? Math.min(100, Math.round((loadedCount / total) * 100)) : 0;
5009
5062
  if (loadedCount - lastLoadedCount > 1000) {
5010
5063
  lastLoadedCount = loadedCount;
5011
5064
  this.updateMemoryIndicator();
5012
5065
  this.dispatchEvent("geometryprogress", {
5013
- percentage: Math.round((loadedCount / totalNodes) * 100),
5066
+ percentage,
5014
5067
  loaded: loadedCount,
5015
- total: totalNodes,
5068
+ total,
5016
5069
  });
5017
5070
  this.dispatchEvent("update");
5018
5071
  await new Promise((resolve) => {
@@ -5022,20 +5075,22 @@ class DynamicGltfLoader {
5022
5075
  };
5023
5076
  try {
5024
5077
  const loadOperations = [];
5078
+ let memoryLimitReached = false;
5025
5079
  for (const nodeId of nodesToLoad) {
5026
5080
  if (this.abortController.signal.aborted) {
5027
5081
  throw new DOMException("Loading aborted", "AbortError");
5028
5082
  }
5029
5083
  const estimatedSize = await this.estimateNodeSize(nodeId);
5030
- if (this.currentMemoryUsage + estimatedSize > this.memoryLimit) {
5031
- console.log(`Memory limit reached after loading ${loadedCount} nodes`);
5032
- this.dispatchEvent("geometryerror", {
5033
- message: "Memory limit reached",
5034
- });
5035
- this.dispatchEvent("update");
5036
- return loadedCount;
5084
+ const estimated = Number(estimatedSize) || 0;
5085
+ const effectiveLimit = this.memoryLimit / this.optimizationMemoryMultiplier / this.memoryEstimationFactor;
5086
+ if (this.currentMemoryUsage + this.pendingMemoryUsage + estimated > effectiveLimit) {
5087
+ memoryLimitReached = true;
5088
+ progressTotal.value = loadOperations.length;
5089
+ console.log(`Memory limit reached after scheduling ${loadOperations.length} nodes`);
5090
+ break;
5037
5091
  }
5038
- loadOperations.push(this.loadNode(nodeId, loadProgress));
5092
+ this.pendingMemoryUsage += estimated;
5093
+ loadOperations.push(this.loadNode(nodeId, loadProgress, estimated));
5039
5094
  }
5040
5095
  for (const structure of this.structures) {
5041
5096
  loadOperations.push(structure.flushBufferRequests());
@@ -5043,7 +5098,8 @@ class DynamicGltfLoader {
5043
5098
  await Promise.all(loadOperations);
5044
5099
  this.dispatchEvent("geometryend", {
5045
5100
  totalLoaded: loadedCount,
5046
- totalNodes,
5101
+ totalNodes: progressTotal.value,
5102
+ memoryLimitReached,
5047
5103
  });
5048
5104
  return loadedCount;
5049
5105
  } catch (error) {
@@ -5353,6 +5409,7 @@ class DynamicGltfLoader {
5353
5409
  this.transformedGeometries.clear();
5354
5410
  this.totalLoadedObjects = 0;
5355
5411
  this.currentMemoryUsage = 0;
5412
+ this.pendingMemoryUsage = 0;
5356
5413
  this.loadedGeometrySize = 0;
5357
5414
  this.abortController = new AbortController();
5358
5415
  this.updateMemoryIndicator();
@@ -5559,6 +5616,7 @@ class DynamicGltfLoader {
5559
5616
  progress: 100,
5560
5617
  message: `Optimization complete! ${this.maxObjectId} objects processed.`,
5561
5618
  });
5619
+ this.updateMemoryIndicator();
5562
5620
  this.dispatchEvent("update");
5563
5621
  }
5564
5622
  async mergeMeshGroups(materialGroups, rootGroup) {